diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000000..fd54a153a160 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,12 @@ + + + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5364c1e9f466..4cf0e5fba537 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,6 +38,8 @@ concurrency: cancel-in-progress: true env: TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate" + # This will be empty in PR jobs. + TOOLSTATE_REPO_ACCESS_TOKEN: ${{ secrets.TOOLSTATE_REPO_ACCESS_TOKEN }} jobs: # The job matrix for `calculate_matrix` is defined in src/ci/github-actions/jobs.yml. # It calculates which jobs should be executed, based on the data of the ${{ github }} context. @@ -93,8 +95,6 @@ jobs: path-type: inherit install: > make - dos2unix - diffutils - name: disable git crlf conversion run: git config --global core.autocrlf false @@ -155,9 +155,6 @@ jobs: - name: checkout submodules run: src/ci/scripts/checkout-submodules.sh - - name: install MSYS2 - run: src/ci/scripts/install-msys2.sh - - name: install MinGW run: src/ci/scripts/install-mingw.sh @@ -190,7 +187,6 @@ jobs: env: AWS_ACCESS_KEY_ID: ${{ env.CACHES_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }} - TOOLSTATE_REPO_ACCESS_TOKEN: ${{ secrets.TOOLSTATE_REPO_ACCESS_TOKEN }} - name: create github artifacts run: src/ci/scripts/create-doc-artifacts.sh @@ -240,4 +236,5 @@ jobs: shell: bash if: needs.calculate_matrix.outputs.run_type == 'auto' env: - TOOLSTATE_REPO_ACCESS_TOKEN: ${{ secrets.TOOLSTATE_REPO_ACCESS_TOKEN }} + TOOLSTATE_ISSUES_API_URL: https://api.github.com/repos/rust-lang/rust/issues + TOOLSTATE_PUBLISH: 1 diff --git a/.gitignore b/.gitignore index 485968d9c56f..87d02563ed04 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,7 @@ build/ /src/tools/x/target # Created by default with `src/ci/docker/run.sh` /obj/ +/rustc-ice* ## Temporary files *~ diff --git a/.gitmodules b/.gitmodules index 802d61eea293..9ad207a0d522 100644 --- a/.gitmodules +++ b/.gitmodules @@ -33,7 +33,7 @@ [submodule "src/llvm-project"] path = src/llvm-project url = https://github.com/rust-lang/llvm-project.git - branch = rustc/18.0-2024-02-13 + branch = rustc/18.1-2024-05-19 shallow = true [submodule "src/doc/embedded-book"] path = src/doc/embedded-book @@ -43,3 +43,7 @@ path = library/backtrace url = https://github.com/rust-lang/backtrace-rs.git shallow = true +[submodule "src/tools/rustc-perf"] + path = src/tools/rustc-perf + url = https://github.com/rust-lang/rustc-perf.git + shallow = true diff --git a/.mailmap b/.mailmap index 0bd16a797ca7..33673244be6d 100644 --- a/.mailmap +++ b/.mailmap @@ -379,6 +379,7 @@ Markus Westerlind Markus Martin Carton Martin Habovštiak Martin Hafskjold Thoresen +Martin Nordholts Matej Lach Matej Ľach Mateusz Mikuła Mateusz Mikuła diff --git a/Cargo.lock b/Cargo.lock index 186892af21c0..da99ac9b8afc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,9 +7,18 @@ name = "addr2line" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli 0.28.1", +] + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "compiler_builtins", - "gimli", + "gimli 0.29.0", "rustc-std-workspace-alloc", "rustc-std-workspace-core", ] @@ -74,9 +83,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "ammonia" -version = "3.3.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e6d1c7838db705c9b756557ee27c384ce695a1c51a6fe528784cb1c6840170" +checksum = "1ab99eae5ee58501ab236beb6f20f6ca39be615267b014899c89b2f0bc18a459" dependencies = [ "html5ever", "maplit", @@ -122,9 +131,9 @@ dependencies = [ [[package]] name = "annotate-snippets" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5a59f105fb9635e9eebdc1e29d53e764fa5795b9cf899a638a53e61567ef61" +checksum = "24e35ed54e5ea7997c14ed4c70ba043478db1112e98263b3b035907aa197d991" dependencies = [ "anstyle", "unicode-width", @@ -180,9 +189,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] @@ -212,9 +221,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" dependencies = [ "backtrace", ] @@ -257,7 +266,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] @@ -275,6 +284,21 @@ dependencies = [ "nom", ] +[[package]] +name = "assert_cmd" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8" +dependencies = [ + "anstyle", + "bstr", + "doc-comment", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + [[package]] name = "autocfg" version = "1.3.0" @@ -287,7 +311,7 @@ version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ - "addr2line", + "addr2line 0.21.0", "cc", "cfg-if", "libc", @@ -348,7 +372,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" dependencies = [ "memchr", - "regex-automata 0.3.9", + "regex-automata 0.3.7", "serde", ] @@ -414,9 +438,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "camino" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] @@ -477,9 +501,9 @@ version = "0.1.0" [[package]] name = "cc" -version = "1.0.97" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" [[package]] name = "cfg-if" @@ -518,7 +542,7 @@ checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" dependencies = [ "chrono", "chrono-tz-build", - "phf 0.11.2", + "phf", ] [[package]] @@ -528,8 +552,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" dependencies = [ "parse-zoneinfo", - "phf 0.11.2", - "phf_codegen 0.11.2", + "phf", + "phf_codegen", ] [[package]] @@ -544,9 +568,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -564,47 +588,47 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", "terminal_size", ] [[package]] name = "clap_complete" -version = "4.5.2" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79504325bf38b10165b02e89b4347300f855f273c4cb30c4a3209e6583275e" +checksum = "d2020fa13af48afc65a9a87335bda648309ab3d154cd03c7ff95b378c7ed39c4" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "clippy" -version = "0.1.80" +version = "0.1.81" dependencies = [ "anstream", "clippy_config", @@ -620,7 +644,7 @@ dependencies = [ "regex", "rustc_tools_util", "serde", - "syn 2.0.62", + "syn 2.0.66", "tempfile", "termize", "tokio", @@ -631,7 +655,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.80" +version = "0.1.81" dependencies = [ "rustc-semver", "serde", @@ -647,14 +671,14 @@ dependencies = [ "clap", "indoc", "itertools 0.12.1", - "opener", + "opener 0.6.1", "shell-escape", "walkdir", ] [[package]] name = "clippy_lints" -version = "0.1.80" +version = "0.1.81" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -664,7 +688,7 @@ dependencies = [ "itertools 0.12.1", "quine-mc_cluskey", "regex", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", "rustc-semver", "semver", "serde", @@ -679,7 +703,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.80" +version = "0.1.81" dependencies = [ "arrayvec", "clippy_config", @@ -730,7 +754,7 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] @@ -825,16 +849,6 @@ dependencies = [ "rand_xorshift", ] -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -864,18 +878,18 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] @@ -901,9 +915,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crypto-common" @@ -957,9 +971,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" dependencies = [ "darling_core", "darling_macro", @@ -967,27 +981,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn 2.0.62", + "strsim", + "syn 2.0.66", ] [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] @@ -996,13 +1010,24 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69" +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi", +] + [[package]] name = "declare_clippy_lint" -version = "0.1.80" +version = "0.1.81" dependencies = [ "itertools 0.12.1", "quote", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] @@ -1043,7 +1068,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] @@ -1053,18 +1078,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.66", ] [[package]] @@ -1076,7 +1101,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] @@ -1085,6 +1110,12 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.10.7" @@ -1165,7 +1196,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] @@ -1188,10 +1219,16 @@ dependencies = [ ] [[package]] -name = "either" -version = "1.11.0" +name = "doc-comment" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "elasticlunr-rs" @@ -1229,15 +1266,6 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" -[[package]] -name = "encoding_rs" -version = "0.8.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" -dependencies = [ - "cfg-if", -] - [[package]] name = "env_filter" version = "0.1.0" @@ -1388,21 +1416,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1503,7 +1516,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] @@ -1575,9 +1588,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -1598,6 +1611,17 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + [[package]] name = "glob" version = "0.3.1" @@ -1626,25 +1650,6 @@ dependencies = [ "serde", ] -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "handlebars" version = "5.1.2" @@ -1689,6 +1694,12 @@ name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", @@ -1729,52 +1740,18 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.26.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" dependencies = [ "log", "mac", "markup5ever", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.66", ] -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - [[package]] name = "humansize" version = "2.1.3" @@ -1790,43 +1767,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "hyper" -version = "0.14.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "iana-time-zone" version = "0.1.60" @@ -1851,10 +1791,22 @@ dependencies = [ ] [[package]] -name = "icu_list" -version = "1.4.0" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6c04ec71ad1bacdbfb47164d4801f80a0533d9340f94f1a880f521eff59f54" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_list" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfeda1d7775b6548edd4e8b7562304a559a91ed56ab56e18961a053f367c365" dependencies = [ "displaydoc", "icu_list_data", @@ -1866,15 +1818,15 @@ dependencies = [ [[package]] name = "icu_list_data" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f6afcf7a9a7fedece70b7f17d7a7ecdfb8df145d37ae46d0277cd1e3932532" +checksum = "e1825170d2c6679cb20dbd96a589d034e49f698aed9a2ef4fafc9a0101ed298f" [[package]] name = "icu_locid" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c0aa2536adc14c07e2a521e95512b75ed8ef832f0fdf9299d4a0a45d2be2a9d" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", "litemap", @@ -1885,9 +1837,9 @@ dependencies = [ [[package]] name = "icu_locid_transform" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c17d8f6524fdca4471101dd71f0a132eb6382b5d6d7f2970441cb25f6f435a" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" dependencies = [ "displaydoc", "icu_locid", @@ -1899,15 +1851,60 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545c6c3e8bf9580e2dafee8de6f9ec14826aaf359787789c7724f1f85f47d3dc" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" [[package]] name = "icu_provider" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba58e782287eb6950247abbf11719f83f5d4e4a5c1f2cd490d30a334bc47c2f4" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ "displaydoc", "icu_locid", @@ -1922,9 +1919,9 @@ dependencies = [ [[package]] name = "icu_provider_adapters" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a229f978260da7c3aabb68cb7dc7316589936680570fe55e50fdd3f97711a4dd" +checksum = "d6324dfd08348a8e0374a447ebd334044d766b1839bb8d5ccf2482a99a77c0bc" dependencies = [ "icu_locid", "icu_locid_transform", @@ -1935,13 +1932,13 @@ dependencies = [ [[package]] name = "icu_provider_macros" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2abdd3a62551e8337af119c5899e600ca0c88ec8f23a46c60ba216c803dcf1a" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] @@ -1952,12 +1949,14 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", ] [[package]] @@ -2044,9 +2043,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] @@ -2070,12 +2069,6 @@ dependencies = [ "unic-langid", ] -[[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - [[package]] name = "is_terminal_polyfill" version = "1.70.0" @@ -2190,13 +2183,23 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "libffi" version = "3.2.0" @@ -2223,7 +2226,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.52.5", + "windows-targets 0.48.5", ] [[package]] @@ -2244,9 +2247,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.16" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" dependencies = [ "cc", "libc", @@ -2254,6 +2257,21 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "line-wrap" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd1bc4d24ad230d21fb898d1116b1801d7adfc449d42026475862ab48b11e70e" + +[[package]] +name = "linereader" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d921fea6860357575519aca014c6e22470585accdd543b370c404a8a72d0dd1d" +dependencies = [ + "memchr", +] + [[package]] name = "linkchecker" version = "0.1.0" @@ -2262,6 +2280,12 @@ dependencies = [ "regex", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "lint-docs" version = "0.1.0" @@ -2273,15 +2297,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "litemap" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d642685b028806386b2b6e75685faadd3eb65a85fff7df711ce18446a422da" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" [[package]] name = "lld-wrapper" @@ -2339,13 +2363,13 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "markup5ever" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" dependencies = [ "log", - "phf 0.10.1", - "phf_codegen 0.10.0", + "phf", + "phf_codegen", "string_cache", "string_cache_codegen", "tendril", @@ -2372,9 +2396,9 @@ dependencies = [ [[package]] name = "mdbook" -version = "0.4.37" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c33564061c3c640bed5ace7d6a2a1b65f2c64257d1ac930c15e94ed0fb561d3" +checksum = "b45a38e19bd200220ef07c892b0157ad3d2365e5b5a267ca01ad12182491eea5" dependencies = [ "ammonia", "anyhow", @@ -2387,7 +2411,7 @@ dependencies = [ "log", "memchr", "once_cell", - "opener", + "opener 0.7.1", "pulldown-cmark 0.10.3", "regex", "serde", @@ -2398,6 +2422,52 @@ dependencies = [ "topological-sort", ] +[[package]] +name = "mdbook-i18n-helpers" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c8f972ab672d366c3dad77ea5aa7bae68db2d25fbeb889849f97469d7b658e4" +dependencies = [ + "anyhow", + "chrono", + "mdbook", + "polib", + "pulldown-cmark 0.10.3", + "pulldown-cmark-to-cmark", + "regex", + "semver", + "serde_json", + "syntect", + "textwrap", +] + +[[package]] +name = "mdbook-trpl-listing" +version = "0.1.0" +dependencies = [ + "assert_cmd", + "clap", + "mdbook", + "pulldown-cmark 0.10.3", + "pulldown-cmark-to-cmark", + "serde_json", + "thiserror", + "toml 0.8.14", + "xmlparser", +] + +[[package]] +name = "mdbook-trpl-note" +version = "1.0.0" +dependencies = [ + "assert_cmd", + "clap", + "mdbook", + "pulldown-cmark 0.10.3", + "pulldown-cmark-to-cmark", + "serde_json", +] + [[package]] name = "measureme" version = "11.0.1" @@ -2470,9 +2540,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", "compiler_builtins", @@ -2480,17 +2550,6 @@ dependencies = [ "rustc-std-workspace-core", ] -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "miow" version = "0.6.0" @@ -2528,24 +2587,6 @@ dependencies = [ name = "miropt-test-tools" version = "0.1.0" -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -2604,9 +2645,9 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.49.0" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c073d3c1930d0751774acf49e66653acecb416c3a54c6ec095a9b11caddb5a68" +checksum = "dd2800e1520bdc966782168a627aa5d1ad92e33b984bf7c7615d31280c83ff14" dependencies = [ "windows-sys 0.48.0", ] @@ -2632,7 +2673,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] @@ -2648,14 +2689,11 @@ version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ - "compiler_builtins", "crc32fast", "flate2", "hashbrown", "indexmap", "memchr", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", "ruzstd 0.5.0", "wasmparser", ] @@ -2671,6 +2709,18 @@ dependencies = [ "ruzstd 0.6.0", ] +[[package]] +name = "object" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" +dependencies = [ + "compiler_builtins", + "memchr", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + [[package]] name = "odht" version = "0.3.1" @@ -2686,6 +2736,28 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "onig" +version = "6.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f" +dependencies = [ + "bitflags 1.3.2", + "libc", + "once_cell", + "onig_sys", +] + +[[package]] +name = "onig_sys" +version = "69.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "opener" version = "0.6.1" @@ -2698,29 +2770,15 @@ dependencies = [ ] [[package]] -name = "openssl" -version = "0.10.64" +name = "opener" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "f8df34be653210fbe9ffaff41d3b92721c56ce82dfee58ee684f9afb5e3a90c0" dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.62", + "bstr", + "dbus", + "normpath", + "windows-sys 0.52.0", ] [[package]] @@ -2756,7 +2814,6 @@ dependencies = [ "humansize", "humantime", "log", - "reqwest", "serde", "serde_json", "sysinfo", @@ -2764,7 +2821,6 @@ dependencies = [ "tar", "tempfile", "xz2", - "zip", ] [[package]] @@ -2830,9 +2886,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -2846,7 +2902,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall 0.5.2", "smallvec", "windows-targets 0.52.5", ] @@ -2912,7 +2968,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] @@ -2926,15 +2982,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", -] - [[package]] name = "phf" version = "0.11.2" @@ -2944,16 +2991,6 @@ dependencies = [ "phf_shared 0.11.2", ] -[[package]] -name = "phf_codegen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", -] - [[package]] name = "phf_codegen" version = "0.11.2" @@ -3020,6 +3057,29 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "plist" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9d34169e64b3c7a80c8621a48adaf44e0cf62c78a9b25dd9dd35f1881a17cf9" +dependencies = [ + "base64", + "indexmap", + "line-wrap", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "polib" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b393b155cf9be86249cba1b56cc81be0e6212c66d94ac0d76d37a1761f3bb1b" +dependencies = [ + "linereader", +] + [[package]] name = "polonius-engine" version = "0.13.0" @@ -3055,6 +3115,33 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "predicates" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" +dependencies = [ + "anstyle", + "difflib", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "prettydiff" version = "0.6.4" @@ -3073,9 +3160,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -3124,6 +3211,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993" dependencies = [ "bitflags 2.5.0", + "getopts", "memchr", "pulldown-cmark-escape", "unicase", @@ -3135,12 +3223,30 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd348ff538bc9caeda7ee8cad2d1d48236a1f443c1fa3913c6a02fe0043b1dd3" +[[package]] +name = "pulldown-cmark-to-cmark" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f609795c8d835f79dcfcf768415b9fb57ef1b74891e99f86e73f43a7a257163b" +dependencies = [ + "pulldown-cmark 0.10.3", +] + [[package]] name = "punycode" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9e1dcb320d6839f6edb64f7a4a59d39b30480d4d1765b56873f7c858538a5fe" +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", +] + [[package]] name = "quine-mc_cluskey" version = "0.2.4" @@ -3158,9 +3264,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "4.4.0" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c47196f636c4cc0634b73b0405323d177753c2e15e866952c64ea22902567a34" +checksum = "e9e935efc5854715dfc0a4c9ef18dc69dee0ec3bf9cc3ab740db831c0fdd86a3" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", @@ -3256,9 +3362,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags 2.5.0", ] @@ -3276,12 +3382,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.4" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" dependencies = [ "aho-corasick", "memchr", + "regex-automata 0.3.7", "regex-syntax 0.7.5", ] @@ -3305,15 +3412,20 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.9" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.5", +] [[package]] name = "regex-lite" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b661b2f27137bdbc16f00eda72866a92bb28af1753ffbd56744fb6e2e9cd8e" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" [[package]] name = "regex-syntax" @@ -3329,9 +3441,9 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "remote-test-client" @@ -3349,46 +3461,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - [[package]] name = "rls" version = "2.0.0" @@ -3398,23 +3470,15 @@ dependencies = [ [[package]] name = "run_make_support" -version = "0.0.0" +version = "0.2.0" dependencies = [ - "gimli", + "gimli 0.28.1", "object 0.34.0", "regex", "similar", "wasmparser", ] -[[package]] -name = "rust-demangler" -version = "0.0.1" -dependencies = [ - "regex", - "rustc-demangle", -] - [[package]] name = "rustbook" version = "0.1.0" @@ -3422,13 +3486,16 @@ dependencies = [ "clap", "env_logger", "mdbook", + "mdbook-i18n-helpers", + "mdbook-trpl-listing", + "mdbook-trpl-note", ] [[package]] name = "rustc-build-sysroot" -version = "0.4.7" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab1dbbd1bdf65fdac44c885f6cca147ba179108ce284b60a08ccc04b1f1dbac0" +checksum = "fa3ca63cc537c1cb69e4c2c0afc5fda2ccd36ac84c97d5a4ae05e69b1c834afb" dependencies = [ "anyhow", "rustc_version", @@ -3570,7 +3637,6 @@ dependencies = [ "rustc_macros", "rustc_serialize", "rustc_span", - "smallvec", ] [[package]] @@ -3651,7 +3717,6 @@ dependencies = [ "icu_locid", "icu_locid_transform", "icu_provider", - "icu_provider_adapters", "zerovec", ] @@ -3832,7 +3897,6 @@ dependencies = [ "portable-atomic", "rustc-hash", "rustc-rayon", - "rustc-rayon-core", "rustc_arena", "rustc_graphviz", "rustc_index", @@ -3873,7 +3937,6 @@ dependencies = [ "rustc_expand", "rustc_feature", "rustc_fluent_macro", - "rustc_hir", "rustc_hir_analysis", "rustc_hir_pretty", "rustc_hir_typeck", @@ -4001,7 +4064,7 @@ dependencies = [ "fluent-syntax", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", "unic-langid", ] @@ -4135,8 +4198,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", - "synstructure", + "syn 2.0.66", ] [[package]] @@ -4228,6 +4290,7 @@ dependencies = [ "rustc_feature", "rustc_fluent_macro", "rustc_hir", + "rustc_hir_pretty", "rustc_index", "rustc_infer", "rustc_macros", @@ -4282,7 +4345,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", "synstructure", ] @@ -4326,7 +4389,6 @@ dependencies = [ "field-offset", "gsgdt", "polonius-engine", - "rustc-rayon", "rustc-rayon-core", "rustc_apfloat", "rustc_arena", @@ -4343,7 +4405,6 @@ dependencies = [ "rustc_hir_pretty", "rustc_index", "rustc_macros", - "rustc_next_trait_solver", "rustc_query_system", "rustc_serialize", "rustc_session", @@ -4359,7 +4420,6 @@ dependencies = [ name = "rustc_mir_build" version = "0.0.0" dependencies = [ - "either", "itertools 0.12.1", "rustc_apfloat", "rustc_arena", @@ -4378,7 +4438,6 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", - "smallvec", "tracing", ] @@ -4418,6 +4477,7 @@ dependencies = [ "rustc_fluent_macro", "rustc_hir", "rustc_index", + "rustc_infer", "rustc_macros", "rustc_middle", "rustc_mir_build", @@ -4452,13 +4512,16 @@ dependencies = [ name = "rustc_next_trait_solver" version = "0.0.0" dependencies = [ + "bitflags 2.5.0", "derivative", "rustc_ast_ir", "rustc_data_structures", + "rustc_index", "rustc_macros", "rustc_serialize", "rustc_type_ir", "rustc_type_ir_macros", + "tracing", ] [[package]] @@ -4563,7 +4626,6 @@ version = "0.0.0" dependencies = [ "field-offset", "measureme", - "rustc-rayon-core", "rustc_data_structures", "rustc_errors", "rustc_hir", @@ -4851,6 +4913,7 @@ dependencies = [ "rustc_span", "rustc_type_ir_macros", "smallvec", + "tracing", ] [[package]] @@ -4859,7 +4922,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", "synstructure", ] @@ -4957,7 +5020,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] @@ -5003,20 +5066,11 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64", -] - [[package]] name = "rustversion" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ruzstd" @@ -5076,29 +5130,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "security-framework" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" -dependencies = [ - "bitflags 2.5.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "self_cell" version = "0.10.3" @@ -5125,22 +5156,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.201" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] @@ -5157,25 +5188,13 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "sha1" version = "0.10.6" @@ -5264,9 +5283,9 @@ dependencies = [ [[package]] name = "spanned" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccdf4f5590b7e6fbd4f2e80d442789079a6fff7c12ef921a9de358b7b353098e" +checksum = "ed14ba8b4b82241bd5daba2c49185d4a0581a0058355fe96537338f002b8605d" dependencies = [ "bstr", "color-eyre", @@ -5312,7 +5331,6 @@ name = "stable_mir" version = "0.1.0-preview" dependencies = [ "scoped-tls", - "tracing", ] [[package]] @@ -5338,7 +5356,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" name = "std" version = "0.0.0" dependencies = [ - "addr2line", + "addr2line 0.22.0", "alloc", "cfg-if", "compiler_builtins", @@ -5346,10 +5364,10 @@ dependencies = [ "dlmalloc", "fortanix-sgx-abi", "hashbrown", - "hermit-abi", + "hermit-abi 0.4.0", "libc", "miniz_oxide", - "object 0.32.2", + "object 0.36.0", "panic_abort", "panic_unwind", "profiler_builtins", @@ -5400,12 +5418,6 @@ dependencies = [ "quote", ] -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -5452,21 +5464,15 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.62" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f660c3bfcefb88c538776b6685a0c472e3128b51e74d48793dc2a488196e8eb" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "synstructure" version = "0.13.1" @@ -5475,7 +5481,29 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", +] + +[[package]] +name = "syntect" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" +dependencies = [ + "bincode", + "bitflags 1.3.2", + "flate2", + "fnv", + "once_cell", + "onig", + "plist", + "regex-syntax 0.8.4", + "serde", + "serde_derive", + "serde_json", + "thiserror", + "walkdir", + "yaml-rust", ] [[package]] @@ -5501,27 +5529,6 @@ dependencies = [ "test", ] -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tabled" version = "0.15.0" @@ -5534,9 +5541,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" dependencies = [ "filetime", "libc", @@ -5606,6 +5613,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + [[package]] name = "test" version = "0.0.0" @@ -5613,11 +5626,15 @@ dependencies = [ "core", "getopts", "libc", - "panic_abort", - "panic_unwind", "std", ] +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" + [[package]] name = "thin-vec" version = "0.2.13" @@ -5626,22 +5643,22 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] @@ -5650,7 +5667,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4db52ee8fec06e119b692ef3dd2c4cf621a99204c1b8c47407870ed050305b9b" dependencies = [ - "gimli", + "gimli 0.28.1", "hashbrown", "object 0.32.2", "tracing", @@ -5726,9 +5743,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c02bf3c538ab32ba913408224323915f4ef9a6d61c0e85d493f355921c0ece" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", @@ -5751,40 +5768,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", - "libc", - "mio", "pin-project-lite", - "socket2", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", ] [[package]] @@ -5805,14 +5795,26 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.14", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] @@ -5827,7 +5829,20 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.13", ] [[package]] @@ -5836,12 +5851,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - [[package]] name = "tracing" version = "0.1.37" @@ -5862,7 +5871,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] @@ -5917,22 +5926,16 @@ dependencies = [ [[package]] name = "tracing-tree" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65139ecd2c3f6484c3b99bc01c77afe21e95473630747c7aca525e78b0666675" +checksum = "b56c62d2c80033cb36fae448730a2f2ef99410fe3ecbffc916681a32f6807dbe" dependencies = [ - "nu-ansi-term 0.49.0", + "nu-ansi-term 0.50.0", "tracing-core", "tracing-log", "tracing-subscriber", ] -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - [[package]] name = "twox-hash" version = "1.6.3" @@ -6007,7 +6010,7 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29e5f4ffcbab82453958fbf59990e981b8e8a177dcd60c2bd8f9b52c3036a6e1" dependencies = [ - "annotate-snippets 0.11.2", + "annotate-snippets 0.11.4", "anyhow", "bstr", "cargo-platform", @@ -6068,7 +6071,7 @@ checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b" dependencies = [ "proc-macro-hack", "quote", - "syn 2.0.62", + "syn 2.0.66", "unic-langid-impl", ] @@ -6088,12 +6091,6 @@ dependencies = [ "ucd-parse", ] -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -6139,9 +6136,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", @@ -6189,15 +6186,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37a19a21a537f635c16c7576f22d0f2f7d63353c1337ad4ce0d8001c7952a25b" dependencies = [ "compiler_builtins", - "gimli", + "gimli 0.28.1", "rustc-std-workspace-core", ] [[package]] name = "url" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" dependencies = [ "form_urlencoded", "idna", @@ -6210,6 +6207,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + [[package]] name = "utf8-width" version = "0.1.7" @@ -6217,10 +6220,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" [[package]] -name = "utf8parse" -version = "0.2.1" +name = "utf8_iter" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" @@ -6249,6 +6258,15 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -6259,15 +6277,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -6300,22 +6309,10 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.92" @@ -6334,7 +6331,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6364,16 +6361,6 @@ dependencies = [ "semver", ] -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "winapi" version = "0.3.9" @@ -6417,15 +6404,15 @@ dependencies = [ [[package]] name = "windows-bindgen" -version = "0.56.0" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28e3ea6330cf17fdcdce8bf08d0549ce93769dca9bedc6c39c36c8c0e17db46" +checksum = "1ccb96113d6277ba543c0f77e1c5494af8094bf9daf9b85acdc3f1b620e7c7b4" dependencies = [ "proc-macro2", "rayon", "serde", "serde_json", - "syn 2.0.62", + "syn 2.0.66", "windows-metadata", ] @@ -6440,9 +6427,9 @@ dependencies = [ [[package]] name = "windows-metadata" -version = "0.56.0" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3993f7827fff10c454e3a24847075598c7c08108304b8b07943c2c73d78f3b34" +checksum = "8308d076825b9d9e5abc64f8113e96d02b2aeeba869b20fdd65c7e70cda13dfc" [[package]] name = "windows-sys" @@ -6593,20 +6580,25 @@ dependencies = [ ] [[package]] -name = "winreg" -version = "0.50.0" +name = "winnow" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ - "cfg-if", - "windows-sys 0.48.0", + "memchr", ] [[package]] -name = "writeable" -version = "0.5.4" +name = "write16" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad7bb64b8ef9c0aa27b6da38b452b0ee9fd82beaf276a87dd796fb55cbae14e" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "xattr" @@ -6619,6 +6611,12 @@ dependencies = [ "rustix", ] +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + [[package]] name = "xz2" version = "0.1.7" @@ -6628,6 +6626,15 @@ dependencies = [ "lzma-sys", ] +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "yansi-term" version = "0.1.2" @@ -6639,9 +6646,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e71b2e4f287f467794c671e2b8f8a5f3716b3c829079a1c44740148eff07e4" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" dependencies = [ "serde", "stable_deref_trait", @@ -6651,13 +6658,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", "synstructure", ] @@ -6678,35 +6685,35 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", ] [[package]] name = "zerofrom" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655b0814c5c0b19ade497851070c640773304939a6c0fd5f5fb43da0696d05b7" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.66", "synstructure", ] [[package]] name = "zerovec" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff4439ae91fb5c72b8abc12f3f2dbf51bd27e6eadb9f8a5bc8898dddb0e27ea" +checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" dependencies = [ "yoke", "zerofrom", @@ -6715,23 +6722,11 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4e5997cbf58990550ef1f0e5124a05e47e1ebd33a84af25739be6031a62c20" +checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", -] - -[[package]] -name = "zip" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" -dependencies = [ - "byteorder", - "crc32fast", - "crossbeam-utils", - "flate2", + "syn 2.0.66", ] diff --git a/Cargo.toml b/Cargo.toml index a601ebf4369e..c17ea99d0376 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ members = [ "src/tools/remote-test-client", "src/tools/remote-test-server", "src/tools/rust-installer", - "src/tools/rust-demangler", "src/tools/rustdoc", "src/tools/rls", "src/tools/rustfmt", diff --git a/RELEASES.md b/RELEASES.md index 3080f03c7210..305da6a3550e 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,130 @@ +Version 1.79.0 (2024-06-13) +========================== + + + +Language +-------- +- [Stabilize inline `const {}` expressions.](https://github.com/rust-lang/rust/pull/104087/) +- [Prevent opaque types being instantiated twice with different regions within the same function.](https://github.com/rust-lang/rust/pull/116935/) +- [Stabilize WebAssembly target features that are in phase 4 and 5.](https://github.com/rust-lang/rust/pull/117457/) +- [Add the `redundant_lifetimes` lint to detect lifetimes which are semantically redundant.](https://github.com/rust-lang/rust/pull/118391/) +- [Stabilize the `unnameable_types` lint for public types that can't be named.](https://github.com/rust-lang/rust/pull/120144/) +- [Enable debuginfo in macros, and stabilize `-C collapse-macro-debuginfo` and `#[collapse_debuginfo]`.](https://github.com/rust-lang/rust/pull/120845/) +- [Propagate temporary lifetime extension into `if` and `match` expressions.](https://github.com/rust-lang/rust/pull/121346/) +- [Restrict promotion of `const fn` calls.](https://github.com/rust-lang/rust/pull/121557/) +- [Warn against refining impls of crate-private traits with `refining_impl_trait` lint.](https://github.com/rust-lang/rust/pull/121720/) +- [Stabilize associated type bounds (RFC 2289).](https://github.com/rust-lang/rust/pull/122055/) +- [Stabilize importing `main` from other modules or crates.](https://github.com/rust-lang/rust/pull/122060/) +- [Check return types of function types for well-formedness](https://github.com/rust-lang/rust/pull/115538) +- [Rework `impl Trait` lifetime inference](https://github.com/rust-lang/rust/pull/116891/) +- [Change inductive trait solver cycles to be ambiguous](https://github.com/rust-lang/rust/pull/122791) + + + +Compiler +-------- +- [Define `-C strip` to only affect binaries, not artifacts like `.pdb`.](https://github.com/rust-lang/rust/pull/115120/) +- [Stabilize `-Crelro-level` for controlling runtime link hardening.](https://github.com/rust-lang/rust/pull/121694/) +- [Stabilize checking of `cfg` names and values at compile-time with `--check-cfg`.](https://github.com/rust-lang/rust/pull/123501/) + *Note that this only stabilizes the compiler part, the Cargo part is still unstable in this release.* +- [Add `aarch64-apple-visionos` and `aarch64-apple-visionos-sim` tier 3 targets.](https://github.com/rust-lang/rust/pull/121419/) +- [Add `riscv32ima-unknown-none-elf` tier 3 target.](https://github.com/rust-lang/rust/pull/122696/) +- [Promote several Windows targets to tier 2](https://github.com/rust-lang/rust/pull/121712): `aarch64-pc-windows-gnullvm`, `i686-pc-windows-gnullvm`, and `x86_64-pc-windows-gnullvm`. + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + + + +Libraries +--------- + +- [Implement `FromIterator` for `(impl Default + Extend, impl Default + Extend)`.](https://github.com/rust-lang/rust/pull/107462/) +- [Implement `{Div,Rem}Assign>` on `X`.](https://github.com/rust-lang/rust/pull/121952/) +- [Document overrides of `clone_from()` in core/std.](https://github.com/rust-lang/rust/pull/122201/) +- [Link MSVC default lib in core.](https://github.com/rust-lang/rust/pull/122268/) +- [Caution against using `transmute` between pointers and integers.](https://github.com/rust-lang/rust/pull/122379/) +- [Enable frame pointers for the standard library.](https://github.com/rust-lang/rust/pull/122646/) + + + +Stabilized APIs +--------------- + +- [`{integer}::unchecked_add`](https://doc.rust-lang.org/stable/core/primitive.i32.html#method.unchecked_add) +- [`{integer}::unchecked_mul`](https://doc.rust-lang.org/stable/core/primitive.i32.html#method.unchecked_mul) +- [`{integer}::unchecked_sub`](https://doc.rust-lang.org/stable/core/primitive.i32.html#method.unchecked_sub) +- [`<[T]>::split_at_unchecked`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.split_at_unchecked) +- [`<[T]>::split_at_mut_unchecked`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.split_at_mut_unchecked) +- [`<[u8]>::utf8_chunks`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.utf8_chunks) +- [`str::Utf8Chunks`](https://doc.rust-lang.org/stable/core/str/struct.Utf8Chunks.html) +- [`str::Utf8Chunk`](https://doc.rust-lang.org/stable/core/str/struct.Utf8Chunk.html) +- [`<*const T>::is_aligned`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.is_aligned) +- [`<*mut T>::is_aligned`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.is_aligned-1) +- [`NonNull::is_aligned`](https://doc.rust-lang.org/stable/core/ptr/struct.NonNull.html#method.is_aligned) +- [`<*const [T]>::len`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.len) +- [`<*mut [T]>::len`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.len-1) +- [`<*const [T]>::is_empty`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.is_empty) +- [`<*mut [T]>::is_empty`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.is_empty-1) +- [`NonNull::<[T]>::is_empty`](https://doc.rust-lang.org/stable/core/ptr/struct.NonNull.html#method.is_empty) +- [`CStr::count_bytes`](https://doc.rust-lang.org/stable/core/ffi/c_str/struct.CStr.html#method.count_bytes) +- [`io::Error::downcast`](https://doc.rust-lang.org/stable/std/io/struct.Error.html#method.downcast) +- [`num::NonZero`](https://doc.rust-lang.org/stable/core/num/struct.NonZero.html) +- [`path::absolute`](https://doc.rust-lang.org/stable/std/path/fn.absolute.html) +- [`proc_macro::Literal::byte_character`](https://doc.rust-lang.org/stable/proc_macro/struct.Literal.html#method.byte_character) +- [`proc_macro::Literal::c_string`](https://doc.rust-lang.org/stable/proc_macro/struct.Literal.html#method.c_string) + +These APIs are now stable in const contexts: + +- [`Atomic*::into_inner`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicUsize.html#method.into_inner) +- [`io::Cursor::new`](https://doc.rust-lang.org/stable/std/io/struct.Cursor.html#method.new) +- [`io::Cursor::get_ref`](https://doc.rust-lang.org/stable/std/io/struct.Cursor.html#method.get_ref) +- [`io::Cursor::position`](https://doc.rust-lang.org/stable/std/io/struct.Cursor.html#method.position) +- [`io::empty`](https://doc.rust-lang.org/stable/std/io/fn.empty.html) +- [`io::repeat`](https://doc.rust-lang.org/stable/std/io/fn.repeat.html) +- [`io::sink`](https://doc.rust-lang.org/stable/std/io/fn.sink.html) +- [`panic::Location::caller`](https://doc.rust-lang.org/stable/std/panic/struct.Location.html#method.caller) +- [`panic::Location::file`](https://doc.rust-lang.org/stable/std/panic/struct.Location.html#method.file) +- [`panic::Location::line`](https://doc.rust-lang.org/stable/std/panic/struct.Location.html#method.line) +- [`panic::Location::column`](https://doc.rust-lang.org/stable/std/panic/struct.Location.html#method.column) + + + +Cargo +----- + +- [Prevent dashes in `lib.name`, always normalizing to `_`.](https://github.com/rust-lang/cargo/pull/12783/) +- [Stabilize MSRV-aware version requirement selection in `cargo add`.](https://github.com/rust-lang/cargo/pull/13608/) +- [Switch to using `gitoxide` by default for listing files.](https://github.com/rust-lang/cargo/pull/13696/) + + + +Rustdoc +----- + +- [Always display stability version even if it's the same as the containing item.](https://github.com/rust-lang/rust/pull/118441/) +- [Show a single search result for items with multiple paths.](https://github.com/rust-lang/rust/pull/119912/) +- [Support typing `/` in docs to begin a search.](https://github.com/rust-lang/rust/pull/123355/) + + + +Misc +---- + + + +Compatibility Notes +------------------- + +- [Update the minimum external LLVM to 17.](https://github.com/rust-lang/rust/pull/122649/) +- [`RustcEncodable` and `RustcDecodable` are soft-destabilized, to be removed + from the prelude in next edition.](https://github.com/rust-lang/rust/pull/116016/) +- [The `wasm_c_abi` future-incompatibility lint will warn about use of the + non-spec-compliant C ABI.](https://github.com/rust-lang/rust/pull/117918/) + Use `wasm-bindgen v0.2.88` to generate forward-compatible bindings. +- [Check return types of function types for well-formedness](https://github.com/rust-lang/rust/pull/115538) + Version 1.78.0 (2024-05-02) ========================== @@ -19,7 +146,7 @@ Language - [Split `refining_impl_trait` lint into `_reachable`, `_internal` variants](https://github.com/rust-lang/rust/pull/121720/) - [Remove unnecessary type inference when using associated types inside of higher ranked `where`-bounds](https://github.com/rust-lang/rust/pull/119849) - [Weaken eager detection of cyclic types during type inference](https://github.com/rust-lang/rust/pull/119989) -- [`trait Trait: Auto {}`: allow upcasting from `dyn Trait` to `dyn Auto`](https://github.com/rust-lang/rust/pull/119338) +- [`trait Trait: Auto {}`: allow upcasting from `dyn Trait` to `dyn Trait + Auto`](https://github.com/rust-lang/rust/pull/119338) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index a95ef4c460f4..9165b4f2df3d 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -970,7 +970,7 @@ fn univariant< let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align }; let mut max_repr_align = repr.align; let mut inverse_memory_index: IndexVec = fields.indices().collect(); - let optimize = !repr.inhibit_struct_field_reordering_opt(); + let optimize = !repr.inhibit_struct_field_reordering(); if optimize && fields.len() > 1 { let end = if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() }; let optimizing = &mut inverse_memory_index.raw[..end]; @@ -1007,13 +1007,15 @@ fn univariant< // Calculates a sort key to group fields by their alignment or possibly some // size-derived pseudo-alignment. let alignment_group_key = |layout: &F| { + // The two branches here return values that cannot be meaningfully compared with + // each other. However, we know that consistently for all executions of + // `alignment_group_key`, one or the other branch will be taken, so this is okay. if let Some(pack) = pack { // Return the packed alignment in bytes. layout.align.abi.min(pack).bytes() } else { - // Returns `log2(effective-align)`. This is ok since `pack` applies to all - // fields equally. The calculation assumes that size is an integer multiple of - // align, except for ZSTs. + // Returns `log2(effective-align)`. The calculation assumes that size is an + // integer multiple of align, except for ZSTs. let align = layout.align.abi.bytes(); let size = layout.size.bytes(); let niche_size = layout.largest_niche.map(|n| n.available(dl)).unwrap_or(0); diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 53aa8ad7cca5..a6662d4e0e4d 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1,7 +1,9 @@ -#![cfg_attr(feature = "nightly", feature(step_trait))] +// tidy-alphabetical-start #![cfg_attr(feature = "nightly", allow(internal_features))] #![cfg_attr(feature = "nightly", doc(rust_logo))] #![cfg_attr(feature = "nightly", feature(rustdoc_internals))] +#![cfg_attr(feature = "nightly", feature(step_trait))] +// tidy-alphabetical-end use std::fmt; use std::num::{NonZeroUsize, ParseIntError}; @@ -137,23 +139,16 @@ impl ReprOptions { self.c() || self.int.is_some() } - /// Returns `true` if this `#[repr()]` should inhibit struct field reordering - /// optimizations, such as with `repr(C)`, `repr(packed(1))`, or `repr()`. - pub fn inhibit_struct_field_reordering_opt(&self) -> bool { - if let Some(pack) = self.pack { - if pack.bytes() == 1 { - return true; - } - } - + /// Returns `true` if this `#[repr()]` guarantees a fixed field order, + /// e.g. `repr(C)` or `repr()`. + pub fn inhibit_struct_field_reordering(&self) -> bool { self.flags.intersects(ReprFlags::IS_UNOPTIMISABLE) || self.int.is_some() } /// Returns `true` if this type is valid for reordering and `-Z randomize-layout` /// was enabled for its declaration crate. pub fn can_randomize_type_layout(&self) -> bool { - !self.inhibit_struct_field_reordering_opt() - && self.flags.contains(ReprFlags::RANDOMIZE_LAYOUT) + !self.inhibit_struct_field_reordering() && self.flags.contains(ReprFlags::RANDOMIZE_LAYOUT) } /// Returns `true` if this `#[repr()]` should inhibit union ABI optimisations. diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index bdbc59821de2..810cb7a9f459 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -7,23 +7,25 @@ //! //! This crate implements several kinds of arena. +// tidy-alphabetical-start +#![allow(clippy::mut_from_ref)] // Arena allocators are one place where this pattern is fine. +#![allow(internal_features)] +#![cfg_attr(test, feature(test))] +#![deny(unsafe_op_in_unsafe_fn)] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(no_crate_inject, attr(deny(warnings))) )] #![doc(rust_logo)] -#![feature(rustdoc_internals)] #![feature(core_intrinsics)] -#![feature(dropck_eyepatch)] -#![feature(new_uninit)] -#![feature(maybe_uninit_slice)] #![feature(decl_macro)] +#![feature(dropck_eyepatch)] +#![feature(maybe_uninit_slice)] +#![feature(new_uninit)] #![feature(rustc_attrs)] -#![cfg_attr(test, feature(test))] +#![feature(rustdoc_internals)] #![feature(strict_provenance)] -#![deny(unsafe_op_in_unsafe_fn)] -#![allow(internal_features)] -#![allow(clippy::mut_from_ref)] // Arena allocators are one of the places where this pattern is fine. +// tidy-alphabetical-end use smallvec::SmallVec; diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 5d37bbd689fe..71932f02017c 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -167,7 +167,7 @@ impl PathSegment { } } -/// The arguments of a path segment. +/// The generic arguments and associated item constraints of a path segment. /// /// E.g., `` as in `Foo` or `(A, B)` as in `Foo(A, B)`. #[derive(Clone, Encodable, Decodable, Debug)] @@ -221,14 +221,13 @@ pub struct AngleBracketedArgs { pub args: ThinVec, } -/// Either an argument for a parameter e.g., `'a`, `Vec`, `0`, -/// or a constraint on an associated item, e.g., `Item = String` or `Item: Bound`. +/// Either an argument for a generic parameter or a constraint on an associated item. #[derive(Clone, Encodable, Decodable, Debug)] pub enum AngleBracketedArg { - /// Argument for a generic parameter. + /// A generic argument for a generic parameter. Arg(GenericArg), - /// Constraint for an associated item. - Constraint(AssocConstraint), + /// A constraint on an associated item. + Constraint(AssocItemConstraint), } impl AngleBracketedArg { @@ -308,6 +307,8 @@ impl TraitBoundModifiers { pub enum GenericBound { Trait(PolyTraitRef, TraitBoundModifiers), Outlives(Lifetime), + /// Precise capturing syntax: `impl Sized + use<'a>` + Use(ThinVec, Span), } impl GenericBound { @@ -315,6 +316,7 @@ impl GenericBound { match self { GenericBound::Trait(t, ..) => t.span, GenericBound::Outlives(l) => l.ident.span, + GenericBound::Use(_, span) => *span, } } } @@ -418,7 +420,7 @@ impl Default for WhereClause { /// A single predicate in a where-clause. #[derive(Clone, Encodable, Decodable, Debug)] pub enum WherePredicate { - /// A type binding (e.g., `for<'c> Foo: Send + Clone + 'c`). + /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`). BoundPredicate(WhereBoundPredicate), /// A lifetime predicate (e.g., `'a: 'b + 'c`). RegionPredicate(WhereRegionPredicate), @@ -489,6 +491,7 @@ pub struct Crate { /// E.g., `#[test]`, `#[derive(..)]`, `#[rustfmt::skip]` or `#[feature = "foo"]`. #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct MetaItem { + pub unsafety: Safety, pub path: Path, pub kind: MetaItemKind, pub span: Span, @@ -972,7 +975,9 @@ impl UnOp { } } -/// A statement +/// A statement. No `attrs` or `tokens` fields because each `StmtKind` variant +/// contains an AST node with those fields. (Except for `StmtKind::Empty`, +/// which never has attrs or tokens) #[derive(Clone, Encodable, Decodable, Debug)] pub struct Stmt { pub id: NodeId, @@ -2034,18 +2039,25 @@ impl UintTy { } } -/// A constraint on an associated type (e.g., `A = Bar` in `Foo` or -/// `A: TraitA + TraitB` in `Foo`). +/// A constraint on an associated item. +/// +/// ### Examples +/// +/// * the `A = Ty` and `B = Ty` in `Trait` +/// * the `G = Ty` in `Trait = Ty>` +/// * the `A: Bound` in `Trait` +/// * the `RetTy` in `Trait(ArgTy, ArgTy) -> RetTy` +/// * the `C = { Ct }` in `Trait` (feature `associated_const_equality`) +/// * the `f(): Bound` in `Trait` (feature `return_type_notation`) #[derive(Clone, Encodable, Decodable, Debug)] -pub struct AssocConstraint { +pub struct AssocItemConstraint { pub id: NodeId, pub ident: Ident, pub gen_args: Option, - pub kind: AssocConstraintKind, + pub kind: AssocItemConstraintKind, pub span: Span, } -/// The kinds of an `AssocConstraint`. #[derive(Clone, Encodable, Decodable, Debug)] pub enum Term { Ty(P), @@ -2064,12 +2076,17 @@ impl From for Term { } } -/// The kinds of an `AssocConstraint`. +/// The kind of [associated item constraint][AssocItemConstraint]. #[derive(Clone, Encodable, Decodable, Debug)] -pub enum AssocConstraintKind { - /// E.g., `A = Bar`, `A = 3` in `Foo` where A is an associated type. +pub enum AssocItemConstraintKind { + /// An equality constraint for an associated item (e.g., `AssocTy = Ty` in `Trait`). + /// + /// Also known as an *associated item binding* (we *bind* an associated item to a term). + /// + /// Furthermore, associated type equality constraints can also be referred to as *associated type + /// bindings*. Similarly with associated const equality constraints and *associated const bindings*. Equality { term: Term }, - /// E.g. `A: TraitA + TraitB` in `Foo`. + /// A bound on an associated type (e.g., `AssocTy: Bound` in `Trait`). Bound { bounds: GenericBounds }, } @@ -2150,7 +2167,7 @@ pub enum TyKind { /// The `NodeId` exists to prevent lowering from having to /// generate `NodeId`s on the fly, which would complicate /// the generation of opaque `type Foo = impl Trait` items significantly. - ImplTrait(NodeId, GenericBounds, Option, Span)>>), + ImplTrait(NodeId, GenericBounds), /// No-op; kept solely so that we can pretty-print faithfully. Paren(P), /// Unused for now. @@ -2490,6 +2507,8 @@ pub enum IsAuto { pub enum Safety { /// `unsafe` an item is explicitly marked as `unsafe`. Unsafe(Span), + /// `safe` an item is explicitly marked as `safe`. + Safe(Span), /// Default means no value was provided, it will take a default value given the context in /// which is used. Default, @@ -2733,6 +2752,13 @@ pub enum UseTreeKind { /// `use prefix` or `use prefix as rename` Simple(Option), /// `use prefix::{...}` + /// + /// The span represents the braces of the nested group and all elements within: + /// + /// ```text + /// use foo::{bar, baz}; + /// ^^^^^^^^^^ + /// ``` Nested { items: ThinVec<(UseTree, NodeId)>, span: Span }, /// `use prefix::*` Glob, @@ -2803,7 +2829,12 @@ pub struct NormalAttr { impl NormalAttr { pub fn from_ident(ident: Ident) -> Self { Self { - item: AttrItem { path: Path::from_ident(ident), args: AttrArgs::Empty, tokens: None }, + item: AttrItem { + unsafety: Safety::Default, + path: Path::from_ident(ident), + args: AttrArgs::Empty, + tokens: None, + }, tokens: None, } } @@ -2811,6 +2842,7 @@ impl NormalAttr { #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct AttrItem { + pub unsafety: Safety, pub path: Path, pub args: AttrArgs, // Tokens for the meta item, e.g. just the `foo` within `#[foo]` or `#![foo]`. @@ -3131,19 +3163,23 @@ pub struct Delegation { pub path: Path, pub rename: Option, pub body: Option>, + /// The item was expanded from a glob delegation item. + pub from_glob: bool, } #[derive(Clone, Encodable, Decodable, Debug)] pub struct DelegationMac { pub qself: Option>, pub prefix: Path, - pub suffixes: ThinVec<(Ident, Option)>, + // Some for list delegation, and None for glob delegation. + pub suffixes: Option)>>, pub body: Option>, } #[derive(Clone, Encodable, Decodable, Debug)] pub struct StaticItem { pub ty: P, + pub safety: Safety, pub mutability: Mutability, pub expr: Option>, } @@ -3153,6 +3189,7 @@ pub struct StaticItem { #[derive(Clone, Encodable, Decodable, Debug)] pub struct StaticForeignItem { pub ty: P, + pub safety: Safety, pub mutability: Mutability, pub expr: Option>, } @@ -3161,6 +3198,7 @@ impl From for StaticForeignItem { fn from(static_item: StaticItem) -> StaticForeignItem { StaticForeignItem { ty: static_item.ty, + safety: static_item.safety, mutability: static_item.mutability, expr: static_item.expr, } @@ -3171,6 +3209,7 @@ impl From for StaticItem { fn from(static_item: StaticForeignItem) -> StaticItem { StaticItem { ty: static_item.ty, + safety: static_item.safety, mutability: static_item.mutability, expr: static_item.expr, } @@ -3260,7 +3299,7 @@ pub enum ItemKind { /// /// E.g. `reuse ::name { target_expr_template }`. Delegation(Box), - /// A list delegation item (`reuse prefix::{a, b, c}`). + /// A list or glob delegation item (`reuse prefix::{a, b, c}`, `reuse prefix::*`). /// Treated similarly to a macro call and expanded early. DelegationMac(Box), } @@ -3341,7 +3380,7 @@ pub enum AssocItemKind { MacCall(P), /// An associated delegation item. Delegation(Box), - /// An associated delegation item list. + /// An associated list or glob delegation item. DelegationMac(Box), } diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 0684163617fe..676a2377c3b3 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -1,6 +1,8 @@ //! Functions dealing with attributes and meta items. -use crate::ast::{AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute}; +use crate::ast::{ + AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, Safety, +}; use crate::ast::{DelimArgs, Expr, ExprKind, LitKind, MetaItemLit}; use crate::ast::{MetaItem, MetaItemKind, NestedMetaItem, NormalAttr}; use crate::ast::{Path, PathSegment, DUMMY_NODE_ID}; @@ -13,6 +15,7 @@ use crate::util::literal::escape_string_symbol; use rustc_index::bit_set::GrowableBitSet; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; +use smallvec::{smallvec, SmallVec}; use std::iter; use std::sync::atomic::{AtomicU32, Ordering}; use thin_vec::{thin_vec, ThinVec}; @@ -87,10 +90,20 @@ impl Attribute { AttrKind::DocComment(..) => None, } } + pub fn name_or_empty(&self) -> Symbol { self.ident().unwrap_or_else(Ident::empty).name } + pub fn path(&self) -> SmallVec<[Symbol; 1]> { + match &self.kind { + AttrKind::Normal(normal) => { + normal.item.path.segments.iter().map(|s| s.ident.name).collect() + } + AttrKind::DocComment(..) => smallvec![sym::doc], + } + } + #[inline] pub fn has_name(&self, name: Symbol) -> bool { match &self.kind { @@ -227,7 +240,12 @@ impl AttrItem { } pub fn meta(&self, span: Span) -> Option { - Some(MetaItem { path: self.path.clone(), kind: self.meta_kind()?, span }) + Some(MetaItem { + unsafety: Safety::Default, + path: self.path.clone(), + kind: self.meta_kind()?, + span, + }) } pub fn meta_kind(&self) -> Option { @@ -360,7 +378,10 @@ impl MetaItem { _ => path.span.hi(), }; let span = path.span.with_hi(hi); - Some(MetaItem { path, kind, span }) + // FIXME: This parses `unsafe()` not as unsafe attribute syntax in `MetaItem`, + // but as a parenthesized list. This (and likely `MetaItem`) should be changed in + // such a way that builtin macros don't accept extraneous `unsafe()`. + Some(MetaItem { unsafety: Safety::Default, path, kind, span }) } } @@ -544,11 +565,12 @@ pub fn mk_doc_comment( pub fn mk_attr( g: &AttrIdGenerator, style: AttrStyle, + unsafety: Safety, path: Path, args: AttrArgs, span: Span, ) -> Attribute { - mk_attr_from_item(g, AttrItem { path, args, tokens: None }, None, style, span) + mk_attr_from_item(g, AttrItem { unsafety, path, args, tokens: None }, None, style, span) } pub fn mk_attr_from_item( @@ -566,15 +588,22 @@ pub fn mk_attr_from_item( } } -pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute { +pub fn mk_attr_word( + g: &AttrIdGenerator, + style: AttrStyle, + unsafety: Safety, + name: Symbol, + span: Span, +) -> Attribute { let path = Path::from_ident(Ident::new(name, span)); let args = AttrArgs::Empty; - mk_attr(g, style, path, args, span) + mk_attr(g, style, unsafety, path, args, span) } pub fn mk_attr_nested_word( g: &AttrIdGenerator, style: AttrStyle, + unsafety: Safety, outer: Symbol, inner: Symbol, span: Span, @@ -590,12 +619,13 @@ pub fn mk_attr_nested_word( delim: Delimiter::Parenthesis, tokens: inner_tokens, }); - mk_attr(g, style, path, attr_args, span) + mk_attr(g, style, unsafety, path, attr_args, span) } pub fn mk_attr_name_value_str( g: &AttrIdGenerator, style: AttrStyle, + unsafety: Safety, name: Symbol, val: Symbol, span: Span, @@ -610,7 +640,7 @@ pub fn mk_attr_name_value_str( }); let path = Path::from_ident(Ident::new(name, span)); let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr)); - mk_attr(g, style, path, args, span) + mk_attr(g, style, unsafety, path, args, span) } pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator { diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 63cde3c6809c..7ca950e50e61 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -4,20 +4,22 @@ //! //! This API is completely unstable and subject to change. +// tidy-alphabetical-start +#![allow(internal_features)] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(attr(deny(warnings))) )] #![doc(rust_logo)] -#![allow(internal_features)] -#![feature(rustdoc_internals)] #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(let_chains)] -#![feature(never_type)] #![feature(negative_impls)] +#![feature(never_type)] +#![feature(rustdoc_internals)] #![feature(stmt_expr_attributes)] +// tidy-alphabetical-end pub mod util { pub mod case; diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 566b20c490ef..35aa53e978c1 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -175,8 +175,8 @@ pub trait MutVisitor: Sized { noop_visit_lifetime(l, self); } - fn visit_constraint(&mut self, t: &mut AssocConstraint) { - noop_visit_constraint(t, self); + fn visit_assoc_item_constraint(&mut self, c: &mut AssocItemConstraint) { + noop_visit_assoc_item_constraint(c, self); } fn visit_foreign_mod(&mut self, nm: &mut ForeignMod) { @@ -463,8 +463,8 @@ pub fn noop_flat_map_arm(mut arm: Arm, vis: &mut T) -> SmallVec<[ smallvec![arm] } -fn noop_visit_constraint( - AssocConstraint { id, ident, gen_args, kind, span }: &mut AssocConstraint, +fn noop_visit_assoc_item_constraint( + AssocItemConstraint { id, ident, gen_args, kind, span }: &mut AssocItemConstraint, vis: &mut T, ) { vis.visit_id(id); @@ -473,11 +473,11 @@ fn noop_visit_constraint( vis.visit_generic_args(gen_args); } match kind { - AssocConstraintKind::Equality { term } => match term { + AssocItemConstraintKind::Equality { term } => match term { Term::Ty(ty) => vis.visit_ty(ty), Term::Const(c) => vis.visit_anon_const(c), }, - AssocConstraintKind::Bound { bounds } => visit_bounds(bounds, vis), + AssocItemConstraintKind::Bound { bounds } => visit_bounds(bounds, vis), } vis.visit_span(span); } @@ -523,14 +523,9 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { TyKind::TraitObject(bounds, _syntax) => { visit_vec(bounds, |bound| vis.visit_param_bound(bound)) } - TyKind::ImplTrait(id, bounds, precise_capturing) => { + TyKind::ImplTrait(id, bounds) => { vis.visit_id(id); visit_vec(bounds, |bound| vis.visit_param_bound(bound)); - if let Some((precise_capturing, _span)) = precise_capturing.as_deref_mut() { - for arg in precise_capturing { - vis.visit_precise_capturing_arg(arg); - } - } } TyKind::MacCall(mac) => vis.visit_mac_call(mac), TyKind::AnonStruct(id, fields) | TyKind::AnonUnion(id, fields) => { @@ -607,7 +602,7 @@ fn noop_visit_angle_bracketed_parameter_data( let AngleBracketedArgs { args, span } = data; visit_thin_vec(args, |arg| match arg { AngleBracketedArg::Arg(arg) => vis.visit_generic_arg(arg), - AngleBracketedArg::Constraint(constraint) => vis.visit_constraint(constraint), + AngleBracketedArg::Constraint(constraint) => vis.visit_assoc_item_constraint(constraint), }); vis.visit_span(span); } @@ -647,8 +642,10 @@ fn noop_visit_attribute(attr: &mut Attribute, vis: &mut T) { let Attribute { kind, id: _, style: _, span } = attr; match kind { AttrKind::Normal(normal) => { - let NormalAttr { item: AttrItem { path, args, tokens }, tokens: attr_tokens } = - &mut **normal; + let NormalAttr { + item: AttrItem { unsafety: _, path, args, tokens }, + tokens: attr_tokens, + } = &mut **normal; vis.visit_path(path); visit_attr_args(args, vis); visit_lazy_tts(tokens, vis); @@ -678,7 +675,7 @@ fn noop_visit_meta_list_item(li: &mut NestedMetaItem, vis: &mut T } fn noop_visit_meta_item(mi: &mut MetaItem, vis: &mut T) { - let MetaItem { path: _, kind, span } = mi; + let MetaItem { unsafety: _, path: _, kind, span } = mi; match kind { MetaItemKind::Word => {} MetaItemKind::List(mis) => visit_thin_vec(mis, |mi| vis.visit_meta_list_item(mi)), @@ -840,7 +837,7 @@ fn visit_nonterminal(nt: &mut token::Nonterminal, vis: &mut T) { token::NtTy(ty) => vis.visit_ty(ty), token::NtLiteral(expr) => vis.visit_expr(expr), token::NtMeta(item) => { - let AttrItem { path, args, tokens } = item.deref_mut(); + let AttrItem { unsafety: _, path, args, tokens } = item.deref_mut(); vis.visit_path(path); visit_attr_args(args, vis); visit_lazy_tts(tokens, vis); @@ -862,6 +859,7 @@ fn visit_defaultness(defaultness: &mut Defaultness, vis: &mut T) fn visit_safety(safety: &mut Safety, vis: &mut T) { match safety { Safety::Unsafe(span) => vis.visit_span(span), + Safety::Safe(span) => vis.visit_span(span), Safety::Default => {} } } @@ -920,6 +918,11 @@ fn noop_visit_param_bound(pb: &mut GenericBound, vis: &mut T) { match pb { GenericBound::Trait(ty, _modifier) => vis.visit_poly_trait_ref(ty), GenericBound::Outlives(lifetime) => noop_visit_lifetime(lifetime, vis), + GenericBound::Use(args, _) => { + for arg in args { + vis.visit_precise_capturing_arg(arg); + } + } } } @@ -1079,7 +1082,7 @@ impl NoopVisitItemKind for ItemKind { match self { ItemKind::ExternCrate(_orig_name) => {} ItemKind::Use(use_tree) => vis.visit_use_tree(use_tree), - ItemKind::Static(box StaticItem { ty, mutability: _, expr }) => { + ItemKind::Static(box StaticItem { ty, safety: _, mutability: _, expr }) => { vis.visit_ty(ty); visit_opt(expr, |expr| vis.visit_expr(expr)); } @@ -1159,7 +1162,14 @@ impl NoopVisitItemKind for ItemKind { } ItemKind::MacCall(m) => vis.visit_mac_call(m), ItemKind::MacroDef(def) => vis.visit_macro_def(def), - ItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => { + ItemKind::Delegation(box Delegation { + id, + qself, + path, + rename, + body, + from_glob: _, + }) => { vis.visit_id(id); vis.visit_qself(qself); vis.visit_path(path); @@ -1173,10 +1183,12 @@ impl NoopVisitItemKind for ItemKind { ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { vis.visit_qself(qself); vis.visit_path(prefix); - for (ident, rename) in suffixes { - vis.visit_ident(ident); - if let Some(rename) = rename { - vis.visit_ident(rename); + if let Some(suffixes) = suffixes { + for (ident, rename) in suffixes { + vis.visit_ident(ident); + if let Some(rename) = rename { + vis.visit_ident(rename); + } } } if let Some(body) = body { @@ -1215,7 +1227,14 @@ impl NoopVisitItemKind for AssocItemKind { visit_opt(ty, |ty| visitor.visit_ty(ty)); } AssocItemKind::MacCall(mac) => visitor.visit_mac_call(mac), - AssocItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => { + AssocItemKind::Delegation(box Delegation { + id, + qself, + path, + rename, + body, + from_glob: _, + }) => { visitor.visit_id(id); visitor.visit_qself(qself); visitor.visit_path(path); @@ -1229,10 +1248,12 @@ impl NoopVisitItemKind for AssocItemKind { AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => { visitor.visit_qself(qself); visitor.visit_path(prefix); - for (ident, rename) in suffixes { - visitor.visit_ident(ident); - if let Some(rename) = rename { - visitor.visit_ident(rename); + if let Some(suffixes) = suffixes { + for (ident, rename) in suffixes { + visitor.visit_ident(ident); + if let Some(rename) = rename { + visitor.visit_ident(rename); + } } } if let Some(body) = body { @@ -1289,7 +1310,12 @@ pub fn noop_flat_map_item( impl NoopVisitItemKind for ForeignItemKind { fn noop_visit(&mut self, visitor: &mut impl MutVisitor) { match self { - ForeignItemKind::Static(box StaticForeignItem { ty, mutability: _, expr }) => { + ForeignItemKind::Static(box StaticForeignItem { + ty, + mutability: _, + expr, + safety: _, + }) => { visitor.visit_ty(ty); visit_opt(expr, |expr| visitor.visit_expr(expr)); } diff --git a/compiler/rustc_ast/src/ptr.rs b/compiler/rustc_ast/src/ptr.rs index e22a523dbc3e..34c539ea16b4 100644 --- a/compiler/rustc_ast/src/ptr.rs +++ b/compiler/rustc_ast/src/ptr.rs @@ -184,7 +184,7 @@ impl<'a, T> IntoIterator for &'a P<[T]> { type Item = &'a T; type IntoIter = slice::Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { - self.ptr.into_iter() + self.ptr.iter() } } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 099a6096d0b5..109c401bb6a2 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -210,6 +210,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: IdentIsRaw) -> boo kw::Unsafe, kw::While, kw::Yield, + kw::Safe, kw::Static, ] .contains(&name) @@ -577,6 +578,7 @@ impl Token { kw::Impl, kw::Unsafe, kw::Const, + kw::Safe, kw::Static, kw::Union, kw::Macro, diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index fb666550e930..3d46415507de 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -661,11 +661,11 @@ impl TokenStream { if attr_style == AttrStyle::Inner { vec![ TokenTree::token_joint(token::Pound, span), - TokenTree::token_alone(token::Not, span), + TokenTree::token_joint_hidden(token::Not, span), body, ] } else { - vec![TokenTree::token_alone(token::Pound, span), body] + vec![TokenTree::token_joint_hidden(token::Pound, span), body] } } } diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index f6e9e1a87c4b..4b2544ac47ed 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -81,8 +81,17 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { } } +pub enum TrailingBrace<'a> { + /// Trailing brace in a macro call, like the one in `x as *const brace! {}`. + /// We will suggest changing the macro call to a different delimiter. + MacCall(&'a ast::MacCall), + /// Trailing brace in any other expression, such as `a + B {}`. We will + /// suggest wrapping the innermost expression in parentheses: `a + (B {})`. + Expr(&'a ast::Expr), +} + /// If an expression ends with `}`, returns the innermost expression ending in the `}` -pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { +pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { loop { match &expr.kind { AddrOf(_, _, e) @@ -111,10 +120,14 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { | Struct(..) | TryBlock(..) | While(..) - | ConstBlock(_) => break Some(expr), + | ConstBlock(_) => break Some(TrailingBrace::Expr(expr)), + + Cast(_, ty) => { + break type_trailing_braced_mac_call(ty).map(TrailingBrace::MacCall); + } MacCall(mac) => { - break (mac.args.delim == Delimiter::Brace).then_some(expr); + break (mac.args.delim == Delimiter::Brace).then_some(TrailingBrace::MacCall(mac)); } InlineAsm(_) | OffsetOf(_, _) | IncludedBytes(_) | FormatArgs(_) => { @@ -131,7 +144,6 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { | MethodCall(_) | Tup(_) | Lit(_) - | Cast(_, _) | Type(_, _) | Await(_, _) | Field(_, _) @@ -148,3 +160,80 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { } } } + +/// If the type's last token is `}`, it must be due to a braced macro call, such +/// as in `*const brace! { ... }`. Returns that trailing macro call. +fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { + loop { + match &ty.kind { + ast::TyKind::MacCall(mac) => { + break (mac.args.delim == Delimiter::Brace).then_some(mac); + } + + ast::TyKind::Ptr(mut_ty) | ast::TyKind::Ref(_, mut_ty) => { + ty = &mut_ty.ty; + } + + ast::TyKind::BareFn(fn_ty) => match &fn_ty.decl.output { + ast::FnRetTy::Default(_) => break None, + ast::FnRetTy::Ty(ret) => ty = ret, + }, + + ast::TyKind::Path(_, path) => match path_return_type(path) { + Some(trailing_ty) => ty = trailing_ty, + None => break None, + }, + + ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => { + match bounds.last() { + Some(ast::GenericBound::Trait(bound, _)) => { + match path_return_type(&bound.trait_ref.path) { + Some(trailing_ty) => ty = trailing_ty, + None => break None, + } + } + Some(ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..)) | None => { + break None; + } + } + } + + ast::TyKind::Slice(..) + | ast::TyKind::Array(..) + | ast::TyKind::Never + | ast::TyKind::Tup(..) + | ast::TyKind::Paren(..) + | ast::TyKind::Typeof(..) + | ast::TyKind::Infer + | ast::TyKind::ImplicitSelf + | ast::TyKind::CVarArgs + | ast::TyKind::Pat(..) + | ast::TyKind::Dummy + | ast::TyKind::Err(..) => break None, + + // These end in brace, but cannot occur in a let-else statement. + // They are only parsed as fields of a data structure. For the + // purpose of denying trailing braces in the expression of a + // let-else, we can disregard these. + ast::TyKind::AnonStruct(..) | ast::TyKind::AnonUnion(..) => break None, + } + } +} + +/// Returns the trailing return type in the given path, if it has one. +/// +/// ```ignore (illustrative) +/// ::std::ops::FnOnce(&str) -> fn() -> *const c_void +/// ^^^^^^^^^^^^^^^^^^^^^ +/// ``` +fn path_return_type(path: &ast::Path) -> Option<&ast::Ty> { + let last_segment = path.segments.last()?; + let args = last_segment.args.as_ref()?; + match &**args { + ast::GenericArgs::Parenthesized(args) => match &args.output { + ast::FnRetTy::Default(_) => None, + ast::FnRetTy::Ty(ret) => Some(ret), + }, + ast::GenericArgs::AngleBracketed(_) => None, + } +} diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 93de42b55cc3..ed34a44db677 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -52,6 +52,16 @@ pub enum BoundKind { /// E.g., `trait A: B` SuperTraits, } +impl BoundKind { + pub fn descr(self) -> &'static str { + match self { + BoundKind::Bound => "bounds", + BoundKind::Impl => "`impl Trait`", + BoundKind::TraitObject => "`dyn` trait object bounds", + BoundKind::SuperTraits => "supertrait bounds", + } + } +} #[derive(Copy, Clone, Debug)] pub enum FnKind<'a> { @@ -246,8 +256,11 @@ pub trait Visitor<'ast>: Sized { fn visit_generic_arg(&mut self, generic_arg: &'ast GenericArg) -> Self::Result { walk_generic_arg(self, generic_arg) } - fn visit_assoc_constraint(&mut self, constraint: &'ast AssocConstraint) -> Self::Result { - walk_assoc_constraint(self, constraint) + fn visit_assoc_item_constraint( + &mut self, + constraint: &'ast AssocItemConstraint, + ) -> Self::Result { + walk_assoc_item_constraint(self, constraint) } fn visit_attribute(&mut self, attr: &'ast Attribute) -> Self::Result { walk_attribute(self, attr) @@ -331,7 +344,7 @@ impl WalkItemKind for ItemKind { match self { ItemKind::ExternCrate(_) => {} ItemKind::Use(use_tree) => try_visit!(visitor.visit_use_tree(use_tree, item.id, false)), - ItemKind::Static(box StaticItem { ty, mutability: _, expr }) => { + ItemKind::Static(box StaticItem { ty, safety: _, mutability: _, expr }) => { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } @@ -395,7 +408,14 @@ impl WalkItemKind for ItemKind { } ItemKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)), ItemKind::MacroDef(ts) => try_visit!(visitor.visit_mac_def(ts, item.id)), - ItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => { + ItemKind::Delegation(box Delegation { + id, + qself, + path, + rename, + body, + from_glob: _, + }) => { if let Some(qself) = qself { try_visit!(visitor.visit_ty(&qself.ty)); } @@ -408,10 +428,12 @@ impl WalkItemKind for ItemKind { try_visit!(visitor.visit_ty(&qself.ty)); } try_visit!(visitor.visit_path(prefix, item.id)); - for (ident, rename) in suffixes { - visitor.visit_ident(*ident); - if let Some(rename) = rename { - visitor.visit_ident(*rename); + if let Some(suffixes) = suffixes { + for (ident, rename) in suffixes { + visitor.visit_ident(*ident); + if let Some(rename) = rename { + visitor.visit_ident(*rename); + } } } visit_opt!(visitor, visit_block, body); @@ -494,13 +516,8 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result { TyKind::TraitObject(bounds, ..) => { walk_list!(visitor, visit_param_bound, bounds, BoundKind::TraitObject); } - TyKind::ImplTrait(_, bounds, precise_capturing) => { + TyKind::ImplTrait(_, bounds) => { walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl); - if let Some((precise_capturing, _span)) = precise_capturing.as_deref() { - for arg in precise_capturing { - try_visit!(visitor.visit_precise_capturing_arg(arg)); - } - } } TyKind::Typeof(expression) => try_visit!(visitor.visit_anon_const(expression)), TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy | TyKind::Err(_) => {} @@ -558,7 +575,7 @@ where match arg { AngleBracketedArg::Arg(a) => try_visit!(visitor.visit_generic_arg(a)), AngleBracketedArg::Constraint(c) => { - try_visit!(visitor.visit_assoc_constraint(c)) + try_visit!(visitor.visit_assoc_item_constraint(c)) } } } @@ -582,18 +599,18 @@ where } } -pub fn walk_assoc_constraint<'a, V: Visitor<'a>>( +pub fn walk_assoc_item_constraint<'a, V: Visitor<'a>>( visitor: &mut V, - constraint: &'a AssocConstraint, + constraint: &'a AssocItemConstraint, ) -> V::Result { try_visit!(visitor.visit_ident(constraint.ident)); visit_opt!(visitor, visit_generic_args, &constraint.gen_args); match &constraint.kind { - AssocConstraintKind::Equality { term } => match term { + AssocItemConstraintKind::Equality { term } => match term { Term::Ty(ty) => try_visit!(visitor.visit_ty(ty)), Term::Const(c) => try_visit!(visitor.visit_anon_const(c)), }, - AssocConstraintKind::Bound { bounds } => { + AssocItemConstraintKind::Bound { bounds } => { walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); } } @@ -655,7 +672,12 @@ impl WalkItemKind for ForeignItemKind { ) -> V::Result { let &Item { id, span, ident, ref vis, .. } = item; match self { - ForeignItemKind::Static(box StaticForeignItem { ty, mutability: _, expr }) => { + ForeignItemKind::Static(box StaticForeignItem { + ty, + mutability: _, + expr, + safety: _, + }) => { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } @@ -680,6 +702,10 @@ pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericB match bound { GenericBound::Trait(typ, _modifier) => visitor.visit_poly_trait_ref(typ), GenericBound::Outlives(lifetime) => visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound), + GenericBound::Use(args, _) => { + walk_list!(visitor, visit_precise_capturing_arg, args); + V::Result::output() + } } } @@ -820,7 +846,14 @@ impl WalkItemKind for AssocItemKind { AssocItemKind::MacCall(mac) => { try_visit!(visitor.visit_mac_call(mac)); } - AssocItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => { + AssocItemKind::Delegation(box Delegation { + id, + qself, + path, + rename, + body, + from_glob: _, + }) => { if let Some(qself) = qself { try_visit!(visitor.visit_ty(&qself.ty)); } @@ -833,10 +866,12 @@ impl WalkItemKind for AssocItemKind { try_visit!(visitor.visit_ty(&qself.ty)); } try_visit!(visitor.visit_path(prefix, item.id)); - for (ident, rename) in suffixes { - visitor.visit_ident(*ident); - if let Some(rename) = rename { - visitor.visit_ident(*rename); + if let Some(suffixes) = suffixes { + for (ident, rename) in suffixes { + visitor.visit_ident(*ident); + if let Some(rename) = rename { + visitor.visit_ident(*rename); + } } } visit_opt!(visitor, visit_block, body); @@ -852,10 +887,10 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>( ctxt: AssocCtxt, ) -> V::Result { let &Item { id: _, span: _, ident, ref vis, ref attrs, ref kind, tokens: _ } = item; - walk_list!(visitor, visit_attribute, attrs); try_visit!(visitor.visit_vis(vis)); try_visit!(visitor.visit_ident(ident)); try_visit!(kind.walk(item, ctxt, visitor)); + walk_list!(visitor, visit_attribute, attrs); V::Result::output() } diff --git a/compiler/rustc_ast_ir/Cargo.toml b/compiler/rustc_ast_ir/Cargo.toml index e761b7adad3f..a78c91e0615b 100644 --- a/compiler/rustc_ast_ir/Cargo.toml +++ b/compiler/rustc_ast_ir/Cargo.toml @@ -9,7 +9,6 @@ rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_macros = { path = "../rustc_macros", optional = true } rustc_serialize = { path = "../rustc_serialize", optional = true } rustc_span = { path = "../rustc_span", optional = true } -smallvec = { version = "1.8.1" } # tidy-alphabetical-end [features] diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs index b1a77639b562..1d0c76f6ceae 100644 --- a/compiler/rustc_ast_ir/src/lib.rs +++ b/compiler/rustc_ast_ir/src/lib.rs @@ -1,6 +1,8 @@ +// tidy-alphabetical-start +#![cfg_attr(feature = "nightly", allow(internal_features))] #![cfg_attr(feature = "nightly", feature(never_type))] #![cfg_attr(feature = "nightly", feature(rustc_attrs))] -#![cfg_attr(feature = "nightly", allow(internal_features))] +// tidy-alphabetical-end #[cfg(feature = "nightly")] use rustc_macros::{Decodable, Encodable, HashStable_NoContext}; diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index 10efe6fba655..7d81e45d314d 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -128,7 +128,7 @@ ast_lowering_never_pattern_with_guard = a guard on a never pattern will never be run .suggestion = remove this guard -ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed on argument-position `impl Trait` +ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed in argument-position `impl Trait` ast_lowering_previously_used_here = previously used here diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index eef6e8280afb..e821a08bf181 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -76,7 +76,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { StmtKind::Empty => {} StmtKind::MacCall(..) => panic!("shouldn't exist here"), } - ast_stmts = &ast_stmts[1..]; + ast_stmts = tail; } (self.arena.alloc_from_iter(stmts), expr) } diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 27f8a6eae02a..d9dd0b3bca53 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -67,7 +67,9 @@ impl<'hir> LoweringContext<'_, 'hir> { return false; }; if let Some(local_sig_id) = sig_id.as_local() { - self.resolver.delegation_fn_sigs[&local_sig_id].has_self + // The value may be missing due to recursive delegation. + // Error will be emmited later during HIR ty lowering. + self.resolver.delegation_fn_sigs.get(&local_sig_id).map_or(false, |sig| sig.has_self) } else { match self.tcx.def_kind(sig_id) { DefKind::Fn => false, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index a55310909284..79cff0fbcd2e 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1716,24 +1716,28 @@ impl<'hir> LoweringContext<'_, 'hir> { // `mut iter => { ... }` let iter_arm = self.arm(iter_pat, loop_expr); - let into_iter_expr = match loop_kind { + let match_expr = match loop_kind { ForLoopKind::For => { // `::std::iter::IntoIterator::into_iter()` - self.expr_call_lang_item_fn( + let into_iter_expr = self.expr_call_lang_item_fn( head_span, hir::LangItem::IntoIterIntoIter, arena_vec![self; head], - ) - } - // ` unsafe { Pin::new_unchecked(&mut into_async_iter()) }` - ForLoopKind::ForAwait => { - // `::core::async_iter::IntoAsyncIterator::into_async_iter()` - let iter = self.expr_call_lang_item_fn( - head_span, - hir::LangItem::IntoAsyncIterIntoIter, - arena_vec![self; head], ); - let iter = self.expr_mut_addr_of(head_span, iter); + + self.arena.alloc(self.expr_match( + for_span, + into_iter_expr, + arena_vec![self; iter_arm], + hir::MatchSource::ForLoopDesugar, + )) + } + // `match into_async_iter() { ref mut iter => match unsafe { Pin::new_unchecked(iter) } { ... } }` + ForLoopKind::ForAwait => { + let iter_ident = iter; + let (async_iter_pat, async_iter_pat_id) = + self.pat_ident_binding_mode(head_span, iter_ident, hir::BindingMode::REF_MUT); + let iter = self.expr_ident_mut(head_span, iter_ident, async_iter_pat_id); // `Pin::new_unchecked(...)` let iter = self.arena.alloc(self.expr_call_lang_item_fn_mut( head_span, @@ -1742,17 +1746,29 @@ impl<'hir> LoweringContext<'_, 'hir> { )); // `unsafe { ... }` let iter = self.arena.alloc(self.expr_unsafe(iter)); - iter + let inner_match_expr = self.arena.alloc(self.expr_match( + for_span, + iter, + arena_vec![self; iter_arm], + hir::MatchSource::ForLoopDesugar, + )); + + // `::core::async_iter::IntoAsyncIterator::into_async_iter()` + let iter = self.expr_call_lang_item_fn( + head_span, + hir::LangItem::IntoAsyncIterIntoIter, + arena_vec![self; head], + ); + let iter_arm = self.arm(async_iter_pat, inner_match_expr); + self.arena.alloc(self.expr_match( + for_span, + iter, + arena_vec![self; iter_arm], + hir::MatchSource::ForLoopDesugar, + )) } }; - let match_expr = self.arena.alloc(self.expr_match( - for_span, - into_iter_expr, - arena_vec![self; iter_arm], - hir::MatchSource::ForLoopDesugar, - )); - // This is effectively `{ let _result = ...; _result }`. // The construct was introduced in #21984 and is necessary to make sure that // temporaries in the `head` expression are dropped and do not leak to the @@ -1805,6 +1821,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let attr = attr::mk_attr_nested_word( &self.tcx.sess.psess.attr_id_generator, AttrStyle::Outer, + Safety::Default, sym::allow, sym::unreachable_code, self.lower_span(span), diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 7254be2b2f42..44f37b5533a6 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -333,10 +333,10 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } - fn visit_assoc_type_binding(&mut self, type_binding: &'hir TypeBinding<'hir>) { - self.insert(type_binding.span, type_binding.hir_id, Node::TypeBinding(type_binding)); - self.with_parent(type_binding.hir_id, |this| { - intravisit::walk_assoc_type_binding(this, type_binding) + fn visit_assoc_item_constraint(&mut self, constraint: &'hir AssocItemConstraint<'hir>) { + self.insert(constraint.span, constraint.hir_id, Node::AssocItemConstraint(constraint)); + self.with_parent(constraint.hir_id, |this| { + intravisit::walk_assoc_item_constraint(this, constraint) }) } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index a15449409df3..c6c0d9a2e608 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -181,7 +181,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_use_tree(use_tree, &prefix, id, vis_span, ident, attrs) } - ItemKind::Static(box ast::StaticItem { ty: t, mutability: m, expr: e }) => { + ItemKind::Static(box ast::StaticItem { ty: t, safety: _, mutability: m, expr: e }) => { let (ty, body_id) = self.lower_const_item(t, span, e.as_deref(), ImplTraitPosition::StaticTy); hir::ItemKind::Static(ty, *m, body_id) @@ -388,7 +388,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(*s)), }; hir::ItemKind::Impl(self.arena.alloc(hir::Impl { - safety: self.lower_safety(*safety), + safety: self.lower_safety(*safety, hir::Safety::Safe), polarity, defaultness, defaultness_span, @@ -418,7 +418,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let items = this.arena.alloc_from_iter( items.iter().map(|item| this.lower_trait_item_ref(item)), ); - let safety = this.lower_safety(*safety); + let safety = this.lower_safety(*safety, hir::Safety::Safe); (safety, items, bounds) }, ); @@ -660,13 +660,21 @@ impl<'hir> LoweringContext<'_, 'hir> { this.lower_fn_params_to_names(fdec), ) }); + let safety = self.lower_safety(sig.header.safety, hir::Safety::Unsafe); - hir::ForeignItemKind::Fn(fn_dec, fn_args, generics) + hir::ForeignItemKind::Fn(fn_dec, fn_args, generics, safety) } - ForeignItemKind::Static(box StaticForeignItem { ty, mutability, expr: _ }) => { + ForeignItemKind::Static(box StaticForeignItem { + ty, + mutability, + expr: _, + safety, + }) => { let ty = self .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy)); - hir::ForeignItemKind::Static(ty, *mutability) + let safety = self.lower_safety(*safety, hir::Safety::Unsafe); + + hir::ForeignItemKind::Static(ty, *mutability, safety) } ForeignItemKind::TyAlias(..) => hir::ForeignItemKind::Type, ForeignItemKind::MacCall(_) => panic!("macro shouldn't exist here"), @@ -1311,6 +1319,14 @@ impl<'hir> LoweringContext<'_, 'hir> { CoroutineKind::AsyncGen { .. } => hir::CoroutineDesugaring::AsyncGen, }; let closure_id = coroutine_kind.closure_id(); + + let span = if let FnRetTy::Default(span) = decl.output + && matches!(coroutine_source, rustc_hir::CoroutineSource::Closure) + { + body_span.with_lo(span.lo()) + } else { + body_span + }; let coroutine_expr = self.make_desugared_coroutine_expr( // The default capture mode here is by-ref. Later on during upvar analysis, // we will force the captured arguments to by-move, but for async closures, @@ -1319,7 +1335,7 @@ impl<'hir> LoweringContext<'_, 'hir> { CaptureBy::Ref, closure_id, None, - body_span, + span, desugaring_kind, coroutine_source, mkbody, @@ -1360,7 +1376,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::IsAsync::NotAsync }; hir::FnHeader { - safety: self.lower_safety(h.safety), + safety: self.lower_safety(h.safety, hir::Safety::Safe), asyncness: asyncness, constness: self.lower_constness(h.constness), abi: self.lower_extern(h.ext), @@ -1410,10 +1426,11 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - pub(super) fn lower_safety(&mut self, s: Safety) -> hir::Safety { + pub(super) fn lower_safety(&mut self, s: Safety, default: hir::Safety) -> hir::Safety { match s { Safety::Unsafe(_) => hir::Safety::Unsafe, - Safety::Default => hir::Safety::Safe, + Safety::Default => default, + Safety::Safe(_) => hir::Safety::Safe, } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a9af5ad74592..da8682d3d095 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -30,12 +30,14 @@ //! get confused if the spans from leaf AST nodes occur in multiple places //! in the HIR, especially for multiple identifiers. +// tidy-alphabetical-start #![allow(internal_features)] -#![feature(rustdoc_internals)] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(let_chains)] +#![feature(rustdoc_internals)] +// tidy-alphabetical-end use crate::errors::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait}; use rustc_ast::node_id::NodeMap; @@ -48,10 +50,10 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; -use rustc_errors::{DiagArgFromDisplay, DiagCtxt, StashKey}; -use rustc_hir as hir; +use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey}; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{LocalDefId, LocalDefIdMap, CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::{self as hir}; use rustc_hir::{ ConstArg, GenericArg, HirId, ItemLocalMap, MissingLifetimeKind, ParamName, TraitCandidate, }; @@ -186,7 +188,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - pub(crate) fn dcx(&self) -> &'hir DiagCtxt { + pub(crate) fn dcx(&self) -> DiagCtxtHandle<'hir> { self.tcx.dcx() } } @@ -905,6 +907,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let kind = match attr.kind { AttrKind::Normal(ref normal) => AttrKind::Normal(P(NormalAttr { item: AttrItem { + unsafety: normal.item.unsafety, path: normal.item.path.clone(), args: self.lower_attr_args(&normal.item.args), tokens: None, @@ -961,24 +964,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { DelimArgs { dspan: args.dspan, delim: args.delim, tokens: args.tokens.flattened() } } - /// Given an associated type constraint like one of these: - /// - /// ```ignore (illustrative) - /// T: Iterator - /// ^^^^^^^^^^^ - /// T: Iterator - /// ^^^^^^^^^^^^ - /// ``` - /// - /// returns a `hir::TypeBinding` representing `Item`. - #[instrument(level = "debug", skip(self))] - fn lower_assoc_ty_constraint( + /// Lower an associated item constraint. + #[instrument(level = "debug", skip_all)] + fn lower_assoc_item_constraint( &mut self, - constraint: &AssocConstraint, + constraint: &AssocItemConstraint, itctx: ImplTraitContext, - ) -> hir::TypeBinding<'hir> { - debug!("lower_assoc_ty_constraint(constraint={:?}, itctx={:?})", constraint, itctx); - // lower generic arguments of identifier in constraint + ) -> hir::AssocItemConstraint<'hir> { + debug!(?constraint, ?itctx); + // Lower the generic arguments for the associated item. let gen_args = if let Some(gen_args) = &constraint.gen_args { let gen_args_ctor = match gen_args { GenericArgs::AngleBracketed(data) => { @@ -994,7 +988,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; GenericArgsCtor { args: Default::default(), - bindings: &[], + constraints: &[], parenthesized, span: data.inputs_span, } @@ -1024,7 +1018,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { err.emit(); GenericArgsCtor { args: Default::default(), - bindings: &[], + constraints: &[], parenthesized: hir::GenericArgsParentheses::ReturnTypeNotation, span: data.span, } @@ -1046,14 +1040,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.arena.alloc(hir::GenericArgs::none()) }; let kind = match &constraint.kind { - AssocConstraintKind::Equality { term } => { + AssocItemConstraintKind::Equality { term } => { let term = match term { Term::Ty(ty) => self.lower_ty(ty, itctx).into(), Term::Const(c) => self.lower_anon_const(c).into(), }; - hir::TypeBindingKind::Equality { term } + hir::AssocItemConstraintKind::Equality { term } } - AssocConstraintKind::Bound { bounds } => { + AssocItemConstraintKind::Bound { bounds } => { // Disallow ATB in dyn types if self.is_in_dyn_type { let suggestion = match itctx { @@ -1077,18 +1071,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }); let err_ty = &*self.arena.alloc(self.ty(constraint.span, hir::TyKind::Err(guar))); - hir::TypeBindingKind::Equality { term: err_ty.into() } + hir::AssocItemConstraintKind::Equality { term: err_ty.into() } } else { - // Desugar `AssocTy: Bounds` into a type binding where the + // Desugar `AssocTy: Bounds` into an assoc type binding where the // later desugars into a trait predicate. let bounds = self.lower_param_bounds(bounds, itctx); - hir::TypeBindingKind::Constraint { bounds } + hir::AssocItemConstraintKind::Bound { bounds } } } }; - hir::TypeBinding { + hir::AssocItemConstraint { hir_id: self.lower_node_id(constraint.id), ident: self.lower_ident(constraint.ident), gen_args, @@ -1324,7 +1318,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params); hir::TyKind::BareFn(self.arena.alloc(hir::BareFnTy { generic_params, - safety: self.lower_safety(f.safety), + safety: self.lower_safety(f.safety, hir::Safety::Safe), abi: self.lower_extern(f.ext), decl: self.lower_fn_decl(&f.decl, t.id, t.span, FnDeclKind::Pointer, None), param_names: self.lower_fn_params_to_names(&f.decl), @@ -1390,6 +1384,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } None } + // Ignore `use` syntax since that is not valid in objects. + GenericBound::Use(_, span) => { + this.dcx() + .span_delayed_bug(*span, "use<> not allowed in dyn types"); + None + } })); let lifetime_bound = lifetime_bound.unwrap_or_else(|| this.elided_dyn_bound(t.span)); @@ -1397,7 +1397,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }); hir::TyKind::TraitObject(bounds, lifetime_bound, *kind) } - TyKind::ImplTrait(def_node_id, bounds, precise_capturing) => { + TyKind::ImplTrait(def_node_id, bounds) => { let span = t.span; match itctx { ImplTraitContext::OpaqueTy { origin, fn_kind } => self.lower_opaque_impl_trait( @@ -1407,12 +1407,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { bounds, fn_kind, itctx, - precise_capturing.as_deref().map(|(args, span)| (args.as_slice(), *span)), ), ImplTraitContext::Universal => { - if let Some(&(_, span)) = precise_capturing.as_deref() { + if let Some(span) = bounds.iter().find_map(|bound| match *bound { + ast::GenericBound::Use(_, span) => Some(span), + _ => None, + }) { self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnApit { span }); - }; + } + let span = t.span; // HACK: pprust breaks strings with newlines when the type @@ -1523,7 +1526,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { bounds: &GenericBounds, fn_kind: Option, itctx: ImplTraitContext, - precise_capturing_args: Option<(&[PreciseCapturingArg], Span)>, ) -> hir::TyKind<'hir> { // Make sure we know that some funky desugaring has been going on here. // This is a first: there is code in other places like for loop @@ -1532,59 +1534,64 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // frequently opened issues show. let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None); - let captured_lifetimes_to_duplicate = - if let Some((precise_capturing, _)) = precise_capturing_args { - // We'll actually validate these later on; all we need is the list of - // lifetimes to duplicate during this portion of lowering. - precise_capturing - .iter() - .filter_map(|arg| match arg { - PreciseCapturingArg::Lifetime(lt) => Some(*lt), - PreciseCapturingArg::Arg(..) => None, - }) - // Add in all the lifetimes mentioned in the bounds. We will error - // them out later, but capturing them here is important to make sure - // they actually get resolved in resolve_bound_vars. - .chain(lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)) - .collect() - } else { - match origin { - hir::OpaqueTyOrigin::TyAlias { .. } => { - // type alias impl trait and associated type position impl trait were - // decided to capture all in-scope lifetimes, which we collect for - // all opaques during resolution. + let captured_lifetimes_to_duplicate = if let Some(args) = + // We only look for one `use<...>` syntax since we syntactially reject more than one. + bounds.iter().find_map( + |bound| match bound { + ast::GenericBound::Use(a, _) => Some(a), + _ => None, + }, + ) { + // We'll actually validate these later on; all we need is the list of + // lifetimes to duplicate during this portion of lowering. + args.iter() + .filter_map(|arg| match arg { + PreciseCapturingArg::Lifetime(lt) => Some(*lt), + PreciseCapturingArg::Arg(..) => None, + }) + // Add in all the lifetimes mentioned in the bounds. We will error + // them out later, but capturing them here is important to make sure + // they actually get resolved in resolve_bound_vars. + .chain(lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)) + .collect() + } else { + match origin { + hir::OpaqueTyOrigin::TyAlias { .. } => { + // type alias impl trait and associated type position impl trait were + // decided to capture all in-scope lifetimes, which we collect for + // all opaques during resolution. + self.resolver + .take_extra_lifetime_params(opaque_ty_node_id) + .into_iter() + .map(|(ident, id, _)| Lifetime { id, ident }) + .collect() + } + hir::OpaqueTyOrigin::FnReturn(..) => { + if matches!( + fn_kind.expect("expected RPITs to be lowered with a FnKind"), + FnDeclKind::Impl | FnDeclKind::Trait + ) || self.tcx.features().lifetime_capture_rules_2024 + || span.at_least_rust_2024() + { + // return-position impl trait in trait was decided to capture all + // in-scope lifetimes, which we collect for all opaques during resolution. self.resolver .take_extra_lifetime_params(opaque_ty_node_id) .into_iter() .map(|(ident, id, _)| Lifetime { id, ident }) .collect() - } - hir::OpaqueTyOrigin::FnReturn(..) => { - if matches!( - fn_kind.expect("expected RPITs to be lowered with a FnKind"), - FnDeclKind::Impl | FnDeclKind::Trait - ) || self.tcx.features().lifetime_capture_rules_2024 - || span.at_least_rust_2024() - { - // return-position impl trait in trait was decided to capture all - // in-scope lifetimes, which we collect for all opaques during resolution. - self.resolver - .take_extra_lifetime_params(opaque_ty_node_id) - .into_iter() - .map(|(ident, id, _)| Lifetime { id, ident }) - .collect() - } else { - // in fn return position, like the `fn test<'a>() -> impl Debug + 'a` - // example, we only need to duplicate lifetimes that appear in the - // bounds, since those are the only ones that are captured by the opaque. - lifetime_collector::lifetimes_in_bounds(self.resolver, bounds) - } - } - hir::OpaqueTyOrigin::AsyncFn(..) => { - unreachable!("should be using `lower_async_fn_ret_ty`") + } else { + // in fn return position, like the `fn test<'a>() -> impl Debug + 'a` + // example, we only need to duplicate lifetimes that appear in the + // bounds, since those are the only ones that are captured by the opaque. + lifetime_collector::lifetimes_in_bounds(self.resolver, bounds) } } - }; + hir::OpaqueTyOrigin::AsyncFn(..) => { + unreachable!("should be using `lower_async_fn_ret_ty`") + } + } + }; debug!(?captured_lifetimes_to_duplicate); self.lower_opaque_inner( @@ -1594,7 +1601,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { captured_lifetimes_to_duplicate, span, opaque_ty_span, - precise_capturing_args, |this| this.lower_param_bounds(bounds, itctx), ) } @@ -1607,7 +1613,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { captured_lifetimes_to_duplicate: FxIndexSet, span: Span, opaque_ty_span: Span, - precise_capturing_args: Option<(&[PreciseCapturingArg], Span)>, lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>], ) -> hir::TyKind<'hir> { let opaque_ty_def_id = self.create_def( @@ -1694,18 +1699,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Install the remapping from old to new (if any). This makes sure that // any lifetimes that would have resolved to the def-id of captured // lifetimes are remapped to the new *synthetic* lifetimes of the opaque. - let (bounds, precise_capturing_args) = - this.with_remapping(captured_to_synthesized_mapping, |this| { - ( - lower_item_bounds(this), - precise_capturing_args.map(|(precise_capturing, span)| { - ( - this.lower_precise_capturing_args(precise_capturing), - this.lower_span(span), - ) - }), - ) - }); + let bounds = this + .with_remapping(captured_to_synthesized_mapping, |this| lower_item_bounds(this)); let generic_params = this.arena.alloc_from_iter(synthesized_lifetime_definitions.iter().map( @@ -1750,7 +1745,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { origin, lifetime_mapping, in_trait, - precise_capturing_args, }; // Generate an `type Foo = impl Trait;` declaration. @@ -1961,7 +1955,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { captured_lifetimes, span, opaque_ty_span, - None, |this| { let bound = this.lower_coroutine_fn_output_type_to_bound( output, @@ -2008,7 +2001,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let bound_args = self.arena.alloc(hir::GenericArgs { args: &[], - bindings: arena_vec![self; self.assoc_ty_binding(assoc_ty_name, opaque_ty_span, output_ty)], + constraints: arena_vec![self; self.assoc_ty_binding(assoc_ty_name, opaque_ty_span, output_ty)], parenthesized: hir::GenericArgsParentheses::No, span_ext: DUMMY_SP, }); @@ -2044,6 +2037,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { GenericBound::Outlives(lifetime) => { hir::GenericBound::Outlives(self.lower_lifetime(lifetime)) } + GenericBound::Use(args, span) => hir::GenericBound::Use( + self.lower_precise_capturing_args(args), + self.lower_span(*span), + ), } } @@ -2581,10 +2578,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } -/// Helper struct for delayed construction of GenericArgs. +/// Helper struct for the delayed construction of [`hir::GenericArgs`]. struct GenericArgsCtor<'hir> { args: SmallVec<[hir::GenericArg<'hir>; 4]>, - bindings: &'hir [hir::TypeBinding<'hir>], + constraints: &'hir [hir::AssocItemConstraint<'hir>], parenthesized: hir::GenericArgsParentheses, span: Span, } @@ -2664,14 +2661,14 @@ impl<'hir> GenericArgsCtor<'hir> { fn is_empty(&self) -> bool { self.args.is_empty() - && self.bindings.is_empty() + && self.constraints.is_empty() && self.parenthesized == hir::GenericArgsParentheses::No } fn into_generic_args(self, this: &LoweringContext<'_, 'hir>) -> &'hir hir::GenericArgs<'hir> { let ga = hir::GenericArgs { args: this.arena.alloc_from_iter(self.args), - bindings: self.bindings, + constraints: self.constraints, parenthesized: self.parenthesized, span_ext: this.lower_span(self.span), }; diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 7679424dceb9..9d38e1e67847 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -281,7 +281,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ( GenericArgsCtor { args: Default::default(), - bindings: &[], + constraints: &[], parenthesized: hir::GenericArgsParentheses::No, span: path_span.shrink_to_hi(), }, @@ -390,13 +390,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { AngleBracketedArg::Constraint(_) => None, }) .collect(); - let bindings = self.arena.alloc_from_iter(data.args.iter().filter_map(|arg| match arg { - AngleBracketedArg::Constraint(c) => Some(self.lower_assoc_ty_constraint(c, itctx)), - AngleBracketedArg::Arg(_) => None, - })); + let constraints = + self.arena.alloc_from_iter(data.args.iter().filter_map(|arg| match arg { + AngleBracketedArg::Constraint(c) => { + Some(self.lower_assoc_item_constraint(c, itctx)) + } + AngleBracketedArg::Arg(_) => None, + })); let ctor = GenericArgsCtor { args, - bindings, + constraints, parenthesized: hir::GenericArgsParentheses::No, span: data.span, }; @@ -454,12 +457,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { Some(bound_modifier_allowed_features), ); } - let binding = self.assoc_ty_binding(sym::Output, output_span, output_ty); + let constraint = self.assoc_ty_binding(sym::Output, output_span, output_ty); ( GenericArgsCtor { args, - bindings: arena_vec![self; binding], + constraints: arena_vec![self; constraint], parenthesized: hir::GenericArgsParentheses::ParenSugar, span: data.inputs_span, }, @@ -467,24 +470,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) } - /// An associated type binding `$assoc_ty_name = $ty`. + /// An associated type binding (i.e., associated type equality constraint). pub(crate) fn assoc_ty_binding( &mut self, assoc_ty_name: rustc_span::Symbol, span: Span, ty: &'hir hir::Ty<'hir>, - ) -> hir::TypeBinding<'hir> { + ) -> hir::AssocItemConstraint<'hir> { let ident = Ident::with_dummy_span(assoc_ty_name); - let kind = hir::TypeBindingKind::Equality { term: ty.into() }; + let kind = hir::AssocItemConstraintKind::Equality { term: ty.into() }; let args = arena_vec![self;]; - let bindings = arena_vec![self;]; + let constraints = arena_vec![self;]; let gen_args = self.arena.alloc(hir::GenericArgs { args, - bindings, + constraints, parenthesized: hir::GenericArgsParentheses::No, span_ext: DUMMY_SP, }); - hir::TypeBinding { + hir::AssocItemConstraint { hir_id: self.next_id(), gen_args, span: self.lower_span(span), diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index a3731e94276b..2626631d8004 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -55,9 +55,6 @@ ast_passes_const_without_body = ast_passes_constraint_on_negative_bound = associated type constraints not allowed on negative bounds -ast_passes_deprecated_where_clause_location = - where clause not allowed here - ast_passes_equality_in_where = equality constraints are not yet supported in `where` clauses .label = not supported .suggestion = if `{$ident}` is an associated type you're trying to set, use the associated type binding syntax @@ -70,6 +67,9 @@ ast_passes_extern_fn_qualifiers = functions in `extern` blocks cannot have quali .label = in this `extern` block .suggestion = remove this qualifier +ast_passes_extern_invalid_safety = items in unadorned `extern` blocks cannot have safety qualifiers + .suggestion = add unsafe to this `extern` block + ast_passes_extern_item_ascii = items in `extern` blocks cannot use non-ascii identifiers .label = in this `extern` block .note = this limitation may be lifted in the future; see issue #83942 for more information @@ -80,8 +80,6 @@ ast_passes_extern_types_cannot = `type`s inside `extern` blocks cannot have {$de .suggestion = remove the {$remove_descr} .label = `extern` block begins here -ast_passes_extern_without_abi = extern declarations without an explicit ABI are deprecated - ast_passes_feature_on_non_nightly = `#![feature]` may not be used on the {$channel} release channel .suggestion = remove the attribute .stable_since = the feature `{$name}` has been stable since `{$since}` and no longer requires an attribute to enable @@ -97,9 +95,6 @@ ast_passes_fn_body_extern = incorrect function inside `extern` block ast_passes_fn_param_c_var_args_not_last = `...` must be the last argument of a C-variadic function -ast_passes_fn_param_c_var_args_only = - C-variadic function must be declared with at least one named argument - ast_passes_fn_param_doc_comment = documentation comments cannot be applied to function parameters .label = doc comments are not allowed here @@ -182,6 +177,8 @@ ast_passes_match_arm_with_no_body = `match` arm with no body .suggestion = add a body after the pattern +ast_passes_missing_unsafe_on_extern = extern blocks must be unsafe + ast_passes_module_nonascii = trying to load file for module `{$name}` with non-ascii identifier name .help = consider using the `#[path]` attribute to specify filesystem path @@ -218,6 +215,11 @@ ast_passes_pattern_in_fn_pointer = patterns aren't allowed in function pointer t ast_passes_pattern_in_foreign = patterns aren't allowed in foreign function declarations .label = pattern not allowed in foreign function +ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing syntax + .label = second `use<...>` here + +ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc} + ast_passes_show_span = {$msg} ast_passes_stability_outside_std = stability attributes may not be used outside of the standard library diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 9d07683f8d66..b274a9b9114a 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -12,10 +12,12 @@ use rustc_ast::visit::{walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor} use rustc_ast::*; use rustc_ast_pretty::pprust::{self, State}; use rustc_data_structures::fx::FxIndexMap; +use rustc_errors::DiagCtxtHandle; use rustc_feature::Features; use rustc_parse::validate_attr; use rustc_session::lint::builtin::{ - DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, PATTERNS_IN_FNS_WITHOUT_BODY, + DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN, + PATTERNS_IN_FNS_WITHOUT_BODY, }; use rustc_session::lint::{BuiltinLintDiag, LintBuffer}; use rustc_session::Session; @@ -27,7 +29,6 @@ use std::ops::{Deref, DerefMut}; use thin_vec::thin_vec; use crate::errors; -use crate::fluent_generated as fluent; /// Is `self` allowed semantically as the first parameter in an `FnDecl`? enum SelfSemantic { @@ -87,6 +88,9 @@ struct AstValidator<'a> { /// or `Foo::Bar` is_impl_trait_banned: bool, + /// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe. + extern_mod_safety: Option, + lint_buffer: &'a mut LintBuffer, } @@ -117,6 +121,12 @@ impl<'a> AstValidator<'a> { self.outer_trait_or_trait_impl = old; } + fn with_in_extern_mod(&mut self, extern_mod_safety: Safety, f: impl FnOnce(&mut Self)) { + let old = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety)); + f(self); + self.extern_mod_safety = old; + } + fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) { let old = mem::replace(&mut self.is_impl_trait_banned, true); f(self); @@ -184,8 +194,24 @@ impl<'a> AstValidator<'a> { // Mirrors `visit::walk_ty`, but tracks relevant state. fn walk_ty(&mut self, t: &'a Ty) { match &t.kind { - TyKind::ImplTrait(..) => { - self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t)) + TyKind::ImplTrait(_, bounds) => { + self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t)); + + // FIXME(precise_capturing): If we were to allow `use` in other positions + // (e.g. GATs), then we must validate those as well. However, we don't have + // a good way of doing this with the current `Visitor` structure. + let mut use_bounds = bounds + .iter() + .filter_map(|bound| match bound { + GenericBound::Use(_, span) => Some(span), + _ => None, + }) + .copied(); + if let Some(bound1) = use_bounds.next() + && let Some(bound2) = use_bounds.next() + { + self.dcx().emit_err(errors::DuplicatePreciseCapturing { bound1, bound2 }); + } } TyKind::TraitObject(..) => self .with_tilde_const(Some(DisallowTildeConstContext::TraitObject), |this| { @@ -244,7 +270,7 @@ impl<'a> AstValidator<'a> { } } - fn dcx(&self) -> &rustc_errors::DiagCtxt { + fn dcx(&self) -> DiagCtxtHandle<'a> { self.session.dcx() } @@ -365,7 +391,7 @@ impl<'a> AstValidator<'a> { fn check_fn_decl(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) { self.check_decl_num_args(fn_decl); - self.check_decl_cvaradic_pos(fn_decl); + self.check_decl_cvariadic_pos(fn_decl); self.check_decl_attrs(fn_decl); self.check_decl_self_param(fn_decl, self_semantic); } @@ -380,13 +406,11 @@ impl<'a> AstValidator<'a> { } } - fn check_decl_cvaradic_pos(&self, fn_decl: &FnDecl) { + /// Emits an error if a function declaration has a variadic parameter in the + /// beginning or middle of parameter list. + /// Example: `fn foo(..., x: i32)` will emit an error. + fn check_decl_cvariadic_pos(&self, fn_decl: &FnDecl) { match &*fn_decl.inputs { - [Param { ty, span, .. }] => { - if let TyKind::CVarArgs = ty.kind { - self.dcx().emit_err(errors::FnParamCVarArgsOnly { span: *span }); - } - } [ps @ .., _] => { for Param { ty, span, .. } in ps { if let TyKind::CVarArgs = ty.kind { @@ -432,6 +456,18 @@ impl<'a> AstValidator<'a> { } } + fn check_foreign_item_safety(&self, item_span: Span, safety: Safety) { + if matches!(safety, Safety::Unsafe(_) | Safety::Safe(_)) + && (self.extern_mod_safety == Some(Safety::Default) + || !self.features.unsafe_extern_blocks) + { + self.dcx().emit_err(errors::InvalidSafetyOnExtern { + item_span, + block: self.current_extern_span(), + }); + } + } + fn check_defaultness(&self, span: Span, defaultness: Defaultness) { if let Defaultness::Default(def_span) = defaultness { let span = self.session.source_map().guess_head_span(span); @@ -521,7 +557,7 @@ impl<'a> AstValidator<'a> { fn check_foreign_fn_headerless( &self, // Deconstruct to ensure exhaustiveness - FnHeader { safety, coroutine_kind, constness, ext }: FnHeader, + FnHeader { safety: _, coroutine_kind, constness, ext }: FnHeader, ) { let report_err = |span| { self.dcx().emit_err(errors::FnQualifierInExtern { @@ -529,10 +565,6 @@ impl<'a> AstValidator<'a> { block: self.current_extern_span(), }); }; - match safety { - Safety::Unsafe(span) => report_err(span), - Safety::Default => (), - } match coroutine_kind { Some(knd) => report_err(knd.span()), None => (), @@ -674,7 +706,7 @@ impl<'a> AstValidator<'a> { let constraint_sugg = data.args.iter().filter_map(|a| match a { AngleBracketedArg::Arg(_) => None, AngleBracketedArg::Constraint(c) => { - Some(pprust::to_string(|s| s.print_assoc_constraint(c))) + Some(pprust::to_string(|s| s.print_assoc_item_constraint(c))) } }); format!( @@ -736,7 +768,7 @@ impl<'a> AstValidator<'a> { } } } - TyKind::ImplTrait(_, bounds, _) => { + TyKind::ImplTrait(_, bounds) => { if self.is_impl_trait_banned { self.dcx().emit_err(errors::ImplTraitPath { span: ty.span }); } @@ -766,11 +798,10 @@ impl<'a> AstValidator<'a> { .span_to_snippet(span) .is_ok_and(|snippet| !snippet.starts_with("#[")) { - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( MISSING_ABI, id, span, - fluent::ast_passes_extern_without_abi, BuiltinLintDiag::MissingAbi(span, abi::Abi::FALLBACK), ) } @@ -779,11 +810,7 @@ impl<'a> AstValidator<'a> { /// Checks that generic parameters are in the correct order, /// which is lifetimes, then types and then consts. (`<'a, T, const N: usize>`) -fn validate_generic_param_order( - dcx: &rustc_errors::DiagCtxt, - generics: &[GenericParam], - span: Span, -) { +fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericParam], span: Span) { let mut max_param: Option = None; let mut out_of_order = FxIndexMap::default(); let mut param_idents = Vec::with_capacity(generics.len()); @@ -1021,19 +1048,39 @@ impl<'a> Visitor<'a> for AstValidator<'a> { return; // Avoid visiting again. } ItemKind::ForeignMod(ForeignMod { abi, safety, .. }) => { - let old_item = mem::replace(&mut self.extern_mod, Some(item)); - self.visibility_not_permitted( - &item.vis, - errors::VisibilityNotPermittedNote::IndividualForeignItems, - ); - if let &Safety::Unsafe(span) = safety { - self.dcx().emit_err(errors::UnsafeItem { span, kind: "extern block" }); - } - if abi.is_none() { - self.maybe_lint_missing_abi(item.span, item.id); - } - visit::walk_item(self, item); - self.extern_mod = old_item; + self.with_in_extern_mod(*safety, |this| { + let old_item = mem::replace(&mut this.extern_mod, Some(item)); + this.visibility_not_permitted( + &item.vis, + errors::VisibilityNotPermittedNote::IndividualForeignItems, + ); + + if this.features.unsafe_extern_blocks { + if &Safety::Default == safety { + if item.span.at_least_rust_2024() { + this.dcx() + .emit_err(errors::MissingUnsafeOnExtern { span: item.span }); + } else { + this.lint_buffer.buffer_lint( + MISSING_UNSAFE_ON_EXTERN, + item.id, + item.span, + BuiltinLintDiag::MissingUnsafeOnExtern { + suggestion: item.span.shrink_to_lo(), + }, + ); + } + } + } else if let &Safety::Unsafe(span) = safety { + this.dcx().emit_err(errors::UnsafeItem { span, kind: "extern block" }); + } + + if abi.is_none() { + this.maybe_lint_missing_abi(item.span, item.id); + } + visit::walk_item(this, item); + this.extern_mod = old_item; + }); return; // Avoid visiting again. } ItemKind::Enum(def, _) => { @@ -1165,6 +1212,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { match &fi.kind { ForeignItemKind::Fn(box Fn { defaultness, sig, body, .. }) => { + self.check_foreign_item_safety(fi.span, sig.header.safety); self.check_defaultness(fi.span, *defaultness); self.check_foreign_fn_bodyless(fi.ident, body.as_deref()); self.check_foreign_fn_headerless(sig.header); @@ -1184,7 +1232,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_foreign_ty_genericless(generics, where_clauses); self.check_foreign_item_ascii_only(fi.ident); } - ForeignItemKind::Static(box StaticForeignItem { ty: _, mutability: _, expr }) => { + ForeignItemKind::Static(box StaticForeignItem { expr, safety, .. }) => { + self.check_foreign_item_safety(fi.span, *safety); self.check_foreign_kind_bodyless(fi.ident, "static", expr.as_ref().map(|b| b.span)); self.check_foreign_item_ascii_only(fi.ident); } @@ -1203,11 +1252,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> { for arg in &data.args { match arg { AngleBracketedArg::Arg(arg) => self.visit_generic_arg(arg), - // Type bindings such as `Item = impl Debug` in `Iterator` - // are allowed to contain nested `impl Trait`. + // Associated type bindings such as `Item = impl Debug` in + // `Iterator` are allowed to contain nested `impl Trait`. AngleBracketedArg::Constraint(constraint) => { self.with_impl_trait(None, |this| { - this.visit_assoc_constraint(constraint); + this.visit_assoc_item_constraint(constraint); }); } } @@ -1268,6 +1317,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } GenericBound::Outlives(_) => {} + GenericBound::Use(..) => {} } } } @@ -1286,95 +1336,110 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } fn visit_param_bound(&mut self, bound: &'a GenericBound, ctxt: BoundKind) { - if let GenericBound::Trait(poly, modifiers) = bound { - match (ctxt, modifiers.constness, modifiers.polarity) { - (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) => { - self.dcx().emit_err(errors::OptionalTraitSupertrait { - span: poly.span, - path_str: pprust::path_to_string(&poly.trait_ref.path), - }); + match bound { + GenericBound::Trait(trait_ref, modifiers) => { + match (ctxt, modifiers.constness, modifiers.polarity) { + (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) => { + self.dcx().emit_err(errors::OptionalTraitSupertrait { + span: trait_ref.span, + path_str: pprust::path_to_string(&trait_ref.trait_ref.path), + }); + } + (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) => { + self.dcx().emit_err(errors::OptionalTraitObject { span: trait_ref.span }); + } + ( + BoundKind::TraitObject, + BoundConstness::Always(_), + BoundPolarity::Positive, + ) => { + self.dcx().emit_err(errors::ConstBoundTraitObject { span: trait_ref.span }); + } + (_, BoundConstness::Maybe(span), BoundPolarity::Positive) + if let Some(reason) = &self.disallow_tilde_const => + { + let reason = match reason { + DisallowTildeConstContext::Fn(FnKind::Closure(..)) => { + errors::TildeConstReason::Closure + } + DisallowTildeConstContext::Fn(FnKind::Fn(_, ident, ..)) => { + errors::TildeConstReason::Function { ident: ident.span } + } + &DisallowTildeConstContext::Trait(span) => { + errors::TildeConstReason::Trait { span } + } + &DisallowTildeConstContext::TraitImpl(span) => { + errors::TildeConstReason::TraitImpl { span } + } + &DisallowTildeConstContext::Impl(span) => { + // FIXME(effects): Consider providing a help message or even a structured + // suggestion for moving such bounds to the assoc const fns if available. + errors::TildeConstReason::Impl { span } + } + &DisallowTildeConstContext::TraitAssocTy(span) => { + errors::TildeConstReason::TraitAssocTy { span } + } + &DisallowTildeConstContext::TraitImplAssocTy(span) => { + errors::TildeConstReason::TraitImplAssocTy { span } + } + &DisallowTildeConstContext::InherentAssocTy(span) => { + errors::TildeConstReason::InherentAssocTy { span } + } + DisallowTildeConstContext::TraitObject => { + errors::TildeConstReason::TraitObject + } + DisallowTildeConstContext::Item => errors::TildeConstReason::Item, + }; + self.dcx().emit_err(errors::TildeConstDisallowed { span, reason }); + } + ( + _, + BoundConstness::Always(_) | BoundConstness::Maybe(_), + BoundPolarity::Negative(_) | BoundPolarity::Maybe(_), + ) => { + self.dcx().emit_err(errors::IncompatibleTraitBoundModifiers { + span: bound.span(), + left: modifiers.constness.as_str(), + right: modifiers.polarity.as_str(), + }); + } + _ => {} } - (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) => { - self.dcx().emit_err(errors::OptionalTraitObject { span: poly.span }); - } - (BoundKind::TraitObject, BoundConstness::Always(_), BoundPolarity::Positive) => { - self.dcx().emit_err(errors::ConstBoundTraitObject { span: poly.span }); - } - (_, BoundConstness::Maybe(span), BoundPolarity::Positive) - if let Some(reason) = &self.disallow_tilde_const => - { - let reason = match reason { - DisallowTildeConstContext::Fn(FnKind::Closure(..)) => { - errors::TildeConstReason::Closure - } - DisallowTildeConstContext::Fn(FnKind::Fn(_, ident, ..)) => { - errors::TildeConstReason::Function { ident: ident.span } - } - &DisallowTildeConstContext::Trait(span) => { - errors::TildeConstReason::Trait { span } - } - &DisallowTildeConstContext::TraitImpl(span) => { - errors::TildeConstReason::TraitImpl { span } - } - &DisallowTildeConstContext::Impl(span) => { - // FIXME(effects): Consider providing a help message or even a structured - // suggestion for moving such bounds to the assoc const fns if available. - errors::TildeConstReason::Impl { span } - } - &DisallowTildeConstContext::TraitAssocTy(span) => { - errors::TildeConstReason::TraitAssocTy { span } - } - &DisallowTildeConstContext::TraitImplAssocTy(span) => { - errors::TildeConstReason::TraitImplAssocTy { span } - } - &DisallowTildeConstContext::InherentAssocTy(span) => { - errors::TildeConstReason::InherentAssocTy { span } - } - DisallowTildeConstContext::TraitObject => { - errors::TildeConstReason::TraitObject - } - DisallowTildeConstContext::Item => errors::TildeConstReason::Item, - }; - self.dcx().emit_err(errors::TildeConstDisallowed { span, reason }); - } - ( - _, - BoundConstness::Always(_) | BoundConstness::Maybe(_), - BoundPolarity::Negative(_) | BoundPolarity::Maybe(_), - ) => { - self.dcx().emit_err(errors::IncompatibleTraitBoundModifiers { - span: bound.span(), - left: modifiers.constness.as_str(), - right: modifiers.polarity.as_str(), - }); - } - _ => {} - } - } - // Negative trait bounds are not allowed to have associated constraints - if let GenericBound::Trait(trait_ref, modifiers) = bound - && let BoundPolarity::Negative(_) = modifiers.polarity - && let Some(segment) = trait_ref.trait_ref.path.segments.last() - { - match segment.args.as_deref() { - Some(ast::GenericArgs::AngleBracketed(args)) => { - for arg in &args.args { - if let ast::AngleBracketedArg::Constraint(constraint) = arg { - self.dcx().emit_err(errors::ConstraintOnNegativeBound { - span: constraint.span, + // Negative trait bounds are not allowed to have associated constraints + if let BoundPolarity::Negative(_) = modifiers.polarity + && let Some(segment) = trait_ref.trait_ref.path.segments.last() + { + match segment.args.as_deref() { + Some(ast::GenericArgs::AngleBracketed(args)) => { + for arg in &args.args { + if let ast::AngleBracketedArg::Constraint(constraint) = arg { + self.dcx().emit_err(errors::ConstraintOnNegativeBound { + span: constraint.span, + }); + } + } + } + // The lowered form of parenthesized generic args contains an associated type binding. + Some(ast::GenericArgs::Parenthesized(args)) => { + self.dcx().emit_err(errors::NegativeBoundWithParentheticalNotation { + span: args.span, }); } + None => {} } } - // The lowered form of parenthesized generic args contains a type binding. - Some(ast::GenericArgs::Parenthesized(args)) => { - self.dcx().emit_err(errors::NegativeBoundWithParentheticalNotation { - span: args.span, + } + GenericBound::Outlives(_) => {} + GenericBound::Use(_, span) => match ctxt { + BoundKind::Impl => {} + BoundKind::Bound | BoundKind::TraitObject | BoundKind::SuperTraits => { + self.dcx().emit_err(errors::PreciseCapturingNotAllowedHere { + loc: ctxt.descr(), + span: *span, }); } - None => {} - } + }, } visit::walk_param_bound(self, bound) @@ -1428,17 +1493,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> { Self::check_decl_no_pat(&sig.decl, |span, ident, mut_ident| { if mut_ident && matches!(ctxt, FnCtxt::Assoc(_)) { if let Some(ident) = ident { - let msg = match ctxt { - FnCtxt::Foreign => fluent::ast_passes_pattern_in_foreign, - _ => fluent::ast_passes_pattern_in_bodiless, - }; - let diag = BuiltinLintDiag::PatternsInFnsWithoutBody(span, ident); - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( PATTERNS_IN_FNS_WITHOUT_BODY, id, span, - msg, - diag, + BuiltinLintDiag::PatternsInFnsWithoutBody { + span, + ident, + is_foreign: matches!(ctxt, FnCtxt::Foreign), + }, ) } } else { @@ -1510,12 +1573,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> { Some((right, snippet)) } }; - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( DEPRECATED_WHERE_CLAUSE_LOCATION, item.id, err.span, - fluent::ast_passes_deprecated_where_clause_location, - BuiltinLintDiag::DeprecatedWhereclauseLocation(sugg), + BuiltinLintDiag::DeprecatedWhereclauseLocation(err.span, sugg), ); } @@ -1596,11 +1658,13 @@ fn deny_equality_constraints( let len = assoc_path.segments.len() - 1; let gen_args = args.as_deref().cloned(); // Build ``. - let arg = AngleBracketedArg::Constraint(AssocConstraint { + let arg = AngleBracketedArg::Constraint(AssocItemConstraint { id: rustc_ast::node_id::DUMMY_NODE_ID, ident: *ident, gen_args, - kind: AssocConstraintKind::Equality { term: predicate.rhs_ty.clone().into() }, + kind: AssocItemConstraintKind::Equality { + term: predicate.rhs_ty.clone().into(), + }, span: ident.span, }); // Add `` to `Foo`. @@ -1741,6 +1805,7 @@ pub fn check_crate( outer_impl_trait: None, disallow_tilde_const: Some(DisallowTildeConstContext::Item), is_impl_trait_banned: false, + extern_mod_safety: None, lint_buffer: lints, }; visit::walk_crate(&mut validator, krate); diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 25a125f83935..601910ded208 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -92,13 +92,6 @@ pub struct FnParamTooMany { pub max_num_args: usize, } -#[derive(Diagnostic)] -#[diag(ast_passes_fn_param_c_var_args_only)] -pub struct FnParamCVarArgsOnly { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(ast_passes_fn_param_c_var_args_not_last)] pub struct FnParamCVarArgsNotLast { @@ -223,6 +216,15 @@ pub enum ExternBlockSuggestion { }, } +#[derive(Diagnostic)] +#[diag(ast_passes_extern_invalid_safety)] +pub struct InvalidSafetyOnExtern { + #[primary_span] + pub item_span: Span, + #[suggestion(code = "", applicability = "maybe-incorrect")] + pub block: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_bound_in_context)] pub struct BoundInContext<'a> { @@ -492,6 +494,13 @@ pub struct UnsafeItem { pub kind: &'static str, } +#[derive(Diagnostic)] +#[diag(ast_passes_missing_unsafe_on_extern)] +pub struct MissingUnsafeOnExtern { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_fieldless_union)] pub struct FieldlessUnion { @@ -669,6 +678,7 @@ pub struct ConstAndCVariadic { #[derive(Diagnostic)] #[diag(ast_passes_pattern_in_foreign, code = E0130)] +// FIXME: deduplicate with rustc_lint (`BuiltinLintDiag::PatternsInFnsWithoutBody`) pub struct PatternInForeign { #[primary_span] #[label] @@ -677,6 +687,7 @@ pub struct PatternInForeign { #[derive(Diagnostic)] #[diag(ast_passes_pattern_in_bodiless, code = E0642)] +// FIXME: deduplicate with rustc_lint (`BuiltinLintDiag::PatternsInFnsWithoutBody`) pub struct PatternInBodiless { #[primary_span] #[label] @@ -833,3 +844,20 @@ pub struct MatchArmWithNoBody { #[suggestion(code = " => todo!(),", applicability = "has-placeholders")] pub suggestion: Span, } + +#[derive(Diagnostic)] +#[diag(ast_passes_precise_capturing_not_allowed_here)] +pub struct PreciseCapturingNotAllowedHere { + #[primary_span] + pub span: Span, + pub loc: &'static str, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_precise_capturing_duplicated)] +pub struct DuplicatePreciseCapturing { + #[primary_span] + pub bound1: Span, + #[label] + pub bound2: Span, +} diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 6622caaaab41..764d942836c2 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,6 +1,6 @@ use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; -use rustc_ast::{attr, AssocConstraint, AssocConstraintKind, NodeId}; +use rustc_ast::{attr, AssocItemConstraint, AssocItemConstraintKind, NodeId}; use rustc_ast::{token, PatKind}; use rustc_feature::{AttributeGate, BuiltinAttribute, Features, GateIssue, BUILTIN_ATTRIBUTE_MAP}; use rustc_session::parse::{feature_err, feature_err_issue, feature_warn}; @@ -344,7 +344,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { for predicate in &g.where_clause.predicates { match predicate { ast::WherePredicate::BoundPredicate(bound_pred) => { - // A type binding, eg `for<'c> Foo: Send+Clone+'c` + // A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`). self.check_late_bound_lifetime_defs(&bound_pred.bound_generic_params); } _ => {} @@ -445,21 +445,21 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { visit::walk_fn(self, fn_kind) } - fn visit_assoc_constraint(&mut self, constraint: &'a AssocConstraint) { - if let AssocConstraintKind::Bound { .. } = constraint.kind { - if let Some(ast::GenericArgs::Parenthesized(args)) = constraint.gen_args.as_ref() - && args.inputs.is_empty() - && matches!(args.output, ast::FnRetTy::Default(..)) - { - gate!( - &self, - return_type_notation, - constraint.span, - "return type notation is experimental" - ); - } + fn visit_assoc_item_constraint(&mut self, constraint: &'a AssocItemConstraint) { + if let AssocItemConstraintKind::Bound { .. } = constraint.kind + && let Some(ast::GenericArgs::Parenthesized(args)) = constraint.gen_args.as_ref() + && args.inputs.is_empty() + && let ast::FnRetTy::Default(..) = args.output + { + gate!( + &self, + return_type_notation, + constraint.span, + "return type notation is experimental" + ); } - visit::walk_assoc_constraint(self, constraint) + + visit::walk_assoc_item_constraint(self, constraint) } fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) { @@ -560,6 +560,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(postfix_match, "postfix match is experimental"); gate_all!(mut_ref, "mutable by-reference bindings are experimental"); gate_all!(precise_capturing, "precise captures on `impl Trait` are experimental"); + gate_all!(global_registration, "global registration is experimental"); + gate_all!(unsafe_attributes, "`#[unsafe()]` markers for attributes are experimental"); if !visitor.features.never_patterns { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 74d0fff2734f..1f4bcd59afa0 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -4,13 +4,15 @@ //! //! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`. +// tidy-alphabetical-start #![allow(internal_features)] #![doc(rust_logo)] -#![feature(rustdoc_internals)] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_is_partitioned)] #![feature(let_chains)] +#![feature(rustdoc_internals)] +// tidy-alphabetical-end pub mod ast_validation; mod errors; diff --git a/compiler/rustc_ast_passes/src/node_count.rs b/compiler/rustc_ast_passes/src/node_count.rs index 2128acba0bab..c61640de6f79 100644 --- a/compiler/rustc_ast_passes/src/node_count.rs +++ b/compiler/rustc_ast_passes/src/node_count.rs @@ -119,9 +119,9 @@ impl<'ast> Visitor<'ast> for NodeCounter { self.count += 1; walk_generic_args(self, generic_args) } - fn visit_assoc_constraint(&mut self, constraint: &AssocConstraint) { + fn visit_assoc_item_constraint(&mut self, constraint: &AssocItemConstraint) { self.count += 1; - walk_assoc_constraint(self, constraint) + walk_assoc_item_constraint(self, constraint) } fn visit_attribute(&mut self, _attr: &Attribute) { self.count += 1; diff --git a/compiler/rustc_ast_passes/src/show_span.rs b/compiler/rustc_ast_passes/src/show_span.rs index 105900742822..e7ba2e7fc30b 100644 --- a/compiler/rustc_ast_passes/src/show_span.rs +++ b/compiler/rustc_ast_passes/src/show_span.rs @@ -8,6 +8,7 @@ use std::str::FromStr; use rustc_ast as ast; use rustc_ast::visit; use rustc_ast::visit::Visitor; +use rustc_errors::DiagCtxtHandle; use crate::errors; @@ -31,7 +32,7 @@ impl FromStr for Mode { } struct ShowSpanVisitor<'a> { - dcx: &'a rustc_errors::DiagCtxt, + dcx: DiagCtxtHandle<'a>, mode: Mode, } @@ -58,7 +59,7 @@ impl<'a> Visitor<'a> for ShowSpanVisitor<'a> { } } -pub fn run(dcx: &rustc_errors::DiagCtxt, mode: &str, krate: &ast::Crate) { +pub fn run(dcx: DiagCtxtHandle<'_>, mode: &str, krate: &ast::Crate) { let Ok(mode) = mode.parse() else { return; }; diff --git a/compiler/rustc_ast_pretty/src/lib.rs b/compiler/rustc_ast_pretty/src/lib.rs index b9e217a21e39..84d9ce278a21 100644 --- a/compiler/rustc_ast_pretty/src/lib.rs +++ b/compiler/rustc_ast_pretty/src/lib.rs @@ -1,7 +1,9 @@ +// tidy-alphabetical-start #![allow(internal_features)] -#![feature(rustdoc_internals)] #![doc(rust_logo)] #![feature(box_patterns)] +#![feature(rustdoc_internals)] +// tidy-alphabetical-end mod helpers; pub mod pp; diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 545b98a9135a..0225c95dca8e 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -16,7 +16,7 @@ use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, To use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree}; use rustc_ast::util::classify; use rustc_ast::util::comments::{Comment, CommentStyle}; -use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind}; +use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind, Safety}; use rustc_ast::{attr, BindingMode, ByRef, DelimArgs, RangeEnd, RangeSyntax, Term}; use rustc_ast::{GenericArg, GenericBound, SelfKind}; use rustc_ast::{InlineAsmOperand, InlineAsmRegOrRegClass}; @@ -249,6 +249,7 @@ pub fn print_crate<'a>( let fake_attr = attr::mk_attr_nested_word( g, ast::AttrStyle::Inner, + Safety::Default, sym::feature, sym::prelude_import, DUMMY_SP, @@ -259,7 +260,13 @@ pub fn print_crate<'a>( // root, so this is not needed, and actually breaks things. if edition.is_rust_2015() { // `#![no_std]` - let fake_attr = attr::mk_attr_word(g, ast::AttrStyle::Inner, sym::no_std, DUMMY_SP); + let fake_attr = attr::mk_attr_word( + g, + ast::AttrStyle::Inner, + Safety::Default, + sym::no_std, + DUMMY_SP, + ); s.print_attribute(&fake_attr); } } @@ -681,22 +688,40 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } } + // The easiest way to implement token stream pretty printing would be to + // print each token followed by a single space. But that would produce ugly + // output, so we go to some effort to do better. + // + // First, we track whether each token that appears in source code is + // followed by a space, with `Spacing`, and reproduce that in the output. + // This works well in a lot of cases. E.g. `stringify!(x + y)` produces + // "x + y" and `stringify!(x+y)` produces "x+y". + // + // But this doesn't work for code produced by proc macros (which have no + // original source text representation) nor for code produced by decl + // macros (which are tricky because the whitespace after tokens appearing + // in macro rules isn't always what you want in the produced output). For + // these we mostly use `Spacing::Alone`, which is the conservative choice. + // + // So we have a backup mechanism for when `Spacing::Alone` occurs between a + // pair of tokens: we check if that pair of tokens can obviously go + // together without a space between them. E.g. token `x` followed by token + // `,` is better printed as `x,` than `x ,`. (Even if the original source + // code was `x ,`.) + // + // Finally, we must be careful about changing the output. Token pretty + // printing is used by `stringify!` and `impl Display for + // proc_macro::TokenStream`, and some programs rely on the output having a + // particular form, even though they shouldn't. In particular, some proc + // macros do `format!({stream})` on a token stream and then "parse" the + // output with simple string matching that can't handle whitespace changes. + // E.g. we have seen cases where a proc macro can handle `a :: b` but not + // `a::b`. See #117433 for some examples. fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) { let mut iter = tts.trees().peekable(); while let Some(tt) = iter.next() { let spacing = self.print_tt(tt, convert_dollar_crate); if let Some(next) = iter.peek() { - // Should we print a space after `tt`? There are two guiding - // factors. - // - `spacing` is the more important and accurate one. Most - // tokens have good spacing information, and - // `Joint`/`JointHidden` get used a lot. - // - `space_between` is the backup. Code produced by proc - // macros has worse spacing information, with no - // `JointHidden` usage and too much `Alone` usage, which - // would result in over-spaced output such as - // `( x () , y . z )`. `space_between` avoids some of the - // excess whitespace. if spacing == Spacing::Alone && space_between(tt, next) { self.space(); } @@ -852,18 +877,11 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } fn nonterminal_to_string(&self, nt: &Nonterminal) -> String { - match nt { - token::NtExpr(e) => self.expr_to_string(e), - token::NtMeta(e) => self.attr_item_to_string(e), - token::NtTy(e) => self.ty_to_string(e), - token::NtPath(e) => self.path_to_string(e), - token::NtItem(e) => self.item_to_string(e), - token::NtBlock(e) => self.block_to_string(e), - token::NtStmt(e) => self.stmt_to_string(e), - token::NtPat(e) => self.pat_to_string(e), - token::NtLiteral(e) => self.expr_to_string(e), - token::NtVis(e) => self.vis_to_string(e), - } + // We extract the token stream from the AST fragment and pretty print + // it, rather than using AST pretty printing, because `Nonterminal` is + // slated for removal in #124141. (This method will also then be + // removed.) + self.tts_to_string(&TokenStream::from_nonterminal_ast(nt)) } /// Print the token kind precisely, without converting `$crate` into its respective crate name. @@ -997,6 +1015,10 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere Self::to_string(|s| s.print_attr_item(ai, ai.path.span)) } + fn tts_to_string(&self, tokens: &TokenStream) -> String { + Self::to_string(|s| s.print_tts(tokens, false)) + } + fn to_string(f: impl FnOnce(&mut State<'_>)) -> String { let mut printer = State::new(); f(&mut printer); @@ -1027,7 +1049,7 @@ impl<'a> PrintState<'a> for State<'a> { self.word("<"); self.commasep(Inconsistent, &data.args, |s, arg| match arg { ast::AngleBracketedArg::Arg(a) => s.print_generic_arg(a), - ast::AngleBracketedArg::Constraint(c) => s.print_assoc_constraint(c), + ast::AngleBracketedArg::Constraint(c) => s.print_assoc_item_constraint(c), }); self.word(">") } @@ -1079,21 +1101,21 @@ impl<'a> State<'a> { } } - pub fn print_assoc_constraint(&mut self, constraint: &ast::AssocConstraint) { + pub fn print_assoc_item_constraint(&mut self, constraint: &ast::AssocItemConstraint) { self.print_ident(constraint.ident); if let Some(args) = constraint.gen_args.as_ref() { self.print_generic_args(args, false) } self.space(); match &constraint.kind { - ast::AssocConstraintKind::Equality { term } => { + ast::AssocItemConstraintKind::Equality { term } => { self.word_space("="); match term { Term::Ty(ty) => self.print_type(ty), Term::Const(c) => self.print_expr_anon_const(c, &[]), } } - ast::AssocConstraintKind::Bound { bounds } => { + ast::AssocItemConstraintKind::Bound { bounds } => { if !bounds.is_empty() { self.word_nbsp(":"); self.print_type_bounds(bounds); @@ -1165,17 +1187,8 @@ impl<'a> State<'a> { } self.print_type_bounds(bounds); } - ast::TyKind::ImplTrait(_, bounds, precise_capturing_args) => { + ast::TyKind::ImplTrait(_, bounds) => { self.word_nbsp("impl"); - if let Some((precise_capturing_args, ..)) = precise_capturing_args.as_deref() { - self.word("use"); - self.word("<"); - self.commasep(Inconsistent, precise_capturing_args, |s, arg| match arg { - ast::PreciseCapturingArg::Arg(p, _) => s.print_path(p, false, 0), - ast::PreciseCapturingArg::Lifetime(lt) => s.print_lifetime(*lt), - }); - self.word(">") - } self.print_type_bounds(bounds); } ast::TyKind::Array(ty, length) => { @@ -1778,6 +1791,15 @@ impl<'a> State<'a> { self.print_poly_trait_ref(tref); } GenericBound::Outlives(lt) => self.print_lifetime(*lt), + GenericBound::Use(args, _) => { + self.word("use"); + self.word("<"); + self.commasep(Inconsistent, args, |s, arg| match arg { + ast::PreciseCapturingArg::Arg(p, _) => s.print_path(p, false, 0), + ast::PreciseCapturingArg::Lifetime(lt) => s.print_lifetime(*lt), + }); + self.word(">") + } } } } @@ -1955,6 +1977,7 @@ impl<'a> State<'a> { fn print_safety(&mut self, s: ast::Safety) { match s { ast::Safety::Default => {} + ast::Safety::Safe(_) => self.word_nbsp("safe"), ast::Safety::Unsafe(_) => self.word_nbsp("unsafe"), } } @@ -2042,10 +2065,6 @@ impl<'a> State<'a> { }) } - pub(crate) fn tts_to_string(&self, tokens: &TokenStream) -> String { - Self::to_string(|s| s.print_tts(tokens, false)) - } - pub(crate) fn path_segment_to_string(&self, p: &ast::PathSegment) -> String { Self::to_string(|s| s.print_path_segment(p, false)) } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 59d9b0c1a8ec..49ac5ece337f 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -9,6 +9,12 @@ use rustc_ast::ptr::P; use rustc_ast::ModKind; use rustc_span::symbol::Ident; +enum DelegationKind<'a> { + Single, + List(&'a [(Ident, Option)]), + Glob, +} + fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String { format!("{}{}", State::to_string(|s| s.print_visibility(vis)), s) } @@ -31,7 +37,13 @@ impl<'a> State<'a> { ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); } - ast::ForeignItemKind::Static(box ast::StaticForeignItem { ty, mutability, expr }) => { + ast::ForeignItemKind::Static(box ast::StaticForeignItem { + ty, + mutability, + expr, + safety, + }) => { + self.print_safety(*safety); self.print_item_const( ident, Some(*mutability), @@ -165,7 +177,8 @@ impl<'a> State<'a> { self.print_use_tree(tree); self.word(";"); } - ast::ItemKind::Static(box StaticItem { ty, mutability: mutbl, expr: body }) => { + ast::ItemKind::Static(box StaticItem { ty, safety, mutability: mutbl, expr: body }) => { + self.print_safety(*safety); self.print_item_const( item.ident, Some(*mutbl), @@ -380,7 +393,7 @@ impl<'a> State<'a> { &item.vis, &deleg.qself, &deleg.path, - None, + DelegationKind::Single, &deleg.body, ), ast::ItemKind::DelegationMac(deleg) => self.print_delegation( @@ -388,7 +401,7 @@ impl<'a> State<'a> { &item.vis, &deleg.qself, &deleg.prefix, - Some(&deleg.suffixes), + deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)), &deleg.body, ), } @@ -572,7 +585,7 @@ impl<'a> State<'a> { vis, &deleg.qself, &deleg.path, - None, + DelegationKind::Single, &deleg.body, ), ast::AssocItemKind::DelegationMac(deleg) => self.print_delegation( @@ -580,20 +593,20 @@ impl<'a> State<'a> { vis, &deleg.qself, &deleg.prefix, - Some(&deleg.suffixes), + deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)), &deleg.body, ), } self.ann.post(self, AnnNode::SubItem(id)) } - pub(crate) fn print_delegation( + fn print_delegation( &mut self, attrs: &[ast::Attribute], vis: &ast::Visibility, qself: &Option>, path: &ast::Path, - suffixes: Option<&[(Ident, Option)]>, + kind: DelegationKind<'_>, body: &Option>, ) { if body.is_some() { @@ -607,21 +620,28 @@ impl<'a> State<'a> { } else { self.print_path(path, false, 0); } - if let Some(suffixes) = suffixes { - self.word("::"); - self.word("{"); - for (i, (ident, rename)) in suffixes.iter().enumerate() { - self.print_ident(*ident); - if let Some(rename) = rename { - self.nbsp(); - self.word_nbsp("as"); - self.print_ident(*rename); - } - if i != suffixes.len() - 1 { - self.word_space(","); + match kind { + DelegationKind::Single => {} + DelegationKind::List(suffixes) => { + self.word("::"); + self.word("{"); + for (i, (ident, rename)) in suffixes.iter().enumerate() { + self.print_ident(*ident); + if let Some(rename) = rename { + self.nbsp(); + self.word_nbsp("as"); + self.print_ident(*rename); + } + if i != suffixes.len() - 1 { + self.word_space(","); + } } + self.word("}"); + } + DelegationKind::Glob => { + self.word("::"); + self.word("*"); } - self.word("}"); } if let Some(body) = body { self.nbsp(); diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index c08bf2877336..34c24a26f7b1 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -528,15 +528,10 @@ pub fn cfg_matches( try_gate_cfg(cfg.name, cfg.span, sess, features); match sess.psess.check_config.expecteds.get(&cfg.name) { Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => { - sess.psess.buffer_lint_with_diagnostic( + sess.psess.buffer_lint( UNEXPECTED_CFGS, cfg.span, lint_node_id, - if let Some(value) = cfg.value { - format!("unexpected `cfg` condition value: `{value}`") - } else { - format!("unexpected `cfg` condition value: (none)") - }, BuiltinLintDiag::UnexpectedCfgValue( (cfg.name, cfg.name_span), cfg.value.map(|v| (v, cfg.value_span.unwrap())), @@ -544,11 +539,10 @@ pub fn cfg_matches( ); } None if sess.psess.check_config.exhaustive_names => { - sess.psess.buffer_lint_with_diagnostic( + sess.psess.buffer_lint( UNEXPECTED_CFGS, cfg.span, lint_node_id, - format!("unexpected `cfg` condition name: `{}`", cfg.name), BuiltinLintDiag::UnexpectedCfgName( (cfg.name, cfg.name_span), cfg.value.map(|v| (v, cfg.value_span.unwrap())), @@ -602,7 +596,7 @@ pub fn eval_condition( features: Option<&Features>, eval: &mut impl FnMut(Condition) -> bool, ) -> bool { - let dcx = &sess.psess.dcx; + let dcx = sess.dcx(); match &cfg.kind { ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => { try_gate_cfg(sym::version, cfg.span, sess, features); diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs index c61b7ea6d822..9cc53ad7ad8c 100644 --- a/compiler/rustc_attr/src/lib.rs +++ b/compiler/rustc_attr/src/lib.rs @@ -4,10 +4,12 @@ //! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax` //! to this crate. +// tidy-alphabetical-start #![allow(internal_features)] -#![feature(rustdoc_internals)] #![doc(rust_logo)] #![feature(let_chains)] +#![feature(rustdoc_internals)] +// tidy-alphabetical-end mod builtin; mod session_diagnostics; diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr/src/session_diagnostics.rs index 303909de3435..0cffeed0a755 100644 --- a/compiler/rustc_attr/src/session_diagnostics.rs +++ b/compiler/rustc_attr/src/session_diagnostics.rs @@ -1,7 +1,8 @@ use std::num::IntErrorKind; use rustc_ast as ast; -use rustc_errors::{codes::*, Applicability, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level}; +use rustc_errors::DiagCtxtHandle; +use rustc_errors::{codes::*, Applicability, Diag, Diagnostic, EmissionGuarantee, Level}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; @@ -49,7 +50,7 @@ pub(crate) struct UnknownMetaItem<'a> { // Manual implementation to be able to format `expected` items correctly. impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnknownMetaItem<'_> { - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let expected = self.expected.iter().map(|name| format!("`{name}`")).collect::>(); Diag::new(dcx, level, fluent::attr_unknown_meta_item) .with_span(self.span) @@ -202,7 +203,7 @@ pub(crate) struct UnsupportedLiteral { } impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral { - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let mut diag = Diag::new( dcx, level, diff --git a/compiler/rustc_baked_icu_data/Cargo.toml b/compiler/rustc_baked_icu_data/Cargo.toml index 48af4e6f600b..e6cfb4887c91 100644 --- a/compiler/rustc_baked_icu_data/Cargo.toml +++ b/compiler/rustc_baked_icu_data/Cargo.toml @@ -9,7 +9,6 @@ icu_list = "1.2" icu_locid = "1.2" icu_locid_transform = "1.3.2" icu_provider = "1.2" -icu_provider_adapters = "1.2" zerovec = "0.10.0" # tidy-alphabetical-end diff --git a/compiler/rustc_baked_icu_data/src/lib.rs b/compiler/rustc_baked_icu_data/src/lib.rs index ffcb290685a1..e964a709757c 100644 --- a/compiler/rustc_baked_icu_data/src/lib.rs +++ b/compiler/rustc_baked_icu_data/src/lib.rs @@ -20,10 +20,12 @@ //! --cldr-tag latest --icuexport-tag latest -o src/data //! ``` -#![allow(internal_features)] -#![feature(rustdoc_internals)] -#![doc(rust_logo)] +// tidy-alphabetical-start #![allow(elided_lifetimes_in_paths)] +#![allow(internal_features)] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +// tidy-alphabetical-end mod data { include!("data/mod.rs"); diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 622feb4e4c73..c1f753282725 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -1,13 +1,13 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -use rustc_errors::{codes::*, struct_span_code_err, Diag, DiagCtxt}; +use rustc_errors::{codes::*, struct_span_code_err, Diag, DiagCtxtHandle}; use rustc_middle::span_bug; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { - pub fn dcx(&self) -> &'tcx DiagCtxt { + pub fn dcx(&self) -> DiagCtxtHandle<'tcx> { self.infcx.dcx() } diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs index ff11e4db1213..b54e05b2b34a 100644 --- a/compiler/rustc_borrowck/src/constraints/mod.rs +++ b/compiler/rustc_borrowck/src/constraints/mod.rs @@ -1,13 +1,11 @@ -use rustc_data_structures::graph::scc::Sccs; +use crate::type_check::Locations; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::ConstraintCategory; -use rustc_middle::ty::{RegionVid, VarianceDiagInfo}; +use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo}; use rustc_span::Span; use std::fmt; use std::ops::Index; -use crate::type_check::Locations; - pub(crate) mod graph; /// A set of NLL region constraints. These include "outlives" @@ -45,18 +43,6 @@ impl<'tcx> OutlivesConstraintSet<'tcx> { graph::ConstraintGraph::new(graph::Reverse, self, num_region_vars) } - /// Computes cycles (SCCs) in the graph of regions. In particular, - /// find all regions R1, R2 such that R1: R2 and R2: R1 and group - /// them into an SCC, and find the relationships between SCCs. - pub(crate) fn compute_sccs( - &self, - constraint_graph: &graph::NormalConstraintGraph, - static_region: RegionVid, - ) -> Sccs { - let region_graph = &constraint_graph.region_graph(self, static_region); - Sccs::new(region_graph) - } - pub(crate) fn outlives( &self, ) -> &IndexSlice> { @@ -97,7 +83,7 @@ pub struct OutlivesConstraint<'tcx> { pub category: ConstraintCategory<'tcx>, /// Variance diagnostic information - pub variance_info: VarianceDiagInfo<'tcx>, + pub variance_info: VarianceDiagInfo>, /// If this constraint is promoted from closure requirements. pub from_closure: bool, diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 9b8b7e8ddda6..5e10f14f31b5 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -1,4 +1,5 @@ use rustc_errors::Diag; +use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::canonical::Canonical; use rustc_infer::infer::error_reporting::nice_region_error::NiceRegionError; use rustc_infer::infer::region_constraints::Constraint; @@ -241,7 +242,7 @@ impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> { mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); let ocx = ObligationCtxt::new(&infcx); type_op_prove_predicate_with_cause(&ocx, key, cause); - try_extract_error_from_fulfill_cx(&ocx, placeholder_region, error_region) + try_extract_error_from_fulfill_cx(&ocx, mbcx.mir_def_id(), placeholder_region, error_region) } } @@ -287,7 +288,7 @@ where let (param_env, value) = key.into_parts(); let _ = ocx.normalize(&cause, param_env, value.value); - try_extract_error_from_fulfill_cx(&ocx, placeholder_region, error_region) + try_extract_error_from_fulfill_cx(&ocx, mbcx.mir_def_id(), placeholder_region, error_region) } } @@ -318,7 +319,7 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> { mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); let ocx = ObligationCtxt::new(&infcx); type_op_ascribe_user_type_with_span(&ocx, key, Some(cause.span)).ok()?; - try_extract_error_from_fulfill_cx(&ocx, placeholder_region, error_region) + try_extract_error_from_fulfill_cx(&ocx, mbcx.mir_def_id(), placeholder_region, error_region) } } @@ -342,6 +343,7 @@ impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> { ) -> Option> { try_extract_error_from_region_constraints( mbcx.infcx, + mbcx.mir_def_id(), placeholder_region, error_region, self.region_constraints.as_ref().unwrap(), @@ -358,6 +360,7 @@ impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> { #[instrument(skip(ocx), level = "debug")] fn try_extract_error_from_fulfill_cx<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, + generic_param_scope: LocalDefId, placeholder_region: ty::Region<'tcx>, error_region: Option>, ) -> Option> { @@ -368,6 +371,7 @@ fn try_extract_error_from_fulfill_cx<'tcx>( let region_constraints = ocx.infcx.with_region_constraints(|r| r.clone()); try_extract_error_from_region_constraints( ocx.infcx, + generic_param_scope, placeholder_region, error_region, ®ion_constraints, @@ -379,6 +383,7 @@ fn try_extract_error_from_fulfill_cx<'tcx>( #[instrument(level = "debug", skip(infcx, region_var_origin, universe_of_region))] fn try_extract_error_from_region_constraints<'tcx>( infcx: &InferCtxt<'tcx>, + generic_param_scope: LocalDefId, placeholder_region: ty::Region<'tcx>, error_region: Option>, region_constraints: &RegionConstraintData<'tcx>, @@ -452,15 +457,18 @@ fn try_extract_error_from_region_constraints<'tcx>( RegionResolutionError::ConcreteFailure(cause.clone(), sub_region, placeholder_region) } }; - NiceRegionError::new(&infcx.err_ctxt(), error).try_report_from_nll().or_else(|| { - if let SubregionOrigin::Subtype(trace) = cause { - Some( - infcx - .err_ctxt() - .report_and_explain_type_error(*trace, TypeError::RegionsPlaceholderMismatch), - ) - } else { - None - } - }) + NiceRegionError::new(&infcx.err_ctxt(), generic_param_scope, error) + .try_report_from_nll() + .or_else(|| { + if let SubregionOrigin::Subtype(trace) = cause { + Some( + infcx.err_ctxt().report_and_explain_type_error( + *trace, + TypeError::RegionsPlaceholderMismatch, + ), + ) + } else { + None + } + }) } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 15050c87b39e..197da3eb6416 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -100,12 +100,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { move_site_vec.iter().map(|move_site| move_site.moi).collect(); if move_out_indices.is_empty() { - let root_place = PlaceRef { projection: &[], ..used_place }; + let root_local = used_place.local; - if !self.uninitialized_error_reported.insert(root_place) { + if !self.uninitialized_error_reported.insert(root_local) { debug!( "report_use_of_moved_or_uninitialized place: error about {:?} suppressed", - root_place + root_local ); return; } @@ -228,7 +228,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { seen_spans.insert(move_span); } - use_spans.var_path_only_subdiag(self.dcx(), &mut err, desired_action); + use_spans.var_path_only_subdiag(&mut err, desired_action); if !is_loop_move { err.span_label( @@ -284,7 +284,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { && let CallKind::FnCall { fn_trait_id, self_ty } = kind && let ty::Param(_) = self_ty.kind() && ty == self_ty - && Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() + && self.infcx.tcx.is_lang_item(fn_trait_id, LangItem::FnOnce) { // this is a type parameter `T: FnOnce()`, don't suggest `T: FnOnce() + Clone`. true @@ -303,24 +303,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if needs_note { if let Some(local) = place.as_local() { let span = self.body.local_decls[local].source_info.span; - err.subdiagnostic( - self.dcx(), - crate::session_diagnostics::TypeNoCopy::Label { - is_partial_move, - ty, - place: ¬e_msg, - span, - }, - ); + err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { + is_partial_move, + ty, + place: ¬e_msg, + span, + }); } else { - err.subdiagnostic( - self.dcx(), - crate::session_diagnostics::TypeNoCopy::Note { - is_partial_move, - ty, - place: ¬e_msg, - }, - ); + err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note { + is_partial_move, + ty, + place: ¬e_msg, + }); }; } @@ -399,8 +393,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } let hir = self.infcx.tcx.hir(); - if let Some(body_id) = hir.maybe_body_owned_by(self.mir_def_id()) { - let expr = hir.body(body_id).value; + if let Some(body) = hir.maybe_body_owned_by(self.mir_def_id()) { + let expr = body.value; let place = &self.move_data.move_paths[mpi].place; let span = place.as_local().map(|local| self.body.local_decls[local].source_info.span); let mut finder = ExpressionFinder { @@ -556,11 +550,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // We use the statements were the binding was initialized, and inspect the HIR to look // for the branching codepaths that aren't covered, to point at them. let map = self.infcx.tcx.hir(); - let body_id = map.body_owned_by(self.mir_def_id()); - let body = map.body(body_id); - - let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] }; - visitor.visit_body(body); + let body = map.body_owned_by(self.mir_def_id()); + let mut visitor = + ConditionVisitor { tcx: self.infcx.tcx, spans: &spans, name: &name, errors: vec![] }; + visitor.visit_body(&body); let mut show_assign_sugg = false; let isnt_initialized = if let InitializationRequiringAction::PartialAssignment @@ -598,7 +591,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { E0381, "{used} binding {desc}{isnt_initialized}" ); - use_spans.var_path_only_subdiag(self.dcx(), &mut err, desired_action); + use_spans.var_path_only_subdiag(&mut err, desired_action); if let InitializationRequiringAction::PartialAssignment | InitializationRequiringAction::Assignment = desired_action @@ -652,7 +645,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } // FIXME: We make sure that this is a normal top-level binding, - // but we could suggest `todo!()` for all uninitialized bindings in the pattern pattern + // but we could suggest `todo!()` for all uninitialized bindings in the pattern if let hir::StmtKind::Let(hir::LetStmt { span, ty, init: None, pat, .. }) = &ex.kind && let hir::PatKind::Binding(..) = pat.kind @@ -665,7 +658,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } let mut visitor = LetVisitor { decl_span, sugg_span: None }; - visitor.visit_body(body); + visitor.visit_body(&body); if let Some(span) = visitor.sugg_span { self.suggest_assign_value(&mut err, moved_place, span); } @@ -709,9 +702,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let ty::ClauseKind::Trait(pred) = pred.kind().skip_binder() && pred.self_ty() == ty { - if Some(pred.def_id()) == tcx.lang_items().fn_trait() { + if tcx.is_lang_item(pred.def_id(), LangItem::Fn) { return Some(hir::Mutability::Not); - } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() { + } else if tcx.is_lang_item(pred.def_id(), LangItem::FnMut) { return Some(hir::Mutability::Mut); } } @@ -997,7 +990,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &self, err: &mut Diag<'_>, ty: Ty<'tcx>, - expr: &'cx hir::Expr<'cx>, + expr: &hir::Expr<'_>, ) { let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); let hir::ExprKind::Struct(struct_qpath, fields, Some(base)) = expr.kind else { return }; @@ -1085,8 +1078,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &self, err: &mut Diag<'_>, ty: Ty<'tcx>, - mut expr: &'cx hir::Expr<'cx>, - mut other_expr: Option<&'cx hir::Expr<'cx>>, + mut expr: &'tcx hir::Expr<'tcx>, + mut other_expr: Option<&'tcx hir::Expr<'tcx>>, use_spans: Option>, ) { if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind { @@ -1348,7 +1341,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { return; }; // Try to find predicates on *generic params* that would allow copying `ty` - let ocx = ObligationCtxt::new(self.infcx); + let ocx = ObligationCtxt::new_with_diagnostics(self.infcx); let cause = ObligationCause::misc(span, self.mir_def_id()); ocx.register_bound(cause, self.param_env, ty, def_id); @@ -1362,7 +1355,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { match *predicate.self_ty().kind() { ty::Param(param_ty) => Ok(( generics.type_param(param_ty, tcx), - predicate.trait_ref.print_only_trait_path().to_string(), + predicate.trait_ref.print_trait_sugared().to_string(), )), _ => Err(()), } @@ -1411,13 +1404,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &value_msg, ); - borrow_spans.var_path_only_subdiag( - self.dcx(), - &mut err, - crate::InitializationRequiringAction::Borrow, - ); + borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow); - move_spans.var_subdiag(self.dcx(), &mut err, None, |kind, var_span| { + move_spans.var_subdiag(&mut err, None, |kind, var_span| { use crate::session_diagnostics::CaptureVarCause::*; match kind { hir::ClosureKind::Coroutine(_) => MoveUseInCoroutine { var_span }, @@ -1469,7 +1458,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { borrow_span, &self.describe_any_place(borrow.borrowed_place.as_ref()), ); - borrow_spans.var_subdiag(self.dcx(), &mut err, Some(borrow.kind), |kind, var_span| { + borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| { use crate::session_diagnostics::CaptureVarCause::*; let place = &borrow.borrowed_place; let desc_place = self.describe_any_place(place.as_ref()); @@ -1634,7 +1623,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { "mutably borrow", ); borrow_spans.var_subdiag( - self.dcx(), &mut err, Some(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }), |kind, var_span| { @@ -1731,64 +1719,45 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }; if issued_spans == borrow_spans { - borrow_spans.var_subdiag( - self.dcx(), - &mut err, - Some(gen_borrow_kind), - |kind, var_span| { - use crate::session_diagnostics::CaptureVarCause::*; - match kind { - hir::ClosureKind::Coroutine(_) => BorrowUsePlaceCoroutine { - place: desc_place, - var_span, - is_single_var: false, - }, - hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => { - BorrowUsePlaceClosure { - place: desc_place, - var_span, - is_single_var: false, - } - } + borrow_spans.var_subdiag(&mut err, Some(gen_borrow_kind), |kind, var_span| { + use crate::session_diagnostics::CaptureVarCause::*; + match kind { + hir::ClosureKind::Coroutine(_) => BorrowUsePlaceCoroutine { + place: desc_place, + var_span, + is_single_var: false, + }, + hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => { + BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: false } } - }, - ); + } + }); } else { - issued_spans.var_subdiag( - self.dcx(), - &mut err, - Some(issued_borrow.kind), - |kind, var_span| { - use crate::session_diagnostics::CaptureVarCause::*; - let borrow_place = &issued_borrow.borrowed_place; - let borrow_place_desc = self.describe_any_place(borrow_place.as_ref()); - match kind { - hir::ClosureKind::Coroutine(_) => { - FirstBorrowUsePlaceCoroutine { place: borrow_place_desc, var_span } - } - hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => { - FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span } - } + issued_spans.var_subdiag(&mut err, Some(issued_borrow.kind), |kind, var_span| { + use crate::session_diagnostics::CaptureVarCause::*; + let borrow_place = &issued_borrow.borrowed_place; + let borrow_place_desc = self.describe_any_place(borrow_place.as_ref()); + match kind { + hir::ClosureKind::Coroutine(_) => { + FirstBorrowUsePlaceCoroutine { place: borrow_place_desc, var_span } } - }, - ); + hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => { + FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span } + } + } + }); - borrow_spans.var_subdiag( - self.dcx(), - &mut err, - Some(gen_borrow_kind), - |kind, var_span| { - use crate::session_diagnostics::CaptureVarCause::*; - match kind { - hir::ClosureKind::Coroutine(_) => { - SecondBorrowUsePlaceCoroutine { place: desc_place, var_span } - } - hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => { - SecondBorrowUsePlaceClosure { place: desc_place, var_span } - } + borrow_spans.var_subdiag(&mut err, Some(gen_borrow_kind), |kind, var_span| { + use crate::session_diagnostics::CaptureVarCause::*; + match kind { + hir::ClosureKind::Coroutine(_) => { + SecondBorrowUsePlaceCoroutine { place: desc_place, var_span } } - }, - ); + hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => { + SecondBorrowUsePlaceClosure { place: desc_place, var_span } + } + } + }); } if union_type_name != "" { @@ -1833,7 +1802,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let hir::ExprKind::MethodCall(..) = ex.kind && let Some(method_def_id) = self.typeck_results.type_dependent_def_id(ex.hir_id) - && self.tcx.lang_items().clone_trait() == Some(self.tcx.parent(method_def_id)) + && self.tcx.is_lang_item(self.tcx.parent(method_def_id), LangItem::Clone) { self.clones.push(ex); } @@ -2017,7 +1986,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } - pub(crate) fn find_expr(&self, span: Span) -> Option<&hir::Expr<'_>> { + pub(crate) fn find_expr(&self, span: Span) -> Option<&'tcx hir::Expr<'tcx>> { let tcx = self.infcx.tcx; let body_id = tcx.hir_node(self.mir_hir_id()).body_id()?; let mut expr_finder = FindExprBySpan::new(span, tcx); @@ -2889,7 +2858,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .. } = explanation { - if let Some(diag) = self.try_report_cannot_return_reference_to_local( + if let Err(diag) = self.try_report_cannot_return_reference_to_local( borrow, borrow_span, span, @@ -2962,7 +2931,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_label(borrow_span, "borrowed value does not live long enough"); err.span_label(drop_span, format!("`{name}` dropped here while still borrowed")); - borrow_spans.args_subdiag(self.dcx(), &mut err, |args_span| { + borrow_spans.args_subdiag(&mut err, |args_span| { crate::session_diagnostics::CaptureArgLabel::Capture { is_within: borrow_spans.for_coroutine(), args_span, @@ -3076,7 +3045,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } = explanation { - if let Some(diag) = self.try_report_cannot_return_reference_to_local( + if let Err(diag) = self.try_report_cannot_return_reference_to_local( borrow, proper_span, span, @@ -3160,8 +3129,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let is_format_arguments_item = if let Some(expr_ty) = expr_ty && let ty::Adt(adt, _) = expr_ty.kind() { - self.infcx.tcx.lang_items().get(LangItem::FormatArguments) - == Some(adt.did()) + self.infcx.tcx.is_lang_item(adt.did(), LangItem::FormatArguments) } else { false }; @@ -3221,7 +3189,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None, ); - borrow_spans.args_subdiag(self.dcx(), &mut err, |args_span| { + borrow_spans.args_subdiag(&mut err, |args_span| { crate::session_diagnostics::CaptureArgLabel::Capture { is_within: borrow_spans.for_coroutine(), args_span, @@ -3238,11 +3206,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { return_span: Span, category: ConstraintCategory<'tcx>, opt_place_desc: Option<&String>, - ) -> Option> { + ) -> Result<(), Diag<'tcx>> { let return_kind = match category { ConstraintCategory::Return(_) => "return", ConstraintCategory::Yield => "yield", - _ => return None, + _ => return Ok(()), }; // FIXME use a better heuristic than Spans @@ -3318,7 +3286,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - Some(err) + Err(err) } #[instrument(level = "debug", skip(self))] @@ -3343,6 +3311,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else if string.starts_with("gen") { // `gen` is 3 chars long Some(3) + } else if string.starts_with("static") { + // `static` is 6 chars long + // This is used for `!Unpin` coroutines + Some(6) } else { None }; @@ -3678,7 +3650,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { "assign", ); - loan_spans.var_subdiag(self.dcx(), &mut err, Some(loan.kind), |kind, var_span| { + loan_spans.var_subdiag(&mut err, Some(loan.kind), |kind, var_span| { use crate::session_diagnostics::CaptureVarCause::*; match kind { hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span }, @@ -3696,7 +3668,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place); - loan_spans.var_subdiag(self.dcx(), &mut err, Some(loan.kind), |kind, var_span| { + loan_spans.var_subdiag(&mut err, Some(loan.kind), |kind, var_span| { use crate::session_diagnostics::CaptureVarCause::*; match kind { hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span }, @@ -4369,13 +4341,14 @@ impl<'hir> Visitor<'hir> for BreakFinder { /// Given a set of spans representing statements initializing the relevant binding, visit all the /// function expressions looking for branching code paths that *do not* initialize the binding. -struct ConditionVisitor<'b> { +struct ConditionVisitor<'b, 'tcx> { + tcx: TyCtxt<'tcx>, spans: &'b [Span], name: &'b str, errors: Vec<(Span, String)>, } -impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> { +impl<'b, 'v, 'tcx> Visitor<'v> for ConditionVisitor<'b, 'tcx> { fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { match ex.kind { hir::ExprKind::If(cond, body, None) => { @@ -4461,6 +4434,12 @@ impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> { ), )); } else if let Some(guard) = &arm.guard { + if matches!( + self.tcx.hir_node(arm.body.hir_id), + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. }) + ) { + continue; + } self.errors.push(( arm.pat.span.to(guard.span), format!( @@ -4470,6 +4449,12 @@ impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> { ), )); } else { + if matches!( + self.tcx.hir_node(arm.body.hir_id), + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. }) + ) { + continue; + } self.errors.push(( arm.pat.span, format!( diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 88172c62a3b2..5b4269caccb5 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -4,14 +4,14 @@ use crate::session_diagnostics::{ CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause, CaptureVarKind, CaptureVarPathUseCause, OnClosureNote, }; +use rustc_errors::MultiSpan; use rustc_errors::{Applicability, Diag}; -use rustc_errors::{DiagCtxt, MultiSpan}; -use rustc_hir as hir; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::CoroutineKind; +use rustc_hir::{self as hir, LangItem}; use rustc_index::IndexSlice; use rustc_infer::infer::BoundRegionConversionTime; -use rustc_infer::traits::{FulfillmentErrorCode, SelectionError}; +use rustc_infer::traits::SelectionError; use rustc_middle::bug; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::{ @@ -29,7 +29,9 @@ use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{FieldIdx, VariantIdx}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; -use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; +use rustc_trait_selection::traits::{ + type_known_to_meet_bound_modulo_regions, FulfillmentErrorCode, +}; use crate::fluent_generated as fluent; @@ -114,7 +116,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { { if let ty::FnDef(id, _) = *const_.ty().kind() { debug!("add_moved_or_invoked_closure_note: id={:?}", id); - if Some(self.infcx.tcx.parent(id)) == self.infcx.tcx.lang_items().fn_once_trait() { + if self.infcx.tcx.is_lang_item(self.infcx.tcx.parent(id), LangItem::FnOnce) { let closure = match args.first() { Some(Spanned { node: Operand::Copy(place) | Operand::Move(place), .. @@ -128,16 +130,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() { let did = did.expect_local(); if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { - diag.subdiagnostic( - self.dcx(), - OnClosureNote::InvokedTwice { - place_name: &ty::place_to_string_for_capture( - self.infcx.tcx, - hir_place, - ), - span: *span, - }, - ); + diag.subdiagnostic(OnClosureNote::InvokedTwice { + place_name: &ty::place_to_string_for_capture( + self.infcx.tcx, + hir_place, + ), + span: *span, + }); return true; } } @@ -150,13 +149,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() { let did = did.expect_local(); if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { - diag.subdiagnostic( - self.dcx(), - OnClosureNote::MovedTwice { - place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place), - span: *span, - }, - ); + diag.subdiagnostic(OnClosureNote::MovedTwice { + place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place), + span: *span, + }); return true; } } @@ -589,14 +585,9 @@ impl UseSpans<'_> { /// Add a span label to the arguments of the closure, if it exists. #[allow(rustc::diagnostic_outside_of_impl)] - pub(super) fn args_subdiag( - self, - dcx: &DiagCtxt, - err: &mut Diag<'_>, - f: impl FnOnce(Span) -> CaptureArgLabel, - ) { + pub(super) fn args_subdiag(self, err: &mut Diag<'_>, f: impl FnOnce(Span) -> CaptureArgLabel) { if let UseSpans::ClosureUse { args_span, .. } = self { - err.subdiagnostic(dcx, f(args_span)); + err.subdiagnostic(f(args_span)); } } @@ -605,7 +596,6 @@ impl UseSpans<'_> { #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn var_path_only_subdiag( self, - dcx: &DiagCtxt, err: &mut Diag<'_>, action: crate::InitializationRequiringAction, ) { @@ -614,26 +604,20 @@ impl UseSpans<'_> { if let UseSpans::ClosureUse { closure_kind, path_span, .. } = self { match closure_kind { hir::ClosureKind::Coroutine(_) => { - err.subdiagnostic( - dcx, - match action { - Borrow => BorrowInCoroutine { path_span }, - MatchOn | Use => UseInCoroutine { path_span }, - Assignment => AssignInCoroutine { path_span }, - PartialAssignment => AssignPartInCoroutine { path_span }, - }, - ); + err.subdiagnostic(match action { + Borrow => BorrowInCoroutine { path_span }, + MatchOn | Use => UseInCoroutine { path_span }, + Assignment => AssignInCoroutine { path_span }, + PartialAssignment => AssignPartInCoroutine { path_span }, + }); } hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => { - err.subdiagnostic( - dcx, - match action { - Borrow => BorrowInClosure { path_span }, - MatchOn | Use => UseInClosure { path_span }, - Assignment => AssignInClosure { path_span }, - PartialAssignment => AssignPartInClosure { path_span }, - }, - ); + err.subdiagnostic(match action { + Borrow => BorrowInClosure { path_span }, + MatchOn | Use => UseInClosure { path_span }, + Assignment => AssignInClosure { path_span }, + PartialAssignment => AssignPartInClosure { path_span }, + }); } } } @@ -643,32 +627,28 @@ impl UseSpans<'_> { #[allow(rustc::diagnostic_outside_of_impl)] pub(super) fn var_subdiag( self, - dcx: &DiagCtxt, err: &mut Diag<'_>, kind: Option, f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause, ) { if let UseSpans::ClosureUse { closure_kind, capture_kind_span, path_span, .. } = self { if capture_kind_span != path_span { - err.subdiagnostic( - dcx, - match kind { - Some(kd) => match kd { - rustc_middle::mir::BorrowKind::Shared - | rustc_middle::mir::BorrowKind::Fake(_) => { - CaptureVarKind::Immut { kind_span: capture_kind_span } - } + err.subdiagnostic(match kind { + Some(kd) => match kd { + rustc_middle::mir::BorrowKind::Shared + | rustc_middle::mir::BorrowKind::Fake(_) => { + CaptureVarKind::Immut { kind_span: capture_kind_span } + } - rustc_middle::mir::BorrowKind::Mut { .. } => { - CaptureVarKind::Mut { kind_span: capture_kind_span } - } - }, - None => CaptureVarKind::Move { kind_span: capture_kind_span }, + rustc_middle::mir::BorrowKind::Mut { .. } => { + CaptureVarKind::Mut { kind_span: capture_kind_span } + } }, - ); + None => CaptureVarKind::Move { kind_span: capture_kind_span }, + }); }; let diag = f(closure_kind, path_span); - err.subdiagnostic(dcx, diag); + err.subdiagnostic(diag); } } @@ -765,13 +745,12 @@ impl<'tcx> BorrowedContentSource<'tcx> { ty::FnDef(def_id, args) => { let trait_id = tcx.trait_of_item(def_id)?; - let lang_items = tcx.lang_items(); - if Some(trait_id) == lang_items.deref_trait() - || Some(trait_id) == lang_items.deref_mut_trait() + if tcx.is_lang_item(trait_id, LangItem::Deref) + || tcx.is_lang_item(trait_id, LangItem::DerefMut) { Some(BorrowedContentSource::OverloadedDeref(args.type_at(0))) - } else if Some(trait_id) == lang_items.index_trait() - || Some(trait_id) == lang_items.index_mut_trait() + } else if tcx.is_lang_item(trait_id, LangItem::Index) + || tcx.is_lang_item(trait_id, LangItem::IndexMut) { Some(BorrowedContentSource::OverloadedIndex(args.type_at(0))) } else { @@ -1039,17 +1018,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .unwrap_or_else(|| "value".to_owned()); match kind { CallKind::FnCall { fn_trait_id, self_ty } - if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() => + if self.infcx.tcx.is_lang_item(fn_trait_id, LangItem::FnOnce) => { - err.subdiagnostic( - self.dcx(), - CaptureReasonLabel::Call { - fn_call_span, - place_name: &place_name, - is_partial, - is_loop_message, - }, - ); + err.subdiagnostic(CaptureReasonLabel::Call { + fn_call_span, + place_name: &place_name, + is_partial, + is_loop_message, + }); // Check if the move occurs on a value because of a call on a closure that comes // from a type parameter `F: FnOnce()`. If so, we provide a targeted `note`: // ``` @@ -1118,27 +1094,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); err.span_note(span, fluent::borrowck_moved_a_fn_once_in_call_call); } else { - err.subdiagnostic( - self.dcx(), - CaptureReasonNote::FnOnceMoveInCall { var_span }, - ); + err.subdiagnostic(CaptureReasonNote::FnOnceMoveInCall { var_span }); } } CallKind::Operator { self_arg, trait_id, .. } => { let self_arg = self_arg.unwrap(); - err.subdiagnostic( - self.dcx(), - CaptureReasonLabel::OperatorUse { - fn_call_span, - place_name: &place_name, - is_partial, - is_loop_message, - }, - ); + err.subdiagnostic(CaptureReasonLabel::OperatorUse { + fn_call_span, + place_name: &place_name, + is_partial, + is_loop_message, + }); if self.fn_self_span_reported.insert(fn_span) { let lang = self.infcx.tcx.lang_items(); err.subdiagnostic( - self.dcx(), if [lang.not_trait(), lang.deref_trait(), lang.neg_trait()] .contains(&Some(trait_id)) { @@ -1163,14 +1132,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); let func = tcx.def_path_str(method_did); - err.subdiagnostic( - self.dcx(), - CaptureReasonNote::FuncTakeSelf { - func, - place_name: place_name.clone(), - span: self_arg.span, - }, - ); + err.subdiagnostic(CaptureReasonNote::FuncTakeSelf { + func, + place_name: place_name.clone(), + span: self_arg.span, + }); } let parent_did = tcx.parent(method_did); let parent_self_ty = @@ -1184,10 +1150,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result)) }); if is_option_or_result && maybe_reinitialized_locations_is_empty { - err.subdiagnostic( - self.dcx(), - CaptureReasonLabel::BorrowContent { var_span }, - ); + err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span }); } if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring { let ty = moved_place.ty(self.body, tcx).ty; @@ -1201,24 +1164,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { _ => false, }; if suggest { - err.subdiagnostic( - self.dcx(), - CaptureReasonSuggest::IterateSlice { - ty, - span: move_span.shrink_to_lo(), - }, - ); + err.subdiagnostic(CaptureReasonSuggest::IterateSlice { + ty, + span: move_span.shrink_to_lo(), + }); } - err.subdiagnostic( - self.dcx(), - CaptureReasonLabel::ImplicitCall { - fn_call_span, - place_name: &place_name, - is_partial, - is_loop_message, - }, - ); + err.subdiagnostic(CaptureReasonLabel::ImplicitCall { + fn_call_span, + place_name: &place_name, + is_partial, + is_loop_message, + }); // If the moved place was a `&mut` ref, then we can // suggest to reborrow it where it was moved, so it // will still be valid by the time we get to the usage. @@ -1242,31 +1199,25 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } else { if let Some((CallDesugaringKind::Await, _)) = desugaring { - err.subdiagnostic( - self.dcx(), - CaptureReasonLabel::Await { - fn_call_span, - place_name: &place_name, - is_partial, - is_loop_message, - }, - ); + err.subdiagnostic(CaptureReasonLabel::Await { + fn_call_span, + place_name: &place_name, + is_partial, + is_loop_message, + }); } else { - err.subdiagnostic( - self.dcx(), - CaptureReasonLabel::MethodCall { - fn_call_span, - place_name: &place_name, - is_partial, - is_loop_message, - }, - ); + err.subdiagnostic(CaptureReasonLabel::MethodCall { + fn_call_span, + place_name: &place_name, + is_partial, + is_loop_message, + }); } // Erase and shadow everything that could be passed to the new infcx. let ty = moved_place.ty(self.body, tcx).ty; if let ty::Adt(def, args) = ty.peel_refs().kind() - && Some(def.did()) == tcx.lang_items().pin_type() + && tcx.is_lang_item(def.did(), LangItem::Pin) && let ty::Ref(_, _, hir::Mutability::Mut) = args.type_at(0).kind() && let self_ty = self.infcx.instantiate_binder_with_fresh_vars( fn_call_span, @@ -1275,12 +1226,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) && self.infcx.can_eq(self.param_env, ty, self_ty) { - err.subdiagnostic( - self.dcx(), - CaptureReasonSuggest::FreshReborrow { - span: move_span.shrink_to_hi(), - }, - ); + err.subdiagnostic(CaptureReasonSuggest::FreshReborrow { + span: move_span.shrink_to_hi(), + }); has_sugg = true; } if let Some(clone_trait) = tcx.lang_items().clone_trait() { @@ -1367,20 +1315,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } else { if move_span != span || is_loop_message { - err.subdiagnostic( - self.dcx(), - CaptureReasonLabel::MovedHere { - move_span, - is_partial, - is_move_msg, - is_loop_message, - }, - ); + err.subdiagnostic(CaptureReasonLabel::MovedHere { + move_span, + is_partial, + is_move_msg, + is_loop_message, + }); } // If the move error occurs due to a loop, don't show // another message for the same span if !is_loop_message { - move_spans.var_subdiag(self.dcx(), err, None, |kind, var_span| match kind { + move_spans.var_subdiag(err, None, |kind, var_span| match kind { hir::ClosureKind::Coroutine(_) => { CaptureVarCause::PartialMoveUseInCoroutine { var_span, is_partial } } diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 1d844c3d6a29..5a7bca9ab036 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -579,15 +579,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.suggest_cloning(err, place_ty, expr, self.find_expr(other_span), None); } - err.subdiagnostic( - self.dcx(), - crate::session_diagnostics::TypeNoCopy::Label { - is_partial_move: false, - ty: place_ty, - place: &place_desc, - span, - }, - ); + err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { + is_partial_move: false, + ty: place_ty, + place: &place_desc, + span, + }); } else { binds_to.sort(); binds_to.dedup(); @@ -620,17 +617,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ); } - err.subdiagnostic( - self.dcx(), - crate::session_diagnostics::TypeNoCopy::Label { - is_partial_move: false, - ty: place_ty, - place: &place_desc, - span: use_span, - }, - ); + err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { + is_partial_move: false, + ty: place_ty, + place: &place_desc, + span: use_span, + }); - use_spans.args_subdiag(self.dcx(), err, |args_span| { + use_spans.args_subdiag(err, |args_span| { crate::session_diagnostics::CaptureArgLabel::MoveOutPlace { place: place_desc, args_span, @@ -733,15 +727,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.suggest_cloning(err, bind_to.ty, expr, None, None); } - err.subdiagnostic( - self.dcx(), - crate::session_diagnostics::TypeNoCopy::Label { - is_partial_move: false, - ty: bind_to.ty, - place: place_desc, - span: binding_span, - }, - ); + err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { + is_partial_move: false, + ty: bind_to.ty, + place: place_desc, + span: binding_span, + }); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 35017b9e6b59..e0b18536dd56 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -6,10 +6,9 @@ use hir::{ExprKind, Param}; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, BindingMode, ByRef, Node}; -use rustc_infer::traits; use rustc_middle::bug; use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem}; -use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt, Upcast}; +use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, Upcast}; use rustc_middle::{ hir::place::PlaceBase, mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location}, @@ -18,6 +17,7 @@ use rustc_span::symbol::{kw, Symbol}; use rustc_span::{sym, BytePos, DesugaringKind, Span}; use rustc_target::abi::FieldIdx; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; use crate::diagnostics::BorrowedContentSource; @@ -230,7 +230,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } if suggest { borrow_spans.var_subdiag( - self.dcx(), &mut err, Some(mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }), |_kind, var_span| { @@ -647,8 +646,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let hir_map = self.infcx.tcx.hir(); let def_id = self.body.source.def_id(); let Some(local_def_id) = def_id.as_local() else { return }; - let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id) else { return }; - let body = self.infcx.tcx.hir().body(body_id); + let Some(body) = hir_map.maybe_body_owned_by(local_def_id) else { return }; let mut v = SuggestIndexOperatorAlternativeVisitor { assign_span: span, @@ -656,7 +654,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ty, suggested: false, }; - v.visit_body(body); + v.visit_body(&body); if !v.suggested { err.help(format!( "to modify a `{ty}`, use `.get_mut()`, `.insert()` or the entry API", @@ -746,9 +744,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // `fn foo(&x: &i32)` -> `fn foo(&(mut x): &i32)` let def_id = self.body.source.def_id(); if let Some(local_def_id) = def_id.as_local() - && let Some(body_id) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id) - && let body = self.infcx.tcx.hir().body(body_id) - && let Some(hir_id) = (BindingFinder { span: pat_span }).visit_body(body).break_value() + && let Some(body) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id) + && let Some(hir_id) = (BindingFinder { span: pat_span }).visit_body(&body).break_value() && let node = self.infcx.tcx.hir_node(hir_id) && let hir::Node::LetStmt(hir::LetStmt { pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. }, @@ -867,8 +864,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } } - if let Some(body_id) = hir_map.maybe_body_owned_by(self.mir_def_id()) - && let Block(block, _) = hir_map.body(body_id).value.kind + if let Some(body) = hir_map.maybe_body_owned_by(self.mir_def_id()) + && let Block(block, _) = body.value.kind { // `span` corresponds to the expression being iterated, find the `for`-loop desugared // expression with that span in order to identify potential fixes when encountering a @@ -939,60 +936,85 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let node = self.infcx.tcx.hir_node(fn_call_id); let def_id = hir.enclosing_body_owner(fn_call_id); let mut look_at_return = true; - // If we can detect the expression to be an `fn` call where the closure was an argument, - // we point at the `fn` definition argument... - if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Call(func, args), .. }) = node { - let arg_pos = args + + // If the HIR node is a function or method call gets the def ID + // of the called function or method and the span and args of the call expr + let get_call_details = || { + let hir::Node::Expr(hir::Expr { hir_id, kind, .. }) = node else { + return None; + }; + + let typeck_results = self.infcx.tcx.typeck(def_id); + + match kind { + hir::ExprKind::Call(expr, args) => { + if let Some(ty::FnDef(def_id, _)) = + typeck_results.node_type_opt(expr.hir_id).as_ref().map(|ty| ty.kind()) + { + Some((*def_id, expr.span, *args)) + } else { + None + } + } + hir::ExprKind::MethodCall(_, _, args, span) => { + if let Some(def_id) = typeck_results.type_dependent_def_id(*hir_id) { + Some((def_id, *span, *args)) + } else { + None + } + } + _ => None, + } + }; + + // If we can detect the expression to be an function or method call where the closure was an argument, + // we point at the function or method definition argument... + if let Some((callee_def_id, call_span, call_args)) = get_call_details() { + let arg_pos = call_args .iter() .enumerate() .filter(|(_, arg)| arg.hir_id == closure_id) .map(|(pos, _)| pos) .next(); - let tables = self.infcx.tcx.typeck(def_id); - if let Some(ty::FnDef(def_id, _)) = - tables.node_type_opt(func.hir_id).as_ref().map(|ty| ty.kind()) - { - let arg = match hir.get_if_local(*def_id) { - Some( - hir::Node::Item(hir::Item { - ident, kind: hir::ItemKind::Fn(sig, ..), .. + + let arg = match hir.get_if_local(callee_def_id) { + Some( + hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. }) + | hir::Node::TraitItem(hir::TraitItem { + ident, + kind: hir::TraitItemKind::Fn(sig, _), + .. + }) + | hir::Node::ImplItem(hir::ImplItem { + ident, + kind: hir::ImplItemKind::Fn(sig, _), + .. + }), + ) => Some( + arg_pos + .and_then(|pos| { + sig.decl.inputs.get( + pos + if sig.decl.implicit_self.has_implicit_self() { + 1 + } else { + 0 + }, + ) }) - | hir::Node::TraitItem(hir::TraitItem { - ident, - kind: hir::TraitItemKind::Fn(sig, _), - .. - }) - | hir::Node::ImplItem(hir::ImplItem { - ident, - kind: hir::ImplItemKind::Fn(sig, _), - .. - }), - ) => Some( - arg_pos - .and_then(|pos| { - sig.decl.inputs.get( - pos + if sig.decl.implicit_self.has_implicit_self() { - 1 - } else { - 0 - }, - ) - }) - .map(|arg| arg.span) - .unwrap_or(ident.span), - ), - _ => None, - }; - if let Some(span) = arg { - err.span_label(span, "change this to accept `FnMut` instead of `Fn`"); - err.span_label(func.span, "expects `Fn` instead of `FnMut`"); - err.span_label(closure_span, "in this closure"); - look_at_return = false; - } + .map(|arg| arg.span) + .unwrap_or(ident.span), + ), + _ => None, + }; + if let Some(span) = arg { + err.span_label(span, "change this to accept `FnMut` instead of `Fn`"); + err.span_label(call_span, "expects `Fn` instead of `FnMut`"); + err.span_label(closure_span, "in this closure"); + look_at_return = false; } } - if look_at_return && hir.get_return_block(closure_id).is_some() { + if look_at_return && hir.get_fn_id_for_return_block(closure_id).is_some() { // ...otherwise we are probably in the tail expression of the function, point at the // return type. match self.infcx.tcx.hir_node_by_def_id(hir.get_parent_item(fn_call_id).def_id) { @@ -1022,7 +1044,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fn suggest_using_iter_mut(&self, err: &mut Diag<'_>) { let source = self.body.source; let hir = self.infcx.tcx.hir(); - if let InstanceDef::Item(def_id) = source.instance + if let InstanceKind::Item(def_id) = source.instance && let Some(Node::Expr(hir::Expr { hir_id, kind, .. })) = hir.get_if_local(def_id) && let ExprKind::Closure(hir::Closure { kind: hir::ClosureKind::Closure, .. }) = kind && let Node::Expr(expr) = self.infcx.tcx.parent_hir_node(*hir_id) @@ -1189,10 +1211,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Some((false, err_label_span, message, _)) => { let def_id = self.body.source.def_id(); let hir_id = if let Some(local_def_id) = def_id.as_local() - && let Some(body_id) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id) + && let Some(body) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id) { - let body = self.infcx.tcx.hir().body(body_id); - BindingFinder { span: err_label_span }.visit_body(body).break_value() + BindingFinder { span: err_label_span }.visit_body(&body).break_value() } else { None }; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 8112fb7b89c6..67c11ff4a5bd 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -361,6 +361,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region); let diag = unexpected_hidden_region_diagnostic( self.infcx.tcx, + self.mir_def_id(), span, named_ty, named_region, @@ -453,7 +454,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // Check if we can use one of the "nice region errors". if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { let infer_err = self.infcx.err_ctxt(); - let nice = NiceRegionError::new_from_span(&infer_err, cause.span, o, f); + let nice = + NiceRegionError::new_from_span(&infer_err, self.mir_def_id(), cause.span, o, f); if let Some(diag) = nice.try_report_from_nll() { self.buffer_error(diag); return; @@ -629,13 +631,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let upvars_map = self.infcx.tcx.upvars_mentioned(def_id).unwrap(); let upvar_def_span = self.infcx.tcx.hir().span(def_hir); let upvar_span = upvars_map.get(&def_hir).unwrap().span; - diag.subdiagnostic(self.dcx(), VarHereDenote::Defined { span: upvar_def_span }); - diag.subdiagnostic(self.dcx(), VarHereDenote::Captured { span: upvar_span }); + diag.subdiagnostic(VarHereDenote::Defined { span: upvar_def_span }); + diag.subdiagnostic(VarHereDenote::Captured { span: upvar_span }); } } if let Some(fr_span) = self.give_region_a_name(*outlived_fr).unwrap().span() { - diag.subdiagnostic(self.dcx(), VarHereDenote::FnMutInferred { span: fr_span }); + diag.subdiagnostic(VarHereDenote::FnMutInferred { span: fr_span }); } self.suggest_move_on_borrowing_closure(&mut diag); @@ -808,7 +810,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }, }; - diag.subdiagnostic(self.dcx(), err_category); + diag.subdiagnostic(err_category); self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr); self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr); @@ -843,14 +845,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if *outlived_f != ty::ReStatic { return; } - let suitable_region = self.infcx.tcx.is_suitable_region(f); + let suitable_region = self.infcx.tcx.is_suitable_region(self.mir_def_id(), f); let Some(suitable_region) = suitable_region else { return; }; let fn_returns = self.infcx.tcx.return_type_impl_or_dyn_traits(suitable_region.def_id); - let param = if let Some(param) = find_param_with_region(self.infcx.tcx, f, outlived_f) { + let param = if let Some(param) = + find_param_with_region(self.infcx.tcx, self.mir_def_id(), f, outlived_f) + { param } else { return; @@ -959,7 +963,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { return; }; - let param = match find_param_with_region(tcx, f, o) { + let param = match find_param_with_region(tcx, self.mir_def_id(), f, o) { Some(param) => param, None => return, }; @@ -1004,7 +1008,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ident.span, "calling this method introduces the `impl`'s `'static` requirement", ); - err.subdiagnostic(self.dcx(), RequireStaticErr::UsedImpl { multi_span }); + err.subdiagnostic(RequireStaticErr::UsedImpl { multi_span }); err.span_suggestion_verbose( span.shrink_to_hi(), "consider relaxing the implicit `'static` requirement", @@ -1022,25 +1026,30 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { return; }; - let Some((ty_sub, _)) = self - .infcx - .tcx - .is_suitable_region(sub) - .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sub, &anon_reg.bound_region)) + let Some((ty_sub, _)) = + self.infcx.tcx.is_suitable_region(self.mir_def_id(), sub).and_then(|anon_reg| { + find_anon_type(self.infcx.tcx, self.mir_def_id(), sub, &anon_reg.bound_region) + }) else { return; }; - let Some((ty_sup, _)) = self - .infcx - .tcx - .is_suitable_region(sup) - .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sup, &anon_reg.bound_region)) + let Some((ty_sup, _)) = + self.infcx.tcx.is_suitable_region(self.mir_def_id(), sup).and_then(|anon_reg| { + find_anon_type(self.infcx.tcx, self.mir_def_id(), sup, &anon_reg.bound_region) + }) else { return; }; - suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag); + suggest_adding_lifetime_params( + self.infcx.tcx, + diag, + self.mir_def_id(), + sub, + ty_sup, + ty_sub, + ); } #[allow(rustc::diagnostic_outside_of_impl)] @@ -1174,8 +1183,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn suggest_move_on_borrowing_closure(&self, diag: &mut Diag<'_>) { let map = self.infcx.tcx.hir(); - let body_id = map.body_owned_by(self.mir_def_id()); - let expr = &map.body(body_id).value.peel_blocks(); + let body = map.body_owned_by(self.mir_def_id()); + let expr = &body.value.peel_blocks(); let mut closure_span = None::; match expr.kind { hir::ExprKind::MethodCall(.., args, _) => { diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 46011c1f43ec..77fb9fb43151 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -12,7 +12,7 @@ use rustc_middle::ty::print::RegionHighlightMode; use rustc_middle::ty::{self, RegionVid, Ty}; use rustc_middle::ty::{GenericArgKind, GenericArgsRef}; use rustc_middle::{bug, span_bug}; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; use crate::{universal_regions::DefiningTy, MirBorrowckCtxt}; @@ -289,7 +289,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { debug!("give_region_a_name: error_region = {:?}", error_region); match *error_region { ty::ReEarlyParam(ebr) => ebr.has_name().then(|| { - let span = tcx.hir().span_if_local(ebr.def_id).unwrap_or(DUMMY_SP); + let def_id = tcx.generics_of(self.mir_def_id()).region_param(ebr, tcx).def_id; + let span = tcx.hir().span_if_local(def_id).unwrap_or(DUMMY_SP); RegionName { name: ebr.name, source: RegionNameSource::NamedEarlyParamRegion(span) } }), @@ -627,9 +628,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { | GenericArgKind::Const(_), _, ) => { - // This was previously a `span_delayed_bug` and could be - // reached by the test for #82126, but no longer. - self.dcx().span_bug( + self.dcx().span_delayed_bug( hir_arg.span(), format!("unmatched arg and hir arg: found {kind:?} vs {hir_arg:?}"), ); @@ -842,13 +841,9 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { }) = opaque_ty.kind && let Some(segment) = trait_ref.trait_ref.path.segments.last() && let Some(args) = segment.args - && let [ - hir::TypeBinding { - ident: Ident { name: sym::Output, .. }, - kind: hir::TypeBindingKind::Equality { term: hir::Term::Ty(ty) }, - .. - }, - ] = args.bindings + && let [constraint] = args.constraints + && constraint.ident.name == sym::Output + && let Some(ty) = constraint.ty() { ty } else { @@ -912,7 +907,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { }; let tcx = self.infcx.tcx; - let region_parent = tcx.parent(region.def_id); + let region_def = tcx.generics_of(self.mir_def_id()).region_param(region, tcx).def_id; + let region_parent = tcx.parent(region_def); let DefKind::Impl { .. } = tcx.def_kind(region_parent) else { return None; }; @@ -925,7 +921,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { Some(RegionName { name: self.synthesize_region_name(), source: RegionNameSource::AnonRegionFromImplSignature( - tcx.def_span(region.def_id), + tcx.def_span(region_def), // FIXME(compiler-errors): Does this ever actually show up // anywhere other than the self type? I couldn't create an // example of a `'_` in the impl's trait being referenceable. diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs index e7faec7bbac1..51ea59e2092a 100644 --- a/compiler/rustc_borrowck/src/facts.rs +++ b/compiler/rustc_borrowck/src/facts.rs @@ -15,8 +15,31 @@ use std::path::Path; #[derive(Copy, Clone, Debug)] pub struct RustcFacts; +rustc_index::newtype_index! { + /// A (kinda) newtype of `RegionVid` so we can implement `Atom` on it. + #[orderable] + #[debug_format = "'?{}"] + pub struct PoloniusRegionVid {} +} + +impl polonius_engine::Atom for PoloniusRegionVid { + fn index(self) -> usize { + self.as_usize() + } +} +impl From for PoloniusRegionVid { + fn from(value: RegionVid) -> Self { + Self::from_usize(value.as_usize()) + } +} +impl From for RegionVid { + fn from(value: PoloniusRegionVid) -> Self { + Self::from_usize(value.as_usize()) + } +} + impl polonius_engine::FactTypes for RustcFacts { - type Origin = RegionVid; + type Origin = PoloniusRegionVid; type Loan = BorrowIndex; type Point = LocationIndex; type Variable = Local; @@ -119,7 +142,7 @@ trait FactRow { ) -> Result<(), Box>; } -impl FactRow for RegionVid { +impl FactRow for PoloniusRegionVid { fn write( &self, out: &mut dyn Write, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index abe57e26af46..b3b53e9cb79e 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1,7 +1,7 @@ //! This query borrow-checks the MIR to (further) ensure it is not broken. +// tidy-alphabetical-start #![allow(internal_features)] -#![feature(rustdoc_internals)] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] @@ -10,8 +10,10 @@ #![feature(min_specialization)] #![feature(never_type)] #![feature(rustc_attrs)] +#![feature(rustdoc_internals)] #![feature(stmt_expr_attributes)] #![feature(try_blocks)] +// tidy-alphabetical-end #[macro_use] extern crate tracing; @@ -564,7 +566,7 @@ struct MirBorrowckCtxt<'cx, 'tcx> { fn_self_span_reported: FxIndexSet, /// This field keeps track of errors reported in the checking of uninitialized variables, /// so that we don't report seemingly duplicate errors. - uninitialized_error_reported: FxIndexSet>, + uninitialized_error_reported: FxIndexSet, /// This field keeps track of all the local variables that are declared mut and are mutated. /// Used for the warning issued by an unused mutable local variable. used_mut: FxIndexSet, @@ -1312,8 +1314,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } - Rvalue::BinaryOp(_bin_op, box (operand1, operand2)) - | Rvalue::CheckedBinaryOp(_bin_op, box (operand1, operand2)) => { + Rvalue::BinaryOp(_bin_op, box (operand1, operand2)) => { self.consume_operand(location, (operand1, span), flow_state); self.consume_operand(location, (operand2, span), flow_state); } diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 49f50babdcb9..5d7ce548469f 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -125,8 +125,8 @@ pub(crate) fn compute_regions<'cx, 'tcx>( placeholder_indices, placeholder_index_to_region: _, liveness_constraints, - outlives_constraints, - member_constraints, + mut outlives_constraints, + mut member_constraints, universe_causes, type_tests, } = constraints; @@ -144,6 +144,16 @@ pub(crate) fn compute_regions<'cx, 'tcx>( &universal_region_relations, ); + if let Some(guar) = universal_regions.tainted_by_errors() { + // Suppress unhelpful extra errors in `infer_opaque_types` by clearing out all + // outlives bounds that we may end up checking. + outlives_constraints = Default::default(); + member_constraints = Default::default(); + + // Also taint the entire scope. + infcx.set_tainted_by_errors(guar); + } + let mut regioncx = RegionInferenceContext::new( infcx, var_origins, diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs index a1e59977ede5..6979910a02d7 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs @@ -302,8 +302,7 @@ impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> { ); } - Rvalue::BinaryOp(_bin_op, box (operand1, operand2)) - | Rvalue::CheckedBinaryOp(_bin_op, box (operand1, operand2)) => { + Rvalue::BinaryOp(_bin_op, box (operand1, operand2)) => { self.consume_operand(location, operand1); self.consume_operand(location, operand2); } diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 40126d50d57e..9984f76e6d44 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData}; use crate::borrow_set::BorrowSet; -use crate::facts::AllFacts; +use crate::facts::{AllFacts, PoloniusRegionVid}; use crate::location::LocationTable; use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::universal_regions::UniversalRegions; @@ -137,7 +137,9 @@ fn emit_universal_region_facts( // the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index // added to the existing number of loans, as if they succeeded them in the set. // - all_facts.universal_region.extend(universal_regions.universal_regions()); + all_facts + .universal_region + .extend(universal_regions.universal_regions().map(PoloniusRegionVid::from)); let borrow_count = borrow_set.len(); debug!( "emit_universal_region_facts: polonius placeholders, num_universals={}, borrow_count={}", @@ -148,7 +150,7 @@ fn emit_universal_region_facts( for universal_region in universal_regions.universal_regions() { let universal_region_idx = universal_region.index(); let placeholder_loan_idx = borrow_count + universal_region_idx; - all_facts.placeholder.push((universal_region, placeholder_loan_idx.into())); + all_facts.placeholder.push((universal_region.into(), placeholder_loan_idx.into())); } // 2: the universal region relations `outlives` constraints are emitted as @@ -160,7 +162,7 @@ fn emit_universal_region_facts( fr1={:?}, fr2={:?}", fr1, fr2 ); - all_facts.known_placeholder_subset.push((fr1, fr2)); + all_facts.known_placeholder_subset.push((fr1.into(), fr2.into())); } } } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 167ca7ba0457..40b585005982 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -4,10 +4,10 @@ use std::rc::Rc; use rustc_data_structures::binary_search_util; use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_data_structures::graph::scc::Sccs; +use rustc_data_structures::graph::scc::{self, Sccs}; use rustc_errors::Diag; use rustc_hir::def_id::CRATE_DEF_ID; -use rustc_index::{IndexSlice, IndexVec}; +use rustc_index::IndexVec; use rustc_infer::infer::outlives::test_type_match; use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq}; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin}; @@ -19,7 +19,7 @@ use rustc_middle::mir::{ }; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::ObligationCauseCode; -use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex}; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_span::Span; @@ -46,6 +46,97 @@ mod reverse_sccs; pub mod values; +pub type ConstraintSccs = Sccs; + +/// An annotation for region graph SCCs that tracks +/// the values of its elements. +#[derive(Copy, Debug, Clone)] +pub struct RegionTracker { + /// The largest universe of a placeholder reached from this SCC. + /// This includes placeholders within this SCC. + max_placeholder_universe_reached: UniverseIndex, + + /// The smallest universe index reachable form the nodes of this SCC. + min_reachable_universe: UniverseIndex, + + /// The representative Region Variable Id for this SCC. We prefer + /// placeholders over existentially quantified variables, otherwise + /// it's the one with the smallest Region Variable ID. + representative: RegionVid, + + /// Is the current representative a placeholder? + representative_is_placeholder: bool, + + /// Is the current representative existentially quantified? + representative_is_existential: bool, +} + +impl scc::Annotation for RegionTracker { + fn merge_scc(mut self, mut other: Self) -> Self { + // Prefer any placeholder over any existential + if other.representative_is_placeholder && self.representative_is_existential { + other.merge_min_max_seen(&self); + return other; + } + + if self.representative_is_placeholder && other.representative_is_existential + || (self.representative <= other.representative) + { + self.merge_min_max_seen(&other); + return self; + } + other.merge_min_max_seen(&self); + other + } + + fn merge_reached(mut self, other: Self) -> Self { + // No update to in-component values, only add seen values. + self.merge_min_max_seen(&other); + self + } +} + +impl RegionTracker { + fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { + let (representative_is_placeholder, representative_is_existential) = match definition.origin + { + rustc_infer::infer::NllRegionVariableOrigin::FreeRegion => (false, false), + rustc_infer::infer::NllRegionVariableOrigin::Placeholder(_) => (true, false), + rustc_infer::infer::NllRegionVariableOrigin::Existential { .. } => (false, true), + }; + + let placeholder_universe = + if representative_is_placeholder { definition.universe } else { UniverseIndex::ROOT }; + + Self { + max_placeholder_universe_reached: placeholder_universe, + min_reachable_universe: definition.universe, + representative: rvid, + representative_is_placeholder, + representative_is_existential, + } + } + fn universe(self) -> UniverseIndex { + self.min_reachable_universe + } + + fn merge_min_max_seen(&mut self, other: &Self) { + self.max_placeholder_universe_reached = std::cmp::max( + self.max_placeholder_universe_reached, + other.max_placeholder_universe_reached, + ); + + self.min_reachable_universe = + std::cmp::min(self.min_reachable_universe, other.min_reachable_universe); + } + + /// Returns `true` if during the annotated SCC reaches a placeholder + /// with a universe larger than the smallest reachable one, `false` otherwise. + pub fn has_incompatible_universes(&self) -> bool { + self.universe().cannot_name(self.max_placeholder_universe_reached) + } +} + pub struct RegionInferenceContext<'tcx> { pub var_infos: VarInfos, @@ -72,7 +163,7 @@ pub struct RegionInferenceContext<'tcx> { /// The SCC computed from `constraints` and the constraint /// graph. We have an edge from SCC A to SCC B if `A: B`. Used to /// compute the values of each region. - constraint_sccs: Rc>, + constraint_sccs: Rc, /// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if /// `B: A`. This is used to compute the universal regions that are required @@ -91,22 +182,6 @@ pub struct RegionInferenceContext<'tcx> { /// Map universe indexes to information on why we created it. universe_causes: FxIndexMap>, - /// Contains the minimum universe of any variable within the same - /// SCC. We will ensure that no SCC contains values that are not - /// visible from this index. - scc_universes: IndexVec, - - /// Contains the "representative" region of each SCC. - /// It is defined as the one with the minimal RegionVid, favoring - /// free regions, then placeholders, then existential regions. - /// - /// It is a hacky way to manage checking regions for equality, - /// since we can 'canonicalize' each region to the representative - /// of its SCC and be sure that -- if they have the same repr -- - /// they *must* be equal (though not having the same repr does not - /// mean they are unequal). - scc_representatives: IndexVec, - /// The final inferred values of the region variables; we compute /// one value per SCC. To get the value for any given *region*, /// you first find which scc it is a part of. @@ -151,7 +226,7 @@ pub(crate) struct AppliedMemberConstraint { } #[derive(Debug)] -pub(crate) struct RegionDefinition<'tcx> { +pub struct RegionDefinition<'tcx> { /// What kind of variable is this -- a free region? existential /// variable? etc. (See the `NllRegionVariableOrigin` for more /// info.) @@ -250,7 +325,7 @@ pub enum ExtraConstraintInfo { } #[instrument(skip(infcx, sccs), level = "debug")] -fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: Rc>) { +fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) { use crate::renumber::RegionCtxt; let var_to_origin = infcx.reg_var_to_origin.borrow(); @@ -264,7 +339,7 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: Rc(infcx: &BorrowckInferCtxt<'tcx>, sccs: Rc>(); + let edge_representatives = sccs + .successors(*scc_idx) + .iter() + .map(|scc_idx| components_representatives[scc_idx]) + .collect::>(); scc_node_to_edges.insert((scc_idx, repr), edge_representatives); } @@ -320,7 +396,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// The `outlives_constraints` and `type_tests` are an initial set /// of constraints produced by the MIR type check. pub(crate) fn new( - _infcx: &BorrowckInferCtxt<'tcx>, + infcx: &BorrowckInferCtxt<'tcx>, var_infos: VarInfos, universal_regions: Rc>, placeholder_indices: Rc, @@ -343,13 +419,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { .map(|info| RegionDefinition::new(info.universe, info.origin)) .collect(); + let fr_static = universal_regions.fr_static; let constraints = Frozen::freeze(outlives_constraints); let constraint_graph = Frozen::freeze(constraints.graph(definitions.len())); - let fr_static = universal_regions.fr_static; - let constraint_sccs = Rc::new(constraints.compute_sccs(&constraint_graph, fr_static)); + let constraint_sccs = { + let constraint_graph = constraints.graph(definitions.len()); + let region_graph = &constraint_graph.region_graph(&constraints, fr_static); + let sccs = ConstraintSccs::new_with_annotation(®ion_graph, |r| { + RegionTracker::new(r, &definitions[r]) + }); + Rc::new(sccs) + }; if cfg!(debug_assertions) { - sccs_info(_infcx, constraint_sccs.clone()); + sccs_info(infcx, &constraint_sccs); } let mut scc_values = @@ -360,10 +443,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { scc_values.merge_liveness(scc, region, &liveness_constraints); } - let scc_universes = Self::compute_scc_universes(&constraint_sccs, &definitions); - - let scc_representatives = Self::compute_scc_representatives(&constraint_sccs, &definitions); - let member_constraints = Rc::new(member_constraints_in.into_mapped(|r| constraint_sccs.scc(r))); @@ -378,8 +457,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { member_constraints, member_constraints_applied: Vec::new(), universe_causes, - scc_universes, - scc_representatives, scc_values, type_tests, universal_regions, @@ -391,123 +468,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { result } - /// Each SCC is the combination of many region variables which - /// have been equated. Therefore, we can associate a universe with - /// each SCC which is minimum of all the universes of its - /// constituent regions -- this is because whatever value the SCC - /// takes on must be a value that each of the regions within the - /// SCC could have as well. This implies that the SCC must have - /// the minimum, or narrowest, universe. - fn compute_scc_universes( - constraint_sccs: &Sccs, - definitions: &IndexSlice>, - ) -> IndexVec { - let num_sccs = constraint_sccs.num_sccs(); - let mut scc_universes = IndexVec::from_elem_n(ty::UniverseIndex::MAX, num_sccs); - - debug!("compute_scc_universes()"); - - // For each region R in universe U, ensure that the universe for the SCC - // that contains R is "no bigger" than U. This effectively sets the universe - // for each SCC to be the minimum of the regions within. - for (region_vid, region_definition) in definitions.iter_enumerated() { - let scc = constraint_sccs.scc(region_vid); - let scc_universe = &mut scc_universes[scc]; - let scc_min = std::cmp::min(region_definition.universe, *scc_universe); - if scc_min != *scc_universe { - *scc_universe = scc_min; - debug!( - "compute_scc_universes: lowered universe of {scc:?} to {scc_min:?} \ - because it contains {region_vid:?} in {region_universe:?}", - scc = scc, - scc_min = scc_min, - region_vid = region_vid, - region_universe = region_definition.universe, - ); - } - } - - // Walk each SCC `A` and `B` such that `A: B` - // and ensure that universe(A) can see universe(B). - // - // This serves to enforce the 'empty/placeholder' hierarchy - // (described in more detail on `RegionKind`): - // - // ``` - // static -----+ - // | | - // empty(U0) placeholder(U1) - // | / - // empty(U1) - // ``` - // - // In particular, imagine we have variables R0 in U0 and R1 - // created in U1, and constraints like this; - // - // ``` - // R1: !1 // R1 outlives the placeholder in U1 - // R1: R0 // R1 outlives R0 - // ``` - // - // Here, we wish for R1 to be `'static`, because it - // cannot outlive `placeholder(U1)` and `empty(U0)` any other way. - // - // Thanks to this loop, what happens is that the `R1: R0` - // constraint lowers the universe of `R1` to `U0`, which in turn - // means that the `R1: !1` constraint will (later) cause - // `R1` to become `'static`. - for scc_a in constraint_sccs.all_sccs() { - for &scc_b in constraint_sccs.successors(scc_a) { - let scc_universe_a = scc_universes[scc_a]; - let scc_universe_b = scc_universes[scc_b]; - let scc_universe_min = std::cmp::min(scc_universe_a, scc_universe_b); - if scc_universe_a != scc_universe_min { - scc_universes[scc_a] = scc_universe_min; - - debug!( - "compute_scc_universes: lowered universe of {scc_a:?} to {scc_universe_min:?} \ - because {scc_a:?}: {scc_b:?} and {scc_b:?} is in universe {scc_universe_b:?}", - scc_a = scc_a, - scc_b = scc_b, - scc_universe_min = scc_universe_min, - scc_universe_b = scc_universe_b - ); - } - } - } - - debug!("compute_scc_universes: scc_universe = {:#?}", scc_universes); - - scc_universes - } - - /// For each SCC, we compute a unique `RegionVid`. See the - /// `scc_representatives` field of `RegionInferenceContext` for - /// more details. - fn compute_scc_representatives( - constraints_scc: &Sccs, - definitions: &IndexSlice>, - ) -> IndexVec { - let num_sccs = constraints_scc.num_sccs(); - let mut scc_representatives = IndexVec::from_elem_n(RegionVid::MAX, num_sccs); - - // Iterate over all RegionVids *in-order* and pick the least RegionVid as the - // representative of its SCC. This naturally prefers free regions over others. - for (vid, def) in definitions.iter_enumerated() { - let repr = &mut scc_representatives[constraints_scc.scc(vid)]; - if *repr == ty::RegionVid::MAX { - *repr = vid; - } else if matches!(def.origin, NllRegionVariableOrigin::Placeholder(_)) - && matches!(definitions[*repr].origin, NllRegionVariableOrigin::Existential { .. }) - { - // Pick placeholders over existentials even if they have a greater RegionVid. - *repr = vid; - } - } - - scc_representatives - } - /// Initializes the region variables for each universally /// quantified region (lifetime parameter). The first N variables /// always correspond to the regions appearing in the function @@ -528,12 +488,45 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// and (b) any universally quantified regions that it outlives, /// which in this case is just itself. R1 (`'b`) in contrast also /// outlives `'a` and hence contains R0 and R1. + /// + /// This bit of logic also handles invalid universe relations + /// for higher-kinded types. + /// + /// We Walk each SCC `A` and `B` such that `A: B` + /// and ensure that universe(A) can see universe(B). + /// + /// This serves to enforce the 'empty/placeholder' hierarchy + /// (described in more detail on `RegionKind`): + /// + /// ```ignore (illustrative) + /// static -----+ + /// | | + /// empty(U0) placeholder(U1) + /// | / + /// empty(U1) + /// ``` + /// + /// In particular, imagine we have variables R0 in U0 and R1 + /// created in U1, and constraints like this; + /// + /// ```ignore (illustrative) + /// R1: !1 // R1 outlives the placeholder in U1 + /// R1: R0 // R1 outlives R0 + /// ``` + /// + /// Here, we wish for R1 to be `'static`, because it + /// cannot outlive `placeholder(U1)` and `empty(U0)` any other way. + /// + /// Thanks to this loop, what happens is that the `R1: R0` + /// constraint has lowered the universe of `R1` to `U0`, which in turn + /// means that the `R1: !1` constraint here will cause + /// `R1` to become `'static`. fn init_free_and_bound_regions(&mut self) { // Update the names (if any) // This iterator has unstable order but we collect it all into an IndexVec for (external_name, variable) in self.universal_regions.named_universal_regions() { debug!( - "init_universal_regions: region {:?} has external name {:?}", + "init_free_and_bound_regions: region {:?} has external name {:?}", variable, external_name ); self.definitions[variable].external_name = Some(external_name); @@ -559,7 +552,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // its universe `ui` and its extensions. So we // can't just add it into `scc` unless the // universe of the scc can name this region. - let scc_universe = self.scc_universes[scc]; + let scc_universe = self.scc_universe(scc); if scc_universe.can_name(placeholder.universe) { self.scc_values.add_element(scc, placeholder); } else { @@ -640,8 +633,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Returns access to the value of `r` for debugging purposes. pub(crate) fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex { - let scc = self.constraint_sccs.scc(r); - self.scc_universes[scc] + self.scc_universe(self.constraint_sccs.scc(r)) } /// Once region solving has completed, this function will return the member constraints that @@ -737,8 +729,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // SCC. For each SCC, we visit its successors and compute // their values, then we union all those values to get our // own. - let constraint_sccs = self.constraint_sccs.clone(); - for scc in constraint_sccs.all_sccs() { + for scc in self.constraint_sccs.all_sccs() { self.compute_value_for_scc(scc); } @@ -817,20 +808,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { // if one exists. for c_r in &mut choice_regions { let scc = self.constraint_sccs.scc(*c_r); - *c_r = self.scc_representatives[scc]; + *c_r = self.scc_representative(scc); } // If the member region lives in a higher universe, we currently choose // the most conservative option by leaving it unchanged. - if self.scc_universes[scc] != ty::UniverseIndex::ROOT { + + if !self.constraint_sccs().annotation(scc).universe().is_root() { return; } - debug_assert!( - self.scc_values.placeholders_contained_in(scc).next().is_none(), - "scc {:?} in a member constraint has placeholder value: {:?}", - scc, - self.scc_values.region_value_str(scc), - ); // The existing value for `scc` is a lower-bound. This will // consist of some set `{P} + {LB}` of points `{P}` and @@ -900,12 +886,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// in `scc_a`. Used during constraint propagation, and only once /// the value of `scc_b` has been computed. fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool { - let universe_a = self.scc_universes[scc_a]; + let universe_a = self.constraint_sccs().annotation(scc_a).universe(); + let universe_b = self.constraint_sccs().annotation(scc_b).universe(); // Quick check: if scc_b's declared universe is a subset of // scc_a's declared universe (typically, both are ROOT), then // it cannot contain any problematic universe elements. - if universe_a.can_name(self.scc_universes[scc_b]) { + if universe_a.can_name(universe_b) { return true; } @@ -1033,7 +1020,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!( "lower_bound = {:?} r_scc={:?} universe={:?}", - lower_bound, r_scc, self.scc_universes[r_scc] + lower_bound, + r_scc, + self.constraint_sccs.annotation(r_scc).universe() ); // If the type test requires that `T: 'a` where `'a` is a @@ -1321,7 +1310,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { tcx.fold_regions(value, |r, _db| { let vid = self.to_region_vid(r); let scc = self.constraint_sccs.scc(vid); - let repr = self.scc_representatives[scc]; + let repr = self.scc_representative(scc); ty::Region::new_var(tcx, repr) }) } @@ -1506,7 +1495,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { subset_errors.sort(); subset_errors.dedup(); - for (longer_fr, shorter_fr) in subset_errors.into_iter() { + for &(longer_fr, shorter_fr) in subset_errors.into_iter() { debug!( "check_polonius_subset_errors: subset_error longer_fr={:?},\ shorter_fr={:?}", @@ -1514,14 +1503,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); let propagated = self.try_propagate_universal_region_error( - *longer_fr, - *shorter_fr, + longer_fr.into(), + shorter_fr.into(), &mut propagated_outlives_requirements, ); if propagated == RegionRelationCheckResult::Error { errors_buffer.push(RegionErrorKind::RegionError { - longer_fr: *longer_fr, - shorter_fr: *shorter_fr, + longer_fr: longer_fr.into(), + shorter_fr: shorter_fr.into(), fr_origin: NllRegionVariableOrigin::FreeRegion, is_reported: true, }); @@ -1547,6 +1536,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } + /// The minimum universe of any variable reachable from this + /// SCC, inside or outside of it. + fn scc_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex { + self.constraint_sccs().annotation(scc).universe() + } /// Checks the final value for the free region `fr` to see if it /// grew too large. In particular, examine what `end(X)` points /// wound up in `fr`'s final value; for each `end(X)` where `X != @@ -1566,8 +1560,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Because this free region must be in the ROOT universe, we // know it cannot contain any bound universes. - assert!(self.scc_universes[longer_fr_scc].is_root()); - debug_assert!(self.scc_values.placeholders_contained_in(longer_fr_scc).next().is_none()); + assert!(self.scc_universe(longer_fr_scc).is_root()); // Only check all of the relations for the main representative of each // SCC, otherwise just check that we outlive said representative. This @@ -1575,7 +1568,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // closures. // Note that the representative will be a universal region if there is // one in this SCC, so we will always check the representative here. - let representative = self.scc_representatives[longer_fr_scc]; + let representative = self.scc_representative(longer_fr_scc); if representative != longer_fr { if let RegionRelationCheckResult::Error = self.check_universal_region_relation( longer_fr, @@ -1796,16 +1789,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// `true` if `r1` cannot name that placeholder in its /// value; otherwise, returns `false`. pub(crate) fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool { - debug!("cannot_name_value_of(r1={:?}, r2={:?})", r1, r2); - match self.definitions[r2].origin { NllRegionVariableOrigin::Placeholder(placeholder) => { - let universe1 = self.definitions[r1].universe; + let r1_universe = self.definitions[r1].universe; debug!( - "cannot_name_value_of: universe1={:?} placeholder={:?}", - universe1, placeholder + "cannot_name_value_of: universe1={r1_universe:?} placeholder={:?}", + placeholder ); - universe1.cannot_name(placeholder.universe) + r1_universe.cannot_name(placeholder.universe) } NllRegionVariableOrigin::FreeRegion | NllRegionVariableOrigin::Existential { .. } => { @@ -1835,6 +1826,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// /// Returns: a series of constraints as well as the region `R` /// that passed the target test. + #[instrument(skip(self, target_test), ret)] pub(crate) fn find_constraint_paths_between_regions( &self, from_region: RegionVid, @@ -1932,7 +1924,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { #[instrument(skip(self), level = "trace", ret)] pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid { trace!(scc = ?self.constraint_sccs.scc(fr1)); - trace!(universe = ?self.scc_universes[self.constraint_sccs.scc(fr1)]); + trace!(universe = ?self.region_universe(fr1)); self.find_constraint_paths_between_regions(fr1, |r| { // First look for some `r` such that `fr1: r` and `r` is live at `location` trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r)); @@ -2252,8 +2244,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// This can be used to quickly under-approximate the regions which are equal to each other /// and their relative orderings. // This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`. - pub fn constraint_sccs(&self) -> &Sccs { - self.constraint_sccs.as_ref() + pub fn constraint_sccs(&self) -> &ConstraintSccs { + &self.constraint_sccs } /// Access to the region graph, built from the outlives constraints. @@ -2282,6 +2274,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { let point = self.liveness_constraints.point_from_location(location); self.liveness_constraints.is_loan_live_at(loan_idx, point) } + + /// Returns the representative `RegionVid` for a given SCC. + /// See `RegionTracker` for how a region variable ID is chosen. + /// + /// It is a hacky way to manage checking regions for equality, + /// since we can 'canonicalize' each region to the representative + /// of its SCC and be sure that -- if they have the same repr -- + /// they *must* be equal (though not having the same repr does not + /// mean they are unequal). + fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid { + self.constraint_sccs.annotation(scc).representative + } } impl<'tcx> RegionDefinition<'tcx> { @@ -2304,5 +2308,5 @@ pub struct BlameConstraint<'tcx> { pub category: ConstraintCategory<'tcx>, pub from_closure: bool, pub cause: ObligationCause<'tcx>, - pub variance_info: ty::VarianceDiagInfo<'tcx>, + pub variance_info: ty::VarianceDiagInfo>, } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index f601a9d70739..51c3648d7307 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -85,7 +85,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Use the SCC representative instead of directly using `region`. // See [rustc-dev-guide chapter] § "Strict lifetime equality". let scc = self.constraint_sccs.scc(region.as_var()); - let vid = self.scc_representatives[scc]; + let vid = self.scc_representative(scc); let named = match self.definitions[vid].origin { // Iterate over all universal regions in a consistent order and find the // *first* equal region. This makes sure that equal lifetimes will have @@ -213,7 +213,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let scc = self.constraint_sccs.scc(vid); // Special handling of higher-ranked regions. - if !self.scc_universes[scc].is_root() { + if !self.scc_universe(scc).is_root() { match self.scc_values.placeholders_contained_in(scc).enumerate().last() { // If the region contains a single placeholder then they're equal. Some((0, placeholder)) => { @@ -340,7 +340,7 @@ fn check_opaque_type_well_formed<'tcx>( .with_next_trait_solver(next_trait_solver) .with_opaque_type_inference(parent_def_id) .build(); - let ocx = ObligationCtxt::new(&infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let identity_args = GenericArgs::identity_for_item(tcx, def_id); // Require that the hidden type actually fulfills all the bounds of the opaque type, even without diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs index d382f264c37b..2858a407e098 100644 --- a/compiler/rustc_borrowck/src/renumber.rs +++ b/compiler/rustc_borrowck/src/renumber.rs @@ -113,7 +113,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for RegionRenumberer<'a, 'tcx> { } #[instrument(skip(self), level = "debug")] - fn visit_constant(&mut self, constant: &mut ConstOperand<'tcx>, location: Location) { + fn visit_const_operand(&mut self, constant: &mut ConstOperand<'tcx>, location: Location) { let const_ = constant.const_; constant.const_ = self.renumber_regions(const_, || RegionCtxt::Location(location)); debug!("constant: {:#?}", constant); diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 5aa8fe213814..0cb4b15b1271 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -10,9 +10,9 @@ use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_span::Span; -use rustc_trait_selection::solve::deeply_normalize; use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; +use rustc_trait_selection::traits::ScrubbedTraitError; use crate::{ constraints::OutlivesConstraint, @@ -136,7 +136,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { fn convert( &mut self, - predicate: ty::OutlivesPredicate, ty::Region<'tcx>>, + predicate: ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>, constraint_category: ConstraintCategory<'tcx>, ) { debug!("generate: constraints at: {:#?}", self.locations); @@ -276,17 +276,18 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { &self, ty: Ty<'tcx>, next_outlives_predicates: &mut Vec<( - ty::OutlivesPredicate, ty::Region<'tcx>>, + ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>, ConstraintCategory<'tcx>, )>, ) -> Ty<'tcx> { let result = CustomTypeOp::new( |ocx| { - deeply_normalize( - ocx.infcx.at(&ObligationCause::dummy_with_span(self.span), self.param_env), + ocx.deeply_normalize( + &ObligationCause::dummy_with_span(self.span), + self.param_env, ty, ) - .map_err(|_| NoSolution) + .map_err(|_: Vec>| NoSolution) }, "normalize type outlives obligation", ) diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 38ec9f7678eb..8b863efad6ca 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -14,7 +14,6 @@ use std::rc::Rc; use crate::{ constraints::OutlivesConstraintSet, facts::{AllFacts, AllFactsExt}, - location::LocationTable, region_infer::values::LivenessValues, universal_regions::UniversalRegions, }; @@ -39,7 +38,6 @@ pub(super) fn generate<'mir, 'tcx>( elements: &Rc, flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, move_data: &MoveData<'tcx>, - location_table: &LocationTable, use_polonius: bool, ) { debug!("liveness::generate"); @@ -53,11 +51,9 @@ pub(super) fn generate<'mir, 'tcx>( compute_relevant_live_locals(typeck.tcx(), &free_regions, body); let facts_enabled = use_polonius || AllFacts::enabled(typeck.tcx()); - let polonius_drop_used = facts_enabled.then(|| { - let mut drop_used = Vec::new(); - polonius::populate_access_facts(typeck, body, location_table, move_data, &mut drop_used); - drop_used - }); + if facts_enabled { + polonius::populate_access_facts(typeck, body, move_data); + }; trace::trace( typeck, @@ -67,7 +63,6 @@ pub(super) fn generate<'mir, 'tcx>( move_data, relevant_live_locals, boring_locals, - polonius_drop_used, ); // Mark regions that should be live where they appear within rvalues or within a call: like diff --git a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs index 7f5302270439..d8f03a07a63c 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs @@ -85,13 +85,10 @@ impl<'a, 'tcx> Visitor<'tcx> for UseFactsExtractor<'a, 'tcx> { pub(super) fn populate_access_facts<'a, 'tcx>( typeck: &mut TypeChecker<'a, 'tcx>, body: &Body<'tcx>, - location_table: &LocationTable, move_data: &MoveData<'tcx>, - //FIXME: this is not mutated, but expected to be modified as - // out param, bug? - dropped_at: &mut Vec<(Local, Location)>, ) { debug!("populate_access_facts()"); + let location_table = typeck.borrowck_context.location_table; if let Some(facts) = typeck.borrowck_context.all_facts.as_mut() { let mut extractor = UseFactsExtractor { @@ -104,10 +101,6 @@ pub(super) fn populate_access_facts<'a, 'tcx>( }; extractor.visit_body(body); - facts.var_dropped_at.extend( - dropped_at.iter().map(|&(local, location)| (local, location_table.mid_index(location))), - ); - for (local, local_decl) in body.local_decls.iter_enumerated() { debug!( "add use_of_var_derefs_origin facts - local={:?}, type={:?}", @@ -117,7 +110,7 @@ pub(super) fn populate_access_facts<'a, 'tcx>( let universal_regions = &typeck.borrowck_context.universal_regions; typeck.infcx.tcx.for_each_free_region(&local_decl.ty, |region| { let region_vid = universal_regions.to_region_vid(region); - facts.use_of_var_derefs_origin.push((local, region_vid)); + facts.use_of_var_derefs_origin.push((local, region_vid.into())); }); } } @@ -136,7 +129,7 @@ pub(super) fn add_drop_of_var_derefs_origin<'tcx>( let universal_regions = &typeck.borrowck_context.universal_regions; typeck.infcx.tcx.for_each_free_region(kind, |drop_live_region| { let region_vid = universal_regions.to_region_vid(drop_live_region); - facts.drop_of_var_derefs_origin.push((local, region_vid)); + facts.drop_of_var_derefs_origin.push((local, region_vid.into())); }); } } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 6cc0e67c0f80..50843c602cc8 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -16,6 +16,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex}; use rustc_mir_dataflow::ResultsCursor; +use crate::location::RichLocation; use crate::{ region_infer::values::{self, LiveLoans}, type_check::liveness::local_use_map::LocalUseMap, @@ -46,7 +47,6 @@ pub(super) fn trace<'mir, 'tcx>( move_data: &MoveData<'tcx>, relevant_live_locals: Vec, boring_locals: Vec, - polonius_drop_used: Option>, ) { let local_use_map = &LocalUseMap::build(&relevant_live_locals, elements, body); @@ -93,9 +93,7 @@ pub(super) fn trace<'mir, 'tcx>( let mut results = LivenessResults::new(cx); - if let Some(drop_used) = polonius_drop_used { - results.add_extra_drop_facts(drop_used, relevant_live_locals.iter().copied().collect()) - } + results.add_extra_drop_facts(&relevant_live_locals); results.compute_for_all_locals(relevant_live_locals); @@ -218,21 +216,38 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { /// /// Add facts for all locals with free regions, since regions may outlive /// the function body only at certain nodes in the CFG. - fn add_extra_drop_facts( - &mut self, - drop_used: Vec<(Local, Location)>, - relevant_live_locals: FxIndexSet, - ) { + fn add_extra_drop_facts(&mut self, relevant_live_locals: &[Local]) -> Option<()> { + let drop_used = self + .cx + .typeck + .borrowck_context + .all_facts + .as_ref() + .map(|facts| facts.var_dropped_at.clone())?; + + let relevant_live_locals: FxIndexSet<_> = relevant_live_locals.iter().copied().collect(); + let locations = IntervalSet::new(self.cx.elements.num_points()); - for (local, location) in drop_used { + for (local, location_index) in drop_used { if !relevant_live_locals.contains(&local) { let local_ty = self.cx.body.local_decls[local].ty; if local_ty.has_free_regions() { + let location = match self + .cx + .typeck + .borrowck_context + .location_table + .to_location(location_index) + { + RichLocation::Start(l) => l, + RichLocation::Mid(l) => l, + }; self.cx.add_drop_live_facts_for(local, local_ty, &[location], &locations); } } } + Some(()) } /// Clear the value of fields that are "per local variable". diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 6cf9ac45aa3e..c7c1b2af6a7b 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -27,8 +27,9 @@ use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{ - self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, Dynamic, - OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, UserType, UserTypeAnnotationIndex, + self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CoroutineArgsExt, + Dynamic, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, UserType, + UserTypeAnnotationIndex, }; use rustc_middle::ty::{GenericArgsRef, UserArgs}; use rustc_middle::{bug, span_bug}; @@ -188,15 +189,7 @@ pub(crate) fn type_check<'mir, 'tcx>( checker.equate_inputs_and_outputs(body, universal_regions, &normalized_inputs_and_output); checker.check_signature_annotation(body); - liveness::generate( - &mut checker, - body, - elements, - flow_inits, - move_data, - location_table, - use_polonius, - ); + liveness::generate(&mut checker, body, elements, flow_inits, move_data, use_polonius); translate_outlives_facts(&mut checker); let opaque_type_values = infcx.take_opaque_types(); @@ -260,16 +253,14 @@ fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) { |constraint: &OutlivesConstraint<'_>| { if let Some(from_location) = constraint.locations.from_location() { Either::Left(iter::once(( - constraint.sup, - constraint.sub, + constraint.sup.into(), + constraint.sub.into(), location_table.mid_index(from_location), ))) } else { - Either::Right( - location_table - .all_points() - .map(move |location| (constraint.sup, constraint.sub, location)), - ) + Either::Right(location_table.all_points().map(move |location| { + (constraint.sup.into(), constraint.sub.into(), location) + })) } }, )); @@ -310,10 +301,10 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { self.sanitize_place(place, location, context); } - fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, location: Location) { - debug!(?constant, ?location, "visit_constant"); + fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, location: Location) { + debug!(?constant, ?location, "visit_const_operand"); - self.super_constant(constant, location); + self.super_const_operand(constant, location); let ty = self.sanitize_type(constant, constant.const_.ty()); self.cx.infcx.tcx.for_each_free_region(&ty, |live_region| { @@ -337,7 +328,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { if let Some(annotation_index) = constant.user_ty { if let Err(terr) = self.cx.relate_type_and_user_type( constant.const_.ty(), - ty::Variance::Invariant, + ty::Invariant, &UserTypeProjection { base: annotation_index, projs: vec![] }, locations, ConstraintCategory::Boring, @@ -355,7 +346,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { } else { let tcx = self.tcx(); let maybe_uneval = match constant.const_ { - Const::Ty(ct) => match ct.kind() { + Const::Ty(_, ct) => match ct.kind() { ty::ConstKind::Unevaluated(_) => { bug!("should not encounter unevaluated Const::Ty here, got {:?}", ct) } @@ -460,7 +451,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { if let Err(terr) = self.cx.relate_type_and_user_type( ty, - ty::Variance::Invariant, + ty::Invariant, user_ty, Locations::All(*span), ConstraintCategory::TypeAnnotation, @@ -1104,7 +1095,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ) -> Result<(), NoSolution> { // Use this order of parameters because the sup type is usually the // "expected" type in diagnostics. - self.relate_types(sup, ty::Variance::Contravariant, sub, locations, category) + self.relate_types(sup, ty::Contravariant, sub, locations, category) } #[instrument(skip(self, category), level = "debug")] @@ -1115,7 +1106,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { locations: Locations, category: ConstraintCategory<'tcx>, ) -> Result<(), NoSolution> { - self.relate_types(expected, ty::Variance::Invariant, found, locations, category) + self.relate_types(expected, ty::Invariant, found, locations, category) } #[instrument(skip(self), level = "debug")] @@ -1155,7 +1146,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { trace!(?curr_projected_ty); let ty = curr_projected_ty.ty; - self.relate_types(ty, v.xform(ty::Variance::Contravariant), a, locations, category)?; + self.relate_types(ty, v.xform(ty::Contravariant), a, locations, category)?; Ok(()) } @@ -1257,7 +1248,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if let Some(annotation_index) = self.rvalue_user_ty(rv) { if let Err(terr) = self.relate_type_and_user_type( rv_ty, - ty::Variance::Invariant, + ty::Invariant, &UserTypeProjection { base: annotation_index, projs: vec![] }, location.to_locations(), ConstraintCategory::Boring, @@ -1865,7 +1856,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if let Operand::Constant(constant) = op { let maybe_uneval = match constant.const_ { - Const::Val(..) | Const::Ty(_) => None, + Const::Val(..) | Const::Ty(_, _) => None, Const::Unevaluated(uv, _) => Some(uv), }; @@ -2417,8 +2408,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.check_operand(op, location); } - Rvalue::BinaryOp(_, box (left, right)) - | Rvalue::CheckedBinaryOp(_, box (left, right)) => { + Rvalue::BinaryOp(_, box (left, right)) => { self.check_operand(left, location); self.check_operand(right, location); } @@ -2445,7 +2435,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | Rvalue::Cast(..) | Rvalue::ShallowInitBox(..) | Rvalue::BinaryOp(..) - | Rvalue::CheckedBinaryOp(..) | Rvalue::NullaryOp(..) | Rvalue::CopyForDeref(..) | Rvalue::UnaryOp(..) @@ -2547,7 +2536,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if let Some(borrow_index) = borrow_set.get_index_of(&location) { let region_vid = borrow_region.as_var(); all_facts.loan_issued_at.push(( - region_vid, + region_vid.into(), borrow_index, location_table.mid_index(location), )); diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index cbd8a4125cd3..b9a82046e59a 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -1,14 +1,15 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; +use rustc_infer::infer::relate::{PredicateEmittingRelation, StructurallyRelateAliases}; +use rustc_infer::infer::relate::{Relate, RelateResult, TypeRelation}; use rustc_infer::infer::NllRegionVariableOrigin; -use rustc_infer::infer::{ObligationEmittingRelation, StructurallyRelateAliases}; -use rustc_infer::traits::{Obligation, PredicateObligations}; +use rustc_infer::traits::solve::Goal; +use rustc_infer::traits::Obligation; use rustc_middle::mir::ConstraintCategory; use rustc_middle::span_bug; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::fold::FnMutDelegate; -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; @@ -49,14 +50,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { locations: Locations, category: ConstraintCategory<'tcx>, ) -> Result<(), NoSolution> { - NllTypeRelating::new( - self, - locations, - category, - UniverseInfo::other(), - ty::Variance::Invariant, - ) - .relate(a, b)?; + NllTypeRelating::new(self, locations, category, UniverseInfo::other(), ty::Invariant) + .relate(a, b)?; Ok(()) } } @@ -82,7 +77,7 @@ pub struct NllTypeRelating<'me, 'bccx, 'tcx> { /// - Bivariant means that it doesn't matter. ambient_variance: ty::Variance, - ambient_variance_info: ty::VarianceDiagInfo<'tcx>, + ambient_variance_info: ty::VarianceDiagInfo>, } impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> { @@ -105,15 +100,15 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> { fn ambient_covariance(&self) -> bool { match self.ambient_variance { - ty::Variance::Covariant | ty::Variance::Invariant => true, - ty::Variance::Contravariant | ty::Variance::Bivariant => false, + ty::Covariant | ty::Invariant => true, + ty::Contravariant | ty::Bivariant => false, } } fn ambient_contravariance(&self) -> bool { match self.ambient_variance { - ty::Variance::Contravariant | ty::Variance::Invariant => true, - ty::Variance::Covariant | ty::Variance::Bivariant => false, + ty::Contravariant | ty::Invariant => true, + ty::Covariant | ty::Bivariant => false, } } @@ -153,9 +148,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> { "expected at least one opaque type in `relate_opaques`, got {a} and {b}." ), }; - let cause = ObligationCause::dummy_with_span(self.span()); - let obligations = infcx.handle_opaque_type(a, b, &cause, self.param_env())?.obligations; - self.register_obligations(obligations); + self.register_goals(infcx.handle_opaque_type(a, b, self.span(), self.param_env())?); Ok(()) } @@ -193,7 +186,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> { types: &mut |_bound_ty: ty::BoundTy| { unreachable!("we only replace regions in nll_relate, not types") }, - consts: &mut |_bound_var: ty::BoundVar, _ty| { + consts: &mut |_bound_var: ty::BoundVar| { unreachable!("we only replace regions in nll_relate, not consts") }, }; @@ -231,7 +224,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> { types: &mut |_bound_ty: ty::BoundTy| { unreachable!("we only replace regions in nll_relate, not types") }, - consts: &mut |_bound_var: ty::BoundVar, _ty| { + consts: &mut |_bound_var: ty::BoundVar| { unreachable!("we only replace regions in nll_relate, not consts") }, }; @@ -296,7 +289,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> { &mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>, - info: ty::VarianceDiagInfo<'tcx>, + info: ty::VarianceDiagInfo>, ) { let sub = self.type_checker.borrowck_context.universal_regions.to_region_vid(sub); let sup = self.type_checker.borrowck_context.universal_regions.to_region_vid(sup); @@ -314,7 +307,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> { } } -impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { +impl<'bccx, 'tcx> TypeRelation> for NllTypeRelating<'_, 'bccx, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.type_checker.infcx.tcx } @@ -324,10 +317,10 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { } #[instrument(skip(self, info), level = "trace", ret)] - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, variance: ty::Variance, - info: ty::VarianceDiagInfo<'tcx>, + info: ty::VarianceDiagInfo>, a: T, b: T, ) -> RelateResult<'tcx, T> { @@ -337,11 +330,7 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { debug!(?self.ambient_variance); // In a bivariant context this always succeeds. - let r = if self.ambient_variance == ty::Variance::Bivariant { - Ok(a) - } else { - self.relate(a, b) - }; + let r = if self.ambient_variance == ty::Bivariant { Ok(a) } else { self.relate(a, b) }; self.ambient_variance = old_ambient_variance; @@ -445,7 +434,7 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { b: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: Relate<'tcx>, + T: Relate>, { // We want that // @@ -475,7 +464,7 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { } match self.ambient_variance { - ty::Variance::Covariant => { + ty::Covariant => { // Covariance, so we want `for<..> A <: for<..> B` -- // therefore we compare any instantiation of A (i.e., A // instantiated with existentials) against every @@ -490,7 +479,7 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { })?; } - ty::Variance::Contravariant => { + ty::Contravariant => { // Contravariance, so we want `for<..> A :> for<..> B` -- // therefore we compare every instantiation of A (i.e., A // instantiated with universals) against any @@ -505,7 +494,7 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { })?; } - ty::Variance::Invariant => { + ty::Invariant => { // Invariant, so we want `for<..> A == for<..> B` -- // therefore we want `exists<..> A == for<..> B` and // `exists<..> B == for<..> A`. @@ -526,14 +515,14 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { })?; } - ty::Variance::Bivariant => {} + ty::Bivariant => {} } Ok(a) } } -impl<'bccx, 'tcx> ObligationEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { +impl<'bccx, 'tcx> PredicateEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { fn span(&self) -> Span { self.locations.span(self.type_checker.body) } @@ -550,22 +539,32 @@ impl<'bccx, 'tcx> ObligationEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx &mut self, obligations: impl IntoIterator, ty::Predicate<'tcx>>>, ) { - self.register_obligations( - obligations - .into_iter() - .map(|to_pred| { - Obligation::new(self.tcx(), ObligationCause::dummy(), self.param_env(), to_pred) - }) - .collect(), + let tcx = self.tcx(); + let param_env = self.param_env(); + self.register_goals( + obligations.into_iter().map(|to_pred| Goal::new(tcx, param_env, to_pred)), ); } - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + fn register_goals( + &mut self, + obligations: impl IntoIterator>>, + ) { let _: Result<_, ErrorGuaranteed> = self.type_checker.fully_perform_op( self.locations, self.category, InstantiateOpaqueType { - obligations, + obligations: obligations + .into_iter() + .map(|goal| { + Obligation::new( + self.tcx(), + ObligationCause::dummy_with_span(self.span()), + goal.param_env, + goal.predicate, + ) + }) + .collect(), // These fields are filled in during execution of the operation base_universe: None, region_constraints: None, @@ -573,25 +572,25 @@ impl<'bccx, 'tcx> ObligationEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx ); } - fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { + fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { self.register_predicates([ty::Binder::dummy(match self.ambient_variance { - ty::Variance::Covariant => ty::PredicateKind::AliasRelate( + ty::Covariant => ty::PredicateKind::AliasRelate( a.into(), b.into(), ty::AliasRelationDirection::Subtype, ), // a :> b is b <: a - ty::Variance::Contravariant => ty::PredicateKind::AliasRelate( + ty::Contravariant => ty::PredicateKind::AliasRelate( b.into(), a.into(), ty::AliasRelationDirection::Subtype, ), - ty::Variance::Invariant => ty::PredicateKind::AliasRelate( + ty::Invariant => ty::PredicateKind::AliasRelate( a.into(), b.into(), ty::AliasRelationDirection::Equate, ), - ty::Variance::Bivariant => { + ty::Bivariant => { unreachable!("cannot defer an alias-relate goal with Bivariant variance (yet?)") } })]); diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index f8123535e2d0..9f5fb59e46c5 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -29,7 +29,8 @@ use rustc_middle::ty::{self, InlineConstArgs, InlineConstArgsParts, RegionVid, T use rustc_middle::ty::{GenericArgs, GenericArgsRef}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::{kw, sym}; -use rustc_span::Symbol; +use rustc_span::{ErrorGuaranteed, Symbol}; +use std::cell::Cell; use std::iter; use crate::renumber::RegionCtxt; @@ -186,6 +187,10 @@ struct UniversalRegionIndices<'tcx> { /// The vid assigned to `'static`. Used only for diagnostics. pub fr_static: RegionVid, + + /// Whether we've encountered an error region. If we have, cancel all + /// outlives errors, as they are likely bogus. + pub tainted_by_errors: Cell>, } #[derive(Debug, PartialEq)] @@ -408,6 +413,10 @@ impl<'tcx> UniversalRegions<'tcx> { } } } + + pub fn tainted_by_errors(&self) -> Option { + self.indices.tainted_by_errors.get() + } } struct UniversalRegionsBuilder<'cx, 'tcx> { @@ -663,7 +672,11 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let global_mapping = iter::once((tcx.lifetimes.re_static, fr_static)); let arg_mapping = iter::zip(identity_args.regions(), fr_args.regions().map(|r| r.as_var())); - UniversalRegionIndices { indices: global_mapping.chain(arg_mapping).collect(), fr_static } + UniversalRegionIndices { + indices: global_mapping.chain(arg_mapping).collect(), + fr_static, + tainted_by_errors: Cell::new(None), + } } fn compute_inputs_and_output( @@ -868,7 +881,8 @@ impl<'tcx> UniversalRegionIndices<'tcx> { pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid { if let ty::ReVar(..) = *r { r.as_var() - } else if r.is_error() { + } else if let ty::ReError(guar) = *r { + self.tainted_by_errors.set(Some(guar)); // We use the `'static` `RegionVid` because `ReError` doesn't actually exist in the // `UniversalRegionIndices`. This is fine because 1) it is a fallback only used if // errors are being emitted and 2) it leaves the happy path unaffected. diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 0f1589903193..2d1269e1b6ad 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -110,6 +110,9 @@ builtin_macros_derive_path_args_list = traits in `#[derive(...)]` don't accept a builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept values .suggestion = remove the value +builtin_macros_derive_unsafe_path = traits in `#[derive(...)]` don't accept `unsafe(...)` + .suggestion = remove the `unsafe(...)` + builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead .custom = use `std::env::var({$var_expr})` to read the variable at run time @@ -247,5 +250,3 @@ builtin_macros_unexpected_lit = expected path to a trait, found literal .label = not a trait .str_lit = try using `#[derive({$sym})]` .other = for example, write `#[derive(Debug)]` for `Debug` - -builtin_macros_unnameable_test_items = cannot test inner items diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 49b1b8cf9926..64238e81b266 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -1,6 +1,7 @@ use crate::errors; use crate::util::expr_to_spanned_string; use ast::token::IdentIsRaw; +use lint::BuiltinLintDiag; use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter}; @@ -45,7 +46,7 @@ pub fn parse_asm_args<'a>( sp: Span, is_global_asm: bool, ) -> PResult<'a, AsmArgs> { - let dcx = &p.psess.dcx; + let dcx = p.dcx(); if p.token == token::Eof { return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp })); @@ -306,7 +307,7 @@ pub fn parse_asm_args<'a>( fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) { // Tool-only output let full_span = if p.token.kind == token::Comma { span.to(p.token.span) } else { span }; - p.psess.dcx.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); + p.dcx().emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); } /// Try to set the provided option in the provided `AsmArgs`. @@ -378,7 +379,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, p.expect(&token::OpenDelim(Delimiter::Parenthesis))?; if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) { - return Err(p.psess.dcx.create_err(errors::NonABI { span: p.token.span })); + return Err(p.dcx().create_err(errors::NonABI { span: p.token.span })); } let mut new_abis = Vec::new(); @@ -389,7 +390,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, } Err(opt_lit) => { let span = opt_lit.map_or(p.token.span, |lit| lit.span); - let mut err = p.psess.dcx.struct_span_err(span, "expected string literal"); + let mut err = p.dcx().struct_span_err(span, "expected string literal"); err.span_label(span, "not a string literal"); return Err(err); } @@ -513,7 +514,7 @@ fn expand_preparsed_asm( lint::builtin::BAD_ASM_STYLE, find_span(".intel_syntax"), ecx.current_expansion.lint_node_id, - "avoid using `.intel_syntax`, Intel syntax is the default", + BuiltinLintDiag::AvoidUsingIntelSyntax, ); } if template_str.contains(".att_syntax") { @@ -521,7 +522,7 @@ fn expand_preparsed_asm( lint::builtin::BAD_ASM_STYLE, find_span(".att_syntax"), ecx.current_expansion.lint_node_id, - "avoid using `.att_syntax`, prefer using `options(att_syntax)` instead", + BuiltinLintDiag::AvoidUsingAttSyntax, ); } } diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 085ea3458bbf..a98cb6f0f760 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -57,7 +57,6 @@ impl<'cx, 'a> Context<'cx, 'a> { /// Builds the whole `assert!` expression. For example, `let elem = 1; assert!(elem == 1);` expands to: /// /// ```rust - /// #![feature(generic_assert_internals)] /// let elem = 1; /// { /// #[allow(unused_imports)] @@ -153,7 +152,7 @@ impl<'cx, 'a> Context<'cx, 'a> { fn build_panic(&self, expr_str: &str, panic_path: Path) -> P { let escaped_expr_str = escape_to_fmt(expr_str); let initial = [ - TokenTree::token_joint_hidden( + TokenTree::token_joint( token::Literal(token::Lit { kind: token::LitKind::Str, symbol: Symbol::intern(&if self.fmt_string.is_empty() { @@ -172,7 +171,7 @@ impl<'cx, 'a> Context<'cx, 'a> { ]; let captures = self.capture_decls.iter().flat_map(|cap| { [ - TokenTree::token_joint_hidden( + TokenTree::token_joint( token::Ident(cap.ident.name, IdentIsRaw::No), cap.ident.span, ), diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 5f63a8ae0a8c..03aff6f96330 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -196,7 +196,7 @@ impl CfgEval<'_, '_> { // Re-parse the tokens, setting the `capture_cfg` flag to save extra information // to the captured `AttrTokenStream` (specifically, we capture // `AttrTokenTree::AttributesData` for all occurrences of `#[cfg]` and `#[cfg_attr]`) - let mut parser = rustc_parse::stream_to_parser(&self.cfg.sess.psess, orig_tokens, None); + let mut parser = Parser::new(&self.cfg.sess.psess, orig_tokens, None); parser.capture_cfg = true; match parse_annotatable_with(&mut parser) { Ok(a) => annotatable = a, diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs index ada82e45712d..58928815e893 100644 --- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs +++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs @@ -4,19 +4,20 @@ use crate::errors; use rustc_ast::attr::mk_attr; use rustc_ast::token; use rustc_ast::{self as ast, AttrItem, AttrStyle}; +use rustc_parse::{new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_session::parse::ParseSess; use rustc_span::FileName; pub fn inject(krate: &mut ast::Crate, psess: &ParseSess, attrs: &[String]) { for raw_attr in attrs { - let mut parser = rustc_parse::new_parser_from_source_str( + let mut parser = unwrap_or_emit_fatal(new_parser_from_source_str( psess, FileName::cli_crate_attr_source_code(raw_attr), raw_attr.clone(), - ); + )); let start_span = parser.token.span; - let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item(false) { + let AttrItem { unsafety, path, args, tokens: _ } = match parser.parse_attr_item(false) { Ok(ai) => ai, Err(err) => { err.emit(); @@ -25,13 +26,14 @@ pub fn inject(krate: &mut ast::Crate, psess: &ParseSess, attrs: &[String]) { }; let end_span = parser.token.span; if parser.token != token::Eof { - psess.dcx.emit_err(errors::InvalidCrateAttr { span: start_span.to(end_span) }); + psess.dcx().emit_err(errors::InvalidCrateAttr { span: start_span.to(end_span) }); continue; } krate.attrs.push(mk_attr( &psess.attr_id_generator, AttrStyle::Inner, + unsafety, path, args, start_span.to(end_span), diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index d14858e5c1db..b5cbfdf0ec6e 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -2,7 +2,7 @@ use crate::cfg_eval::cfg_eval; use crate::errors; use rustc_ast as ast; -use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind}; +use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, Safety, StmtKind}; use rustc_expand::base::{ Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier, }; @@ -60,6 +60,7 @@ impl MultiItemModifier for Expander { // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the // paths. report_path_args(sess, meta); + report_unsafe_args(sess, meta); meta.path.clone() }) .map(|path| DeriveResolution { @@ -159,3 +160,13 @@ fn report_path_args(sess: &Session, meta: &ast::MetaItem) { } } } + +fn report_unsafe_args(sess: &Session, meta: &ast::MetaItem) { + match meta.unsafety { + Safety::Unsafe(span) => { + sess.dcx().emit_err(errors::DeriveUnsafePath { span }); + } + Safety::Default => {} + Safety::Safe(_) => unreachable!(), + } +} diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index bf92ddb33701..577523a1d5a3 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -3,7 +3,7 @@ use crate::deriving::generic::*; use crate::errors; use core::ops::ControlFlow; use rustc_ast as ast; -use rustc_ast::visit::walk_list; +use rustc_ast::visit::visit_opt; use rustc_ast::{attr, EnumDef, VariantData}; use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt}; use rustc_span::symbol::Ident; @@ -224,7 +224,7 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, ' self.visit_ident(v.ident); self.visit_vis(&v.vis); self.visit_variant_data(&v.data); - walk_list!(self, visit_anon_const, &v.disr_expr); + visit_opt!(self, visit_anon_const, &v.disr_expr); for attr in &v.attrs { rustc_ast::visit::walk_attribute(self, attr); } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 46949f731aaa..ba289f9552e2 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -412,6 +412,15 @@ fn find_type_parameters( impl<'a, 'b> visit::Visitor<'a> for Visitor<'a, 'b> { fn visit_ty(&mut self, ty: &'a ast::Ty) { + let stack_len = self.bound_generic_params_stack.len(); + if let ast::TyKind::BareFn(bare_fn) = &ty.kind + && !bare_fn.generic_params.is_empty() + { + // Given a field `x: for<'a> fn(T::SomeType<'a>)`, we wan't to account for `'a` so + // that we generate `where for<'a> T::SomeType<'a>: ::core::clone::Clone`. #122622 + self.bound_generic_params_stack.extend(bare_fn.generic_params.iter().cloned()); + } + if let ast::TyKind::Path(_, path) = &ty.kind && let Some(segment) = path.segments.first() && self.ty_param_names.contains(&segment.ident.name) @@ -422,7 +431,8 @@ fn find_type_parameters( }); } - visit::walk_ty(self, ty) + visit::walk_ty(self, ty); + self.bound_generic_params_stack.truncate(stack_len); } // Place bound generic params on a stack, to extract them when a type is encountered. @@ -1621,14 +1631,13 @@ impl<'a> TraitDef<'a> { }; if let Some(ty) = exception { - cx.sess.psess.buffer_lint_with_diagnostic( + cx.sess.psess.buffer_lint( BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE, sp, ast::CRATE_NODE_ID, - format!( - "{ty} slice in a packed struct that derives a built-in trait" - ), - rustc_lint_defs::BuiltinLintDiag::ByteSliceInPackedStructWithDerive, + rustc_lint_defs::BuiltinLintDiag::ByteSliceInPackedStructWithDerive { + ty: ty.to_string(), + }, ); } else { // Wrap the expression in `{...}`, causing a copy. diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index d157703723be..ed2f98f2a393 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1,5 +1,5 @@ use rustc_errors::{ - codes::*, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level, MultiSpan, + codes::*, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan, SingleLabelManySpans, SubdiagMessageOp, Subdiagnostic, }; use rustc_macros::{Diagnostic, Subdiagnostic}; @@ -295,6 +295,13 @@ pub(crate) struct DerivePathArgsValue { pub(crate) span: Span, } +#[derive(Diagnostic)] +#[diag(builtin_macros_derive_unsafe_path)] +pub(crate) struct DeriveUnsafePath { + #[primary_span] + pub(crate) span: Span, +} + #[derive(Diagnostic)] #[diag(builtin_macros_no_default_variant)] #[help] @@ -427,7 +434,7 @@ pub(crate) struct EnvNotDefinedWithUserMessage { // Hand-written implementation to support custom user messages. impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for EnvNotDefinedWithUserMessage { #[track_caller] - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { #[expect( rustc::untranslatable_diagnostic, reason = "cannot translate user-provided messages" @@ -794,7 +801,7 @@ pub(crate) struct AsmClobberNoReg { } impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AsmClobberNoReg { - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { // eager translation as `span_labels` takes `AsRef` let lbl1 = dcx.eagerly_translate_to_string( crate::fluent_generated::builtin_macros_asm_clobber_abi, diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index a5fc74f1d666..5cb0407bd59e 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -556,7 +556,6 @@ fn make_format_args( let arg_name = args.explicit_args()[index].kind.ident().unwrap(); ecx.buffered_early_lint.push(BufferedEarlyLint { span: arg_name.span.into(), - msg: format!("named argument `{}` is not used by name", arg_name.name).into(), node_id: rustc_ast::CRATE_NODE_ID, lint_id: LintId::of(NAMED_ARGUMENTS_USED_POSITIONALLY), diagnostic: BuiltinLintDiag::NamedArgumentUsedPositionally { diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 744c7f9d0900..35b0f43d8af7 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -1,12 +1,12 @@ //! This crate contains implementations of built-in macros and other code generating facilities //! injecting code into the crate before it is lowered to HIR. +// tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -#![feature(rustdoc_internals)] -#![doc(rust_logo)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(decl_macro)] @@ -15,7 +15,9 @@ #![feature(lint_reasons)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] +#![feature(rustdoc_internals)] #![feature(try_blocks)] +// tidy-alphabetical-end extern crate proc_macro; diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 477e5c8bec53..99d0191958d6 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -3,6 +3,7 @@ use rustc_ast::ptr::P; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{self as ast, attr, NodeId}; use rustc_ast_pretty::pprust; +use rustc_errors::DiagCtxtHandle; use rustc_expand::base::{parse_macro_name_and_helper_attrs, ExtCtxt, ResolverExpand}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_feature::Features; @@ -38,7 +39,7 @@ enum ProcMacro { struct CollectProcMacros<'a> { macros: Vec, in_root: bool, - dcx: &'a rustc_errors::DiagCtxt, + dcx: DiagCtxtHandle<'a>, source_map: &'a SourceMap, is_proc_macro_crate: bool, is_test_crate: bool, @@ -52,7 +53,7 @@ pub fn inject( is_proc_macro_crate: bool, has_proc_macro_decls: bool, is_test_crate: bool, - dcx: &rustc_errors::DiagCtxt, + dcx: DiagCtxtHandle<'_>, ) { let ecfg = ExpansionConfig::default("proc_macro".to_string(), features); let mut cx = ExtCtxt::new(sess, ecfg, resolver, None); diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 47b2ee975ca8..dc1d82df0c39 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -11,8 +11,9 @@ use rustc_expand::base::{ resolve_path, DummyResult, ExpandResult, ExtCtxt, MacEager, MacResult, MacroExpanderResult, }; use rustc_expand::module::DirOwnership; -use rustc_parse::new_parser_from_file; +use rustc_lint_defs::BuiltinLintDiag; use rustc_parse::parser::{ForceCollect, Parser}; +use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; use rustc_span::source_map::SourceMap; use rustc_span::symbol::Symbol; @@ -125,7 +126,7 @@ pub(crate) fn expand_include<'cx>( return ExpandResult::Ready(DummyResult::any(sp, guar)); } }; - let p = new_parser_from_file(cx.psess(), &file, Some(sp)); + let p = unwrap_or_emit_fatal(new_parser_from_file(cx.psess(), &file, Some(sp))); // If in the included file we have e.g., `mod bar;`, // then the path of `bar.rs` should be relative to the directory of `file`. @@ -147,7 +148,7 @@ pub(crate) fn expand_include<'cx>( INCOMPLETE_INCLUDE, self.p.token.span, self.node_id, - "include macro expected single expression in source", + BuiltinLintDiag::IncompleteInclude, ); } Some(expr) diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 8cf431482ff7..9d032eb190a0 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -6,9 +6,11 @@ use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::visit::{walk_item, Visitor}; use rustc_ast::{attr, ModKind}; +use rustc_errors::DiagCtxtHandle; use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_feature::Features; +use rustc_lint_defs::BuiltinLintDiag; use rustc_session::lint::builtin::UNNAMEABLE_TEST_ITEMS; use rustc_session::Session; use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency}; @@ -163,7 +165,7 @@ impl<'a> Visitor<'a> for InnerItemLinter<'_> { UNNAMEABLE_TEST_ITEMS, attr.span, i.id, - crate::fluent_generated::builtin_macros_unnameable_test_items, + BuiltinLintDiag::UnnameableTestItems, ); } } @@ -202,6 +204,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> { let allow_dead_code = attr::mk_attr_nested_word( &self.sess.psess.attr_id_generator, ast::AttrStyle::Outer, + ast::Safety::Default, sym::allow, sym::dead_code, self.def_site, @@ -389,7 +392,7 @@ fn get_test_name(i: &ast::Item) -> Option { attr::first_attr_value_str_by_name(&i.attrs, sym::rustc_test_marker) } -fn get_test_runner(dcx: &rustc_errors::DiagCtxt, krate: &ast::Crate) -> Option { +fn get_test_runner(dcx: DiagCtxtHandle<'_>, krate: &ast::Crate) -> Option { let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?; let meta_list = test_attr.meta_item_list()?; let span = test_attr.span; diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs index 8dc7bc14ec3e..652e34268ea9 100644 --- a/compiler/rustc_builtin_macros/src/util.rs +++ b/compiler/rustc_builtin_macros/src/util.rs @@ -5,7 +5,7 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt}; use rustc_expand::expand::AstFragment; use rustc_feature::AttributeTemplate; -use rustc_lint_defs::builtin::DUPLICATE_MACRO_ATTRIBUTES; +use rustc_lint_defs::{builtin::DUPLICATE_MACRO_ATTRIBUTES, BuiltinLintDiag}; use rustc_parse::{parser, validate_attr}; use rustc_session::errors::report_lit_error; use rustc_span::{BytePos, Span, Symbol}; @@ -46,7 +46,7 @@ pub(crate) fn warn_on_duplicate_attribute(ecx: &ExtCtxt<'_>, item: &Annotatable, DUPLICATE_MACRO_ATTRIBUTES, attr.span, ecx.current_expansion.lint_node_id, - "duplicated attribute", + BuiltinLintDiag::DuplicateMacroAttribute, ); } } diff --git a/compiler/rustc_codegen_cranelift/example/issue-72793.rs b/compiler/rustc_codegen_cranelift/example/issue-72793.rs index 166b00600438..2e08fbca8ef2 100644 --- a/compiler/rustc_codegen_cranelift/example/issue-72793.rs +++ b/compiler/rustc_codegen_cranelift/example/issue-72793.rs @@ -2,20 +2,23 @@ #![feature(type_alias_impl_trait)] -trait T { - type Item; -} +mod helper { + pub trait T { + type Item; + } -type Alias<'a> = impl T; + pub type Alias<'a> = impl T; -struct S; -impl<'a> T for &'a S { - type Item = &'a (); -} + struct S; + impl<'a> T for &'a S { + type Item = &'a (); + } -fn filter_positive<'a>() -> Alias<'a> { - &S + pub fn filter_positive<'a>() -> Alias<'a> { + &S + } } +use helper::*; fn with_positive(fun: impl Fn(Alias<'_>)) { fun(filter_positive()); diff --git a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-rawdylib-processprng.patch b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-rawdylib-processprng.patch index 6af11e54d88a..584dbdb647f6 100644 --- a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-rawdylib-processprng.patch +++ b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-rawdylib-processprng.patch @@ -12,7 +12,7 @@ diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/wind index ad8e01bfa9b..9ca8e4c16ce 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs -@@ -323,7 +323,7 @@ pub unsafe fn NtWriteFile( +@@ -312,7 +312,7 @@ pub unsafe fn NtWriteFile( // Use raw-dylib to import ProcessPrng as we can't rely on there being an import library. cfg_if::cfg_if! { @@ -26,8 +26,8 @@ index e427546222a..f2fe42a4d51 100644 --- a/library/std/src/sys/pal/windows/rand.rs +++ b/library/std/src/sys/pal/windows/rand.rs @@ -2,7 +2,7 @@ - use core::mem; - use core::ptr; + + use crate::sys::c; -#[cfg(not(target_vendor = "win7"))] +#[cfg(any())] diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 4bcef15ad047..695dbaf2804b 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -399,7 +399,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( } match instance.def { - InstanceDef::Intrinsic(_) => { + InstanceKind::Intrinsic(_) => { match crate::intrinsics::codegen_intrinsic_call( fx, instance, @@ -412,7 +412,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( Err(instance) => Some(instance), } } - InstanceDef::DropGlue(_, None) | ty::InstanceDef::AsyncDropGlueCtorShim(_, None) => { + InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None) => { // empty drop glue - a nop. let dest = target.expect("Non terminating drop_in_place_real???"); let ret_block = fx.get_block(dest); @@ -494,7 +494,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( let (func_ref, first_arg_override) = match instance { // Trait object call - Some(Instance { def: InstanceDef::Virtual(_, idx), .. }) => { + Some(Instance { def: InstanceKind::Virtual(_, idx), .. }) => { if fx.clif_comments.enabled() { let nop_inst = fx.bcx.ins().nop(); fx.add_comment( @@ -593,11 +593,12 @@ pub(crate) fn codegen_drop<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, source_info: mir::SourceInfo, drop_place: CPlace<'tcx>, + target: BasicBlock, ) { let ty = drop_place.layout().ty; let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty).polymorphize(fx.tcx); - if let ty::InstanceDef::DropGlue(_, None) | ty::InstanceDef::AsyncDropGlueCtorShim(_, None) = + if let ty::InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None) = drop_instance.def { // we don't actually need to drop anything @@ -620,10 +621,16 @@ pub(crate) fn codegen_drop<'tcx>( let ptr = ptr.get_addr(fx); let drop_fn = crate::vtable::drop_fn_of_obj(fx, vtable); + let is_null = fx.bcx.ins().icmp_imm(IntCC::Equal, drop_fn, 0); + let target_block = fx.get_block(target); + let continued = fx.bcx.create_block(); + fx.bcx.ins().brif(is_null, target_block, &[], continued, &[]); + fx.bcx.switch_to_block(continued); + // FIXME(eddyb) perhaps move some of this logic into // `Instance::resolve_drop_in_place`? let virtual_drop = Instance { - def: ty::InstanceDef::Virtual(drop_instance.def_id(), 0), + def: ty::InstanceKind::Virtual(drop_instance.def_id(), 0), args: drop_instance.args, }; let fn_abi = @@ -659,8 +666,14 @@ pub(crate) fn codegen_drop<'tcx>( let (data, vtable) = drop_place.to_cvalue(fx).dyn_star_force_data_on_stack(fx); let drop_fn = crate::vtable::drop_fn_of_obj(fx, vtable); + let is_null = fx.bcx.ins().icmp_imm(IntCC::Equal, drop_fn, 0); + let target_block = fx.get_block(target); + let continued = fx.bcx.create_block(); + fx.bcx.ins().brif(is_null, target_block, &[], continued, &[]); + fx.bcx.switch_to_block(continued); + let virtual_drop = Instance { - def: ty::InstanceDef::Virtual(drop_instance.def_id(), 0), + def: ty::InstanceKind::Virtual(drop_instance.def_id(), 0), args: drop_instance.args, }; let fn_abi = @@ -671,7 +684,7 @@ pub(crate) fn codegen_drop<'tcx>( fx.bcx.ins().call_indirect(sig, drop_fn, &[data]); } _ => { - assert!(!matches!(drop_instance.def, InstanceDef::Virtual(_, _))); + assert!(!matches!(drop_instance.def, InstanceKind::Virtual(_, _))); let fn_abi = RevealAllLayoutCx(fx.tcx).fn_abi_of_instance(drop_instance, ty::List::empty()); @@ -697,4 +710,7 @@ pub(crate) fn codegen_drop<'tcx>( } } } + + let target_block = fx.get_block(target); + fx.bcx.ins().jump(target_block, &[]); } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 5846689643fd..b117dc496c2b 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -548,10 +548,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { } TerminatorKind::Drop { place, target, unwind: _, replace: _ } => { let drop_place = codegen_place(fx, *place); - crate::abi::codegen_drop(fx, source_info, drop_place); - - let target_block = fx.get_block(*target); - fx.bcx.ins().jump(target_block, &[]); + crate::abi::codegen_drop(fx, source_info, drop_place, *target); } }; } @@ -609,35 +606,44 @@ fn codegen_stmt<'tcx>( let lhs = codegen_operand(fx, &lhs_rhs.0); let rhs = codegen_operand(fx, &lhs_rhs.1); - let res = crate::num::codegen_binop(fx, bin_op, lhs, rhs); - lval.write_cvalue(fx, res); - } - Rvalue::CheckedBinaryOp(bin_op, ref lhs_rhs) => { - let lhs = codegen_operand(fx, &lhs_rhs.0); - let rhs = codegen_operand(fx, &lhs_rhs.1); - - let res = crate::num::codegen_checked_int_binop(fx, bin_op, lhs, rhs); + let res = if let Some(bin_op) = bin_op.overflowing_to_wrapping() { + crate::num::codegen_checked_int_binop(fx, bin_op, lhs, rhs) + } else { + crate::num::codegen_binop(fx, bin_op, lhs, rhs) + }; lval.write_cvalue(fx, res); } Rvalue::UnaryOp(un_op, ref operand) => { let operand = codegen_operand(fx, operand); let layout = operand.layout(); - let val = operand.load_scalar(fx); let res = match un_op { - UnOp::Not => match layout.ty.kind() { - ty::Bool => { - let res = fx.bcx.ins().icmp_imm(IntCC::Equal, val, 0); - CValue::by_val(res, layout) + UnOp::Not => { + let val = operand.load_scalar(fx); + match layout.ty.kind() { + ty::Bool => { + let res = fx.bcx.ins().icmp_imm(IntCC::Equal, val, 0); + CValue::by_val(res, layout) + } + ty::Uint(_) | ty::Int(_) => { + CValue::by_val(fx.bcx.ins().bnot(val), layout) + } + _ => unreachable!("un op Not for {:?}", layout.ty), } - ty::Uint(_) | ty::Int(_) => { - CValue::by_val(fx.bcx.ins().bnot(val), layout) + } + UnOp::Neg => { + let val = operand.load_scalar(fx); + match layout.ty.kind() { + ty::Int(_) => CValue::by_val(fx.bcx.ins().ineg(val), layout), + ty::Float(_) => CValue::by_val(fx.bcx.ins().fneg(val), layout), + _ => unreachable!("un op Neg for {:?}", layout.ty), } - _ => unreachable!("un op Not for {:?}", layout.ty), - }, - UnOp::Neg => match layout.ty.kind() { - ty::Int(_) => CValue::by_val(fx.bcx.ins().ineg(val), layout), - ty::Float(_) => CValue::by_val(fx.bcx.ins().fneg(val), layout), - _ => unreachable!("un op Neg for {:?}", layout.ty), + } + UnOp::PtrMetadata => match layout.abi { + Abi::Scalar(_) => CValue::zst(dest_layout), + Abi::ScalarPair(_, _) => { + CValue::by_val(operand.load_scalar_pair(fx).1, dest_layout) + } + _ => bug!("Unexpected `PtrToMetadata` operand: {operand:?}"), }, }; lval.write_cvalue(fx, res); @@ -671,21 +677,22 @@ fn codegen_stmt<'tcx>( CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer), ref operand, to_ty, - ) - | Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::MutToConstPointer), - ref operand, - to_ty, - ) - | Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::ArrayToPointer), - ref operand, - to_ty, ) => { let to_layout = fx.layout_of(fx.monomorphize(to_ty)); let operand = codegen_operand(fx, operand); lval.write_cvalue(fx, operand.cast_pointer_to(to_layout)); } + Rvalue::Cast( + CastKind::PointerCoercion( + PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, + ), + .., + ) => { + bug!( + "{:?} is for borrowck, and should never appear in codegen", + to_place_and_rval.1 + ); + } Rvalue::Cast( CastKind::IntToInt | CastKind::FloatToFloat @@ -826,9 +833,10 @@ fn codegen_stmt<'tcx>( let val = match null_op { NullOp::SizeOf => layout.size.bytes(), NullOp::AlignOf => layout.align.abi.bytes(), - NullOp::OffsetOf(fields) => { - layout.offset_of_subfield(fx, fields.iter()).bytes() - } + NullOp::OffsetOf(fields) => fx + .tcx + .offset_of_subfield(ParamEnv::reveal_all(), layout, fields.iter()) + .bytes(), NullOp::UbChecks => { let val = fx.tcx.sess.ub_checks(); let val = CValue::by_val( diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index 4a5ef352151f..e16b77648d12 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -70,6 +70,7 @@ pub(crate) fn maybe_codegen<'tcx>( } BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne | BinOp::Cmp => None, BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None, + BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow => unreachable!(), } } @@ -132,6 +133,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>( Some(out_place.to_cvalue(fx)) } BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(), + BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow => unreachable!(), BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"), BinOp::Div | BinOp::Rem => unreachable!(), BinOp::Cmp => unreachable!(), diff --git a/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs b/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs index a73860cf18b2..2093b49ff31a 100644 --- a/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs +++ b/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs @@ -1,6 +1,7 @@ use std::sync::{Arc, Condvar, Mutex}; use jobserver::HelperThread; +use rustc_errors::DiagCtxtHandle; use rustc_session::Session; // FIXME don't panic when a worker thread panics @@ -46,7 +47,7 @@ impl ConcurrencyLimiter { } } - pub(super) fn acquire(&self, dcx: &rustc_errors::DiagCtxt) -> ConcurrencyLimiterToken { + pub(super) fn acquire(&self, dcx: DiagCtxtHandle<'_>) -> ConcurrencyLimiterToken { let mut state = self.state.lock().unwrap(); loop { state.assert_invariants(); diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 64e83e43d327..87c5da3b7c3e 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -50,7 +50,7 @@ pub(crate) fn codegen_tls_ref<'tcx>( ) -> CValue<'tcx> { let tls_ptr = if !def_id.is_local() && fx.tcx.needs_thread_local_shim(def_id) { let instance = ty::Instance { - def: ty::InstanceDef::ThreadLocalShim(def_id), + def: ty::InstanceKind::ThreadLocalShim(def_id), args: ty::GenericArgs::empty(), }; let func_ref = fx.get_function_ref(instance); @@ -100,7 +100,7 @@ pub(crate) fn codegen_const_value<'tcx>( assert!(layout.is_sized(), "unsized const value"); if layout.is_zst() { - return CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout); + return CValue::zst(layout); } match const_val { @@ -110,7 +110,7 @@ pub(crate) fn codegen_const_value<'tcx>( if fx.clif_type(layout.ty).is_some() { return CValue::const_val(fx, layout, int); } else { - let raw_val = int.size().truncate(int.assert_bits(int.size())); + let raw_val = int.size().truncate(int.to_bits(int.size())); let val = match int.size().bytes() { 1 => fx.bcx.ins().iconst(types::I8, raw_val as i64), 2 => fx.bcx.ins().iconst(types::I16, raw_val as i64), @@ -501,12 +501,12 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( Ordering::Equal => scalar_int, Ordering::Less => match ty.kind() { ty::Uint(_) => ScalarInt::try_from_uint( - scalar_int.assert_uint(scalar_int.size()), + scalar_int.to_uint(scalar_int.size()), fx.layout_of(*ty).size, ) .unwrap(), ty::Int(_) => ScalarInt::try_from_int( - scalar_int.assert_int(scalar_int.size()), + scalar_int.to_int(scalar_int.size()), fx.layout_of(*ty).size, ) .unwrap(), diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index fce4690f97dc..dcafac21bc74 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -200,7 +200,7 @@ fn produce_final_output_artifacts( // to get rid of it. for output_type in crate_output.outputs.keys() { match *output_type { - OutputType::Bitcode => { + OutputType::Bitcode | OutputType::ThinLinkBitcode => { // Cranelift doesn't have bitcode // user_wants_bitcode = true; // // Copy to .bc, but always keep the .0.bc. There is a later @@ -288,6 +288,29 @@ fn produce_final_output_artifacts( } } + if sess.opts.json_artifact_notifications { + if codegen_results.modules.len() == 1 { + codegen_results.modules[0].for_each_output(|_path, ty| { + if sess.opts.output_types.contains_key(&ty) { + let descr = ty.shorthand(); + // for single cgu file is renamed to drop cgu specific suffix + // so we regenerate it the same way + let path = crate_output.path(ty); + sess.dcx().emit_artifact_notification(path.as_path(), descr); + } + }); + } else { + for module in &codegen_results.modules { + module.for_each_output(|path, ty| { + if sess.opts.output_types.contains_key(&ty) { + let descr = ty.shorthand(); + sess.dcx().emit_artifact_notification(&path, descr); + } + }); + } + } + } + // We leave the following files around by default: // - #crate#.o // - #crate#.crate.metadata.o diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index 2de804f5e042..c6b26dd873bd 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -4,6 +4,7 @@ use std::fmt::Write; use cranelift_codegen::isa::CallConv; use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_hir::LangItem; use rustc_span::sym; use rustc_target::asm::*; use target_lexicon::BinaryFormat; @@ -927,7 +928,7 @@ fn call_inline_asm<'tcx>( fn asm_clif_type<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> Option { match ty.kind() { // Adapted from https://github.com/rust-lang/rust/blob/f3c66088610c1b80110297c2d9a8b5f9265b013f/compiler/rustc_hir_analysis/src/check/intrinsicck.rs#L136-L151 - ty::Adt(adt, args) if Some(adt.did()) == fx.tcx.lang_items().maybe_uninit() => { + ty::Adt(adt, args) if fx.tcx.is_lang_item(adt.did(), LangItem::MaybeUninit) => { let fields = &adt.non_enum_variant().fields; let ty = fields[FieldIdx::from_u32(1)].ty(fx.tcx, args); let ty::Adt(ty, args) = ty.kind() else { diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs index 27b55ecc72ee..d454f3c1de7e 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs @@ -902,7 +902,7 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( .span_fatal(span, "Index argument for `_mm_cmpestri` is not a constant"); }; - let imm8 = imm8.try_to_u8().unwrap_or_else(|_| panic!("kind not scalar: {:?}", imm8)); + let imm8 = imm8.to_u8(); codegen_inline_asm_inner( fx, @@ -955,7 +955,7 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( .span_fatal(span, "Index argument for `_mm_cmpestrm` is not a constant"); }; - let imm8 = imm8.try_to_u8().unwrap_or_else(|_| panic!("kind not scalar: {:?}", imm8)); + let imm8 = imm8.to_u8(); codegen_inline_asm_inner( fx, @@ -1003,7 +1003,7 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( ); }; - let imm8 = imm8.try_to_u8().unwrap_or_else(|_| panic!("kind not scalar: {:?}", imm8)); + let imm8 = imm8.to_u8(); codegen_inline_asm_inner( fx, @@ -1040,7 +1040,7 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( ); }; - let imm8 = imm8.try_to_u8().unwrap_or_else(|_| panic!("kind not scalar: {:?}", imm8)); + let imm8 = imm8.to_u8(); codegen_inline_asm_inner( fx, @@ -1195,7 +1195,7 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( .span_fatal(span, "Func argument for `_mm_sha1rnds4_epu32` is not a constant"); }; - let func = func.try_to_u8().unwrap_or_else(|_| panic!("kind not scalar: {:?}", func)); + let func = func.to_u8(); codegen_inline_asm_inner( fx, diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index cafdc051db5a..b21c559e6686 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1261,7 +1261,7 @@ fn codegen_regular_intrinsic_call<'tcx>( } // Unimplemented intrinsics must have a fallback body. The fallback body is obtained - // by converting the `InstanceDef::Intrinsic` to an `InstanceDef::Item`. + // by converting the `InstanceKind::Intrinsic` to an `InstanceKind::Item`. _ => { let intrinsic = fx.tcx.intrinsic(instance.def_id()).unwrap(); if intrinsic.must_be_overridden { diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index 452b5988dab4..ca910dccb0d0 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -133,6 +133,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( .expect_const() .eval(fx.tcx, ty::ParamEnv::reveal_all(), span) .unwrap() + .1 .unwrap_branch(); assert_eq!(x.layout(), y.layout()); @@ -146,8 +147,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let total_len = lane_count * 2; - let indexes = - idx.iter().map(|idx| idx.unwrap_leaf().try_to_u32().unwrap()).collect::>(); + let indexes = idx.iter().map(|idx| idx.unwrap_leaf().to_u32()).collect::>(); for &idx in &indexes { assert!(u64::from(idx) < total_len, "idx {} out of range 0..{}", idx, total_len); @@ -281,9 +281,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( fx.tcx.dcx().span_fatal(span, "Index argument for `simd_insert` is not a constant"); }; - let idx: u32 = idx_const - .try_to_u32() - .unwrap_or_else(|_| panic!("kind not scalar: {:?}", idx_const)); + let idx: u32 = idx_const.to_u32(); let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx); if u64::from(idx) >= lane_count { fx.tcx.dcx().span_fatal( @@ -329,9 +327,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( return; }; - let idx = idx_const - .try_to_u32() - .unwrap_or_else(|_| panic!("kind not scalar: {:?}", idx_const)); + let idx = idx_const.to_u32(); let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx); if u64::from(idx) >= lane_count { fx.tcx.dcx().span_fatal( @@ -348,6 +344,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( | sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctlz + | sym::simd_ctpop | sym::simd_cttz => { intrinsic_args!(fx, args => (a); intrinsic); @@ -367,6 +364,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( (ty::Uint(_) | ty::Int(_), sym::simd_bswap) => fx.bcx.ins().bswap(lane), (ty::Uint(_) | ty::Int(_), sym::simd_bitreverse) => fx.bcx.ins().bitrev(lane), (ty::Uint(_) | ty::Int(_), sym::simd_ctlz) => fx.bcx.ins().clz(lane), + (ty::Uint(_) | ty::Int(_), sym::simd_ctpop) => fx.bcx.ins().popcnt(lane), (ty::Uint(_) | ty::Int(_), sym::simd_cttz) => fx.bcx.ins().ctz(lane), _ => unreachable!(), diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 39bbad16b0c0..2edb34e7c20d 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -1,13 +1,16 @@ -#![cfg_attr(doc, allow(internal_features))] -#![cfg_attr(doc, feature(rustdoc_internals))] -#![cfg_attr(doc, doc(rust_logo))] -#![feature(rustc_private)] -// Note: please avoid adding other feature gates where possible +// tidy-alphabetical-start #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(doc, allow(internal_features))] +#![cfg_attr(doc, doc(rust_logo))] +#![cfg_attr(doc, feature(rustdoc_internals))] +// Note: please avoid adding other feature gates where possible +#![feature(rustc_private)] +// Note: please avoid adding other feature gates where possible #![warn(rust_2018_idioms)] -#![warn(unused_lifetimes)] #![warn(unreachable_pub)] +#![warn(unused_lifetimes)] +// tidy-alphabetical-end extern crate jobserver; #[macro_use] @@ -95,7 +98,7 @@ mod prelude { pub(crate) use rustc_middle::mir::{self, *}; pub(crate) use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; pub(crate) use rustc_middle::ty::{ - self, FloatTy, Instance, InstanceDef, IntTy, ParamEnv, Ty, TyCtxt, UintTy, + self, FloatTy, Instance, InstanceKind, IntTy, ParamEnv, Ty, TyCtxt, UintTy, }; pub(crate) use rustc_span::Span; pub(crate) use rustc_target::abi::{Abi, FieldIdx, Scalar, Size, VariantIdx, FIRST_VARIANT}; diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs index 4d96a26ea4fa..fb18f45d7dca 100644 --- a/compiler/rustc_codegen_cranelift/src/num.rs +++ b/compiler/rustc_codegen_cranelift/src/num.rs @@ -179,6 +179,9 @@ pub(crate) fn codegen_int_binop<'tcx>( } } BinOp::Offset => unreachable!("Offset is not an integer operation"), + BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow => { + unreachable!("Overflow binops handled by `codegen_checked_int_binop`") + } // Compare binops handles by `codegen_binop`. BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge | BinOp::Cmp => { unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty); diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 4acbc8a27edb..967aa53abbda 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -39,8 +39,7 @@ pub(crate) fn unsized_info<'tcx>( } // trait upcasting coercion - let vptr_entry_idx = - fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((source, target)); + let vptr_entry_idx = fx.tcx.supertrait_vtable_slot((source, target)); if let Some(entry_idx) = vptr_entry_idx { let entry_idx = u32::try_from(entry_idx).unwrap(); diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 4146137c2263..1aa28daeafc7 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -95,6 +95,14 @@ impl<'tcx> CValue<'tcx> { CValue(CValueInner::ByValPair(value, extra), layout) } + /// Create an instance of a ZST + /// + /// The is represented by a dangling pointer of suitable alignment. + pub(crate) fn zst(layout: TyAndLayout<'tcx>) -> CValue<'tcx> { + assert!(layout.is_zst()); + CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout) + } + pub(crate) fn layout(&self) -> TyAndLayout<'tcx> { self.1 } @@ -319,7 +327,7 @@ impl<'tcx> CValue<'tcx> { let val = match layout.ty.kind() { ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => { - let const_val = const_val.assert_bits(layout.size); + let const_val = const_val.to_bits(layout.size); let lsb = fx.bcx.ins().iconst(types::I64, const_val as u64 as i64); let msb = fx.bcx.ins().iconst(types::I64, (const_val >> 64) as u64 as i64); fx.bcx.ins().iconcat(lsb, msb) @@ -331,7 +339,7 @@ impl<'tcx> CValue<'tcx> { | ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..) => { - let raw_val = const_val.size().truncate(const_val.assert_bits(layout.size)); + let raw_val = const_val.size().truncate(const_val.to_bits(layout.size)); fx.bcx.ins().iconst(clif_ty, raw_val as i64) } ty::Float(FloatTy::F32) => { diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index 61e0f203ee0b..ec70fbdddb0f 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -28,7 +28,7 @@ use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind}; use rustc_data_structures::memmap::Mmap; -use rustc_errors::{DiagCtxt, FatalError}; +use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::dep_graph::WorkProduct; use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; @@ -59,7 +59,7 @@ struct LtoData { fn prepare_lto( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, ) -> Result { let export_threshold = match cgcx.lto { // We're just doing LTO for our one crate @@ -179,12 +179,13 @@ pub(crate) fn run_fat( cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Result, FatalError> { let dcx = cgcx.create_dcx(); - let lto_data = prepare_lto(cgcx, &dcx)?; + let dcx = dcx.handle(); + let lto_data = prepare_lto(cgcx, dcx)?; /*let symbols_below_threshold = lto_data.symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>();*/ fat_lto( cgcx, - &dcx, + dcx, modules, cached_modules, lto_data.upstream_modules, @@ -195,7 +196,7 @@ pub(crate) fn run_fat( fn fat_lto( cgcx: &CodegenContext, - _dcx: &DiagCtxt, + _dcx: DiagCtxtHandle<'_>, modules: Vec>, cached_modules: Vec<(SerializedModule, WorkProduct)>, mut serialized_modules: Vec<(SerializedModule, CString)>, diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index 3ea5be1ee562..b9c7f72d0b72 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -4,7 +4,7 @@ use gccjit::OutputKind; use rustc_codegen_ssa::back::link::ensure_removed; use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig}; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; -use rustc_errors::DiagCtxt; +use rustc_errors::DiagCtxtHandle; use rustc_fs_util::link_or_copy; use rustc_session::config::OutputType; use rustc_span::fatal_error::FatalError; @@ -15,7 +15,7 @@ use crate::{GccCodegenBackend, GccContext}; pub(crate) unsafe fn codegen( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, config: &ModuleConfig, ) -> Result { @@ -166,7 +166,7 @@ pub(crate) unsafe fn codegen( pub(crate) fn link( _cgcx: &CodegenContext, - _dcx: &DiagCtxt, + _dcx: DiagCtxtHandle<'_>, mut _modules: Vec>, ) -> Result, FatalError> { unimplemented!(); diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 78d943192db0..548c23cc7948 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -166,7 +166,7 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { Scalar::Int(int) => { - let data = int.assert_bits(layout.size(self)); + let data = int.to_bits(layout.size(self)); // FIXME(antoyo): there's some issues with using the u128 code that follows, so hard-code // the paths for floating-point values. diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index aee2b077dba1..6bada3d334ce 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/src/errors.rs @@ -1,4 +1,4 @@ -use rustc_errors::{Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level}; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::Span; @@ -90,13 +90,13 @@ pub(crate) struct TargetFeatureDisableOrEnable<'a> { pub(crate) struct MissingFeatures; impl Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> { - fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let mut diag = Diag::new(dcx, level, fluent::codegen_gcc_target_feature_disable_or_enable); if let Some(span) = self.span { diag.span(span); }; if let Some(missing_features) = self.missing_features { - diag.subdiagnostic(dcx, missing_features); + diag.subdiagnostic(missing_features); } diag.arg("features", self.features.join(", ")); diag diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 0c7cffbe7308..1625e6ecdb70 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -14,6 +14,7 @@ use rustc_codegen_ssa::mir::operand::OperandRef; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods}; use rustc_hir as hir; +use rustc_middle::mir::BinOp; use rustc_middle::span_bug; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{self, Ty}; @@ -122,12 +123,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let in_ty = arg_tys[0]; let comparison = match name { - sym::simd_eq => Some(hir::BinOpKind::Eq), - sym::simd_ne => Some(hir::BinOpKind::Ne), - sym::simd_lt => Some(hir::BinOpKind::Lt), - sym::simd_le => Some(hir::BinOpKind::Le), - sym::simd_gt => Some(hir::BinOpKind::Gt), - sym::simd_ge => Some(hir::BinOpKind::Ge), + sym::simd_eq => Some(BinOp::Eq), + sym::simd_ne => Some(BinOp::Ne), + sym::simd_lt => Some(BinOp::Lt), + sym::simd_le => Some(BinOp::Le), + sym::simd_gt => Some(BinOp::Gt), + sym::simd_ge => Some(BinOp::Ge), _ => None, }; diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 3f2fadce9e4d..24856506c464 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -16,13 +16,7 @@ #![allow(internal_features)] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![feature( - rustc_private, - decl_macro, - never_type, - trusted_len, - hash_raw_entry -)] +#![feature(rustc_private, decl_macro, never_type, trusted_len, hash_raw_entry)] #![allow(broken_intra_doc_links)] #![recursion_limit = "256"] #![warn(rust_2018_idioms)] @@ -104,7 +98,7 @@ use rustc_codegen_ssa::traits::{ use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::IntoDynSyncSend; -use rustc_errors::{DiagCtxt, ErrorGuaranteed}; +use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; @@ -335,6 +329,10 @@ impl ThinBufferMethods for ThinBuffer { fn data(&self) -> &[u8] { unimplemented!(); } + + fn thin_link_data(&self) -> &[u8] { + unimplemented!(); + } } pub struct GccContext { @@ -382,7 +380,7 @@ impl WriteBackendMethods for GccCodegenBackend { unsafe fn optimize( _cgcx: &CodegenContext, - _dcx: &DiagCtxt, + _dcx: DiagCtxtHandle<'_>, module: &ModuleCodegen, config: &ModuleConfig, ) -> Result<(), FatalError> { @@ -407,14 +405,17 @@ impl WriteBackendMethods for GccCodegenBackend { unsafe fn codegen( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, config: &ModuleConfig, ) -> Result { back::write::codegen(cgcx, dcx, module, config) } - fn prepare_thin(_module: ModuleCodegen) -> (String, Self::ThinBuffer) { + fn prepare_thin( + _module: ModuleCodegen, + _emit_summary: bool, + ) -> (String, Self::ThinBuffer) { unimplemented!(); } @@ -424,7 +425,7 @@ impl WriteBackendMethods for GccCodegenBackend { fn run_link( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, modules: Vec>, ) -> Result, FatalError> { back::write::link(cgcx, dcx, modules) diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 2155cabe171c..a88d50cb4340 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -5,7 +5,7 @@ use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, LayoutTypeM use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TypeVisitableExt}; use rustc_target::abi::call::{CastTarget, FnAbi, Reg}; use rustc_target::abi::{ self, Abi, Align, FieldsShape, Float, Int, Integer, PointeeInfo, Pointer, Size, TyAbiInterface, diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index d14fe0299e64..1c126e797621 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -18,6 +18,8 @@ codegen_llvm_error_creating_import_library = codegen_llvm_error_writing_def_file = Error writing .DEF file: {$error} +codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture + codegen_llvm_from_llvm_diag = {$message} codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message} diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 71d4343ee07f..60e63b956db6 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -16,6 +16,7 @@ use rustc_middle::{bug, span_bug, ty::Instance}; use rustc_span::{Pos, Span}; use rustc_target::abi::*; use rustc_target::asm::*; +use tracing::debug; use libc::{c_char, c_uint}; use smallvec::SmallVec; @@ -958,6 +959,43 @@ fn llvm_fixup_input<'ll, 'tcx>( InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::zmm_reg), Abi::Vector { .. }, ) if layout.size.bytes() == 64 => bx.bitcast(value, bx.cx.type_vector(bx.cx.type_f64(), 8)), + ( + InlineAsmRegClass::X86( + X86InlineAsmRegClass::xmm_reg + | X86InlineAsmRegClass::ymm_reg + | X86InlineAsmRegClass::zmm_reg, + ), + Abi::Scalar(s), + ) if bx.sess().asm_arch == Some(InlineAsmArch::X86) + && s.primitive() == Primitive::Float(Float::F128) => + { + bx.bitcast(value, bx.type_vector(bx.type_i32(), 4)) + } + ( + InlineAsmRegClass::X86( + X86InlineAsmRegClass::xmm_reg + | X86InlineAsmRegClass::ymm_reg + | X86InlineAsmRegClass::zmm_reg, + ), + Abi::Scalar(s), + ) if s.primitive() == Primitive::Float(Float::F16) => { + let value = bx.insert_element( + bx.const_undef(bx.type_vector(bx.type_f16(), 8)), + value, + bx.const_usize(0), + ); + bx.bitcast(value, bx.type_vector(bx.type_i16(), 8)) + } + ( + InlineAsmRegClass::X86( + X86InlineAsmRegClass::xmm_reg + | X86InlineAsmRegClass::ymm_reg + | X86InlineAsmRegClass::zmm_reg, + ), + Abi::Vector { element, count: count @ (8 | 16) }, + ) if element.primitive() == Primitive::Float(Float::F16) => { + bx.bitcast(value, bx.type_vector(bx.type_i16(), count)) + } ( InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg | ArmInlineAsmRegClass::sreg_low16), Abi::Scalar(s), @@ -1035,6 +1073,39 @@ fn llvm_fixup_output<'ll, 'tcx>( InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::zmm_reg), Abi::Vector { .. }, ) if layout.size.bytes() == 64 => bx.bitcast(value, layout.llvm_type(bx.cx)), + ( + InlineAsmRegClass::X86( + X86InlineAsmRegClass::xmm_reg + | X86InlineAsmRegClass::ymm_reg + | X86InlineAsmRegClass::zmm_reg, + ), + Abi::Scalar(s), + ) if bx.sess().asm_arch == Some(InlineAsmArch::X86) + && s.primitive() == Primitive::Float(Float::F128) => + { + bx.bitcast(value, bx.type_f128()) + } + ( + InlineAsmRegClass::X86( + X86InlineAsmRegClass::xmm_reg + | X86InlineAsmRegClass::ymm_reg + | X86InlineAsmRegClass::zmm_reg, + ), + Abi::Scalar(s), + ) if s.primitive() == Primitive::Float(Float::F16) => { + let value = bx.bitcast(value, bx.type_vector(bx.type_f16(), 8)); + bx.extract_element(value, bx.const_usize(0)) + } + ( + InlineAsmRegClass::X86( + X86InlineAsmRegClass::xmm_reg + | X86InlineAsmRegClass::ymm_reg + | X86InlineAsmRegClass::zmm_reg, + ), + Abi::Vector { element, count: count @ (8 | 16) }, + ) if element.primitive() == Primitive::Float(Float::F16) => { + bx.bitcast(value, bx.type_vector(bx.type_f16(), count)) + } ( InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg | ArmInlineAsmRegClass::sreg_low16), Abi::Scalar(s), @@ -1108,6 +1179,36 @@ fn llvm_fixup_output_type<'ll, 'tcx>( InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::zmm_reg), Abi::Vector { .. }, ) if layout.size.bytes() == 64 => cx.type_vector(cx.type_f64(), 8), + ( + InlineAsmRegClass::X86( + X86InlineAsmRegClass::xmm_reg + | X86InlineAsmRegClass::ymm_reg + | X86InlineAsmRegClass::zmm_reg, + ), + Abi::Scalar(s), + ) if cx.sess().asm_arch == Some(InlineAsmArch::X86) + && s.primitive() == Primitive::Float(Float::F128) => + { + cx.type_vector(cx.type_i32(), 4) + } + ( + InlineAsmRegClass::X86( + X86InlineAsmRegClass::xmm_reg + | X86InlineAsmRegClass::ymm_reg + | X86InlineAsmRegClass::zmm_reg, + ), + Abi::Scalar(s), + ) if s.primitive() == Primitive::Float(Float::F16) => cx.type_vector(cx.type_i16(), 8), + ( + InlineAsmRegClass::X86( + X86InlineAsmRegClass::xmm_reg + | X86InlineAsmRegClass::ymm_reg + | X86InlineAsmRegClass::zmm_reg, + ), + Abi::Vector { element, count: count @ (8 | 16) }, + ) if element.primitive() == Primitive::Float(Float::F16) => { + cx.type_vector(cx.type_i16(), count) + } ( InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg | ArmInlineAsmRegClass::sreg_low16), Abi::Scalar(s), diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index d4a3e39cef72..a354f3d35361 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -18,6 +18,7 @@ use rustc_codegen_ssa::back::archive::{ get_native_object_symbols, try_extract_macho_fat_archive, ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder, UnknownArchiveKind, }; +use tracing::trace; use rustc_session::cstore::DllImport; use rustc_session::Session; @@ -127,11 +128,7 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { is_direct_dependency: bool, ) -> PathBuf { let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; - let output_path = { - let mut output_path: PathBuf = tmpdir.to_path_buf(); - output_path.push(format!("{lib_name}{name_suffix}")); - output_path.with_extension("lib") - }; + let output_path = tmpdir.join(format!("{lib_name}{name_suffix}.lib")); let target = &sess.target; let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(target); @@ -156,8 +153,7 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { // that loaded but crashed with an AV upon calling one of the imported // functions. Therefore, use binutils to create the import library instead, // by writing a .DEF file to the temp dir and calling binutils's dlltool. - let def_file_path = - tmpdir.join(format!("{lib_name}{name_suffix}")).with_extension("def"); + let def_file_path = tmpdir.join(format!("{lib_name}{name_suffix}.def")); let def_file_content = format!( "EXPORTS\n{}", @@ -200,21 +196,20 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { _ => panic!("unsupported arch {}", sess.target.arch), }; let mut dlltool_cmd = std::process::Command::new(&dlltool); - dlltool_cmd.args([ - "-d", - def_file_path.to_str().unwrap(), - "-D", - lib_name, - "-l", - output_path.to_str().unwrap(), - "-m", - dlltool_target_arch, - "-f", - dlltool_target_bitness, - "--no-leading-underscore", - "--temp-prefix", - temp_prefix.to_str().unwrap(), - ]); + dlltool_cmd + .arg("-d") + .arg(def_file_path) + .arg("-D") + .arg(lib_name) + .arg("-l") + .arg(&output_path) + .arg("-m") + .arg(dlltool_target_arch) + .arg("-f") + .arg(dlltool_target_bitness) + .arg("--no-leading-underscore") + .arg("--temp-prefix") + .arg(temp_prefix); match dlltool_cmd.output() { Err(e) => { diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index e61af863dc07..aff3e3d70760 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -14,12 +14,13 @@ use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::memmap::Mmap; -use rustc_errors::{DiagCtxt, FatalError}; +use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; use rustc_middle::dep_graph::WorkProduct; use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; use rustc_session::config::{self, CrateType, Lto}; +use tracing::{debug, info}; use std::collections::BTreeMap; use std::ffi::{CStr, CString}; @@ -48,7 +49,7 @@ pub fn crate_type_allows_lto(crate_type: CrateType) -> bool { fn prepare_lto( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, ) -> Result<(Vec, Vec<(SerializedModule, CString)>), FatalError> { let export_threshold = match cgcx.lto { // We're just doing LTO for our one crate @@ -202,10 +203,11 @@ pub(crate) fn run_fat( cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Result, FatalError> { let dcx = cgcx.create_dcx(); - let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, &dcx)?; + let dcx = dcx.handle(); + let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?; let symbols_below_threshold = symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>(); - fat_lto(cgcx, &dcx, modules, cached_modules, upstream_modules, &symbols_below_threshold) + fat_lto(cgcx, dcx, modules, cached_modules, upstream_modules, &symbols_below_threshold) } /// Performs thin LTO by performing necessary global analysis and returning two @@ -217,7 +219,8 @@ pub(crate) fn run_thin( cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Result<(Vec>, Vec), FatalError> { let dcx = cgcx.create_dcx(); - let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, &dcx)?; + let dcx = dcx.handle(); + let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?; let symbols_below_threshold = symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>(); if cgcx.opts.cg.linker_plugin_lto.enabled() { @@ -226,18 +229,21 @@ pub(crate) fn run_thin( is deferred to the linker" ); } - thin_lto(cgcx, &dcx, modules, upstream_modules, cached_modules, &symbols_below_threshold) + thin_lto(cgcx, dcx, modules, upstream_modules, cached_modules, &symbols_below_threshold) } -pub(crate) fn prepare_thin(module: ModuleCodegen) -> (String, ThinBuffer) { +pub(crate) fn prepare_thin( + module: ModuleCodegen, + emit_summary: bool, +) -> (String, ThinBuffer) { let name = module.name; - let buffer = ThinBuffer::new(module.module_llvm.llmod(), true); + let buffer = ThinBuffer::new(module.module_llvm.llmod(), true, emit_summary); (name, buffer) } fn fat_lto( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, modules: Vec>, cached_modules: Vec<(SerializedModule, WorkProduct)>, mut serialized_modules: Vec<(SerializedModule, CString)>, @@ -432,7 +438,7 @@ impl Drop for Linker<'_> { /// they all go out of scope. fn thin_lto( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, modules: Vec<(String, ThinBuffer)>, serialized_modules: Vec<(SerializedModule, CString)>, cached_modules: Vec<(SerializedModule, WorkProduct)>, @@ -589,7 +595,7 @@ fn thin_lto( pub(crate) fn run_pass_manager( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, module: &mut ModuleCodegen, thin: bool, ) -> Result<(), FatalError> { @@ -671,9 +677,9 @@ unsafe impl Send for ThinBuffer {} unsafe impl Sync for ThinBuffer {} impl ThinBuffer { - pub fn new(m: &llvm::Module, is_thin: bool) -> ThinBuffer { + pub fn new(m: &llvm::Module, is_thin: bool, emit_summary: bool) -> ThinBuffer { unsafe { - let buffer = llvm::LLVMRustThinLTOBufferCreate(m, is_thin); + let buffer = llvm::LLVMRustThinLTOBufferCreate(m, is_thin, emit_summary); ThinBuffer(buffer) } } @@ -687,6 +693,14 @@ impl ThinBufferMethods for ThinBuffer { slice::from_raw_parts(ptr, len) } } + + fn thin_link_data(&self) -> &[u8] { + unsafe { + let ptr = llvm::LLVMRustThinLTOBufferThinLinkDataPtr(self.0) as *const _; + let len = llvm::LLVMRustThinLTOBufferThinLinkDataLen(self.0); + slice::from_raw_parts(ptr, len) + } + } } impl Drop for ThinBuffer { @@ -702,10 +716,11 @@ pub unsafe fn optimize_thin_module( cgcx: &CodegenContext, ) -> Result, FatalError> { let dcx = cgcx.create_dcx(); + let dcx = dcx.handle(); let module_name = &thin_module.shared.module_names[thin_module.idx]; let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, module_name.to_str().unwrap()); - let tm = (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&dcx, e))?; + let tm = (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(dcx, e))?; // Right now the implementation we've got only works over serialized // modules, so we create a fresh new LLVM context and parse the module @@ -713,7 +728,7 @@ pub unsafe fn optimize_thin_module( // crates but for locally codegened modules we may be able to reuse // that LLVM Context and Module. let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); - let llmod_raw = parse_module(llcx, module_name, thin_module.data(), &dcx)? as *const _; + let llmod_raw = parse_module(llcx, module_name, thin_module.data(), dcx)? as *const _; let mut module = ModuleCodegen { module_llvm: ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(tm) }, name: thin_module.name().to_string(), @@ -736,7 +751,7 @@ pub unsafe fn optimize_thin_module( let _timer = cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name()); if !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) { - return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule)); + return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule)); } save_temp_bitcode(cgcx, &module, "thin-lto-after-rename"); } @@ -746,7 +761,7 @@ pub unsafe fn optimize_thin_module( .prof .generic_activity_with_arg("LLVM_thin_lto_resolve_weak", thin_module.name()); if !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) { - return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule)); + return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule)); } save_temp_bitcode(cgcx, &module, "thin-lto-after-resolve"); } @@ -756,7 +771,7 @@ pub unsafe fn optimize_thin_module( .prof .generic_activity_with_arg("LLVM_thin_lto_internalize", thin_module.name()); if !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) { - return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule)); + return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule)); } save_temp_bitcode(cgcx, &module, "thin-lto-after-internalize"); } @@ -765,7 +780,7 @@ pub unsafe fn optimize_thin_module( let _timer = cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name()); if !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, target) { - return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule)); + return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule)); } save_temp_bitcode(cgcx, &module, "thin-lto-after-import"); } @@ -777,7 +792,7 @@ pub unsafe fn optimize_thin_module( // little differently. { info!("running thin lto passes over {}", module.name); - run_pass_manager(cgcx, &dcx, &mut module, true)?; + run_pass_manager(cgcx, dcx, &mut module, true)?; save_temp_bitcode(cgcx, &module, "thin-lto-after-pm"); } } @@ -847,7 +862,7 @@ pub fn parse_module<'a>( cx: &'a llvm::Context, name: &CStr, data: &[u8], - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, ) -> Result<&'a llvm::Module, FatalError> { unsafe { llvm::LLVMRustParseBitcodeForLTO(cx, data.as_ptr(), data.len(), name.as_ptr()) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 49f9d7ddab65..bbfc697407bf 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -26,7 +26,7 @@ use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::small_c_str::SmallCStr; -use rustc_errors::{DiagCtxt, FatalError, Level}; +use rustc_errors::{DiagCtxtHandle, FatalError, Level}; use rustc_fs_util::{link_or_copy, path_to_c_string}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{self, Lto, OutputType, Passes}; @@ -35,6 +35,7 @@ use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::InnerSpan; use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; +use tracing::debug; use crate::llvm::diagnostic::OptimizationDiagnosticKind; use libc::{c_char, c_int, c_void, size_t}; @@ -46,7 +47,7 @@ use std::slice; use std::str; use std::sync::Arc; -pub fn llvm_err<'a>(dcx: &rustc_errors::DiagCtxt, err: LlvmError<'a>) -> FatalError { +pub fn llvm_err<'a>(dcx: DiagCtxtHandle<'_>, err: LlvmError<'a>) -> FatalError { match llvm::last_error() { Some(llvm_err) => dcx.emit_almost_fatal(WithLlvmError(err, llvm_err)), None => dcx.emit_almost_fatal(err), @@ -54,7 +55,7 @@ pub fn llvm_err<'a>(dcx: &rustc_errors::DiagCtxt, err: LlvmError<'a>) -> FatalEr } pub fn write_output_file<'ll>( - dcx: &rustc_errors::DiagCtxt, + dcx: DiagCtxtHandle<'_>, target: &'ll llvm::TargetMachine, pm: &llvm::PassManager<'ll>, m: &'ll llvm::Module, @@ -330,7 +331,7 @@ pub enum CodegenDiagnosticsStage { } pub struct DiagnosticHandlers<'a> { - data: *mut (&'a CodegenContext, &'a DiagCtxt), + data: *mut (&'a CodegenContext, DiagCtxtHandle<'a>), llcx: &'a llvm::Context, old_handler: Option<&'a llvm::DiagnosticHandler>, } @@ -338,7 +339,7 @@ pub struct DiagnosticHandlers<'a> { impl<'a> DiagnosticHandlers<'a> { pub fn new( cgcx: &'a CodegenContext, - dcx: &'a DiagCtxt, + dcx: DiagCtxtHandle<'a>, llcx: &'a llvm::Context, module: &ModuleCodegen, stage: CodegenDiagnosticsStage, @@ -427,7 +428,7 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void if user.is_null() { return; } - let (cgcx, dcx) = *(user as *const (&CodegenContext, &DiagCtxt)); + let (cgcx, dcx) = *(user as *const (&CodegenContext, DiagCtxtHandle<'_>)); match llvm::diagnostic::Diagnostic::unpack(info) { llvm::diagnostic::InlineAsm(inline) => { @@ -505,7 +506,7 @@ fn get_instr_profile_output_path(config: &ModuleConfig) -> Option { pub(crate) unsafe fn llvm_optimize( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, module: &ModuleCodegen, config: &ModuleConfig, opt_level: config::OptLevel, @@ -603,7 +604,7 @@ pub(crate) unsafe fn llvm_optimize( // Unsafe due to LLVM calls. pub(crate) unsafe fn optimize( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, module: &ModuleCodegen, config: &ModuleConfig, ) -> Result<(), FatalError> { @@ -636,7 +637,7 @@ pub(crate) unsafe fn optimize( pub(crate) fn link( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, mut modules: Vec>, ) -> Result, FatalError> { use super::lto::{Linker, ModuleBuffer}; @@ -659,7 +660,7 @@ pub(crate) fn link( pub(crate) unsafe fn codegen( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, config: &ModuleConfig, ) -> Result { @@ -708,13 +709,15 @@ pub(crate) unsafe fn codegen( // asm from LLVM and use `gcc` to create the object file. let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); + let bc_summary_out = + cgcx.output_filenames.temp_path(OutputType::ThinLinkBitcode, module_name); let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); if config.bitcode_needed() { let _timer = cgcx .prof .generic_activity_with_arg("LLVM_module_codegen_make_bitcode", &*module.name); - let thin = ThinBuffer::new(llmod, config.emit_thin_lto); + let thin = ThinBuffer::new(llmod, config.emit_thin_lto, config.emit_thin_lto_summary); let data = thin.data(); if let Some(bitcode_filename) = bc_out.file_name() { @@ -725,6 +728,25 @@ pub(crate) unsafe fn codegen( ); } + if config.emit_thin_lto_summary + && let Some(thin_link_bitcode_filename) = bc_summary_out.file_name() + { + let summary_data = thin.thin_link_data(); + cgcx.prof.artifact_size( + "llvm_bitcode_summary", + thin_link_bitcode_filename.to_string_lossy(), + summary_data.len() as u64, + ); + + let _timer = cgcx.prof.generic_activity_with_arg( + "LLVM_module_codegen_emit_bitcode_summary", + &*module.name, + ); + if let Err(err) = fs::write(&bc_summary_out, summary_data) { + dcx.emit_err(WriteBytecode { path: &bc_summary_out, err }); + } + } + if config.emit_bc || config.emit_obj == EmitObj::Bitcode { let _timer = cgcx .prof diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 230c58dfcafb..72ff9ea118e2 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -30,6 +30,7 @@ use std::borrow::Cow; use std::iter; use std::ops::Deref; use std::ptr; +use tracing::{debug, instrument}; // All Builders must have an llfn associated with them #[must_use] diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index e675362ac338..659c6ae0d863 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -12,6 +12,7 @@ use crate::value::Value; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt}; use rustc_middle::ty::{self, Instance, TypeVisitableExt}; +use tracing::debug; /// Codegens a reference to a fn/method item, monomorphizing and /// inlining as it goes. diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index ec33ce6292ac..4ffc92eb6335 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -19,6 +19,7 @@ use rustc_target::spec::Target; use libc::{c_char, c_uint}; use std::fmt::Write; +use tracing::debug; /* * A note on nomenclature of linking: "extern", "foreign", and "upcall". @@ -243,7 +244,7 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { Scalar::Int(int) => { - let data = int.assert_bits(layout.size(self)); + let data = int.to_bits(layout.size(self)); let llval = self.const_uint_big(self.type_ix(bitsize), data); if matches!(layout.primitive(), Pointer(_)) { unsafe { llvm::LLVMConstIntToPtr(llval, llty) } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 9e85c2d88f9b..a2314f4850c7 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -25,6 +25,7 @@ use rustc_target::abi::{ Align, AlignFromBytesError, HasDataLayout, Primitive, Scalar, Size, WrappingRange, }; use std::ops::Range; +use tracing::{debug, instrument, trace}; pub fn const_alloc_to_llvm<'ll>( cx: &CodegenCx<'ll, '_>, diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 53a098d178e4..7d92888feeed 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -142,6 +142,14 @@ pub unsafe fn create_module<'ll>( } } + if llvm_version < (19, 0, 0) { + if sess.target.arch == "loongarch64" { + // LLVM 19 updates the LoongArch64 data layout. + // See https://github.com/llvm/llvm-project/pull/93814 + target_data_layout = target_data_layout.replace("-n32:64", "-n64"); + } + } + // Ensure the data-layout values hardcoded remain the defaults. { let tm = crate::back::write::create_informational_target_machine(tcx.sess); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index 12a846a49ec1..584d033d6bdd 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -118,7 +118,7 @@ pub mod mcdc { #[derive(Clone, Copy, Debug, Default)] pub struct DecisionParameters { bitmap_idx: u32, - conditions_num: u16, + num_conditions: u16, } // ConditionId in llvm is `unsigned int` at 18 while `int16_t` at [19](https://github.com/llvm/llvm-project/pull/81257) @@ -177,8 +177,9 @@ pub mod mcdc { } impl From for DecisionParameters { - fn from(value: DecisionInfo) -> Self { - Self { bitmap_idx: value.bitmap_idx, conditions_num: value.conditions_num } + fn from(info: DecisionInfo) -> Self { + let DecisionInfo { bitmap_idx, num_conditions } = info; + Self { bitmap_idx, num_conditions } } } } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs index d85d9411f03b..b969fe27a99b 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs @@ -9,6 +9,7 @@ use rustc_middle::mir::coverage::{ }; use rustc_middle::ty::Instance; use rustc_span::Symbol; +use tracing::{debug, instrument}; /// Holds all of the coverage mapping data associated with a function instance, /// collected during traversal of `Coverage` statements in the function's MIR. diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 3f3969bbca35..d2c0f20c285b 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -14,6 +14,7 @@ use rustc_middle::mir; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::def_id::DefIdSet; use rustc_span::Symbol; +use tracing::debug; /// Generates and exports the Coverage Map. /// diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 26ea95f0f0d5..7b7f8c885bbb 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -17,6 +17,7 @@ use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::Instance; use rustc_target::abi::{Align, Size}; +use tracing::{debug, instrument}; use std::cell::RefCell; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index f44738ba6421..8de4e0effad2 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -31,16 +31,18 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{ - self, AdtKind, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt, Visibility, + self, AdtKind, CoroutineArgsExt, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt, + Visibility, }; use rustc_session::config::{self, DebugInfo, Lto}; use rustc_span::symbol::Symbol; -use rustc_span::FileName; +use rustc_span::{hygiene, FileName, DUMMY_SP}; use rustc_span::{FileNameDisplayPreference, SourceFile}; use rustc_symbol_mangling::typeid_for_trait_ref; use rustc_target::abi::{Align, Size}; use rustc_target::spec::DebuginfoKind; use smallvec::smallvec; +use tracing::{debug, instrument}; use libc::{c_char, c_longlong, c_uint}; use std::borrow::Cow; @@ -1304,7 +1306,7 @@ pub fn build_global_var_di_node<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, glo // We may want to remove the namespace scope if we're in an extern block (see // https://github.com/rust-lang/rust/pull/46457#issuecomment-351750952). let var_scope = get_namespace_for_item(cx, def_id); - let span = tcx.def_span(def_id); + let span = hygiene::walk_chain_collapsed(tcx.def_span(def_id), DUMMY_SP); let (file_metadata, line_number) = if !span.is_dummy() { let loc = cx.lookup_debug_loc(span.lo()); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index 4edef14422e5..12f98eef97d4 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -12,7 +12,7 @@ use rustc_middle::{ ty::{ self, layout::{LayoutOf, TyAndLayout}, - AdtDef, CoroutineArgs, Ty, + AdtDef, CoroutineArgs, CoroutineArgsExt, Ty, }, }; use rustc_target::abi::{Align, Endian, Size, TagEncoding, VariantIdx, Variants}; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs index bacd74f430f7..2b00bb14593e 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -10,7 +10,7 @@ use rustc_middle::{ ty::{ self, layout::{IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout}, - AdtDef, CoroutineArgs, Ty, VariantDef, + AdtDef, CoroutineArgs, CoroutineArgsExt, Ty, VariantDef, }, }; use rustc_span::Symbol; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 6149c18696cf..3486ce4becb4 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -42,6 +42,7 @@ use std::cell::OnceCell; use std::cell::RefCell; use std::iter; use std::ops::Range; +use tracing::debug; mod create_scope_map; pub mod gdb; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index c758010c5819..155e7a89fd8b 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -6,7 +6,7 @@ use super::CodegenUnitDebugContext; use rustc_hir::def_id::DefId; use rustc_middle::ty::layout::{HasParamEnv, LayoutOf}; use rustc_middle::ty::{self, Ty}; -use trace; +use tracing::trace; use crate::common::CodegenCx; use crate::llvm; diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 7117c4a0ed91..bf86d0e0569b 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -24,6 +24,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_middle::ty::{Instance, Ty}; use rustc_sanitizers::{cfi, kcfi}; use smallvec::SmallVec; +use tracing::debug; /// Declare a function. /// diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index e15eda7c66c1..40ac2f9c8bab 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -4,7 +4,7 @@ use std::path::Path; use crate::fluent_generated as fluent; use rustc_data_structures::small_c_str::SmallCStr; -use rustc_errors::{Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level}; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::Span; @@ -100,7 +100,7 @@ pub(crate) struct DynamicLinkingWithLTO; pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>); impl Diagnostic<'_, G> for ParseTargetMachineConfig<'_> { - fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let diag: Diag<'_, G> = self.0.into_diag(dcx, level); let (message, _) = diag.messages.first().expect("`LlvmError` with no message"); let message = dcx.eagerly_translate_to_string(message.clone(), diag.args.iter()); @@ -120,13 +120,13 @@ pub(crate) struct TargetFeatureDisableOrEnable<'a> { pub(crate) struct MissingFeatures; impl Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> { - fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let mut diag = Diag::new(dcx, level, fluent::codegen_llvm_target_feature_disable_or_enable); if let Some(span) = self.span { diag.span(span); }; if let Some(missing_features) = self.missing_features { - diag.subdiagnostic(dcx, missing_features); + diag.subdiagnostic(missing_features); } diag.arg("features", self.features.join(", ")); diag @@ -180,7 +180,7 @@ pub enum LlvmError<'a> { pub(crate) struct WithLlvmError<'a>(pub LlvmError<'a>, pub String); impl Diagnostic<'_, G> for WithLlvmError<'_> { - fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { use LlvmError::*; let msg_with_llvm_err = match &self.0 { WriteOutput { .. } => fluent::codegen_llvm_write_output_with_llvm_err, @@ -254,3 +254,9 @@ pub struct MismatchedDataLayout<'a> { pub(crate) struct InvalidTargetFeaturePrefix<'a> { pub feature: &'a str, } + +#[derive(Diagnostic)] +#[diag(codegen_llvm_fixed_x18_invalid_arch)] +pub(crate) struct FixedX18InvalidArch<'a> { + pub arch: &'a str, +} diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index c0a1208a8c7d..b5b0086f7405 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -14,12 +14,14 @@ use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; use rustc_hir as hir; +use rustc_middle::mir::BinOp; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, LayoutOf}; use rustc_middle::ty::{self, GenericArgsRef, Ty}; use rustc_middle::{bug, span_bug}; use rustc_span::{sym, Span, Symbol}; use rustc_target::abi::{self, Align, Float, HasDataLayout, Primitive, Size}; use rustc_target::spec::{HasTargetSpec, PanicStrategy}; +use tracing::debug; use std::cmp::Ordering; @@ -480,8 +482,60 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { } _ if name.as_str().starts_with("simd_") => { + // Unpack non-power-of-2 #[repr(packed, simd)] arguments. + // This gives them the expected layout of a regular #[repr(simd)] vector. + let mut loaded_args = Vec::new(); + for (ty, arg) in arg_tys.iter().zip(args) { + loaded_args.push( + // #[repr(packed, simd)] vectors are passed like arrays (as references, + // with reduced alignment and no padding) rather than as immediates. + // We can use a vector load to fix the layout and turn the argument + // into an immediate. + if ty.is_simd() + && let OperandValue::Ref(place) = arg.val + { + let (size, elem_ty) = ty.simd_size_and_type(self.tcx()); + let elem_ll_ty = match elem_ty.kind() { + ty::Float(f) => self.type_float_from_ty(*f), + ty::Int(i) => self.type_int_from_ty(*i), + ty::Uint(u) => self.type_uint_from_ty(*u), + ty::RawPtr(_, _) => self.type_ptr(), + _ => unreachable!(), + }; + let loaded = + self.load_from_place(self.type_vector(elem_ll_ty, size), place); + OperandRef::from_immediate_or_packed_pair(self, loaded, arg.layout) + } else { + *arg + }, + ); + } + + let llret_ty = if ret_ty.is_simd() + && let abi::Abi::Aggregate { .. } = self.layout_of(ret_ty).layout.abi + { + let (size, elem_ty) = ret_ty.simd_size_and_type(self.tcx()); + let elem_ll_ty = match elem_ty.kind() { + ty::Float(f) => self.type_float_from_ty(*f), + ty::Int(i) => self.type_int_from_ty(*i), + ty::Uint(u) => self.type_uint_from_ty(*u), + ty::RawPtr(_, _) => self.type_ptr(), + _ => unreachable!(), + }; + self.type_vector(elem_ll_ty, size) + } else { + llret_ty + }; + match generic_simd_intrinsic( - self, name, callee_ty, fn_args, args, ret_ty, llret_ty, span, + self, + name, + callee_ty, + fn_args, + &loaded_args, + ret_ty, + llret_ty, + span, ) { Ok(llval) => llval, Err(()) => return Ok(()), @@ -1055,10 +1109,12 @@ fn generic_simd_intrinsic<'ll, 'tcx>( tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), callee_ty.fn_sig(tcx)); let arg_tys = sig.inputs(); - // Vectors must be immediates (non-power-of-2 #[repr(packed)] are not) - for (ty, arg) in arg_tys.iter().zip(args) { - if ty.is_simd() && !matches!(arg.val, OperandValue::Immediate(_)) { - return_error!(InvalidMonomorphization::SimdArgument { span, name, ty: *ty }); + // Sanity-check: all vector arguments must be immediates. + if cfg!(debug_assertions) { + for (ty, arg) in arg_tys.iter().zip(args) { + if ty.is_simd() { + assert!(matches!(arg.val, OperandValue::Immediate(_))); + } } } @@ -1104,12 +1160,12 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let in_ty = arg_tys[0]; let comparison = match name { - sym::simd_eq => Some(hir::BinOpKind::Eq), - sym::simd_ne => Some(hir::BinOpKind::Ne), - sym::simd_lt => Some(hir::BinOpKind::Lt), - sym::simd_le => Some(hir::BinOpKind::Le), - sym::simd_gt => Some(hir::BinOpKind::Gt), - sym::simd_ge => Some(hir::BinOpKind::Ge), + sym::simd_eq => Some(BinOp::Eq), + sym::simd_ne => Some(BinOp::Ne), + sym::simd_lt => Some(BinOp::Lt), + sym::simd_le => Some(BinOp::Le), + sym::simd_gt => Some(BinOp::Gt), + sym::simd_ge => Some(BinOp::Ge), _ => None, }; @@ -1147,6 +1203,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( .expect_const() .eval(tcx, ty::ParamEnv::reveal_all(), span) .unwrap() + .1 .unwrap_branch(); let n = idx.len() as u64; @@ -1166,7 +1223,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( .iter() .enumerate() .map(|(arg_idx, val)| { - let idx = val.unwrap_leaf().try_to_i32().unwrap(); + let idx = val.unwrap_leaf().to_i32(); if idx >= i32::try_from(total_len).unwrap() { bx.sess().dcx().emit_err(InvalidMonomorphization::SimdIndexOutOfBounds { span, @@ -2336,7 +2393,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } // Unary integer intrinsics - if matches!(name, sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctlz | sym::simd_cttz) { + if matches!( + name, + sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctlz | sym::simd_ctpop | sym::simd_cttz + ) { let vec_ty = bx.cx.type_vector( match *in_elem.kind() { ty::Int(i) => bx.cx.type_int_from_ty(i), @@ -2354,31 +2414,38 @@ fn generic_simd_intrinsic<'ll, 'tcx>( sym::simd_bswap => "bswap", sym::simd_bitreverse => "bitreverse", sym::simd_ctlz => "ctlz", + sym::simd_ctpop => "ctpop", sym::simd_cttz => "cttz", _ => unreachable!(), }; let int_size = in_elem.int_size_and_signed(bx.tcx()).0.bits(); let llvm_intrinsic = &format!("llvm.{}.v{}i{}", intrinsic_name, in_len, int_size,); - return if name == sym::simd_bswap && int_size == 8 { + return match name { // byte swap is no-op for i8/u8 - Ok(args[0].immediate()) - } else if matches!(name, sym::simd_ctlz | sym::simd_cttz) { - let fn_ty = bx.type_func(&[vec_ty, bx.type_i1()], vec_ty); - let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - Ok(bx.call( - fn_ty, - None, - None, - f, - &[args[0].immediate(), bx.const_int(bx.type_i1(), 0)], - None, - None, - )) - } else { - let fn_ty = bx.type_func(&[vec_ty], vec_ty); - let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None, None)) + sym::simd_bswap if int_size == 8 => Ok(args[0].immediate()), + sym::simd_ctlz | sym::simd_cttz => { + // for the (int, i1 immediate) pair, the second arg adds `(0, true) => poison` + let fn_ty = bx.type_func(&[vec_ty, bx.type_i1()], vec_ty); + let dont_poison_on_zero = bx.const_int(bx.type_i1(), 0); + let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); + Ok(bx.call( + fn_ty, + None, + None, + f, + &[args[0].immediate(), dont_poison_on_zero], + None, + None, + )) + } + sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctpop => { + // simple unary argument cases + let fn_ty = bx.type_func(&[vec_ty], vec_ty); + let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); + Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None, None)) + } + _ => unreachable!(), }; } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 1cecf682e5db..4b7a26430071 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -4,19 +4,18 @@ //! //! This API is completely unstable and subject to change. +// tidy-alphabetical-start #![allow(internal_features)] -#![feature(rustdoc_internals)] -#![doc(rust_logo)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![doc(rust_logo)] #![feature(exact_size_is_empty)] #![feature(extern_types)] #![feature(hash_raw_entry)] +#![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] #![feature(let_chains)] -#![feature(impl_trait_in_assoc_type)] - -#[macro_use] -extern crate tracing; +#![feature(rustdoc_internals)] +// tidy-alphabetical-end use back::owned_target_machine::OwnedTargetMachine; use back::write::{create_informational_target_machine, create_target_machine}; @@ -32,7 +31,7 @@ use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::{CodegenResults, CompiledModule}; use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::{DiagCtxt, ErrorGuaranteed, FatalError}; +use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; @@ -192,7 +191,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { } fn run_link( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, modules: Vec>, ) -> Result, FatalError> { back::write::link(cgcx, dcx, modules) @@ -213,7 +212,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { } unsafe fn optimize( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, module: &ModuleCodegen, config: &ModuleConfig, ) -> Result<(), FatalError> { @@ -224,7 +223,8 @@ impl WriteBackendMethods for LlvmCodegenBackend { module: &mut ModuleCodegen, ) -> Result<(), FatalError> { let dcx = cgcx.create_dcx(); - back::lto::run_pass_manager(cgcx, &dcx, module, false) + let dcx = dcx.handle(); + back::lto::run_pass_manager(cgcx, dcx, module, false) } unsafe fn optimize_thin( cgcx: &CodegenContext, @@ -234,14 +234,17 @@ impl WriteBackendMethods for LlvmCodegenBackend { } unsafe fn codegen( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, config: &ModuleConfig, ) -> Result { back::write::codegen(cgcx, dcx, module, config) } - fn prepare_thin(module: ModuleCodegen) -> (String, Self::ThinBuffer) { - back::lto::prepare_thin(module) + fn prepare_thin( + module: ModuleCodegen, + emit_summary: bool, + ) -> (String, Self::ThinBuffer) { + back::lto::prepare_thin(module, emit_summary) } fn serialize_module(module: ModuleCodegen) -> (String, Self::ModuleBuffer) { (module.name, back::lto::ModuleBuffer::new(module.module_llvm.llmod())) @@ -439,7 +442,7 @@ impl ModuleLlvm { cgcx: &CodegenContext, name: &CStr, buffer: &[u8], - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, ) -> Result { unsafe { let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7a34e21628dc..132e1f9e8fd9 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2350,10 +2350,16 @@ extern "C" { #[allow(improper_ctypes)] pub fn LLVMRustModuleInstructionStats(M: &Module, Str: &RustString); - pub fn LLVMRustThinLTOBufferCreate(M: &Module, is_thin: bool) -> &'static mut ThinLTOBuffer; + pub fn LLVMRustThinLTOBufferCreate( + M: &Module, + is_thin: bool, + emit_summary: bool, + ) -> &'static mut ThinLTOBuffer; pub fn LLVMRustThinLTOBufferFree(M: &'static mut ThinLTOBuffer); pub fn LLVMRustThinLTOBufferPtr(M: &ThinLTOBuffer) -> *const c_char; pub fn LLVMRustThinLTOBufferLen(M: &ThinLTOBuffer) -> size_t; + pub fn LLVMRustThinLTOBufferThinLinkDataPtr(M: &ThinLTOBuffer) -> *const c_char; + pub fn LLVMRustThinLTOBufferThinLinkDataLen(M: &ThinLTOBuffer) -> size_t; pub fn LLVMRustCreateThinLTOData( Modules: *const ThinLTOModule, NumModules: c_uint, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 5552b3810251..7e0f264a4aed 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -1,6 +1,6 @@ use crate::back::write::create_informational_target_machine; use crate::errors::{ - InvalidTargetFeaturePrefix, PossibleFeature, TargetFeatureDisableOrEnable, + FixedX18InvalidArch, InvalidTargetFeaturePrefix, PossibleFeature, TargetFeatureDisableOrEnable, UnknownCTargetFeature, UnknownCTargetFeaturePrefix, UnstableCTargetFeature, }; use crate::llvm; @@ -394,10 +394,15 @@ fn print_target_features(out: &mut dyn PrintBackendInfo, sess: &Session, tm: &ll (*feature, desc) }) .collect::>(); + + // Since we add this at the end ... rustc_target_features.extend_from_slice(&[( "crt-static", "Enables C Run-time Libraries to be statically linked", )]); + // ... we need to sort the list again. + rustc_target_features.sort(); + llvm_target_features.retain(|(f, _d)| !known_llvm_target_features.contains(f)); let max_feature_len = llvm_target_features @@ -615,6 +620,15 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> { fn predefine_static( diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 5f60fd23a5fe..7be941ed7498 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -4,10 +4,11 @@ use rustc_codegen_ssa::traits::*; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; -use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TypeVisitableExt}; use rustc_target::abi::{Abi, Align, FieldsShape}; use rustc_target::abi::{Float, Int, Pointer}; use rustc_target::abi::{Scalar, Size, Variants}; +use tracing::debug; use std::fmt::Write; diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index d159fe58d3ee..1a851ad04a1f 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -212,6 +212,8 @@ codegen_ssa_rlib_only_rmeta_found = could not find rlib for: `{$crate_name}`, fo codegen_ssa_select_cpp_build_tool_workload = in the Visual Studio installer, ensure the "C++ build tools" workload is selected +codegen_ssa_self_contained_linker_missing = the self-contained linker was requested, but it wasn't found in the target's sysroot, or in rustc's sysroot + codegen_ssa_shuffle_indices_evaluation = could not evaluate shuffle_indices at compile time codegen_ssa_specify_libraries_to_link = use the `-l` flag to specify native libraries to link diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index e441aea8400b..6e7d736eb29d 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -37,6 +37,7 @@ use rustc_span::{Span, Symbol}; use std::borrow::Cow; use std::fmt; use thin_vec::ThinVec; +use tracing::debug; #[allow(missing_docs)] pub fn assert_module_sources(tcx: TyCtxt<'_>, set_reuse: &dyn Fn(&mut CguReuseTracker)) { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 6939674ce9dd..c352100b01b1 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -3,7 +3,7 @@ use rustc_ast::CRATE_NODE_ID; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; -use rustc_errors::{DiagCtxt, ErrorGuaranteed, FatalError}; +use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError}; use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::find_native_static_library; @@ -52,8 +52,9 @@ use std::ops::Deref; use std::path::{Path, PathBuf}; use std::process::{ExitStatus, Output, Stdio}; use std::{env, fmt, fs, io, mem, str}; +use tracing::{debug, info, warn}; -pub fn ensure_removed(dcx: &DiagCtxt, path: &Path) { +pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { if let Err(e) = fs::remove_file(path) { if e.kind() != io::ErrorKind::NotFound { dcx.err(format!("failed to remove {}: {}", path.display(), e)); @@ -786,12 +787,33 @@ fn link_natively( if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _)) && unknown_arg_regex.is_match(&out) && out.contains("-no-pie") - && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") + && cmd.get_args().iter().any(|e| e == "-no-pie") { info!("linker output: {:?}", out); warn!("Linker does not support -no-pie command line option. Retrying without."); for arg in cmd.take_args() { - if arg.to_string_lossy() != "-no-pie" { + if arg != "-no-pie" { + cmd.arg(arg); + } + } + info!("{:?}", &cmd); + continue; + } + + // Check if linking failed with an error message that indicates the driver didn't recognize + // the `-fuse-ld=lld` option. If so, re-perform the link step without it. This avoids having + // to spawn multiple instances on the happy path to do version checking, and ensures things + // keep working on the tier 1 baseline of GLIBC 2.17+. That is generally understood as GCCs + // circa RHEL/CentOS 7, 4.5 or so, whereas lld support was added in GCC 9. + if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, Lld::Yes)) + && unknown_arg_regex.is_match(&out) + && out.contains("-fuse-ld=lld") + && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-fuse-ld=lld") + { + info!("linker output: {:?}", out); + warn!("The linker driver does not support `-fuse-ld=lld`. Retrying without it."); + for arg in cmd.take_args() { + if arg.to_string_lossy() != "-fuse-ld=lld" { cmd.arg(arg); } } @@ -804,7 +826,7 @@ fn link_natively( if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _)) && unknown_arg_regex.is_match(&out) && (out.contains("-static-pie") || out.contains("--no-dynamic-linker")) - && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-static-pie") + && cmd.get_args().iter().any(|e| e == "-static-pie") { info!("linker output: {:?}", out); warn!( @@ -843,7 +865,7 @@ fn link_natively( assert!(pre_objects_static.is_empty() || !pre_objects_static_pie.is_empty()); assert!(post_objects_static.is_empty() || !post_objects_static_pie.is_empty()); for arg in cmd.take_args() { - if arg.to_string_lossy() == "-static-pie" { + if arg == "-static-pie" { // Replace the output kind. cmd.arg("-static"); } else if pre_objects_static_pie.contains(&arg) { @@ -1230,7 +1252,10 @@ fn add_sanitizer_libraries( if sanitizer.contains(SanitizerSet::DATAFLOW) { link_sanitizer_runtime(sess, flavor, linker, "dfsan"); } - if sanitizer.contains(SanitizerSet::LEAK) { + if sanitizer.contains(SanitizerSet::LEAK) + && !sanitizer.contains(SanitizerSet::ADDRESS) + && !sanitizer.contains(SanitizerSet::HWADDRESS) + { link_sanitizer_runtime(sess, flavor, linker, "lsan"); } if sanitizer.contains(SanitizerSet::MEMORY) { @@ -3116,13 +3141,21 @@ fn add_lld_args( let self_contained_linker = self_contained_cli || self_contained_target; if self_contained_linker && !sess.opts.cg.link_self_contained.is_linker_disabled() { + let mut linker_path_exists = false; for path in sess.get_tools_search_paths(false) { + let linker_path = path.join("gcc-ld"); + linker_path_exists |= linker_path.exists(); cmd.arg({ let mut arg = OsString::from("-B"); - arg.push(path.join("gcc-ld")); + arg.push(linker_path); arg }); } + if !linker_path_exists { + // As a sanity check, we emit an error if none of these paths exist: we want + // self-contained linking and have no linker. + sess.dcx().emit_fatal(errors::SelfContainedLinkerMissing); + } } // 2. Implement the "linker flavor" part of this feature by asking `cc` to use some kind of diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index f85056f8ad41..a82478900b17 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -22,6 +22,7 @@ use rustc_session::Session; use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld}; use cc::windows_registry; +use tracing::{debug, warn}; /// Disables non-English messages from localized linkers. /// Such messages may cause issues with text encoding on Windows (#35785) diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs index ebbf49af1848..f499bbcf8533 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs @@ -3,6 +3,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_fs_util::try_canonicalize; use std::ffi::OsString; use std::path::{Path, PathBuf}; +use tracing::debug; pub struct RPathConfig<'a> { pub libs: &'a [&'a Path], @@ -84,6 +85,11 @@ fn get_rpath_relative_to_output(config: &RPathConfig<'_>, lib: &Path) -> OsStrin // Strip filenames let lib = lib.parent().unwrap(); let output = config.out_filename.parent().unwrap(); + + // If output or lib is empty, just assume it locates in current path + let lib = if lib == Path::new("") { Path::new(".") } else { lib }; + let output = if output == Path::new("") { Path::new(".") } else { output }; + let lib = try_canonicalize(lib).unwrap(); let output = try_canonicalize(output).unwrap(); let relative = path_relative_from(&lib, &output) diff --git a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs index ac2e54072c41..0de90a1036ec 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs @@ -57,6 +57,22 @@ fn test_rpath_relative() { } } +#[test] +fn test_rpath_relative_issue_119571() { + let config = &mut RPathConfig { + libs: &[], + out_filename: PathBuf::from("rustc"), + has_rpath: true, + is_like_osx: false, + linker_is_gnu: true, + }; + // Should not panic when out_filename only contains filename. + // Issue 119571 + let _ = get_rpath_relative_to_output(config, Path::new("lib/libstd.so")); + // Should not panic when lib only contains filename. + let _ = get_rpath_relative_to_output(config, Path::new("libstd.so")); +} + #[test] fn test_xlinker() { let args = rpaths_to_flags(vec!["a/normal/path".into(), "a,comma,path".into()]); diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 0e335bf00cf0..6abe4fa1c380 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -18,6 +18,7 @@ use rustc_middle::ty::{GenericArgKind, GenericArgsRef}; use rustc_middle::util::Providers; use rustc_session::config::{CrateType, OomStrategy}; use rustc_target::spec::{SanitizerSet, TlsModel}; +use tracing::debug; pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel { crates_export_threshold(tcx.crate_types()) @@ -309,7 +310,7 @@ fn exported_symbols_provider_local( if tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics() { use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility}; - use rustc_middle::ty::InstanceDef; + use rustc_middle::ty::InstanceKind; // Normally, we require that shared monomorphizations are not hidden, // because if we want to re-use a monomorphization from a Rust dylib, it @@ -336,7 +337,7 @@ fn exported_symbols_provider_local( } match *mono_item { - MonoItem::Fn(Instance { def: InstanceDef::Item(def), args }) => { + MonoItem::Fn(Instance { def: InstanceKind::Item(def), args }) => { if args.non_erasable_generics(tcx, def).next().is_some() { let symbol = ExportedSymbol::Generic(def, args); symbols.push(( @@ -349,7 +350,7 @@ fn exported_symbols_provider_local( )); } } - MonoItem::Fn(Instance { def: InstanceDef::DropGlue(def_id, Some(ty)), args }) => { + MonoItem::Fn(Instance { def: InstanceKind::DropGlue(def_id, Some(ty)), args }) => { // A little sanity-check debug_assert_eq!( args.non_erasable_generics(tcx, def_id).next(), @@ -365,12 +366,12 @@ fn exported_symbols_provider_local( )); } MonoItem::Fn(Instance { - def: InstanceDef::AsyncDropGlueCtorShim(def_id, Some(ty)), + def: InstanceKind::AsyncDropGlueCtorShim(def_id, Some(ty)), args, }) => { // A little sanity-check debug_assert_eq!( - args.non_erasable_generics(tcx, def_id).skip(1).next(), + args.non_erasable_generics(tcx, def_id).next(), Some(GenericArgKind::Type(ty)) ); symbols.push(( @@ -421,10 +422,7 @@ fn upstream_monomorphizations_provider( } ExportedSymbol::AsyncDropGlueCtorShim(ty) => { if let Some(async_drop_in_place_fn_def_id) = async_drop_in_place_fn_def_id { - ( - async_drop_in_place_fn_def_id, - tcx.mk_args(&[tcx.lifetimes.re_erased.into(), ty.into()]), - ) + (async_drop_in_place_fn_def_id, tcx.mk_args(&[ty.into()])) } else { // `drop_in_place` in place does not exist, don't try // to use it. @@ -472,11 +470,16 @@ fn upstream_drop_glue_for_provider<'tcx>( tcx: TyCtxt<'tcx>, args: GenericArgsRef<'tcx>, ) -> Option { - if let Some(def_id) = tcx.lang_items().drop_in_place_fn() { - tcx.upstream_monomorphizations_for(def_id).and_then(|monos| monos.get(&args).cloned()) - } else { - None - } + let def_id = tcx.lang_items().drop_in_place_fn()?; + tcx.upstream_monomorphizations_for(def_id)?.get(&args).cloned() +} + +fn upstream_async_drop_glue_for_provider<'tcx>( + tcx: TyCtxt<'tcx>, + args: GenericArgsRef<'tcx>, +) -> Option { + let def_id = tcx.lang_items().async_drop_in_place_fn()?; + tcx.upstream_monomorphizations_for(def_id)?.get(&args).cloned() } fn is_unreachable_local_definition_provider(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { @@ -490,6 +493,7 @@ pub fn provide(providers: &mut Providers) { providers.upstream_monomorphizations = upstream_monomorphizations_provider; providers.is_unreachable_local_definition = is_unreachable_local_definition_provider; providers.upstream_drop_glue_for = upstream_drop_glue_for_provider; + providers.upstream_async_drop_glue_for = upstream_async_drop_glue_for_provider; providers.wasm_import_module_map = wasm_import_module_map; providers.extern_queries.is_reachable_non_generic = is_reachable_non_generic_provider_extern; providers.extern_queries.upstream_monomorphizations_for = @@ -555,7 +559,7 @@ pub fn symbol_name_for_instance_in_crate<'tcx>( rustc_symbol_mangling::symbol_name_for_instance_in_crate( tcx, ty::Instance { - def: ty::InstanceDef::ThreadLocalShim(def_id), + def: ty::InstanceKind::ThreadLocalShim(def_id), args: ty::GenericArgs::empty(), }, instantiating_crate, diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 53ba0da7d049..064be4988bdd 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -49,6 +49,7 @@ use std::str; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::Arc; use std::thread; +use tracing::debug; const PRE_LTO_BC_EXT: &str = "pre-lto.bc"; @@ -107,6 +108,7 @@ pub struct ModuleConfig { pub emit_asm: bool, pub emit_obj: EmitObj, pub emit_thin_lto: bool, + pub emit_thin_lto_summary: bool, pub bc_cmdline: String, // Miscellaneous flags. These are mostly copied from command-line @@ -231,6 +233,10 @@ impl ModuleConfig { ), emit_obj, emit_thin_lto: sess.opts.unstable_opts.emit_thin_lto, + emit_thin_lto_summary: if_regular!( + sess.opts.output_types.contains_key(&OutputType::ThinLinkBitcode), + false + ), bc_cmdline: sess.target.bitcode_llvm_cmdline.to_string(), verify_llvm_ir: sess.verify_llvm_ir(), @@ -282,6 +288,7 @@ impl ModuleConfig { pub fn bitcode_needed(&self) -> bool { self.emit_bc + || self.emit_thin_lto_summary || self.emit_obj == EmitObj::Bitcode || self.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) } @@ -629,6 +636,9 @@ fn produce_final_output_artifacts( // them for making an rlib. copy_if_one_unit(OutputType::Bitcode, true); } + OutputType::ThinLinkBitcode => { + copy_if_one_unit(OutputType::ThinLinkBitcode, false); + } OutputType::LlvmAssembly => { copy_if_one_unit(OutputType::LlvmAssembly, false); } @@ -707,6 +717,29 @@ fn produce_final_output_artifacts( } } + if sess.opts.json_artifact_notifications { + if compiled_modules.modules.len() == 1 { + compiled_modules.modules[0].for_each_output(|_path, ty| { + if sess.opts.output_types.contains_key(&ty) { + let descr = ty.shorthand(); + // for single cgu file is renamed to drop cgu specific suffix + // so we regenerate it the same way + let path = crate_output.path(ty); + sess.dcx().emit_artifact_notification(path.as_path(), descr); + } + }); + } else { + for module in &compiled_modules.modules { + module.for_each_output(|path, ty| { + if sess.opts.output_types.contains_key(&ty) { + let descr = ty.shorthand(); + sess.dcx().emit_artifact_notification(&path, descr); + } + }); + } + } + } + // We leave the following files around by default: // - #crate#.o // - #crate#.crate.metadata.o @@ -858,9 +891,10 @@ fn execute_optimize_work_item( module_config: &ModuleConfig, ) -> Result, FatalError> { let dcx = cgcx.create_dcx(); + let dcx = dcx.handle(); unsafe { - B::optimize(cgcx, &dcx, &module, module_config)?; + B::optimize(cgcx, dcx, &module, module_config)?; } // After we've done the initial round of optimizations we need to @@ -882,7 +916,7 @@ fn execute_optimize_work_item( match lto_type { ComputedLtoType::No => finish_intra_module_work(cgcx, module, module_config), ComputedLtoType::Thin => { - let (name, thin_buffer) = B::prepare_thin(module); + let (name, thin_buffer) = B::prepare_thin(module, false); if let Some(path) = bitcode { fs::write(&path, thin_buffer.data()).unwrap_or_else(|e| { panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); @@ -921,7 +955,11 @@ fn execute_copy_from_cache_work_item( match link_or_copy(&source_file, &output_path) { Ok(_) => Some(output_path), Err(error) => { - cgcx.create_dcx().emit_err(errors::CopyPathBuf { source_file, output_path, error }); + cgcx.create_dcx().handle().emit_err(errors::CopyPathBuf { + source_file, + output_path, + error, + }); None } } @@ -954,7 +992,7 @@ fn execute_copy_from_cache_work_item( let bytecode = load_from_incr_cache(module_config.emit_bc, OutputType::Bitcode); let object = load_from_incr_cache(should_emit_obj, OutputType::Object); if should_emit_obj && object.is_none() { - cgcx.create_dcx().emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name }) + cgcx.create_dcx().handle().emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name }) } WorkItemResult::Finished(CompiledModule { @@ -983,12 +1021,13 @@ fn finish_intra_module_work( module_config: &ModuleConfig, ) -> Result, FatalError> { let dcx = cgcx.create_dcx(); + let dcx = dcx.handle(); if !cgcx.opts.unstable_opts.combine_cgu || module.kind == ModuleKind::Metadata || module.kind == ModuleKind::Allocator { - let module = unsafe { B::codegen(cgcx, &dcx, module, module_config)? }; + let module = unsafe { B::codegen(cgcx, dcx, module, module_config)? }; Ok(WorkItemResult::Finished(module)) } else { Ok(WorkItemResult::NeedsLink(module)) @@ -1659,9 +1698,10 @@ fn start_executing_work( if !needs_link.is_empty() { assert!(compiled_modules.is_empty()); let dcx = cgcx.create_dcx(); - let module = B::run_link(&cgcx, &dcx, needs_link).map_err(|_| ())?; + let dcx = dcx.handle(); + let module = B::run_link(&cgcx, dcx, needs_link).map_err(|_| ())?; let module = unsafe { - B::codegen(&cgcx, &dcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())? + B::codegen(&cgcx, dcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())? }; compiled_modules.push(module); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 877e5b75912e..c18816533a2f 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -20,7 +20,6 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::par_map; use rustc_data_structures::unord::UnordMap; -use rustc_hir as hir; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_metadata::EncodedMetadata; @@ -31,6 +30,7 @@ use rustc_middle::middle::exported_symbols; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_middle::middle::lang_items; use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem}; +use rustc_middle::mir::BinOp; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; @@ -45,33 +45,34 @@ use std::collections::BTreeSet; use std::time::{Duration, Instant}; use itertools::Itertools; +use tracing::{debug, info}; -pub fn bin_op_to_icmp_predicate(op: hir::BinOpKind, signed: bool) -> IntPredicate { +pub fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate { match op { - hir::BinOpKind::Eq => IntPredicate::IntEQ, - hir::BinOpKind::Ne => IntPredicate::IntNE, - hir::BinOpKind::Lt => { + BinOp::Eq => IntPredicate::IntEQ, + BinOp::Ne => IntPredicate::IntNE, + BinOp::Lt => { if signed { IntPredicate::IntSLT } else { IntPredicate::IntULT } } - hir::BinOpKind::Le => { + BinOp::Le => { if signed { IntPredicate::IntSLE } else { IntPredicate::IntULE } } - hir::BinOpKind::Gt => { + BinOp::Gt => { if signed { IntPredicate::IntSGT } else { IntPredicate::IntUGT } } - hir::BinOpKind::Ge => { + BinOp::Ge => { if signed { IntPredicate::IntSGE } else { @@ -86,14 +87,14 @@ pub fn bin_op_to_icmp_predicate(op: hir::BinOpKind, signed: bool) -> IntPredicat } } -pub fn bin_op_to_fcmp_predicate(op: hir::BinOpKind) -> RealPredicate { +pub fn bin_op_to_fcmp_predicate(op: BinOp) -> RealPredicate { match op { - hir::BinOpKind::Eq => RealPredicate::RealOEQ, - hir::BinOpKind::Ne => RealPredicate::RealUNE, - hir::BinOpKind::Lt => RealPredicate::RealOLT, - hir::BinOpKind::Le => RealPredicate::RealOLE, - hir::BinOpKind::Gt => RealPredicate::RealOGT, - hir::BinOpKind::Ge => RealPredicate::RealOGE, + BinOp::Eq => RealPredicate::RealOEQ, + BinOp::Ne => RealPredicate::RealUNE, + BinOp::Lt => RealPredicate::RealOLT, + BinOp::Le => RealPredicate::RealOLE, + BinOp::Gt => RealPredicate::RealOGT, + BinOp::Ge => RealPredicate::RealOGE, op => { bug!( "comparison_op_to_fcmp_predicate: expected comparison operator, \ @@ -110,7 +111,7 @@ pub fn compare_simd_types<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( rhs: Bx::Value, t: Ty<'tcx>, ret_ty: Bx::Type, - op: hir::BinOpKind, + op: BinOp, ) -> Bx::Value { let signed = match t.kind() { ty::Float(_) => { @@ -162,8 +163,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // trait upcasting coercion - let vptr_entry_idx = - cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot((source, target)); + let vptr_entry_idx = cx.tcx().supertrait_vtable_slot((source, target)); if let Some(entry_idx) = vptr_entry_idx { let ptr_size = bx.data_layout().pointer_size; @@ -293,12 +293,13 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } -/// Returns `rhs` sufficiently masked, truncated, and/or extended so that -/// it can be used to shift `lhs`. +/// Returns `rhs` sufficiently masked, truncated, and/or extended so that it can be used to shift +/// `lhs`: it has the same size as `lhs`, and the value, when interpreted unsigned (no matter its +/// type), will not exceed the size of `lhs`. /// -/// Shifts in MIR are all allowed to have mismatched LHS & RHS types. +/// Shifts in MIR are all allowed to have mismatched LHS & RHS types, and signed RHS. /// The shift methods in `BuilderMethods`, however, are fully homogeneous -/// (both parameters and the return type are all the same type). +/// (both parameters and the return type are all the same size) and assume an unsigned RHS. /// /// If `is_unchecked` is false, this masks the RHS to ensure it stays in-bounds, /// as the `BuilderMethods` shifts are UB for out-of-bounds shift amounts. diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 9bf055b17394..15955170e873 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -324,6 +324,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { let linkage = Some(linkage_by_name(tcx, did, val.as_str())); if tcx.is_foreign_item(did) { codegen_fn_attrs.import_linkage = linkage; + + if tcx.is_mutable_static(did.into()) { + let mut diag = tcx.dcx().struct_span_err( + attr.span, + "extern mutable statics are not allowed with `#[linkage]`", + ); + diag.note( + "marking the extern static mutable would allow changing which symbol \ + the static references rather than make the target of the symbol \ + mutable", + ); + diag.emit(); + } } else { codegen_fn_attrs.linkage = linkage; } @@ -564,8 +577,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, - "`no_sanitize` will have no effect after inlining", |lint| { + lint.primary_message("`no_sanitize` will have no effect after inlining"); lint.span_note(inline_span, "inlining requested here"); }, ) diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index e4a36b3f5918..27b0f127e926 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -163,7 +163,7 @@ pub fn asm_const_to_str<'tcx>( let mir::ConstValue::Scalar(scalar) = const_value else { span_bug!(sp, "expected Scalar for promoted asm const, but got {:#?}", const_value) }; - let value = scalar.assert_bits(ty_and_layout.size); + let value = scalar.assert_scalar_int().to_bits(ty_and_layout.size); match ty_and_layout.ty.kind() { ty::Uint(_) => value.to_string(), ty::Int(int_ty) => match int_ty.normalize(tcx.sess.target.pointer_width) { diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 07473ee476ba..c4e5c8582404 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -263,7 +263,7 @@ fn push_debuginfo_type_name<'tcx>( let ExistentialProjection { def_id: item_def_id, term, .. } = tcx.instantiate_bound_regions_with_erased(bound); // FIXME(associated_const_equality): allow for consts here - (item_def_id, term.ty().unwrap()) + (item_def_id, term.expect_type()) }) .collect(); @@ -693,41 +693,46 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S ty::ConstKind::Param(param) => { write!(output, "{}", param.name) } - _ => match ct.ty().kind() { - ty::Int(ity) => { - let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all()); - let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; - write!(output, "{val}") - } - ty::Uint(_) => { - let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all()); - write!(output, "{val}") - } - ty::Bool => { - let val = ct.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap(); - write!(output, "{val}") - } - _ => { - // If we cannot evaluate the constant to a known type, we fall back - // to emitting a stable hash value of the constant. This isn't very pretty - // but we get a deterministic, virtually unique value for the constant. - // - // Let's only emit 64 bits of the hash value. That should be plenty for - // avoiding collisions and will make the emitted type names shorter. - let hash_short = tcx.with_stable_hashing_context(|mut hcx| { - let mut hasher = StableHasher::new(); - let ct = ct.eval(tcx, ty::ParamEnv::reveal_all(), DUMMY_SP).unwrap(); - hcx.while_hashing_spans(false, |hcx| ct.hash_stable(hcx, &mut hasher)); - hasher.finish::() - }); + ty::ConstKind::Value(ty, _) => { + match ty.kind() { + ty::Int(ity) => { + // FIXME: directly extract the bits from a valtree instead of evaluating an + // alreay evaluated `Const` in order to get the bits. + let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all()); + let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; + write!(output, "{val}") + } + ty::Uint(_) => { + let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all()); + write!(output, "{val}") + } + ty::Bool => { + let val = ct.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap(); + write!(output, "{val}") + } + _ => { + // If we cannot evaluate the constant to a known type, we fall back + // to emitting a stable hash value of the constant. This isn't very pretty + // but we get a deterministic, virtually unique value for the constant. + // + // Let's only emit 64 bits of the hash value. That should be plenty for + // avoiding collisions and will make the emitted type names shorter. + let hash_short = tcx.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + let ct = ct.eval(tcx, ty::ParamEnv::reveal_all(), DUMMY_SP).unwrap(); + hcx.while_hashing_spans(false, |hcx| ct.hash_stable(hcx, &mut hasher)); + hasher.finish::() + }); - if cpp_like_debuginfo(tcx) { - write!(output, "CONST${hash_short:x}") - } else { - write!(output, "{{CONST#{hash_short:x}}}") + if cpp_like_debuginfo(tcx) { + write!(output, "CONST${hash_short:x}") + } else { + write!(output, "{{CONST#{hash_short:x}}}") + } } } - }, + } + _ => bug!("Invalid `Const` during codegen: {:?}", ct), } .unwrap(); } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index ed6a0c244106..e6ba31c51650 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -4,7 +4,7 @@ use crate::assert_module_sources::CguReuse; use crate::back::command::Command; use crate::fluent_generated as fluent; use rustc_errors::{ - codes::*, Diag, DiagArgValue, DiagCtxt, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, + codes::*, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, }; use rustc_macros::Diagnostic; use rustc_middle::ty::layout::LayoutError; @@ -215,7 +215,7 @@ pub enum LinkRlibError { pub struct ThorinErrorWrapper(pub thorin::Error); impl Diagnostic<'_, G> for ThorinErrorWrapper { - fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let build = |msg| Diag::new(dcx, level, msg); match self.0 { thorin::Error::ReadInput(_) => build(fluent::codegen_ssa_thorin_read_input_failure), @@ -348,7 +348,7 @@ pub struct LinkingFailed<'a> { } impl Diagnostic<'_, G> for LinkingFailed<'_> { - fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_linking_failed); diag.arg("linker_path", format!("{}", self.linker_path.display())); diag.arg("exit_status", format!("{}", self.exit_status)); @@ -413,6 +413,10 @@ pub struct UnableToExeLinker { #[diag(codegen_ssa_msvc_missing_linker)] pub struct MsvcMissingLinker; +#[derive(Diagnostic)] +#[diag(codegen_ssa_self_contained_linker_missing)] +pub struct SelfContainedLinkerMissing; + #[derive(Diagnostic)] #[diag(codegen_ssa_check_installed_visual_studio)] pub struct CheckInstalledVisualStudio; diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 4eb24d71009a..e801af400141 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -1,23 +1,22 @@ -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![doc(rust_logo)] -#![feature(rustdoc_internals)] +// tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![doc(rust_logo)] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(let_chains)] #![feature(negative_impls)] +#![feature(rustdoc_internals)] #![feature(strict_provenance)] #![feature(try_blocks)] +// tidy-alphabetical-end //! This crate contains codegen code that is used by all codegen backends (LLVM and others). //! The backend-agnostic functions of this crate use functions defined in various traits that //! have to be implemented by each backend. -#[macro_use] -extern crate tracing; - use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxIndexMap; @@ -109,6 +108,24 @@ pub struct CompiledModule { pub llvm_ir: Option, // --emit=llvm-ir, llvm-bc is in bytecode } +impl CompiledModule { + /// Call `emit` function with every artifact type currently compiled + pub fn for_each_output(&self, mut emit: impl FnMut(&Path, OutputType)) { + if let Some(path) = self.object.as_deref() { + emit(path, OutputType::Object); + } + if let Some(path) = self.bytecode.as_deref() { + emit(path, OutputType::Bitcode); + } + if let Some(path) = self.llvm_ir.as_deref() { + emit(path, OutputType::LlvmAssembly); + } + if let Some(path) = self.assembly.as_deref() { + emit(path, OutputType::Assembly); + } + } +} + pub struct CachedModuleCodegen { pub name: String, pub source: WorkProduct, @@ -195,6 +212,7 @@ pub enum CodegenErrors { EmptyVersionNumber, EncodingVersionMismatch { version_array: String, rlink_version: u32 }, RustcVersionMismatch { rustc_version: String }, + CorruptFile, } pub fn provide(providers: &mut Providers) { @@ -265,7 +283,9 @@ impl CodegenResults { }); } - let mut decoder = MemDecoder::new(&data[4..], 0); + let Ok(mut decoder) = MemDecoder::new(&data[4..], 0) else { + return Err(CodegenErrors::CorruptFile); + }; let rustc_version = decoder.read_str(); if rustc_version != sess.cfg_version { return Err(CodegenErrors::RustcVersionMismatch { diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 23036e9bea02..ddc6797388e7 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -5,6 +5,7 @@ use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_session::config::Lto; use rustc_symbol_mangling::typeid_for_trait_ref; use rustc_target::abi::call::FnAbi; +use tracing::{debug, instrument}; #[derive(Copy, Clone, Debug)] pub struct VirtualIndex(u64); @@ -14,12 +15,13 @@ impl<'a, 'tcx> VirtualIndex { VirtualIndex(index as u64) } - pub fn get_fn>( + fn get_fn_inner>( self, bx: &mut Bx, llvtable: Bx::Value, ty: Ty<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + nonnull: bool, ) -> Bx::Value { // Load the function pointer from the object. debug!("get_fn({llvtable:?}, {ty:?}, {self:?})"); @@ -40,13 +42,35 @@ impl<'a, 'tcx> VirtualIndex { } else { let gep = bx.inbounds_ptradd(llvtable, bx.const_usize(vtable_byte_offset)); let ptr = bx.load(llty, gep, ptr_align); - bx.nonnull_metadata(ptr); // VTable loads are invariant. bx.set_invariant_load(ptr); + if nonnull { + bx.nonnull_metadata(ptr); + } ptr } } + pub fn get_optional_fn>( + self, + bx: &mut Bx, + llvtable: Bx::Value, + ty: Ty<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + ) -> Bx::Value { + self.get_fn_inner(bx, llvtable, ty, fn_abi, false) + } + + pub fn get_fn>( + self, + bx: &mut Bx, + llvtable: Bx::Value, + ty: Ty<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + ) -> Bx::Value { + self.get_fn_inner(bx, llvtable, ty, fn_abi, true) + } + pub fn get_usize>( self, bx: &mut Bx, diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 09ae7cf64109..0577ba32ffdc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -11,6 +11,7 @@ use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceC use rustc_middle::mir::{self, DefLocation, Location, TerminatorKind}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::{bug, span_bug}; +use tracing::debug; pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx: &FunctionCx<'a, 'tcx, Bx>, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index ba6aad51316c..57138d3b9dbd 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -24,6 +24,7 @@ use rustc_span::{source_map::Spanned, sym, Span}; use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode, Reg}; use rustc_target::abi::{self, HasDataLayout, WrappingRange}; use rustc_target::spec::abi::Abi; +use tracing::{debug, info}; use std::cmp; @@ -499,6 +500,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &mut self, helper: TerminatorCodegenHelper<'tcx>, bx: &mut Bx, + source_info: &mir::SourceInfo, location: mir::Place<'tcx>, target: mir::BasicBlock, unwind: mir::UnwindAction, @@ -508,7 +510,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ty = self.monomorphize(ty); let drop_fn = Instance::resolve_drop_in_place(bx.tcx(), ty); - if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { + if let ty::InstanceKind::DropGlue(_, None) = drop_fn.def { // we don't actually need to drop anything. return helper.funclet_br(self, bx, target, mergeable_succ); } @@ -522,90 +524,106 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args1 = [place.val.llval]; &args1[..] }; - let (drop_fn, fn_abi, drop_instance) = - match ty.kind() { - // FIXME(eddyb) perhaps move some of this logic into - // `Instance::resolve_drop_in_place`? - ty::Dynamic(_, _, ty::Dyn) => { - // IN THIS ARM, WE HAVE: - // ty = *mut (dyn Trait) - // which is: exists ( *mut T, Vtable ) - // args[0] args[1] - // - // args = ( Data, Vtable ) - // | - // v - // /-------\ - // | ... | - // \-------/ - // - let virtual_drop = Instance { - def: ty::InstanceDef::Virtual(drop_fn.def_id(), 0), // idx 0: the drop function - args: drop_fn.args, - }; - debug!("ty = {:?}", ty); - debug!("drop_fn = {:?}", drop_fn); - debug!("args = {:?}", args); - let fn_abi = bx.fn_abi_of_instance(virtual_drop, ty::List::empty()); - let vtable = args[1]; - // Truncate vtable off of args list - args = &args[..1]; - ( - meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE) - .get_fn(bx, vtable, ty, fn_abi), - fn_abi, - virtual_drop, - ) - } - ty::Dynamic(_, _, ty::DynStar) => { - // IN THIS ARM, WE HAVE: - // ty = *mut (dyn* Trait) - // which is: *mut exists (T, Vtable) - // - // args = [ * ] - // | - // v - // ( Data, Vtable ) - // | - // v - // /-------\ - // | ... | - // \-------/ - // - // - // WE CAN CONVERT THIS INTO THE ABOVE LOGIC BY DOING - // - // data = &(*args[0]).0 // gives a pointer to Data above (really the same pointer) - // vtable = (*args[0]).1 // loads the vtable out - // (data, vtable) // an equivalent Rust `*mut dyn Trait` - // - // SO THEN WE CAN USE THE ABOVE CODE. - let virtual_drop = Instance { - def: ty::InstanceDef::Virtual(drop_fn.def_id(), 0), // idx 0: the drop function - args: drop_fn.args, - }; - debug!("ty = {:?}", ty); - debug!("drop_fn = {:?}", drop_fn); - debug!("args = {:?}", args); - let fn_abi = bx.fn_abi_of_instance(virtual_drop, ty::List::empty()); - let meta_ptr = place.project_field(bx, 1); - let meta = bx.load_operand(meta_ptr); - // Truncate vtable off of args list - args = &args[..1]; - debug!("args' = {:?}", args); - ( - meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE) - .get_fn(bx, meta.immediate(), ty, fn_abi), - fn_abi, - virtual_drop, - ) - } - _ => ( - bx.get_fn_addr(drop_fn), - bx.fn_abi_of_instance(drop_fn, ty::List::empty()), - drop_fn, - ), - }; + let (maybe_null, drop_fn, fn_abi, drop_instance) = match ty.kind() { + // FIXME(eddyb) perhaps move some of this logic into + // `Instance::resolve_drop_in_place`? + ty::Dynamic(_, _, ty::Dyn) => { + // IN THIS ARM, WE HAVE: + // ty = *mut (dyn Trait) + // which is: exists ( *mut T, Vtable ) + // args[0] args[1] + // + // args = ( Data, Vtable ) + // | + // v + // /-------\ + // | ... | + // \-------/ + // + let virtual_drop = Instance { + def: ty::InstanceKind::Virtual(drop_fn.def_id(), 0), // idx 0: the drop function + args: drop_fn.args, + }; + debug!("ty = {:?}", ty); + debug!("drop_fn = {:?}", drop_fn); + debug!("args = {:?}", args); + let fn_abi = bx.fn_abi_of_instance(virtual_drop, ty::List::empty()); + let vtable = args[1]; + // Truncate vtable off of args list + args = &args[..1]; + ( + true, + meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE) + .get_optional_fn(bx, vtable, ty, fn_abi), + fn_abi, + virtual_drop, + ) + } + ty::Dynamic(_, _, ty::DynStar) => { + // IN THIS ARM, WE HAVE: + // ty = *mut (dyn* Trait) + // which is: *mut exists (T, Vtable) + // + // args = [ * ] + // | + // v + // ( Data, Vtable ) + // | + // v + // /-------\ + // | ... | + // \-------/ + // + // + // WE CAN CONVERT THIS INTO THE ABOVE LOGIC BY DOING + // + // data = &(*args[0]).0 // gives a pointer to Data above (really the same pointer) + // vtable = (*args[0]).1 // loads the vtable out + // (data, vtable) // an equivalent Rust `*mut dyn Trait` + // + // SO THEN WE CAN USE THE ABOVE CODE. + let virtual_drop = Instance { + def: ty::InstanceKind::Virtual(drop_fn.def_id(), 0), // idx 0: the drop function + args: drop_fn.args, + }; + debug!("ty = {:?}", ty); + debug!("drop_fn = {:?}", drop_fn); + debug!("args = {:?}", args); + let fn_abi = bx.fn_abi_of_instance(virtual_drop, ty::List::empty()); + let meta_ptr = place.project_field(bx, 1); + let meta = bx.load_operand(meta_ptr); + // Truncate vtable off of args list + args = &args[..1]; + debug!("args' = {:?}", args); + ( + true, + meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE) + .get_optional_fn(bx, meta.immediate(), ty, fn_abi), + fn_abi, + virtual_drop, + ) + } + _ => ( + false, + bx.get_fn_addr(drop_fn), + bx.fn_abi_of_instance(drop_fn, ty::List::empty()), + drop_fn, + ), + }; + + // We generate a null check for the drop_fn. This saves a bunch of relocations being + // generated for no-op drops. + if maybe_null { + let is_not_null = bx.append_sibling_block("is_not_null"); + let llty = bx.fn_ptr_backend_type(fn_abi); + let null = bx.const_null(llty); + let non_null = + bx.icmp(base::bin_op_to_icmp_predicate(mir::BinOp::Ne, false), drop_fn, null); + bx.cond_br(non_null, is_not_null, helper.llbb_with_cleanup(self, target)); + bx.switch_to_block(is_not_null); + self.set_debug_loc(bx, *source_info); + } + helper.do_call( self, bx, @@ -616,7 +634,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { unwind, &[], Some(drop_instance), - mergeable_succ, + !maybe_null && mergeable_succ, ) } @@ -837,7 +855,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let def = instance.map(|i| i.def); if let Some( - ty::InstanceDef::DropGlue(_, None) | ty::InstanceDef::AsyncDropGlueCtorShim(_, None), + ty::InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None), ) = def { // Empty drop glue; a no-op. @@ -853,7 +871,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Handle intrinsics old codegen wants Expr's for, ourselves. let intrinsic = match def { - Some(ty::InstanceDef::Intrinsic(def_id)) => Some(bx.tcx().intrinsic(def_id).unwrap()), + Some(ty::InstanceKind::Intrinsic(def_id)) => Some(bx.tcx().intrinsic(def_id).unwrap()), _ => None, }; @@ -1008,7 +1026,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { 'make_args: for (i, arg) in first_args.iter().enumerate() { let mut op = self.codegen_operand(bx, &arg.node); - if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { + if let (0, Some(ty::InstanceKind::Virtual(_, idx))) = (i, def) { match op.val { Pair(data_ptr, meta) => { // In the case of Rc, we need to explicitly pass a @@ -1345,9 +1363,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { MergingSucc::False } - mir::TerminatorKind::Drop { place, target, unwind, replace: _ } => { - self.codegen_drop_terminator(helper, bx, place, target, unwind, mergeable_succ()) - } + mir::TerminatorKind::Drop { place, target, unwind, replace: _ } => self + .codegen_drop_terminator( + helper, + bx, + &terminator.source_info, + place, + target, + unwind, + mergeable_succ(), + ), mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, unwind } => self .codegen_assert_terminator( diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index dba5fbefd8a7..822f5c2c44a2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -40,10 +40,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) -> Result>, ErrorHandled> { let uv = match self.monomorphize(constant.const_) { mir::Const::Unevaluated(uv, _) => uv.shrink(), - mir::Const::Ty(c) => match c.kind() { + mir::Const::Ty(_, c) => match c.kind() { // A constant that came from a const generic but was then used as an argument to old-style // simd_shuffle (passing as argument instead of as a generic param). - rustc_type_ir::ConstKind::Value(valtree) => return Ok(Some(valtree)), + rustc_type_ir::ConstKind::Value(_, valtree) => return Ok(Some(valtree)), other => span_bug!(constant.span, "{other:#?}"), }, // We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate diff --git a/compiler/rustc_codegen_ssa/src/mir/locals.rs b/compiler/rustc_codegen_ssa/src/mir/locals.rs index 7db260c9f5bd..c7f63eab8298 100644 --- a/compiler/rustc_codegen_ssa/src/mir/locals.rs +++ b/compiler/rustc_codegen_ssa/src/mir/locals.rs @@ -7,6 +7,8 @@ use rustc_index::IndexVec; use rustc_middle::mir; use rustc_middle::ty::print::with_no_trimmed_paths; use std::ops::{Index, IndexMut}; +use tracing::{debug, warn}; + pub(super) struct Locals<'tcx, V> { values: IndexVec>, } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index cf6e2e8d14c6..e8da98428829 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -10,6 +10,7 @@ use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_target::abi::call::{FnAbi, PassMode}; +use tracing::{debug, instrument}; use std::iter; diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 32fd9b657f99..cc0e91396506 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -16,6 +16,7 @@ use std::fmt; use arrayvec::ArrayVec; use either::Either; +use tracing::debug; /// The representation of a Rust value. The enum variant is in fact /// uniquely determined by the value's type, but is kept as a @@ -564,6 +565,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { for elem in place_ref.projection.iter() { match elem { mir::ProjectionElem::Field(ref f, _) => { + debug_assert!( + !o.layout.ty.is_any_ptr(), + "Bad PlaceRef: destructing pointers should use cast/PtrMetadata, \ + but tried to access field {f:?} of pointer {o:?}", + ); o = o.extract_field(bx, f.index()); } mir::ProjectionElem::Index(_) diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 971ac2defdc0..449fd9ae0db9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -12,6 +12,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; use rustc_target::abi::{Align, FieldsShape, Int, Pointer, Size, TagEncoding}; use rustc_target::abi::{VariantIdx, Variants}; +use tracing::{debug, instrument}; /// The location and extra runtime properties of the place. /// @@ -159,9 +160,9 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { bx.inbounds_ptradd(self.val.llval, bx.const_usize(offset.bytes())) }; let val = PlaceValue { - llval, + llval, llextra: if bx.cx().type_has_metadata(field.ty) { self.val.llextra } else { None }, - align: effective_field_align, + align: effective_field_align, }; val.with_type(field) }; @@ -408,9 +409,9 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { }; let llval = bx.inbounds_gep( - bx.cx().backend_type(self.layout), - self.val.llval, - &[bx.cx().const_usize(0), llindex], + bx.cx().backend_type(self.layout), + self.val.llval, + &[bx.cx().const_usize(0), llindex], ); let align = self.val.align.restrict_for_offset(offset); PlaceValue::new_sized(llval, align).with_type(layout) @@ -479,6 +480,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { cg_base = match *elem { mir::ProjectionElem::Deref => bx.load_operand(cg_base).deref(bx.cx()), mir::ProjectionElem::Field(ref field, _) => { + debug_assert!( + !cg_base.layout.ty.is_any_ptr(), + "Bad PlaceRef: destructing pointers should use cast/PtrMetadata, \ + but tried to access field {field:?} of pointer {cg_base:?}", + ); cg_base.project_field(bx, field.index()) } mir::ProjectionElem::OpaqueCast(ty) => { diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index ff176a796759..05861e337674 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -7,7 +7,6 @@ use crate::common::IntPredicate; use crate::traits::*; use crate::MemFlags; -use rustc_hir as hir; use rustc_middle::mir; use rustc_middle::ty::cast::{CastTy, IntTy}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; @@ -18,6 +17,7 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{self, FieldIdx, FIRST_VARIANT}; use arrayvec::ArrayVec; +use tracing::{debug, instrument}; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { #[instrument(level = "trace", skip(self, bx))] @@ -456,8 +456,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { base::unsize_ptr(bx, lldata, operand.layout.ty, cast.ty, llextra); OperandValue::Pair(lldata, llextra) } - mir::CastKind::PointerCoercion(PointerCoercion::MutToConstPointer) - | mir::CastKind::PtrToPtr + mir::CastKind::PointerCoercion( + PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, + ) => { + bug!("{kind:?} is for borrowck, and should never appear in codegen"); + } + mir::CastKind::PtrToPtr if bx.cx().is_backend_scalar_pair(operand.layout) => { if let OperandValue::Pair(data_ptr, meta) = operand.val { @@ -477,9 +481,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { base::cast_to_dyn_star(bx, lldata, operand.layout, cast.ty, llextra); OperandValue::Pair(lldata, llextra) } - mir::CastKind::PointerCoercion( - PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, - ) | mir::CastKind::IntToInt | mir::CastKind::FloatToInt | mir::CastKind::FloatToFloat @@ -576,6 +577,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } + mir::Rvalue::BinaryOp(op_with_overflow, box (ref lhs, ref rhs)) + if let Some(op) = op_with_overflow.overflowing_to_wrapping() => + { + let lhs = self.codegen_operand(bx, lhs); + let rhs = self.codegen_operand(bx, rhs); + let result = self.codegen_scalar_checked_binop( + bx, + op, + lhs.immediate(), + rhs.immediate(), + lhs.layout.ty, + ); + let val_ty = op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty); + let operand_ty = Ty::new_tup(bx.tcx(), &[val_ty, bx.tcx().types.bool]); + OperandRef { val: result, layout: bx.cx().layout_of(operand_ty) } + } mir::Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => { let lhs = self.codegen_operand(bx, lhs); let rhs = self.codegen_operand(bx, rhs); @@ -604,36 +621,39 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { layout: bx.cx().layout_of(op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty)), } } - mir::Rvalue::CheckedBinaryOp(op, box (ref lhs, ref rhs)) => { - let lhs = self.codegen_operand(bx, lhs); - let rhs = self.codegen_operand(bx, rhs); - let result = self.codegen_scalar_checked_binop( - bx, - op, - lhs.immediate(), - rhs.immediate(), - lhs.layout.ty, - ); - let val_ty = op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty); - let operand_ty = Ty::new_tup(bx.tcx(), &[val_ty, bx.tcx().types.bool]); - OperandRef { val: result, layout: bx.cx().layout_of(operand_ty) } - } mir::Rvalue::UnaryOp(op, ref operand) => { let operand = self.codegen_operand(bx, operand); - let lloperand = operand.immediate(); let is_float = operand.layout.ty.is_floating_point(); - let llval = match op { - mir::UnOp::Not => bx.not(lloperand), + let (val, layout) = match op { + mir::UnOp::Not => { + let llval = bx.not(operand.immediate()); + (OperandValue::Immediate(llval), operand.layout) + } mir::UnOp::Neg => { - if is_float { - bx.fneg(lloperand) + let llval = if is_float { + bx.fneg(operand.immediate()) } else { - bx.neg(lloperand) + bx.neg(operand.immediate()) + }; + (OperandValue::Immediate(llval), operand.layout) + } + mir::UnOp::PtrMetadata => { + debug_assert!(operand.layout.ty.is_unsafe_ptr()); + let (_, meta) = operand.val.pointer_parts(); + assert_eq!(operand.layout.fields.count() > 1, meta.is_some()); + if let Some(meta) = meta { + (OperandValue::Immediate(meta), operand.layout.field(self.cx, 1)) + } else { + (OperandValue::ZeroSized, bx.cx().layout_of(bx.tcx().types.unit)) } } }; - OperandRef { val: OperandValue::Immediate(llval), layout: operand.layout } + debug_assert!( + val.is_expected_variant_for_type(self.cx, layout), + "Made wrong variant {val:?} for type {layout:?}", + ); + OperandRef { val, layout } } mir::Rvalue::Discriminant(ref place) => { @@ -661,7 +681,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.cx().const_usize(val) } mir::NullOp::OffsetOf(fields) => { - let val = layout.offset_of_subfield(bx.cx(), fields.iter()).bytes(); + let val = bx + .tcx() + .offset_of_subfield(bx.param_env(), layout, fields.iter()) + .bytes(); bx.cx().const_usize(val) } mir::NullOp::UbChecks => { @@ -682,7 +705,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let static_ = if !def_id.is_local() && bx.cx().tcx().needs_thread_local_shim(def_id) { let instance = ty::Instance { - def: ty::InstanceDef::ThreadLocalShim(def_id), + def: ty::InstanceKind::ThreadLocalShim(def_id), args: ty::GenericArgs::empty(), }; let fn_ptr = bx.get_fn_addr(instance); @@ -716,8 +739,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let values = op.val.immediates_or_place().left_or_else(|p| { bug!("Field {field_idx:?} is {p:?} making {layout:?}"); }); - inputs.extend(values); let scalars = self.value_kind(op.layout).scalars().unwrap(); + debug_assert_eq!(values.len(), scalars.len()); + inputs.extend(values); input_scalars.extend(scalars); } @@ -894,9 +918,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | mir::BinOp::Le | mir::BinOp::Ge => { if is_float { - bx.fcmp(base::bin_op_to_fcmp_predicate(op.to_hir_binop()), lhs, rhs) + bx.fcmp(base::bin_op_to_fcmp_predicate(op), lhs, rhs) } else { - bx.icmp(base::bin_op_to_icmp_predicate(op.to_hir_binop(), is_signed), lhs, rhs) + bx.icmp(base::bin_op_to_icmp_predicate(op, is_signed), lhs, rhs) } } mir::BinOp::Cmp => { @@ -910,16 +934,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // `PartialOrd`, so only use it in debug for now. Once LLVM can handle it // better (see ), it'll // be worth trying it in optimized builds as well. - let is_gt = bx.icmp(pred(hir::BinOpKind::Gt), lhs, rhs); + let is_gt = bx.icmp(pred(mir::BinOp::Gt), lhs, rhs); let gtext = bx.zext(is_gt, bx.type_i8()); - let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs); + let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs); let ltext = bx.zext(is_lt, bx.type_i8()); bx.unchecked_ssub(gtext, ltext) } else { // These operations are those expected by `tests/codegen/integer-cmp.rs`, // from . - let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs); - let is_ne = bx.icmp(pred(hir::BinOpKind::Ne), lhs, rhs); + let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs); + let is_ne = bx.icmp(pred(mir::BinOp::Ne), lhs, rhs); let ge = bx.select( is_ne, bx.cx().const_i8(Ordering::Greater as i8), @@ -928,6 +952,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.select(is_lt, bx.cx().const_i8(Ordering::Less as i8), ge) } } + mir::BinOp::AddWithOverflow + | mir::BinOp::SubWithOverflow + | mir::BinOp::MulWithOverflow => { + bug!("{op:?} needs to return a pair, so call codegen_scalar_checked_binop instead") + } } } @@ -1040,7 +1069,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::Cast(..) | // (*) mir::Rvalue::ShallowInitBox(..) | // (*) mir::Rvalue::BinaryOp(..) | - mir::Rvalue::CheckedBinaryOp(..) | mir::Rvalue::UnaryOp(..) | mir::Rvalue::Discriminant(..) | mir::Rvalue::NullaryOp(..) | diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index a0429022587c..27494f48b099 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -1,6 +1,7 @@ use rustc_middle::mir::{self, NonDivergingIntrinsic}; use rustc_middle::span_bug; use rustc_session::config::OptLevel; +use tracing::instrument; use super::FunctionCx; use super::LocalRef; diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index 40921c2932fa..0fbcb938d1a7 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -9,6 +9,7 @@ use rustc_middle::span_bug; use rustc_middle::ty; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::ty::Instance; +use tracing::debug; pub trait MonoItemExt<'a, 'tcx> { fn define>(&self, cx: &'a Bx::CodegenCx); diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index 032699f1fa15..130fe2eaf2fe 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -9,6 +9,7 @@ use rustc_middle::bug; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Ty}; use rustc_target::abi::WrappingRange; +use tracing::{debug, trace}; pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 9fd6eb8edab3..2f0daefa46a6 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -110,8 +110,16 @@ pub trait BuilderMethods<'a, 'tcx>: fn frem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn frem_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn frem_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// Generate a left-shift. Both operands must have the same size. The right operand must be + /// interpreted as unsigned and can be assumed to be less than the size of the left operand. fn shl(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// Generate a logical right-shift. Both operands must have the same size. The right operand + /// must be interpreted as unsigned and can be assumed to be less than the size of the left + /// operand. fn lshr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// Generate an arithmetic right-shift. Both operands must have the same size. The right operand + /// must be interpreted as unsigned and can be assumed to be less than the size of the left + /// operand. fn ashr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn unchecked_sadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn unchecked_uadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 048540894ac9..f4b1421a5329 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -2,7 +2,7 @@ use crate::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig}; use crate::{CompiledModule, ModuleCodegen}; -use rustc_errors::{DiagCtxt, FatalError}; +use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_middle::dep_graph::WorkProduct; pub trait WriteBackendMethods: 'static + Sized + Clone { @@ -16,7 +16,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { /// Merge all modules into main_module and returning it fn run_link( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, modules: Vec>, ) -> Result, FatalError>; /// Performs fat LTO by merging all modules into a single one and returning it @@ -38,7 +38,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { fn print_statistics(&self); unsafe fn optimize( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, module: &ModuleCodegen, config: &ModuleConfig, ) -> Result<(), FatalError>; @@ -52,16 +52,20 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { ) -> Result, FatalError>; unsafe fn codegen( cgcx: &CodegenContext, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, module: ModuleCodegen, config: &ModuleConfig, ) -> Result; - fn prepare_thin(module: ModuleCodegen) -> (String, Self::ThinBuffer); + fn prepare_thin( + module: ModuleCodegen, + want_summary: bool, + ) -> (String, Self::ThinBuffer); fn serialize_module(module: ModuleCodegen) -> (String, Self::ModuleBuffer); } pub trait ThinBufferMethods: Send + Sync { fn data(&self) -> &[u8]; + fn thin_link_data(&self) -> &[u8]; } pub trait ModuleBufferMethods: Send + Sync { diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 20f0f27517ff..1a7e5bd70921 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -73,8 +73,6 @@ const_eval_division_by_zero = dividing by zero const_eval_division_overflow = overflow in signed division (dividing MIN by -1) -const_eval_double_storage_live = - StorageLive on a local that was already live const_eval_dyn_call_not_a_method = `dyn` call trying to call something that is not a method @@ -246,11 +244,10 @@ const_eval_offset_from_unsigned_overflow = const_eval_operator_non_const = cannot call non-const operator in {const_eval_const_context}s -const_eval_overflow = - overflow executing `{$name}` - +const_eval_overflow_arith = + arithmetic overflow in `{$intrinsic}` const_eval_overflow_shift = - overflowing shift by {$val} in `{$name}` + overflowing shift by {$shift_amount} in `{$intrinsic}` const_eval_panic = the evaluated program panicked at '{$msg}', {$file}:{$line}:{$col} @@ -400,7 +397,6 @@ const_eval_unwind_past_top = ## The `front_matter`s here refer to either `const_eval_front_matter_invalid_value` or `const_eval_front_matter_invalid_value_with_path`. ## (We'd love to sort this differently to make that more clear but tidy won't let us...) -const_eval_validation_box_to_static = {$front_matter}: encountered a box pointing to a static variable in a constant const_eval_validation_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty} const_eval_validation_const_ref_to_extern = {$front_matter}: encountered reference to `extern` static in `const` @@ -455,7 +451,6 @@ const_eval_validation_out_of_range = {$front_matter}: encountered {$value}, but const_eval_validation_partial_pointer = {$front_matter}: encountered a partial pointer or a mix of pointers const_eval_validation_pointer_as_int = {$front_matter}: encountered a pointer, but {$expected} const_eval_validation_ptr_out_of_range = {$front_matter}: encountered a pointer, but expected something that cannot possibly fail to be {$in_range} -const_eval_validation_ref_to_static = {$front_matter}: encountered a reference pointing to a static variable in a constant const_eval_validation_ref_to_uninhabited = {$front_matter}: encountered a reference pointing to uninhabited type {$ty} const_eval_validation_unaligned_box = {$front_matter}: encountered an unaligned box (required {$required_bytes} byte alignment but found {$found_bytes}) const_eval_validation_unaligned_ref = {$front_matter}: encountered an unaligned reference (required {$required_bytes} byte alignment but found {$found_bytes}) diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs similarity index 98% rename from compiler/rustc_const_eval/src/transform/check_consts/check.rs rename to compiler/rustc_const_eval/src/check_consts/check.rs index 5edf5bb39dd4..ab60cc379209 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -1,8 +1,8 @@ //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations. use rustc_errors::{Diag, ErrorGuaranteed}; -use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, LangItem}; use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::ObligationCause; @@ -10,7 +10,7 @@ use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceC use rustc_middle::mir::*; use rustc_middle::span_bug; use rustc_middle::ty::{self, adjustment::PointerCoercion, Ty, TyCtxt}; -use rustc_middle::ty::{Instance, InstanceDef, TypeVisitableExt}; +use rustc_middle::ty::{Instance, InstanceKind, TypeVisitableExt}; use rustc_mir_dataflow::Analysis; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; @@ -20,6 +20,8 @@ use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitor}; use std::mem; use std::ops::Deref; +use tracing::{debug, instrument, trace}; + use super::ops::{self, NonConstOp, Status}; use super::qualifs::{self, HasMutInterior, NeedsDrop, NeedsNonConstDrop}; use super::resolver::FlowSensitiveAnalysis; @@ -580,7 +582,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } - Rvalue::BinaryOp(op, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(op, box (lhs, rhs)) => { + Rvalue::BinaryOp(op, box (lhs, rhs)) => { let lhs_ty = lhs.ty(self.body, self.tcx); let rhs_ty = rhs.ty(self.body, self.tcx); @@ -733,7 +735,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // which path expressions are getting called on and which path expressions are only used // as function pointers. This is required for correctness. let infcx = tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(&infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let predicates = tcx.predicates_of(callee).instantiate(tcx, fn_args); let cause = ObligationCause::new( @@ -767,7 +769,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { if let Ok(Some(instance)) = Instance::resolve(tcx, param_env, callee, fn_args) - && let InstanceDef::Item(def) = instance.def + && let InstanceKind::Item(def) = instance.def { // Resolve a trait method call to its concrete implementation, which may be in a // `const` trait impl. This is only used for the const stability check below, since @@ -799,7 +801,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // const-eval. // const-eval of the `begin_panic` fn assumes the argument is `&str` - if Some(callee) == tcx.lang_items().begin_panic_fn() { + if tcx.is_lang_item(callee, LangItem::BeginPanic) { match args[0].node.ty(&self.ccx.body.local_decls, tcx).kind() { ty::Ref(_, ty, _) if ty.is_str() => return, _ => self.check_op(ops::PanicNonStr), @@ -817,7 +819,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } - if Some(callee) == tcx.lang_items().exchange_malloc_fn() { + if tcx.is_lang_item(callee, LangItem::ExchangeMalloc) { self.check_op(ops::HeapAllocation); return; } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs similarity index 98% rename from compiler/rustc_const_eval/src/transform/check_consts/mod.rs rename to compiler/rustc_const_eval/src/check_consts/mod.rs index 308b90cd470d..ac8f0d842ee4 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -5,7 +5,7 @@ //! it finds operations that are invalid in a certain context. use rustc_attr as attr; -use rustc_errors::DiagCtxt; +use rustc_errors::DiagCtxtHandle; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::bug; @@ -46,7 +46,7 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> { ConstCx { body, tcx, param_env, const_kind } } - pub(crate) fn dcx(&self) -> &'tcx DiagCtxt { + pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> { self.tcx.dcx() } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs similarity index 98% rename from compiler/rustc_const_eval/src/transform/check_consts/ops.rs rename to compiler/rustc_const_eval/src/check_consts/ops.rs index 8775685e8c73..55432e63ef9d 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -19,6 +19,7 @@ use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{BytePos, Pos, Span, Symbol}; use rustc_trait_selection::traits::SelectionContext; +use tracing::debug; use super::ConstCx; use crate::errors; @@ -112,7 +113,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { if let Some(generics) = tcx.hir_node_by_def_id(caller).generics() { let constraint = with_no_trimmed_paths!(format!( "~const {}", - trait_ref.print_only_trait_path() + trait_ref.print_trait_sugared(), )); suggest_constraining_type_param( tcx, @@ -137,7 +138,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { // FIXME(effects) revisit this if !tcx.is_const_trait_impl_raw(data.impl_def_id) { let span = tcx.def_span(data.impl_def_id); - err.subdiagnostic(tcx.dcx(), errors::NonConstImplNote { span }); + err.subdiagnostic(errors::NonConstImplNote { span }); } } } @@ -217,7 +218,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { } else { let mut sugg = None; - if Some(trait_id) == ccx.tcx.lang_items().eq_trait() { + if ccx.tcx.is_lang_item(trait_id, LangItem::PartialEq) { match (args[0].unpack(), args[1].unpack()) { (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty)) if self_ty == rhs_ty @@ -308,7 +309,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { } if let ConstContext::Static(_) = ccx.const_kind() { - err.note("consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell"); + err.note("consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`"); } err diff --git a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs similarity index 99% rename from compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs rename to compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs index 5cd13783c231..f0763f1e490f 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs @@ -2,6 +2,7 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{self, BasicBlock, Location}; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_span::{symbol::sym, Span}; +use tracing::trace; use super::check::Qualifs; use super::ops::{self, NonConstOp}; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs similarity index 98% rename from compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs rename to compiler/rustc_const_eval/src/check_consts/qualifs.rs index eae0e2f27dbd..9fd7219499b2 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -13,6 +13,7 @@ use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty}; use rustc_trait_selection::traits::{ ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext, }; +use tracing::{instrument, trace}; use super::ConstCx; @@ -261,7 +262,7 @@ where | Rvalue::Cast(_, operand, _) | Rvalue::ShallowInitBox(operand, _) => in_operand::(cx, in_local, operand), - Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => { + Rvalue::BinaryOp(_, box (lhs, rhs)) => { in_operand::(cx, in_local, lhs) || in_operand::(cx, in_local, rhs) } @@ -356,15 +357,15 @@ where // Check the qualifs of the value of `const` items. let uneval = match constant.const_ { - Const::Ty(ct) + Const::Ty(_, ct) if matches!( ct.kind(), - ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_) + ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_, _) ) => { None } - Const::Ty(c) => { + Const::Ty(_, c) => { bug!("expected ConstKind::Param or ConstKind::Value here, found {:?}", c) } Const::Unevaluated(uv, _) => Some(uv), diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs similarity index 99% rename from compiler/rustc_const_eval/src/transform/check_consts/resolver.rs rename to compiler/rustc_const_eval/src/check_consts/resolver.rs index 5ae3ffaaec2f..011341472b43 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -200,7 +200,6 @@ where | mir::Rvalue::Repeat(..) | mir::Rvalue::Len(..) | mir::Rvalue::BinaryOp(..) - | mir::Rvalue::CheckedBinaryOp(..) | mir::Rvalue::NullaryOp(..) | mir::Rvalue::UnaryOp(..) | mir::Rvalue::Discriminant(..) diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs index 94c9f056b302..9a98677a8448 100644 --- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -44,8 +44,8 @@ impl HasStaticRootDefId for DummyMachine { } } -impl<'mir, 'tcx: 'mir> interpret::Machine<'mir, 'tcx> for DummyMachine { - interpret::compile_time_machine!(<'mir, 'tcx>); +impl<'tcx> interpret::Machine<'tcx> for DummyMachine { + interpret::compile_time_machine!(<'tcx>); type MemoryKind = !; const PANIC_ON_ALLOC_FAIL: bool = true; @@ -53,11 +53,11 @@ impl<'mir, 'tcx: 'mir> interpret::Machine<'mir, 'tcx> for DummyMachine { const ALL_CONSTS_ARE_PRECHECKED: bool = false; #[inline(always)] - fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { + fn enforce_alignment(_ecx: &InterpCx<'tcx, Self>) -> bool { false // no reason to enforce alignment } - fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool { + fn enforce_validity(_ecx: &InterpCx<'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool { false } @@ -83,26 +83,26 @@ impl<'mir, 'tcx: 'mir> interpret::Machine<'mir, 'tcx> for DummyMachine { } fn find_mir_or_eval_fn( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ecx: &mut InterpCx<'tcx, Self>, _instance: ty::Instance<'tcx>, _abi: rustc_target::spec::abi::Abi, _args: &[interpret::FnArg<'tcx, Self::Provenance>], _destination: &interpret::MPlaceTy<'tcx, Self::Provenance>, _target: Option, _unwind: UnwindAction, - ) -> interpret::InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> { + ) -> interpret::InterpResult<'tcx, Option<(&'tcx Body<'tcx>, ty::Instance<'tcx>)>> { unimplemented!() } fn panic_nounwind( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ecx: &mut InterpCx<'tcx, Self>, _msg: &str, ) -> interpret::InterpResult<'tcx> { unimplemented!() } fn call_intrinsic( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ecx: &mut InterpCx<'tcx, Self>, _instance: ty::Instance<'tcx>, _args: &[interpret::OpTy<'tcx, Self::Provenance>], _destination: &interpret::MPlaceTy<'tcx, Self::Provenance>, @@ -113,7 +113,7 @@ impl<'mir, 'tcx: 'mir> interpret::Machine<'mir, 'tcx> for DummyMachine { } fn assert_panic( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ecx: &mut InterpCx<'tcx, Self>, _msg: &rustc_middle::mir::AssertMessage<'tcx>, _unwind: UnwindAction, ) -> interpret::InterpResult<'tcx> { @@ -121,11 +121,11 @@ impl<'mir, 'tcx: 'mir> interpret::Machine<'mir, 'tcx> for DummyMachine { } fn binary_ptr_op( - ecx: &InterpCx<'mir, 'tcx, Self>, + ecx: &InterpCx<'tcx, Self>, bin_op: BinOp, left: &interpret::ImmTy<'tcx, Self::Provenance>, right: &interpret::ImmTy<'tcx, Self::Provenance>, - ) -> interpret::InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)> { + ) -> interpret::InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>> { use rustc_middle::mir::BinOp::*; Ok(match bin_op { Eq | Ne | Lt | Le | Gt | Ge => { @@ -154,7 +154,7 @@ impl<'mir, 'tcx: 'mir> interpret::Machine<'mir, 'tcx> for DummyMachine { Ge => left >= right, _ => bug!(), }; - (ImmTy::from_bool(res, *ecx.tcx), false) + ImmTy::from_bool(res, *ecx.tcx) } // Some more operations are possible with atomics. @@ -168,32 +168,30 @@ impl<'mir, 'tcx: 'mir> interpret::Machine<'mir, 'tcx> for DummyMachine { } fn expose_ptr( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ecx: &mut InterpCx<'tcx, Self>, _ptr: interpret::Pointer, ) -> interpret::InterpResult<'tcx> { unimplemented!() } - fn init_frame_extra( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _frame: interpret::Frame<'mir, 'tcx, Self::Provenance>, - ) -> interpret::InterpResult< - 'tcx, - interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, - > { + fn init_frame( + _ecx: &mut InterpCx<'tcx, Self>, + _frame: interpret::Frame<'tcx, Self::Provenance>, + ) -> interpret::InterpResult<'tcx, interpret::Frame<'tcx, Self::Provenance, Self::FrameExtra>> + { unimplemented!() } fn stack<'a>( - _ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { + _ecx: &'a InterpCx<'tcx, Self>, + ) -> &'a [interpret::Frame<'tcx, Self::Provenance, Self::FrameExtra>] { // Return an empty stack instead of panicking, as `cur_span` uses it to evaluate constants. &[] } fn stack_mut<'a>( - _ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec> { + _ecx: &'a mut InterpCx<'tcx, Self>, + ) -> &'a mut Vec> { unimplemented!() } } diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 08c9609eacfa..b17dc7f3ddde 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -1,15 +1,14 @@ use std::mem; use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, Diagnostic, IntoDiagArg}; -use rustc_hir::CRATE_HIR_ID; -use rustc_middle::mir::interpret::Provenance; +use rustc_middle::mir::interpret::{Provenance, ReportedErrorInfo}; use rustc_middle::mir::AssertKind; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{layout::LayoutError, ConstInt}; use rustc_span::{Span, Symbol}; -use super::CompileTimeInterpreter; +use super::CompileTimeMachine; use crate::errors::{self, FrameNote, ReportErrorExt}; use crate::interpret::{err_inval, err_machine_stop}; use crate::interpret::{ErrorHandled, Frame, InterpError, InterpErrorInfo, MachineStopType}; @@ -58,13 +57,10 @@ impl<'tcx> Into> for ConstEvalErrKind { } } -pub fn get_span_and_frames<'tcx, 'mir>( +pub fn get_span_and_frames<'tcx>( tcx: TyCtxtAt<'tcx>, - stack: &[Frame<'mir, 'tcx, impl Provenance, impl Sized>], -) -> (Span, Vec) -where - 'tcx: 'mir, -{ + stack: &[Frame<'tcx, impl Provenance, impl Sized>], +) -> (Span, Vec) { let mut stacktrace = Frame::generate_stacktrace_from_stack(stack); // Filter out `requires_caller_location` frames. stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx)); @@ -140,7 +136,7 @@ where } err_inval!(AlreadyReported(guar)) => ErrorHandled::Reported(guar, span), err_inval!(Layout(LayoutError::ReferencesError(guar))) => { - ErrorHandled::Reported(guar.into(), span) + ErrorHandled::Reported(ReportedErrorInfo::tainted_by_errors(guar), span) } // Report remaining errors. _ => { @@ -159,11 +155,11 @@ where } } -/// Emit a lint from a const-eval situation. +/// Emit a lint from a const-eval situation, with a backtrace. // Even if this is unused, please don't remove it -- chances are we will need to emit a lint during const-eval again in the future! -pub(super) fn lint<'tcx, 'mir, L>( +pub(super) fn lint<'tcx, L>( tcx: TyCtxtAt<'tcx>, - machine: &CompileTimeInterpreter<'mir, 'tcx>, + machine: &CompileTimeMachine<'tcx>, lint: &'static rustc_session::lint::Lint, decorator: impl FnOnce(Vec) -> L, ) where @@ -171,12 +167,5 @@ pub(super) fn lint<'tcx, 'mir, L>( { let (span, frames) = get_span_and_frames(tcx, &machine.stack); - tcx.emit_node_span_lint( - lint, - // We use the root frame for this so the crate that defines the const defines whether the - // lint is emitted. - machine.stack.first().and_then(|frame| frame.lint_root()).unwrap_or(CRATE_HIR_ID), - span, - decorator(frames), - ); + tcx.emit_node_span_lint(lint, machine.best_lint_scope(*tcx), span, decorator(frames)); } diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 6a9a21bbd8e0..c60df06bb0ef 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -1,6 +1,7 @@ use std::sync::atomic::Ordering::Relaxed; use either::{Left, Right}; +use tracing::{debug, instrument, trace}; use rustc_hir::def::DefKind; use rustc_middle::bug; @@ -16,7 +17,7 @@ use rustc_span::def_id::LocalDefId; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{self, Abi}; -use super::{CanAccessMutGlobal, CompileTimeEvalContext, CompileTimeInterpreter}; +use super::{CanAccessMutGlobal, CompileTimeInterpCx, CompileTimeMachine}; use crate::const_eval::CheckAlignment; use crate::errors::ConstEvalError; use crate::errors::{self, DanglingPtrInFinal}; @@ -30,10 +31,10 @@ use crate::CTRL_C_RECEIVED; // Returns a pointer to where the result lives #[instrument(level = "trace", skip(ecx, body))] -fn eval_body_using_ecx<'mir, 'tcx, R: InterpretationResult<'tcx>>( - ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, +fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( + ecx: &mut CompileTimeInterpCx<'tcx>, cid: GlobalId<'tcx>, - body: &'mir mir::Body<'tcx>, + body: &'tcx mir::Body<'tcx>, ) -> InterpResult<'tcx, R> { trace!(?ecx.param_env); let tcx = *ecx.tcx; @@ -113,7 +114,7 @@ fn eval_body_using_ecx<'mir, 'tcx, R: InterpretationResult<'tcx>>( let err_diag = errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }; ecx.tcx.emit_node_span_lint( lint::builtin::CONST_EVAL_MUTABLE_PTR_IN_FINAL_VALUE, - ecx.best_lint_scope(), + ecx.machine.best_lint_scope(*ecx.tcx), err_diag.span, err_diag, ) @@ -133,29 +134,29 @@ fn eval_body_using_ecx<'mir, 'tcx, R: InterpretationResult<'tcx>>( /// that inform us about the generic bounds of the constant. E.g., using an associated constant /// of a function's generic parameter will require knowledge about the bounds on the generic /// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument. -pub(crate) fn mk_eval_cx_to_read_const_val<'mir, 'tcx>( +pub(crate) fn mk_eval_cx_to_read_const_val<'tcx>( tcx: TyCtxt<'tcx>, root_span: Span, param_env: ty::ParamEnv<'tcx>, can_access_mut_global: CanAccessMutGlobal, -) -> CompileTimeEvalContext<'mir, 'tcx> { +) -> CompileTimeInterpCx<'tcx> { debug!("mk_eval_cx: {:?}", param_env); InterpCx::new( tcx, root_span, param_env, - CompileTimeInterpreter::new(can_access_mut_global, CheckAlignment::No), + CompileTimeMachine::new(can_access_mut_global, CheckAlignment::No), ) } /// Create an interpreter context to inspect the given `ConstValue`. /// Returns both the context and an `OpTy` that represents the constant. -pub fn mk_eval_cx_for_const_val<'mir, 'tcx>( +pub fn mk_eval_cx_for_const_val<'tcx>( tcx: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>, val: mir::ConstValue<'tcx>, ty: Ty<'tcx>, -) -> Option<(CompileTimeEvalContext<'mir, 'tcx>, OpTy<'tcx>)> { +) -> Option<(CompileTimeInterpCx<'tcx>, OpTy<'tcx>)> { let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No); let op = ecx.const_val_to_op(val, ty, None).ok()?; Some((ecx, op)) @@ -169,7 +170,7 @@ pub fn mk_eval_cx_for_const_val<'mir, 'tcx>( /// encounter an `Indirect` they cannot handle. #[instrument(skip(ecx), level = "debug")] pub(super) fn op_to_const<'tcx>( - ecx: &CompileTimeEvalContext<'_, 'tcx>, + ecx: &CompileTimeInterpCx<'tcx>, op: &OpTy<'tcx>, for_diagnostics: bool, ) -> ConstValue<'tcx> { @@ -288,7 +289,7 @@ pub fn eval_to_const_value_raw_provider<'tcx>( // We call `const_eval` for zero arg intrinsics, too, in order to cache their value. // Catch such calls and evaluate them instead of trying to load a constant's MIR. - if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def { + if let ty::InstanceKind::Intrinsic(def_id) = key.value.instance.def { let ty = key.value.instance.ty(tcx, key.param_env); let ty::FnDef(_, args) = ty.kind() else { bug!("intrinsic with type {:?}", ty); @@ -325,16 +326,16 @@ pub trait InterpretationResult<'tcx> { /// This function takes the place where the result of the evaluation is stored /// and prepares it for returning it in the appropriate format needed by the specific /// evaluation query. - fn make_result<'mir>( + fn make_result( mplace: MPlaceTy<'tcx>, - ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>, ) -> Self; } impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> { - fn make_result<'mir>( + fn make_result( mplace: MPlaceTy<'tcx>, - _ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + _ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>, ) -> Self { ConstAlloc { alloc_id: mplace.ptr().provenance.unwrap().alloc_id(), ty: mplace.layout.ty } } @@ -382,7 +383,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( // they do not have to behave "as if" they were evaluated at runtime. // For consts however we want to ensure they behave "as if" they were evaluated at runtime, // so we have to reject reading mutable global memory. - CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error), + CompileTimeMachine::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error), ); let res = ecx.load_mir(cid.instance.def, cid.promoted); res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)).map_err(|error| { @@ -415,8 +416,8 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( } #[inline(always)] -fn const_validate_mplace<'mir, 'tcx>( - ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, +fn const_validate_mplace<'tcx>( + ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>, mplace: &MPlaceTy<'tcx>, cid: GlobalId<'tcx>, ) -> Result<(), ErrorHandled> { @@ -445,8 +446,8 @@ fn const_validate_mplace<'mir, 'tcx>( } #[inline(always)] -fn report_validation_error<'mir, 'tcx>( - ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, +fn report_validation_error<'tcx>( + ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>, error: InterpErrorInfo<'tcx>, alloc_id: AllocId, ) -> ErrorHandled { diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 836e548ae2b7..99276bac0350 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -6,28 +6,29 @@ use std::ops::ControlFlow; use rustc_ast::Mutability; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::fx::IndexEntry; -use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::def_id::LocalDefId; use rustc_hir::LangItem; +use rustc_hir::{self as hir, CRATE_HIR_ID}; use rustc_middle::bug; use rustc_middle::mir; use rustc_middle::mir::AssertMessage; use rustc_middle::query::TyCtxtAt; -use rustc_middle::ty; use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::builtin::WRITES_THROUGH_IMMUTABLE_POINTER; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi as CallAbi; +use tracing::debug; use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ - self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, + self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, throw_unsup, throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, FnVal, Frame, - ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar, + GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar, }; use super::error::*; @@ -44,7 +45,7 @@ const TINY_LINT_TERMINATOR_LIMIT: usize = 20; const PROGRESS_INDICATOR_START: usize = 4_000_000; /// Extra machine state for CTFE, and the Machine instance -pub struct CompileTimeInterpreter<'mir, 'tcx> { +pub struct CompileTimeMachine<'tcx> { /// The number of terminators that have been evaluated. /// /// This is used to produce lints informing the user that the compiler is not stuck. @@ -52,7 +53,7 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> { pub(super) num_evaluated_steps: usize, /// The virtual call stack. - pub(super) stack: Vec>, + pub(super) stack: Vec>, /// Pattern matching on consts with references would be unsound if those references /// could point to anything mutable. Therefore, when evaluating consts and when constructing valtrees, @@ -89,12 +90,12 @@ impl From for CanAccessMutGlobal { } } -impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> { +impl<'tcx> CompileTimeMachine<'tcx> { pub(crate) fn new( can_access_mut_global: CanAccessMutGlobal, check_alignment: CheckAlignment, ) -> Self { - CompileTimeInterpreter { + CompileTimeMachine { num_evaluated_steps: 0, stack: Vec::new(), can_access_mut_global, @@ -163,8 +164,7 @@ impl interpret::AllocMap for FxIndexMap { } } -pub(crate) type CompileTimeEvalContext<'mir, 'tcx> = - InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>; +pub(crate) type CompileTimeInterpCx<'tcx> = InterpCx<'tcx, CompileTimeMachine<'tcx>>; #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum MemoryKind { @@ -196,7 +196,7 @@ impl interpret::MayLeak for ! { } } -impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { +impl<'tcx> CompileTimeInterpCx<'tcx> { fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) { let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo()); @@ -230,7 +230,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { let def_id = instance.def_id(); if self.tcx.has_attr(def_id, sym::rustc_const_panic_str) - || Some(def_id) == self.tcx.lang_items().begin_panic_fn() + || self.tcx.is_lang_item(def_id, LangItem::BeginPanic) { let args = self.copy_fn_args(args); // &str or &&str @@ -245,7 +245,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { let span = self.find_closest_untracked_caller_location(); let (file, line, col) = self.location_triple_for_span(span); return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into()); - } else if Some(def_id) == self.tcx.lang_items().panic_fmt() { + } else if self.tcx.is_lang_item(def_id, LangItem::PanicFmt) { // For panic_fmt, call const_panic_fmt instead. let const_def_id = self.tcx.require_lang_item(LangItem::ConstPanicFmt, None); let new_instance = ty::Instance::expect_resolve( @@ -256,7 +256,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { ); return Ok(Some(new_instance)); - } else if Some(def_id) == self.tcx.lang_items().align_offset_fn() { + } else if self.tcx.is_lang_item(def_id, LangItem::AlignOffset) { let args = self.copy_fn_args(args); // For align_offset, we replace the function call if the pointer has no address. match self.align_offset(instance, &args, dest, ret)? { @@ -370,53 +370,51 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { } } -impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, 'tcx> { - compile_time_machine!(<'mir, 'tcx>); +impl<'tcx> CompileTimeMachine<'tcx> { + #[inline(always)] + /// Find the first stack frame that is within the current crate, if any. + /// Otherwise, return the crate's HirId + pub fn best_lint_scope(&self, tcx: TyCtxt<'tcx>) -> hir::HirId { + self.stack.iter().find_map(|frame| frame.lint_root(tcx)).unwrap_or(CRATE_HIR_ID) + } +} + +impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { + compile_time_machine!(<'tcx>); type MemoryKind = MemoryKind; const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error #[inline(always)] - fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { + fn enforce_alignment(ecx: &InterpCx<'tcx, Self>) -> bool { matches!(ecx.machine.check_alignment, CheckAlignment::Error) } #[inline(always)] - fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool { + fn enforce_validity(ecx: &InterpCx<'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool { ecx.tcx.sess.opts.unstable_opts.extra_const_ub_checks || layout.abi.is_uninhabited() } fn load_mir( - ecx: &InterpCx<'mir, 'tcx, Self>, - instance: ty::InstanceDef<'tcx>, + ecx: &InterpCx<'tcx, Self>, + instance: ty::InstanceKind<'tcx>, ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> { match instance { - ty::InstanceDef::Item(def) => { - if ecx.tcx.is_ctfe_mir_available(def) { - Ok(ecx.tcx.mir_for_ctfe(def)) - } else if ecx.tcx.def_kind(def) == DefKind::AssocConst { - ecx.tcx.dcx().bug("This is likely a const item that is missing from its impl"); - } else { - // `find_mir_or_eval_fn` checks that this is a const fn before even calling us, - // so this should be unreachable. - let path = ecx.tcx.def_path_str(def); - bug!("trying to call extern function `{path}` at compile-time"); - } - } + ty::InstanceKind::Item(def) => Ok(ecx.tcx.mir_for_ctfe(def)), _ => Ok(ecx.tcx.instance_mir(instance)), } } fn find_mir_or_eval_fn( - ecx: &mut InterpCx<'mir, 'tcx, Self>, + ecx: &mut InterpCx<'tcx, Self>, orig_instance: ty::Instance<'tcx>, _abi: CallAbi, args: &[FnArg<'tcx>], dest: &MPlaceTy<'tcx>, ret: Option, _unwind: mir::UnwindAction, // unwinding is not supported in consts - ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> { + ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> { debug!("find_mir_or_eval_fn: {:?}", orig_instance); // Replace some functions. @@ -426,7 +424,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, }; // Only check non-glue functions - if let ty::InstanceDef::Item(def) = instance.def { + if let ty::InstanceKind::Item(def) = instance.def { // Execution might have wandered off into other crates, so we cannot do a stability- // sensitive check here. But we can at least rule out functions that are not const at // all. That said, we have to allow calling functions inside a trait marked with @@ -447,7 +445,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, Ok(Some((ecx.load_mir(instance.def, None)?, orig_instance))) } - fn panic_nounwind(ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: &str) -> InterpResult<'tcx> { + fn panic_nounwind(ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx> { let msg = Symbol::intern(msg); let span = ecx.find_closest_untracked_caller_location(); let (file, line, col) = ecx.location_triple_for_span(span); @@ -455,7 +453,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } fn call_intrinsic( - ecx: &mut InterpCx<'mir, 'tcx, Self>, + ecx: &mut InterpCx<'tcx, Self>, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx, Self::Provenance>, @@ -542,7 +540,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, ); } return Ok(Some(ty::Instance { - def: ty::InstanceDef::Item(instance.def_id()), + def: ty::InstanceKind::Item(instance.def_id()), args: instance.args, })); } @@ -554,7 +552,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } fn assert_panic( - ecx: &mut InterpCx<'mir, 'tcx, Self>, + ecx: &mut InterpCx<'tcx, Self>, msg: &AssertMessage<'tcx>, _unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { @@ -585,15 +583,15 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } fn binary_ptr_op( - _ecx: &InterpCx<'mir, 'tcx, Self>, + _ecx: &InterpCx<'tcx, Self>, _bin_op: mir::BinOp, _left: &ImmTy<'tcx>, _right: &ImmTy<'tcx>, - ) -> InterpResult<'tcx, (ImmTy<'tcx>, bool)> { + ) -> InterpResult<'tcx, ImmTy<'tcx>> { throw_unsup_format!("pointer arithmetic or comparison is not supported at compile-time"); } - fn increment_const_eval_counter(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + fn increment_const_eval_counter(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { // The step limit has already been hit in a previous call to `increment_const_eval_counter`. if let Some(new_steps) = ecx.machine.num_evaluated_steps.checked_add(1) { @@ -612,7 +610,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, // By default, we stop after a million steps, but the user can disable this lint // to be able to run until the heat death of the universe or power loss, whichever // comes first. - let hir_id = ecx.best_lint_scope(); + let hir_id = ecx.machine.best_lint_scope(*ecx.tcx); let is_error = ecx .tcx .lint_level_at_node( @@ -649,16 +647,16 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } #[inline(always)] - fn expose_ptr(_ecx: &mut InterpCx<'mir, 'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx> { + fn expose_ptr(_ecx: &mut InterpCx<'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx> { // This is only reachable with -Zunleash-the-miri-inside-of-you. throw_unsup_format!("exposing pointers is not possible at compile-time") } #[inline(always)] - fn init_frame_extra( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - frame: Frame<'mir, 'tcx>, - ) -> InterpResult<'tcx, Frame<'mir, 'tcx>> { + fn init_frame( + ecx: &mut InterpCx<'tcx, Self>, + frame: Frame<'tcx>, + ) -> InterpResult<'tcx, Frame<'tcx>> { // Enforce stack size limit. Add 1 because this is run before the new frame is pushed. if !ecx.recursion_limit.value_within_limit(ecx.stack().len() + 1) { throw_exhaust!(StackFrameLimitReached) @@ -669,15 +667,15 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, #[inline(always)] fn stack<'a>( - ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { + ecx: &'a InterpCx<'tcx, Self>, + ) -> &'a [Frame<'tcx, Self::Provenance, Self::FrameExtra>] { &ecx.machine.stack } #[inline(always)] fn stack_mut<'a>( - ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec> { + ecx: &'a mut InterpCx<'tcx, Self>, + ) -> &'a mut Vec> { &mut ecx.machine.stack } @@ -714,7 +712,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } fn retag_ptr_value( - ecx: &mut InterpCx<'mir, 'tcx, Self>, + ecx: &mut InterpCx<'tcx, Self>, _kind: mir::RetagKind, val: &ImmTy<'tcx, CtfeProvenance>, ) -> InterpResult<'tcx, ImmTy<'tcx, CtfeProvenance>> { @@ -755,15 +753,22 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, Ok(()) } - fn before_alloc_read( - ecx: &InterpCx<'mir, 'tcx, Self>, - alloc_id: AllocId, - ) -> InterpResult<'tcx> { + fn before_alloc_read(ecx: &InterpCx<'tcx, Self>, alloc_id: AllocId) -> InterpResult<'tcx> { + // Check if this is the currently evaluated static. if Some(alloc_id) == ecx.machine.static_root_ids.map(|(id, _)| id) { - Err(ConstEvalErrKind::RecursiveStatic.into()) - } else { - Ok(()) + return Err(ConstEvalErrKind::RecursiveStatic.into()); } + // If this is another static, make sure we fire off the query to detect cycles. + // But only do that when checks for static recursion are enabled. + if ecx.machine.static_root_ids.is_some() { + if let Some(GlobalAlloc::Static(def_id)) = ecx.tcx.try_get_global_alloc(alloc_id) { + if ecx.tcx.is_foreign_item(def_id) { + throw_unsup!(ExternStatic(def_id)); + } + ecx.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?; + } + } + Ok(()) } } diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index a5c8c0bb82ad..4ae4816e33ab 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -6,6 +6,7 @@ use rustc_middle::mir::interpret::InterpErrorInfo; use rustc_middle::query::{Key, TyCtxtAt}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_target::abi::VariantIdx; +use tracing::instrument; use crate::interpret::{format_interp_error, InterpCx}; diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index fbf2ca5ab0a6..2e8ad445cf5e 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -6,9 +6,10 @@ use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; use rustc_span::DUMMY_SP; use rustc_target::abi::{Abi, VariantIdx}; +use tracing::{debug, instrument, trace}; use super::eval_queries::{mk_eval_cx_to_read_const_val, op_to_const}; -use super::machine::CompileTimeEvalContext; +use super::machine::CompileTimeInterpCx; use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES}; use crate::const_eval::CanAccessMutGlobal; use crate::errors::MaxNumNodesInConstErr; @@ -20,7 +21,7 @@ use crate::interpret::{ #[instrument(skip(ecx), level = "debug")] fn branches<'tcx>( - ecx: &CompileTimeEvalContext<'tcx, 'tcx>, + ecx: &CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>, n: usize, variant: Option, @@ -58,7 +59,7 @@ fn branches<'tcx>( #[instrument(skip(ecx), level = "debug")] fn slice_branches<'tcx>( - ecx: &CompileTimeEvalContext<'tcx, 'tcx>, + ecx: &CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>, num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { @@ -76,7 +77,7 @@ fn slice_branches<'tcx>( #[instrument(skip(ecx), level = "debug")] fn const_to_valtree_inner<'tcx>( - ecx: &CompileTimeEvalContext<'tcx, 'tcx>, + ecx: &CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>, num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { @@ -94,10 +95,10 @@ fn const_to_valtree_inner<'tcx>( } ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { let val = ecx.read_immediate(place)?; - let val = val.to_scalar(); + let val = val.to_scalar_int().unwrap(); *num_nodes += 1; - Ok(ty::ValTree::Leaf(val.assert_int())) + Ok(ty::ValTree::Leaf(val)) } ty::Pat(base, ..) => { @@ -124,7 +125,7 @@ fn const_to_valtree_inner<'tcx>( let val = val.to_scalar(); // We are in the CTFE machine, so ptr-to-int casts will fail. // This can only be `Ok` if `val` already is an integer. - let Ok(val) = val.try_to_int() else { + let Ok(val) = val.try_to_scalar_int() else { return Err(ValTreeCreationError::NonSupportedType); }; // It's just a ScalarInt! @@ -218,7 +219,7 @@ fn reconstruct_place_meta<'tcx>( #[instrument(skip(ecx), level = "debug", ret)] fn create_valtree_place<'tcx>( - ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, + ecx: &mut CompileTimeInterpCx<'tcx>, layout: TyAndLayout<'tcx>, valtree: ty::ValTree<'tcx>, ) -> MPlaceTy<'tcx> { @@ -363,7 +364,7 @@ pub fn valtree_to_const_value<'tcx>( /// Put a valtree into memory and return a reference to that. fn valtree_to_ref<'tcx>( - ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, + ecx: &mut CompileTimeInterpCx<'tcx>, valtree: ty::ValTree<'tcx>, pointee_ty: Ty<'tcx>, ) -> Immediate { @@ -379,7 +380,7 @@ fn valtree_to_ref<'tcx>( #[instrument(skip(ecx), level = "debug")] fn valtree_into_mplace<'tcx>( - ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, + ecx: &mut CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>, valtree: ty::ValTree<'tcx>, ) { @@ -410,7 +411,7 @@ fn valtree_into_mplace<'tcx>( ty::Adt(def, _) if def.is_enum() => { // First element of valtree corresponds to variant let scalar_int = branches[0].unwrap_leaf(); - let variant_idx = VariantIdx::from_u32(scalar_int.try_to_u32().unwrap()); + let variant_idx = VariantIdx::from_u32(scalar_int.to_u32()); let variant = def.variant(variant_idx); debug!(?variant); @@ -456,6 +457,6 @@ fn valtree_into_mplace<'tcx>( } } -fn dump_place<'tcx>(ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: &MPlaceTy<'tcx>) { +fn dump_place<'tcx>(ecx: &CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>) { trace!("{:?}", ecx.dump_place(&PlaceTy::from(place.clone()))); } diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 90d4f1168e4f..5fa48a59794b 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -1,7 +1,8 @@ use std::borrow::Cow; +use either::Either; use rustc_errors::{ - codes::*, Diag, DiagArgValue, DiagCtxt, DiagMessage, Diagnostic, EmissionGuarantee, Level, + codes::*, Diag, DiagArgValue, DiagCtxtHandle, DiagMessage, Diagnostic, EmissionGuarantee, Level, }; use rustc_hir::ConstContext; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; @@ -452,7 +453,7 @@ pub trait ReportErrorExt { } } -fn bad_pointer_message(msg: CheckInAllocMsg, dcx: &DiagCtxt) -> String { +fn bad_pointer_message(msg: CheckInAllocMsg, dcx: DiagCtxtHandle<'_>) -> String { use crate::fluent_generated::*; let msg = match msg { @@ -481,6 +482,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { DivisionOverflow => const_eval_division_overflow, RemainderOverflow => const_eval_remainder_overflow, PointerArithOverflow => const_eval_pointer_arithmetic_overflow, + ArithOverflow { .. } => const_eval_overflow_arith, + ShiftOverflow { .. } => const_eval_overflow_shift, InvalidMeta(InvalidMetaKind::SliceTooBig) => const_eval_invalid_meta_slice, InvalidMeta(InvalidMetaKind::TooBig) => const_eval_invalid_meta, UnterminatedCString(_) => const_eval_unterminated_c_string, @@ -539,6 +542,19 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { | UninhabitedEnumVariantWritten(_) | UninhabitedEnumVariantRead(_) => {} + ArithOverflow { intrinsic } => { + diag.arg("intrinsic", intrinsic); + } + ShiftOverflow { intrinsic, shift_amount } => { + diag.arg("intrinsic", intrinsic); + diag.arg( + "shift_amount", + match shift_amount { + Either::Left(v) => v.to_string(), + Either::Right(v) => v.to_string(), + }, + ); + } BoundsCheckFailed { len, index } => { diag.arg("len", len); diag.arg("index", index); @@ -624,9 +640,6 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { const_eval_validation_ref_to_uninhabited } - PtrToStatic { ptr_kind: PointerKind::Box } => const_eval_validation_box_to_static, - PtrToStatic { ptr_kind: PointerKind::Ref(_) } => const_eval_validation_ref_to_static, - PointerAsInt { .. } => const_eval_validation_pointer_as_int, PartialPointer => const_eval_validation_partial_pointer, ConstRefToMutable => const_eval_validation_const_ref_to_mutable, @@ -791,7 +804,6 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { ); } NullPtr { .. } - | PtrToStatic { .. } | ConstRefToMutable | ConstRefToExtern | MutableRefToImmutable diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 799e12f9ac97..a13630ce084d 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -10,6 +10,7 @@ use rustc_middle::ty::{self, FloatTy, Ty}; use rustc_middle::{bug, span_bug}; use rustc_target::abi::Integer; use rustc_type_ir::TyKind::*; +use tracing::trace; use super::{ err_inval, throw_ub, throw_ub_custom, util::ensure_monomorphic_enough, FnVal, ImmTy, Immediate, @@ -18,7 +19,7 @@ use super::{ use crate::fluent_generated as fluent; -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn cast( &mut self, src: &OpTy<'tcx, M::Provenance>, @@ -69,9 +70,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { CastKind::PointerCoercion( PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer, ) => { - // These are NOPs, but can be wide pointers. - let v = self.read_immediate(src)?; - self.write_immediate(*v, dest)?; + bug!("{cast_kind:?} casts are for borrowck only, not runtime MIR"); } CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => { @@ -206,7 +205,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert!(cast_to.ty.is_unsafe_ptr()); // Handle casting any ptr to raw ptr (might be a fat ptr). if cast_to.size == src.layout.size { - // Thin or fat pointer that just hast the ptr kind of target type changed. + // Thin or fat pointer that just has the ptr kind of target type changed. return Ok(ImmTy::from_immediate(**src, cast_to)); } else { // Casting the metadata away from a fat ptr. @@ -273,9 +272,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Let's make sure v is sign-extended *if* it has a signed type. let signed = src_layout.abi.is_signed(); // Also asserts that abi is `Scalar`. - let v = scalar.to_bits(src_layout.size)?; - let v = if signed { self.sign_extend(v, src_layout) } else { v }; - trace!("cast_from_scalar: {}, {} -> {}", v, src_layout.ty, cast_ty); + let v = match src_layout.ty.kind() { + Uint(_) | RawPtr(..) | FnPtr(..) => scalar.to_uint(src_layout.size)?, + Int(_) => scalar.to_int(src_layout.size)? as u128, // we will cast back to `i128` below if the sign matters + Bool => scalar.to_bool()?.into(), + Char => scalar.to_char()?.into(), + _ => span_bug!(self.cur_span(), "invalid int-like cast from {}", src_layout.ty), + }; Ok(match *cast_ty.kind() { // int -> int @@ -323,13 +326,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { use rustc_type_ir::TyKind::*; fn adjust_nan< - 'mir, - 'tcx: 'mir, - M: Machine<'mir, 'tcx>, + 'tcx, + M: Machine<'tcx>, F1: rustc_apfloat::Float + FloatConvert, F2: rustc_apfloat::Float, >( - ecx: &InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'tcx, M>, f1: F1, f2: F2, ) -> F2 { @@ -383,7 +385,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) { (&ty::Array(_, length), &ty::Slice(_)) => { let ptr = self.read_pointer(src)?; - // u64 cast is from usize to u64, which is always good let val = Immediate::new_slice( ptr, length.eval_target_usize(*self.tcx, self.param_env), @@ -401,13 +402,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (old_data, old_vptr) = val.to_scalar_pair(); let old_data = old_data.to_pointer(self)?; let old_vptr = old_vptr.to_pointer(self)?; - let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?; - if old_trait != data_a.principal() { - throw_ub!(InvalidVTableTrait { - expected_trait: data_a, - vtable_trait: old_trait, - }); - } + let ty = self.get_ptr_vtable_ty(old_vptr, Some(data_a))?; let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?; self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) } diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index 8ddc741de239..b3a139d553ad 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -3,15 +3,16 @@ use rustc_middle::mir; use rustc_middle::span_bug; use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt}; -use rustc_middle::ty::{self, ScalarInt, Ty}; +use rustc_middle::ty::{self, CoroutineArgsExt, ScalarInt, Ty}; use rustc_target::abi::{self, TagEncoding}; use rustc_target::abi::{VariantIdx, Variants}; +use tracing::{instrument, trace}; use super::{ err_ub, throw_ub, ImmTy, InterpCx, InterpResult, Machine, Readable, Scalar, Writeable, }; -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Writes the discriminant of the given variant. /// /// If the variant is uninhabited, this is UB. @@ -122,14 +123,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // (`tag_bits` itself is only used for error messages below.) let tag_bits = tag_val .to_scalar() - .try_to_int() + .try_to_scalar_int() .map_err(|dbg_val| err_ub!(InvalidTag(dbg_val)))? - .assert_bits(tag_layout.size); + .to_bits(tag_layout.size); // Cast bits from tag layout to discriminant layout. // After the checks we did above, this cannot fail, as // discriminants are int-like. let discr_val = self.int_to_int_or_float(&tag_val, discr_layout).unwrap(); - let discr_bits = discr_val.to_scalar().assert_bits(discr_layout.size); + let discr_bits = discr_val.to_scalar().to_bits(discr_layout.size)?; // Convert discriminant to variant index, and catch invalid discriminants. let index = match *ty.kind() { ty::Adt(adt, _) => { @@ -151,7 +152,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // discriminant (encoded in niche/tag) and variant index are the same. let variants_start = niche_variants.start().as_u32(); let variants_end = niche_variants.end().as_u32(); - let variant = match tag_val.try_to_int() { + let variant = match tag_val.try_to_scalar_int() { Err(dbg_val) => { // So this is a pointer then, and casting to an int failed. // Can only happen during CTFE. @@ -166,15 +167,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { untagged_variant } Ok(tag_bits) => { - let tag_bits = tag_bits.assert_bits(tag_layout.size); + let tag_bits = tag_bits.to_bits(tag_layout.size); // We need to use machine arithmetic to get the relative variant idx: // variant_index_relative = tag_val - niche_start_val let tag_val = ImmTy::from_uint(tag_bits, tag_layout); let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); let variant_index_relative_val = - self.wrapping_binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?; + self.binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?; let variant_index_relative = - variant_index_relative_val.to_scalar().assert_bits(tag_val.layout.size); + variant_index_relative_val.to_scalar().to_bits(tag_val.layout.size)?; // Check if this is in the range that indicates an actual discriminant. if variant_index_relative <= u128::from(variants_end - variants_start) { let variant_index_relative = u32::try_from(variant_index_relative) @@ -240,8 +241,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { variant_index: VariantIdx, ) -> InterpResult<'tcx, Option<(ScalarInt, usize)>> { match self.layout_of(ty)?.variants { - abi::Variants::Single { index } => { - assert_eq!(index, variant_index); + abi::Variants::Single { .. } => { + // The tag of a `Single` enum is like the tag of the niched + // variant: there's no tag as the discriminant is encoded + // entirely implicitly. If `write_discriminant` ever hits this + // case, we do a "validation read" to ensure the the right + // discriminant is encoded implicitly, so any attempt to write + // the wrong discriminant for a `Single` enum will reliably + // result in UB. Ok(None) } @@ -292,13 +299,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let variant_index_relative_val = ImmTy::from_uint(variant_index_relative, tag_layout); let tag = self - .wrapping_binary_op( - mir::BinOp::Add, - &variant_index_relative_val, - &niche_start_val, - )? - .to_scalar() - .assert_int(); + .binary_op(mir::BinOp::Add, &variant_index_relative_val, &niche_start_val)? + .to_scalar_int()?; Ok(Some((tag, tag_field))) } } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 344bb7cd98be..67eeb1b3b871 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -2,9 +2,9 @@ use std::cell::Cell; use std::{fmt, mem}; use either::{Either, Left, Right}; +use tracing::{debug, info, info_span, instrument, trace}; -use hir::CRATE_HIR_ID; -use rustc_errors::DiagCtxt; +use rustc_errors::DiagCtxtHandle; use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData}; use rustc_index::IndexVec; use rustc_middle::mir; @@ -33,7 +33,7 @@ use crate::errors; use crate::util; use crate::{fluent_generated as fluent, ReportErrorExt}; -pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> { +pub struct InterpCx<'tcx, M: Machine<'tcx>> { /// Stores the `Machine` instance. /// /// Note: the stack is provided by the machine. @@ -48,7 +48,7 @@ pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> { pub(crate) param_env: ty::ParamEnv<'tcx>, /// The virtual memory system. - pub memory: Memory<'mir, 'tcx, M>, + pub memory: Memory<'tcx, M>, /// The recursion limit (cached from `tcx.recursion_limit(())`) pub recursion_limit: Limit, @@ -89,12 +89,12 @@ impl Drop for SpanGuard { } /// A stack frame. -pub struct Frame<'mir, 'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { +pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { //////////////////////////////////////////////////////////////////////////////// // Function and callsite information //////////////////////////////////////////////////////////////////////////////// /// The MIR for the function called on this frame. - pub body: &'mir mir::Body<'tcx>, + pub body: &'tcx mir::Body<'tcx>, /// The def_id and args of the current function. pub instance: ty::Instance<'tcx>, @@ -231,8 +231,8 @@ impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> { } } -impl<'mir, 'tcx, Prov: Provenance> Frame<'mir, 'tcx, Prov> { - pub fn with_extra(self, extra: Extra) -> Frame<'mir, 'tcx, Prov, Extra> { +impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> { + pub fn with_extra(self, extra: Extra) -> Frame<'tcx, Prov, Extra> { Frame { body: self.body, instance: self.instance, @@ -246,10 +246,10 @@ impl<'mir, 'tcx, Prov: Provenance> Frame<'mir, 'tcx, Prov> { } } -impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> { +impl<'tcx, Prov: Provenance, Extra> Frame<'tcx, Prov, Extra> { /// Get the current location within the Frame. /// - /// If this is `Left`, we are not currently executing any particular statement in + /// If this is `Right`, we are not currently executing any particular statement in /// this frame (can happen e.g. during frame initialization, and during unwinding on /// frames without cleanup code). /// @@ -270,13 +270,18 @@ impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> { } } - pub fn lint_root(&self) -> Option { - self.current_source_info().and_then(|source_info| { - match &self.body.source_scopes[source_info.scope].local_data { + pub fn lint_root(&self, tcx: TyCtxt<'tcx>) -> Option { + // We first try to get a HirId via the current source scope, + // and fall back to `body.source`. + self.current_source_info() + .and_then(|source_info| match &self.body.source_scopes[source_info.scope].local_data { mir::ClearCrossCrate::Set(data) => Some(data.lint_root), mir::ClearCrossCrate::Clear => None, - } - }) + }) + .or_else(|| { + let def_id = self.body.source.def_id().as_local(); + def_id.map(|def_id| tcx.local_def_id_to_hir_id(def_id)) + }) } /// Returns the address of the buffer where the locals are stored. This is used by `Place` as a @@ -344,16 +349,16 @@ impl<'tcx> FrameInfo<'tcx> { } } -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> HasDataLayout for InterpCx<'tcx, M> { #[inline] fn data_layout(&self) -> &TargetDataLayout { &self.tcx.data_layout } } -impl<'mir, 'tcx, M> layout::HasTyCtxt<'tcx> for InterpCx<'mir, 'tcx, M> +impl<'tcx, M> layout::HasTyCtxt<'tcx> for InterpCx<'tcx, M> where - M: Machine<'mir, 'tcx>, + M: Machine<'tcx>, { #[inline] fn tcx(&self) -> TyCtxt<'tcx> { @@ -361,16 +366,16 @@ where } } -impl<'mir, 'tcx, M> layout::HasParamEnv<'tcx> for InterpCx<'mir, 'tcx, M> +impl<'tcx, M> layout::HasParamEnv<'tcx> for InterpCx<'tcx, M> where - M: Machine<'mir, 'tcx>, + M: Machine<'tcx>, { fn param_env(&self) -> ty::ParamEnv<'tcx> { self.param_env } } -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { type LayoutOfResult = InterpResult<'tcx, TyAndLayout<'tcx>>; #[inline] @@ -390,7 +395,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> LayoutOfHelpers<'tcx> for InterpC } } -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> { type FnAbiOfResult = InterpResult<'tcx, &'tcx FnAbi<'tcx, Ty<'tcx>>>; fn handle_fn_abi_err( @@ -469,7 +474,7 @@ pub(super) fn from_known_layout<'tcx>( /// /// This is NOT the preferred way to render an error; use `report` from `const_eval` instead. /// However, this is useful when error messages appear in ICEs. -pub fn format_interp_error<'tcx>(dcx: &DiagCtxt, e: InterpErrorInfo<'tcx>) -> String { +pub fn format_interp_error<'tcx>(dcx: DiagCtxtHandle<'_>, e: InterpErrorInfo<'tcx>) -> String { let (e, backtrace) = e.into_parts(); backtrace.print_backtrace(); // FIXME(fee1-dead), HACK: we want to use the error as title therefore we can just extract the @@ -483,7 +488,7 @@ pub fn format_interp_error<'tcx>(dcx: &DiagCtxt, e: InterpErrorInfo<'tcx>) -> St s } -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn new( tcx: TyCtxt<'tcx>, root_span: Span, @@ -499,6 +504,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } + /// Returns the span of the currently executed statement/terminator. + /// This is the span typically used for error reporting. #[inline(always)] pub fn cur_span(&self) -> Span { // This deliberately does *not* honor `requires_caller_location` since it is used for much @@ -506,24 +513,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.stack().last().map_or(self.tcx.span, |f| f.current_span()) } - #[inline(always)] - /// Find the first stack frame that is within the current crate, if any, otherwise return the crate's HirId - pub fn best_lint_scope(&self) -> hir::HirId { - self.stack() - .iter() - .find_map(|frame| frame.body.source.def_id().as_local()) - .map_or(CRATE_HIR_ID, |def_id| self.tcx.local_def_id_to_hir_id(def_id)) - } - - #[inline(always)] - pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>] { + pub(crate) fn stack(&self) -> &[Frame<'tcx, M::Provenance, M::FrameExtra>] { M::stack(self) } #[inline(always)] - pub(crate) fn stack_mut( - &mut self, - ) -> &mut Vec> { + pub(crate) fn stack_mut(&mut self) -> &mut Vec> { M::stack_mut(self) } @@ -535,17 +530,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } #[inline(always)] - pub fn frame(&self) -> &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra> { + pub fn frame(&self) -> &Frame<'tcx, M::Provenance, M::FrameExtra> { self.stack().last().expect("no call frames exist") } #[inline(always)] - pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx, M::Provenance, M::FrameExtra> { + pub fn frame_mut(&mut self) -> &mut Frame<'tcx, M::Provenance, M::FrameExtra> { self.stack_mut().last_mut().expect("no call frames exist") } #[inline(always)] - pub fn body(&self) -> &'mir mir::Body<'tcx> { + pub fn body(&self) -> &'tcx mir::Body<'tcx> { self.frame().body } @@ -567,7 +562,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn load_mir( &self, - instance: ty::InstanceDef<'tcx>, + instance: ty::InstanceKind<'tcx>, promoted: Option, ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> { trace!("load mir(instance={:?}, promoted={:?})", instance, promoted); @@ -601,7 +596,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { T: TypeFoldable>, >( &self, - frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>, + frame: &Frame<'tcx, M::Provenance, M::FrameExtra>, value: T, ) -> Result { frame @@ -633,7 +628,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a - /// frame which is not `#[track_caller]`. This is the fancy version of `cur_span`. + /// frame which is not `#[track_caller]`. This matches the `caller_location` intrinsic, + /// and is primarily intended for the panic machinery. pub(crate) fn find_closest_untracked_caller_location(&self) -> Span { for frame in self.stack().iter().rev() { debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance); @@ -679,7 +675,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub(super) fn layout_of_local( &self, - frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>, + frame: &Frame<'tcx, M::Provenance, M::FrameExtra>, local: mir::Local, layout: Option>, ) -> InterpResult<'tcx, TyAndLayout<'tcx>> { @@ -766,10 +762,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } Ok(Some((full_size, full_align))) } - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(expected_trait, _, ty::Dyn) => { let vtable = metadata.unwrap_meta().to_pointer(self)?; // Read size and align from vtable (already checks size). - Ok(Some(self.get_vtable_size_and_align(vtable)?)) + Ok(Some(self.get_vtable_size_and_align(vtable, Some(expected_trait))?)) } ty::Slice(_) | ty::Str => { @@ -802,7 +798,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn push_stack_frame( &mut self, instance: ty::Instance<'tcx>, - body: &'mir mir::Body<'tcx>, + body: &'tcx mir::Body<'tcx>, return_place: &MPlaceTy<'tcx, M::Provenance>, return_to_block: StackPopCleanup, ) -> InterpResult<'tcx> { @@ -820,7 +816,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { tracing_span: SpanGuard::new(), extra: (), }; - let frame = M::init_frame_extra(self, pre_frame)?; + let frame = M::init_frame(self, pre_frame)?; self.stack_mut().push(frame); // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check). @@ -1058,7 +1054,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false, - ty::Tuple(tys) => tys.last().iter().all(|ty| is_very_trivially_sized(**ty)), + ty::Tuple(tys) => tys.last().is_none_or(|ty| is_very_trivially_sized(*ty)), ty::Pat(ty, ..) => is_very_trivially_sized(*ty), @@ -1104,11 +1100,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Operand::Immediate(Immediate::Uninit) }); - // StorageLive expects the local to be dead, and marks it live. + // If the local is already live, deallocate its old memory. let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val); - if !matches!(old, LocalValue::Dead) { - throw_ub_custom!(fluent::const_eval_double_storage_live); - } + self.deallocate_local(old)?; Ok(()) } @@ -1122,7 +1116,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert!(local != mir::RETURN_PLACE, "Cannot make return place dead"); trace!("{:?} is now dead", local); - // It is entirely okay for this local to be already dead (at least that's how we currently generate MIR) + // If the local is already dead, this is a NOP. let old = mem::replace(&mut self.frame_mut().locals[local].value, LocalValue::Dead); self.deallocate_local(old)?; Ok(()) @@ -1181,9 +1175,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { M::eval_mir_constant(self, *val, span, layout, |ecx, val, span, layout| { let const_val = val.eval(*ecx.tcx, ecx.param_env, span).map_err(|err| { - if M::ALL_CONSTS_ARE_PRECHECKED && !matches!(err, ErrorHandled::TooGeneric(..)) { - // Looks like the const is not captued by `required_consts`, that's bad. - bug!("interpret const eval failure of {val:?} which is not in required_consts"); + if M::ALL_CONSTS_ARE_PRECHECKED { + match err { + ErrorHandled::TooGeneric(..) => {}, + ErrorHandled::Reported(reported, span) => { + if reported.is_tainted_by_errors() { + // const-eval will return "tainted" errors if e.g. the layout cannot + // be computed as the type references non-existing names. + // See . + } else { + // Looks like the const is not captued by `required_consts`, that's bad. + span_bug!(span, "interpret const eval failure of {val:?} which is not in required_consts"); + } + } + } } err.emit_note(*ecx.tcx); err @@ -1193,10 +1198,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } #[must_use] - pub fn dump_place( - &self, - place: &PlaceTy<'tcx, M::Provenance>, - ) -> PlacePrinter<'_, 'mir, 'tcx, M> { + pub fn dump_place(&self, place: &PlaceTy<'tcx, M::Provenance>) -> PlacePrinter<'_, 'tcx, M> { PlacePrinter { ecx: self, place: *place.place() } } @@ -1208,14 +1210,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[doc(hidden)] /// Helper struct for the `dump_place` function. -pub struct PlacePrinter<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { - ecx: &'a InterpCx<'mir, 'tcx, M>, +pub struct PlacePrinter<'a, 'tcx, M: Machine<'tcx>> { + ecx: &'a InterpCx<'tcx, M>, place: Place, } -impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug - for PlacePrinter<'a, 'mir, 'tcx, M> -{ +impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for PlacePrinter<'a, 'tcx, M> { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.place { Place::Local { local, offset, locals_addr } => { diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 3565b4fb5165..8b0a2afa4d66 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -23,13 +23,13 @@ use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::TyAndLayout; use rustc_span::def_id::LocalDefId; use rustc_span::sym; +use tracing::{instrument, trace}; use super::{err_ub, AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy}; use crate::const_eval; use crate::errors::NestedStaticInThreadLocal; -pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine< - 'mir, +pub trait CompileTimeMachine<'tcx, T> = Machine< 'tcx, MemoryKind = T, Provenance = CtfeProvenance, @@ -45,7 +45,7 @@ pub trait HasStaticRootDefId { fn static_def_id(&self) -> Option; } -impl HasStaticRootDefId for const_eval::CompileTimeInterpreter<'_, '_> { +impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> { fn static_def_id(&self) -> Option { Some(self.static_root_ids?.1) } @@ -58,8 +58,8 @@ impl HasStaticRootDefId for const_eval::CompileTimeInterpreter<'_, '_> { /// already mutable (as a sanity check). /// /// Returns an iterator over all relocations referred to by this allocation. -fn intern_shallow<'rt, 'mir, 'tcx, T, M: CompileTimeMachine<'mir, 'tcx, T>>( - ecx: &'rt mut InterpCx<'mir, 'tcx, M>, +fn intern_shallow<'rt, 'tcx, T, M: CompileTimeMachine<'tcx, T>>( + ecx: &'rt mut InterpCx<'tcx, M>, alloc_id: AllocId, mutability: Mutability, ) -> Result + 'tcx, ()> { @@ -102,7 +102,7 @@ fn intern_as_new_static<'tcx>( let feed = tcx.create_def( static_id, sym::nested, - DefKind::Static { mutability: alloc.0.mutability, nested: true }, + DefKind::Static { safety: hir::Safety::Safe, mutability: alloc.0.mutability, nested: true }, ); tcx.set_nested_alloc_id_static(alloc_id, feed.def_id()); @@ -145,12 +145,8 @@ pub enum InternResult { /// /// For `InternKind::Static` the root allocation will not be interned, but must be handled by the caller. #[instrument(level = "debug", skip(ecx))] -pub fn intern_const_alloc_recursive< - 'mir, - 'tcx: 'mir, - M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>, ->( - ecx: &mut InterpCx<'mir, 'tcx, M>, +pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx, const_eval::MemoryKind>>( + ecx: &mut InterpCx<'tcx, M>, intern_kind: InternKind, ret: &MPlaceTy<'tcx>, ) -> Result<(), InternResult> { @@ -289,13 +285,8 @@ pub fn intern_const_alloc_recursive< /// Intern `ret`. This function assumes that `ret` references no other allocation. #[instrument(level = "debug", skip(ecx))] -pub fn intern_const_alloc_for_constprop< - 'mir, - 'tcx: 'mir, - T, - M: CompileTimeMachine<'mir, 'tcx, T>, ->( - ecx: &mut InterpCx<'mir, 'tcx, M>, +pub fn intern_const_alloc_for_constprop<'tcx, T, M: CompileTimeMachine<'tcx, T>>( + ecx: &mut InterpCx<'tcx, M>, alloc_id: AllocId, ) -> InterpResult<'tcx, ()> { if ecx.tcx.try_get_global_alloc(alloc_id).is_some() { @@ -314,19 +305,14 @@ pub fn intern_const_alloc_for_constprop< Ok(()) } -impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>> - InterpCx<'mir, 'tcx, M> -{ +impl<'tcx, M: super::intern::CompileTimeMachine<'tcx, !>> InterpCx<'tcx, M> { /// A helper function that allocates memory for the layout given and gives you access to mutate /// it. Once your own mutation code is done, the backing `Allocation` is removed from the /// current `Memory` and interned as read-only into the global memory. pub fn intern_with_temp_alloc( &mut self, layout: TyAndLayout<'tcx>, - f: impl FnOnce( - &mut InterpCx<'mir, 'tcx, M>, - &PlaceTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, ()>, + f: impl FnOnce(&mut InterpCx<'tcx, M>, &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, ()>, ) -> InterpResult<'tcx, AllocId> { // `allocate` picks a fresh AllocId that we will associate with its data below. let dest = self.allocate(layout, MemoryKind::Stack)?; diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index dce4d56f7e00..1d54da267eeb 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -14,6 +14,7 @@ use rustc_middle::{ }; use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::Size; +use tracing::trace; use super::{ err_inval, err_ub_custom, err_unsup_format, memory::MemoryKind, throw_inval, throw_ub_custom, @@ -97,7 +98,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>( }) } -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Returns `true` if emulation happened. /// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own /// intrinsic handling. @@ -196,7 +197,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW)) let layout_val = self.layout_of(instance_args.type_at(0))?; let val = self.read_scalar(&args[0])?; - let val_bits = val.to_bits(layout_val.size)?; + let val_bits = val.to_bits(layout_val.size)?; // sign is ignored here let layout_raw_shift = self.layout_of(self.tcx.types.u32)?; let raw_shift = self.read_scalar(&args[1])?; @@ -255,6 +256,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { name = intrinsic_name, ); } + // This will always return 0. (a, b) } (Err(_), _) | (_, Err(_)) => { @@ -285,9 +287,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (val, overflowed) = { let a_offset = ImmTy::from_uint(a_offset, usize_layout); let b_offset = ImmTy::from_uint(b_offset, usize_layout); - self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)? + self.binary_op(BinOp::SubWithOverflow, &a_offset, &b_offset)? + .to_scalar_pair() }; - if overflowed { + if overflowed.to_bool()? { // a < b if intrinsic_name == sym::ptr_offset_from_unsigned { throw_ub_custom!( @@ -299,7 +302,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // The signed form of the intrinsic allows this. If we interpret the // difference as isize, we'll get the proper signed difference. If that // seems *positive*, they were more than isize::MAX apart. - let dist = val.to_scalar().to_target_isize(self)?; + let dist = val.to_target_isize(self)?; if dist >= 0 { throw_ub_custom!( fluent::const_eval_offset_from_underflow, @@ -309,7 +312,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { dist } else { // b >= a - let dist = val.to_scalar().to_target_isize(self)?; + let dist = val.to_target_isize(self)?; // If converting to isize produced a *negative* result, we had an overflow // because they were more than isize::MAX apart. if dist < 0 { @@ -429,12 +432,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::vtable_size => { let ptr = self.read_pointer(&args[0])?; - let (size, _align) = self.get_vtable_size_and_align(ptr)?; + // `None` because we don't know which trait to expect here; any vtable is okay. + let (size, _align) = self.get_vtable_size_and_align(ptr, None)?; self.write_scalar(Scalar::from_target_usize(size.bytes(), self), dest)?; } sym::vtable_align => { let ptr = self.read_pointer(&args[0])?; - let (_size, align) = self.get_vtable_size_and_align(ptr)?; + // `None` because we don't know which trait to expect here; any vtable is okay. + let (_size, align) = self.get_vtable_size_and_align(ptr, None)?; self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?; } @@ -481,7 +486,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ret_layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, Scalar> { assert!(layout.ty.is_integral(), "invalid type for numeric intrinsic: {}", layout.ty); - let bits = val.to_bits(layout.size)?; + let bits = val.to_bits(layout.size)?; // these operations all ignore the sign let extra = 128 - u128::from(layout.size.bits()); let bits_out = match name { sym::ctpop => u128::from(bits.count_ones()), @@ -515,9 +520,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Performs an exact division, resulting in undefined behavior where // `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`. // First, check x % y != 0 (or if that computation overflows). - let (res, overflow) = self.overflowing_binary_op(BinOp::Rem, a, b)?; - assert!(!overflow); // All overflow is UB, so this should never return on overflow. - if res.to_scalar().assert_bits(a.layout.size) != 0 { + let rem = self.binary_op(BinOp::Rem, a, b)?; + // sign does not matter for 0 test, so `to_bits` is fine + if rem.to_scalar().to_bits(a.layout.size)? != 0 { throw_ub_custom!( fluent::const_eval_exact_div_has_remainder, a = format!("{a}"), @@ -525,7 +530,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) } // `Rem` says this is all right, so we can let `Div` do its job. - self.binop_ignore_overflow(BinOp::Div, a, b, &dest.clone().into()) + let res = self.binary_op(BinOp::Div, a, b)?; + self.write_immediate(*res, dest) } pub fn saturating_arith( @@ -538,25 +544,23 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert!(matches!(l.layout.ty.kind(), ty::Int(..) | ty::Uint(..))); assert!(matches!(mir_op, BinOp::Add | BinOp::Sub)); - let (val, overflowed) = self.overflowing_binary_op(mir_op, l, r)?; - Ok(if overflowed { + let (val, overflowed) = + self.binary_op(mir_op.wrapping_to_overflowing().unwrap(), l, r)?.to_scalar_pair(); + Ok(if overflowed.to_bool()? { let size = l.layout.size; - let num_bits = size.bits(); if l.layout.abi.is_signed() { // For signed ints the saturated value depends on the sign of the first // term since the sign of the second term can be inferred from this and // the fact that the operation has overflowed (if either is 0 no // overflow can occur) - let first_term: u128 = l.to_scalar().to_bits(l.layout.size)?; - let first_term_positive = first_term & (1 << (num_bits - 1)) == 0; - if first_term_positive { + let first_term: i128 = l.to_scalar().to_int(l.layout.size)?; + if first_term >= 0 { // Negative overflow not possible since the positive first term // can only increase an (in range) negative term for addition - // or corresponding negated positive term for subtraction + // or corresponding negated positive term for subtraction. Scalar::from_int(size.signed_int_max(), size) } else { - // Positive overflow not possible for similar reason - // max negative + // Positive overflow not possible for similar reason. Scalar::from_int(size.signed_int_min(), size) } } else { @@ -570,7 +574,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } } else { - val.to_scalar() + val }) } @@ -601,9 +605,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Copy `count*size_of::()` many bytes from `*src` to `*dst`. pub(crate) fn copy_intrinsic( &mut self, - src: &OpTy<'tcx, >::Provenance>, - dst: &OpTy<'tcx, >::Provenance>, - count: &OpTy<'tcx, >::Provenance>, + src: &OpTy<'tcx, >::Provenance>, + dst: &OpTy<'tcx, >::Provenance>, + count: &OpTy<'tcx, >::Provenance>, nonoverlapping: bool, ) -> InterpResult<'tcx> { let count = self.read_target_usize(count)?; @@ -630,8 +634,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Does a *typed* swap of `*left` and `*right`. fn typed_swap_intrinsic( &mut self, - left: &OpTy<'tcx, >::Provenance>, - right: &OpTy<'tcx, >::Provenance>, + left: &OpTy<'tcx, >::Provenance>, + right: &OpTy<'tcx, >::Provenance>, ) -> InterpResult<'tcx> { let left = self.deref_pointer(left)?; let right = self.deref_pointer(right)?; @@ -647,9 +651,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub(crate) fn write_bytes_intrinsic( &mut self, - dst: &OpTy<'tcx, >::Provenance>, - byte: &OpTy<'tcx, >::Provenance>, - count: &OpTy<'tcx, >::Provenance>, + dst: &OpTy<'tcx, >::Provenance>, + byte: &OpTy<'tcx, >::Provenance>, + count: &OpTy<'tcx, >::Provenance>, ) -> InterpResult<'tcx> { let layout = self.layout_of(dst.layout.ty.builtin_deref(true).unwrap())?; @@ -669,9 +673,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub(crate) fn compare_bytes_intrinsic( &mut self, - left: &OpTy<'tcx, >::Provenance>, - right: &OpTy<'tcx, >::Provenance>, - byte_count: &OpTy<'tcx, >::Provenance>, + left: &OpTy<'tcx, >::Provenance>, + right: &OpTy<'tcx, >::Provenance>, + byte_count: &OpTy<'tcx, >::Provenance>, ) -> InterpResult<'tcx, Scalar> { let left = self.read_pointer(left)?; let right = self.read_pointer(right)?; @@ -687,14 +691,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub(crate) fn raw_eq_intrinsic( &mut self, - lhs: &OpTy<'tcx, >::Provenance>, - rhs: &OpTy<'tcx, >::Provenance>, + lhs: &OpTy<'tcx, >::Provenance>, + rhs: &OpTy<'tcx, >::Provenance>, ) -> InterpResult<'tcx, Scalar> { let layout = self.layout_of(lhs.layout.ty.builtin_deref(true).unwrap())?; assert!(layout.is_sized()); - let get_bytes = |this: &InterpCx<'mir, 'tcx, M>, - op: &OpTy<'tcx, >::Provenance>, + let get_bytes = |this: &InterpCx<'tcx, M>, + op: &OpTy<'tcx, >::Provenance>, size| -> InterpResult<'tcx, &[u8]> { let ptr = this.read_pointer(op)?; diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 2eaebc1924bc..e91ab7c17916 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -94,7 +94,7 @@ pub trait AllocMap { /// Methods of this trait signifies a point where CTFE evaluation would fail /// and some use case dependent behaviour can instead be applied. -pub trait Machine<'mir, 'tcx: 'mir>: Sized { +pub trait Machine<'tcx>: Sized { /// Additional memory kinds a machine wishes to distinguish from the builtin ones type MemoryKind: Debug + std::fmt::Display + MayLeak + Eq + 'static; @@ -145,12 +145,12 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { const ALL_CONSTS_ARE_PRECHECKED: bool = true; /// Whether memory accesses should be alignment-checked. - fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; + fn enforce_alignment(ecx: &InterpCx<'tcx, Self>) -> bool; /// Gives the machine a chance to detect more misalignment than the built-in checks would catch. #[inline(always)] fn alignment_check( - _ecx: &InterpCx<'mir, 'tcx, Self>, + _ecx: &InterpCx<'tcx, Self>, _alloc_id: AllocId, _alloc_align: Align, _alloc_kind: AllocKind, @@ -161,23 +161,23 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { } /// Whether to enforce the validity invariant for a specific layout. - fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool; + fn enforce_validity(ecx: &InterpCx<'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool; /// Whether function calls should be [ABI](CallAbi)-checked. - fn enforce_abi(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { + fn enforce_abi(_ecx: &InterpCx<'tcx, Self>) -> bool { true } /// Whether Assert(OverflowNeg) and Assert(Overflow) MIR terminators should actually /// check for overflow. - fn ignore_optional_overflow_checks(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; + fn ignore_optional_overflow_checks(_ecx: &InterpCx<'tcx, Self>) -> bool; /// Entry point for obtaining the MIR of anything that should get evaluated. /// So not just functions and shims, but also const/static initializers, anonymous /// constants, ... fn load_mir( - ecx: &InterpCx<'mir, 'tcx, Self>, - instance: ty::InstanceDef<'tcx>, + ecx: &InterpCx<'tcx, Self>, + instance: ty::InstanceKind<'tcx>, ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> { Ok(ecx.tcx.instance_mir(instance)) } @@ -193,19 +193,19 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them /// was used. fn find_mir_or_eval_fn( - ecx: &mut InterpCx<'mir, 'tcx, Self>, + ecx: &mut InterpCx<'tcx, Self>, instance: ty::Instance<'tcx>, abi: CallAbi, args: &[FnArg<'tcx, Self::Provenance>], destination: &MPlaceTy<'tcx, Self::Provenance>, target: Option, unwind: mir::UnwindAction, - ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>>; + ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>>; /// Execute `fn_val`. It is the hook's responsibility to advance the instruction /// pointer as appropriate. fn call_extra_fn( - ecx: &mut InterpCx<'mir, 'tcx, Self>, + ecx: &mut InterpCx<'tcx, Self>, fn_val: Self::ExtraFnVal, abi: CallAbi, args: &[FnArg<'tcx, Self::Provenance>], @@ -220,7 +220,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// Returns `None` if the intrinsic was fully handled. /// Otherwise, returns an `Instance` of the function that implements the intrinsic. fn call_intrinsic( - ecx: &mut InterpCx<'mir, 'tcx, Self>, + ecx: &mut InterpCx<'tcx, Self>, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx, Self::Provenance>], destination: &MPlaceTy<'tcx, Self::Provenance>, @@ -230,17 +230,17 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// Called to evaluate `Assert` MIR terminators that trigger a panic. fn assert_panic( - ecx: &mut InterpCx<'mir, 'tcx, Self>, + ecx: &mut InterpCx<'tcx, Self>, msg: &mir::AssertMessage<'tcx>, unwind: mir::UnwindAction, ) -> InterpResult<'tcx>; /// Called to trigger a non-unwinding panic. - fn panic_nounwind(_ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: &str) -> InterpResult<'tcx>; + fn panic_nounwind(_ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx>; /// Called when unwinding reached a state where execution should be terminated. fn unwind_terminate( - ecx: &mut InterpCx<'mir, 'tcx, Self>, + ecx: &mut InterpCx<'tcx, Self>, reason: mir::UnwindTerminateReason, ) -> InterpResult<'tcx>; @@ -248,16 +248,16 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// /// Returns a (value, overflowed) pair if the operation succeeded fn binary_ptr_op( - ecx: &InterpCx<'mir, 'tcx, Self>, + ecx: &InterpCx<'tcx, Self>, bin_op: mir::BinOp, left: &ImmTy<'tcx, Self::Provenance>, right: &ImmTy<'tcx, Self::Provenance>, - ) -> InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)>; + ) -> InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>>; /// Generate the NaN returned by a float operation, given the list of inputs. /// (This is all inputs, not just NaN inputs!) fn generate_nan, F2: Float>( - _ecx: &InterpCx<'mir, 'tcx, Self>, + _ecx: &InterpCx<'tcx, Self>, _inputs: &[F1], ) -> F2 { // By default we always return the preferred NaN. @@ -266,14 +266,14 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// Called before a basic block terminator is executed. #[inline] - fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + fn before_terminator(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { Ok(()) } /// Called when the interpreter encounters a `StatementKind::ConstEvalCounter` instruction. /// You can use this to detect long or endlessly running programs. #[inline] - fn increment_const_eval_counter(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + fn increment_const_eval_counter(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { Ok(()) } @@ -293,7 +293,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// Return the `AllocId` for the given thread-local static in the current thread. fn thread_local_static_pointer( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ecx: &mut InterpCx<'tcx, Self>, def_id: DefId, ) -> InterpResult<'tcx, Pointer> { throw_unsup!(ThreadLocalStatic(def_id)) @@ -301,20 +301,20 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// Return the `AllocId` for the given `extern static`. fn extern_static_pointer( - ecx: &InterpCx<'mir, 'tcx, Self>, + ecx: &InterpCx<'tcx, Self>, def_id: DefId, ) -> InterpResult<'tcx, Pointer>; /// "Int-to-pointer cast" fn ptr_from_addr_cast( - ecx: &InterpCx<'mir, 'tcx, Self>, + ecx: &InterpCx<'tcx, Self>, addr: u64, ) -> InterpResult<'tcx, Pointer>>; /// Marks a pointer as exposed, allowing it's provenance /// to be recovered. "Pointer-to-int cast" fn expose_ptr( - ecx: &mut InterpCx<'mir, 'tcx, Self>, + ecx: &mut InterpCx<'tcx, Self>, ptr: Pointer, ) -> InterpResult<'tcx>; @@ -325,31 +325,45 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// /// When this fails, that means the pointer does not point to a live allocation. fn ptr_get_alloc( - ecx: &InterpCx<'mir, 'tcx, Self>, + ecx: &InterpCx<'tcx, Self>, ptr: Pointer, ) -> Option<(AllocId, Size, Self::ProvenanceExtra)>; - /// Called to adjust allocations to the Provenance and AllocExtra of this machine. + /// Called to adjust global allocations to the Provenance and AllocExtra of this machine. /// /// If `alloc` contains pointers, then they are all pointing to globals. /// - /// The way we construct allocations is to always first construct it without extra and then add - /// the extra. This keeps uniform code paths for handling both allocations created by CTFE for - /// globals, and allocations created by Miri during evaluation. - /// - /// `kind` is the kind of the allocation being adjusted; it can be `None` when - /// it's a global and `GLOBAL_KIND` is `None`. - /// /// This should avoid copying if no work has to be done! If this returns an owned /// allocation (because a copy had to be done to adjust things), machine memory will /// cache the result. (This relies on `AllocMap::get_or` being able to add the /// owned allocation to the map even when the map is shared.) - fn adjust_allocation<'b>( - ecx: &InterpCx<'mir, 'tcx, Self>, + fn adjust_global_allocation<'b>( + ecx: &InterpCx<'tcx, Self>, id: AllocId, - alloc: Cow<'b, Allocation>, - kind: Option>, - ) -> InterpResult<'tcx, Cow<'b, Allocation>>; + alloc: &'b Allocation, + ) -> InterpResult<'tcx, Cow<'b, Allocation>> + { + // The default implementation does a copy; CTFE machines have a more efficient implementation + // based on their particular choice for `Provenance`, `AllocExtra`, and `Bytes`. + let kind = Self::GLOBAL_KIND + .expect("if GLOBAL_KIND is None, adjust_global_allocation must be overwritten"); + let alloc = alloc.adjust_from_tcx(&ecx.tcx, |ptr| ecx.global_root_pointer(ptr))?; + let extra = + Self::init_alloc_extra(ecx, id, MemoryKind::Machine(kind), alloc.size(), alloc.align)?; + Ok(Cow::Owned(alloc.with_extra(extra))) + } + + /// Initialize the extra state of an allocation. + /// + /// This is guaranteed to be called exactly once on all allocations that are accessed by the + /// program. + fn init_alloc_extra( + ecx: &InterpCx<'tcx, Self>, + id: AllocId, + kind: MemoryKind, + size: Size, + align: Align, + ) -> InterpResult<'tcx, Self::AllocExtra>; /// Return a "root" pointer for the given allocation: the one that is used for direct /// accesses to this static/const/fn allocation, or the one returned from the heap allocator. @@ -359,7 +373,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// `kind` is the kind of the allocation the pointer points to; it can be `None` when /// it's a global and `GLOBAL_KIND` is `None`. fn adjust_alloc_root_pointer( - ecx: &InterpCx<'mir, 'tcx, Self>, + ecx: &InterpCx<'tcx, Self>, ptr: Pointer, kind: Option>, ) -> InterpResult<'tcx, Pointer>; @@ -370,7 +384,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// is triggered, `targets[0]` when the assembly falls through, or diverge in case of /// `InlineAsmOptions::NORETURN` being set. fn eval_inline_asm( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ecx: &mut InterpCx<'tcx, Self>, _template: &'tcx [InlineAsmTemplatePiece], _operands: &[mir::InlineAsmOperand<'tcx>], _options: InlineAsmOptions, @@ -406,10 +420,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// /// Used to prevent statics from self-initializing by reading from their own memory /// as it is being initialized. - fn before_alloc_read( - _ecx: &InterpCx<'mir, 'tcx, Self>, - _alloc_id: AllocId, - ) -> InterpResult<'tcx> { + fn before_alloc_read(_ecx: &InterpCx<'tcx, Self>, _alloc_id: AllocId) -> InterpResult<'tcx> { Ok(()) } @@ -444,7 +455,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// Returns the possibly adjusted pointer. #[inline] fn retag_ptr_value( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ecx: &mut InterpCx<'tcx, Self>, _kind: mir::RetagKind, val: &ImmTy<'tcx, Self::Provenance>, ) -> InterpResult<'tcx, ImmTy<'tcx, Self::Provenance>> { @@ -455,7 +466,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// Replaces all pointers stored in the given place. #[inline] fn retag_place_contents( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ecx: &mut InterpCx<'tcx, Self>, _kind: mir::RetagKind, _place: &PlaceTy<'tcx, Self::Provenance>, ) -> InterpResult<'tcx> { @@ -467,7 +478,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// These places need to be protected to make sure the program cannot tell whether the /// argument/return value was actually copied or passed in-place.. fn protect_in_place_function_argument( - ecx: &mut InterpCx<'mir, 'tcx, Self>, + ecx: &mut InterpCx<'tcx, Self>, mplace: &MPlaceTy<'tcx, Self::Provenance>, ) -> InterpResult<'tcx> { // Without an aliasing model, all we can do is put `Uninit` into the place. @@ -476,30 +487,30 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { } /// Called immediately before a new stack frame gets pushed. - fn init_frame_extra( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - frame: Frame<'mir, 'tcx, Self::Provenance>, - ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>>; + fn init_frame( + ecx: &mut InterpCx<'tcx, Self>, + frame: Frame<'tcx, Self::Provenance>, + ) -> InterpResult<'tcx, Frame<'tcx, Self::Provenance, Self::FrameExtra>>; /// Borrow the current thread's stack. fn stack<'a>( - ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>]; + ecx: &'a InterpCx<'tcx, Self>, + ) -> &'a [Frame<'tcx, Self::Provenance, Self::FrameExtra>]; /// Mutably borrow the current thread's stack. fn stack_mut<'a>( - ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec>; + ecx: &'a mut InterpCx<'tcx, Self>, + ) -> &'a mut Vec>; /// Called immediately after a stack frame got pushed and its locals got initialized. - fn after_stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + fn after_stack_push(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { Ok(()) } /// Called just before the return value is copied to the caller-provided return place. fn before_stack_pop( - _ecx: &InterpCx<'mir, 'tcx, Self>, - _frame: &Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, + _ecx: &InterpCx<'tcx, Self>, + _frame: &Frame<'tcx, Self::Provenance, Self::FrameExtra>, ) -> InterpResult<'tcx> { Ok(()) } @@ -508,8 +519,8 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// The `locals` have already been destroyed! #[inline(always)] fn after_stack_pop( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _frame: Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, + _ecx: &mut InterpCx<'tcx, Self>, + _frame: Frame<'tcx, Self::Provenance, Self::FrameExtra>, unwinding: bool, ) -> InterpResult<'tcx, StackPopJump> { // By default, we do not support unwinding from panics @@ -521,7 +532,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// but before the local's stack frame is updated to point to that memory. #[inline(always)] fn after_local_allocated( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ecx: &mut InterpCx<'tcx, Self>, _local: mir::Local, _mplace: &MPlaceTy<'tcx, Self::Provenance>, ) -> InterpResult<'tcx> { @@ -532,7 +543,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// but this hook has the chance to do some pre/postprocessing. #[inline(always)] fn eval_mir_constant( - ecx: &InterpCx<'mir, 'tcx, Self>, + ecx: &InterpCx<'tcx, Self>, val: mir::Const<'tcx>, span: Span, layout: Option>, @@ -540,7 +551,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { ) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>> where F: Fn( - &InterpCx<'mir, 'tcx, Self>, + &InterpCx<'tcx, Self>, mir::Const<'tcx>, Span, Option>, @@ -552,7 +563,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines /// (CTFE and ConstProp) use the same instance. Here, we share that code. -pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { +pub macro compile_time_machine(<$tcx: lifetime>) { type Provenance = CtfeProvenance; type ProvenanceExtra = bool; // the "immutable" flag @@ -567,13 +578,13 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { type Bytes = Box<[u8]>; #[inline(always)] - fn ignore_optional_overflow_checks(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { + fn ignore_optional_overflow_checks(_ecx: &InterpCx<$tcx, Self>) -> bool { false } #[inline(always)] fn unwind_terminate( - _ecx: &mut InterpCx<$mir, $tcx, Self>, + _ecx: &mut InterpCx<$tcx, Self>, _reason: mir::UnwindTerminateReason, ) -> InterpResult<$tcx> { unreachable!("unwinding cannot happen during compile-time evaluation") @@ -581,7 +592,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { #[inline(always)] fn call_extra_fn( - _ecx: &mut InterpCx<$mir, $tcx, Self>, + _ecx: &mut InterpCx<$tcx, Self>, fn_val: !, _abi: CallAbi, _args: &[FnArg<$tcx>], @@ -593,17 +604,27 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] - fn adjust_allocation<'b>( - _ecx: &InterpCx<$mir, $tcx, Self>, + fn adjust_global_allocation<'b>( + _ecx: &InterpCx<$tcx, Self>, _id: AllocId, - alloc: Cow<'b, Allocation>, - _kind: Option>, + alloc: &'b Allocation, ) -> InterpResult<$tcx, Cow<'b, Allocation>> { - Ok(alloc) + // Overwrite default implementation: no need to adjust anything. + Ok(Cow::Borrowed(alloc)) + } + + fn init_alloc_extra( + _ecx: &InterpCx<$tcx, Self>, + _id: AllocId, + _kind: MemoryKind, + _size: Size, + _align: Align, + ) -> InterpResult<$tcx, Self::AllocExtra> { + Ok(()) } fn extern_static_pointer( - ecx: &InterpCx<$mir, $tcx, Self>, + ecx: &InterpCx<$tcx, Self>, def_id: DefId, ) -> InterpResult<$tcx, Pointer> { // Use the `AllocId` associated with the `DefId`. Any actual *access* will fail. @@ -612,7 +633,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { #[inline(always)] fn adjust_alloc_root_pointer( - _ecx: &InterpCx<$mir, $tcx, Self>, + _ecx: &InterpCx<$tcx, Self>, ptr: Pointer, _kind: Option>, ) -> InterpResult<$tcx, Pointer> { @@ -621,7 +642,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { #[inline(always)] fn ptr_from_addr_cast( - _ecx: &InterpCx<$mir, $tcx, Self>, + _ecx: &InterpCx<$tcx, Self>, addr: u64, ) -> InterpResult<$tcx, Pointer>> { // Allow these casts, but make the pointer not dereferenceable. @@ -632,7 +653,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { #[inline(always)] fn ptr_get_alloc( - _ecx: &InterpCx<$mir, $tcx, Self>, + _ecx: &InterpCx<$tcx, Self>, ptr: Pointer, ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> { // We know `offset` is relative to the allocation, so we can use `into_parts`. diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 350fd480fbaa..9a26ac04b85a 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -21,6 +21,8 @@ use rustc_middle::mir::display_allocation; use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt}; use rustc_target::abi::{Align, HasDataLayout, Size}; +use tracing::{debug, instrument, trace}; + use crate::fluent_generated as fluent; use super::{ @@ -94,7 +96,7 @@ impl<'tcx, Other> FnVal<'tcx, Other> { // `Memory` has to depend on the `Machine` because some of its operations // (e.g., `get`) call a `Machine` hook. -pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> { +pub struct Memory<'tcx, M: Machine<'tcx>> { /// Allocations local to this instance of the interpreter. The kind /// helps ensure that the same mechanism is used for allocation and /// deallocation. When an allocation is not found here, it is a @@ -140,7 +142,7 @@ pub struct AllocRefMut<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes = Bo alloc_id: AllocId, } -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> Memory<'tcx, M> { pub fn new() -> Self { Memory { alloc_map: M::MemoryMap::default(), @@ -156,7 +158,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { } } -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Call this to turn untagged "global" pointers (obtained via `tcx`) into /// the machine pointer to the allocation. Must never be used /// for any other pointers, nor for TLS statics. @@ -237,7 +239,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn allocate_raw_ptr( &mut self, - alloc: Allocation, + alloc: Allocation, kind: MemoryKind, ) -> InterpResult<'tcx, Pointer> { let id = self.tcx.reserve_alloc_id(); @@ -246,8 +248,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { M::GLOBAL_KIND.map(MemoryKind::Machine), "dynamically allocating global memory" ); - let alloc = M::adjust_allocation(self, id, Cow::Owned(alloc), Some(kind))?; - self.memory.alloc_map.insert(id, (kind, alloc.into_owned())); + // We have set things up so we don't need to call `adjust_from_tcx` here, + // so we avoid copying the entire allocation contents. + let extra = M::init_alloc_extra(self, id, kind, alloc.size(), alloc.align)?; + let alloc = alloc.with_extra(extra); + self.memory.alloc_map.insert(id, (kind, alloc)); M::adjust_alloc_root_pointer(self, Pointer::from(id), Some(kind)) } @@ -413,6 +418,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// to the allocation it points to. Supports both shared and mutable references, as the actual /// checking is offloaded to a helper closure. /// + /// `alloc_size` will only get called for non-zero-sized accesses. + /// /// Returns `None` if and only if the size is 0. fn check_and_deref_ptr( &self, @@ -425,20 +432,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { M::ProvenanceExtra, ) -> InterpResult<'tcx, (Size, Align, T)>, ) -> InterpResult<'tcx, Option> { + // Everything is okay with size 0. + if size.bytes() == 0 { + return Ok(None); + } + Ok(match self.ptr_try_get_alloc_id(ptr) { Err(addr) => { - // We couldn't get a proper allocation. This is only okay if the access size is 0, - // and the address is not null. - if size.bytes() > 0 || addr == 0 { - throw_ub!(DanglingIntPointer(addr, msg)); - } - None + // We couldn't get a proper allocation. + throw_ub!(DanglingIntPointer(addr, msg)); } Ok((alloc_id, offset, prov)) => { let (alloc_size, _alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?; - // Test bounds. This also ensures non-null. + // Test bounds. // It is sufficient to check this for the end pointer. Also check for overflow! - if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) { + if offset.checked_add(size, &self.tcx).is_none_or(|end| end > alloc_size) { throw_ub!(PointerOutOfBounds { alloc_id, alloc_size, @@ -447,14 +455,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { msg, }) } - // Ensure we never consider the null pointer dereferenceable. - if M::Provenance::OFFSET_IS_ADDR { - assert_ne!(ptr.addr(), Size::ZERO); - } - // We can still be zero-sized in this branch, in which case we have to - // return `None`. - if size.bytes() == 0 { None } else { Some(ret_val) } + Some(ret_val) } }) } @@ -525,7 +527,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// This function is used by Miri's provenance GC to remove unreachable entries from the dead_alloc_map. pub fn remove_unreachable_allocs(&mut self, reachable_allocs: &FxHashSet) { // Unlike all the other GC helpers where we check if an `AllocId` is found in the interpreter or @@ -537,7 +539,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } /// Allocation accessors -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Helper function to obtain a global (tcx) allocation. /// This attempts to return a reference to an existing allocation if /// one can be found in `tcx`. That, however, is only possible if `tcx` and @@ -584,11 +586,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; M::before_access_global(self.tcx, &self.machine, id, alloc, def_id, is_write)?; // We got tcx memory. Let the machine initialize its "extra" stuff. - M::adjust_allocation( + M::adjust_global_allocation( self, id, // always use the ID we got as input, not the "hidden" one. - Cow::Borrowed(alloc.inner()), - M::GLOBAL_KIND.map(MemoryKind::Machine), + alloc.inner(), ) } @@ -641,16 +642,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { size, CheckInAllocMsg::MemoryAccessTest, |alloc_id, offset, prov| { - if !self.memory.validation_in_progress.get() { - // We want to call the hook on *all* accesses that involve an AllocId, - // including zero-sized accesses. That means we have to do it here - // rather than below in the `Some` branch. - M::before_alloc_read(self, alloc_id)?; - } let alloc = self.get_alloc_raw(alloc_id)?; Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc))) }, )?; + // We want to call the hook on *all* accesses that involve an AllocId, including zero-sized + // accesses. That means we cannot rely on the closure above or the `Some` branch below. We + // do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked. + if !self.memory.validation_in_progress.get() { + if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr) { + M::before_alloc_read(self, alloc_id)?; + } + } if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc { let range = alloc_range(offset, size); @@ -864,19 +867,26 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .ok_or_else(|| err_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))).into()) } - pub fn get_ptr_vtable( + /// Get the dynamic type of the given vtable pointer. + /// If `expected_trait` is `Some`, it must be a vtable for the given trait. + pub fn get_ptr_vtable_ty( &self, ptr: Pointer>, - ) -> InterpResult<'tcx, (Ty<'tcx>, Option>)> { + expected_trait: Option<&'tcx ty::List>>, + ) -> InterpResult<'tcx, Ty<'tcx>> { trace!("get_ptr_vtable({:?})", ptr); let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?; if offset.bytes() != 0 { throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) } - match self.tcx.try_get_global_alloc(alloc_id) { - Some(GlobalAlloc::VTable(ty, trait_ref)) => Ok((ty, trait_ref)), - _ => throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))), + let Some(GlobalAlloc::VTable(ty, vtable_trait)) = self.tcx.try_get_global_alloc(alloc_id) + else { + throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) + }; + if let Some(expected_trait) = expected_trait { + self.check_vtable_for_type(vtable_trait, expected_trait)?; } + Ok(ty) } pub fn alloc_mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> { @@ -887,14 +897,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Create a lazy debug printer that prints the given allocation and all allocations it points /// to, recursively. #[must_use] - pub fn dump_alloc<'a>(&'a self, id: AllocId) -> DumpAllocs<'a, 'mir, 'tcx, M> { + pub fn dump_alloc<'a>(&'a self, id: AllocId) -> DumpAllocs<'a, 'tcx, M> { self.dump_allocs(vec![id]) } /// Create a lazy debug printer for a list of allocations and all allocations they point to, /// recursively. #[must_use] - pub fn dump_allocs<'a>(&'a self, mut allocs: Vec) -> DumpAllocs<'a, 'mir, 'tcx, M> { + pub fn dump_allocs<'a>(&'a self, mut allocs: Vec) -> DumpAllocs<'a, 'tcx, M> { allocs.sort(); allocs.dedup(); DumpAllocs { ecx: self, allocs } @@ -974,12 +984,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[doc(hidden)] /// There's no way to use this directly, it's just a helper struct for the `dump_alloc(s)` methods. -pub struct DumpAllocs<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { - ecx: &'a InterpCx<'mir, 'tcx, M>, +pub struct DumpAllocs<'a, 'tcx, M: Machine<'tcx>> { + ecx: &'a InterpCx<'tcx, M>, allocs: Vec, } -impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, 'mir, 'tcx, M> { +impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Cannot be a closure because it is generic in `Prov`, `Extra`. fn write_allocation_track_relocs<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( @@ -1124,7 +1134,7 @@ impl<'tcx, 'a, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRef<'a, 'tcx, Pr } } -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Reads the given number of bytes from memory, and strips their provenance if possible. /// Returns them as a slice. /// @@ -1337,11 +1347,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } /// Machine pointer introspection. -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Test if this value might be null. /// If the machine does not support ptr-to-int casts, this is conservative. pub fn scalar_may_be_null(&self, scalar: Scalar) -> InterpResult<'tcx, bool> { - Ok(match scalar.try_to_int() { + Ok(match scalar.try_to_scalar_int() { Ok(int) => int.is_null(), Err(_) => { // Can only happen during CTFE. diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index bad9732f4831..0a7e9853763f 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -4,10 +4,11 @@ use std::assert_matches::assert_matches; use either::{Either, Left, Right}; +use tracing::trace; use rustc_hir::def::Namespace; use rustc_middle::mir::interpret::ScalarSizeMismatch; -use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; +use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter}; use rustc_middle::ty::{ConstInt, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -86,6 +87,12 @@ impl Immediate { } } + #[inline] + #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) + pub fn to_scalar_int(self) -> ScalarInt { + self.to_scalar().try_to_scalar_int().unwrap() + } + #[inline] #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) pub fn to_scalar_pair(self) -> (Scalar, Scalar) { @@ -218,19 +225,11 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { Self::from_scalar(Scalar::from(s), layout) } - #[inline] - pub fn try_from_uint(i: impl Into, layout: TyAndLayout<'tcx>) -> Option { - Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout)) - } #[inline] pub fn from_uint(i: impl Into, layout: TyAndLayout<'tcx>) -> Self { Self::from_scalar(Scalar::from_uint(i, layout.size), layout) } - #[inline] - pub fn try_from_int(i: impl Into, layout: TyAndLayout<'tcx>) -> Option { - Some(Self::from_scalar(Scalar::try_from_int(i, layout.size)?, layout)) - } #[inline] pub fn from_int(i: impl Into, layout: TyAndLayout<'tcx>) -> Self { Self::from_scalar(Scalar::from_int(i, layout.size), layout) @@ -249,6 +248,15 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { Self::from_scalar(Scalar::from_i8(c as i8), layout) } + pub fn from_pair(a: Self, b: Self, tcx: TyCtxt<'tcx>) -> Self { + let layout = tcx + .layout_of( + ty::ParamEnv::reveal_all().and(Ty::new_tup(tcx, &[a.layout.ty, b.layout.ty])), + ) + .unwrap(); + Self::from_scalar_pair(a.to_scalar(), b.to_scalar(), layout) + } + /// Return the immediate as a `ScalarInt`. Ensures that it has the size that the layout of the /// immediate indicates. #[inline] @@ -266,10 +274,22 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { #[inline] pub fn to_const_int(self) -> ConstInt { assert!(self.layout.ty.is_integral()); - let int = self.to_scalar().assert_int(); + let int = self.imm.to_scalar_int(); + assert_eq!(int.size(), self.layout.size); ConstInt::new(int, self.layout.ty.is_signed(), self.layout.ty.is_ptr_sized_integral()) } + #[inline] + #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) + pub fn to_pair(self, cx: &(impl HasTyCtxt<'tcx> + HasParamEnv<'tcx>)) -> (Self, Self) { + let layout = self.layout; + let (val0, val1) = self.to_scalar_pair(); + ( + ImmTy::from_scalar(val0, layout.field(cx, 0)), + ImmTy::from_scalar(val1, layout.field(cx, 1)), + ) + } + /// Compute the "sub-immediate" that is located within the `base` at the given offset with the /// given layout. // Not called `offset` to avoid confusion with the trait method. @@ -353,21 +373,21 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> { MemPlaceMeta::None } - fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn offset_with_meta>( &self, offset: Size, _mode: OffsetMode, meta: MemPlaceMeta, layout: TyAndLayout<'tcx>, - ecx: &InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self> { assert_matches!(meta, MemPlaceMeta::None); // we can't store this anywhere anyway Ok(self.offset_(offset, layout, ecx)) } - fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn to_op>( &self, - _ecx: &InterpCx<'mir, 'tcx, M>, + _ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { Ok(self.clone().into()) } @@ -436,13 +456,13 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> { } } - fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn offset_with_meta>( &self, offset: Size, mode: OffsetMode, meta: MemPlaceMeta, layout: TyAndLayout<'tcx>, - ecx: &InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self> { match self.as_mplace_or_imm() { Left(mplace) => Ok(mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into()), @@ -454,9 +474,9 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> { } } - fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn to_op>( &self, - _ecx: &InterpCx<'mir, 'tcx, M>, + _ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { Ok(self.clone()) } @@ -488,7 +508,7 @@ impl<'tcx, Prov: Provenance> Readable<'tcx, Prov> for ImmTy<'tcx, Prov> { } } -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`. /// Returns `None` if the layout does not permit loading this as a value. /// diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 5f59e3d887e4..73bdf96627ae 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -1,78 +1,23 @@ +use either::Either; + use rustc_apfloat::{Float, FloatConvert}; use rustc_middle::mir; use rustc_middle::mir::interpret::{InterpResult, Scalar}; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; -use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty}; +use rustc_middle::ty::{self, FloatTy, ScalarInt}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::sym; -use rustc_target::abi::Abi; +use tracing::trace; -use super::{err_ub, throw_ub, throw_ub_custom, ImmTy, Immediate, InterpCx, Machine, PlaceTy}; +use super::{err_ub, throw_ub, ImmTy, InterpCx, Machine, MemPlaceMeta}; -use crate::fluent_generated as fluent; - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { - /// Applies the binary operation `op` to the two operands and writes a tuple of the result - /// and a boolean signifying the potential overflow to the destination. - pub fn binop_with_overflow( - &mut self, - op: mir::BinOp, - left: &ImmTy<'tcx, M::Provenance>, - right: &ImmTy<'tcx, M::Provenance>, - dest: &PlaceTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx> { - let (val, overflowed) = self.overflowing_binary_op(op, left, right)?; - debug_assert_eq!( - Ty::new_tup(self.tcx.tcx, &[val.layout.ty, self.tcx.types.bool]), - dest.layout.ty, - "type mismatch for result of {op:?}", - ); - // Write the result to `dest`. - if let Abi::ScalarPair(..) = dest.layout.abi { - // We can use the optimized path and avoid `place_field` (which might do - // `force_allocation`). - let pair = Immediate::ScalarPair(val.to_scalar(), Scalar::from_bool(overflowed)); - self.write_immediate(pair, dest)?; - } else { - assert!(self.tcx.sess.opts.unstable_opts.randomize_layout); - // With randomized layout, `(int, bool)` might cease to be a `ScalarPair`, so we have to - // do a component-wise write here. This code path is slower than the above because - // `place_field` will have to `force_allocate` locals here. - let val_field = self.project_field(dest, 0)?; - self.write_scalar(val.to_scalar(), &val_field)?; - let overflowed_field = self.project_field(dest, 1)?; - self.write_scalar(Scalar::from_bool(overflowed), &overflowed_field)?; - } - Ok(()) - } - - /// Applies the binary operation `op` to the arguments and writes the result to the - /// destination. - pub fn binop_ignore_overflow( - &mut self, - op: mir::BinOp, - left: &ImmTy<'tcx, M::Provenance>, - right: &ImmTy<'tcx, M::Provenance>, - dest: &PlaceTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx> { - let val = self.wrapping_binary_op(op, left, right)?; - assert_eq!(val.layout.ty, dest.layout.ty, "type mismatch for result of {op:?}"); - self.write_immediate(*val, dest) - } -} - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { - fn three_way_compare(&self, lhs: T, rhs: T) -> (ImmTy<'tcx, M::Provenance>, bool) { +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { + fn three_way_compare(&self, lhs: T, rhs: T) -> ImmTy<'tcx, M::Provenance> { let res = Ord::cmp(&lhs, &rhs); - return (ImmTy::from_ordering(res, *self.tcx), false); + return ImmTy::from_ordering(res, *self.tcx); } - fn binary_char_op( - &self, - bin_op: mir::BinOp, - l: char, - r: char, - ) -> (ImmTy<'tcx, M::Provenance>, bool) { + fn binary_char_op(&self, bin_op: mir::BinOp, l: char, r: char) -> ImmTy<'tcx, M::Provenance> { use rustc_middle::mir::BinOp::*; if bin_op == Cmp { @@ -88,15 +33,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ge => l >= r, _ => span_bug!(self.cur_span(), "Invalid operation on char: {:?}", bin_op), }; - (ImmTy::from_bool(res, *self.tcx), false) + ImmTy::from_bool(res, *self.tcx) } - fn binary_bool_op( - &self, - bin_op: mir::BinOp, - l: bool, - r: bool, - ) -> (ImmTy<'tcx, M::Provenance>, bool) { + fn binary_bool_op(&self, bin_op: mir::BinOp, l: bool, r: bool) -> ImmTy<'tcx, M::Provenance> { use rustc_middle::mir::BinOp::*; let res = match bin_op { @@ -111,7 +51,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { BitXor => l ^ r, _ => span_bug!(self.cur_span(), "Invalid operation on bool: {:?}", bin_op), }; - (ImmTy::from_bool(res, *self.tcx), false) + ImmTy::from_bool(res, *self.tcx) } fn binary_float_op + Into>>( @@ -120,14 +60,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { layout: TyAndLayout<'tcx>, l: F, r: F, - ) -> (ImmTy<'tcx, M::Provenance>, bool) { + ) -> ImmTy<'tcx, M::Provenance> { use rustc_middle::mir::BinOp::*; // Performs appropriate non-deterministic adjustments of NaN results. let adjust_nan = |f: F| -> F { if f.is_nan() { M::generate_nan(self, &[l, r]) } else { f } }; - let val = match bin_op { + match bin_op { Eq => ImmTy::from_bool(l == r, *self.tcx), Ne => ImmTy::from_bool(l != r, *self.tcx), Lt => ImmTy::from_bool(l < r, *self.tcx), @@ -140,8 +80,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Div => ImmTy::from_scalar(adjust_nan((l / r).value).into(), layout), Rem => ImmTy::from_scalar(adjust_nan((l % r).value).into(), layout), _ => span_bug!(self.cur_span(), "invalid float op: `{:?}`", bin_op), - }; - (val, false) + } } fn binary_int_op( @@ -149,17 +88,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { bin_op: mir::BinOp, left: &ImmTy<'tcx, M::Provenance>, right: &ImmTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> { + ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { use rustc_middle::mir::BinOp::*; // This checks the size, so that we can just assert it below. let l = left.to_scalar_int()?; let r = right.to_scalar_int()?; // Prepare to convert the values to signed or unsigned form. - let l_signed = || l.assert_int(left.layout.size); - let l_unsigned = || l.assert_uint(left.layout.size); - let r_signed = || r.assert_int(right.layout.size); - let r_unsigned = || r.assert_uint(right.layout.size); + let l_signed = || l.to_int(left.layout.size); + let l_unsigned = || l.to_uint(left.layout.size); + let r_signed = || r.to_int(right.layout.size); + let r_unsigned = || r.to_uint(right.layout.size); let throw_ub_on_overflow = match bin_op { AddUnchecked => Some(sym::unchecked_add), @@ -169,27 +108,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ShrUnchecked => Some(sym::unchecked_shr), _ => None, }; + let with_overflow = bin_op.is_overflowing(); // Shift ops can have an RHS with a different numeric type. if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) { - let size = left.layout.size.bits(); - // The shift offset is implicitly masked to the type size. (This is the one MIR operator - // that does *not* directly map to a single LLVM operation.) Compute how much we - // actually shift and whether there was an overflow due to shifting too much. + let l_bits = left.layout.size.bits(); + // Compute the equivalent shift modulo `size` that is in the range `0..size`. (This is + // the one MIR operator that does *not* directly map to a single LLVM operation.) let (shift_amount, overflow) = if right.layout.abi.is_signed() { let shift_amount = r_signed(); - let overflow = shift_amount < 0 || shift_amount >= i128::from(size); - // Deliberately wrapping `as` casts: shift_amount *can* be negative, but the result - // of the `as` will be equal modulo `size` (since it is a power of two). - let masked_amount = (shift_amount as u128) % u128::from(size); - assert_eq!(overflow, shift_amount != (masked_amount as i128)); - (masked_amount, overflow) + let rem = shift_amount.rem_euclid(l_bits.into()); + // `rem` is guaranteed positive, so the `unwrap` cannot fail + (u128::try_from(rem).unwrap(), rem != shift_amount) } else { let shift_amount = r_unsigned(); - let masked_amount = shift_amount % u128::from(size); - (masked_amount, shift_amount != masked_amount) + let rem = shift_amount.rem_euclid(l_bits.into()); + (rem, rem != shift_amount) }; - let shift_amount = u32::try_from(shift_amount).unwrap(); // we masked so this will always fit + let shift_amount = u32::try_from(shift_amount).unwrap(); // we brought this in the range `0..size` so this will always fit // Compute the shifted result. let result = if left.layout.abi.is_signed() { let l = l_signed(); @@ -209,19 +145,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ScalarInt::truncate_from_uint(result, left.layout.size).0 }; - if overflow && let Some(intrinsic_name) = throw_ub_on_overflow { - throw_ub_custom!( - fluent::const_eval_overflow_shift, - val = if right.layout.abi.is_signed() { - r_signed().to_string() + if overflow && let Some(intrinsic) = throw_ub_on_overflow { + throw_ub!(ShiftOverflow { + intrinsic, + shift_amount: if right.layout.abi.is_signed() { + Either::Right(r_signed()) } else { - r_unsigned().to_string() - }, - name = intrinsic_name - ); + Either::Left(r_unsigned()) + } + }); } - return Ok((ImmTy::from_scalar_int(result, left.layout), overflow)); + return Ok(ImmTy::from_scalar_int(result, left.layout)); } // For the remaining ops, the types must be the same on both sides @@ -246,7 +181,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { _ => None, }; if let Some(op) = op { - return Ok((ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx), false)); + return Ok(ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx)); } if bin_op == Cmp { return Ok(self.three_way_compare(l_signed(), r_signed())); @@ -256,9 +191,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Rem if r.is_null() => throw_ub!(RemainderByZero), Div => Some(i128::overflowing_div), Rem => Some(i128::overflowing_rem), - Add | AddUnchecked => Some(i128::overflowing_add), - Sub | SubUnchecked => Some(i128::overflowing_sub), - Mul | MulUnchecked => Some(i128::overflowing_mul), + Add | AddUnchecked | AddWithOverflow => Some(i128::overflowing_add), + Sub | SubUnchecked | SubWithOverflow => Some(i128::overflowing_sub), + Mul | MulUnchecked | MulWithOverflow => Some(i128::overflowing_mul), _ => None, }; if let Some(op) = op { @@ -282,10 +217,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // If that truncation loses any information, we have an overflow. let (result, lossy) = ScalarInt::truncate_from_int(result, left.layout.size); let overflow = oflo || lossy; - if overflow && let Some(intrinsic_name) = throw_ub_on_overflow { - throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name); + if overflow && let Some(intrinsic) = throw_ub_on_overflow { + throw_ub!(ArithOverflow { intrinsic }); } - return Ok((ImmTy::from_scalar_int(result, left.layout), overflow)); + let res = ImmTy::from_scalar_int(result, left.layout); + return Ok(if with_overflow { + let overflow = ImmTy::from_bool(overflow, *self.tcx); + ImmTy::from_pair(res, overflow, *self.tcx) + } else { + res + }); } } // From here on it's okay to treat everything as unsigned. @@ -296,7 +237,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { return Ok(self.three_way_compare(l, r)); } - let val = match bin_op { + Ok(match bin_op { Eq => ImmTy::from_bool(l == r, *self.tcx), Ne => ImmTy::from_bool(l != r, *self.tcx), @@ -309,40 +250,42 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { BitAnd => ImmTy::from_uint(l & r, left.layout), BitXor => ImmTy::from_uint(l ^ r, left.layout), - Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => { + _ => { assert!(!left.layout.abi.is_signed()); let op: fn(u128, u128) -> (u128, bool) = match bin_op { - Add | AddUnchecked => u128::overflowing_add, - Sub | SubUnchecked => u128::overflowing_sub, - Mul | MulUnchecked => u128::overflowing_mul, + Add | AddUnchecked | AddWithOverflow => u128::overflowing_add, + Sub | SubUnchecked | SubWithOverflow => u128::overflowing_sub, + Mul | MulUnchecked | MulWithOverflow => u128::overflowing_mul, Div if r == 0 => throw_ub!(DivisionByZero), Rem if r == 0 => throw_ub!(RemainderByZero), Div => u128::overflowing_div, Rem => u128::overflowing_rem, - _ => bug!(), + _ => span_bug!( + self.cur_span(), + "invalid binary op {:?}: {:?}, {:?} (both {})", + bin_op, + left, + right, + right.layout.ty, + ), }; let (result, oflo) = op(l, r); // Truncate to target type. // If that truncation loses any information, we have an overflow. let (result, lossy) = ScalarInt::truncate_from_uint(result, left.layout.size); let overflow = oflo || lossy; - if overflow && let Some(intrinsic_name) = throw_ub_on_overflow { - throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name); + if overflow && let Some(intrinsic) = throw_ub_on_overflow { + throw_ub!(ArithOverflow { intrinsic }); + } + let res = ImmTy::from_scalar_int(result, left.layout); + if with_overflow { + let overflow = ImmTy::from_bool(overflow, *self.tcx); + ImmTy::from_pair(res, overflow, *self.tcx) + } else { + res } - return Ok((ImmTy::from_scalar_int(result, left.layout), overflow)); } - - _ => span_bug!( - self.cur_span(), - "invalid binary op {:?}: {:?}, {:?} (both {})", - bin_op, - left, - right, - right.layout.ty, - ), - }; - - Ok((val, false)) + }) } fn binary_ptr_op( @@ -350,7 +293,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { bin_op: mir::BinOp, left: &ImmTy<'tcx, M::Provenance>, right: &ImmTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> { + ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { use rustc_middle::mir::BinOp::*; match bin_op { @@ -369,10 +312,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?; let offset_ptr = self.ptr_offset_inbounds(ptr, offset_bytes)?; - Ok(( - ImmTy::from_scalar(Scalar::from_maybe_pointer(offset_ptr, self), left.layout), - false, - )) + Ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(offset_ptr, self), left.layout)) } // Fall back to machine hook so Miri can support more pointer ops. @@ -380,13 +320,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - /// Returns the result of the specified operation, and whether it overflowed. - pub fn overflowing_binary_op( + /// Returns the result of the specified operation. + /// + /// Whether this produces a scalar or a pair depends on the specific `bin_op`. + pub fn binary_op( &self, bin_op: mir::BinOp, left: &ImmTy<'tcx, M::Provenance>, right: &ImmTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> { + ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { trace!( "Running binary op {:?}: {:?} ({}), {:?} ({})", bin_op, @@ -415,14 +357,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let left = left.to_scalar(); let right = right.to_scalar(); Ok(match fty { - FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F16 => { + self.binary_float_op(bin_op, layout, left.to_f16()?, right.to_f16()?) + } FloatTy::F32 => { self.binary_float_op(bin_op, layout, left.to_f32()?, right.to_f32()?) } FloatTy::F64 => { self.binary_float_op(bin_op, layout, left.to_f64()?, right.to_f64()?) } - FloatTy::F128 => unimplemented!("f16_f128"), + FloatTy::F128 => { + self.binary_float_op(bin_op, layout, left.to_f128()?, right.to_f128()?) + } }) } _ if left.layout.ty.is_integral() => { @@ -458,77 +404,80 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - #[inline] - pub fn wrapping_binary_op( - &self, - bin_op: mir::BinOp, - left: &ImmTy<'tcx, M::Provenance>, - right: &ImmTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { - let (val, _overflow) = self.overflowing_binary_op(bin_op, left, right)?; - Ok(val) - } - /// Returns the result of the specified operation, whether it overflowed, and /// the result type. - pub fn overflowing_unary_op( + pub fn unary_op( &self, un_op: mir::UnOp, val: &ImmTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> { + ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { use rustc_middle::mir::UnOp::*; let layout = val.layout; - let val = val.to_scalar(); trace!("Running unary op {:?}: {:?} ({})", un_op, val, layout.ty); match layout.ty.kind() { ty::Bool => { + let val = val.to_scalar(); let val = val.to_bool()?; let res = match un_op { Not => !val, _ => span_bug!(self.cur_span(), "Invalid bool op {:?}", un_op), }; - Ok((ImmTy::from_bool(res, *self.tcx), false)) + Ok(ImmTy::from_bool(res, *self.tcx)) } ty::Float(fty) => { + let val = val.to_scalar(); + if un_op != Neg { + span_bug!(self.cur_span(), "Invalid float op {:?}", un_op); + } + // No NaN adjustment here, `-` is a bitwise operation! - let res = match (un_op, fty) { - (Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?), - (Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?), - _ => span_bug!(self.cur_span(), "Invalid float op {:?}", un_op), + let res = match fty { + FloatTy::F16 => Scalar::from_f16(-val.to_f16()?), + FloatTy::F32 => Scalar::from_f32(-val.to_f32()?), + FloatTy::F64 => Scalar::from_f64(-val.to_f64()?), + FloatTy::F128 => Scalar::from_f128(-val.to_f128()?), }; - Ok((ImmTy::from_scalar(res, layout), false)) + Ok(ImmTy::from_scalar(res, layout)) + } + ty::Int(..) => { + let val = val.to_scalar().to_int(layout.size)?; + let res = match un_op { + Not => !val, + Neg => val.wrapping_neg(), + _ => span_bug!(self.cur_span(), "Invalid integer op {:?}", un_op), + }; + let res = ScalarInt::truncate_from_int(res, layout.size).0; + Ok(ImmTy::from_scalar(res.into(), layout)) + } + ty::Uint(..) => { + let val = val.to_scalar().to_uint(layout.size)?; + let res = match un_op { + Not => !val, + _ => span_bug!(self.cur_span(), "Invalid unsigned integer op {:?}", un_op), + }; + let res = ScalarInt::truncate_from_uint(res, layout.size).0; + Ok(ImmTy::from_scalar(res.into(), layout)) + } + ty::RawPtr(..) => { + assert_eq!(un_op, PtrMetadata); + let (_, meta) = val.to_scalar_and_meta(); + Ok(match meta { + MemPlaceMeta::Meta(scalar) => { + let ty = un_op.ty(*self.tcx, val.layout.ty); + let layout = self.layout_of(ty)?; + ImmTy::from_scalar(scalar, layout) + } + MemPlaceMeta::None => { + let unit_layout = self.layout_of(self.tcx.types.unit)?; + ImmTy::uninit(unit_layout) + } + }) } _ => { - assert!(layout.ty.is_integral()); - let val = val.to_bits(layout.size)?; - let (res, overflow) = match un_op { - Not => (self.truncate(!val, layout), false), // bitwise negation, then truncate - Neg => { - // arithmetic negation - assert!(layout.abi.is_signed()); - let val = self.sign_extend(val, layout) as i128; - let (res, overflow) = val.overflowing_neg(); - let res = res as u128; - // Truncate to target type. - // If that truncation loses any information, we have an overflow. - let truncated = self.truncate(res, layout); - (truncated, overflow || self.sign_extend(truncated, layout) != res) - } - }; - Ok((ImmTy::from_uint(res, layout), overflow)) + bug!("Unexpected unary op argument {val:?}") } } } - - #[inline] - pub fn wrapping_unary_op( - &self, - un_op: mir::UnOp, - val: &ImmTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { - let (val, _overflow) = self.overflowing_unary_op(un_op, val)?; - Ok(val) - } } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 9ced825853bd..08d3165867c1 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -5,10 +5,10 @@ use std::assert_matches::assert_matches; use either::{Either, Left, Right}; +use tracing::{instrument, trace}; use rustc_ast::Mutability; use rustc_middle::mir; -use rustc_middle::ty; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::Ty; use rustc_middle::{bug, span_bug}; @@ -76,12 +76,12 @@ impl MemPlace { #[inline] // Not called `offset_with_meta` to avoid confusion with the trait method. - fn offset_with_meta_<'mir, 'tcx, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn offset_with_meta_<'tcx, M: Machine<'tcx, Provenance = Prov>>( self, offset: Size, mode: OffsetMode, meta: MemPlaceMeta, - ecx: &InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self> { debug_assert!( !meta.has_meta() || self.meta.has_meta(), @@ -161,20 +161,20 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> { self.mplace.meta } - fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn offset_with_meta>( &self, offset: Size, mode: OffsetMode, meta: MemPlaceMeta, layout: TyAndLayout<'tcx>, - ecx: &InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self> { Ok(MPlaceTy { mplace: self.mplace.offset_with_meta_(offset, mode, meta, ecx)?, layout }) } - fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn to_op>( &self, - _ecx: &InterpCx<'mir, 'tcx, M>, + _ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { Ok(self.clone().into()) } @@ -273,13 +273,13 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> { } } - fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn offset_with_meta>( &self, offset: Size, mode: OffsetMode, meta: MemPlaceMeta, layout: TyAndLayout<'tcx>, - ecx: &InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self> { Ok(match self.as_mplace_or_local() { Left(mplace) => mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into(), @@ -304,9 +304,9 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> { }) } - fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn to_op>( &self, - ecx: &InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { ecx.place_to_op(self) } @@ -340,9 +340,9 @@ pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> { &self, ) -> Either, (mir::Local, Option, usize, TyAndLayout<'tcx>)>; - fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn force_mplace>( &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, + ecx: &mut InterpCx<'tcx, M>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>>; } @@ -356,9 +356,9 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> { } #[inline(always)] - fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn force_mplace>( &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, + ecx: &mut InterpCx<'tcx, M>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>> { ecx.force_allocation(self) } @@ -373,19 +373,19 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> { } #[inline(always)] - fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn force_mplace>( &self, - _ecx: &mut InterpCx<'mir, 'tcx, M>, + _ecx: &mut InterpCx<'tcx, M>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>> { Ok(self.clone()) } } // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 -impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M> +impl<'tcx, Prov, M> InterpCx<'tcx, M> where Prov: Provenance, - M: Machine<'mir, 'tcx, Provenance = Prov>, + M: Machine<'tcx, Provenance = Prov>, { pub fn ptr_with_meta_to_mplace( &self, @@ -498,13 +498,14 @@ where &self, mplace: &MPlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, u64)> { - // Basically we just transmute this place into an array following simd_size_and_type. - // (Transmuting is okay since this is an in-memory place. We also double-check the size - // stays the same.) + // Basically we want to transmute this place into an array following simd_size_and_type. let (len, e_ty) = mplace.layout.ty.simd_size_and_type(*self.tcx); - let array = Ty::new_array(self.tcx.tcx, e_ty, len); - let layout = self.layout_of(array)?; - let mplace = mplace.transmute(layout, self)?; + // Some SIMD types have padding, so `len` many `e_ty` does not cover the entire place. + // Therefore we cannot transmute, and instead we project at offset 0, which side-steps + // the size check. + let array_layout = self.layout_of(Ty::new_array(self.tcx.tcx, e_ty, len))?; + assert!(array_layout.size <= mplace.layout.size); + let mplace = mplace.offset(Size::ZERO, array_layout, self)?; Ok((mplace, len)) } @@ -1016,54 +1017,6 @@ where let layout = self.layout_of(raw.ty)?; Ok(self.ptr_to_mplace(ptr.into(), layout)) } - - /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. - /// Aso returns the vtable. - pub(super) fn unpack_dyn_trait( - &self, - mplace: &MPlaceTy<'tcx, M::Provenance>, - expected_trait: &'tcx ty::List>, - ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, Pointer>)> { - assert!( - matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)), - "`unpack_dyn_trait` only makes sense on `dyn*` types" - ); - let vtable = mplace.meta().unwrap_meta().to_pointer(self)?; - let (ty, vtable_trait) = self.get_ptr_vtable(vtable)?; - if expected_trait.principal() != vtable_trait { - throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); - } - // This is a kind of transmute, from a place with unsized type and metadata to - // a place with sized type and no metadata. - let layout = self.layout_of(ty)?; - let mplace = - MPlaceTy { mplace: MemPlace { meta: MemPlaceMeta::None, ..mplace.mplace }, layout }; - Ok((mplace, vtable)) - } - - /// Turn a `dyn* Trait` type into an value with the actual dynamic type. - /// Also returns the vtable. - pub(super) fn unpack_dyn_star>( - &self, - val: &P, - expected_trait: &'tcx ty::List>, - ) -> InterpResult<'tcx, (P, Pointer>)> { - assert!( - matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)), - "`unpack_dyn_star` only makes sense on `dyn*` types" - ); - let data = self.project_field(val, 0)?; - let vtable = self.project_field(val, 1)?; - let vtable = self.read_pointer(&vtable.to_op(self)?)?; - let (ty, vtable_trait) = self.get_ptr_vtable(vtable)?; - if expected_trait.principal() != vtable_trait { - throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); - } - // `data` is already the right thing but has the wrong type. So we transmute it. - let layout = self.layout_of(ty)?; - let data = data.transmute(layout, self)?; - Ok((data, vtable)) - } } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 0a2fedb48401..efa01b543426 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -18,6 +18,8 @@ use rustc_middle::{bug, span_bug}; use rustc_target::abi::Size; use rustc_target::abi::{self, VariantIdx}; +use tracing::{debug, instrument}; + use super::{ throw_ub, throw_unsup_format, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar, @@ -41,9 +43,9 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug { fn meta(&self) -> MemPlaceMeta; /// Get the length of a slice/string/array stored here. - fn len<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn len>( &self, - ecx: &InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, u64> { let layout = self.layout(); if layout.is_unsized() { @@ -63,29 +65,31 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug { } /// Offset the value by the given amount, replacing the layout and metadata. - fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn offset_with_meta>( &self, offset: Size, mode: OffsetMode, meta: MemPlaceMeta, layout: TyAndLayout<'tcx>, - ecx: &InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self>; - fn offset<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn offset>( &self, offset: Size, layout: TyAndLayout<'tcx>, - ecx: &InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self> { assert!(layout.is_sized()); + // We sometimes do pointer arithmetic with this function, disregarding the source type. + // So we don't check the sizes here. self.offset_with_meta(offset, OffsetMode::Inbounds, MemPlaceMeta::None, layout, ecx) } - fn transmute<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn transmute>( &self, layout: TyAndLayout<'tcx>, - ecx: &InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Self> { assert!(self.layout().is_sized() && layout.is_sized()); assert_eq!(self.layout().size, layout.size); @@ -94,9 +98,9 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug { /// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for /// reading from this thing. - fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + fn to_op>( &self, - ecx: &InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; } @@ -111,9 +115,9 @@ pub struct ArrayIterator<'tcx, 'a, Prov: Provenance, P: Projectable<'tcx, Prov>> impl<'tcx, 'a, Prov: Provenance, P: Projectable<'tcx, Prov>> ArrayIterator<'tcx, 'a, Prov, P> { /// Should be the same `ecx` on each call, and match the one used to create the iterator. - pub fn next<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + pub fn next>( &mut self, - ecx: &InterpCx<'mir, 'tcx, M>, + ecx: &InterpCx<'tcx, M>, ) -> InterpResult<'tcx, Option<(u64, P)>> { let Some(idx) = self.range.next() else { return Ok(None) }; // We use `Wrapping` here since the offset has already been checked when the iterator was created. @@ -131,10 +135,10 @@ impl<'tcx, 'a, Prov: Provenance, P: Projectable<'tcx, Prov>> ArrayIterator<'tcx, } // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 -impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M> +impl<'tcx, Prov, M> InterpCx<'tcx, M> where Prov: Provenance, - M: Machine<'mir, 'tcx, Provenance = Prov>, + M: Machine<'tcx, Provenance = Prov>, { /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is /// always possible without allocating, so it can take `&self`. Also return the field's layout. @@ -296,7 +300,7 @@ where ) -> InterpResult<'tcx, P> { let len = base.len(self)?; // also asserts that we have a type where this makes sense let actual_to = if from_end { - if from.checked_add(to).map_or(true, |to| to > len) { + if from.checked_add(to).is_none_or(|to| to > len) { // This can only be reached in ConstProp and non-rustc-MIR. throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) }); } diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index ee415c380de1..1baf62baa816 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -3,6 +3,7 @@ //! The main entry point is the `step` method. use either::Either; +use tracing::{info, instrument, trace}; use rustc_index::IndexSlice; use rustc_middle::mir; @@ -15,7 +16,7 @@ use super::{ }; use crate::util; -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Returns `true` as long as there are more things to do. /// /// This is used by [priroda](https://github.com/oli-obk/priroda) @@ -167,23 +168,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let left = self.read_immediate(&self.eval_operand(left, layout)?)?; let layout = util::binop_right_homogeneous(bin_op).then_some(left.layout); let right = self.read_immediate(&self.eval_operand(right, layout)?)?; - self.binop_ignore_overflow(bin_op, &left, &right, &dest)?; - } - - CheckedBinaryOp(bin_op, box (ref left, ref right)) => { - // Due to the extra boolean in the result, we can never reuse the `dest.layout`. - let left = self.read_immediate(&self.eval_operand(left, None)?)?; - let layout = util::binop_right_homogeneous(bin_op).then_some(left.layout); - let right = self.read_immediate(&self.eval_operand(right, layout)?)?; - self.binop_with_overflow(bin_op, &left, &right, &dest)?; + let result = self.binary_op(bin_op, &left, &right)?; + assert_eq!(result.layout, dest.layout, "layout mismatch for result of {bin_op:?}"); + self.write_immediate(*result, &dest)?; } UnaryOp(un_op, ref operand) => { // The operand always has the same type as the result. let val = self.read_immediate(&self.eval_operand(operand, Some(dest.layout))?)?; - let val = self.wrapping_unary_op(un_op, &val)?; - assert_eq!(val.layout, dest.layout, "layout mismatch for result of {un_op:?}"); - self.write_immediate(*val, &dest)?; + let result = self.unary_op(un_op, &val)?; + assert_eq!(result.layout, dest.layout, "layout mismatch for result of {un_op:?}"); + self.write_immediate(*result, &dest)?; } Aggregate(box ref kind, ref operands) => { @@ -258,7 +253,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Scalar::from_target_usize(val, self) } mir::NullOp::OffsetOf(fields) => { - let val = layout.offset_of_subfield(self, fields.iter()).bytes(); + let val = self + .tcx + .offset_of_subfield(self.param_env, layout, fields.iter()) + .bytes(); Scalar::from_target_usize(val, self) } mir::NullOp::UbChecks => Scalar::from_bool(self.tcx.sess.ub_checks()), diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index b82c18578588..74521d0f4934 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -1,6 +1,8 @@ use std::borrow::Cow; use either::Either; +use rustc_middle::ty::TyCtxt; +use tracing::trace; use rustc_middle::span_bug; use rustc_middle::{ @@ -45,7 +47,7 @@ impl<'tcx, Prov: Provenance> FnArg<'tcx, Prov> { } } -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Make a copy of the given fn_arg. Any `InPlace` are degenerated to copies, no protection of the /// original memory occurs. pub fn copy_fn_arg(&self, arg: &FnArg<'tcx, M::Provenance>) -> OpTy<'tcx, M::Provenance> { @@ -97,7 +99,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { for (const_int, target) in targets.iter() { // Compare using MIR BinOp::Eq, to also support pointer values. // (Avoiding `self.binary_op` as that does some redundant layout computation.) - let res = self.wrapping_binary_op( + let res = self.binary_op( mir::BinOp::Eq, &discr, &ImmTy::from_uint(const_int, discr.layout), @@ -173,7 +175,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Drop { place, target, unwind, replace: _ } => { let place = self.eval_place(place)?; let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); - if let ty::InstanceDef::DropGlue(_, None) = instance.def { + if let ty::InstanceKind::DropGlue(_, None) = instance.def { // This is the branch we enter if and only if the dropped type has no drop glue // whatsoever. This can happen as a result of monomorphizing a drop of a // generic. In order to make sure that generic and non-generic code behaves @@ -293,17 +295,30 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Unwrap types that are guaranteed a null-pointer-optimization fn unfold_npo(&self, layout: TyAndLayout<'tcx>) -> InterpResult<'tcx, TyAndLayout<'tcx>> { - // Check if this is `Option` wrapping some type. - let inner = match layout.ty.kind() { - ty::Adt(def, args) if self.tcx.is_diagnostic_item(sym::Option, def.did()) => { - args[0].as_type().unwrap() - } - _ => { - // Not an `Option`. - return Ok(layout); - } + // Check if this is `Option` wrapping some type or if this is `Result` wrapping a 1-ZST and + // another type. + let ty::Adt(def, args) = layout.ty.kind() else { + // Not an ADT, so definitely no NPO. + return Ok(layout); }; - let inner = self.layout_of(inner)?; + let inner = if self.tcx.is_diagnostic_item(sym::Option, def.did()) { + // The wrapped type is the only arg. + self.layout_of(args[0].as_type().unwrap())? + } else if self.tcx.is_diagnostic_item(sym::Result, def.did()) { + // We want to extract which (if any) of the args is not a 1-ZST. + let lhs = self.layout_of(args[0].as_type().unwrap())?; + let rhs = self.layout_of(args[1].as_type().unwrap())?; + if lhs.is_1zst() { + rhs + } else if rhs.is_1zst() { + lhs + } else { + return Ok(layout); // no NPO + } + } else { + return Ok(layout); // no NPO + }; + // Check if the inner type is one of the NPO-guaranteed ones. // For that we first unpeel transparent *structs* (but not unions). let is_npo = |def: AdtDef<'tcx>| { @@ -535,7 +550,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; match instance.def { - ty::InstanceDef::Intrinsic(def_id) => { + ty::InstanceKind::Intrinsic(def_id) => { assert!(self.tcx.intrinsic(def_id).is_some()); // FIXME: Should `InPlace` arguments be reset to uninit? if let Some(fallback) = M::call_intrinsic( @@ -547,7 +562,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { unwind, )? { assert!(!self.tcx.intrinsic(fallback.def_id()).unwrap().must_be_overridden); - assert!(matches!(fallback.def, ty::InstanceDef::Item(_))); + assert!(matches!(fallback.def, ty::InstanceKind::Item(_))); return self.eval_fn_call( FnVal::Instance(fallback), (caller_abi, caller_fn_abi), @@ -561,18 +576,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(()) } } - ty::InstanceDef::VTableShim(..) - | ty::InstanceDef::ReifyShim(..) - | ty::InstanceDef::ClosureOnceShim { .. } - | ty::InstanceDef::ConstructCoroutineInClosureShim { .. } - | ty::InstanceDef::CoroutineKindShim { .. } - | ty::InstanceDef::FnPtrShim(..) - | ty::InstanceDef::DropGlue(..) - | ty::InstanceDef::CloneShim(..) - | ty::InstanceDef::FnPtrAddrShim(..) - | ty::InstanceDef::ThreadLocalShim(..) - | ty::InstanceDef::AsyncDropGlueCtorShim(..) - | ty::InstanceDef::Item(_) => { + ty::InstanceKind::VTableShim(..) + | ty::InstanceKind::ReifyShim(..) + | ty::InstanceKind::ClosureOnceShim { .. } + | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } + | ty::InstanceKind::CoroutineKindShim { .. } + | ty::InstanceKind::FnPtrShim(..) + | ty::InstanceKind::DropGlue(..) + | ty::InstanceKind::CloneShim(..) + | ty::InstanceKind::FnPtrAddrShim(..) + | ty::InstanceKind::ThreadLocalShim(..) + | ty::InstanceKind::AsyncDropGlueCtorShim(..) + | ty::InstanceKind::Item(_) => { // We need MIR for this fn let Some((body, instance)) = M::find_mir_or_eval_fn( self, @@ -771,9 +786,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(()) => Ok(()), } } - // `InstanceDef::Virtual` does not have callable MIR. Calls to `Virtual` instances must be + // `InstanceKind::Virtual` does not have callable MIR. Calls to `Virtual` instances must be // codegen'd / interpreted as virtual calls through the vtable. - ty::InstanceDef::Virtual(def_id, idx) => { + ty::InstanceKind::Virtual(def_id, idx) => { let mut args = args.to_vec(); // We have to implement all "object safe receivers". So we have to go search for a // pointer or `dyn Trait` type, but it could be wrapped in newtypes. So recursively @@ -813,20 +828,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; // Obtain the underlying trait we are working on, and the adjusted receiver argument. - let (vptr, dyn_ty, adjusted_receiver) = if let ty::Dynamic(data, _, ty::DynStar) = + let (dyn_trait, dyn_ty, adjusted_recv) = if let ty::Dynamic(data, _, ty::DynStar) = receiver_place.layout.ty.kind() { - let (recv, vptr) = self.unpack_dyn_star(&receiver_place, data)?; - let (dyn_ty, _dyn_trait) = self.get_ptr_vtable(vptr)?; + let recv = self.unpack_dyn_star(&receiver_place, data)?; - (vptr, dyn_ty, recv.ptr()) + (data.principal(), recv.layout.ty, recv.ptr()) } else { // Doesn't have to be a `dyn Trait`, but the unsized tail must be `dyn Trait`. // (For that reason we also cannot use `unpack_dyn_trait`.) let receiver_tail = self .tcx .struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env); - let ty::Dynamic(data, _, ty::Dyn) = receiver_tail.kind() else { + let ty::Dynamic(receiver_trait, _, ty::Dyn) = receiver_tail.kind() else { span_bug!( self.cur_span(), "dynamic call on non-`dyn` type {}", @@ -837,25 +851,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Get the required information from the vtable. let vptr = receiver_place.meta().unwrap_meta().to_pointer(self)?; - let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?; - if dyn_trait != data.principal() { - throw_ub!(InvalidVTableTrait { - expected_trait: data, - vtable_trait: dyn_trait, - }); - } + let dyn_ty = self.get_ptr_vtable_ty(vptr, Some(receiver_trait))?; // It might be surprising that we use a pointer as the receiver even if this // is a by-val case; this works because by-val passing of an unsized `dyn // Trait` to a function is actually desugared to a pointer. - (vptr, dyn_ty, receiver_place.ptr()) + (receiver_trait.principal(), dyn_ty, receiver_place.ptr()) }; // Now determine the actual method to call. We can do that in two different ways and // compare them to ensure everything fits. - let Some(ty::VtblEntry::Method(fn_inst)) = - self.get_vtable_entries(vptr)?.get(idx).copied() - else { + let vtable_entries = if let Some(dyn_trait) = dyn_trait { + let trait_ref = dyn_trait.with_self_ty(*self.tcx, dyn_ty); + let trait_ref = self.tcx.erase_regions(trait_ref); + self.tcx.vtable_entries(trait_ref) + } else { + TyCtxt::COMMON_VTABLE_ENTRIES + }; + let Some(ty::VtblEntry::Method(fn_inst)) = vtable_entries.get(idx).copied() else { // FIXME(fee1-dead) these could be variants of the UB info enum instead of this throw_ub_custom!(fluent::const_eval_dyn_call_not_a_method); }; @@ -884,7 +897,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let receiver_ty = Ty::new_mut_ptr(self.tcx.tcx, dyn_ty); args[0] = FnArg::Copy( ImmTy::from_immediate( - Scalar::from_maybe_pointer(adjusted_receiver, self).into(), + Scalar::from_maybe_pointer(adjusted_recv, self).into(), self.layout_of(receiver_ty)?, ) .into(), @@ -952,7 +965,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let place = self.force_allocation(place)?; // We behave a bit different from codegen here. - // Codegen creates an `InstanceDef::Virtual` with index 0 (the slot of the drop method) and + // Codegen creates an `InstanceKind::Virtual` with index 0 (the slot of the drop method) and // then dispatches that to the normal call machinery. However, our call machinery currently // only supports calling `VtblEntry::Method`; it would choke on a `MetadataDropInPlace`. So // instead we do the virtual call stuff ourselves. It's easier here than in `eval_fn_call` @@ -960,11 +973,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let place = match place.layout.ty.kind() { ty::Dynamic(data, _, ty::Dyn) => { // Dropping a trait object. Need to find actual drop fn. - self.unpack_dyn_trait(&place, data)?.0 + self.unpack_dyn_trait(&place, data)? } ty::Dynamic(data, _, ty::DynStar) => { // Dropping a `dyn*`. Need to find actual drop fn. - self.unpack_dyn_star(&place, data)?.0 + self.unpack_dyn_star(&place, data)? } _ => { debug_assert_eq!( diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index b603ef0d27a4..bd2c65194218 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -1,12 +1,16 @@ +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::ObligationCause; use rustc_middle::mir::interpret::{InterpResult, Pointer}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty}; use rustc_target::abi::{Align, Size}; +use rustc_trait_selection::traits::ObligationCtxt; +use tracing::trace; use super::util::ensure_monomorphic_enough; -use super::{InterpCx, Machine}; +use super::{throw_ub, InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable}; -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for /// objects. /// @@ -32,28 +36,90 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(vtable_ptr.into()) } - /// Returns a high-level representation of the entries of the given vtable. - pub fn get_vtable_entries( - &self, - vtable: Pointer>, - ) -> InterpResult<'tcx, &'tcx [ty::VtblEntry<'tcx>]> { - let (ty, poly_trait_ref) = self.get_ptr_vtable(vtable)?; - Ok(if let Some(poly_trait_ref) = poly_trait_ref { - let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty); - let trait_ref = self.tcx.erase_regions(trait_ref); - self.tcx.vtable_entries(trait_ref) - } else { - TyCtxt::COMMON_VTABLE_ENTRIES - }) - } - pub fn get_vtable_size_and_align( &self, vtable: Pointer>, + expected_trait: Option<&'tcx ty::List>>, ) -> InterpResult<'tcx, (Size, Align)> { - let (ty, _trait_ref) = self.get_ptr_vtable(vtable)?; + let ty = self.get_ptr_vtable_ty(vtable, expected_trait)?; let layout = self.layout_of(ty)?; assert!(layout.is_sized(), "there are no vtables for unsized types"); Ok((layout.size, layout.align.abi)) } + + /// Check that the given vtable trait is valid for a pointer/reference/place with the given + /// expected trait type. + pub(super) fn check_vtable_for_type( + &self, + vtable_trait: Option>, + expected_trait: &'tcx ty::List>, + ) -> InterpResult<'tcx> { + // Fast path: if they are equal, it's all fine. + if expected_trait.principal() == vtable_trait { + return Ok(()); + } + if let (Some(expected_trait), Some(vtable_trait)) = + (expected_trait.principal(), vtable_trait) + { + // Slow path: spin up an inference context to check if these traits are sufficiently equal. + let infcx = self.tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + let cause = ObligationCause::dummy_with_span(self.cur_span()); + // equate the two trait refs after normalization + let expected_trait = ocx.normalize(&cause, self.param_env, expected_trait); + let vtable_trait = ocx.normalize(&cause, self.param_env, vtable_trait); + if ocx.eq(&cause, self.param_env, expected_trait, vtable_trait).is_ok() { + if ocx.select_all_or_error().is_empty() { + // All good. + return Ok(()); + } + } + } + throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); + } + + /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. + pub(super) fn unpack_dyn_trait( + &self, + mplace: &MPlaceTy<'tcx, M::Provenance>, + expected_trait: &'tcx ty::List>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { + assert!( + matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)), + "`unpack_dyn_trait` only makes sense on `dyn*` types" + ); + let vtable = mplace.meta().unwrap_meta().to_pointer(self)?; + let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?; + // This is a kind of transmute, from a place with unsized type and metadata to + // a place with sized type and no metadata. + let layout = self.layout_of(ty)?; + let mplace = mplace.offset_with_meta( + Size::ZERO, + OffsetMode::Wrapping, + MemPlaceMeta::None, + layout, + self, + )?; + Ok(mplace) + } + + /// Turn a `dyn* Trait` type into an value with the actual dynamic type. + pub(super) fn unpack_dyn_star>( + &self, + val: &P, + expected_trait: &'tcx ty::List>, + ) -> InterpResult<'tcx, P> { + assert!( + matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)), + "`unpack_dyn_star` only makes sense on `dyn*` types" + ); + let data = self.project_field(val, 0)?; + let vtable = self.project_field(val, 1)?; + let vtable = self.read_pointer(&vtable.to_op(self)?)?; + let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?; + // `data` is already the right thing but has the wrong type. So we transmute it. + let layout = self.layout_of(ty)?; + let data = data.transmute(layout, self)?; + Ok(data) + } } diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index e304d1e1cc5e..bbe5e5fe3edb 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -1,4 +1,4 @@ -use crate::const_eval::{CompileTimeEvalContext, CompileTimeInterpreter, InterpretationResult}; +use crate::const_eval::{CompileTimeInterpCx, CompileTimeMachine, InterpretationResult}; use rustc_hir::def_id::LocalDefId; use rustc_middle::mir; use rustc_middle::mir::interpret::{Allocation, InterpResult, Pointer}; @@ -7,6 +7,7 @@ use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; use std::ops::ControlFlow; +use tracing::debug; use super::{throw_inval, InterpCx, MPlaceTy, MemPlaceMeta, MemoryKind}; @@ -43,7 +44,7 @@ where | ty::CoroutineClosure(def_id, args, ..) | ty::Coroutine(def_id, args, ..) | ty::FnDef(def_id, args) => { - let instance = ty::InstanceDef::Item(def_id); + let instance = ty::InstanceKind::Item(def_id); let unused_params = self.tcx.unused_generic_params(instance); for (index, arg) in args.into_iter().enumerate() { let index = index @@ -81,9 +82,9 @@ where } impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> { - fn make_result<'mir>( + fn make_result( mplace: MPlaceTy<'tcx>, - ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>, ) -> Self { let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); let alloc = ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1; @@ -91,8 +92,8 @@ impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> } } -pub(crate) fn create_static_alloc<'mir, 'tcx: 'mir>( - ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, +pub(crate) fn create_static_alloc<'tcx>( + ecx: &mut CompileTimeInterpCx<'tcx>, static_def_id: LocalDefId, layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 2bd4d9dc07a5..ca8b98849338 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -8,6 +8,7 @@ use std::fmt::Write; use std::num::NonZero; use either::{Left, Right}; +use tracing::trace; use hir::def::DefKind; use rustc_ast::Mutability; @@ -28,7 +29,7 @@ use rustc_target::abi::{ use std::hash::Hash; use super::{ - err_ub, format_interp_error, machine::AllocMap, throw_ub, AllocId, CheckInAllocMsg, + err_ub, format_interp_error, machine::AllocMap, throw_ub, AllocId, AllocKind, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar, ValueVisitor, }; @@ -204,7 +205,7 @@ fn write_path(out: &mut String, path: &[PathElem]) { } } -struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { +struct ValidityVisitor<'rt, 'tcx, M: Machine<'tcx>> { /// The `path` may be pushed to, but the part that is present when a function /// starts must not be changed! `visit_fields` and `visit_array` rely on /// this stack discipline. @@ -212,10 +213,10 @@ struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { ref_tracking: Option<&'rt mut RefTracking, Vec>>, /// `None` indicates this is not validating for CTFE (but for runtime). ctfe_mode: Option, - ecx: &'rt InterpCx<'mir, 'tcx, M>, + ecx: &'rt InterpCx<'tcx, M>, } -impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M> { +impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { fn aggregate_field_path_elem(&mut self, layout: TyAndLayout<'tcx>, field: usize) -> PathElem { // First, check if we are projecting to a variant. match layout.variants { @@ -342,20 +343,16 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' match tail.kind() { ty::Dynamic(data, _, ty::Dyn) => { let vtable = meta.unwrap_meta().to_pointer(self.ecx)?; - // Make sure it is a genuine vtable pointer. - let (_dyn_ty, dyn_trait) = try_validation!( - self.ecx.get_ptr_vtable(vtable), + // Make sure it is a genuine vtable pointer for the right trait. + try_validation!( + self.ecx.get_ptr_vtable_ty(vtable, Some(data)), self.path, Ub(DanglingIntPointer(..) | InvalidVTablePointer(..)) => - InvalidVTablePtr { value: format!("{vtable}") } + InvalidVTablePtr { value: format!("{vtable}") }, + Ub(InvalidVTableTrait { expected_trait, vtable_trait }) => { + InvalidMetaWrongTrait { expected_trait, vtable_trait: *vtable_trait } + }, ); - // Make sure it is for the right trait. - if dyn_trait != data.principal() { - throw_validation_failure!( - self.path, - InvalidMetaWrongTrait { expected_trait: data, vtable_trait: dyn_trait } - ); - } } ty::Slice(..) | ty::Str => { let _len = meta.unwrap_meta().to_target_usize(self.ecx)?; @@ -416,8 +413,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' Ub(PointerOutOfBounds { .. }) => DanglingPtrOutOfBounds { ptr_kind }, - // This cannot happen during const-eval (because interning already detects - // dangling pointers), but it can happen in Miri. Ub(PointerUseAfterFree(..)) => DanglingPtrUseAfterFree { ptr_kind, }, @@ -434,6 +429,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' found_bytes: has.bytes() }, ); + // Make sure this is non-null. We checked dereferenceability above, but if `size` is zero + // that does not imply non-null. + if self.ecx.scalar_may_be_null(Scalar::from_maybe_pointer(place.ptr(), self.ecx))? { + throw_validation_failure!(self.path, NullPtr { ptr_kind }) + } // Do not allow pointers to uninhabited types. if place.layout.abi.is_uninhabited() { let ty = place.layout.ty; @@ -456,8 +456,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // `!` is a ZST and we want to validate it. if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) { let mut skip_recursive_check = false; - let alloc_actual_mutbl = mutability(self.ecx, alloc_id); - if let GlobalAlloc::Static(did) = self.ecx.tcx.global_alloc(alloc_id) { + if let Some(GlobalAlloc::Static(did)) = self.ecx.tcx.try_get_global_alloc(alloc_id) + { let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { bug!() }; // Special handling for pointers to statics (irrespective of their type). assert!(!self.ecx.tcx.is_thread_local_static(did)); @@ -491,10 +491,19 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' } } - // Mutability check. + // Dangling and Mutability check. + let (size, _align, alloc_kind) = self.ecx.get_alloc_info(alloc_id); + if alloc_kind == AllocKind::Dead { + // This can happen for zero-sized references. We can't have *any* references to non-existing + // allocations though, interning rejects them all as the rest of rustc isn't happy with them... + // so we throw an error, even though this isn't really UB. + // A potential future alternative would be to resurrect this as a zero-sized allocation + // (which codegen will then compile to an aligned dummy pointer anyway). + throw_validation_failure!(self.path, DanglingPtrUseAfterFree { ptr_kind }); + } // If this allocation has size zero, there is no actual mutability here. - let (size, _align, _alloc_kind) = self.ecx.get_alloc_info(alloc_id); if size != Size::ZERO { + let alloc_actual_mutbl = mutability(self.ecx, alloc_id); // Mutable pointer to immutable memory is no good. if ptr_expected_mutbl == Mutability::Mut && alloc_actual_mutbl == Mutability::Not @@ -646,8 +655,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' let WrappingRange { start, end } = valid_range; let max_value = size.unsigned_int_max(); assert!(end <= max_value); - let bits = match scalar.try_to_int() { - Ok(int) => int.assert_bits(size), + let bits = match scalar.try_to_scalar_int() { + Ok(int) => int.to_bits(size), Err(_) => { // So this is a pointer then, and casting to an int failed. // Can only happen during CTFE. @@ -699,15 +708,14 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' /// Returns whether the allocation is mutable, and whether it's actually a static. /// For "root" statics we look at the type to account for interior /// mutability; for nested statics we have no type and directly use the annotated mutability. -fn mutability<'mir, 'tcx: 'mir>( - ecx: &InterpCx<'mir, 'tcx, impl Machine<'mir, 'tcx>>, - alloc_id: AllocId, -) -> Mutability { +fn mutability<'tcx>(ecx: &InterpCx<'tcx, impl Machine<'tcx>>, alloc_id: AllocId) -> Mutability { // Let's see what kind of memory this points to. // We're not using `try_global_alloc` since dangling pointers have already been handled. match ecx.tcx.global_alloc(alloc_id) { GlobalAlloc::Static(did) => { - let DefKind::Static { mutability, nested } = ecx.tcx.def_kind(did) else { bug!() }; + let DefKind::Static { safety: _, mutability, nested } = ecx.tcx.def_kind(did) else { + bug!() + }; if nested { assert!( ecx.memory.alloc_map.get(alloc_id).is_none(), @@ -744,13 +752,11 @@ fn mutability<'mir, 'tcx: 'mir>( } } -impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> - for ValidityVisitor<'rt, 'mir, 'tcx, M> -{ +impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, 'tcx, M> { type V = OpTy<'tcx, M::Provenance>; #[inline(always)] - fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> { + fn ecx(&self) -> &InterpCx<'tcx, M> { self.ecx } @@ -831,6 +837,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> trace!("visit_value: {:?}, {:?}", *op, op.layout); // Check primitive types -- the leaves of our recursive descent. + // We assume that the Scalar validity range does not restrict these values + // any further than `try_visit_primitive` does! if self.try_visit_primitive(op)? { return Ok(()); } @@ -1000,7 +1008,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } } -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { +impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn validate_operand_internal( &self, op: &OpTy<'tcx, M::Provenance>, diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index 59bcc5174cb2..71c057e549b4 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -6,17 +6,18 @@ use rustc_middle::mir::interpret::InterpResult; use rustc_middle::ty::{self, Ty}; use rustc_target::abi::FieldIdx; use rustc_target::abi::{FieldsShape, VariantIdx, Variants}; +use tracing::trace; use std::num::NonZero; use super::{throw_inval, InterpCx, MPlaceTy, Machine, Projectable}; /// How to traverse a value and what to do when we are at the leaves. -pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { +pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { type V: Projectable<'tcx, M::Provenance> + From>; /// The visitor must have an `InterpCx` in it. - fn ecx(&self) -> &InterpCx<'mir, 'tcx, M>; + fn ecx(&self) -> &InterpCx<'tcx, M>; /// `read_discriminant` can be hooked for better error messages. #[inline(always)] @@ -94,7 +95,7 @@ pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { // unsized values are never immediate, so we can assert_mem_place let op = v.to_op(self.ecx())?; let dest = op.assert_mem_place(); - let inner_mplace = self.ecx().unpack_dyn_trait(&dest, data)?.0; + let inner_mplace = self.ecx().unpack_dyn_trait(&dest, data)?; trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout); // recurse with the inner type return self.visit_field(v, 0, &inner_mplace.into()); @@ -103,7 +104,7 @@ pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { // DynStar types. Very different from a dyn type (but strangely part of the // same variant in `TyKind`): These are pairs where the 2nd component is the // vtable, and the first component is the data (which must be ptr-sized). - let data = self.ecx().unpack_dyn_star(v, data)?.0; + let data = self.ecx().unpack_dyn_star(v, data)?; return self.visit_field(v, 0, &data); } // Slices do not need special handling here: they have `Array` field diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index a525b838afa2..50a4d0612ccd 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -1,32 +1,26 @@ -/*! - -Rust MIR: a lowered representation of Rust. - -*/ - +// tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] -#![feature(rustdoc_internals)] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(decl_macro)] +#![feature(if_let_guard)] +#![feature(is_none_or)] #![feature(let_chains)] +#![feature(never_type)] +#![feature(rustdoc_internals)] #![feature(slice_ptr_get)] #![feature(strict_provenance)] -#![feature(never_type)] #![feature(trait_alias)] #![feature(try_blocks)] #![feature(yeet_expr)] -#![feature(if_let_guard)] - -#[macro_use] -extern crate tracing; +// tidy-alphabetical-end +pub mod check_consts; pub mod const_eval; mod errors; pub mod interpret; -pub mod transform; pub mod util; use std::sync::atomic::AtomicBool; diff --git a/compiler/rustc_const_eval/src/transform/mod.rs b/compiler/rustc_const_eval/src/transform/mod.rs deleted file mode 100644 index e3582c7d3174..000000000000 --- a/compiler/rustc_const_eval/src/transform/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod check_consts; -pub mod validate; diff --git a/compiler/rustc_const_eval/src/util/alignment.rs b/compiler/rustc_const_eval/src/util/alignment.rs index 8642dfccd784..528274e6abac 100644 --- a/compiler/rustc_const_eval/src/util/alignment.rs +++ b/compiler/rustc_const_eval/src/util/alignment.rs @@ -1,6 +1,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; use rustc_target::abi::Align; +use tracing::debug; /// Returns `true` if this place is allowed to be less aligned /// than its containing struct (because it is within a packed diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs index 403bc1eca134..3b07bee2d9c2 100644 --- a/compiler/rustc_const_eval/src/util/caller_location.rs +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -5,13 +5,14 @@ use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Mutability}; use rustc_span::symbol::Symbol; +use tracing::trace; -use crate::const_eval::{mk_eval_cx_to_read_const_val, CanAccessMutGlobal, CompileTimeEvalContext}; +use crate::const_eval::{mk_eval_cx_to_read_const_val, CanAccessMutGlobal, CompileTimeInterpCx}; use crate::interpret::*; /// Allocate a `const core::panic::Location` with the provided filename and line/column numbers. -fn alloc_caller_location<'mir, 'tcx>( - ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, +fn alloc_caller_location<'tcx>( + ecx: &mut CompileTimeInterpCx<'tcx>, filename: Symbol, line: u32, col: u32, diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 68fb122a765d..daf57285ebe6 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -3,7 +3,7 @@ use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, Val use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt}; use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants}; -use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeInterpreter}; +use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeMachine}; use crate::interpret::{InterpCx, MemoryKind, OpTy}; /// Determines if this type permits "raw" initialization by just transmuting some memory into an @@ -45,7 +45,7 @@ fn might_permit_raw_init_strict<'tcx>( tcx: TyCtxt<'tcx>, kind: ValidityRequirement, ) -> Result> { - let machine = CompileTimeInterpreter::new(CanAccessMutGlobal::No, CheckAlignment::Error); + let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error); let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine); diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index 0c3b59a0e78e..66a1addfb525 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -19,7 +19,9 @@ pub fn binop_left_homogeneous(op: mir::BinOp) -> bool { match op { Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => true, - Eq | Ne | Lt | Le | Gt | Ge | Cmp => false, + AddWithOverflow | SubWithOverflow | MulWithOverflow | Eq | Ne | Lt | Le | Gt | Ge | Cmp => { + false + } } } @@ -29,8 +31,9 @@ pub fn binop_left_homogeneous(op: mir::BinOp) -> bool { pub fn binop_right_homogeneous(op: mir::BinOp) -> bool { use rustc_middle::mir::BinOp::*; match op { - Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor - | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge | Cmp => true, + Add | AddUnchecked | AddWithOverflow | Sub | SubUnchecked | SubWithOverflow | Mul + | MulUnchecked | MulWithOverflow | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt + | Le | Gt | Ge | Cmp => true, Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => false, } } diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 2b61e17efa28..ff0a94f8e9b2 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -12,11 +12,9 @@ elsa = "=1.7.1" ena = "0.14.3" indexmap = { version = "2.0.0" } jobserver_crate = { version = "0.1.28", package = "jobserver" } -libc = "0.2" measureme = "11" rustc-hash = "1.1.0" rustc-rayon = { version = "0.5.0", optional = true } -rustc-rayon-core = { version = "0.5.0", optional = true } rustc_arena = { path = "../rustc_arena" } rustc_graphviz = { path = "../rustc_graphviz" } rustc_index = { path = "../rustc_index", package = "rustc_index" } @@ -42,6 +40,11 @@ features = [ "Win32_System_Threading", ] +[target.'cfg(unix)'.dependencies] +# tidy-alphabetical-start +libc = "0.2" +# tidy-alphabetical-end + [target.'cfg(not(target_arch = "wasm32"))'.dependencies] # tidy-alphabetical-start memmap2 = "0.2.1" @@ -52,5 +55,5 @@ portable-atomic = "1.5.1" [features] # tidy-alphabetical-start -rustc_use_parallel_compiler = ["indexmap/rustc-rayon", "rustc-rayon", "rustc-rayon-core"] +rustc_use_parallel_compiler = ["indexmap/rustc-rayon", "rustc-rayon"] # tidy-alphabetical-end diff --git a/compiler/rustc_data_structures/src/flock.rs b/compiler/rustc_data_structures/src/flock.rs index 008565e4c7b9..e03962a54eca 100644 --- a/compiler/rustc_data_structures/src/flock.rs +++ b/compiler/rustc_data_structures/src/flock.rs @@ -9,6 +9,10 @@ cfg_match! { mod linux; use linux as imp; } + cfg(target_os = "redox") => { + mod linux; + use linux as imp; + } cfg(unix) => { mod unix; use unix as imp; diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 30e240cf85b8..d1d2de670b82 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -93,7 +93,7 @@ fn dominators_impl(graph: &G) -> Inner { // These are all done here rather than through one of the 'standard' // graph traversals to help make this fast. 'recurse: while let Some(frame) = stack.last_mut() { - while let Some(successor) = frame.iter.next() { + for successor in frame.iter.by_ref() { if real_to_pre_order[successor].is_none() { let pre_order_idx = pre_order_to_real.push(successor); real_to_pre_order[successor] = Some(pre_order_idx); diff --git a/compiler/rustc_data_structures/src/graph/iterate/mod.rs b/compiler/rustc_data_structures/src/graph/iterate/mod.rs index 78d05a6e1951..6fca57d32f77 100644 --- a/compiler/rustc_data_structures/src/graph/iterate/mod.rs +++ b/compiler/rustc_data_structures/src/graph/iterate/mod.rs @@ -48,7 +48,7 @@ fn post_order_walk( let node = frame.node; visited[node] = true; - while let Some(successor) = frame.iter.next() { + for successor in frame.iter.by_ref() { if !visited[successor] { stack.push(PostOrderFrame { node: successor, iter: graph.successors(successor) }); continue 'recurse; @@ -112,7 +112,7 @@ where /// This is equivalent to just invoke `next` repeatedly until /// you get a `None` result. pub fn complete_search(&mut self) { - while let Some(_) = self.next() {} + for _ in self.by_ref() {} } /// Returns true if node has been visited thus far. diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index 914a6a16348c..8b96b36a8512 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -4,54 +4,121 @@ //! node in the graph. This uses [Tarjan's algorithm]( //! https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm) //! that completes in *O*(*n*) time. +//! Optionally, also annotate the SCC nodes with some commutative data. +//! Typical examples would include: minimum element in SCC, maximum element +//! reachable from it, etc. use crate::fx::FxHashSet; use crate::graph::vec_graph::VecGraph; use crate::graph::{DirectedGraph, NumEdges, Successors}; use rustc_index::{Idx, IndexSlice, IndexVec}; +use std::fmt::Debug; use std::ops::Range; use tracing::{debug, instrument}; #[cfg(test)] mod tests; +/// An annotation for an SCC. This can be a representative, +/// the max/min element of the SCC, or all of the above. +/// +/// Concretely, the both merge operations must commute, e.g. where `merge` +/// is `merge_scc` and `merge_reached`: `a.merge(b) == b.merge(a)` +/// +/// In general, what you want is probably always min/max according +/// to some ordering, potentially with side constraints (min x such +/// that P holds). +pub trait Annotation: Debug + Copy { + /// Merge two existing annotations into one during + /// path compression.o + fn merge_scc(self, other: Self) -> Self; + + /// Merge a successor into this annotation. + fn merge_reached(self, other: Self) -> Self; + + fn update_scc(&mut self, other: Self) { + *self = self.merge_scc(other) + } + + fn update_reachable(&mut self, other: Self) { + *self = self.merge_reached(other) + } +} + +/// The empty annotation, which does nothing. +impl Annotation for () { + fn merge_reached(self, _other: Self) -> Self { + () + } + fn merge_scc(self, _other: Self) -> Self { + () + } +} + /// Strongly connected components (SCC) of a graph. The type `N` is /// the index type for the graph nodes and `S` is the index type for /// the SCCs. We can map from each node to the SCC that it /// participates in, and we also have the successors of each SCC. -pub struct Sccs { +pub struct Sccs { /// For each node, what is the SCC index of the SCC to which it /// belongs. scc_indices: IndexVec, - /// Data about each SCC. - scc_data: SccData, + /// Data about all the SCCs. + scc_data: SccData, } -pub struct SccData { - /// For each SCC, the range of `all_successors` where its +/// Information about an invidividual SCC node. +struct SccDetails { + /// For this SCC, the range of `all_successors` where its /// successors can be found. - ranges: IndexVec>, + range: Range, + + /// User-specified metadata about the SCC. + annotation: A, +} + +// The name of this struct should discourage you from making it public and leaking +// its representation. This message was left here by one who came before you, +// who learnt the hard way that making even small changes in representation +// is difficult when it's publicly inspectable. +// +// Obey the law of Demeter! +struct SccData { + /// Maps SCC indices to their metadata, including + /// offsets into `all_successors`. + scc_details: IndexVec>, /// Contains the successors for all the Sccs, concatenated. The /// range of indices corresponding to a given SCC is found in its - /// SccData. + /// `scc_details.range`. all_successors: Vec, } -impl Sccs { - pub fn new(graph: &(impl DirectedGraph + Successors)) -> Self { - SccsConstruction::construct(graph) +impl Sccs { + /// Compute SCCs without annotations. + pub fn new(graph: &impl Successors) -> Self { + Self::new_with_annotation(graph, |_| ()) + } +} + +impl Sccs { + /// Compute SCCs and annotate them with a user-supplied annotation + pub fn new_with_annotation A>( + graph: &impl Successors, + to_annotation: F, + ) -> Self { + SccsConstruction::construct(graph, to_annotation) + } + + pub fn annotation(&self, scc: S) -> A { + self.scc_data.annotation(scc) } pub fn scc_indices(&self) -> &IndexSlice { &self.scc_indices } - pub fn scc_data(&self) -> &SccData { - &self.scc_data - } - /// Returns the number of SCCs in the graph. pub fn num_sccs(&self) -> usize { self.scc_data.len() @@ -90,7 +157,7 @@ impl Sccs { } } -impl DirectedGraph for Sccs { +impl DirectedGraph for Sccs { type Node = S; fn num_nodes(&self) -> usize { @@ -98,43 +165,33 @@ impl DirectedGraph for Sccs { } } -impl NumEdges for Sccs { +impl NumEdges for Sccs { fn num_edges(&self) -> usize { self.scc_data.all_successors.len() } } -impl Successors for Sccs { +impl Successors for Sccs { fn successors(&self, node: S) -> impl Iterator { self.successors(node).iter().cloned() } } -impl SccData { +impl SccData { /// Number of SCCs, fn len(&self) -> usize { - self.ranges.len() - } - - pub fn ranges(&self) -> &IndexSlice> { - &self.ranges - } - - pub fn all_successors(&self) -> &Vec { - &self.all_successors + self.scc_details.len() } /// Returns the successors of the given SCC. fn successors(&self, scc: S) -> &[S] { - // Annoyingly, `range` does not implement `Copy`, so we have - // to do `range.start..range.end`: - let range = &self.ranges[scc]; - &self.all_successors[range.start..range.end] + &self.all_successors[self.scc_details[scc].range.clone()] } /// Creates a new SCC with `successors` as its successors and + /// the maximum weight of its internal nodes `scc_max_weight` and /// returns the resulting index. - fn create_scc(&mut self, successors: impl IntoIterator) -> S { + fn create_scc(&mut self, successors: impl IntoIterator, annotation: A) -> S { // Store the successors on `scc_successors_vec`, remembering // the range of indices. let all_successors_start = self.all_successors.len(); @@ -142,22 +199,35 @@ impl SccData { let all_successors_end = self.all_successors.len(); debug!( - "create_scc({:?}) successors={:?}", - self.ranges.len(), + "create_scc({:?}) successors={:?}, annotation={:?}", + self.len(), &self.all_successors[all_successors_start..all_successors_end], + annotation ); - self.ranges.push(all_successors_start..all_successors_end) + let range = all_successors_start..all_successors_end; + let metadata = SccDetails { range, annotation }; + self.scc_details.push(metadata) + } + + fn annotation(&self, scc: S) -> A { + self.scc_details[scc].annotation } } -struct SccsConstruction<'c, G: DirectedGraph + Successors, S: Idx> { +struct SccsConstruction<'c, G, S, A, F> +where + G: DirectedGraph + Successors, + S: Idx, + A: Annotation, + F: Fn(G::Node) -> A, +{ graph: &'c G, /// The state of each node; used during walk to record the stack /// and after walk to record what cycle each node ended up being /// in. - node_states: IndexVec>, + node_states: IndexVec>, /// The stack of nodes that we are visiting as part of the DFS. node_stack: Vec, @@ -174,26 +244,34 @@ struct SccsConstruction<'c, G: DirectedGraph + Successors, S: Idx> { /// around between successors to amortize memory allocation costs. duplicate_set: FxHashSet, - scc_data: SccData, + scc_data: SccData, + + /// A function that constructs an initial SCC annotation + /// out of a single node. + to_annotation: F, } #[derive(Copy, Clone, Debug)] -enum NodeState { +enum NodeState { /// This node has not yet been visited as part of the DFS. /// /// After SCC construction is complete, this state ought to be /// impossible. NotVisited, - /// This node is currently being walk as part of our DFS. It is on - /// the stack at the depth `depth`. + /// This node is currently being walked as part of our DFS. It is on + /// the stack at the depth `depth` and its current annotation is + /// `annotation`. /// /// After SCC construction is complete, this state ought to be /// impossible. - BeingVisited { depth: usize }, + BeingVisited { depth: usize, annotation: A }, - /// Indicates that this node is a member of the given cycle. - InCycle { scc_index: S }, + /// Indicates that this node is a member of the given cycle where + /// the merged annotation is `annotation`. + /// Note that an SCC can have several cycles, so its final annotation + /// is the merged value of all its member annotations. + InCycle { scc_index: S, annotation: A }, /// Indicates that this node is a member of whatever cycle /// `parent` is a member of. This state is transient: whenever we @@ -203,16 +281,27 @@ enum NodeState { InCycleWith { parent: N }, } +/// The state of walking a given node. #[derive(Copy, Clone, Debug)] -enum WalkReturn { - Cycle { min_depth: usize }, - Complete { scc_index: S }, +enum WalkReturn { + /// The walk found a cycle, but the entire component is not known to have + /// been fully walked yet. We only know the minimum depth of this + /// component in a minimum spanning tree of the graph. This component + /// is tentatively represented by the state of the first node of this + /// cycle we met, which is at `min_depth`. + Cycle { min_depth: usize, annotation: A }, + /// The SCC and everything reachable from it have been fully walked. + /// At this point we know what is inside the SCC as we have visited every + /// node reachable from it. The SCC can now be fully represented by its ID. + Complete { scc_index: S, annotation: A }, } -impl<'c, G, S> SccsConstruction<'c, G, S> +impl<'c, G, S, A, F> SccsConstruction<'c, G, S, A, F> where G: DirectedGraph + Successors, S: Idx, + F: Fn(G::Node) -> A, + A: Annotation, { /// Identifies SCCs in the graph `G` and computes the resulting /// DAG. This uses a variant of [Tarjan's @@ -225,8 +314,10 @@ where /// D' (i.e., D' < D), we know that N, N', and all nodes in /// between them on the stack are part of an SCC. /// + /// Additionally, we keep track of a current annotation of the SCC. + /// /// [wikipedia]: https://bit.ly/2EZIx84 - fn construct(graph: &'c G) -> Sccs { + fn construct(graph: &'c G, to_annotation: F) -> Sccs { let num_nodes = graph.num_nodes(); let mut this = Self { @@ -234,15 +325,16 @@ where node_states: IndexVec::from_elem_n(NodeState::NotVisited, num_nodes), node_stack: Vec::with_capacity(num_nodes), successors_stack: Vec::new(), - scc_data: SccData { ranges: IndexVec::new(), all_successors: Vec::new() }, + scc_data: SccData { scc_details: IndexVec::new(), all_successors: Vec::new() }, duplicate_set: FxHashSet::default(), + to_annotation, }; let scc_indices = (0..num_nodes) .map(G::Node::new) .map(|node| match this.start_walk_from(node) { - WalkReturn::Complete { scc_index } => scc_index, - WalkReturn::Cycle { min_depth } => { + WalkReturn::Complete { scc_index, .. } => scc_index, + WalkReturn::Cycle { min_depth, .. } => { panic!("`start_walk_node({node:?})` returned cycle with depth {min_depth:?}") } }) @@ -251,12 +343,8 @@ where Sccs { scc_indices, scc_data: this.scc_data } } - fn start_walk_from(&mut self, node: G::Node) -> WalkReturn { - if let Some(result) = self.inspect_node(node) { - result - } else { - self.walk_unvisited_node(node) - } + fn start_walk_from(&mut self, node: G::Node) -> WalkReturn { + self.inspect_node(node).unwrap_or_else(|| self.walk_unvisited_node(node)) } /// Inspect a node during the DFS. We first examine its current @@ -271,11 +359,15 @@ where /// Otherwise, we are looking at a node that has already been /// completely visited. We therefore return `WalkReturn::Complete` /// with its associated SCC index. - fn inspect_node(&mut self, node: G::Node) -> Option> { + fn inspect_node(&mut self, node: G::Node) -> Option> { Some(match self.find_state(node) { - NodeState::InCycle { scc_index } => WalkReturn::Complete { scc_index }, + NodeState::InCycle { scc_index, annotation } => { + WalkReturn::Complete { scc_index, annotation } + } - NodeState::BeingVisited { depth: min_depth } => WalkReturn::Cycle { min_depth }, + NodeState::BeingVisited { depth: min_depth, annotation } => { + WalkReturn::Cycle { min_depth, annotation } + } NodeState::NotVisited => return None, @@ -290,7 +382,7 @@ where /// of `r2` (and updates `r` to reflect current result). This is /// basically the "find" part of a standard union-find algorithm /// (with path compression). - fn find_state(&mut self, mut node: G::Node) -> NodeState { + fn find_state(&mut self, mut node: G::Node) -> NodeState { // To avoid recursion we temporarily reuse the `parent` of each // InCycleWith link to encode a downwards link while compressing // the path. After we have found the root or deepest node being @@ -306,24 +398,40 @@ where // found the initial self-loop. let mut previous_node = node; - // Ultimately assigned by the parent when following + // Ultimately propagated to all the transitive parents when following // `InCycleWith` upwards. - let node_state = loop { - debug!("find_state(r = {:?} in state {:?})", node, self.node_states[node]); - match self.node_states[node] { - NodeState::InCycle { scc_index } => break NodeState::InCycle { scc_index }, - NodeState::BeingVisited { depth } => break NodeState::BeingVisited { depth }, - NodeState::NotVisited => break NodeState::NotVisited, - NodeState::InCycleWith { parent } => { - // We test this, to be extremely sure that we never - // ever break our termination condition for the - // reverse iteration loop. - assert!(node != parent, "Node can not be in cycle with itself"); - // Store the previous node as an inverted list link - self.node_states[node] = NodeState::InCycleWith { parent: previous_node }; - // Update to parent node. - previous_node = node; - node = parent; + // This loop performs the downward link encoding mentioned above. Details below! + // Note that there are two different states being assigned: the root state, and + // a potentially derived version of the root state for non-root nodes in the chain. + let (root_state, assigned_state) = { + loop { + debug!("find_state(r = {node:?} in state {:?})", self.node_states[node]); + match self.node_states[node] { + // This must have been the first and only state since it is unexplored*; + // no update needed! * Unless there is a bug :') + s @ NodeState::NotVisited => return s, + // We are in a completely discovered SCC; every node on our path is in that SCC: + s @ NodeState::InCycle { .. } => break (s, s), + // The Interesting Third Base Case: we are a path back to a root node + // still being explored. Now we need that node to keep its state and + // every other node to be recorded as being in whatever component that + // ends up in. + s @ NodeState::BeingVisited { depth, .. } => { + break (s, NodeState::InCycleWith { parent: self.node_stack[depth] }); + } + // We are not at the head of a path; keep compressing it! + NodeState::InCycleWith { parent } => { + // We test this, to be extremely sure that we never + // ever break our termination condition for the + // reverse iteration loop. + assert!(node != parent, "Node can not be in cycle with itself"); + + // Store the previous node as an inverted list link + self.node_states[node] = NodeState::InCycleWith { parent: previous_node }; + // Update to parent node. + previous_node = node; + node = parent; + } } } }; @@ -365,10 +473,14 @@ where // Move backwards until we found the node where we started. We // will know when we hit the state where previous_node == node. loop { - // Back at the beginning, we can return. + // Back at the beginning, we can return. Note that we return the root state. + // This is becuse for components being explored, we would otherwise get a + // `node_state[n] = InCycleWith{ parent: n }` and that's wrong. if previous_node == node { - return node_state; + return root_state; } + debug!("Compressing {node:?} down to {previous_node:?} with state {assigned_state:?}"); + // Update to previous node in the link. match self.node_states[previous_node] { NodeState::InCycleWith { parent: previous } => { @@ -376,34 +488,14 @@ where previous_node = previous; } // Only InCycleWith nodes were added to the reverse linked list. - other => panic!("Invalid previous link while compressing cycle: {other:?}"), + other => unreachable!("Invalid previous link while compressing cycle: {other:?}"), } - debug!("find_state: parent_state = {:?}", node_state); - - // Update the node state from the parent state. The assigned - // state is actually a loop invariant but it will only be - // evaluated if there is at least one backlink to follow. - // Fully trusting llvm here to find this loop optimization. - match node_state { - // Path compression, make current node point to the same root. - NodeState::InCycle { .. } => { - self.node_states[node] = node_state; - } - // Still visiting nodes, compress to cycle to the node - // at that depth. - NodeState::BeingVisited { depth } => { - self.node_states[node] = - NodeState::InCycleWith { parent: self.node_stack[depth] }; - } - // These are never allowed as parent nodes. InCycleWith - // should have been followed to a real parent and - // NotVisited can not be part of a cycle since it should - // have instead gotten explored. - NodeState::NotVisited | NodeState::InCycleWith { .. } => { - panic!("invalid parent state: {node_state:?}") - } - } + // Update the node state to the (potentially derived) state. + // If the root is still being explored, this is + // `InCycleWith{ parent: }`, otherwise + // `assigned_state == root_state`. + self.node_states[node] = assigned_state; } } @@ -413,30 +505,36 @@ where /// caller decide avoids mutual recursion between the two methods and allows /// us to maintain an allocated stack for nodes on the path between calls. #[instrument(skip(self, initial), level = "debug")] - fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn { - struct VisitingNodeFrame { + fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn { + debug!("Walk unvisited node: {initial:?}"); + struct VisitingNodeFrame { node: G::Node, - iter: Option, + successors: Option, depth: usize, min_depth: usize, successors_len: usize, min_cycle_root: G::Node, successor_node: G::Node, + /// The annotation for the SCC starting in `node`. It may or may + /// not contain other nodes. + current_component_annotation: A, } // Move the stack to a local variable. We want to utilize the existing allocation and // mutably borrow it without borrowing self at the same time. let mut successors_stack = core::mem::take(&mut self.successors_stack); + debug_assert_eq!(successors_stack.len(), 0); - let mut stack: Vec> = vec![VisitingNodeFrame { + let mut stack: Vec> = vec![VisitingNodeFrame { node: initial, depth: 0, min_depth: 0, - iter: None, + successors: None, successors_len: 0, min_cycle_root: initial, successor_node: initial, + current_component_annotation: (self.to_annotation)(initial), }]; let mut return_value = None; @@ -445,18 +543,26 @@ where let VisitingNodeFrame { node, depth, - iter, + successors, successors_len, min_depth, min_cycle_root, successor_node, + current_component_annotation, } = frame; - let node = *node; let depth = *depth; - let successors = match iter { - Some(iter) => iter, + // node is definitely in the current component, add it to the annotation. + if node != initial { + current_component_annotation.update_scc((self.to_annotation)(node)); + } + debug!( + "Visiting {node:?} at depth {depth:?}, annotation: {current_component_annotation:?}" + ); + + let successors = match successors { + Some(successors) => successors, None => { // This None marks that we still have the initialize this node's frame. debug!(?depth, ?node); @@ -464,7 +570,10 @@ where debug_assert!(matches!(self.node_states[node], NodeState::NotVisited)); // Push `node` onto the stack. - self.node_states[node] = NodeState::BeingVisited { depth }; + self.node_states[node] = NodeState::BeingVisited { + depth, + annotation: *current_component_annotation, + }; self.node_stack.push(node); // Walk each successor of the node, looking to see if any of @@ -472,11 +581,11 @@ where // so, that means they can also reach us. *successors_len = successors_stack.len(); // Set and return a reference, this is currently empty. - iter.get_or_insert(self.graph.successors(node)) + successors.get_or_insert(self.graph.successors(node)) } }; - // Now that iter is initialized, this is a constant for this frame. + // Now that the successors iterator is initialized, this is a constant for this frame. let successors_len = *successors_len; // Construct iterators for the nodes and walk results. There are two cases: @@ -489,10 +598,17 @@ where debug!(?node, ?successor_node); (successor_node, self.inspect_node(successor_node)) }); - for (successor_node, walk) in returned_walk.chain(successor_walk) { match walk { - Some(WalkReturn::Cycle { min_depth: successor_min_depth }) => { + // The starting node `node` leads to a cycle whose earliest node, + // `successor_node`, is at `min_depth`. There may be more cycles. + Some(WalkReturn::Cycle { + min_depth: successor_min_depth, + annotation: successor_annotation, + }) => { + debug!( + "Cycle found from {node:?}, minimum depth: {successor_min_depth:?}, annotation: {successor_annotation:?}" + ); // Track the minimum depth we can reach. assert!(successor_min_depth <= depth); if successor_min_depth < *min_depth { @@ -500,41 +616,56 @@ where *min_depth = successor_min_depth; *min_cycle_root = successor_node; } + current_component_annotation.update_scc(successor_annotation); } - - Some(WalkReturn::Complete { scc_index: successor_scc_index }) => { + // The starting node `node` is succeeded by a fully identified SCC + // which is now added to the set under `scc_index`. + Some(WalkReturn::Complete { + scc_index: successor_scc_index, + annotation: successor_annotation, + }) => { + debug!( + "Complete; {node:?} is root of complete-visited SCC idx {successor_scc_index:?} with annotation {successor_annotation:?}" + ); // Push the completed SCC indices onto // the `successors_stack` for later. debug!(?node, ?successor_scc_index); successors_stack.push(successor_scc_index); + current_component_annotation.update_reachable(successor_annotation); } - + // `node` has no more (direct) successors; search recursively. None => { let depth = depth + 1; + debug!("Recursing down into {successor_node:?} at depth {depth:?}"); debug!(?depth, ?successor_node); // Remember which node the return value will come from. frame.successor_node = successor_node; - // Start a new stack frame the step into it. + // Start a new stack frame, then step into it. stack.push(VisitingNodeFrame { node: successor_node, depth, - iter: None, + successors: None, successors_len: 0, min_depth: depth, min_cycle_root: successor_node, successor_node, + current_component_annotation: (self.to_annotation)(successor_node), }); continue 'recurse; } } } + debug!("Finished walk from {node:?} with annotation: {current_component_annotation:?}"); + // Completed walk, remove `node` from the stack. let r = self.node_stack.pop(); debug_assert_eq!(r, Some(node)); // Remove the frame, it's done. let frame = stack.pop().unwrap(); + let current_component_annotation = frame.current_component_annotation; + debug_assert_eq!(frame.node, node); // If `min_depth == depth`, then we are the root of the // cycle: we can't reach anyone further down the stack. @@ -543,6 +674,8 @@ where // We return one frame at a time so there can't be another return value. debug_assert!(return_value.is_none()); return_value = Some(if frame.min_depth == depth { + // We are at the head of the component. + // Note that successor stack may have duplicates, so we // want to remove those: let deduplicated_successors = { @@ -552,15 +685,25 @@ where .drain(successors_len..) .filter(move |&i| duplicate_set.insert(i)) }; - let scc_index = self.scc_data.create_scc(deduplicated_successors); - self.node_states[node] = NodeState::InCycle { scc_index }; - WalkReturn::Complete { scc_index } + + debug!("Creating SCC rooted in {node:?} with successor {:?}", frame.successor_node); + + let scc_index = + self.scc_data.create_scc(deduplicated_successors, current_component_annotation); + + self.node_states[node] = + NodeState::InCycle { scc_index, annotation: current_component_annotation }; + + WalkReturn::Complete { scc_index, annotation: current_component_annotation } } else { // We are not the head of the cycle. Return back to our // caller. They will take ownership of the // `self.successors` data that we pushed. self.node_states[node] = NodeState::InCycleWith { parent: frame.min_cycle_root }; - WalkReturn::Cycle { min_depth: frame.min_depth } + WalkReturn::Cycle { + min_depth: frame.min_depth, + annotation: current_component_annotation, + } }); } diff --git a/compiler/rustc_data_structures/src/graph/scc/tests.rs b/compiler/rustc_data_structures/src/graph/scc/tests.rs index 513df666d0da..373f87bfdbcf 100644 --- a/compiler/rustc_data_structures/src/graph/scc/tests.rs +++ b/compiler/rustc_data_structures/src/graph/scc/tests.rs @@ -3,10 +3,53 @@ extern crate test; use super::*; use crate::graph::tests::TestGraph; +#[derive(Copy, Clone, Debug)] +struct MaxReached(usize); +type UsizeSccs = Sccs; +type MaxReachedSccs = Sccs; + +impl Annotation for MaxReached { + fn merge_scc(self, other: Self) -> Self { + Self(std::cmp::max(other.0, self.0)) + } + + fn merge_reached(self, other: Self) -> Self { + self.merge_scc(other) + } +} + +impl PartialEq for MaxReached { + fn eq(&self, other: &usize) -> bool { + &self.0 == other + } +} + +impl MaxReached { + fn from_usize(nr: usize) -> Self { + Self(nr) + } +} + +#[derive(Copy, Clone, Debug)] +struct MinMaxIn { + min: usize, + max: usize, +} + +impl Annotation for MinMaxIn { + fn merge_scc(self, other: Self) -> Self { + Self { min: std::cmp::min(self.min, other.min), max: std::cmp::max(self.max, other.max) } + } + + fn merge_reached(self, _other: Self) -> Self { + self + } +} + #[test] fn diamond() { let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3)]); - let sccs: Sccs<_, usize> = Sccs::new(&graph); + let sccs: UsizeSccs = Sccs::new(&graph); assert_eq!(sccs.num_sccs(), 4); assert_eq!(sccs.num_sccs(), 4); } @@ -34,7 +77,7 @@ fn test_big_scc() { +-- 2 <--+ */ let graph = TestGraph::new(0, &[(0, 1), (1, 2), (1, 3), (2, 0), (3, 2)]); - let sccs: Sccs<_, usize> = Sccs::new(&graph); + let sccs: UsizeSccs = Sccs::new(&graph); assert_eq!(sccs.num_sccs(), 1); } @@ -50,7 +93,7 @@ fn test_three_sccs() { +-- 2 <--+ */ let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 1), (3, 2)]); - let sccs: Sccs<_, usize> = Sccs::new(&graph); + let sccs: UsizeSccs = Sccs::new(&graph); assert_eq!(sccs.num_sccs(), 3); assert_eq!(sccs.scc(0), 1); assert_eq!(sccs.scc(1), 0); @@ -106,7 +149,7 @@ fn test_find_state_2() { // 2 InCycleWith { 1 } // 3 InCycleWith { 0 } - let sccs: Sccs<_, usize> = Sccs::new(&graph); + let sccs: UsizeSccs = Sccs::new(&graph); assert_eq!(sccs.num_sccs(), 1); assert_eq!(sccs.scc(0), 0); assert_eq!(sccs.scc(1), 0); @@ -130,7 +173,7 @@ fn test_find_state_3() { */ let graph = TestGraph::new(0, &[(0, 1), (0, 4), (1, 2), (1, 3), (2, 1), (3, 0), (4, 2), (5, 2)]); - let sccs: Sccs<_, usize> = Sccs::new(&graph); + let sccs: UsizeSccs = Sccs::new(&graph); assert_eq!(sccs.num_sccs(), 2); assert_eq!(sccs.scc(0), 0); assert_eq!(sccs.scc(1), 0); @@ -165,7 +208,7 @@ fn test_deep_linear() { nodes.push((i - 1, i)); } let graph = TestGraph::new(0, nodes.as_slice()); - let sccs: Sccs<_, usize> = Sccs::new(&graph); + let sccs: UsizeSccs = Sccs::new(&graph); assert_eq!(sccs.num_sccs(), NR_NODES); assert_eq!(sccs.scc(0), NR_NODES - 1); assert_eq!(sccs.scc(NR_NODES - 1), 0); @@ -210,7 +253,164 @@ fn bench_sccc(b: &mut test::Bencher) { graph[21] = (7, 4); let graph = TestGraph::new(0, &graph[..]); b.iter(|| { - let sccs: Sccs<_, usize> = Sccs::new(&graph); + let sccs: UsizeSccs = Sccs::new(&graph); assert_eq!(sccs.num_sccs(), 3); }); } + +#[test] +fn test_max_self_loop() { + let graph = TestGraph::new(0, &[(0, 0)]); + let sccs: MaxReachedSccs = + Sccs::new_with_annotation(&graph, |n| if n == 0 { MaxReached(17) } else { MaxReached(0) }); + assert_eq!(sccs.annotation(0), 17); +} + +#[test] +fn test_max_branch() { + let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 4)]); + let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize); + assert_eq!(sccs.annotation(sccs.scc(0)), 4); + assert_eq!(sccs.annotation(sccs.scc(1)), 3); + assert_eq!(sccs.annotation(sccs.scc(2)), 4); +} +#[test] +fn test_single_cycle_max() { + let graph = TestGraph::new(0, &[(0, 2), (2, 3), (2, 4), (4, 1), (1, 2)]); + let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize); + assert_eq!(sccs.annotation(sccs.scc(2)), 4); + assert_eq!(sccs.annotation(sccs.scc(0)), 4); +} + +#[test] +fn test_simple_cycle_max() { + let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 0)]); + let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize); + assert_eq!(sccs.num_sccs(), 1); +} + +#[test] +fn test_double_cycle_max() { + let graph = + TestGraph::new(0, &[(0, 1), (1, 2), (1, 4), (2, 3), (2, 4), (3, 5), (4, 1), (5, 4)]); + let sccs: MaxReachedSccs = + Sccs::new_with_annotation(&graph, |n| if n == 5 { MaxReached(2) } else { MaxReached(1) }); + + assert_eq!(sccs.annotation(sccs.scc(0)).0, 2); +} + +#[test] +fn test_bug_minimised() { + let graph = TestGraph::new(0, &[(0, 3), (0, 1), (3, 2), (2, 3), (1, 4), (4, 5), (5, 4)]); + let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |n| match n { + 3 => MaxReached(1), + _ => MaxReached(0), + }); + assert_eq!(sccs.annotation(sccs.scc(2)), 1); + assert_eq!(sccs.annotation(sccs.scc(1)), 0); + assert_eq!(sccs.annotation(sccs.scc(4)), 0); +} + +#[test] +fn test_bug_max_leak_minimised() { + let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3)]); + let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w { + 4 => MaxReached(1), + _ => MaxReached(0), + }); + + assert_eq!(sccs.annotation(sccs.scc(2)), 0); + assert_eq!(sccs.annotation(sccs.scc(3)), 1); + assert_eq!(sccs.annotation(sccs.scc(0)), 1); +} + +#[test] +fn test_bug_max_leak() { + let graph = TestGraph::new( + 8, + &[ + (0, 0), + (0, 18), + (0, 19), + (0, 1), + (0, 2), + (0, 7), + (0, 8), + (0, 23), + (18, 0), + (18, 12), + (19, 0), + (19, 25), + (12, 18), + (12, 3), + (12, 5), + (3, 12), + (3, 21), + (3, 22), + (5, 13), + (21, 3), + (22, 3), + (13, 5), + (13, 4), + (4, 13), + (4, 0), + (2, 11), + (7, 6), + (6, 20), + (20, 6), + (8, 17), + (17, 9), + (9, 16), + (16, 26), + (26, 15), + (15, 10), + (10, 14), + (14, 27), + (23, 24), + ], + ); + let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w { + 22 => MaxReached(1), + 24 => MaxReached(2), + 27 => MaxReached(2), + _ => MaxReached(0), + }); + + assert_eq!(sccs.annotation(sccs.scc(2)), 0); + assert_eq!(sccs.annotation(sccs.scc(7)), 0); + assert_eq!(sccs.annotation(sccs.scc(8)), 2); + assert_eq!(sccs.annotation(sccs.scc(23)), 2); + assert_eq!(sccs.annotation(sccs.scc(3)), 2); + assert_eq!(sccs.annotation(sccs.scc(0)), 2); +} + +#[test] +fn test_bug_max_zero_stick_shape() { + let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 3), (3, 2), (3, 4)]); + + let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w { + 4 => MaxReached(1), + _ => MaxReached(0), + }); + + assert_eq!(sccs.annotation(sccs.scc(0)), 1); + assert_eq!(sccs.annotation(sccs.scc(1)), 1); + assert_eq!(sccs.annotation(sccs.scc(2)), 1); + assert_eq!(sccs.annotation(sccs.scc(3)), 1); + assert_eq!(sccs.annotation(sccs.scc(4)), 1); +} + +#[test] +fn test_min_max_in() { + let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3), (3, 5)]); + let sccs: Sccs = + Sccs::new_with_annotation(&graph, |w| MinMaxIn { min: w, max: w }); + + assert_eq!(sccs.annotation(sccs.scc(2)).min, 2); + assert_eq!(sccs.annotation(sccs.scc(2)).max, 2); + assert_eq!(sccs.annotation(sccs.scc(0)).min, 0); + assert_eq!(sccs.annotation(sccs.scc(0)).max, 4); + assert_eq!(sccs.annotation(sccs.scc(3)).min, 0); + assert_eq!(sccs.annotation(sccs.scc(3)).max, 4); + assert_eq!(sccs.annotation(sccs.scc(5)).min, 5); +} diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 85b5a3cdb7c3..9781aae22eb3 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -24,7 +24,6 @@ #![feature(extend_one)] #![feature(hash_raw_entry)] #![feature(hasher_prefixfree_extras)] -#![feature(lazy_cell)] #![feature(lint_reasons)] #![feature(macro_metavar_expr)] #![feature(map_try_insert)] diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index d477b86da74e..3883b0736db0 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs @@ -146,8 +146,6 @@ pub enum ProcessResult { #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] struct ObligationTreeId(usize); -type ObligationTreeIdGenerator = impl Iterator; - pub struct ObligationForest { /// The list of obligations. In between calls to [Self::process_obligations], /// this list only contains nodes in the `Pending` or `Waiting` state. @@ -310,18 +308,25 @@ pub struct Error { pub backtrace: Vec, } -impl ObligationForest { - pub fn new() -> ObligationForest { - ObligationForest { - nodes: vec![], - done_cache: Default::default(), - active_cache: Default::default(), - reused_node_vec: vec![], - obligation_tree_id_generator: (0..).map(ObligationTreeId), - error_cache: Default::default(), +mod helper { + use super::*; + pub type ObligationTreeIdGenerator = impl Iterator; + impl ObligationForest { + pub fn new() -> ObligationForest { + ObligationForest { + nodes: vec![], + done_cache: Default::default(), + active_cache: Default::default(), + reused_node_vec: vec![], + obligation_tree_id_generator: (0..).map(ObligationTreeId), + error_cache: Default::default(), + } } } +} +use helper::*; +impl ObligationForest { /// Returns the total number of nodes in the forest that have not /// yet been fully resolved. pub fn len(&self) -> usize { diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index c6d51a5d6b4f..240f2671c3b2 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -562,7 +562,7 @@ impl SelfProfiler { // ASLR is disabled and the heap is otherwise deterministic. let pid: u32 = process::id(); let filename = format!("{crate_name}-{pid:07}.rustc_profile"); - let path = output_directory.join(&filename); + let path = output_directory.join(filename); let profiler = Profiler::with_counter(&path, measureme::counters::Counter::by_name(counter_name)?)?; diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs index 21d7c91ec482..885f023122ab 100644 --- a/compiler/rustc_data_structures/src/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs @@ -125,13 +125,13 @@ impl SortedMap { /// Iterate over the keys, sorted #[inline] - pub fn keys(&self) -> impl Iterator + ExactSizeIterator + DoubleEndedIterator { + pub fn keys(&self) -> impl ExactSizeIterator + DoubleEndedIterator { self.data.iter().map(|(k, _)| k) } /// Iterate over values, sorted by key #[inline] - pub fn values(&self) -> impl Iterator + ExactSizeIterator + DoubleEndedIterator { + pub fn values(&self) -> impl ExactSizeIterator + DoubleEndedIterator { self.data.iter().map(|(_, v)| v) } diff --git a/compiler/rustc_data_structures/src/sync/lock.rs b/compiler/rustc_data_structures/src/sync/lock.rs index 756984642c74..780be7739458 100644 --- a/compiler/rustc_data_structures/src/sync/lock.rs +++ b/compiler/rustc_data_structures/src/sync/lock.rs @@ -69,7 +69,7 @@ mod maybe_sync { match self.mode { Mode::NoSync => { let cell = unsafe { &self.lock.mode_union.no_sync }; - debug_assert_eq!(cell.get(), true); + debug_assert!(cell.get()); cell.set(false); } // SAFETY (unlock): We know that the lock is locked as this type is a proof of that. diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index acd93b0b2a60..a03834c519d5 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -1,8 +1,10 @@ // This crate is intentionally empty and a re-export of `rustc_driver_impl` to allow the code in // `rustc_driver_impl` to be compiled in parallel with other crates. +// tidy-alphabetical-start #![allow(internal_features)] -#![feature(rustdoc_internals)] #![doc(rust_logo)] +#![feature(rustdoc_internals)] +// tidy-alphabetical-end pub use rustc_driver_impl::*; diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 5f7504add8d6..91cbffcd7072 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -19,7 +19,6 @@ rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } -rustc_hir = { path = "../rustc_hir" } rustc_hir_analysis = { path = "../rustc_hir_analysis" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_hir_typeck = { path = "../rustc_hir_typeck" } diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl index 5b39248302e7..31837e017643 100644 --- a/compiler/rustc_driver_impl/messages.ftl +++ b/compiler/rustc_driver_impl/messages.ftl @@ -10,6 +10,8 @@ driver_impl_ice_path_error = the ICE couldn't be written to `{$path}`: {$error} driver_impl_ice_path_error_env = the environment variable `RUSTC_ICE` is set to `{$env_var}` driver_impl_ice_version = rustc {$version} running on {$triple} +driver_impl_rlink_corrupt_file = corrupt metadata encountered in `{$file}` + driver_impl_rlink_empty_version_number = The input does not contain version number driver_impl_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}` diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index ba6b9ef07846..5ffa3a6099c0 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -4,16 +4,18 @@ //! //! This API is completely unstable and subject to change. +// tidy-alphabetical-start +#![allow(internal_features)] #![allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(rustdoc_internals)] -#![allow(internal_features)] #![feature(decl_macro)] #![feature(let_chains)] #![feature(panic_backtrace_config)] #![feature(panic_update_hook)] #![feature(result_flattening)] +#![feature(rustdoc_internals)] +// tidy-alphabetical-end use rustc_ast as ast; use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults}; @@ -32,6 +34,7 @@ use rustc_interface::{interface, Queries}; use rustc_lint::unerased_lint_store; use rustc_metadata::creader::MetadataLoader; use rustc_metadata::locator; +use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS}; use rustc_session::config::{ErrorOutputType, Input, OutFileName, OutputType}; use rustc_session::getopts::{self, Matches}; @@ -51,13 +54,13 @@ use std::ffi::OsString; use std::fmt::Write as _; use std::fs::{self, File}; use std::io::{self, IsTerminal, Read, Write}; -use std::panic::{self, catch_unwind, PanicInfo}; +use std::panic::{self, catch_unwind, PanicHookInfo}; use std::path::PathBuf; use std::process::{self, Command, Stdio}; use std::str; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, OnceLock}; -use std::time::{Instant, SystemTime}; +use std::time::{Duration, Instant, SystemTime}; use time::OffsetDateTime; use tracing::trace; @@ -96,7 +99,7 @@ mod signal_handler { use crate::session_diagnostics::{ RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch, - RLinkWrongFileType, RlinkNotAFile, RlinkUnableToRead, + RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead, }; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -645,8 +648,7 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { match err { CodegenErrors::WrongFileType => dcx.emit_fatal(RLinkWrongFileType), CodegenErrors::EmptyVersionNumber => dcx.emit_fatal(RLinkEmptyVersionNumber), - CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => sess - .dcx() + CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => dcx .emit_fatal(RLinkEncodingVersionMismatch { version_array, rlink_version }), CodegenErrors::RustcVersionMismatch { rustc_version } => { dcx.emit_fatal(RLinkRustcVersionMismatch { @@ -654,6 +656,9 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { current_version: sess.cfg_version, }) } + CodegenErrors::CorruptFile => { + dcx.emit_fatal(RlinkCorruptFile { file }); + } }; } }; @@ -802,6 +807,43 @@ fn print_crate_info( println_info!("{cfg}"); } } + CheckCfg => { + let mut check_cfgs: Vec = Vec::with_capacity(410); + + // INSTABILITY: We are sorting the output below. + #[allow(rustc::potential_query_instability)] + for (name, expected_values) in &sess.psess.check_config.expecteds { + use crate::config::ExpectedValues; + match expected_values { + ExpectedValues::Any => check_cfgs.push(format!("{name}=any()")), + ExpectedValues::Some(values) => { + if !values.is_empty() { + check_cfgs.extend(values.iter().map(|value| { + if let Some(value) = value { + format!("{name}=\"{value}\"") + } else { + name.to_string() + } + })) + } else { + check_cfgs.push(format!("{name}=")) + } + } + } + } + + check_cfgs.sort_unstable(); + if !sess.psess.check_config.exhaustive_names { + if !sess.psess.check_config.exhaustive_values { + println_info!("any()=any()"); + } else { + println_info!("any()"); + } + } + for check_cfg in check_cfgs { + println_info!("{check_cfg}"); + } + } CallingConventions => { let mut calling_conventions = rustc_target::spec::abi::all_names(); calling_conventions.sort_unstable(); @@ -1225,12 +1267,13 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option(sess: &'a Session) -> PResult<'a, ast::AttrVec> { - match &sess.io.input { - Input::File(ifile) => rustc_parse::parse_crate_attrs_from_file(ifile, &sess.psess), + let mut parser = unwrap_or_emit_fatal(match &sess.io.input { + Input::File(file) => new_parser_from_file(&sess.psess, file, None), Input::Str { name, input } => { - rustc_parse::parse_crate_attrs_from_source_str(name.clone(), input.clone(), &sess.psess) + new_parser_from_source_str(&sess.psess, name.clone(), input.clone()) } - } + }); + parser.parse_inner_attributes() } /// Runs a closure and catches unwinds triggered by fatal errors. @@ -1325,11 +1368,10 @@ pub fn install_ice_hook( let using_internal_features = Arc::new(std::sync::atomic::AtomicBool::default()); let using_internal_features_hook = using_internal_features.clone(); panic::update_hook(Box::new( - move |default_hook: &(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static), - info: &PanicInfo<'_>| { + move |default_hook: &(dyn Fn(&PanicHookInfo<'_>) + Send + Sync + 'static), + info: &PanicHookInfo<'_>| { // Lock stderr to prevent interleaving of concurrent panics. let _guard = io::stderr().lock(); - // If the error was caused by a broken pipe then this is not a bug. // Write the error and return immediately. See #98700. #[cfg(windows)] @@ -1390,7 +1432,7 @@ pub fn install_ice_hook( /// When `install_ice_hook` is called, this function will be called as the panic /// hook. fn report_ice( - info: &panic::PanicInfo<'_>, + info: &panic::PanicHookInfo<'_>, bug_report_url: &str, extra_info: fn(&DiagCtxt), using_internal_features: &AtomicBool, @@ -1402,6 +1444,7 @@ fn report_ice( fallback_bundle, )); let dcx = rustc_errors::DiagCtxt::new(emitter); + let dcx = dcx.handle(); // a .span_bug or .bug call has already printed what // it wants to print. @@ -1467,7 +1510,7 @@ fn report_ice( let num_frames = if backtrace { None } else { Some(2) }; - interface::try_print_query_stack(&dcx, num_frames, file); + interface::try_print_query_stack(dcx, num_frames, file); // We don't trust this callback not to panic itself, so run it at the end after we're sure we've // printed all the relevant info. @@ -1500,14 +1543,13 @@ pub fn init_logger(early_dcx: &EarlyDiagCtxt, cfg: rustc_log::LoggerConfig) { pub fn install_ctrlc_handler() { #[cfg(not(target_family = "wasm"))] ctrlc::set_handler(move || { - // Indicate that we have been signaled to stop. If we were already signaled, exit - // immediately. In our interpreter loop we try to consult this value often, but if for - // whatever reason we don't get to that check or the cleanup we do upon finding that - // this bool has become true takes a long time, the exit here will promptly exit the - // process on the second Ctrl-C. - if CTRL_C_RECEIVED.swap(true, Ordering::Relaxed) { - std::process::exit(1); - } + // Indicate that we have been signaled to stop, then give the rest of the compiler a bit of + // time to check CTRL_C_RECEIVED and run its own shutdown logic, but after a short amount + // of time exit the process. This sleep+exit ensures that even if nobody is checking + // CTRL_C_RECEIVED, the compiler exits reasonably promptly. + CTRL_C_RECEIVED.store(true, Ordering::Relaxed); + std::thread::sleep(Duration::from_millis(100)); + std::process::exit(1); }) .expect("Unable to install ctrlc handler"); } diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index fe426b8111cc..31de0a747b24 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -3,7 +3,6 @@ use rustc_ast as ast; use rustc_ast_pretty::pprust as pprust_ast; use rustc_errors::FatalError; -use rustc_hir as hir; use rustc_hir_pretty as pprust_hir; use rustc_middle::bug; use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty}; @@ -70,11 +69,7 @@ struct HirIdentifiedAnn<'tcx> { impl<'tcx> pprust_hir::PpAnn for HirIdentifiedAnn<'tcx> { fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) { - pprust_hir::PpAnn::nested( - &(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>), - state, - nested, - ) + self.tcx.nested(state, nested) } fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) { @@ -152,8 +147,7 @@ impl<'tcx> pprust_hir::PpAnn for HirTypedAnn<'tcx> { if let pprust_hir::Nested::Body(id) = nested { self.maybe_typeck_results.set(Some(self.tcx.typeck_body(id))); } - let pp_ann = &(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>); - pprust_hir::PpAnn::nested(pp_ann, state, nested); + self.tcx.nested(state, nested); self.maybe_typeck_results.set(old_maybe_typeck_results); } @@ -169,7 +163,7 @@ impl<'tcx> pprust_hir::PpAnn for HirTypedAnn<'tcx> { self.tcx .hir() .maybe_body_owned_by(expr.hir_id.owner.def_id) - .map(|body_id| self.tcx.typeck_body(body_id)) + .map(|body_id| self.tcx.typeck_body(body_id.id())) }); if let Some(typeck_results) = typeck_results { diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs index 1a9683e840af..449878f28c4e 100644 --- a/compiler/rustc_driver_impl/src/session_diagnostics.rs +++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs @@ -32,6 +32,12 @@ pub(crate) struct RLinkRustcVersionMismatch<'a> { #[diag(driver_impl_rlink_no_a_file)] pub(crate) struct RlinkNotAFile; +#[derive(Diagnostic)] +#[diag(driver_impl_rlink_corrupt_file)] +pub(crate) struct RlinkCorruptFile<'a> { + pub file: &'a std::path::Path, +} + #[derive(Diagnostic)] #[diag(driver_impl_ice)] pub(crate) struct Ice; diff --git a/compiler/rustc_error_codes/src/error_codes/E0229.md b/compiler/rustc_error_codes/src/error_codes/E0229.md index a8fab057d43a..f4a983cb9efc 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0229.md +++ b/compiler/rustc_error_codes/src/error_codes/E0229.md @@ -1,5 +1,4 @@ -An associated type binding was done outside of the type parameter declaration -and `where` clause. +An associated item constraint was written in an unexpected context. Erroneous code example: @@ -16,12 +15,12 @@ impl Foo for isize { fn boo(&self) -> usize { 42 } } -fn baz(x: &>::A) {} -// error: associated type bindings are not allowed here +fn baz(x: &>::A) {} +// error: associated item constraint are not allowed here ``` -To solve this error, please move the type bindings in the type parameter -declaration: +To solve this error, please move the associated item constraints to the type +parameter declaration: ``` # struct Bar; @@ -29,7 +28,7 @@ declaration: fn baz>(x: &::A) {} // ok! ``` -Or in the `where` clause: +Or into the where-clause: ``` # struct Bar; diff --git a/compiler/rustc_error_codes/src/error_codes/E0582.md b/compiler/rustc_error_codes/src/error_codes/E0582.md index e50cc60ea330..b2cdb509c95c 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0582.md +++ b/compiler/rustc_error_codes/src/error_codes/E0582.md @@ -27,6 +27,40 @@ fn bar(t: F, u: G) fn main() { } ``` +This error also includes the use of associated types with lifetime parameters. +```compile_fail,E0582 +trait Foo { + type Assoc<'a>; +} + +struct Bar +where + X: Foo, + F: for<'a> Fn(X::Assoc<'a>) -> &'a i32 +{ + x: X, + f: F +} +``` +The latter scenario encounters this error because `Foo::Assoc<'a>` could be +implemented by a type that does not use the `'a` parameter, so there is no +guarentee that `X::Assoc<'a>` actually uses `'a`. + +To fix this we can pass a dummy parameter: +``` +# trait Foo { +# type Assoc<'a>; +# } +struct Bar +where + X: Foo, + F: for<'a> Fn(X::Assoc<'a>, /* dummy */ &'a ()) -> &'a i32 +{ + x: X, + f: F +} +``` + Note: The examples above used to be (erroneously) accepted by the compiler, but this was since corrected. See [issue #33685] for more details. diff --git a/compiler/rustc_error_codes/src/error_codes/E0792.md b/compiler/rustc_error_codes/src/error_codes/E0792.md index bad2b5abfe4d..5e3dcc4aa727 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0792.md +++ b/compiler/rustc_error_codes/src/error_codes/E0792.md @@ -39,6 +39,8 @@ type Foo = impl std::fmt::Debug; fn foo() -> Foo { 5u32 } + +fn main() {} ``` This means that no matter the generic parameter to `foo`, @@ -57,4 +59,6 @@ type Foo = impl Debug; fn foo() -> Foo { Vec::::new() } + +fn main() {} ``` diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index f4a33a05c1b3..d13d5e1bca21 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -1,10 +1,12 @@ //! This library is used to gather all error codes into one place, to make //! their maintenance easier. +// tidy-alphabetical-start #![allow(internal_features)] -#![feature(rustdoc_internals)] -#![doc(rust_logo)] #![deny(rustdoc::invalid_codeblock_attributes)] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +// tidy-alphabetical-end // This higher-order macro defines the error codes that are in use. It is used // in the `rustc_errors` crate. Removed error codes are listed in the comment diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index ad82c2d96e7a..26a68454ab3b 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -1,12 +1,10 @@ -#![doc(rust_logo)] -#![feature(rustdoc_internals)] -#![feature(lazy_cell)] -#![feature(rustc_attrs)] -#![feature(type_alias_impl_trait)] +// tidy-alphabetical-start #![allow(internal_features)] - -#[macro_use] -extern crate tracing; +#![doc(rust_logo)] +#![feature(rustc_attrs)] +#![feature(rustdoc_internals)] +#![feature(type_alias_impl_trait)] +// tidy-alphabetical-end use fluent_bundle::FluentResource; use fluent_syntax::parser::ParserError; @@ -20,6 +18,7 @@ use std::fmt; use std::fs; use std::io; use std::path::{Path, PathBuf}; +use tracing::{instrument, trace}; #[cfg(not(parallel_compiler))] use std::cell::LazyCell as Lazy; @@ -367,17 +366,6 @@ impl From> for DiagMessage { } } -/// A workaround for must_produce_diag ICEs when formatting types in disabled lints. -/// -/// Delays formatting until `.into(): DiagMessage` is used. -pub struct DelayDm(pub F); - -impl String> From> for DiagMessage { - fn from(DelayDm(f): DelayDm) -> Self { - DiagMessage::from(f()) - } -} - /// Translating *into* a subdiagnostic message from a diagnostic message is a little strange - but /// the subdiagnostic functions (e.g. `span_label`) take a `SubdiagMessage` and the /// subdiagnostic derive refers to typed identifiers that are `DiagMessage`s, so need to be diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 18bb71bd99f7..e580910af779 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1,7 +1,7 @@ use crate::snippet::Style; use crate::{ - CodeSuggestion, DiagCtxt, DiagMessage, ErrCode, ErrorGuaranteed, ExplicitBug, Level, MultiSpan, - StashKey, SubdiagMessage, Substitution, SubstitutionPart, SuggestionStyle, + CodeSuggestion, DiagCtxtHandle, DiagMessage, ErrCode, ErrorGuaranteed, ExplicitBug, Level, + MultiSpan, StashKey, SubdiagMessage, Substitution, SubstitutionPart, SuggestionStyle, }; use rustc_data_structures::fx::FxIndexMap; use rustc_error_messages::fluent_value_from_str_list_sep_by_and; @@ -133,7 +133,7 @@ impl EmissionGuarantee for rustc_span::fatal_error::FatalError { pub trait Diagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> { /// Write out as a diagnostic out of `DiagCtxt`. #[must_use] - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G>; + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G>; } impl<'a, T, G> Diagnostic<'a, G> for Spanned @@ -141,7 +141,7 @@ where T: Diagnostic<'a, G>, G: EmissionGuarantee, { - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { self.node.into_diag(dcx, level).with_span(self.span) } } @@ -200,8 +200,6 @@ pub trait SubdiagMessageOp = pub trait LintDiagnostic<'a, G: EmissionGuarantee> { /// Decorate and emit a lint. fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>); - - fn msg(&self) -> DiagMessage; } #[derive(Clone, Debug, Encodable, Decodable)] @@ -492,7 +490,7 @@ pub struct Subdiag { /// the methods of `Diag` here, consider extending `DiagCtxtFlags`. #[must_use] pub struct Diag<'a, G: EmissionGuarantee = ErrorGuaranteed> { - pub dcx: &'a DiagCtxt, + pub dcx: DiagCtxtHandle<'a>, /// Why the `Option`? It is always `Some` until the `Diag` is consumed via /// `emit`, `cancel`, etc. At that point it is consumed and replaced with @@ -580,13 +578,13 @@ macro_rules! with_fn { impl<'a, G: EmissionGuarantee> Diag<'a, G> { #[rustc_lint_diagnostics] #[track_caller] - pub fn new(dcx: &'a DiagCtxt, level: Level, message: impl Into) -> Self { + pub fn new(dcx: DiagCtxtHandle<'a>, level: Level, message: impl Into) -> Self { Self::new_diagnostic(dcx, DiagInner::new(level, message)) } /// Creates a new `Diag` with an already constructed diagnostic. #[track_caller] - pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diag: DiagInner) -> Self { + pub(crate) fn new_diagnostic(dcx: DiagCtxtHandle<'a>, diag: DiagInner) -> Self { debug!("Created new diagnostic"); Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData } } @@ -1194,11 +1192,8 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of /// interpolated variables). #[rustc_lint_diagnostics] - pub fn subdiagnostic( - &mut self, - dcx: &crate::DiagCtxt, - subdiagnostic: impl Subdiagnostic, - ) -> &mut Self { + pub fn subdiagnostic(&mut self, subdiagnostic: impl Subdiagnostic) -> &mut Self { + let dcx = self.dcx; subdiagnostic.add_to_diag_with(self, &|diag, msg| { let args = diag.args.iter(); let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); @@ -1343,7 +1338,8 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// See `DiagCtxt::stash_diagnostic` for details. pub fn stash(mut self, span: Span, key: StashKey) -> Option { - self.dcx.stash_diagnostic(span, key, self.take_diag()) + let diag = self.take_diag(); + self.dcx.stash_diagnostic(span, key, diag) } /// Delay emission of this diagnostic as a bug. diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 4bf7dccab923..0af80bc5c67b 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -1,7 +1,7 @@ use crate::diagnostic::DiagLocation; -use crate::{fluent_generated as fluent, Subdiagnostic}; +use crate::{fluent_generated as fluent, DiagCtxtHandle, Subdiagnostic}; use crate::{ - Diag, DiagArgValue, DiagCtxt, Diagnostic, EmissionGuarantee, ErrCode, IntoDiagArg, Level, + Diag, DiagArgValue, Diagnostic, EmissionGuarantee, ErrCode, IntoDiagArg, Level, SubdiagMessageOp, }; use rustc_ast as ast; @@ -118,6 +118,15 @@ impl IntoDiagArg for rustc_type_ir::FnSig { } } +impl IntoDiagArg for rustc_type_ir::Binder +where + T: IntoDiagArg, +{ + fn into_diag_arg(self) -> DiagArgValue { + self.skip_binder().into_diag_arg() + } +} + into_diag_arg_for_number!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize); impl IntoDiagArg for bool { @@ -282,6 +291,12 @@ impl IntoDiagArg for ClosureKind { } } +impl IntoDiagArg for hir::def::Namespace { + fn into_diag_arg(self) -> DiagArgValue { + DiagArgValue::Str(Cow::Borrowed(self.descr())) + } +} + #[derive(Clone)] pub struct DiagSymbolList(Vec); @@ -300,7 +315,7 @@ impl IntoDiagArg for DiagSymbolList { } impl Diagnostic<'_, G> for TargetDataLayoutErrors<'_> { - fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { match self { TargetDataLayoutErrors::InvalidAddressSpace { addr_space, err, cause } => { Diag::new(dcx, level, fluent::errors_target_invalid_address_space) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 5d4d2555100e..245deda50d5c 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -567,7 +567,7 @@ impl Emitter for SilentEmitter { if let Some(fatal_note) = &self.fatal_note { diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new()); } - self.fatal_dcx.emit_diagnostic(diag); + self.fatal_dcx.handle().emit_diagnostic(diag); } } } diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index 80b4d2bf75c0..e4b29fc9103d 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -55,8 +55,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { ); let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1)); - let dcx = DiagCtxt::new(Box::new(je)); - dcx.span_err(span, "foo"); + DiagCtxt::new(Box::new(je)).handle().span_err(span, "foo"); let bytes = output.lock().unwrap(); let actual_output = str::from_utf8(&bytes).unwrap(); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 3b884ff864ab..83d5bbff0b0a 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -39,7 +39,7 @@ pub use diagnostic_impls::{ }; pub use emitter::ColorConfig; pub use rustc_error_messages::{ - fallback_fluent_bundle, fluent_bundle, DelayDm, DiagMessage, FluentBundle, LanguageIdentifier, + fallback_fluent_bundle, fluent_bundle, DiagMessage, FluentBundle, LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagMessage, }; pub use rustc_lint_defs::{pluralize, Applicability}; @@ -414,6 +414,19 @@ pub struct DiagCtxt { inner: Lock, } +#[derive(Copy, Clone)] +pub struct DiagCtxtHandle<'a> { + dcx: &'a DiagCtxt, +} + +impl<'a> std::ops::Deref for DiagCtxtHandle<'a> { + type Target = &'a DiagCtxt; + + fn deref(&self) -> &Self::Target { + &self.dcx + } +} + /// 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. @@ -572,8 +585,8 @@ impl Drop for DiagCtxtInner { if let Some(backtrace) = &self.must_produce_diag { panic!( "must_produce_diag: `trimmed_def_paths` called but no diagnostics emitted; \ - use `DelayDm` for lints or `with_no_trimmed_paths` for debugging. \ - called at: {backtrace}" + `with_no_trimmed_paths` for debugging. \ + called at: {backtrace}" ); } } @@ -608,7 +621,7 @@ impl DiagCtxt { } pub fn make_silent( - &mut self, + &self, fallback_bundle: LazyFallbackBundle, fatal_note: Option, emit_fatal_diagnostic: bool, @@ -623,7 +636,7 @@ impl DiagCtxt { }); } - fn wrap_emitter(&mut self, f: F) + fn wrap_emitter(&self, f: F) where F: FnOnce(DiagCtxtInner) -> Box, { @@ -738,6 +751,12 @@ impl DiagCtxt { *fulfilled_expectations = Default::default(); } + pub fn handle<'a>(&'a self) -> DiagCtxtHandle<'a> { + DiagCtxtHandle { dcx: self } + } +} + +impl<'a> DiagCtxtHandle<'a> { /// Stashes a diagnostic for possible later improvement in a different, /// later stage of the compiler. Possible actions depend on the diagnostic /// level: @@ -745,8 +764,8 @@ impl DiagCtxt { /// - Level::Error: immediately counted as an error that has occurred, because it /// is guaranteed to be emitted eventually. Can be later accessed with the /// provided `span` and `key` through - /// [`DiagCtxt::try_steal_modify_and_emit_err`] or - /// [`DiagCtxt::try_steal_replace_and_emit_err`]. These do not allow + /// [`DiagCtxtHandle::try_steal_modify_and_emit_err`] or + /// [`DiagCtxtHandle::try_steal_replace_and_emit_err`]. These do not allow /// cancellation or downgrading of the error. Returns /// `Some(ErrorGuaranteed)`. /// - Level::DelayedBug: this does happen occasionally with errors that are @@ -757,7 +776,7 @@ impl DiagCtxt { /// user-facing error. Returns `Some(ErrorGuaranteed)` as is normal for /// delayed bugs. /// - Level::Warning and lower (i.e. !is_error()): can be accessed with the - /// provided `span` and `key` through [`DiagCtxt::steal_non_err()`]. This + /// provided `span` and `key` through [`DiagCtxtHandle::steal_non_err()`]. This /// allows cancelling and downgrading of the diagnostic. Returns `None`. pub fn stash_diagnostic( &self, @@ -793,7 +812,7 @@ impl DiagCtxt { /// Steal a previously stashed non-error diagnostic with the given `Span` /// and [`StashKey`] as the key. Panics if the found diagnostic is an /// error. - pub fn steal_non_err(&self, span: Span, key: StashKey) -> Option> { + pub fn steal_non_err(self, span: Span, key: StashKey) -> Option> { let key = (span.with_parent(None), key); // FIXME(#120456) - is `swap_remove` correct? let (diag, guar) = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key)?; @@ -807,7 +826,7 @@ impl DiagCtxt { /// no matching diagnostic is found. Panics if the found diagnostic's level /// isn't `Level::Error`. pub fn try_steal_modify_and_emit_err( - &self, + self, span: Span, key: StashKey, mut modify_err: F, @@ -833,7 +852,7 @@ impl DiagCtxt { /// [`StashKey`] as the key, cancels it if found, and emits `new_err`. /// Panics if the found diagnostic's level isn't `Level::Error`. pub fn try_steal_replace_and_emit_err( - &self, + self, span: Span, key: StashKey, new_err: Diag<'_>, @@ -1106,18 +1125,18 @@ impl DiagCtxt { // // Functions beginning with `struct_`/`create_` create a diagnostic. Other // functions create and emit a diagnostic all in one go. -impl DiagCtxt { +impl<'a> DiagCtxtHandle<'a> { // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't // user-facing. #[track_caller] - pub fn struct_bug(&self, msg: impl Into>) -> Diag<'_, BugAbort> { + pub fn struct_bug(self, msg: impl Into>) -> Diag<'a, BugAbort> { Diag::new(self, Bug, msg.into()) } // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't // user-facing. #[track_caller] - pub fn bug(&self, msg: impl Into>) -> ! { + pub fn bug(self, msg: impl Into>) -> ! { self.struct_bug(msg).emit() } @@ -1125,111 +1144,108 @@ impl DiagCtxt { // user-facing. #[track_caller] pub fn struct_span_bug( - &self, + self, span: impl Into, msg: impl Into>, - ) -> Diag<'_, BugAbort> { + ) -> Diag<'a, BugAbort> { self.struct_bug(msg).with_span(span) } // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't // user-facing. #[track_caller] - pub fn span_bug(&self, span: impl Into, msg: impl Into>) -> ! { + pub fn span_bug(self, span: impl Into, msg: impl Into>) -> ! { self.struct_span_bug(span, msg.into()).emit() } #[track_caller] - pub fn create_bug<'a>(&'a self, bug: impl Diagnostic<'a, BugAbort>) -> Diag<'a, BugAbort> { + pub fn create_bug(self, bug: impl Diagnostic<'a, BugAbort>) -> Diag<'a, BugAbort> { bug.into_diag(self, Bug) } #[track_caller] - pub fn emit_bug<'a>(&'a self, bug: impl Diagnostic<'a, BugAbort>) -> ! { + pub fn emit_bug(self, bug: impl Diagnostic<'a, BugAbort>) -> ! { self.create_bug(bug).emit() } #[rustc_lint_diagnostics] #[track_caller] - pub fn struct_fatal(&self, msg: impl Into) -> Diag<'_, FatalAbort> { + pub fn struct_fatal(self, msg: impl Into) -> Diag<'a, FatalAbort> { Diag::new(self, Fatal, msg) } #[rustc_lint_diagnostics] #[track_caller] - pub fn fatal(&self, msg: impl Into) -> ! { + pub fn fatal(self, msg: impl Into) -> ! { self.struct_fatal(msg).emit() } #[rustc_lint_diagnostics] #[track_caller] pub fn struct_span_fatal( - &self, + self, span: impl Into, msg: impl Into, - ) -> Diag<'_, FatalAbort> { + ) -> Diag<'a, FatalAbort> { self.struct_fatal(msg).with_span(span) } #[rustc_lint_diagnostics] #[track_caller] - pub fn span_fatal(&self, span: impl Into, msg: impl Into) -> ! { + pub fn span_fatal(self, span: impl Into, msg: impl Into) -> ! { self.struct_span_fatal(span, msg).emit() } #[track_caller] - pub fn create_fatal<'a>( - &'a self, - fatal: impl Diagnostic<'a, FatalAbort>, - ) -> Diag<'a, FatalAbort> { + pub fn create_fatal(self, fatal: impl Diagnostic<'a, FatalAbort>) -> Diag<'a, FatalAbort> { fatal.into_diag(self, Fatal) } #[track_caller] - pub fn emit_fatal<'a>(&'a self, fatal: impl Diagnostic<'a, FatalAbort>) -> ! { + pub fn emit_fatal(self, fatal: impl Diagnostic<'a, FatalAbort>) -> ! { self.create_fatal(fatal).emit() } #[track_caller] - pub fn create_almost_fatal<'a>( - &'a self, + pub fn create_almost_fatal( + self, fatal: impl Diagnostic<'a, FatalError>, ) -> Diag<'a, FatalError> { fatal.into_diag(self, Fatal) } #[track_caller] - pub fn emit_almost_fatal<'a>(&'a self, fatal: impl Diagnostic<'a, FatalError>) -> FatalError { + pub fn emit_almost_fatal(self, fatal: impl Diagnostic<'a, FatalError>) -> FatalError { self.create_almost_fatal(fatal).emit() } // FIXME: This method should be removed (every error should have an associated error code). #[rustc_lint_diagnostics] #[track_caller] - pub fn struct_err(&self, msg: impl Into) -> Diag<'_> { + pub fn struct_err(self, msg: impl Into) -> Diag<'a> { Diag::new(self, Error, msg) } #[rustc_lint_diagnostics] #[track_caller] - pub fn err(&self, msg: impl Into) -> ErrorGuaranteed { + pub fn err(self, msg: impl Into) -> ErrorGuaranteed { self.struct_err(msg).emit() } #[rustc_lint_diagnostics] #[track_caller] pub fn struct_span_err( - &self, + self, span: impl Into, msg: impl Into, - ) -> Diag<'_> { + ) -> Diag<'a> { self.struct_err(msg).with_span(span) } #[rustc_lint_diagnostics] #[track_caller] pub fn span_err( - &self, + self, span: impl Into, msg: impl Into, ) -> ErrorGuaranteed { @@ -1237,12 +1253,12 @@ impl DiagCtxt { } #[track_caller] - pub fn create_err<'a>(&'a self, err: impl Diagnostic<'a>) -> Diag<'a> { + pub fn create_err(self, err: impl Diagnostic<'a>) -> Diag<'a> { err.into_diag(self, Error) } #[track_caller] - pub fn emit_err<'a>(&'a self, err: impl Diagnostic<'a>) -> ErrorGuaranteed { + pub fn emit_err(self, err: impl Diagnostic<'a>) -> ErrorGuaranteed { self.create_err(err).emit() } @@ -1251,7 +1267,7 @@ impl DiagCtxt { // No `#[rustc_lint_diagnostics]` and no `impl Into` because bug messages aren't // user-facing. #[track_caller] - pub fn delayed_bug(&self, msg: impl Into>) -> ErrorGuaranteed { + pub fn delayed_bug(self, msg: impl Into>) -> ErrorGuaranteed { Diag::::new(self, DelayedBug, msg.into()).emit() } @@ -1264,7 +1280,7 @@ impl DiagCtxt { // user-facing. #[track_caller] pub fn span_delayed_bug( - &self, + self, sp: impl Into, msg: impl Into>, ) -> ErrorGuaranteed { @@ -1273,45 +1289,45 @@ impl DiagCtxt { #[rustc_lint_diagnostics] #[track_caller] - pub fn struct_warn(&self, msg: impl Into) -> Diag<'_, ()> { + pub fn struct_warn(self, msg: impl Into) -> Diag<'a, ()> { Diag::new(self, Warning, msg) } #[rustc_lint_diagnostics] #[track_caller] - pub fn warn(&self, msg: impl Into) { + pub fn warn(self, msg: impl Into) { self.struct_warn(msg).emit() } #[rustc_lint_diagnostics] #[track_caller] pub fn struct_span_warn( - &self, + self, span: impl Into, msg: impl Into, - ) -> Diag<'_, ()> { + ) -> Diag<'a, ()> { self.struct_warn(msg).with_span(span) } #[rustc_lint_diagnostics] #[track_caller] - pub fn span_warn(&self, span: impl Into, msg: impl Into) { + pub fn span_warn(self, span: impl Into, msg: impl Into) { self.struct_span_warn(span, msg).emit() } #[track_caller] - pub fn create_warn<'a>(&'a self, warning: impl Diagnostic<'a, ()>) -> Diag<'a, ()> { + pub fn create_warn(self, warning: impl Diagnostic<'a, ()>) -> Diag<'a, ()> { warning.into_diag(self, Warning) } #[track_caller] - pub fn emit_warn<'a>(&'a self, warning: impl Diagnostic<'a, ()>) { + pub fn emit_warn(self, warning: impl Diagnostic<'a, ()>) { self.create_warn(warning).emit() } #[rustc_lint_diagnostics] #[track_caller] - pub fn struct_note(&self, msg: impl Into) -> Diag<'_, ()> { + pub fn struct_note(self, msg: impl Into) -> Diag<'a, ()> { Diag::new(self, Note, msg) } @@ -1324,54 +1340,50 @@ impl DiagCtxt { #[rustc_lint_diagnostics] #[track_caller] pub fn struct_span_note( - &self, + self, span: impl Into, msg: impl Into, - ) -> Diag<'_, ()> { + ) -> Diag<'a, ()> { self.struct_note(msg).with_span(span) } #[rustc_lint_diagnostics] #[track_caller] - pub fn span_note(&self, span: impl Into, msg: impl Into) { + pub fn span_note(self, span: impl Into, msg: impl Into) { self.struct_span_note(span, msg).emit() } #[track_caller] - pub fn create_note<'a>(&'a self, note: impl Diagnostic<'a, ()>) -> Diag<'a, ()> { + pub fn create_note(self, note: impl Diagnostic<'a, ()>) -> Diag<'a, ()> { note.into_diag(self, Note) } #[track_caller] - pub fn emit_note<'a>(&'a self, note: impl Diagnostic<'a, ()>) { + pub fn emit_note(self, note: impl Diagnostic<'a, ()>) { self.create_note(note).emit() } #[rustc_lint_diagnostics] #[track_caller] - pub fn struct_help(&self, msg: impl Into) -> Diag<'_, ()> { + pub fn struct_help(self, msg: impl Into) -> Diag<'a, ()> { Diag::new(self, Help, msg) } #[rustc_lint_diagnostics] #[track_caller] - pub fn struct_failure_note(&self, msg: impl Into) -> Diag<'_, ()> { + pub fn struct_failure_note(self, msg: impl Into) -> Diag<'a, ()> { Diag::new(self, FailureNote, msg) } #[rustc_lint_diagnostics] #[track_caller] - pub fn struct_allow(&self, msg: impl Into) -> Diag<'_, ()> { + pub fn struct_allow(self, msg: impl Into) -> Diag<'a, ()> { Diag::new(self, Allow, msg) } #[rustc_lint_diagnostics] #[track_caller] - pub fn struct_expect( - &self, - msg: impl Into, - id: LintExpectationId, - ) -> Diag<'_, ()> { + pub fn struct_expect(self, msg: impl Into, id: LintExpectationId) -> Diag<'a, ()> { Diag::new(self, Expect(id), msg) } } diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 530b37aadb1f..6113580491ef 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -35,8 +35,8 @@ expand_duplicate_matcher_binding = duplicate matcher binding .label = duplicate binding .label2 = previous binding -expand_empty_delegation_list = - empty list delegation is not supported +expand_empty_delegation_mac = + empty {$kind} delegation is not supported expand_expected_paren_or_brace = expected `(` or `{"{"}`, found `{$token}` @@ -58,6 +58,9 @@ expand_feature_removed = .label = feature has been removed .reason = {$reason} +expand_glob_delegation_outside_impls = + glob delegation is only supported in impls + expand_helper_attribute_name_invalid = `{$name}` cannot be a name of derive helper attribute @@ -124,6 +127,9 @@ expand_not_a_meta_item = expand_only_one_word = must only be one word +expand_proc_macro_back_compat = using an old version of `{$crate_name}` + .note = older versions of the `{$crate_name}` crate no longer compile; please update to `{$crate_name}` v{$fixed_version}, or switch to one of the `{$crate_name}` alternatives + expand_proc_macro_derive_panicked = proc-macro derive panicked .help = message: {$message} @@ -154,7 +160,7 @@ expand_unsupported_key_value = key-value macro attributes are not supported expand_var_still_repeating = - variable '{$ident}' is still repeating at this depth + variable `{$ident}` is still repeating at this depth expand_wrong_fragment_kind = non-{$kind} macro in {$kind} position: {$name} diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 12868a666056..d8e6d3525da8 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -12,11 +12,10 @@ use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind use rustc_attr::{self as attr, Deprecation, Stability}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{self, Lrc}; -use rustc_errors::{DiagCtxt, ErrorGuaranteed, PResult}; +use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult}; use rustc_feature::Features; -use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; -use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, RegisteredTools}; -use rustc_parse::{parser, MACRO_ARGUMENTS}; +use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools}; +use rustc_parse::{parser::Parser, MACRO_ARGUMENTS}; use rustc_session::config::CollapseMacroDebuginfo; use rustc_session::{parse::ParseSess, Limit, Session}; use rustc_span::def_id::{CrateNum, DefId, LocalDefId}; @@ -358,6 +357,10 @@ where } } +pub trait GlobDelegationExpander { + fn expand(&self, ecx: &mut ExtCtxt<'_>) -> ExpandResult)>, ()>; +} + // Use a macro because forwarding to a simple function has type system issues macro_rules! make_stmts_default { ($me:expr) => { @@ -715,6 +718,9 @@ pub enum SyntaxExtensionKind { /// The produced AST fragment is appended to the input AST fragment. Box, ), + + /// A glob delegation. + GlobDelegation(Box), } /// A struct representing a macro definition in "lowered" form ready for expansion. @@ -749,7 +755,9 @@ impl SyntaxExtension { /// Returns which kind of macro calls this syntax extension. pub fn macro_kind(&self) -> MacroKind { match self.kind { - SyntaxExtensionKind::Bang(..) | SyntaxExtensionKind::LegacyBang(..) => MacroKind::Bang, + SyntaxExtensionKind::Bang(..) + | SyntaxExtensionKind::LegacyBang(..) + | SyntaxExtensionKind::GlobDelegation(..) => MacroKind::Bang, SyntaxExtensionKind::Attr(..) | SyntaxExtensionKind::LegacyAttr(..) | SyntaxExtensionKind::NonMacroAttr => MacroKind::Attr, @@ -923,6 +931,32 @@ impl SyntaxExtension { SyntaxExtension::default(SyntaxExtensionKind::NonMacroAttr, edition) } + pub fn glob_delegation( + trait_def_id: DefId, + impl_def_id: LocalDefId, + edition: Edition, + ) -> SyntaxExtension { + struct GlobDelegationExpanderImpl { + trait_def_id: DefId, + impl_def_id: LocalDefId, + } + impl GlobDelegationExpander for GlobDelegationExpanderImpl { + fn expand( + &self, + ecx: &mut ExtCtxt<'_>, + ) -> ExpandResult)>, ()> { + match ecx.resolver.glob_delegation_suffixes(self.trait_def_id, self.impl_def_id) { + Ok(suffixes) => ExpandResult::Ready(suffixes), + Err(Indeterminate) if ecx.force_mode => ExpandResult::Ready(Vec::new()), + Err(Indeterminate) => ExpandResult::Retry(()), + } + } + } + + let expander = GlobDelegationExpanderImpl { trait_def_id, impl_def_id }; + SyntaxExtension::default(SyntaxExtensionKind::GlobDelegation(Box::new(expander)), edition) + } + pub fn expn_data( &self, parent: LocalExpnId, @@ -1031,6 +1065,16 @@ pub trait ResolverExpand { /// Tools registered with `#![register_tool]` and used by tool attributes and lints. fn registered_tools(&self) -> &RegisteredTools; + + /// Mark this invocation id as a glob delegation. + fn register_glob_delegation(&mut self, invoc_id: LocalExpnId); + + /// Names of specific methods to which glob delegation expands. + fn glob_delegation_suffixes( + &mut self, + trait_def_id: DefId, + impl_def_id: LocalDefId, + ) -> Result)>, Indeterminate>; } pub trait LintStoreExpand { @@ -1136,7 +1180,7 @@ impl<'a> ExtCtxt<'a> { } } - pub fn dcx(&self) -> &'a DiagCtxt { + pub fn dcx(&self) -> DiagCtxtHandle<'a> { self.sess.dcx() } @@ -1150,8 +1194,8 @@ impl<'a> ExtCtxt<'a> { pub fn monotonic_expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> { expand::MacroExpander::new(self, true) } - pub fn new_parser_from_tts(&self, stream: TokenStream) -> parser::Parser<'a> { - rustc_parse::stream_to_parser(&self.sess.psess, stream, MACRO_ARGUMENTS) + pub fn new_parser_from_tts(&self, stream: TokenStream) -> Parser<'a> { + Parser::new(&self.sess.psess, stream, MACRO_ARGUMENTS) } pub fn source_map(&self) -> &'a SourceMap { self.sess.psess.source_map() @@ -1257,7 +1301,7 @@ pub fn resolve_path(sess: &Session, path: impl Into, span: Span) -> PRe } pub fn parse_macro_name_and_helper_attrs( - dcx: &rustc_errors::DiagCtxt, + dcx: DiagCtxtHandle<'_>, attr: &Attribute, macro_type: &str, ) -> Option<(Symbol, Vec)> { @@ -1330,83 +1374,63 @@ pub fn parse_macro_name_and_helper_attrs( Some((trait_ident.name, proc_attrs)) } -/// This nonterminal looks like some specific enums from -/// `proc-macro-hack` and `procedural-masquerade` crates. -/// We need to maintain some special pretty-printing behavior for them due to incorrect -/// asserts in old versions of those crates and their wide use in the ecosystem. -/// See issue #73345 for more details. +/// If this item looks like a specific enums from `rental`, emit a fatal error. +/// See #73345 and #83125 for more details. /// FIXME(#73933): Remove this eventually. -fn pretty_printing_compatibility_hack(item: &Item, sess: &Session) -> bool { +fn pretty_printing_compatibility_hack(item: &Item, sess: &Session) { let name = item.ident.name; - if name == sym::ProceduralMasqueradeDummyType { - if let ast::ItemKind::Enum(enum_def, _) = &item.kind { - if let [variant] = &*enum_def.variants { - if variant.ident.name == sym::Input { - let filename = sess.source_map().span_to_filename(item.ident.span); - if let FileName::Real(real) = filename { - if let Some(c) = real - .local_path() - .unwrap_or(Path::new("")) - .components() - .flat_map(|c| c.as_os_str().to_str()) - .find(|c| c.starts_with("rental") || c.starts_with("allsorts-rental")) - { - let crate_matches = if c.starts_with("allsorts-rental") { - true - } else { - let mut version = c.trim_start_matches("rental-").split('.'); - version.next() == Some("0") - && version.next() == Some("5") - && version - .next() - .and_then(|c| c.parse::().ok()) - .is_some_and(|v| v < 6) - }; + if name == sym::ProceduralMasqueradeDummyType + && let ast::ItemKind::Enum(enum_def, _) = &item.kind + && let [variant] = &*enum_def.variants + && variant.ident.name == sym::Input + && let FileName::Real(real) = sess.source_map().span_to_filename(item.ident.span) + && let Some(c) = real + .local_path() + .unwrap_or(Path::new("")) + .components() + .flat_map(|c| c.as_os_str().to_str()) + .find(|c| c.starts_with("rental") || c.starts_with("allsorts-rental")) + { + let crate_matches = if c.starts_with("allsorts-rental") { + true + } else { + let mut version = c.trim_start_matches("rental-").split('.'); + version.next() == Some("0") + && version.next() == Some("5") + && version.next().and_then(|c| c.parse::().ok()).is_some_and(|v| v < 6) + }; - if crate_matches { - // FIXME: make this translatable - #[allow(rustc::untranslatable_diagnostic)] - sess.psess.buffer_lint_with_diagnostic( - PROC_MACRO_BACK_COMPAT, - item.ident.span, - ast::CRATE_NODE_ID, - "using an old version of `rental`", - BuiltinLintDiag::ProcMacroBackCompat( - "older versions of the `rental` crate will stop compiling in future versions of Rust; \ - please update to `rental` v0.5.6, or switch to one of the `rental` alternatives".to_string() - ) - ); - return true; - } - } - } - } - } + if crate_matches { + // FIXME: make this translatable + #[allow(rustc::untranslatable_diagnostic)] + sess.dcx().emit_fatal(errors::ProcMacroBackCompat { + crate_name: "rental".to_string(), + fixed_version: "0.5.6".to_string(), + }); } } - false } -pub(crate) fn ann_pretty_printing_compatibility_hack(ann: &Annotatable, sess: &Session) -> bool { +pub(crate) fn ann_pretty_printing_compatibility_hack(ann: &Annotatable, sess: &Session) { let item = match ann { Annotatable::Item(item) => item, Annotatable::Stmt(stmt) => match &stmt.kind { ast::StmtKind::Item(item) => item, - _ => return false, + _ => return, }, - _ => return false, + _ => return, }; pretty_printing_compatibility_hack(item, sess) } -pub(crate) fn nt_pretty_printing_compatibility_hack(nt: &Nonterminal, sess: &Session) -> bool { +pub(crate) fn nt_pretty_printing_compatibility_hack(nt: &Nonterminal, sess: &Session) { let item = match nt { Nonterminal::NtItem(item) => item, Nonterminal::NtStmt(stmt) => match &stmt.kind { ast::StmtKind::Item(item) => item, - _ => return false, + _ => return, }, - _ => return false, + _ => return, }; pretty_printing_compatibility_hack(item, sess) } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 1b6e191c2eb0..37dfd8305126 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -631,7 +631,10 @@ impl<'a> ExtCtxt<'a> { span, name, AttrVec::new(), - ast::ItemKind::Static(ast::StaticItem { ty, mutability, expr: Some(expr) }.into()), + ast::ItemKind::Static( + ast::StaticItem { ty, safety: ast::Safety::Default, mutability, expr: Some(expr) } + .into(), + ), ) } @@ -663,7 +666,7 @@ impl<'a> ExtCtxt<'a> { // Builds `#[name]`. pub fn attr_word(&self, name: Symbol, span: Span) -> ast::Attribute { let g = &self.sess.psess.attr_id_generator; - attr::mk_attr_word(g, ast::AttrStyle::Outer, name, span) + attr::mk_attr_word(g, ast::AttrStyle::Outer, ast::Safety::Default, name, span) } // Builds `#[name = val]`. @@ -671,12 +674,26 @@ impl<'a> ExtCtxt<'a> { // Note: `span` is used for both the identifier and the value. pub fn attr_name_value_str(&self, name: Symbol, val: Symbol, span: Span) -> ast::Attribute { let g = &self.sess.psess.attr_id_generator; - attr::mk_attr_name_value_str(g, ast::AttrStyle::Outer, name, val, span) + attr::mk_attr_name_value_str( + g, + ast::AttrStyle::Outer, + ast::Safety::Default, + name, + val, + span, + ) } // Builds `#[outer(inner)]`. pub fn attr_nested_word(&self, outer: Symbol, inner: Symbol, span: Span) -> ast::Attribute { let g = &self.sess.psess.attr_id_generator; - attr::mk_attr_nested_word(g, ast::AttrStyle::Outer, outer, inner, span) + attr::mk_attr_nested_word( + g, + ast::AttrStyle::Outer, + ast::Safety::Default, + outer, + inner, + span, + ) } } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 35f0d8abffc5..badfa6d3aa32 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -14,6 +14,7 @@ use rustc_attr as attr; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_feature::Features; use rustc_feature::{ACCEPTED_FEATURES, REMOVED_FEATURES, UNSTABLE_FEATURES}; +use rustc_lint_defs::BuiltinLintDiag; use rustc_parse::validate_attr; use rustc_session::parse::feature_err; use rustc_session::Session; @@ -248,7 +249,6 @@ impl<'a> StripUnconfigured<'a> { /// Gives a compiler warning when the `cfg_attr` contains no attributes and /// is in the original source file. Gives a compiler error if the syntax of /// the attribute is incorrect. - #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub(crate) fn expand_cfg_attr(&self, attr: &Attribute, recursive: bool) -> Vec { let Some((cfg_predicate, expanded_attrs)) = rustc_parse::parse_cfg_attr(attr, &self.sess.psess) @@ -262,7 +262,7 @@ impl<'a> StripUnconfigured<'a> { rustc_lint_defs::builtin::UNUSED_ATTRIBUTES, attr.span, ast::CRATE_NODE_ID, - "`#[cfg_attr]` does not expand to any attributes", + BuiltinLintDiag::CfgAttrNoAttributes, ); } @@ -283,7 +283,6 @@ impl<'a> StripUnconfigured<'a> { } } - #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn expand_cfg_attr_item( &self, attr: &Attribute, @@ -346,7 +345,7 @@ impl<'a> StripUnconfigured<'a> { rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, attr.span, ast::CRATE_NODE_ID, - "`crate_type` within an `#![cfg_attr] attribute is deprecated`", + BuiltinLintDiag::CrateTypeInCfgAttr, ); } if attr.has_name(sym::crate_name) { @@ -354,7 +353,7 @@ impl<'a> StripUnconfigured<'a> { rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, attr.span, ast::CRATE_NODE_ID, - "`crate_name` within an `#![cfg_attr] attribute is deprecated`", + BuiltinLintDiag::CrateNameInCfgAttr, ); } attr diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index a5fc9e9d89c1..c883121fb406 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -435,8 +435,26 @@ pub struct ExpectedParenOrBrace<'a> { } #[derive(Diagnostic)] -#[diag(expand_empty_delegation_list)] -pub(crate) struct EmptyDelegationList { +#[diag(expand_empty_delegation_mac)] +pub(crate) struct EmptyDelegationMac { + #[primary_span] + pub span: Span, + pub kind: String, +} + +#[derive(Diagnostic)] +#[diag(expand_glob_delegation_outside_impls)] +pub(crate) struct GlobDelegationOutsideImpls { #[primary_span] pub span: Span, } + +// This used to be the `proc_macro_back_compat` lint (#83125). It was later +// turned into a hard error. +#[derive(Diagnostic)] +#[diag(expand_proc_macro_back_compat)] +#[note] +pub struct ProcMacroBackCompat { + pub crate_name: String, + pub fixed_version: String, +} diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index a049ac251e1e..716bfc8c26b1 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1,8 +1,8 @@ use crate::base::*; use crate::config::StripUnconfigured; use crate::errors::{ - EmptyDelegationList, IncompleteParse, RecursionLimitReached, RemoveExprNotSupported, - RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind, + EmptyDelegationMac, GlobDelegationOutsideImpls, IncompleteParse, RecursionLimitReached, + RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind, }; use crate::mbe::diagnostics::annotate_err_with_kind; use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod}; @@ -343,6 +343,9 @@ pub enum InvocationKind { is_const: bool, item: Annotatable, }, + GlobDelegation { + item: P, + }, } impl InvocationKind { @@ -370,6 +373,7 @@ impl Invocation { InvocationKind::Bang { span, .. } => *span, InvocationKind::Attr { attr, .. } => attr.span, InvocationKind::Derive { path, .. } => path.span, + InvocationKind::GlobDelegation { item } => item.span, } } @@ -378,6 +382,7 @@ impl Invocation { InvocationKind::Bang { span, .. } => span, InvocationKind::Attr { attr, .. } => &mut attr.span, InvocationKind::Derive { path, .. } => &mut path.span, + InvocationKind::GlobDelegation { item } => &mut item.span, } } } @@ -778,7 +783,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> { if let SyntaxExtensionKind::Derive(..) = ext { self.gate_proc_macro_input(&item); } - let meta = ast::MetaItem { kind: MetaItemKind::Word, span, path }; + // The `MetaItem` representing the trait to derive can't + // have an unsafe around it (as of now). + let meta = ast::MetaItem { + unsafety: ast::Safety::Default, + kind: MetaItemKind::Word, + span, + path, + }; let items = match expander.expand(self.cx, span, &meta, item, is_const) { ExpandResult::Ready(items) => items, ExpandResult::Retry(item) => { @@ -793,6 +805,36 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } _ => unreachable!(), }, + InvocationKind::GlobDelegation { item } => { + let AssocItemKind::DelegationMac(deleg) = &item.kind else { unreachable!() }; + let suffixes = match ext { + SyntaxExtensionKind::GlobDelegation(expander) => match expander.expand(self.cx) + { + ExpandResult::Ready(suffixes) => suffixes, + ExpandResult::Retry(()) => { + // Reassemble the original invocation for retrying. + return ExpandResult::Retry(Invocation { + kind: InvocationKind::GlobDelegation { item }, + ..invoc + }); + } + }, + SyntaxExtensionKind::LegacyBang(..) => { + let msg = "expanded a dummy glob delegation"; + let guar = self.cx.dcx().span_delayed_bug(span, msg); + return ExpandResult::Ready(fragment_kind.dummy(span, guar)); + } + _ => unreachable!(), + }; + + type Node = AstNodeWrapper, ImplItemTag>; + let single_delegations = build_single_delegations::( + self.cx, deleg, &item, &suffixes, item.span, true, + ); + fragment_kind.expect_from_annotatables( + single_delegations.map(|item| Annotatable::ImplItem(P(item))), + ) + } }) } @@ -1060,7 +1102,7 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized { fn take_mac_call(self) -> (P, Self::AttrsTy, AddSemicolon) { unreachable!() } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { None } fn delegation_item_kind(_deleg: Box) -> Self::ItemKind { @@ -1119,7 +1161,7 @@ impl InvocationCollectorNode for P { _ => unreachable!(), } } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { match &self.kind { ItemKind::DelegationMac(deleg) => Some((deleg, self)), _ => None, @@ -1263,7 +1305,7 @@ impl InvocationCollectorNode for AstNodeWrapper, TraitItemTag> _ => unreachable!(), } } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { match &self.wrapped.kind { AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)), _ => None, @@ -1304,7 +1346,7 @@ impl InvocationCollectorNode for AstNodeWrapper, ImplItemTag> _ => unreachable!(), } } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { match &self.wrapped.kind { AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)), _ => None, @@ -1480,7 +1522,7 @@ impl InvocationCollectorNode for ast::Stmt { }; (mac, attrs, if add_semicolon { AddSemicolon::Yes } else { AddSemicolon::No }) } - fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { + fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item)> { match &self.kind { StmtKind::Item(item) => match &item.kind { ItemKind::DelegationMac(deleg) => Some((deleg, item)), @@ -1677,6 +1719,44 @@ impl InvocationCollectorNode for AstNodeWrapper, MethodReceiverTag> } } +fn build_single_delegations<'a, Node: InvocationCollectorNode>( + ecx: &ExtCtxt<'_>, + deleg: &'a ast::DelegationMac, + item: &'a ast::Item, + suffixes: &'a [(Ident, Option)], + item_span: Span, + from_glob: bool, +) -> impl Iterator> + 'a { + if suffixes.is_empty() { + // Report an error for now, to avoid keeping stem for resolution and + // stability checks. + let kind = String::from(if from_glob { "glob" } else { "list" }); + ecx.dcx().emit_err(EmptyDelegationMac { span: item.span, kind }); + } + + suffixes.iter().map(move |&(ident, rename)| { + let mut path = deleg.prefix.clone(); + path.segments.push(ast::PathSegment { ident, id: ast::DUMMY_NODE_ID, args: None }); + + ast::Item { + attrs: item.attrs.clone(), + id: ast::DUMMY_NODE_ID, + span: if from_glob { item_span } else { ident.span }, + vis: item.vis.clone(), + ident: rename.unwrap_or(ident), + kind: Node::delegation_item_kind(Box::new(ast::Delegation { + id: ast::DUMMY_NODE_ID, + qself: deleg.qself.clone(), + path, + rename, + body: deleg.body.clone(), + from_glob, + })), + tokens: None, + } + }) +} + struct InvocationCollector<'a, 'b> { cx: &'a mut ExtCtxt<'b>, invocations: Vec<(Invocation, Option>)>, @@ -1695,6 +1775,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect(&mut self, fragment_kind: AstFragmentKind, kind: InvocationKind) -> AstFragment { let expn_id = LocalExpnId::fresh_empty(); + if matches!(kind, InvocationKind::GlobDelegation { .. }) { + // In resolver we need to know which invocation ids are delegations early, + // before their `ExpnData` is filled. + self.cx.resolver.register_glob_delegation(expn_id); + } let vis = kind.placeholder_visibility(); self.invocations.push(( Invocation { @@ -1727,6 +1812,14 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.collect(kind, InvocationKind::Attr { attr, pos, item, derives }) } + fn collect_glob_delegation( + &mut self, + item: P, + kind: AstFragmentKind, + ) -> AstFragment { + self.collect(kind, InvocationKind::GlobDelegation { item }) + } + /// If `item` is an attribute invocation, remove the attribute and return it together with /// its position and derives following it. We have to collect the derives in order to resolve /// legacy derive helpers (helpers written before derives that introduce them). @@ -1799,11 +1892,10 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } if attr.is_doc_comment() { - self.cx.sess.psess.buffer_lint_with_diagnostic( + self.cx.sess.psess.buffer_lint( UNUSED_DOC_COMMENTS, current_span, self.cx.current_expansion.lint_node_id, - "unused doc comment", BuiltinLintDiag::UnusedDocComment(attr.span), ); } else if rustc_attr::is_builtin_attr(attr) { @@ -1811,11 +1903,10 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { // `#[cfg]` and `#[cfg_attr]` are special - they are // eagerly evaluated. if attr_name != sym::cfg && attr_name != sym::cfg_attr { - self.cx.sess.psess.buffer_lint_with_diagnostic( + self.cx.sess.psess.buffer_lint( UNUSED_ATTRIBUTES, attr.span, self.cx.current_expansion.lint_node_id, - format!("unused attribute `{attr_name}`"), BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name: pprust::path_to_string(&call.path), @@ -1896,37 +1987,27 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { Node::post_flat_map_node_collect_bang(&mut res, add_semicolon); res } - None if let Some((deleg, item)) = node.delegation_list() => { - if deleg.suffixes.is_empty() { - // Report an error for now, to avoid keeping stem for resolution and - // stability checks. - self.cx.dcx().emit_err(EmptyDelegationList { span: item.span }); - } - - Node::flatten_outputs(deleg.suffixes.iter().map(|&(ident, rename)| { - let mut path = deleg.prefix.clone(); - path.segments.push(ast::PathSegment { - ident, - id: ast::DUMMY_NODE_ID, - args: None, - }); - - let mut item = Node::from_item(ast::Item { - attrs: item.attrs.clone(), - id: ast::DUMMY_NODE_ID, - span: ident.span, - vis: item.vis.clone(), - ident: rename.unwrap_or(ident), - kind: Node::delegation_item_kind(Box::new(ast::Delegation { - id: ast::DUMMY_NODE_ID, - qself: deleg.qself.clone(), - path, - rename, - body: deleg.body.clone(), - })), - tokens: None, - }); + None if let Some((deleg, item)) = node.delegation() => { + let Some(suffixes) = &deleg.suffixes else { + let item = match node.to_annotatable() { + Annotatable::ImplItem(item) => item, + ann @ (Annotatable::Item(_) + | Annotatable::TraitItem(_) + | Annotatable::Stmt(_)) => { + let span = ann.span(); + self.cx.dcx().emit_err(GlobDelegationOutsideImpls { span }); + return Default::default(); + } + _ => unreachable!(), + }; + return self.collect_glob_delegation(item, Node::KIND).make_ast::(); + }; + let single_delegations = build_single_delegations::( + self.cx, deleg, item, suffixes, item.span, false, + ); + Node::flatten_outputs(single_delegations.map(|item| { + let mut item = Node::from_item(item); assign_id!(self, item.node_id_mut(), || item.noop_flat_map(self)) })) } @@ -1978,7 +2059,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.collect_bang(mac, Node::KIND).make_ast::() }) } - None if node.delegation_list().is_some() => unreachable!(), + None if node.delegation().is_some() => unreachable!(), None => { assign_id!(self, node.node_id_mut(), || node.noop_visit(self)) } diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs index a805c4fcf7b9..08d4a0394548 100644 --- a/compiler/rustc_expand/src/mbe.rs +++ b/compiler/rustc_expand/src/mbe.rs @@ -68,12 +68,15 @@ pub(crate) enum KleeneOp { /// `MetaVarExpr` are "first-class" token trees. Useful for parsing macros. #[derive(Debug, PartialEq, Encodable, Decodable)] enum TokenTree { + /// A token. Unlike `tokenstream::TokenTree::Token` this lacks a `Spacing`. + /// See the comments about `Spacing` in the `transcribe` function. Token(Token), /// A delimited sequence, e.g. `($e:expr)` (RHS) or `{ $e }` (LHS). Delimited(DelimSpan, DelimSpacing, Delimited), /// A kleene-style repetition sequence, e.g. `$($e:expr)*` (RHS) or `$($e),*` (LHS). Sequence(DelimSpan, SequenceRepetition), - /// e.g., `$var`. + /// e.g., `$var`. The span covers the leading dollar and the ident. (The span within the ident + /// only covers the ident, e.g. `var`.) MetaVar(Span, Ident), /// e.g., `$var:expr`. Only appears on the LHS. MetaVarDecl(Span, Ident /* name to bind */, Option), diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 442fd654b6ae..bf475c1dc96f 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -7,7 +7,7 @@ use crate::mbe::{ use rustc_ast::token::{self, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; -use rustc_errors::{Applicability, Diag, DiagCtxt, DiagMessage}; +use rustc_errors::{Applicability, Diag, DiagMessage}; use rustc_macros::Subdiagnostic; use rustc_parse::parser::{Parser, Recovery}; use rustc_span::source_map::SourceMap; @@ -61,7 +61,7 @@ pub(super) fn failed_to_match_macro<'cx>( err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro"); } - annotate_doc_comment(cx.sess.dcx(), &mut err, psess.source_map(), span); + annotate_doc_comment(&mut err, psess.source_map(), span); if let Some(span) = remaining_matcher.span() { err.span_note(span, format!("while trying to match {remaining_matcher}")); @@ -324,12 +324,12 @@ enum ExplainDocComment { }, } -pub(super) fn annotate_doc_comment(dcx: &DiagCtxt, err: &mut Diag<'_>, sm: &SourceMap, span: Span) { +pub(super) fn annotate_doc_comment(err: &mut Diag<'_>, sm: &SourceMap, span: Span) { if let Ok(src) = sm.span_to_snippet(span) { if src.starts_with("///") || src.starts_with("/**") { - err.subdiagnostic(dcx, ExplainDocComment::Outer { span }); + err.subdiagnostic(ExplainDocComment::Outer { span }); } else if src.starts_with("//!") || src.starts_with("/*!") { - err.subdiagnostic(dcx, ExplainDocComment::Inner { span }); + err.subdiagnostic(ExplainDocComment::Inner { span }); } } } diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index dce8e0c36edf..d9a945a32150 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -110,7 +110,8 @@ use crate::mbe::{KleeneToken, TokenTree}; use rustc_ast::token::{Delimiter, IdentIsRaw, Token, TokenKind}; use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{DiagMessage, MultiSpan}; +use rustc_errors::MultiSpan; +use rustc_lint_defs::BuiltinLintDiag; use rustc_session::lint::builtin::{META_VARIABLE_MISUSE, MISSING_FRAGMENT_SPECIFIER}; use rustc_session::parse::ParseSess; use rustc_span::symbol::kw; @@ -205,7 +206,7 @@ pub(super) fn check_meta_variables( rhses: &[TokenTree], ) -> Result<(), ErrorGuaranteed> { if lhses.len() != rhses.len() { - psess.dcx.span_bug(span, "length mismatch between LHSes and RHSes") + psess.dcx().span_bug(span, "length mismatch between LHSes and RHSes") } let mut guar = None; for (lhs, rhs) in iter::zip(lhses, rhses) { @@ -244,7 +245,7 @@ fn check_binders( // MetaVar(fragment) and not as MetaVarDecl(y, fragment). TokenTree::MetaVar(span, name) => { if macros.is_empty() { - psess.dcx.span_bug(span, "unexpected MetaVar in lhs"); + psess.dcx().span_bug(span, "unexpected MetaVar in lhs"); } let name = MacroRulesNormalizedIdent::new(name); // There are 3 possibilities: @@ -252,7 +253,7 @@ fn check_binders( // 1. The meta-variable is already bound in the current LHS: This is an error. let mut span = MultiSpan::from_span(span); span.push_span_label(prev_info.span, "previous declaration"); - buffer_lint(psess, span, node_id, "duplicate matcher binding"); + buffer_lint(psess, span, node_id, BuiltinLintDiag::DuplicateMatcherBinding); } else if get_binder_info(macros, binders, name).is_none() { // 2. The meta-variable is free: This is a binder. binders.insert(name, BinderInfo { span, ops: ops.into() }); @@ -271,11 +272,11 @@ fn check_binders( MISSING_FRAGMENT_SPECIFIER, span, node_id, - "missing fragment specifier", + BuiltinLintDiag::MissingFragmentSpecifier, ); } if !macros.is_empty() { - psess.dcx.span_bug(span, "unexpected MetaVarDecl in nested lhs"); + psess.dcx().span_bug(span, "unexpected MetaVarDecl in nested lhs"); } let name = MacroRulesNormalizedIdent::new(name); if let Some(prev_info) = get_binder_info(macros, binders, name) { @@ -283,7 +284,7 @@ fn check_binders( // for nested macro definitions. *guar = Some( psess - .dcx + .dcx() .emit_err(errors::DuplicateMatcherBinding { span, prev: prev_info.span }), ); } else { @@ -343,7 +344,7 @@ fn check_occurrences( match *rhs { TokenTree::Token(..) => {} TokenTree::MetaVarDecl(span, _name, _kind) => { - psess.dcx.span_bug(span, "unexpected MetaVarDecl in rhs") + psess.dcx().span_bug(span, "unexpected MetaVarDecl in rhs") } TokenTree::MetaVar(span, name) => { let name = MacroRulesNormalizedIdent::new(name); @@ -595,7 +596,7 @@ fn check_ops_is_prefix( return; } } - buffer_lint(psess, span.into(), node_id, format!("unknown macro variable `{name}`")); + buffer_lint(psess, span.into(), node_id, BuiltinLintDiag::UnknownMacroVariable(name)); } /// Returns whether `binder_ops` is a prefix of `occurrence_ops`. @@ -628,8 +629,7 @@ fn ops_is_prefix( if i >= occurrence_ops.len() { let mut span = MultiSpan::from_span(span); span.push_span_label(binder.span, "expected repetition"); - let message = format!("variable '{name}' is still repeating at this depth"); - buffer_lint(psess, span, node_id, message); + buffer_lint(psess, span, node_id, BuiltinLintDiag::MetaVariableStillRepeating(name)); return; } let occurrence = &occurrence_ops[i]; @@ -637,21 +637,15 @@ fn ops_is_prefix( let mut span = MultiSpan::from_span(span); span.push_span_label(binder.span, "expected repetition"); span.push_span_label(occurrence.span, "conflicting repetition"); - let message = "meta-variable repeats with different Kleene operator"; - buffer_lint(psess, span, node_id, message); + buffer_lint(psess, span, node_id, BuiltinLintDiag::MetaVariableWrongOperator); return; } } } -fn buffer_lint( - psess: &ParseSess, - span: MultiSpan, - node_id: NodeId, - message: impl Into, -) { +fn buffer_lint(psess: &ParseSess, span: MultiSpan, node_id: NodeId, diag: BuiltinLintDiag) { // Macros loaded from other crates have dummy node ids. if node_id != DUMMY_NODE_ID { - psess.buffer_lint(META_VARIABLE_MISUSE, span, node_id, message); + psess.buffer_lint(META_VARIABLE_MISUSE, span, node_id, diag); } } diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 8f18055f8381..49b1f5ce0e3e 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -79,11 +79,10 @@ impl<'a> ParserAnyMacro<'a> { // but `m!()` is allowed in expression positions (cf. issue #34706). if kind == AstFragmentKind::Expr && parser.token == token::Semi { if is_local { - parser.psess.buffer_lint_with_diagnostic( + parser.psess.buffer_lint( SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, parser.token.span, lint_node_id, - "trailing semicolon in macro used in expression position", BuiltinLintDiag::TrailingMacro(is_trailing_mac, macro_ident), ); } @@ -224,7 +223,8 @@ fn expand_macro<'cx>( let arm_span = rhses[i].span(); // rhs has holes ( `$id` and `$(...)` that need filled) - let tts = match transcribe(cx, &named_matches, rhs, rhs_span, transparency) { + let id = cx.current_expansion.id; + let tts = match transcribe(psess, &named_matches, rhs, rhs_span, transparency, id) { Ok(tts) => tts, Err(err) => { let guar = err.emit(); @@ -383,7 +383,7 @@ pub fn compile_declarative_macro( }; let dummy_syn_ext = |guar| (mk_syn_ext(Box::new(DummyExpander(guar))), Vec::new()); - let dcx = &sess.psess.dcx; + let dcx = sess.dcx(); let lhs_nm = Ident::new(sym::lhs, def.span); let rhs_nm = Ident::new(sym::rhs, def.span); let tt_spec = Some(NonterminalKind::TT); @@ -463,7 +463,7 @@ pub fn compile_declarative_macro( let sp = token.span.substitute_dummy(def.span); let mut err = sess.dcx().struct_span_err(sp, s); err.span_label(sp, msg); - annotate_doc_comment(sess.dcx(), &mut err, sess.source_map(), sp); + annotate_doc_comment(&mut err, sess.source_map(), sp); let guar = err.emit(); return dummy_syn_ext(guar); } @@ -1154,11 +1154,10 @@ fn check_matcher_core<'tt>( name, Some(NonterminalKind::PatParam { inferred: false }), )); - sess.psess.buffer_lint_with_diagnostic( + sess.psess.buffer_lint( RUST_2021_INCOMPATIBLE_OR_PATTERNS, span, ast::CRATE_NODE_ID, - "the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro", BuiltinLintDiag::OrPatternsBackCompat(span, suggestion), ); } diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index 128e9f48ff57..25958e03028f 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -8,9 +8,14 @@ use rustc_session::parse::ParseSess; use rustc_span::symbol::Ident; use rustc_span::Span; +pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers"; + /// A meta-variable expression, for expansions based on properties of meta-variables. -#[derive(Debug, Clone, PartialEq, Encodable, Decodable)] +#[derive(Debug, PartialEq, Encodable, Decodable)] pub(crate) enum MetaVarExpr { + /// Unification of two or more identifiers. + Concat(Box<[MetaVarExprConcatElem]>), + /// The number of repetitions of an identifier. Count(Ident, usize), @@ -37,11 +42,36 @@ impl MetaVarExpr { let ident = parse_ident(&mut tts, psess, outer_span)?; let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = tts.next() else { let msg = "meta-variable expression parameter must be wrapped in parentheses"; - return Err(psess.dcx.struct_span_err(ident.span, msg)); + return Err(psess.dcx().struct_span_err(ident.span, msg)); }; check_trailing_token(&mut tts, psess)?; let mut iter = args.trees(); let rslt = match ident.as_str() { + "concat" => { + let mut result = Vec::new(); + loop { + let is_var = try_eat_dollar(&mut iter); + let element_ident = parse_ident(&mut iter, psess, outer_span)?; + let element = if is_var { + MetaVarExprConcatElem::Var(element_ident) + } else { + MetaVarExprConcatElem::Ident(element_ident) + }; + result.push(element); + if iter.look_ahead(0).is_none() { + break; + } + if !try_eat_comma(&mut iter) { + return Err(psess.dcx().struct_span_err(outer_span, "expected comma")); + } + } + if result.len() < 2 { + return Err(psess + .dcx() + .struct_span_err(ident.span, "`concat` must have at least two elements")); + } + MetaVarExpr::Concat(result.into()) + } "count" => parse_count(&mut iter, psess, ident.span)?, "ignore" => { eat_dollar(&mut iter, psess, ident.span)?; @@ -51,7 +81,7 @@ impl MetaVarExpr { "len" => MetaVarExpr::Len(parse_depth(&mut iter, psess, ident.span)?), _ => { let err_msg = "unrecognized meta-variable expression"; - let mut err = psess.dcx.struct_span_err(ident.span, err_msg); + let mut err = psess.dcx().struct_span_err(ident.span, err_msg); err.span_suggestion( ident.span, "supported expressions are count, ignore, index and len", @@ -68,11 +98,21 @@ impl MetaVarExpr { pub(crate) fn ident(&self) -> Option { match *self { MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => Some(ident), - MetaVarExpr::Index(..) | MetaVarExpr::Len(..) => None, + MetaVarExpr::Concat { .. } | MetaVarExpr::Index(..) | MetaVarExpr::Len(..) => None, } } } +#[derive(Debug, Decodable, Encodable, PartialEq)] +pub(crate) enum MetaVarExprConcatElem { + /// There is NO preceding dollar sign, which means that this identifier should be interpreted + /// as a literal. + Ident(Ident), + /// There is a preceding dollar sign, which means that this identifier should be expanded + /// and interpreted as a variable. + Var(Ident), +} + // Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}` fn check_trailing_token<'psess>( iter: &mut RefTokenTreeCursor<'_>, @@ -80,7 +120,7 @@ fn check_trailing_token<'psess>( ) -> PResult<'psess, ()> { if let Some(tt) = iter.next() { let mut diag = psess - .dcx + .dcx() .struct_span_err(tt.span(), format!("unexpected token: {}", pprust::tt_to_string(tt))); diag.span_note(tt.span(), "meta-variable expression must not have trailing tokens"); Err(diag) @@ -99,7 +139,7 @@ fn parse_count<'psess>( let ident = parse_ident(iter, psess, span)?; let depth = if try_eat_comma(iter) { if iter.look_ahead(0).is_none() { - return Err(psess.dcx.struct_span_err( + return Err(psess.dcx().struct_span_err( span, "`count` followed by a comma must have an associated index indicating its depth", )); @@ -120,7 +160,7 @@ fn parse_depth<'psess>( let Some(tt) = iter.next() else { return Ok(0) }; let TokenTree::Token(token::Token { kind: token::TokenKind::Literal(lit), .. }, _) = tt else { return Err(psess - .dcx + .dcx() .struct_span_err(span, "meta-variable expression depth must be a literal")); }; if let Ok(lit_kind) = LitKind::from_token_lit(*lit) @@ -130,7 +170,7 @@ fn parse_depth<'psess>( Ok(n_usize) } else { let msg = "only unsuffixes integer literals are supported in meta-variable expressions"; - Err(psess.dcx.struct_span_err(span, msg)) + Err(psess.dcx().struct_span_err(span, msg)) } } @@ -138,26 +178,31 @@ fn parse_depth<'psess>( fn parse_ident<'psess>( iter: &mut RefTokenTreeCursor<'_>, psess: &'psess ParseSess, - span: Span, + fallback_span: Span, ) -> PResult<'psess, Ident> { - if let Some(tt) = iter.next() - && let TokenTree::Token(token, _) = tt - { - if let Some((elem, IdentIsRaw::No)) = token.ident() { - return Ok(elem); + let Some(tt) = iter.next() else { + return Err(psess.dcx().struct_span_err(fallback_span, "expected identifier")); + }; + let TokenTree::Token(token, _) = tt else { + return Err(psess.dcx().struct_span_err(tt.span(), "expected identifier")); + }; + if let Some((elem, is_raw)) = token.ident() { + if let IdentIsRaw::Yes = is_raw { + return Err(psess.dcx().struct_span_err(elem.span, RAW_IDENT_ERR)); } - let token_str = pprust::token_to_string(token); - let mut err = - psess.dcx.struct_span_err(span, format!("expected identifier, found `{}`", &token_str)); - err.span_suggestion( - token.span, - format!("try removing `{}`", &token_str), - "", - Applicability::MaybeIncorrect, - ); - return Err(err); + return Ok(elem); } - Err(psess.dcx.struct_span_err(span, "expected identifier")) + let token_str = pprust::token_to_string(token); + let mut err = psess + .dcx() + .struct_span_err(token.span, format!("expected identifier, found `{token_str}`")); + err.span_suggestion( + token.span, + format!("try removing `{token_str}`"), + "", + Applicability::MaybeIncorrect, + ); + Err(err) } /// Tries to move the iterator forward returning `true` if there is a comma. If not, then the @@ -170,6 +215,17 @@ fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool { false } +/// Tries to move the iterator forward returning `true` if there is a dollar sign. If not, then the +/// iterator is not modified and the result is `false`. +fn try_eat_dollar(iter: &mut RefTokenTreeCursor<'_>) -> bool { + if let Some(TokenTree::Token(token::Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) + { + let _ = iter.next(); + return true; + } + false +} + /// Expects that the next item is a dollar sign. fn eat_dollar<'psess>( iter: &mut RefTokenTreeCursor<'_>, @@ -181,7 +237,7 @@ fn eat_dollar<'psess>( let _ = iter.next(); return Ok(()); } - Err(psess.dcx.struct_span_err( + Err(psess.dcx().struct_span_err( span, "meta-variables within meta-variable expressions must be referenced using a dollar sign", )) diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index d3ea48e2e2a8..74f78c0ef785 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -62,7 +62,10 @@ pub(super) fn parse( match tree { TokenTree::MetaVar(start_sp, ident) if parsing_patterns => { let span = match trees.next() { - Some(&tokenstream::TokenTree::Token(Token { kind: token::Colon, span }, _)) => { + Some(&tokenstream::TokenTree::Token( + Token { kind: token::Colon, span: colon_span }, + _, + )) => { match trees.next() { Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() { Some((fragment, _)) => { @@ -126,10 +129,12 @@ pub(super) fn parse( } _ => token.span, }, - tree => tree.map_or(span, tokenstream::TokenTree::span), + Some(tree) => tree.span(), + None => colon_span, } } - tree => tree.map_or(start_sp, tokenstream::TokenTree::span), + Some(tree) => tree.span(), + None => start_sp, }; result.push(TokenTree::MetaVarDecl(span, ident, None)); @@ -150,6 +155,13 @@ fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &Session, sp } } +fn maybe_emit_macro_metavar_expr_concat_feature(features: &Features, sess: &Session, span: Span) { + if !features.macro_metavar_expr_concat { + let msg = "the `concat` meta-variable expression is unstable"; + feature_err(sess, sym::macro_metavar_expr_concat, span, msg).emit(); + } +} + /// Takes a `tokenstream::TokenTree` and returns a `self::TokenTree`. Specifically, this takes a /// generic `TokenTree`, such as is used in the rest of the compiler, and returns a `TokenTree` /// for use in parsing a macro. @@ -176,7 +188,7 @@ fn parse_tree<'a>( // Depending on what `tree` is, we could be parsing different parts of a macro match tree { // `tree` is a `$` token. Look at the next token in `trees` - &tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _) => { + &tokenstream::TokenTree::Token(Token { kind: token::Dollar, span: dollar_span }, _) => { // FIXME: Handle `Invisible`-delimited groups in a more systematic way // during parsing. let mut next = outer_trees.next(); @@ -209,14 +221,22 @@ fn parse_tree<'a>( err.emit(); // Returns early the same read `$` to avoid spanning // unrelated diagnostics that could be performed afterwards - return TokenTree::token(token::Dollar, span); + return TokenTree::token(token::Dollar, dollar_span); } Ok(elem) => { - maybe_emit_macro_metavar_expr_feature( - features, - sess, - delim_span.entire(), - ); + if let MetaVarExpr::Concat(_) = elem { + maybe_emit_macro_metavar_expr_concat_feature( + features, + sess, + delim_span.entire(), + ); + } else { + maybe_emit_macro_metavar_expr_feature( + features, + sess, + delim_span.entire(), + ); + } return TokenTree::MetaVarExpr(delim_span, elem); } } @@ -251,7 +271,7 @@ fn parse_tree<'a>( // special metavariable that names the crate of the invocation. Some(tokenstream::TokenTree::Token(token, _)) if token.is_ident() => { let (ident, is_raw) = token.ident().unwrap(); - let span = ident.span.with_lo(span.lo()); + let span = ident.span.with_lo(dollar_span.lo()); if ident.name == kw::Crate && matches!(is_raw, IdentIsRaw::No) { TokenTree::token(token::Ident(kw::DollarCrate, is_raw), span) } else { @@ -260,16 +280,19 @@ fn parse_tree<'a>( } // `tree` is followed by another `$`. This is an escaped `$`. - Some(&tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _)) => { + Some(&tokenstream::TokenTree::Token( + Token { kind: token::Dollar, span: dollar_span2 }, + _, + )) => { if parsing_patterns { span_dollar_dollar_or_metavar_in_the_lhs_err( sess, - &Token { kind: token::Dollar, span }, + &Token { kind: token::Dollar, span: dollar_span2 }, ); } else { - maybe_emit_macro_metavar_expr_feature(features, sess, span); + maybe_emit_macro_metavar_expr_feature(features, sess, dollar_span2); } - TokenTree::token(token::Dollar, span) + TokenTree::token(token::Dollar, dollar_span2) } // `tree` is followed by some other token. This is an error. @@ -281,7 +304,7 @@ fn parse_tree<'a>( } // There are no more tokens. Just return the `$` we already have. - None => TokenTree::token(token::Dollar, span), + None => TokenTree::token(token::Dollar, dollar_span), } } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 3901b82eb52e..f935f1b77e0b 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -1,20 +1,21 @@ -use crate::base::ExtCtxt; use crate::errors::{ CountRepetitionMisplaced, MetaVarExprUnrecognizedVar, MetaVarsDifSeqMatchers, MustRepeatOnce, NoSyntaxVarsExprRepeat, VarStillRepeating, }; use crate::mbe::macro_parser::{NamedMatch, NamedMatch::*}; +use crate::mbe::metavar_expr::{MetaVarExprConcatElem, RAW_IDENT_ERR}; use crate::mbe::{self, KleeneOp, MetaVarExpr}; use rustc_ast::mut_visit::{self, MutVisitor}; +use rustc_ast::token::IdentIsRaw; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{pluralize, Diag, PResult}; +use rustc_errors::{pluralize, Diag, DiagCtxtHandle, PResult}; use rustc_parse::parser::ParseNtResult; +use rustc_session::parse::ParseSess; use rustc_span::hygiene::{LocalExpnId, Transparency}; use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent}; -use rustc_span::{with_metavar_spans, Span, SyntaxContext}; - +use rustc_span::{with_metavar_spans, Span, Symbol, SyntaxContext}; use smallvec::{smallvec, SmallVec}; use std::mem; @@ -30,11 +31,11 @@ impl MutVisitor for Marker { // it's some advanced case with macro-generated macros. So if we cache the marked version // of that context once, we'll typically have a 100% cache hit rate after that. let Marker(expn_id, transparency, ref mut cache) = *self; - let data = span.data(); - let marked_ctxt = *cache - .entry(data.ctxt) - .or_insert_with(|| data.ctxt.apply_mark(expn_id.to_expn_id(), transparency)); - *span = data.with_ctxt(marked_ctxt); + *span = span.map_ctxt(|ctxt| { + *cache + .entry(ctxt) + .or_insert_with(|| ctxt.apply_mark(expn_id.to_expn_id(), transparency)) + }); } } @@ -99,11 +100,12 @@ impl<'a> Iterator for Frame<'a> { /// /// Along the way, we do some additional error checking. pub(super) fn transcribe<'a>( - cx: &ExtCtxt<'a>, + psess: &'a ParseSess, interp: &FxHashMap, src: &mbe::Delimited, src_span: DelimSpan, transparency: Transparency, + expand_id: LocalExpnId, ) -> PResult<'a, TokenStream> { // Nothing for us to transcribe... if src.tts.is_empty() { @@ -137,8 +139,9 @@ pub(super) fn transcribe<'a>( // again, and we are done transcribing. let mut result: Vec = Vec::new(); let mut result_stack = Vec::new(); - let mut marker = Marker(cx.current_expansion.id, transparency, Default::default()); + let mut marker = Marker(expand_id, transparency, Default::default()); + let dcx = psess.dcx(); loop { // Look at the last frame on the stack. // If it still has a TokenTree we have not looked at yet, use that tree. @@ -201,9 +204,7 @@ pub(super) fn transcribe<'a>( seq @ mbe::TokenTree::Sequence(_, seq_rep) => { match lockstep_iter_size(seq, interp, &repeats) { LockstepIterSize::Unconstrained => { - return Err(cx - .dcx() - .create_err(NoSyntaxVarsExprRepeat { span: seq.span() })); + return Err(dcx.create_err(NoSyntaxVarsExprRepeat { span: seq.span() })); } LockstepIterSize::Contradiction(msg) => { @@ -211,9 +212,9 @@ pub(super) fn transcribe<'a>( // happens when two meta-variables are used in the same repetition in a // sequence, but they come from different sequence matchers and repeat // different amounts. - return Err(cx - .dcx() - .create_err(MetaVarsDifSeqMatchers { span: seq.span(), msg })); + return Err( + dcx.create_err(MetaVarsDifSeqMatchers { span: seq.span(), msg }) + ); } LockstepIterSize::Constraint(len, _) => { @@ -227,9 +228,7 @@ pub(super) fn transcribe<'a>( // FIXME: this really ought to be caught at macro definition // time... It happens when the Kleene operator in the matcher and // the body for the same meta-variable do not match. - return Err(cx - .dcx() - .create_err(MustRepeatOnce { span: sp.entire() })); + return Err(dcx.create_err(MustRepeatOnce { span: sp.entire() })); } } else { // 0 is the initial counter (we have done 0 repetitions so far). `len` @@ -253,13 +252,27 @@ pub(super) fn transcribe<'a>( mbe::TokenTree::MetaVar(mut sp, mut original_ident) => { // Find the matched nonterminal from the macro invocation, and use it to replace // the meta-var. + // + // We use `Spacing::Alone` everywhere here, because that's the conservative choice + // and spacing of declarative macros is tricky. E.g. in this macro: + // ``` + // macro_rules! idents { + // ($($a:ident,)*) => { stringify!($($a)*) } + // } + // ``` + // `$a` has no whitespace after it and will be marked `JointHidden`. If you then + // call `idents!(x,y,z,)`, each of `x`, `y`, and `z` will be marked as `Joint`. So + // if you choose to use `$x`'s spacing or the identifier's spacing, you'll end up + // producing "xyz", which is bad because it effectively merges tokens. + // `Spacing::Alone` is the safer option. Fortunately, `space_between` will avoid + // some of the unnecessary whitespace. let ident = MacroRulesNormalizedIdent::new(original_ident); if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) { let tt = match cur_matched { MatchedSingle(ParseNtResult::Tt(tt)) => { // `tt`s are emitted into the output stream directly as "raw tokens", // without wrapping them into groups. - maybe_use_metavar_location(cx, &stack, sp, tt, &mut marker) + maybe_use_metavar_location(psess, &stack, sp, tt, &mut marker) } MatchedSingle(ParseNtResult::Ident(ident, is_raw)) => { marker.visit_span(&mut sp); @@ -280,7 +293,7 @@ pub(super) fn transcribe<'a>( } MatchedSeq(..) => { // We were unable to descend far enough. This is an error. - return Err(cx.dcx().create_err(VarStillRepeating { span: sp, ident })); + return Err(dcx.create_err(VarStillRepeating { span: sp, ident })); } }; result.push(tt) @@ -299,7 +312,7 @@ pub(super) fn transcribe<'a>( // Replace meta-variable expressions with the result of their expansion. mbe::TokenTree::MetaVarExpr(sp, expr) => { - transcribe_metavar_expr(cx, expr, interp, &mut marker, &repeats, &mut result, sp)?; + transcribe_metavar_expr(dcx, expr, interp, &mut marker, &repeats, &mut result, sp)?; } // If we are entering a new delimiter, we push its contents to the `stack` to be @@ -359,7 +372,7 @@ pub(super) fn transcribe<'a>( /// combine with each other and not with tokens outside of the sequence. /// - The metavariable span comes from a different crate, then we prefer the more local span. fn maybe_use_metavar_location( - cx: &ExtCtxt<'_>, + psess: &ParseSess, stack: &[Frame<'_>], mut metavar_span: Span, orig_tt: &TokenTree, @@ -397,7 +410,7 @@ fn maybe_use_metavar_location( && insert(mspans, dspan.entire(), metavar_span) }), }; - if no_collision || cx.source_map().is_imported(metavar_span) { + if no_collision || psess.source_map().is_imported(metavar_span) { return orig_tt.clone(); } @@ -558,7 +571,7 @@ fn lockstep_iter_size( /// * `[ $( ${count(foo, 1)} ),* ]` will return an error because `${count(foo, 1)}` is /// declared inside a single repetition and the index `1` implies two nested repetitions. fn count_repetitions<'a>( - cx: &ExtCtxt<'a>, + dcx: DiagCtxtHandle<'a>, depth_user: usize, mut matched: &NamedMatch, repeats: &[(usize, usize)], @@ -595,7 +608,7 @@ fn count_repetitions<'a>( .and_then(|el| el.checked_sub(repeats.len())) .unwrap_or_default(); if depth_user > depth_max { - return Err(out_of_bounds_err(cx, depth_max + 1, sp.entire(), "count")); + return Err(out_of_bounds_err(dcx, depth_max + 1, sp.entire(), "count")); } // `repeats` records all of the nested levels at which we are currently @@ -611,7 +624,7 @@ fn count_repetitions<'a>( } if let MatchedSingle(_) = matched { - return Err(cx.dcx().create_err(CountRepetitionMisplaced { span: sp.entire() })); + return Err(dcx.create_err(CountRepetitionMisplaced { span: sp.entire() })); } count(depth_user, depth_max, matched) @@ -619,7 +632,7 @@ fn count_repetitions<'a>( /// Returns a `NamedMatch` item declared on the LHS given an arbitrary [Ident] fn matched_from_ident<'ctx, 'interp, 'rslt>( - cx: &ExtCtxt<'ctx>, + dcx: DiagCtxtHandle<'ctx>, ident: Ident, interp: &'interp FxHashMap, ) -> PResult<'ctx, &'rslt NamedMatch> @@ -628,12 +641,12 @@ where { let span = ident.span; let key = MacroRulesNormalizedIdent::new(ident); - interp.get(&key).ok_or_else(|| cx.dcx().create_err(MetaVarExprUnrecognizedVar { span, key })) + interp.get(&key).ok_or_else(|| dcx.create_err(MetaVarExprUnrecognizedVar { span, key })) } /// Used by meta-variable expressions when an user input is out of the actual declared bounds. For /// example, index(999999) in an repetition of only three elements. -fn out_of_bounds_err<'a>(cx: &ExtCtxt<'a>, max: usize, span: Span, ty: &str) -> Diag<'a> { +fn out_of_bounds_err<'a>(dcx: DiagCtxtHandle<'a>, max: usize, span: Span, ty: &str) -> Diag<'a> { let msg = if max == 0 { format!( "meta-variable expression `{ty}` with depth parameter \ @@ -645,11 +658,11 @@ fn out_of_bounds_err<'a>(cx: &ExtCtxt<'a>, max: usize, span: Span, ty: &str) -> must be less than {max}" ) }; - cx.dcx().struct_span_err(span, msg) + dcx.struct_span_err(span, msg) } fn transcribe_metavar_expr<'a>( - cx: &ExtCtxt<'a>, + dcx: DiagCtxtHandle<'a>, expr: &MetaVarExpr, interp: &FxHashMap, marker: &mut Marker, @@ -663,9 +676,26 @@ fn transcribe_metavar_expr<'a>( span }; match *expr { + MetaVarExpr::Concat(ref elements) => { + let mut concatenated = String::new(); + for element in elements.into_iter() { + let string = match element { + MetaVarExprConcatElem::Ident(ident) => ident.to_string(), + MetaVarExprConcatElem::Var(ident) => extract_ident(dcx, *ident, interp)?, + }; + concatenated.push_str(&string); + } + // The current implementation marks the span as coming from the macro regardless of + // contexts of the concatenated identifiers but this behavior may change in the + // future. + result.push(TokenTree::Token( + Token::from_ast_ident(Ident::new(Symbol::intern(&concatenated), visited_span())), + Spacing::Alone, + )); + } MetaVarExpr::Count(original_ident, depth) => { - let matched = matched_from_ident(cx, original_ident, interp)?; - let count = count_repetitions(cx, depth, matched, repeats, sp)?; + let matched = matched_from_ident(dcx, original_ident, interp)?; + let count = count_repetitions(dcx, depth, matched, repeats, sp)?; let tt = TokenTree::token_alone( TokenKind::lit(token::Integer, sym::integer(count), None), visited_span(), @@ -674,7 +704,7 @@ fn transcribe_metavar_expr<'a>( } MetaVarExpr::Ignore(original_ident) => { // Used to ensure that `original_ident` is present in the LHS - let _ = matched_from_ident(cx, original_ident, interp)?; + let _ = matched_from_ident(dcx, original_ident, interp)?; } MetaVarExpr::Index(depth) => match repeats.iter().nth_back(depth) { Some((index, _)) => { @@ -683,7 +713,7 @@ fn transcribe_metavar_expr<'a>( visited_span(), )); } - None => return Err(out_of_bounds_err(cx, repeats.len(), sp.entire(), "index")), + None => return Err(out_of_bounds_err(dcx, repeats.len(), sp.entire(), "index")), }, MetaVarExpr::Len(depth) => match repeats.iter().nth_back(depth) { Some((_, length)) => { @@ -692,8 +722,38 @@ fn transcribe_metavar_expr<'a>( visited_span(), )); } - None => return Err(out_of_bounds_err(cx, repeats.len(), sp.entire(), "len")), + None => return Err(out_of_bounds_err(dcx, repeats.len(), sp.entire(), "len")), }, } Ok(()) } + +/// Extracts an identifier that can be originated from a `$var:ident` variable or from a token tree. +fn extract_ident<'a>( + dcx: DiagCtxtHandle<'a>, + ident: Ident, + interp: &FxHashMap, +) -> PResult<'a, String> { + if let NamedMatch::MatchedSingle(pnr) = matched_from_ident(dcx, ident, interp)? { + if let ParseNtResult::Ident(nt_ident, is_raw) = pnr { + if let IdentIsRaw::Yes = is_raw { + return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR)); + } + return Ok(nt_ident.to_string()); + } + if let ParseNtResult::Tt(TokenTree::Token( + Token { kind: TokenKind::Ident(token_ident, is_raw), .. }, + _, + )) = pnr + { + if let IdentIsRaw::Yes = is_raw { + return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR)); + } + return Ok(token_ident.to_string()); + } + } + Err(dcx.struct_span_err( + ident.span, + "`${concat(..)}` currently only accepts identifiers or meta-variables as parameters", + )) +} diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index c8983619e701..506bd445be35 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -5,8 +5,8 @@ use crate::errors::{ use rustc_ast::ptr::P; use rustc_ast::{token, AttrVec, Attribute, Inline, Item, ModSpans}; use rustc_errors::{Diag, ErrorGuaranteed}; -use rustc_parse::new_parser_from_file; use rustc_parse::validate_attr; +use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal}; use rustc_session::parse::ParseSess; use rustc_session::Session; use rustc_span::symbol::{sym, Ident}; @@ -66,7 +66,8 @@ pub(crate) fn parse_external_mod( } // Actually parse the external file as a module. - let mut parser = new_parser_from_file(&sess.psess, &mp.file_path, Some(span)); + let mut parser = + unwrap_or_emit_fatal(new_parser_from_file(&sess.psess, &mp.file_path, Some(span))); let (inner_attrs, items, inner_span) = parser.parse_mod(&token::Eof).map_err(|err| ModError::ParserError(err))?; attrs.extend(inner_attrs); diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 530059e53c21..96145affe0ae 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -4,14 +4,12 @@ use crate::proc_macro_server; use rustc_ast as ast; use rustc_ast::ptr::P; -use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; -use rustc_data_structures::sync::Lrc; use rustc_errors::ErrorGuaranteed; -use rustc_parse::parser::ForceCollect; +use rustc_parse::parser::{ForceCollect, Parser}; use rustc_session::config::ProcMacroExecutionStrategy; use rustc_span::profiling::SpannedEventArgRecorder; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::Span; struct MessagePipe { tx: std::sync::mpsc::SyncSender, @@ -120,18 +118,13 @@ impl MultiItemModifier for DeriveProcMacro { // We need special handling for statement items // (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`) let is_stmt = matches!(item, Annotatable::Stmt(..)); - let hack = crate::base::ann_pretty_printing_compatibility_hack(&item, &ecx.sess); - let input = if hack { - let nt = match item { - Annotatable::Item(item) => token::NtItem(item), - Annotatable::Stmt(stmt) => token::NtStmt(stmt), - _ => unreachable!(), - }; - TokenStream::token_alone(token::Interpolated(Lrc::new(nt)), DUMMY_SP) - } else { - item.to_tokens() - }; + // We used to have an alternative behaviour for crates that needed it. + // We had a lint for a long time, but now we just emit a hard error. + // Eventually we might remove the special case hard error check + // altogether. See #73345. + crate::base::ann_pretty_printing_compatibility_hack(&item, &ecx.sess); + let input = item.to_tokens(); let stream = { let _timer = ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| { @@ -161,8 +154,7 @@ impl MultiItemModifier for DeriveProcMacro { }; let error_count_before = ecx.dcx().err_count(); - let mut parser = - rustc_parse::stream_to_parser(&ecx.sess.psess, stream, Some("proc-macro derive")); + let mut parser = Parser::new(&ecx.sess.psess, stream, Some("proc-macro derive")); let mut items = vec![]; loop { diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 1f3547c841a9..5508358f53bb 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -13,7 +13,8 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan, PResult}; use rustc_parse::lexer::nfc_normalize; -use rustc_parse::parse_stream_from_source_str; +use rustc_parse::parser::Parser; +use rustc_parse::{new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; use rustc_session::parse::ParseSess; use rustc_span::def_id::CrateNum; use rustc_span::symbol::{self, sym, Symbol}; @@ -276,21 +277,20 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec { let stream = TokenStream::from_nonterminal_ast(&nt); - // A hack used to pass AST fragments to attribute and derive - // macros as a single nonterminal token instead of a token - // stream. Such token needs to be "unwrapped" and not - // represented as a delimited group. - // FIXME: It needs to be removed, but there are some - // compatibility issues (see #73345). - if crate::base::nt_pretty_printing_compatibility_hack(&nt, rustc.ecx.sess) { - trees.extend(Self::from_internal((stream, rustc))); - } else { - trees.push(TokenTree::Group(Group { - delimiter: pm::Delimiter::None, - stream: Some(stream), - span: DelimSpan::from_single(span), - })) - } + // We used to have an alternative behaviour for crates that + // needed it: a hack used to pass AST fragments to + // attribute and derive macros as a single nonterminal + // token instead of a token stream. Such token needs to be + // "unwrapped" and not represented as a delimited group. We + // had a lint for a long time, but now we just emit a hard + // error. Eventually we might remove the special case hard + // error check altogether. See #73345. + crate::base::nt_pretty_printing_compatibility_hack(&nt, rustc.ecx.sess); + trees.push(TokenTree::Group(Group { + delimiter: pm::Delimiter::None, + stream: Some(stream), + span: DelimSpan::from_single(span), + })) } OpenDelim(..) | CloseDelim(..) => unreachable!(), @@ -309,10 +309,10 @@ impl ToInternal> use rustc_ast::token::*; // The code below is conservative, using `token_alone`/`Spacing::Alone` - // in most places. When the resulting code is pretty-printed by - // `print_tts` it ends up with spaces between most tokens, which is - // safe but ugly. It's hard in general to do better when working at the - // token level. + // in most places. It's hard in general to do better when working at + // the token level. When the resulting code is pretty-printed by + // `print_tts` the `space_between` function helps avoid a lot of + // unnecessary whitespace, so the results aren't too bad. let (tree, rustc) = self; match tree { TokenTree::Punct(Punct { ch, joint, span }) => { @@ -467,7 +467,8 @@ impl server::FreeFunctions for Rustc<'_, '_> { fn literal_from_str(&mut self, s: &str) -> Result, ()> { let name = FileName::proc_macro_source_code(s); - let mut parser = rustc_parse::new_parser_from_source_str(self.psess(), name, s.to_owned()); + let mut parser = + unwrap_or_emit_fatal(new_parser_from_source_str(self.psess(), name, s.to_owned())); let first_span = parser.token.span.data(); let minus_present = parser.eat(&token::BinOp(token::Minus)); @@ -521,7 +522,7 @@ impl server::FreeFunctions for Rustc<'_, '_> { fn emit_diagnostic(&mut self, diagnostic: Diagnostic) { let message = rustc_errors::DiagMessage::from(diagnostic.message); let mut diag: Diag<'_, ()> = - Diag::new(&self.psess().dcx, diagnostic.level.to_internal(), message); + Diag::new(self.psess().dcx(), diagnostic.level.to_internal(), message); diag.span(MultiSpan::from_spans(diagnostic.spans)); for child in diagnostic.children { // This message comes from another diagnostic, and we are just reconstructing the @@ -539,12 +540,12 @@ impl server::TokenStream for Rustc<'_, '_> { } fn from_str(&mut self, src: &str) -> Self::TokenStream { - parse_stream_from_source_str( + unwrap_or_emit_fatal(source_str_to_stream( + self.psess(), FileName::proc_macro_source_code(src), src.to_string(), - self.psess(), Some(self.call_site), - ) + )) } fn to_string(&mut self, stream: &Self::TokenStream) -> String { @@ -554,11 +555,7 @@ impl server::TokenStream for Rustc<'_, '_> { fn expand_expr(&mut self, stream: &Self::TokenStream) -> Result { // Parse the expression from our tokenstream. let expr: PResult<'_, _> = try { - let mut p = rustc_parse::stream_to_parser( - self.psess(), - stream.clone(), - Some("proc_macro expand expr"), - ); + let mut p = Parser::new(self.psess(), stream.clone(), Some("proc_macro expand expr")); let expr = p.parse_expr()?; if p.token != token::Eof { p.unexpected()?; diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index bb6a54fae707..ef141c7c25e5 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -138,6 +138,8 @@ declare_features! ( (accepted, copy_closures, "1.26.0", Some(44490)), /// Allows `crate` in paths. (accepted, crate_in_paths, "1.30.0", Some(45477)), + /// Allows users to provide classes for fenced code block using `class:classname`. + (accepted, custom_code_classes_in_docs, "1.80.0", Some(79483)), /// Allows using `#[debugger_visualizer]` attribute. (accepted, debugger_visualizer, "1.71.0", Some(95939)), /// Allows rustc to inject a default alloc_error_handler @@ -163,7 +165,7 @@ declare_features! ( /// Allows using `dyn Trait` as a syntax for trait objects. (accepted, dyn_trait, "1.27.0", Some(44662)), /// Allows `X..Y` patterns. - (accepted, exclusive_range_pattern, "CURRENT_RUSTC_VERSION", Some(37854)), + (accepted, exclusive_range_pattern, "1.80.0", Some(37854)), /// Allows integer match exhaustiveness checking (RFC 2591). (accepted, exhaustive_integer_patterns, "1.33.0", Some(50907)), /// Allows explicit generic arguments specification with `impl Trait` present. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index dbb88e42a3eb..c165620f657b 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -59,6 +59,16 @@ pub enum AttributeType { CrateLevel, } +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum AttributeSafety { + /// Normal attribute that does not need `#[unsafe(...)]` + Normal, + + /// Unsafe attribute that requires safety obligations + /// to be discharged + Unsafe, +} + #[derive(Clone, Copy)] pub enum AttributeGate { /// Is gated by a given feature gate, reason @@ -172,11 +182,23 @@ macro_rules! template { } macro_rules! ungated { + (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => { + BuiltinAttribute { + name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, + safety: AttributeSafety::Unsafe, + template: $tpl, + gate: Ungated, + duplicates: $duplicates, + } + }; ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, encode_cross_crate: $encode_cross_crate, type_: $typ, + safety: AttributeSafety::Normal, template: $tpl, gate: Ungated, duplicates: $duplicates, @@ -185,11 +207,34 @@ macro_rules! ungated { } macro_rules! gated { + (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => { + BuiltinAttribute { + name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, + safety: AttributeSafety::Unsafe, + template: $tpl, + duplicates: $duplicates, + gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)), + } + }; + (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => { + BuiltinAttribute { + name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, + safety: AttributeSafety::Unsafe, + template: $tpl, + duplicates: $duplicates, + gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)), + } + }; ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, encode_cross_crate: $encode_cross_crate, type_: $typ, + safety: AttributeSafety::Normal, template: $tpl, duplicates: $duplicates, gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)), @@ -200,6 +245,7 @@ macro_rules! gated { name: sym::$attr, encode_cross_crate: $encode_cross_crate, type_: $typ, + safety: AttributeSafety::Normal, template: $tpl, duplicates: $duplicates, gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)), @@ -228,6 +274,7 @@ macro_rules! rustc_attr { name: sym::$attr, encode_cross_crate: $encode_cross_crate, type_: $typ, + safety: AttributeSafety::Normal, template: $tpl, duplicates: $duplicates, gate: Gated(Stability::Unstable, sym::rustc_attrs, $msg, cfg_fn!(rustc_attrs)), @@ -258,6 +305,7 @@ pub struct BuiltinAttribute { /// Otherwise, it can only be used in the local crate. pub encode_cross_crate: EncodeCrossCrate, pub type_: AttributeType, + pub safety: AttributeSafety, pub template: AttributeTemplate, pub duplicates: AttributeDuplicates, pub gate: AttributeGate, @@ -375,9 +423,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), ungated!(no_link, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, EncodeCrossCrate::No), - ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), - ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), - ungated!(no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), + ungated!(unsafe export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), + ungated!(unsafe link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), + ungated!(unsafe no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, EncodeCrossCrate::No), ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, EncodeCrossCrate::Yes), @@ -486,11 +534,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), gated!( - ffi_pure, Normal, template!(Word), WarnFollowing, + unsafe ffi_pure, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, experimental!(ffi_pure) ), gated!( - ffi_const, Normal, template!(Word), WarnFollowing, + unsafe ffi_const, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, experimental!(ffi_const) ), gated!( @@ -515,12 +563,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::Yes, experimental!(deprecated_safe), ), - // RFC 2397 - gated!( - do_not_recommend, Normal, template!(Word), WarnFollowing, - EncodeCrossCrate::Yes, experimental!(do_not_recommend) - ), - // `#[cfi_encoding = ""]` gated!( cfi_encoding, Normal, template!(NameValueStr: "encoding"), ErrorPreceding, @@ -584,6 +626,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \ through unstable paths" ), + rustc_attr!( + rustc_deprecated_safe_2024, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::Yes, + "rustc_deprecated_safe_2024 is supposed to be used in libstd only", + ), + // ========================================================================== // Internal attributes: Type system related: @@ -850,6 +898,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`. encode_cross_crate: EncodeCrossCrate::Yes, type_: Normal, + safety: AttributeSafety::Normal, template: template!(NameValueStr: "name"), duplicates: ErrorFollowing, gate: Gated( @@ -899,10 +948,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "the `#[rustc_main]` attribute is used internally to specify test entry point function", ), rustc_attr!( - rustc_skip_array_during_method_dispatch, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::No, - "the `#[rustc_skip_array_during_method_dispatch]` attribute is used to exclude a trait \ - from method dispatch when the receiver is an array, for compatibility in editions < 2021." + rustc_skip_during_method_dispatch, Normal, template!(List: "array, boxed_slice"), WarnFollowing, + EncodeCrossCrate::No, + "the `#[rustc_skip_during_method_dispatch]` attribute is used to exclude a trait \ + from method dispatch when the receiver is of the following type, for compatibility in \ + editions < 2021 (array) or editions < 2024 (boxed_slice)." ), rustc_attr!( rustc_must_implement_one_of, Normal, template!(List: "function1, function2, ..."), @@ -1038,14 +1088,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ErrorFollowing, EncodeCrossCrate::No, "the `#[custom_mir]` attribute is just used for the Rust test suite", ), - rustc_attr!( - TEST, rustc_dump_program_clauses, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::No - ), - rustc_attr!( - TEST, rustc_dump_env_program_clauses, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::No - ), rustc_attr!( TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No @@ -1095,6 +1137,10 @@ pub fn is_valid_for_get_attr(name: Symbol) -> bool { }) } +pub fn is_unsafe_attr(name: Symbol) -> bool { + BUILTIN_ATTRIBUTE_MAP.get(&name).is_some_and(|attr| attr.safety == AttributeSafety::Unsafe) +} + pub static BUILTIN_ATTRIBUTE_MAP: LazyLock> = LazyLock::new(|| { let mut map = FxHashMap::default(); diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 36ef8fe7816f..fb3b7c0a1272 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -11,10 +11,11 @@ //! even if it is stabilized or removed, *do not remove it*. Instead, move the //! symbol to the `accepted` or `removed` modules respectively. +// tidy-alphabetical-start #![allow(internal_features)] -#![feature(rustdoc_internals)] #![doc(rust_logo)] -#![feature(lazy_cell)] +#![feature(rustdoc_internals)] +// tidy-alphabetical-end mod accepted; mod builtin_attrs; @@ -124,8 +125,8 @@ pub use accepted::ACCEPTED_FEATURES; pub use builtin_attrs::AttributeDuplicates; pub use builtin_attrs::{ deprecated_attributes, encode_cross_crate, find_gated_cfg, is_builtin_attr_name, - is_valid_for_get_attr, AttributeGate, AttributeTemplate, AttributeType, BuiltinAttribute, - GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP, + is_unsafe_attr, is_valid_for_get_attr, AttributeGate, AttributeTemplate, AttributeType, + BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP, }; pub use removed::REMOVED_FEATURES; pub use unstable::{Features, INCOMPATIBLE_FEATURES, UNSTABLE_FEATURES}; diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index eaaf7ca34e01..aea447b2aff1 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -128,6 +128,8 @@ declare_features! ( /// Allows the use of type alias impl trait in function return positions (removed, min_type_alias_impl_trait, "1.56.0", Some(63063), Some("removed in favor of full type_alias_impl_trait")), + /// Make `mut` not reset the binding mode on edition >= 2024. + (removed, mut_preserve_binding_mode_2024, "1.79.0", Some(123076), Some("superseded by `ref_pat_eat_one_layer_2024`")), (removed, needs_allocator, "1.4.0", Some(27389), Some("subsumed by `#![feature(allocator_internals)]`")), /// Allows use of unary negate on unsigned integers, e.g., -e for e: u8 @@ -181,6 +183,7 @@ declare_features! ( (removed, pushpop_unsafe, "1.2.0", None, None), (removed, quad_precision_float, "1.0.0", None, None), (removed, quote, "1.33.0", Some(29601), None), + (removed, ref_pat_everywhere, "1.79.0", Some(123076), Some("superseded by `ref_pat_eat_one_layer_2024")), (removed, reflect, "1.0.0", Some(27749), None), /// Allows using the `#[register_attr]` attribute. (removed, register_attr, "1.65.0", Some(66080), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index c6fc9de119dc..5cfbcdcbbbe0 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -177,8 +177,6 @@ declare_features! ( /// Allows using the `unadjusted` ABI; perma-unstable. (internal, abi_unadjusted, "1.16.0", None), - /// Allows using the `vectorcall` ABI. - (unstable, abi_vectorcall, "1.7.0", None), /// Allows using `#![needs_allocator]`, an implementation detail of `#[global_allocator]`. (internal, allocator_internals, "1.20.0", None), /// Allows using `#[allow_internal_unsafe]`. This is an @@ -243,6 +241,8 @@ declare_features! ( // feature-group-start: internal feature gates // ------------------------------------------------------------------------- + /// Allows using the `vectorcall` ABI. + (unstable, abi_vectorcall, "1.7.0", Some(124485)), /// Allows features specific to auto traits. /// Renamed from `optin_builtin_traits`. (unstable, auto_traits, "1.50.0", Some(13231)), @@ -424,8 +424,6 @@ declare_features! ( /// Allows function attribute `#[coverage(on/off)]`, to control coverage /// instrumentation of that function. (unstable, coverage_attribute, "1.74.0", Some(84605)), - /// Allows users to provide classes for fenced code block using `class:classname`. - (unstable, custom_code_classes_in_docs, "1.74.0", Some(79483)), /// Allows non-builtin attributes in inner attribute position. (unstable, custom_inner_attributes, "1.30.0", Some(54726)), /// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`. @@ -459,7 +457,7 @@ declare_features! ( /// Allows explicit tail calls via `become` expression. (incomplete, explicit_tail_calls, "1.72.0", Some(112788)), /// Uses 2024 rules for matching `expr` fragments in macros. Also enables `expr_2021` fragment. - (incomplete, expr_fragment_specifier_2024, "CURRENT_RUSTC_VERSION", Some(123742)), + (incomplete, expr_fragment_specifier_2024, "1.80.0", Some(123742)), /// Allows using `efiapi`, `sysv64` and `win64` as calling convention /// for functions with varargs. (unstable, extended_varargs_abi_support, "1.65.0", Some(100189)), @@ -489,6 +487,8 @@ declare_features! ( (incomplete, generic_const_exprs, "1.56.0", Some(76560)), /// Allows generic parameters and where-clauses on free & associated const items. (incomplete, generic_const_items, "1.73.0", Some(113521)), + /// Allows registering static items globally, possibly across crates, to iterate over at runtime. + (unstable, global_registration, "1.80.0", Some(125119)), /// Allows using `..=X` as a patterns in slices. (unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)), /// Allows `if let` guard in match arms. @@ -516,6 +516,8 @@ declare_features! ( (unstable, lint_reasons, "1.31.0", Some(54503)), /// Give access to additional metadata about declarative macro meta-variables. (unstable, macro_metavar_expr, "1.61.0", Some(83527)), + /// Provides a way to concatenate identifiers using metavariable expressions. + (unstable, macro_metavar_expr_concat, "CURRENT_RUSTC_VERSION", Some(124225)), /// Allows `#[marker]` on certain traits allowing overlapping implementations. (unstable, marker_trait_attr, "1.30.0", Some(29864)), /// Allows exhaustive pattern matching on types that contain uninhabited types in cases that are @@ -529,8 +531,6 @@ declare_features! ( (unstable, more_qualified_paths, "1.54.0", Some(86935)), /// Allows the `#[must_not_suspend]` attribute. (unstable, must_not_suspend, "1.57.0", Some(83310)), - /// Make `mut` not reset the binding mode on edition >= 2024. - (incomplete, mut_preserve_binding_mode_2024, "1.79.0", Some(123076)), /// Allows `mut ref` and `mut ref mut` identifier patterns. (incomplete, mut_ref, "1.79.0", Some(123076)), /// Allows using `#[naked]` on functions. @@ -561,20 +561,20 @@ declare_features! ( (unstable, offset_of_enum, "1.75.0", Some(120141)), /// Allows using multiple nested field accesses in offset_of! (unstable, offset_of_nested, "1.77.0", Some(120140)), + /// Allows using fields with slice type in offset_of! + (unstable, offset_of_slice, "CURRENT_RUSTC_VERSION", Some(126151)), /// Allows using `#[optimize(X)]`. (unstable, optimize_attribute, "1.34.0", Some(54882)), /// Allows postfix match `expr.match { ... }` (unstable, postfix_match, "1.79.0", Some(121618)), - /// Allows `use<'a, 'b, A, B>` in `impl use<...> Trait` for precise capture of generic args. - (incomplete, precise_capturing, "1.79.0", Some(123432)), + /// Allows `use<'a, 'b, A, B>` in `impl Trait + use<...>` for precise capture of generic args. + (unstable, precise_capturing, "1.79.0", Some(123432)), /// Allows macro attributes on expressions, statements and non-inline modules. (unstable, proc_macro_hygiene, "1.30.0", Some(54727)), /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions. (unstable, raw_ref_op, "1.41.0", Some(64490)), /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024. (incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)), - /// Allows `&` and `&mut` patterns to consume match-ergonomics-inserted references. - (incomplete, ref_pat_everywhere, "1.79.0", Some(123076)), /// Allows using the `#[register_tool]` attribute. (unstable, register_tool, "1.41.0", Some(66079)), /// Allows the `#[repr(i128)]` attribute for enums. @@ -583,11 +583,13 @@ declare_features! ( (unstable, repr_simd, "1.4.0", Some(27731)), /// Allows enums like Result to be used across FFI, if T's niche value can /// be used to describe E or vise-versa. - (unstable, result_ffi_guarantees, "CURRENT_RUSTC_VERSION", Some(110503)), + (unstable, result_ffi_guarantees, "1.80.0", Some(110503)), /// Allows bounding the return type of AFIT/RPITIT. (incomplete, return_type_notation, "1.70.0", Some(109417)), /// Allows `extern "rust-cold"`. (unstable, rust_cold_cc, "1.63.0", Some(97544)), + /// Shortern the tail expression lifetime + (unstable, shorter_tail_lifetimes, "1.79.0", Some(123739)), /// Allows the use of SIMD types in functions declared in `extern` blocks. (unstable, simd_ffi, "1.0.0", Some(27731)), /// Allows specialization of implementations (RFC 1210). @@ -624,6 +626,10 @@ declare_features! ( (unstable, type_changing_struct_update, "1.58.0", Some(86555)), /// Allows unnamed fields of struct and union type (incomplete, unnamed_fields, "1.74.0", Some(49804)), + /// Allows unsafe attributes. + (unstable, unsafe_attributes, "1.80.0", Some(123757)), + /// Allows unsafe on extern declarations and safety qualifiers over internal items. + (unstable, unsafe_extern_blocks, "1.80.0", Some(123743)), /// Allows unsized fn parameters. (unstable, unsized_fn_params, "1.49.0", Some(48055)), /// Allows unsized rvalues at arguments and parameters. diff --git a/compiler/rustc_fluent_macro/src/lib.rs b/compiler/rustc_fluent_macro/src/lib.rs index 2303785f94e6..0a04e6743a8b 100644 --- a/compiler/rustc_fluent_macro/src/lib.rs +++ b/compiler/rustc_fluent_macro/src/lib.rs @@ -1,10 +1,12 @@ +// tidy-alphabetical-start +#![allow(internal_features)] +#![allow(rustc::default_hash_types)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![allow(internal_features)] -#![feature(rustdoc_internals)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_span)] -#![allow(rustc::default_hash_types)] +#![feature(rustdoc_internals)] +// tidy-alphabetical-end use proc_macro::TokenStream; diff --git a/compiler/rustc_fs_util/src/lib.rs b/compiler/rustc_fs_util/src/lib.rs index d376c24cb589..5a9b15204d58 100644 --- a/compiler/rustc_fs_util/src/lib.rs +++ b/compiler/rustc_fs_util/src/lib.rs @@ -1,7 +1,9 @@ +// tidy-alphabetical-start use std::ffi::CString; use std::fs; use std::io; use std::path::{absolute, Path, PathBuf}; +// tidy-alphabetical-end // Unfortunately, on windows, it looks like msvcrt.dll is silently translating // verbatim paths under the hood to non-verbatim paths! This manifests itself as diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs index 43bee5c4be02..c0fe98254f08 100644 --- a/compiler/rustc_graphviz/src/lib.rs +++ b/compiler/rustc_graphviz/src/lib.rs @@ -269,13 +269,15 @@ //! //! * [DOT language](https://www.graphviz.org/doc/info/lang.html) +// tidy-alphabetical-start +#![allow(internal_features)] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(attr(allow(unused_variables), deny(warnings))) )] -#![feature(rustdoc_internals)] #![doc(rust_logo)] -#![allow(internal_features)] +#![feature(rustdoc_internals)] +// tidy-alphabetical-end use LabelText::*; diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 649a08b6972f..b1854923247f 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -76,6 +76,8 @@ pub enum DefKind { /// Constant generic parameter: `struct Foo { ... }` ConstParam, Static { + /// Whether it's a `unsafe static`, `safe static` (inside extern only) or just a `static`. + safety: hir::Safety, /// Whether it's a `static mut` or just a `static`. mutability: ast::Mutability, /// Whether it's an anonymous static generated for nested allocations. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 2f4dcdbdf2b1..22a6c06bba32 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -300,23 +300,30 @@ impl GenericArg<'_> { } } +/// The generic arguments and associated item constraints of a path segment. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct GenericArgs<'hir> { /// The generic arguments for this path segment. pub args: &'hir [GenericArg<'hir>], - /// Bindings (equality constraints) on associated types, if present. - /// E.g., `Foo`. - pub bindings: &'hir [TypeBinding<'hir>], - /// Were arguments written in parenthesized form `Fn(T) -> U`? + /// The associated item constraints for this path segment. + pub constraints: &'hir [AssocItemConstraint<'hir>], + /// Whether the arguments were written in parenthesized form (e.g., `Fn(T) -> U`). + /// /// This is required mostly for pretty-printing and diagnostics, /// but also for changing lifetime elision rules to be "function-like". pub parenthesized: GenericArgsParentheses, - /// The span encompassing arguments and the surrounding brackets `<>` or `()` + /// The span encompassing the arguments, constraints and the surrounding brackets (`<>` or `()`). + /// + /// For example: + /// + /// ```ignore (illustrative) /// Foo Fn(T, U, V) -> W /// ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ + /// ``` + /// /// Note that this may be: /// - empty, if there are no generic brackets (but there may be hidden lifetimes) - /// - dummy, if this was generated while desugaring + /// - dummy, if this was generated during desugaring pub span_ext: Span, } @@ -324,39 +331,63 @@ impl<'hir> GenericArgs<'hir> { pub const fn none() -> Self { Self { args: &[], - bindings: &[], + constraints: &[], parenthesized: GenericArgsParentheses::No, span_ext: DUMMY_SP, } } - pub fn inputs(&self) -> &[Ty<'hir>] { - if self.parenthesized == GenericArgsParentheses::ParenSugar { - for arg in self.args { - match arg { - GenericArg::Lifetime(_) => {} - GenericArg::Type(ref ty) => { - if let TyKind::Tup(ref tys) = ty.kind { - return tys; - } - break; - } - GenericArg::Const(_) => {} - GenericArg::Infer(_) => {} - } - } + /// Obtain the list of input types and the output type if the generic arguments are parenthesized. + /// + /// Returns the `Ty0, Ty1, ...` and the `RetTy` in `Trait(Ty0, Ty1, ...) -> RetTy`. + /// Panics if the parenthesized arguments have an incorrect form (this shouldn't happen). + pub fn paren_sugar_inputs_output(&self) -> Option<(&[Ty<'hir>], &Ty<'hir>)> { + if self.parenthesized != GenericArgsParentheses::ParenSugar { + return None; } - panic!("GenericArgs::inputs: not a `Fn(T) -> U`"); + + let inputs = self + .args + .iter() + .find_map(|arg| { + let GenericArg::Type(ty) = arg else { return None }; + let TyKind::Tup(tys) = &ty.kind else { return None }; + Some(tys) + }) + .unwrap(); + + Some((inputs, self.paren_sugar_output_inner())) } - pub fn has_err(&self) -> bool { - self.args.iter().any(|arg| match arg { - GenericArg::Type(ty) => matches!(ty.kind, TyKind::Err(_)), - _ => false, - }) || self.bindings.iter().any(|arg| match arg.kind { - TypeBindingKind::Equality { term: Term::Ty(ty) } => matches!(ty.kind, TyKind::Err(_)), - _ => false, - }) + /// Obtain the output type if the generic arguments are parenthesized. + /// + /// Returns the `RetTy` in `Trait(Ty0, Ty1, ...) -> RetTy`. + /// Panics if the parenthesized arguments have an incorrect form (this shouldn't happen). + pub fn paren_sugar_output(&self) -> Option<&Ty<'hir>> { + (self.parenthesized == GenericArgsParentheses::ParenSugar) + .then(|| self.paren_sugar_output_inner()) + } + + fn paren_sugar_output_inner(&self) -> &Ty<'hir> { + let [constraint] = self.constraints.try_into().unwrap(); + debug_assert_eq!(constraint.ident.name, sym::Output); + constraint.ty().unwrap() + } + + pub fn has_err(&self) -> Option { + self.args + .iter() + .find_map(|arg| { + let GenericArg::Type(ty) = arg else { return None }; + let TyKind::Err(guar) = ty.kind else { return None }; + Some(guar) + }) + .or_else(|| { + self.constraints.iter().find_map(|constraint| { + let TyKind::Err(guar) = constraint.ty()?.kind else { return None }; + Some(guar) + }) + }) } #[inline] @@ -383,9 +414,11 @@ impl<'hir> GenericArgs<'hir> { .count() } - /// The span encompassing the text inside the surrounding brackets. - /// It will also include bindings if they aren't in the form `-> Ret` - /// Returns `None` if the span is empty (e.g. no brackets) or dummy + /// The span encompassing the arguments and constraints[^1] inside the surrounding brackets. + /// + /// Returns `None` if the span is empty (i.e., no brackets) or dummy. + /// + /// [^1]: Unless of the form `-> Ty` (see [`GenericArgsParentheses`]). pub fn span(&self) -> Option { let span_ext = self.span_ext()?; Some(span_ext.with_lo(span_ext.lo() + BytePos(1)).with_hi(span_ext.hi() - BytePos(1))) @@ -430,6 +463,7 @@ pub enum TraitBoundModifier { pub enum GenericBound<'hir> { Trait(PolyTraitRef<'hir>, TraitBoundModifier), Outlives(&'hir Lifetime), + Use(&'hir [PreciseCapturingArg<'hir>], Span), } impl GenericBound<'_> { @@ -444,6 +478,7 @@ impl GenericBound<'_> { match self { GenericBound::Trait(t, ..) => t.span, GenericBound::Outlives(l) => l.ident.span, + GenericBound::Use(_, span) => *span, } } } @@ -660,9 +695,7 @@ impl<'hir> Generics<'hir> { |bound| { let span_for_parentheses = if let Some(trait_ref) = bound.trait_ref() && let [.., segment] = trait_ref.path.segments - && segment.args().parenthesized == GenericArgsParentheses::ParenSugar - && let [binding] = segment.args().bindings - && let TypeBindingKind::Equality { term: Term::Ty(ret_ty) } = binding.kind + && let Some(ret_ty) = segment.args().paren_sugar_output() && let ret_ty = ret_ty.peel_refs() && let TyKind::TraitObject( _, @@ -748,7 +781,7 @@ impl<'hir> Generics<'hir> { /// A single predicate in a where-clause. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum WherePredicate<'hir> { - /// A type binding (e.g., `for<'c> Foo: Send + Clone + 'c`). + /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`). BoundPredicate(WhereBoundPredicate<'hir>), /// A lifetime predicate (e.g., `'a: 'b + 'c`). RegionPredicate(WhereRegionPredicate<'hir>), @@ -1007,6 +1040,7 @@ pub struct Block<'hir> { pub hir_id: HirId, /// Distinguishes between `unsafe { ... }` and `{ ... }`. pub rules: BlockCheckMode, + /// The span includes the curly braces `{` and `}` around the block. pub span: Span, /// If true, then there may exist `break 'a` values that aim to /// break out of this block early. @@ -1601,6 +1635,13 @@ pub struct ConstBlock { } /// An expression. +/// +/// For more details, see the [rust lang reference]. +/// Note that the reference does not document nightly-only features. +/// There may be also slight differences in the names and representation of AST nodes between +/// the compiler and the reference. +/// +/// [rust lang reference]: https://doc.rust-lang.org/reference/expressions.html #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Expr<'hir> { pub hir_id: HirId, @@ -2319,7 +2360,9 @@ impl ImplItemId { } } -/// Represents anything within an `impl` block. +/// Represents an associated item within an impl block. +/// +/// Refer to [`Impl`] for an impl block declaration. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct ImplItem<'hir> { pub ident: Ident, @@ -2361,24 +2404,43 @@ pub enum ImplItemKind<'hir> { Type(&'hir Ty<'hir>), } -/// An associated item binding. +/// A constraint on an associated item. /// /// ### Examples /// -/// * `Trait` -/// * `Trait = Ty>` -/// * `Trait` -/// * `Trait` (under feature `associated_const_equality`) -/// * `Trait` (under feature `return_type_notation`) +/// * the `A = Ty` and `B = Ty` in `Trait` +/// * the `G = Ty` in `Trait = Ty>` +/// * the `A: Bound` in `Trait` +/// * the `RetTy` in `Trait(ArgTy, ArgTy) -> RetTy` +/// * the `C = { Ct }` in `Trait` (feature `associated_const_equality`) +/// * the `f(): Bound` in `Trait` (feature `return_type_notation`) #[derive(Debug, Clone, Copy, HashStable_Generic)] -pub struct TypeBinding<'hir> { +pub struct AssocItemConstraint<'hir> { pub hir_id: HirId, pub ident: Ident, pub gen_args: &'hir GenericArgs<'hir>, - pub kind: TypeBindingKind<'hir>, + pub kind: AssocItemConstraintKind<'hir>, pub span: Span, } +impl<'hir> AssocItemConstraint<'hir> { + /// Obtain the type on the RHS of an assoc ty equality constraint if applicable. + pub fn ty(self) -> Option<&'hir Ty<'hir>> { + match self.kind { + AssocItemConstraintKind::Equality { term: Term::Ty(ty) } => Some(ty), + _ => None, + } + } + + /// Obtain the const on the RHS of an assoc const equality constraint if applicable. + pub fn ct(self) -> Option<&'hir AnonConst> { + match self.kind { + AssocItemConstraintKind::Equality { term: Term::Const(ct) } => Some(ct), + _ => None, + } + } +} + #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum Term<'hir> { Ty(&'hir Ty<'hir>), @@ -2397,26 +2459,25 @@ impl<'hir> From<&'hir AnonConst> for Term<'hir> { } } -// Represents the two kinds of type bindings. +/// The kind of [associated item constraint][AssocItemConstraint]. #[derive(Debug, Clone, Copy, HashStable_Generic)] -pub enum TypeBindingKind<'hir> { - /// E.g., `Foo`. - Constraint { bounds: &'hir [GenericBound<'hir>] }, - /// E.g., `Foo`. +pub enum AssocItemConstraintKind<'hir> { + /// An equality constraint for an associated item (e.g., `AssocTy = Ty` in `Trait`). + /// + /// Also known as an *associated item binding* (we *bind* an associated item to a term). + /// + /// Furthermore, associated type equality constraints can also be referred to as *associated type + /// bindings*. Similarly with associated const equality constraints and *associated const bindings*. Equality { term: Term<'hir> }, + /// A bound on an associated type (e.g., `AssocTy: Bound` in `Trait`). + Bound { bounds: &'hir [GenericBound<'hir>] }, } -impl TypeBinding<'_> { - pub fn ty(&self) -> &Ty<'_> { - match self.kind { - TypeBindingKind::Equality { term: Term::Ty(ref ty) } => ty, - _ => panic!("expected equality type binding for parenthesized generic args"), - } - } - pub fn opt_const(&self) -> Option<&'_ AnonConst> { - match self.kind { - TypeBindingKind::Equality { term: Term::Const(ref c) } => Some(c), - _ => None, +impl<'hir> AssocItemConstraintKind<'hir> { + pub fn descr(&self) -> &'static str { + match self { + AssocItemConstraintKind::Equality { .. } => "binding", + AssocItemConstraintKind::Bound { .. } => "constraint", } } } @@ -2630,8 +2691,6 @@ pub struct OpaqueTy<'hir> { /// originating from a trait method. This makes it so that the opaque is /// lowered as an associated type. pub in_trait: bool, - /// List of arguments captured via `impl use<'a, P, ...> Trait` syntax. - pub precise_capturing_args: Option<(&'hir [PreciseCapturingArg<'hir>], Span)>, } #[derive(Debug, Clone, Copy, HashStable_Generic)] @@ -3104,6 +3163,13 @@ impl ItemId { /// An item /// /// The name might be a dummy name in case of anonymous items +/// +/// For more details, see the [rust lang reference]. +/// Note that the reference does not document nightly-only features. +/// There may be also slight differences in the names and representation of AST nodes between +/// the compiler and the reference. +/// +/// [rust lang reference]: https://doc.rust-lang.org/reference/items.html #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Item<'hir> { pub ident: Ident, @@ -3292,6 +3358,10 @@ pub enum ItemKind<'hir> { Impl(&'hir Impl<'hir>), } +/// Represents an impl block declaration. +/// +/// E.g., `impl $Type { .. }` or `impl $Trait for $Type { .. }` +/// Refer to [`ImplItem`] for an associated item within an impl block. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Impl<'hir> { pub safety: Safety, @@ -3440,9 +3510,9 @@ impl ForeignItem<'_> { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum ForeignItemKind<'hir> { /// A foreign function. - Fn(&'hir FnDecl<'hir>, &'hir [Ident], &'hir Generics<'hir>), + Fn(&'hir FnDecl<'hir>, &'hir [Ident], &'hir Generics<'hir>, Safety), /// A foreign static item (`static ext: u8`). - Static(&'hir Ty<'hir>, Mutability), + Static(&'hir Ty<'hir>, Mutability, Safety), /// A foreign type. Type, } @@ -3510,7 +3580,7 @@ impl<'hir> OwnerNode<'hir> { | OwnerNode::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) | OwnerNode::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig.decl), OwnerNode::ForeignItem(ForeignItem { - kind: ForeignItemKind::Fn(fn_decl, _, _), + kind: ForeignItemKind::Fn(fn_decl, _, _, _), .. }) => Some(fn_decl), _ => None, @@ -3615,7 +3685,7 @@ pub enum Node<'hir> { Stmt(&'hir Stmt<'hir>), PathSegment(&'hir PathSegment<'hir>), Ty(&'hir Ty<'hir>), - TypeBinding(&'hir TypeBinding<'hir>), + AssocItemConstraint(&'hir AssocItemConstraint<'hir>), TraitRef(&'hir TraitRef<'hir>), Pat(&'hir Pat<'hir>), PatField(&'hir PatField<'hir>), @@ -3664,7 +3734,7 @@ impl<'hir> Node<'hir> { | Node::PathSegment(PathSegment { ident, .. }) => Some(*ident), Node::Lifetime(lt) => Some(lt.ident), Node::GenericParam(p) => Some(p.name.ident()), - Node::TypeBinding(b) => Some(b.ident), + Node::AssocItemConstraint(c) => Some(c.ident), Node::PatField(f) => Some(f.ident), Node::ExprField(f) => Some(f.ident), Node::PreciseCapturingNonLifetimeArg(a) => Some(a.ident), @@ -3695,8 +3765,23 @@ impl<'hir> Node<'hir> { | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) | Node::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig.decl), Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl, .. }), .. }) - | Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => { - Some(fn_decl) + | Node::ForeignItem(ForeignItem { + kind: ForeignItemKind::Fn(fn_decl, _, _, _), .. + }) => Some(fn_decl), + _ => None, + } + } + + /// Get a `hir::Impl` if the node is an impl block for the given `trait_def_id`. + pub fn impl_block_of_trait(self, trait_def_id: DefId) -> Option<&'hir Impl<'hir>> { + match self { + Node::Item(Item { kind: ItemKind::Impl(impl_block), .. }) + if impl_block + .of_trait + .and_then(|trait_ref| trait_ref.trait_def_id()) + .is_some_and(|trait_id| trait_id == trait_def_id) => + { + Some(impl_block) } _ => None, } @@ -3781,7 +3866,7 @@ impl<'hir> Node<'hir> { pub fn generics(self) -> Option<&'hir Generics<'hir>> { match self { Node::ForeignItem(ForeignItem { - kind: ForeignItemKind::Fn(_, _, generics), .. + kind: ForeignItemKind::Fn(_, _, generics, _), .. }) | Node::TraitItem(TraitItem { generics, .. }) | Node::ImplItem(ImplItem { generics, .. }) => Some(generics), @@ -3843,7 +3928,7 @@ impl<'hir> Node<'hir> { expect_stmt, &'hir Stmt<'hir>, Node::Stmt(n), n; expect_path_segment, &'hir PathSegment<'hir>, Node::PathSegment(n), n; expect_ty, &'hir Ty<'hir>, Node::Ty(n), n; - expect_type_binding, &'hir TypeBinding<'hir>, Node::TypeBinding(n), n; + expect_assoc_item_constraint, &'hir AssocItemConstraint<'hir>, Node::AssocItemConstraint(n), n; expect_trait_ref, &'hir TraitRef<'hir>, Node::TraitRef(n), n; expect_pat, &'hir Pat<'hir>, Node::Pat(n), n; expect_pat_field, &'hir PatField<'hir>, Node::PatField(n), n; diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index b202ea8dca37..065ecc5d7b7b 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -299,7 +299,7 @@ pub trait Visitor<'v>: Sized { walk_item(self, i) } - fn visit_body(&mut self, b: &'v Body<'v>) -> Self::Result { + fn visit_body(&mut self, b: &Body<'v>) -> Self::Result { walk_body(self, b) } @@ -456,8 +456,11 @@ pub trait Visitor<'v>: Sized { fn visit_generic_args(&mut self, generic_args: &'v GenericArgs<'v>) -> Self::Result { walk_generic_args(self, generic_args) } - fn visit_assoc_type_binding(&mut self, type_binding: &'v TypeBinding<'v>) -> Self::Result { - walk_assoc_type_binding(self, type_binding) + fn visit_assoc_item_constraint( + &mut self, + constraint: &'v AssocItemConstraint<'v>, + ) -> Self::Result { + walk_assoc_item_constraint(self, constraint) } fn visit_attribute(&mut self, _attr: &'v Attribute) -> Self::Result { Self::Result::output() @@ -529,15 +532,10 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: try_visit!(visitor.visit_ty(ty)); try_visit!(visitor.visit_generics(generics)); } - ItemKind::OpaqueTy(&OpaqueTy { generics, bounds, precise_capturing_args, .. }) => { + ItemKind::OpaqueTy(&OpaqueTy { generics, bounds, .. }) => { try_visit!(visitor.visit_id(item.hir_id())); try_visit!(walk_generics(visitor, generics)); walk_list!(visitor, visit_param_bound, bounds); - if let Some((precise_capturing_args, _)) = precise_capturing_args { - for arg in precise_capturing_args { - try_visit!(visitor.visit_precise_capturing_arg(arg)); - } - } } ItemKind::Enum(ref enum_definition, ref generics) => { try_visit!(visitor.visit_generics(generics)); @@ -581,7 +579,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: V::Result::output() } -pub fn walk_body<'v, V: Visitor<'v>>(visitor: &mut V, body: &'v Body<'v>) -> V::Result { +pub fn walk_body<'v, V: Visitor<'v>>(visitor: &mut V, body: &Body<'v>) -> V::Result { walk_list!(visitor, visit_param, body.params); visitor.visit_expr(body.value) } @@ -608,12 +606,14 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>( try_visit!(visitor.visit_ident(foreign_item.ident)); match foreign_item.kind { - ForeignItemKind::Fn(ref function_declaration, param_names, ref generics) => { + ForeignItemKind::Fn(ref function_declaration, param_names, ref generics, _) => { try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_fn_decl(function_declaration)); walk_list!(visitor, visit_ident, param_names.iter().copied()); } - ForeignItemKind::Static(ref typ, _) => try_visit!(visitor.visit_ty(typ)), + ForeignItemKind::Static(ref typ, _, _) => { + try_visit!(visitor.visit_ty(typ)); + } ForeignItemKind::Type => (), } V::Result::output() @@ -1142,6 +1142,10 @@ pub fn walk_param_bound<'v, V: Visitor<'v>>( match *bound { GenericBound::Trait(ref typ, _modifier) => visitor.visit_poly_trait_ref(typ), GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime), + GenericBound::Use(args, _) => { + walk_list!(visitor, visit_precise_capturing_arg, args); + V::Result::output() + } } } @@ -1259,23 +1263,25 @@ pub fn walk_generic_args<'v, V: Visitor<'v>>( generic_args: &'v GenericArgs<'v>, ) -> V::Result { walk_list!(visitor, visit_generic_arg, generic_args.args); - walk_list!(visitor, visit_assoc_type_binding, generic_args.bindings); + walk_list!(visitor, visit_assoc_item_constraint, generic_args.constraints); V::Result::output() } -pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>( +pub fn walk_assoc_item_constraint<'v, V: Visitor<'v>>( visitor: &mut V, - type_binding: &'v TypeBinding<'v>, + constraint: &'v AssocItemConstraint<'v>, ) -> V::Result { - try_visit!(visitor.visit_id(type_binding.hir_id)); - try_visit!(visitor.visit_ident(type_binding.ident)); - try_visit!(visitor.visit_generic_args(type_binding.gen_args)); - match type_binding.kind { - TypeBindingKind::Equality { ref term } => match term { + try_visit!(visitor.visit_id(constraint.hir_id)); + try_visit!(visitor.visit_ident(constraint.ident)); + try_visit!(visitor.visit_generic_args(constraint.gen_args)); + match constraint.kind { + AssocItemConstraintKind::Equality { ref term } => match term { Term::Ty(ref ty) => try_visit!(visitor.visit_ty(ty)), Term::Const(ref c) => try_visit!(visitor.visit_anon_const(c)), }, - TypeBindingKind::Constraint { bounds } => walk_list!(visitor, visit_param_bound, bounds), + AssocItemConstraintKind::Bound { bounds } => { + walk_list!(visitor, visit_param_bound, bounds) + } } V::Result::output() } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index c4be67cdd887..69461957f804 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -176,6 +176,7 @@ language_item_table! { AsyncDropSlice, sym::async_drop_slice, async_drop_slice_fn, Target::Fn, GenericRequirement::Exact(1); AsyncDropChain, sym::async_drop_chain, async_drop_chain_fn, Target::Fn, GenericRequirement::Exact(2); AsyncDropNoop, sym::async_drop_noop, async_drop_noop_fn, Target::Fn, GenericRequirement::Exact(0); + AsyncDropDeferredDropInPlace, sym::async_drop_deferred_drop_in_place, async_drop_deferred_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1); AsyncDropFuse, sym::async_drop_fuse, async_drop_fuse_fn, Target::Fn, GenericRequirement::Exact(1); AsyncDropDefer, sym::async_drop_defer, async_drop_defer_fn, Target::Fn, GenericRequirement::Exact(1); AsyncDropEither, sym::async_drop_either, async_drop_either_fn, Target::Fn, GenericRequirement::Exact(3); @@ -228,17 +229,24 @@ language_item_table! { AsyncFn, sym::async_fn, async_fn_trait, Target::Trait, GenericRequirement::Exact(1); AsyncFnMut, sym::async_fn_mut, async_fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); AsyncFnOnce, sym::async_fn_once, async_fn_once_trait, Target::Trait, GenericRequirement::Exact(1); - AsyncFnKindHelper, sym::async_fn_kind_helper,async_fn_kind_helper, Target::Trait, GenericRequirement::Exact(1); + AsyncFnOnceOutput, sym::async_fn_once_output, async_fn_once_output, Target::AssocTy, GenericRequirement::Exact(1); + CallOnceFuture, sym::call_once_future, call_once_future, Target::AssocTy, GenericRequirement::Exact(1); + CallRefFuture, sym::call_ref_future, call_ref_future, Target::AssocTy, GenericRequirement::Exact(2); + AsyncFnKindHelper, sym::async_fn_kind_helper, async_fn_kind_helper, Target::Trait, GenericRequirement::Exact(1); + AsyncFnKindUpvars, sym::async_fn_kind_upvars, async_fn_kind_upvars, Target::AssocTy, GenericRequirement::Exact(5); FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; Iterator, sym::iterator, iterator_trait, Target::Trait, GenericRequirement::Exact(0); FusedIterator, sym::fused_iterator, fused_iterator_trait, Target::Trait, GenericRequirement::Exact(0); Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); + FutureOutput, sym::future_output, future_output, Target::AssocTy, GenericRequirement::Exact(0); AsyncIterator, sym::async_iterator, async_iterator_trait, Target::Trait, GenericRequirement::Exact(0); CoroutineState, sym::coroutine_state, coroutine_state, Target::Enum, GenericRequirement::None; - Coroutine, sym::coroutine, coroutine_trait, Target::Trait, GenericRequirement::Minimum(1); + Coroutine, sym::coroutine, coroutine_trait, Target::Trait, GenericRequirement::Exact(1); + CoroutineReturn, sym::coroutine_return, coroutine_return, Target::AssocTy, GenericRequirement::Exact(1); + CoroutineYield, sym::coroutine_yield, coroutine_yield, Target::AssocTy, GenericRequirement::Exact(1); CoroutineResume, sym::coroutine_resume, coroutine_resume, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 600a0dce03b9..e517c3fd07a1 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -2,13 +2,15 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html +// tidy-alphabetical-start +#![allow(internal_features)] #![feature(associated_type_defaults)] #![feature(closure_track_caller)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustc_attrs)] #![feature(variant_count)] -#![allow(internal_features)] +// tidy-alphabetical-end extern crate self as rustc_hir; diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 3edea0191faf..8c740d87e953 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -4,6 +4,14 @@ hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_ hir_analysis_ambiguous_lifetime_bound = ambiguous lifetime bound, explicit lifetime bound required +hir_analysis_assoc_item_constraints_not_allowed_here = + associated item constraints are not allowed here + .label = associated item constraint not allowed here + +hir_analysis_assoc_item_is_private = {$kind} `{$name}` is private + .label = private {$kind} + .defined_here_label = the {$kind} is defined here + hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$ty_param_name}` hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named -> @@ -24,10 +32,6 @@ hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got} hir_analysis_assoc_kind_mismatch_wrap_in_braces_sugg = consider adding braces here -hir_analysis_assoc_type_binding_not_allowed = - associated type bindings are not allowed here - .label = associated type not allowed here - hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the associated type of a trait with uninferred generic parameters .suggestion = use a fully qualified path with inferred lifetimes @@ -371,9 +375,9 @@ hir_analysis_pass_to_variadic_function = can't pass `{$ty}` to variadic function .suggestion = cast the value to `{$cast_ty}` .help = cast the value to `{$cast_ty}` -hir_analysis_pattern_type_non_const_range = "range patterns must have constant range start and end" -hir_analysis_pattern_type_wild_pat = "wildcard patterns are not permitted for pattern types" - .label = "this type is the same as the inner type without a pattern" +hir_analysis_pattern_type_non_const_range = range patterns must have constant range start and end +hir_analysis_pattern_type_wild_pat = wildcard patterns are not permitted for pattern types + .label = this type is the same as the inner type without a pattern hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind} .label = not allowed in type signatures @@ -460,6 +464,10 @@ hir_analysis_static_specialize = cannot specialize on `'static` lifetime hir_analysis_tait_forward_compat = item constrains opaque type that is not in its signature .note = this item must mention the opaque type in its signature in order to be able to register hidden types +hir_analysis_tait_forward_compat2 = item does not constrain `{$opaque_type}`, but has it in its signature + .note = consider moving the opaque type's declaration and defining uses into a separate module + .opaque = this opaque type is in the signature + hir_analysis_target_feature_on_main = `main` function is not allowed to have `#[target_feature]` hir_analysis_too_large_static = extern static is too large for the current architecture diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index 38ecd7dd0821..7f0d72b3a8d4 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -53,7 +53,7 @@ impl<'tcx> Bounds<'tcx> { span, ); // FIXME(-Znext-solver): We can likely remove this hack once the new trait solver lands. - if tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { + if tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) { self.clauses.insert(0, clause); } else { self.clauses.push(clause); diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index b5c067514059..3b53c253195f 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -15,6 +15,7 @@ use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::stability::EvalResult; use rustc_middle::span_bug; +use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::util::{Discr, InspectCoroutineFields, IntTypeExt}; @@ -46,13 +47,9 @@ pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) { .emit(); } None => { - tcx.node_span_lint( - UNSUPPORTED_CALLING_CONVENTIONS, - hir_id, - span, - "use of calling convention not supported on this target", - |_| {}, - ); + tcx.node_span_lint(UNSUPPORTED_CALLING_CONVENTIONS, hir_id, span, |lint| { + lint.primary_message("use of calling convention not supported on this target"); + }); } } @@ -243,8 +240,8 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) { UNINHABITED_STATIC, tcx.local_def_id_to_hir_id(def_id), span, - "static of uninhabited type", |lint| { + lint.primary_message("static of uninhabited type"); lint .note("uninhabited statics cannot be initialized, and any access would be an immediate error"); }, @@ -346,7 +343,7 @@ fn check_opaque_meets_bounds<'tcx>( let param_env = tcx.param_env(defining_use_anchor); let infcx = tcx.infer_ctxt().with_opaque_type_inference(defining_use_anchor).build(); - let ocx = ObligationCtxt::new(&infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let args = match *origin { hir::OpaqueTyOrigin::FnReturn(parent) @@ -484,9 +481,12 @@ fn sanity_check_found_hidden_type<'tcx>( /// 2. Checking that all lifetimes that are implicitly captured are mentioned. /// 3. Asserting that all parameters mentioned in the captures list are invariant. fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId) { - let hir::OpaqueTy { precise_capturing_args, .. } = + let hir::OpaqueTy { bounds, .. } = *tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); - let Some((precise_capturing_args, _)) = precise_capturing_args else { + let Some(precise_capturing_args) = bounds.iter().find_map(|bound| match *bound { + hir::GenericBound::Use(bounds, ..) => Some(bounds), + _ => None, + }) else { // No precise capturing args; nothing to validate return; }; @@ -538,11 +538,9 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe // the cases that were stabilized with the `impl_trait_projection` // feature -- see . if let DefKind::LifetimeParam = tcx.def_kind(def_id) - && let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) - | ty::ReLateParam(ty::LateParamRegion { - bound_region: ty::BoundRegionKind::BrNamed(def_id, _), - .. - }) = *tcx.map_opaque_lifetime_to_parent_lifetime(def_id.expect_local()) + && let Some(def_id) = tcx + .map_opaque_lifetime_to_parent_lifetime(def_id.expect_local()) + .opt_param_def_id(tcx, tcx.parent(opaque_def_id.to_def_id())) { shadowed_captures.insert(def_id); } @@ -585,12 +583,9 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe // Check if the lifetime param was captured but isn't named in the precise captures list. if variances[param.index as usize] == ty::Invariant { if let DefKind::OpaqueTy = tcx.def_kind(tcx.parent(param.def_id)) - && let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) - | ty::ReLateParam(ty::LateParamRegion { - bound_region: ty::BoundRegionKind::BrNamed(def_id, _), - .. - }) = *tcx + && let Some(def_id) = tcx .map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()) + .opt_param_def_id(tcx, tcx.parent(opaque_def_id.to_def_id())) { tcx.dcx().emit_err(errors::LifetimeNotCaptured { opaque_span, @@ -810,7 +805,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { let item = tcx.hir().foreign_item(item.id); match &item.kind { - hir::ForeignItemKind::Fn(fn_decl, _, _) => { + hir::ForeignItemKind::Fn(fn_decl, _, _, _) => { require_c_abi_if_c_variadic(tcx, fn_decl, abi, item.span); } hir::ForeignItemKind::Static(..) => { @@ -1315,9 +1310,11 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, tcx.local_def_id_to_hir_id(adt.did().expect_local()), span, - "zero-sized fields in `repr(transparent)` cannot \ - contain external non-exhaustive types", |lint| { + lint.primary_message( + "zero-sized fields in `repr(transparent)` cannot \ + contain external non-exhaustive types", + ); let note = if non_exhaustive { "is marked with `#[non_exhaustive]`" } else { @@ -1734,7 +1731,7 @@ pub(super) fn check_coroutine_obligations( .with_opaque_type_inference(def_id) .build(); - let ocx = ObligationCtxt::new(&infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); for (predicate, cause) in &typeck_results.coroutine_stalled_predicates { ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, *predicate)); } diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 44bf8fd2d93e..82b57cdd1069 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -10,7 +10,7 @@ use rustc_hir::intravisit; use rustc_hir::{GenericParamKind, ImplItemKind}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::{util, FulfillmentError}; +use rustc_infer::traits::util; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::util::ExplicitSelf; @@ -25,7 +25,7 @@ use rustc_trait_selection::regions::InferCtxtRegionExt; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal, + self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal, }; use std::borrow::Cow; use std::iter; @@ -225,7 +225,7 @@ fn compare_method_predicate_entailment<'tcx>( let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); let infcx = &tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(infcx); + let ocx = ObligationCtxt::new_with_diagnostics(infcx); debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds()); @@ -449,7 +449,7 @@ impl<'tcx> TypeFolder> for RemapLateBound<'_, 'tcx> { pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( tcx: TyCtxt<'tcx>, impl_m_def_id: LocalDefId, -) -> Result<&'tcx DefIdMap>>, ErrorGuaranteed> { +) -> Result<&'tcx DefIdMap>>, ErrorGuaranteed> { let impl_m = tcx.opt_associated_item(impl_m_def_id.to_def_id()).unwrap(); let trait_m = tcx.opt_associated_item(impl_m.trait_item_def_id.unwrap()).unwrap(); let impl_trait_ref = @@ -493,7 +493,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( ); let infcx = &tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(infcx); + let ocx = ObligationCtxt::new_with_diagnostics(infcx); // Normalize the impl signature with fresh variables for lifetime inference. let misc_cause = ObligationCause::misc(return_span, impl_m_def_id); @@ -764,17 +764,20 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( Ok(&*tcx.arena.alloc(remapped_types)) } -struct ImplTraitInTraitCollector<'a, 'tcx> { - ocx: &'a ObligationCtxt<'a, 'tcx>, +struct ImplTraitInTraitCollector<'a, 'tcx, E> { + ocx: &'a ObligationCtxt<'a, 'tcx, E>, types: FxIndexMap, ty::GenericArgsRef<'tcx>)>, span: Span, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, } -impl<'a, 'tcx> ImplTraitInTraitCollector<'a, 'tcx> { +impl<'a, 'tcx, E> ImplTraitInTraitCollector<'a, 'tcx, E> +where + E: 'tcx, +{ fn new( - ocx: &'a ObligationCtxt<'a, 'tcx>, + ocx: &'a ObligationCtxt<'a, 'tcx, E>, span: Span, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, @@ -783,7 +786,10 @@ impl<'a, 'tcx> ImplTraitInTraitCollector<'a, 'tcx> { } } -impl<'tcx> TypeFolder> for ImplTraitInTraitCollector<'_, 'tcx> { +impl<'tcx, E> TypeFolder> for ImplTraitInTraitCollector<'_, 'tcx, E> +where + E: 'tcx, +{ fn interner(&self) -> TyCtxt<'tcx> { self.ocx.infcx.tcx } @@ -876,7 +882,8 @@ impl<'tcx> ty::FallibleTypeFolder> for RemapHiddenTyRegions<'tcx> { ty::ReLateParam(_) => {} // Remap early-bound regions as long as they don't come from the `impl` itself, // in which case we don't really need to renumber them. - ty::ReEarlyParam(ebr) if self.tcx.parent(ebr.def_id) != self.impl_def_id => {} + ty::ReEarlyParam(ebr) + if ebr.index >= self.tcx.generics_of(self.impl_def_id).count() as u32 => {} _ => return Ok(region), } @@ -889,12 +896,8 @@ impl<'tcx> ty::FallibleTypeFolder> for RemapHiddenTyRegions<'tcx> { ); } } else { - let guar = match region.kind() { - ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) - | ty::ReLateParam(ty::LateParamRegion { - bound_region: ty::BoundRegionKind::BrNamed(def_id, _), - .. - }) => { + let guar = match region.opt_param_def_id(self.tcx, self.tcx.parent(self.def_id)) { + Some(def_id) => { let return_span = if let ty::Alias(ty::Opaque, opaque_ty) = self.ty.kind() { self.tcx.def_span(opaque_ty.def_id) } else { @@ -914,7 +917,7 @@ impl<'tcx> ty::FallibleTypeFolder> for RemapHiddenTyRegions<'tcx> { .with_note(format!("hidden type inferred to be `{}`", self.ty)) .emit() } - _ => { + None => { // This code path is not reached in any tests, but may be // reachable. If this is triggered, it should be converted // to `delayed_bug` and the triggering case turned into a @@ -928,7 +931,6 @@ impl<'tcx> ty::FallibleTypeFolder> for RemapHiddenTyRegions<'tcx> { Ok(ty::Region::new_early_param( self.tcx, ty::EarlyParamRegion { - def_id: e.def_id, name: e.name, index: (e.index as usize - self.num_trait_args + self.num_impl_args) as u32, }, @@ -1781,7 +1783,7 @@ fn compare_const_predicate_entailment<'tcx>( ); let infcx = tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(&infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let impl_ct_own_bounds = impl_ct_predicates.instantiate_own(tcx, impl_args); for (predicate, span) in impl_ct_own_bounds { @@ -1914,7 +1916,7 @@ fn compare_type_predicate_entailment<'tcx>( let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing); let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); let infcx = tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(&infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds()); @@ -1981,7 +1983,7 @@ pub(super) fn check_type_bounds<'tcx>( let rebased_args = impl_ty_args.rebase_onto(tcx, container_id, impl_trait_ref.args); let infcx = tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(&infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); // A synthetic impl Trait for RPITIT desugaring has no HIR, which we currently use to get the // span for an impl's associated type. Instead, for these, use the def_span for the synthesized @@ -2030,10 +2032,19 @@ pub(super) fn check_type_bounds<'tcx>( // to its definition type. This should be the param-env we use to *prove* the // predicate too, but we don't do that because of performance issues. // See . + let trait_projection_ty = Ty::new_projection(tcx, trait_ty.def_id, rebased_args); + let impl_identity_ty = tcx.type_of(impl_ty.def_id).instantiate_identity(); let normalize_param_env = param_env_with_gat_bounds(tcx, impl_ty, impl_trait_ref); for mut obligation in util::elaborate(tcx, obligations) { - let normalized_predicate = - ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate); + let normalized_predicate = if infcx.next_trait_solver() { + obligation.predicate.fold_with(&mut ReplaceTy { + tcx, + from: trait_projection_ty, + to: impl_identity_ty, + }) + } else { + ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate) + }; debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate); obligation.predicate = normalized_predicate; @@ -2054,6 +2065,22 @@ pub(super) fn check_type_bounds<'tcx>( ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env) } +struct ReplaceTy<'tcx> { + tcx: TyCtxt<'tcx>, + from: Ty<'tcx>, + to: Ty<'tcx>, +} + +impl<'tcx> TypeFolder> for ReplaceTy<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if self.from == ty { self.to } else { ty.super_fold_with(self) } + } +} + /// Install projection predicates that allow GATs to project to their own /// definition types. This is not allowed in general in cases of default /// associated types in trait definitions, or when specialization is involved, @@ -2171,9 +2198,6 @@ fn param_env_with_gat_bounds<'tcx>( tcx, ty::INNERMOST, ty::BoundVar::from_usize(bound_vars.len() - 1), - tcx.type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"), ) .into() } @@ -2254,7 +2278,7 @@ fn try_report_async_mismatch<'tcx>( && let Some(proj) = proj.no_bound_vars() && infcx.can_eq( error.root_obligation.param_env, - proj.term.ty().unwrap(), + proj.term.expect_type(), impl_sig.output(), ) { diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index ca08eeea2275..10b097a1060f 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -267,7 +267,7 @@ fn report_mismatched_rpitit_signature<'tcx>( .explicit_item_bounds(future_ty.def_id) .iter_instantiated_copied(tcx, future_ty.args) .find_map(|(clause, _)| match clause.kind().no_bound_vars()? { - ty::ClauseKind::Projection(proj) => proj.term.ty(), + ty::ClauseKind::Projection(proj) => proj.term.as_type(), _ => None, }) else { diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index be412dde9680..8ec6dd12a78d 100644 --- a/compiler/rustc_hir_analysis/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -123,7 +123,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( adt_to_impl_args: GenericArgsRef<'tcx>, ) -> Result<(), ErrorGuaranteed> { let infcx = tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(&infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); // Take the param-env of the adt and instantiate the args that show up in // the implementation's self type. This gives us the assumptions that the diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index 25ac31c16c7c..cc52a7658020 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -133,7 +133,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { main_diagnostics_def_id, ObligationCauseCode::MainFunctionType, ); - let ocx = traits::ObligationCtxt::new(&infcx); + let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx); let norm_return_ty = ocx.normalize(&cause, param_env, return_ty); ocx.register_bound(cause, param_env, norm_return_ty, term_did); let errors = ocx.select_all_or_error(); diff --git a/compiler/rustc_hir_analysis/src/check/errs.rs b/compiler/rustc_hir_analysis/src/check/errs.rs index a49626eed35a..17cb20df7546 100644 --- a/compiler/rustc_hir_analysis/src/check/errs.rs +++ b/compiler/rustc_hir_analysis/src/check/errs.rs @@ -41,7 +41,8 @@ fn path_if_static_mut(tcx: TyCtxt<'_>, expr: &hir::Expr<'_>) -> Option { if let hir::ExprKind::Path(qpath) = expr.kind && let hir::QPath::Resolved(_, path) = qpath && let hir::def::Res::Def(def_kind, _) = path.res - && let hir::def::DefKind::Static { mutability: Mutability::Mut, nested: false } = def_kind + && let hir::def::DefKind::Static { safety: _, mutability: Mutability::Mut, nested: false } = + def_kind { return Some(qpath_to_string(&tcx, &qpath)); } @@ -62,7 +63,7 @@ fn handle_static_mut_ref( } else { (errors::StaticMutRefSugg::Shared { span, var }, "shared") }; - tcx.sess.psess.dcx.emit_err(errors::StaticMutRef { span, sugg, shared }); + tcx.dcx().emit_err(errors::StaticMutRef { span, sugg, shared }); } else { let (sugg, shared) = if mutable == Mutability::Mut { (errors::RefOfMutStaticSugg::Mut { span, var }, "mutable") diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index d829e720d9e1..13180fa2673b 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -29,7 +29,7 @@ fn equate_intrinsic_type<'tcx>( let (own_counts, span) = match tcx.hir_node_by_def_id(def_id) { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, generics, _), .. }) | hir::Node::ForeignItem(hir::ForeignItem { - kind: hir::ForeignItemKind::Fn(.., generics), + kind: hir::ForeignItemKind::Fn(.., generics, _), .. }) => { let own_counts = tcx.generics_of(def_id).own_counts(); @@ -130,6 +130,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::is_val_statically_known | sym::ptr_mask | sym::aggregate_raw_ptr + | sym::ptr_metadata | sym::ub_checks | sym::fadd_algebraic | sym::fsub_algebraic @@ -164,9 +165,8 @@ pub fn check_intrinsic_type( ) { let generics = tcx.generics_of(intrinsic_id); let param = |n| { - if let Some(&ty::GenericParamDef { - name, kind: ty::GenericParamDefKind::Type { .. }, .. - }) = generics.opt_param_at(n as usize, tcx) + if let &ty::GenericParamDef { name, kind: ty::GenericParamDefKind::Type { .. }, .. } = + generics.param_at(n as usize, tcx) { Ty::new_param(tcx, n, name) } else { @@ -577,6 +577,7 @@ pub fn check_intrinsic_type( // This type check is not particularly useful, but the `where` bounds // on the definition in `core` do the heavy lifting for checking it. sym::aggregate_raw_ptr => (3, 1, vec![param(1), param(2)], param(0)), + sym::ptr_metadata => (2, 1, vec![Ty::new_imm_ptr(tcx, param(0))], param(1)), sym::ub_checks => (0, 1, Vec::new(), tcx.types.bool), @@ -607,6 +608,7 @@ pub fn check_intrinsic_type( | sym::simd_bitreverse | sym::simd_ctlz | sym::simd_cttz + | sym::simd_ctpop | sym::simd_fsqrt | sym::simd_fsin | sym::simd_fcos diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index b09de1a4a098..2e965c59ebb5 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -1,6 +1,6 @@ use rustc_ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxIndexSet; -use rustc_hir as hir; +use rustc_hir::{self as hir, LangItem}; use rustc_middle::bug; use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy}; use rustc_session::lint; @@ -62,8 +62,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64), ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize), + ty::Float(FloatTy::F16) => Some(InlineAsmType::F16), ty::Float(FloatTy::F32) => Some(InlineAsmType::F32), ty::Float(FloatTy::F64) => Some(InlineAsmType::F64), + ty::Float(FloatTy::F128) => Some(InlineAsmType::F128), ty::FnPtr(_) => Some(asm_ty_isize), ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Some(asm_ty_isize), ty::Adt(adt, args) if adt.repr().simd() => { @@ -105,8 +107,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { width => bug!("unsupported pointer width: {width}"), }) } + ty::Float(FloatTy::F16) => Some(InlineAsmType::VecF16(size)), ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(size)), ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(size)), + ty::Float(FloatTy::F128) => Some(InlineAsmType::VecF128(size)), _ => None, } } @@ -134,7 +138,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { // `!` is allowed for input but not for output (issue #87802) ty::Never if is_input => return None, _ if ty.references_error() => return None, - ty::Adt(adt, args) if Some(adt.did()) == self.tcx.lang_items().maybe_uninit() => { + ty::Adt(adt, args) if self.tcx.is_lang_item(adt.did(), LangItem::MaybeUninit) => { let fields = &adt.non_enum_variant().fields; let ty = fields[FieldIdx::from_u32(1)].ty(self.tcx, args); // FIXME: Are we just trying to map to the `T` in `MaybeUninit`? @@ -281,8 +285,8 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { lint::builtin::ASM_SUB_REGISTER, expr.hir_id, spans, - "formatting may not be suitable for sub-register argument", |lint| { + lint.primary_message("formatting may not be suitable for sub-register argument"); lint.span_label(expr.span, "for this argument"); lint.help(format!( "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)", diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 0083da2a1e44..4d1b96d9c1ba 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -112,6 +112,7 @@ pub fn provide(providers: &mut Providers) { wfcheck::provide(providers); *providers = Providers { adt_destructor, + adt_async_destructor, region_scope_tree, collect_return_position_impl_trait_in_trait_tys, compare_impl_const: compare_impl_item::compare_impl_const_raw, @@ -124,6 +125,10 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option tcx.calculate_dtor(def_id.to_def_id(), dropck::check_drop_impl) } +fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { + tcx.calculate_async_dtor(def_id.to_def_id(), dropck::check_drop_impl) +} + /// Given a `DefId` for an opaque type in return position, find its parent item's return /// expressions. fn get_owner_return_paths( @@ -436,7 +441,9 @@ fn fn_sig_suggestion<'tcx>( output = if let ty::Alias(_, alias_ty) = *output.kind() { tcx.explicit_item_super_predicates(alias_ty.def_id) .iter_instantiated_copied(tcx, alias_ty.args) - .find_map(|(bound, _)| bound.as_projection_clause()?.no_bound_vars()?.term.ty()) + .find_map(|(bound, _)| { + bound.as_projection_clause()?.no_bound_vars()?.term.as_type() + }) .unwrap_or_else(|| { span_bug!( ident.span, @@ -594,7 +601,7 @@ pub fn check_function_signature<'tcx>( let param_env = ty::ParamEnv::empty(); let infcx = &tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(infcx); + let ocx = ObligationCtxt::new_with_diagnostics(infcx); let actual_sig = tcx.fn_sig(fn_id).instantiate_identity(); diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 4540310937d0..2b5efd3b2f6f 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -6,7 +6,6 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html -use rustc_ast::visit::walk_list; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -168,7 +167,14 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement), } } - walk_list!(visitor, visit_expr, &blk.expr); + if let Some(tail_expr) = blk.expr { + if visitor.tcx.features().shorter_tail_lifetimes + && blk.span.edition().at_least_rust_2024() + { + visitor.terminating_scopes.insert(tail_expr.hir_id.local_id); + } + visitor.visit_expr(tail_expr); + } } visitor.cx = prev_cx; @@ -782,25 +788,8 @@ impl<'tcx> RegionResolutionVisitor<'tcx> { } self.enter_scope(Scope { id, data: ScopeData::Node }); } -} - -impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { - fn visit_block(&mut self, b: &'tcx Block<'tcx>) { - resolve_block(self, b); - } - - fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) { - let body_id = body.id(); - let owner_id = self.tcx.hir().body_owner_def_id(body_id); - - debug!( - "visit_body(id={:?}, span={:?}, body.id={:?}, cx.parent={:?})", - owner_id, - self.tcx.sess.source_map().span_to_diagnostic_string(body.value.span), - body_id, - self.cx.parent - ); + fn enter_body(&mut self, hir_id: hir::HirId, f: impl FnOnce(&mut Self)) { // Save all state that is specific to the outer function // body. These will be restored once down below, once we've // visited the body. @@ -812,43 +801,12 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { // control flow assumptions. This doesn't apply to nested // bodies within the `+=` statements. See #69307. let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false); - self.terminating_scopes.insert(body.value.hir_id.local_id); + self.terminating_scopes.insert(hir_id.local_id); - self.enter_scope(Scope { id: body.value.hir_id.local_id, data: ScopeData::CallSite }); - self.enter_scope(Scope { id: body.value.hir_id.local_id, data: ScopeData::Arguments }); + self.enter_scope(Scope { id: hir_id.local_id, data: ScopeData::CallSite }); + self.enter_scope(Scope { id: hir_id.local_id, data: ScopeData::Arguments }); - // The arguments and `self` are parented to the fn. - self.cx.var_parent = self.cx.parent.take(); - for param in body.params { - self.visit_pat(param.pat); - } - - // The body of the every fn is a root scope. - self.cx.parent = self.cx.var_parent; - if self.tcx.hir().body_owner_kind(owner_id).is_fn_or_closure() { - self.visit_expr(body.value) - } else { - // Only functions have an outer terminating (drop) scope, while - // temporaries in constant initializers may be 'static, but only - // according to rvalue lifetime semantics, using the same - // syntactical rules used for let initializers. - // - // e.g., in `let x = &f();`, the temporary holding the result from - // the `f()` call lives for the entirety of the surrounding block. - // - // Similarly, `const X: ... = &f();` would have the result of `f()` - // live for `'static`, implying (if Drop restrictions on constants - // ever get lifted) that the value *could* have a destructor, but - // it'd get leaked instead of the destructor running during the - // evaluation of `X` (if at all allowed by CTFE). - // - // However, `const Y: ... = g(&f());`, like `let y = g(&f());`, - // would *not* let the `f()` temporary escape into an outer scope - // (i.e., `'static`), which means that after `g` returns, it drops, - // and all the associated destruction scope rules apply. - self.cx.var_parent = None; - resolve_local(self, None, Some(body.value)); - } + f(self); // Restore context we had at the start. self.expr_and_pat_count = outer_ec; @@ -856,6 +814,60 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { self.terminating_scopes = outer_ts; self.pessimistic_yield = outer_pessimistic_yield; } +} + +impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { + fn visit_block(&mut self, b: &'tcx Block<'tcx>) { + resolve_block(self, b); + } + + fn visit_body(&mut self, body: &hir::Body<'tcx>) { + let body_id = body.id(); + let owner_id = self.tcx.hir().body_owner_def_id(body_id); + + debug!( + "visit_body(id={:?}, span={:?}, body.id={:?}, cx.parent={:?})", + owner_id, + self.tcx.sess.source_map().span_to_diagnostic_string(body.value.span), + body_id, + self.cx.parent + ); + + self.enter_body(body.value.hir_id, |this| { + if this.tcx.hir().body_owner_kind(owner_id).is_fn_or_closure() { + // The arguments and `self` are parented to the fn. + this.cx.var_parent = this.cx.parent.take(); + for param in body.params { + this.visit_pat(param.pat); + } + + // The body of the every fn is a root scope. + this.cx.parent = this.cx.var_parent; + this.visit_expr(body.value) + } else { + // Only functions have an outer terminating (drop) scope, while + // temporaries in constant initializers may be 'static, but only + // according to rvalue lifetime semantics, using the same + // syntactical rules used for let initializers. + // + // e.g., in `let x = &f();`, the temporary holding the result from + // the `f()` call lives for the entirety of the surrounding block. + // + // Similarly, `const X: ... = &f();` would have the result of `f()` + // live for `'static`, implying (if Drop restrictions on constants + // ever get lifted) that the value *could* have a destructor, but + // it'd get leaked instead of the destructor running during the + // evaluation of `X` (if at all allowed by CTFE). + // + // However, `const Y: ... = g(&f());`, like `let y = g(&f());`, + // would *not* let the `f()` temporary escape into an outer scope + // (i.e., `'static`), which means that after `g` returns, it drops, + // and all the associated destruction scope rules apply. + this.cx.var_parent = None; + resolve_local(this, None, Some(body.value)); + } + }) + } fn visit_arm(&mut self, a: &'tcx Arm<'tcx>) { resolve_arm(self, a); @@ -887,7 +899,7 @@ pub fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { return tcx.region_scope_tree(typeck_root_def_id); } - let scope_tree = if let Some(body_id) = tcx.hir().maybe_body_owned_by(def_id.expect_local()) { + let scope_tree = if let Some(body) = tcx.hir().maybe_body_owned_by(def_id.expect_local()) { let mut visitor = RegionResolutionVisitor { tcx, scope_tree: ScopeTree::default(), @@ -898,9 +910,8 @@ pub fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { fixup_scopes: vec![], }; - let body = tcx.hir().body(body_id); visitor.scope_tree.root_body = Some(body.value.hir_id); - visitor.visit_body(body); + visitor.visit_body(&body); visitor.scope_tree } else { ScopeTree::default() diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index e137aab21099..b206d8046ee9 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -37,7 +37,7 @@ use rustc_trait_selection::traits::misc::{ use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc, + self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc, }; use rustc_type_ir::TypeFlags; @@ -45,13 +45,13 @@ use std::cell::LazyCell; use std::ops::{ControlFlow, Deref}; pub(super) struct WfCheckingCtxt<'a, 'tcx> { - pub(super) ocx: ObligationCtxt<'a, 'tcx>, + pub(super) ocx: ObligationCtxt<'a, 'tcx, FulfillmentError<'tcx>>, span: Span, body_def_id: LocalDefId, param_env: ty::ParamEnv<'tcx>, } impl<'a, 'tcx> Deref for WfCheckingCtxt<'a, 'tcx> { - type Target = ObligationCtxt<'a, 'tcx>; + type Target = ObligationCtxt<'a, 'tcx, FulfillmentError<'tcx>>; fn deref(&self) -> &Self::Target { &self.ocx } @@ -106,7 +106,7 @@ where { let param_env = tcx.param_env(body_def_id); let infcx = &tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(infcx); + let ocx = ObligationCtxt::new_with_diagnostics(infcx); let mut wfcx = WfCheckingCtxt { ocx, span, body_def_id, param_env }; @@ -432,7 +432,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { } let gat_generics = tcx.generics_of(gat_def_id); // FIXME(jackh726): we can also warn in the more general case - if gat_generics.own_params.is_empty() { + if gat_generics.is_own_empty() { continue; } @@ -675,11 +675,7 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable>>( let region_param = gat_generics.param_at(*region_a_idx, tcx); let region_param = ty::Region::new_early_param( tcx, - ty::EarlyParamRegion { - def_id: region_param.def_id, - index: region_param.index, - name: region_param.name, - }, + ty::EarlyParamRegion { index: region_param.index, name: region_param.name }, ); // The predicate we expect to see. (In our example, // `Self: 'me`.) @@ -708,21 +704,13 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable>>( let region_a_param = gat_generics.param_at(*region_a_idx, tcx); let region_a_param = ty::Region::new_early_param( tcx, - ty::EarlyParamRegion { - def_id: region_a_param.def_id, - index: region_a_param.index, - name: region_a_param.name, - }, + ty::EarlyParamRegion { index: region_a_param.index, name: region_a_param.name }, ); // Same for the region. let region_b_param = gat_generics.param_at(*region_b_idx, tcx); let region_b_param = ty::Region::new_early_param( tcx, - ty::EarlyParamRegion { - def_id: region_b_param.def_id, - index: region_b_param.index, - name: region_b_param.name, - }, + ty::EarlyParamRegion { index: region_b_param.index, name: region_b_param.name }, ); // The predicate we expect to see. bounds.insert( @@ -893,7 +881,7 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem _ => {} } if !trait_should_be_self.is_empty() { - if tcx.check_is_object_safe(trait_def_id) { + if tcx.is_object_safe(trait_def_id) { return; } let sugg = trait_should_be_self.iter().map(|span| (*span, "Self".to_string())).collect(); @@ -2101,16 +2089,14 @@ fn lint_redundant_lifetimes<'tcx>( } for &victim in &lifetimes[(idx + 1)..] { - // We should only have late-bound lifetimes of the `BrNamed` variety, - // since we get these signatures straight from `hir_lowering`. And any - // other regions (ReError/ReStatic/etc.) shouldn't matter, since we + // All region parameters should have a `DefId` available as: + // - Late-bound parameters should be of the`BrNamed` variety, + // since we get these signatures straight from `hir_lowering`. + // - Early-bound parameters unconditionally have a `DefId` available. + // + // Any other regions (ReError/ReStatic/etc.) shouldn't matter, since we // can't really suggest to remove them. - let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) - | ty::ReLateParam(ty::LateParamRegion { - bound_region: ty::BoundRegionKind::BrNamed(def_id, _), - .. - })) = victim.kind() - else { + let Some(def_id) = victim.opt_param_def_id(tcx, owner_id.to_def_id()) else { continue; }; diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs index aa5db4f6aa7b..ed23dc2a827b 100644 --- a/compiler/rustc_hir_analysis/src/check_unused.rs +++ b/compiler/rustc_hir_analysis/src/check_unused.rs @@ -35,11 +35,12 @@ fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) { continue; } let (path, _) = item.expect_use(); - let msg = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) { - format!("unused import: `{snippet}`") - } else { - "unused import".to_owned() - }; - tcx.node_span_lint(lint::builtin::UNUSED_IMPORTS, item.hir_id(), path.span, msg, |_| {}); + tcx.node_span_lint(lint::builtin::UNUSED_IMPORTS, item.hir_id(), path.span, |lint| { + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) { + lint.primary_message(format!("unused import: `{snippet}`")); + } else { + lint.primary_message("unused import"); + } + }); } } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 8f0aba1c38c1..61adb7a3cbae 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -267,7 +267,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() .join(", "), })); } else { - let ocx = ObligationCtxt::new(&infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); for field in coerced_fields { ocx.register_obligation(Obligation::new( tcx, @@ -480,7 +480,7 @@ pub fn coerce_unsized_info<'tcx>( }; // Register an obligation for `A: Trait`. - let ocx = ObligationCtxt::new(&infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let cause = traits::ObligationCause::misc(span, impl_did); let obligation = Obligation::new( tcx, @@ -554,7 +554,7 @@ fn infringing_fields_error( if let ty::Param(_) = ty.kind() { bounds.push(( format!("{ty}"), - trait_ref.print_only_trait_path().to_string(), + trait_ref.print_trait_sugared().to_string(), Some(trait_ref.def_id), )); } diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 054a3af212a5..e9961d3ad086 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -8,11 +8,11 @@ use crate::errors; use rustc_errors::{codes::*, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::LangItem; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_session::parse::feature_err; use rustc_span::{sym, ErrorGuaranteed}; -use rustc_trait_selection::traits; mod builtin; mod inherent_impls; @@ -50,7 +50,7 @@ fn enforce_trait_manually_implementable( ) -> Result<(), ErrorGuaranteed> { let impl_header_span = tcx.def_span(impl_def_id); - if tcx.lang_items().freeze_trait() == Some(trait_def_id) { + if tcx.is_lang_item(trait_def_id, LangItem::Freeze) { if !tcx.features().freeze_impls { feature_err( &tcx.sess, @@ -76,7 +76,7 @@ fn enforce_trait_manually_implementable( // Maintain explicit error code for `Unsize`, since it has a useful // explanation about using `CoerceUnsized` instead. - if Some(trait_def_id) == tcx.lang_items().unsize_trait() { + if tcx.is_lang_item(trait_def_id, LangItem::Unsize) { err.code(E0328); } @@ -192,14 +192,14 @@ fn check_object_overlap<'tcx>( }); for component_def_id in component_def_ids { - if !tcx.check_is_object_safe(component_def_id) { + if !tcx.is_object_safe(component_def_id) { // Without the 'object_safe_for_dispatch' feature this is an error // which will be reported by wfcheck. Ignore it here. // This is tested by `coherence-impl-trait-for-trait-object-safe.rs`. // With the feature enabled, the trait is not implemented automatically, // so this is valid. } else { - let mut supertrait_def_ids = traits::supertrait_def_ids(tcx, component_def_id); + let mut supertrait_def_ids = tcx.supertrait_def_ids(component_def_id); if supertrait_def_ids.any(|d| d == trait_def_id) { let span = tcx.def_span(impl_def_id); return Err(struct_span_code_err!( diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index bdac0d9b0b40..9421269e51ea 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -14,7 +14,6 @@ use rustc_middle::{bug, span_bug}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_trait_selection::traits::{self, IsFirstInputType, UncoveredTyParams}; use rustc_trait_selection::traits::{OrphanCheckErr, OrphanCheckMode}; -use rustc_trait_selection::traits::{StructurallyNormalizeExt, TraitEngineExt}; #[instrument(level = "debug", skip(tcx))] pub(crate) fn orphan_check_impl( @@ -317,12 +316,12 @@ fn orphan_check<'tcx>( } let ty = if infcx.next_trait_solver() { - let mut fulfill_cx = >::new(&infcx); - infcx - .at(&cause, ty::ParamEnv::empty()) - .structurally_normalize(ty, &mut *fulfill_cx) - .map(|ty| infcx.resolve_vars_if_possible(ty)) - .unwrap_or(ty) + ocx.structurally_normalize( + &cause, + ty::ParamEnv::empty(), + infcx.resolve_vars_if_possible(ty), + ) + .unwrap_or(ty) } else { ty }; @@ -404,74 +403,56 @@ fn emit_orphan_check_error<'tcx>( match *ty.kind() { ty::Slice(_) => { if is_foreign { - diag.subdiagnostic( - tcx.dcx(), - errors::OnlyCurrentTraitsForeign { span }, - ); + diag.subdiagnostic(errors::OnlyCurrentTraitsForeign { span }); } else { - diag.subdiagnostic( - tcx.dcx(), - errors::OnlyCurrentTraitsName { span, name: "slices" }, - ); + diag.subdiagnostic(errors::OnlyCurrentTraitsName { + span, + name: "slices", + }); } } ty::Array(..) => { if is_foreign { - diag.subdiagnostic( - tcx.dcx(), - errors::OnlyCurrentTraitsForeign { span }, - ); + diag.subdiagnostic(errors::OnlyCurrentTraitsForeign { span }); } else { - diag.subdiagnostic( - tcx.dcx(), - errors::OnlyCurrentTraitsName { span, name: "arrays" }, - ); + diag.subdiagnostic(errors::OnlyCurrentTraitsName { + span, + name: "arrays", + }); } } ty::Tuple(..) => { if is_foreign { - diag.subdiagnostic( - tcx.dcx(), - errors::OnlyCurrentTraitsForeign { span }, - ); + diag.subdiagnostic(errors::OnlyCurrentTraitsForeign { span }); } else { - diag.subdiagnostic( - tcx.dcx(), - errors::OnlyCurrentTraitsName { span, name: "tuples" }, - ); + diag.subdiagnostic(errors::OnlyCurrentTraitsName { + span, + name: "tuples", + }); } } ty::Alias(ty::Opaque, ..) => { - diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsOpaque { span }); + diag.subdiagnostic(errors::OnlyCurrentTraitsOpaque { span }); } ty::RawPtr(ptr_ty, mutbl) => { if !trait_ref.self_ty().has_param() { - diag.subdiagnostic( - tcx.dcx(), - errors::OnlyCurrentTraitsPointerSugg { - wrapper_span: impl_.self_ty.span, - struct_span: item.span.shrink_to_lo(), - mut_key: mutbl.prefix_str(), - ptr_ty, - }, - ); + diag.subdiagnostic(errors::OnlyCurrentTraitsPointerSugg { + wrapper_span: impl_.self_ty.span, + struct_span: item.span.shrink_to_lo(), + mut_key: mutbl.prefix_str(), + ptr_ty, + }); } - diag.subdiagnostic( - tcx.dcx(), - errors::OnlyCurrentTraitsPointer { span, pointer: ty }, - ); + diag.subdiagnostic(errors::OnlyCurrentTraitsPointer { span, pointer: ty }); } ty::Adt(adt_def, _) => { - diag.subdiagnostic( - tcx.dcx(), - errors::OnlyCurrentTraitsAdt { - span, - name: tcx.def_path_str(adt_def.did()), - }, - ); + diag.subdiagnostic(errors::OnlyCurrentTraitsAdt { + span, + name: tcx.def_path_str(adt_def.did()), + }); } _ => { - diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsTy { span, ty }); + diag.subdiagnostic(errors::OnlyCurrentTraitsTy { span, ty }); } } } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index aa28b2c8e2cf..c6e8759327f0 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -18,11 +18,11 @@ use rustc_ast::Recovered; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::UnordMap; -use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey}; -use rustc_hir as hir; +use rustc_errors::{struct_span_code_err, Applicability, Diag, ErrorGuaranteed, StashKey, E0228}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::intravisit::{self, walk_generics, Visitor}; +use rustc_hir::{self as hir}; use rustc_hir::{GenericParamKind, Node}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; @@ -44,7 +44,7 @@ use std::ops::Bound; use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::errors; -use crate::hir_ty_lowering::HirTyLowerer; +use crate::hir_ty_lowering::{HirTyLowerer, RegionInferReason}; pub use type_of::test_opaque_hidden_types; mod generics_of; @@ -370,32 +370,34 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { self.tcx } - fn item_def_id(&self) -> DefId { - self.item_def_id.to_def_id() + fn item_def_id(&self) -> LocalDefId { + self.item_def_id } - fn allow_infer(&self) -> bool { - false - } - - fn re_infer(&self, _: Option<&ty::GenericParamDef>, _: Span) -> Option> { - None + fn re_infer(&self, span: Span, reason: RegionInferReason<'_>) -> ty::Region<'tcx> { + if let RegionInferReason::BorrowedObjectLifetimeDefault = reason { + let e = struct_span_code_err!( + self.tcx().dcx(), + span, + E0228, + "the lifetime bound for this object type cannot be deduced \ + from context; please supply an explicit bound" + ) + .emit(); + self.set_tainted_by_errors(e); + ty::Region::new_error(self.tcx(), e) + } else { + // This indicates an illegal lifetime in a non-assoc-trait position + ty::Region::new_error_with_message(self.tcx(), span, "unelided lifetime in signature") + } } fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { Ty::new_error_with_message(self.tcx(), span, "bad placeholder type") } - fn ct_infer(&self, ty: Ty<'tcx>, _: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx> { - let ty = self.tcx.fold_regions(ty, |r, _| match *r { - rustc_type_ir::RegionKind::ReStatic => r, - - // This is never reached in practice. If it ever is reached, - // `ReErased` should be changed to `ReStatic`, and any other region - // left alone. - r => bug!("unexpected region: {r:?}"), - }); - ty::Const::new_error_with_message(self.tcx(), ty, span, "bad placeholder constant") + fn ct_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx> { + ty::Const::new_error_with_message(self.tcx(), span, "bad placeholder constant") } fn probe_ty_param_bounds( @@ -453,7 +455,6 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { poly_trait_ref, |_| { ty::Region::new_early_param(self.tcx, ty::EarlyParamRegion { - def_id: item_def_id, index: 0, name: Symbol::intern(<_name), }) @@ -511,6 +512,89 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { fn set_tainted_by_errors(&self, err: ErrorGuaranteed) { self.tainted_by_errors.set(Some(err)); } + + fn lower_fn_sig( + &self, + decl: &hir::FnDecl<'tcx>, + generics: Option<&hir::Generics<'_>>, + hir_id: rustc_hir::HirId, + hir_ty: Option<&hir::Ty<'_>>, + ) -> (Vec>, Ty<'tcx>) { + let tcx = self.tcx(); + // We proactively collect all the inferred type params to emit a single error per fn def. + let mut visitor = HirPlaceholderCollector::default(); + let mut infer_replacements = vec![]; + + if let Some(generics) = generics { + walk_generics(&mut visitor, generics); + } + + let input_tys = decl + .inputs + .iter() + .enumerate() + .map(|(i, a)| { + if let hir::TyKind::Infer = a.kind { + if let Some(suggested_ty) = + self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i)) + { + infer_replacements.push((a.span, suggested_ty.to_string())); + return Ty::new_error_with_message(tcx, a.span, suggested_ty.to_string()); + } + } + + // Only visit the type looking for `_` if we didn't fix the type above + visitor.visit_ty(a); + self.lowerer().lower_arg_ty(a, None) + }) + .collect(); + + let output_ty = match decl.output { + hir::FnRetTy::Return(output) => { + if let hir::TyKind::Infer = output.kind + && let Some(suggested_ty) = + self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None) + { + infer_replacements.push((output.span, suggested_ty.to_string())); + Ty::new_error_with_message(tcx, output.span, suggested_ty.to_string()) + } else { + visitor.visit_ty(output); + self.lower_ty(output) + } + } + hir::FnRetTy::DefaultReturn(..) => tcx.types.unit, + }; + + if !(visitor.0.is_empty() && infer_replacements.is_empty()) { + // We check for the presence of + // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`. + + let mut diag = crate::collect::placeholder_type_error_diag( + tcx, + generics, + visitor.0, + infer_replacements.iter().map(|(s, _)| *s).collect(), + true, + hir_ty, + "function", + ); + + if !infer_replacements.is_empty() { + diag.multipart_suggestion( + format!( + "try replacing `_` with the type{} in the corresponding trait method signature", + rustc_errors::pluralize!(infer_replacements.len()), + ), + infer_replacements, + Applicability::MachineApplicable, + ); + } + + self.set_tainted_by_errors(diag.emit()); + } + + (input_tys, output_ty) + } } /// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present. @@ -1117,8 +1201,24 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { let is_marker = tcx.has_attr(def_id, sym::marker); let rustc_coinductive = tcx.has_attr(def_id, sym::rustc_coinductive); - let skip_array_during_method_dispatch = - tcx.has_attr(def_id, sym::rustc_skip_array_during_method_dispatch); + + // FIXME: We could probably do way better attribute validation here. + let mut skip_array_during_method_dispatch = false; + let mut skip_boxed_slice_during_method_dispatch = false; + for attr in tcx.get_attrs(def_id, sym::rustc_skip_during_method_dispatch) { + if let Some(lst) = attr.meta_item_list() { + for item in lst { + if let Some(ident) = item.ident() { + match ident.as_str() { + "array" => skip_array_during_method_dispatch = true, + "boxed_slice" => skip_boxed_slice_during_method_dispatch = true, + _ => (), + } + } + } + } + } + let specialization_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) { ty::trait_def::TraitSpecializationKind::Marker } else if tcx.has_attr(def_id, sym::rustc_specialization_trait) { @@ -1253,6 +1353,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { is_marker, is_coinductive: rustc_coinductive || is_auto, skip_array_during_method_dispatch, + skip_boxed_slice_during_method_dispatch, specialization_kind, must_implement_one_of, implement_via_object, @@ -1261,7 +1362,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { } #[instrument(level = "debug", skip(tcx))] -fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder> { +fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFnSig<'_>> { use rustc_hir::Node::*; use rustc_hir::*; @@ -1305,9 +1406,11 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder { + ForeignItem(&hir::ForeignItem { + kind: ForeignItemKind::Fn(fn_decl, _, _, safety), .. + }) => { let abi = tcx.hir().get_foreign_abi(hir_id); - compute_sig_of_foreign_fn_decl(tcx, def_id, fn_decl, abi) + compute_sig_of_foreign_fn_decl(tcx, def_id, fn_decl, abi, safety) } Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor().is_some() => { @@ -1462,7 +1565,7 @@ pub fn suggest_impl_trait<'tcx>( ), ( infcx.tcx.lang_items().future_trait(), - infcx.tcx.get_diagnostic_item(sym::FutureOutput), + infcx.tcx.lang_items().future_output(), format_as_assoc, ), ( @@ -1679,11 +1782,12 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( def_id: LocalDefId, decl: &'tcx hir::FnDecl<'tcx>, abi: abi::Abi, + safety: hir::Safety, ) -> ty::PolyFnSig<'tcx> { let safety = if abi == abi::Abi::RustIntrinsic { intrinsic_operation_unsafety(tcx, def_id) } else { - hir::Safety::Unsafe + safety }; let hir_id = tcx.local_def_id_to_hir_id(def_id); let fty = diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 6b41f79cf1e7..303fa23dbc1e 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -11,6 +11,7 @@ use rustc_session::lint; use rustc_span::symbol::{kw, Symbol}; use rustc_span::Span; +#[instrument(level = "debug", skip(tcx))] pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { use rustc_hir::*; @@ -66,7 +67,22 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // FIXME(#43408) always enable this once `lazy_normalization` is // stable enough and does not need a feature gate anymore. Node::AnonConst(_) => { - let parent_def_id = tcx.hir().get_parent_item(hir_id); + let parent_did = tcx.parent(def_id.to_def_id()); + + // We don't do this unconditionally because the `DefId` parent of an anon const + // might be an implicitly created closure during `async fn` desugaring. This would + // have the wrong generics. + // + // i.e. `async fn foo<'a>() { let a = [(); { 1 + 2 }]; bar().await() }` + // would implicitly have a closure in its body that would be the parent of + // the `{ 1 + 2 }` anon const. This closure's generics is simply a witness + // instead of `['a]`. + let parent_did = if let DefKind::AnonConst = tcx.def_kind(parent_did) { + parent_did + } else { + tcx.hir().get_parent_item(hir_id).to_def_id() + }; + debug!(?parent_did); let mut in_param_ty = false; for (_parent, node) in tcx.hir().parent_iter(hir_id) { @@ -121,7 +137,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // // This has some implications for how we get the predicates available to the anon const // see `explicit_predicates_of` for more information on this - let generics = tcx.generics_of(parent_def_id.to_def_id()); + let generics = tcx.generics_of(parent_did); let param_def_idx = generics.param_def_id_to_index[¶m_id.to_def_id()]; // In the above example this would be .params[..N#0] let own_params = generics.params_to(param_def_idx as usize, tcx).to_owned(); @@ -147,7 +163,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // // Note that we do not supply the parent generics when using // `min_const_generics`. - Some(parent_def_id.to_def_id()) + Some(parent_did) } } else { let parent_node = tcx.parent_hir_node(hir_id); @@ -159,7 +175,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { Node::Expr(Expr { kind: ExprKind::Repeat(_, constant), .. }) if constant.hir_id() == hir_id => { - Some(parent_def_id.to_def_id()) + Some(parent_did) } // Exclude `GlobalAsm` here which cannot have generics. Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) @@ -171,7 +187,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { _ => false, }) => { - Some(parent_def_id.to_def_id()) + Some(parent_did) } _ => None, } @@ -317,8 +333,9 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { lint::builtin::INVALID_TYPE_PARAM_DEFAULT, param.hir_id, param.span, - TYPE_DEFAULT_NOT_ALLOWED, - |_| {}, + |lint| { + lint.primary_message(TYPE_DEFAULT_NOT_ALLOWED); + }, ); } Defaults::Deny => { diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index dd1d209389dc..94d6e13d751f 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -59,7 +59,7 @@ fn associated_type_bounds<'tcx>( /// impl trait it isn't possible to write a suitable predicate on the /// containing function and for type-alias impl trait we don't have a backwards /// compatibility issue. -#[instrument(level = "trace", skip(tcx), ret)] +#[instrument(level = "trace", skip(tcx, item_ty))] fn opaque_type_bounds<'tcx>( tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId, @@ -82,14 +82,14 @@ fn opaque_type_bounds<'tcx>( pub(super) fn explicit_item_bounds( tcx: TyCtxt<'_>, def_id: LocalDefId, -) -> ty::EarlyBinder<&'_ [(ty::Clause<'_>, Span)]> { +) -> ty::EarlyBinder<'_, &'_ [(ty::Clause<'_>, Span)]> { explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::All) } pub(super) fn explicit_item_super_predicates( tcx: TyCtxt<'_>, def_id: LocalDefId, -) -> ty::EarlyBinder<&'_ [(ty::Clause<'_>, Span)]> { +) -> ty::EarlyBinder<'_, &'_ [(ty::Clause<'_>, Span)]> { explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::SelfOnly) } @@ -97,7 +97,7 @@ pub(super) fn explicit_item_bounds_with_filter( tcx: TyCtxt<'_>, def_id: LocalDefId, filter: PredicateFilter, -) -> ty::EarlyBinder<&'_ [(ty::Clause<'_>, Span)]> { +) -> ty::EarlyBinder<'_, &'_ [(ty::Clause<'_>, Span)]> { match tcx.opt_rpitit_info(def_id.to_def_id()) { // RPITIT's bounds are the same as opaque type bounds, but with // a projection self type. @@ -166,7 +166,7 @@ pub(super) fn explicit_item_bounds_with_filter( ty::EarlyBinder::bind(bounds) } -pub(super) fn item_bounds(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder> { +pub(super) fn item_bounds(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<'_, ty::Clauses<'_>> { tcx.explicit_item_bounds(def_id).map_bound(|bounds| { tcx.mk_clauses_from_iter(util::elaborate(tcx, bounds.iter().map(|&(bound, _span)| bound))) }) @@ -175,7 +175,7 @@ pub(super) fn item_bounds(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder, def_id: DefId, -) -> ty::EarlyBinder> { +) -> ty::EarlyBinder<'_, ty::Clauses<'_>> { tcx.explicit_item_super_predicates(def_id).map_bound(|bounds| { tcx.mk_clauses_from_iter( util::elaborate(tcx, bounds.iter().map(|&(bound, _span)| bound)).filter_only_self(), @@ -186,7 +186,7 @@ pub(super) fn item_super_predicates( pub(super) fn item_non_self_assumptions( tcx: TyCtxt<'_>, def_id: DefId, -) -> ty::EarlyBinder> { +) -> ty::EarlyBinder<'_, ty::Clauses<'_>> { let all_bounds: FxIndexSet<_> = tcx.item_bounds(def_id).skip_binder().iter().collect(); let own_bounds: FxIndexSet<_> = tcx.item_super_predicates(def_id).skip_binder().iter().collect(); diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index db36aba7edf4..3421c8da4e9f 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -1,7 +1,7 @@ use crate::bounds::Bounds; use crate::collect::ItemCtxt; use crate::constrained_generic_params as cgp; -use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter}; +use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter, RegionInferReason}; use hir::{HirId, Node}; use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; @@ -117,7 +117,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen let mut is_trait = None; let mut is_default_impl_trait = None; - // FIXME: Should ItemCtxt take a LocalDefId? let icx = ItemCtxt::new(tcx, def_id); const NO_GENERICS: &hir::Generics<'_> = hir::Generics::empty(); @@ -170,12 +169,11 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen predicates.insert((trait_ref.upcast(tcx), tcx.def_span(def_id))); } - // Collect the predicates that were written inline by the user on each - // type parameter (e.g., ``). Also add `ConstArgHasType` predicates - // for each const parameter. + // Add implicit predicates that should be treated as if the user has written them, + // including the implicit `T: Sized` for all generic parameters, and `ConstArgHasType` + // for const params. for param in hir_generics.params { match param.kind { - // We already dealt with early bound lifetimes above. GenericParamKind::Lifetime { .. } => (), GenericParamKind::Type { .. } => { let param_ty = icx.lowerer().lower_ty_param(param.hir_id); @@ -197,7 +195,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen .type_of(param.def_id.to_def_id()) .no_bound_vars() .expect("const parameters cannot be generic"); - let ct = icx.lowerer().lower_const_param(param.hir_id, ct_ty); + let ct = icx.lowerer().lower_const_param(param.hir_id); predicates .insert((ty::ClauseKind::ConstArgHasType(ct, ct_ty).upcast(tcx), param.span)); } @@ -205,7 +203,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } trace!(?predicates); - // Add in the bounds that appear in the where-clause. + // Add inline `` bounds and bounds in the where clause. for predicate in hir_generics.predicates { match predicate { hir::WherePredicate::BoundPredicate(bound_pred) => { @@ -244,12 +242,15 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } hir::WherePredicate::RegionPredicate(region_pred) => { - let r1 = icx.lowerer().lower_lifetime(region_pred.lifetime, None); + let r1 = icx + .lowerer() + .lower_lifetime(region_pred.lifetime, RegionInferReason::RegionPredicate); predicates.extend(region_pred.bounds.iter().map(|bound| { let (r2, span) = match bound { - hir::GenericBound::Outlives(lt) => { - (icx.lowerer().lower_lifetime(lt, None), lt.ident.span) - } + hir::GenericBound::Outlives(lt) => ( + icx.lowerer().lower_lifetime(lt, RegionInferReason::RegionPredicate), + lt.ident.span, + ), bound => { span_bug!( bound.span(), @@ -323,7 +324,7 @@ fn compute_bidirectional_outlives_predicates<'tcx>( if let ty::ReEarlyParam(..) = *orig_lifetime { let dup_lifetime = ty::Region::new_early_param( tcx, - ty::EarlyParamRegion { def_id: param.def_id, index: param.index, name: param.name }, + ty::EarlyParamRegion { index: param.index, name: param.name }, ); let span = tcx.def_span(param.def_id); predicates.push(( @@ -461,83 +462,55 @@ pub(super) fn explicit_predicates_of<'tcx>( } } } else { - if matches!(def_kind, DefKind::AnonConst) && tcx.features().generic_const_exprs { - let hir_id = tcx.local_def_id_to_hir_id(def_id); - let parent_def_id = tcx.hir().get_parent_item(hir_id); + if matches!(def_kind, DefKind::AnonConst) + && tcx.features().generic_const_exprs + && let Some(defaulted_param_def_id) = + tcx.hir().opt_const_param_default_param_def_id(tcx.local_def_id_to_hir_id(def_id)) + { + // In `generics_of` we set the generics' parent to be our parent's parent which means that + // we lose out on the predicates of our actual parent if we dont return those predicates here. + // (See comment in `generics_of` for more information on why the parent shenanigans is necessary) + // + // struct Foo::ASSOC }>(T) where T: Trait; + // ^^^ ^^^^^^^^^^^^^^^^^^^^^^^ the def id we are calling + // ^^^ explicit_predicates_of on + // parent item we dont have set as the + // parent of generics returned by `generics_of` + // + // In the above code we want the anon const to have predicates in its param env for `T: Trait` + // and we would be calling `explicit_predicates_of(Foo)` here + let parent_def_id = tcx.local_parent(def_id); + let parent_preds = tcx.explicit_predicates_of(parent_def_id); - if let Some(defaulted_param_def_id) = - tcx.hir().opt_const_param_default_param_def_id(hir_id) - { - // In `generics_of` we set the generics' parent to be our parent's parent which means that - // we lose out on the predicates of our actual parent if we dont return those predicates here. - // (See comment in `generics_of` for more information on why the parent shenanigans is necessary) - // - // struct Foo::ASSOC }>(T) where T: Trait; - // ^^^ ^^^^^^^^^^^^^^^^^^^^^^^ the def id we are calling - // ^^^ explicit_predicates_of on - // parent item we dont have set as the - // parent of generics returned by `generics_of` - // - // In the above code we want the anon const to have predicates in its param env for `T: Trait` - // and we would be calling `explicit_predicates_of(Foo)` here - let parent_preds = tcx.explicit_predicates_of(parent_def_id); - - // If we dont filter out `ConstArgHasType` predicates then every single defaulted const parameter - // will ICE because of #106994. FIXME(generic_const_exprs): remove this when a more general solution - // to #106994 is implemented. - let filtered_predicates = parent_preds - .predicates - .into_iter() - .filter(|(pred, _)| { - if let ty::ClauseKind::ConstArgHasType(ct, _) = pred.kind().skip_binder() { - match ct.kind() { - ty::ConstKind::Param(param_const) => { - let defaulted_param_idx = tcx - .generics_of(parent_def_id) - .param_def_id_to_index[&defaulted_param_def_id.to_def_id()]; - param_const.index < defaulted_param_idx - } - _ => bug!( - "`ConstArgHasType` in `predicates_of`\ - that isn't a `Param` const" - ), + // If we dont filter out `ConstArgHasType` predicates then every single defaulted const parameter + // will ICE because of #106994. FIXME(generic_const_exprs): remove this when a more general solution + // to #106994 is implemented. + let filtered_predicates = parent_preds + .predicates + .into_iter() + .filter(|(pred, _)| { + if let ty::ClauseKind::ConstArgHasType(ct, _) = pred.kind().skip_binder() { + match ct.kind() { + ty::ConstKind::Param(param_const) => { + let defaulted_param_idx = tcx + .generics_of(parent_def_id) + .param_def_id_to_index[&defaulted_param_def_id.to_def_id()]; + param_const.index < defaulted_param_idx } - } else { - true + _ => bug!( + "`ConstArgHasType` in `predicates_of`\ + that isn't a `Param` const" + ), } - }) - .cloned(); - return GenericPredicates { - parent: parent_preds.parent, - predicates: { tcx.arena.alloc_from_iter(filtered_predicates) }, - }; - } - - let parent_def_kind = tcx.def_kind(parent_def_id); - if matches!(parent_def_kind, DefKind::OpaqueTy) { - // In `instantiate_identity` we inherit the predicates of our parent. - // However, opaque types do not have a parent (see `gather_explicit_predicates_of`), which means - // that we lose out on the predicates of our actual parent if we dont return those predicates here. - // - // - // fn foo() -> impl Iterator::ASSOC }> > { todo!() } - // ^^^^^^^^^^^^^^^^^^^ the def id we are calling - // explicit_predicates_of on - // - // In the above code we want the anon const to have predicates in its param env for `T: Trait`. - // However, the anon const cannot inherit predicates from its parent since it's opaque. - // - // To fix this, we call `explicit_predicates_of` directly on `foo`, the parent's parent. - - // In the above example this is `foo::{opaque#0}` or `impl Iterator` - let parent_hir_id = tcx.local_def_id_to_hir_id(parent_def_id.def_id); - - // In the above example this is the function `foo` - let item_def_id = tcx.hir().get_parent_item(parent_hir_id); - - // In the above code example we would be calling `explicit_predicates_of(foo)` here - return tcx.explicit_predicates_of(item_def_id); - } + } else { + true + } + }) + .cloned(); + return GenericPredicates { + parent: parent_preds.parent, + predicates: { tcx.arena.alloc_from_iter(filtered_predicates) }, + }; } gather_explicit_predicates_of(tcx, def_id) } diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 5c7733065c61..abc3bb838db3 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -603,7 +603,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) { match item.kind { - hir::ForeignItemKind::Fn(_, _, generics) => { + hir::ForeignItemKind::Fn(_, _, generics, _) => { self.visit_early_late(item.hir_id(), generics, |this| { intravisit::walk_foreign_item(this, item); }) @@ -1468,12 +1468,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { depth: usize, generic_args: &'tcx hir::GenericArgs<'tcx>, ) { - if generic_args.parenthesized == hir::GenericArgsParentheses::ParenSugar { - self.visit_fn_like_elision( - generic_args.inputs(), - Some(generic_args.bindings[0].ty()), - false, - ); + if let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() { + self.visit_fn_like_elision(inputs, Some(output), false); return; } @@ -1608,8 +1604,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } } - // Hack: when resolving the type `XX` in binding like `dyn - // Foo<'b, Item = XX>`, the current object-lifetime default + // Hack: When resolving the type `XX` in an assoc ty binding like + // `dyn Foo<'b, Item = XX>`, the current object-lifetime default // would be to examine the trait `Foo` to check whether it has // a lifetime bound declared on `Item`. e.g., if `Foo` is // declared like so, then the default object lifetime bound in @@ -1637,7 +1633,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // Resolve lifetimes found in the bindings, so either in the type `XX` in `Item = XX` or // in the trait ref `YY<...>` in `Item: YY<...>`. - for binding in generic_args.bindings { + for constraint in generic_args.constraints { let scope = Scope::ObjectLifetimeDefault { lifetime: if has_lifetime_parameter { None @@ -1646,7 +1642,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { }, s: self.scope, }; - // If the binding is parenthesized, then this must be `feature(return_type_notation)`. + // If the args are parenthesized, then this must be `feature(return_type_notation)`. // In that case, introduce a binder over all of the function's early and late bound vars. // // For example, given @@ -1659,13 +1655,14 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // `for<'a> T::Trait<'a, x(): for<'b> Other<'b>>` // this is going to expand to something like: // `for<'a> for<'r, T> >::x::<'r, T>::{opaque#0}: for<'b> Other<'b>`. - if binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation { + if constraint.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation + { let bound_vars = if let Some(type_def_id) = type_def_id && self.tcx.def_kind(type_def_id) == DefKind::Trait && let Some((mut bound_vars, assoc_fn)) = BoundVarContext::supertrait_hrtb_vars( self.tcx, type_def_id, - binding.ident, + constraint.ident, ty::AssocKind::Fn, ) { bound_vars.extend(self.tcx.generics_of(assoc_fn.def_id).own_params.iter().map( @@ -1686,22 +1683,22 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } else { self.tcx .dcx() - .span_delayed_bug(binding.ident.span, "bad return type notation here"); + .span_delayed_bug(constraint.ident.span, "bad return type notation here"); vec![] }; self.with(scope, |this| { let scope = Scope::Supertrait { bound_vars, s: this.scope }; this.with(scope, |this| { let (bound_vars, _) = this.poly_trait_ref_binder_info(); - this.record_late_bound_vars(binding.hir_id, bound_vars); - this.visit_assoc_type_binding(binding) + this.record_late_bound_vars(constraint.hir_id, bound_vars); + this.visit_assoc_item_constraint(constraint) }); }); } else if let Some(type_def_id) = type_def_id { let bound_vars = BoundVarContext::supertrait_hrtb_vars( self.tcx, type_def_id, - binding.ident, + constraint.ident, ty::AssocKind::Type, ) .map(|(bound_vars, _)| bound_vars); @@ -1710,10 +1707,10 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { bound_vars: bound_vars.unwrap_or_default(), s: this.scope, }; - this.with(scope, |this| this.visit_assoc_type_binding(binding)); + this.with(scope, |this| this.visit_assoc_item_constraint(constraint)); }); } else { - self.with(scope, |this| this.visit_assoc_type_binding(binding)); + self.with(scope, |this| this.visit_assoc_item_constraint(constraint)); } } } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 1475e53c47c2..2684467a4381 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -220,9 +220,10 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { .position(|arg| arg.hir_id() == hir_id) .map(|index| (index, seg)) .or_else(|| { - args.bindings + args.constraints .iter() - .filter_map(TypeBinding::opt_const) + .copied() + .filter_map(AssocItemConstraint::ct) .position(|ct| ct.hir_id == hir_id) .map(|idx| (idx, seg)) }) @@ -309,7 +310,7 @@ fn get_path_containing_arg_in_pat<'hir>( arg_path } -pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder> { +pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, Ty<'_>> { use rustc_hir::*; use rustc_middle::ty::Ty; @@ -463,7 +464,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder icx.lower_ty(t), + ForeignItemKind::Static(t, _, _) => icx.lower_ty(t), ForeignItemKind::Type => Ty::new_foreign(tcx, def_id.to_def_id()), }, @@ -502,7 +503,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder, def_id: LocalDefId) -> ty::EarlyBinder, def_id: DefId, -) -> Result>, CyclePlaceholder> { +) -> Result>, CyclePlaceholder> { if let Some(def_id) = def_id.as_local() { use rustc_hir::*; diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index 1bec8c496ad1..2b2f07001d2f 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -8,7 +8,7 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP}; -use crate::errors::{TaitForwardCompat, TypeOf, UnconstrainedOpaqueType}; +use crate::errors::{TaitForwardCompat, TaitForwardCompat2, TypeOf, UnconstrainedOpaqueType}; pub fn test_opaque_hidden_types(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { let mut res = Ok(()); @@ -229,13 +229,14 @@ impl TaitConstraintLocator<'_> { return; } + let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id); + let mut constrained = false; for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types { if opaque_type_key.def_id != self.def_id { continue; } constrained = true; - let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id); if !opaque_types_defined_by.contains(&self.def_id) { self.tcx.dcx().emit_err(TaitForwardCompat { @@ -259,6 +260,16 @@ impl TaitConstraintLocator<'_> { if !constrained { debug!("no constraints in typeck results"); + if opaque_types_defined_by.contains(&self.def_id) { + self.tcx.dcx().emit_err(TaitForwardCompat2 { + span: self + .tcx + .def_ident_span(item_def_id) + .unwrap_or_else(|| self.tcx.def_span(item_def_id)), + opaque_type_span: self.tcx.def_span(self.def_id), + opaque_type: self.tcx.def_path_str(self.def_id), + }); + } return; }; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 1c99713b3ae1..cff8d5a5ea50 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -2,7 +2,7 @@ use crate::fluent_generated as fluent; use rustc_errors::{ - codes::*, Applicability, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level, MultiSpan, + codes::*, Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; @@ -55,6 +55,18 @@ pub struct AssocKindMismatchWrapInBracesSugg { pub hi: Span, } +#[derive(Diagnostic)] +#[diag(hir_analysis_assoc_item_is_private, code = E0624)] +pub struct AssocItemIsPrivate { + #[primary_span] + #[label] + pub span: Span, + pub kind: &'static str, + pub name: Ident, + #[label(hir_analysis_defined_here_label)] + pub defined_here_label: Span, +} + #[derive(Diagnostic)] #[diag(hir_analysis_assoc_item_not_found, code = E0220)] pub struct AssocItemNotFound<'a> { @@ -290,8 +302,8 @@ pub struct AmbiguousLifetimeBound { } #[derive(Diagnostic)] -#[diag(hir_analysis_assoc_type_binding_not_allowed, code = E0229)] -pub struct AssocTypeBindingNotAllowed { +#[diag(hir_analysis_assoc_item_constraints_not_allowed_here, code = E0229)] +pub struct AssocItemConstraintsNotAllowedHere { #[primary_span] #[label] pub span: Span, @@ -390,6 +402,17 @@ pub struct TaitForwardCompat { pub item_span: Span, } +#[derive(Diagnostic)] +#[diag(hir_analysis_tait_forward_compat2)] +#[note] +pub struct TaitForwardCompat2 { + #[primary_span] + pub span: Span, + #[note(hir_analysis_opaque)] + pub opaque_type_span: Span, + pub opaque_type: String, +} + pub struct MissingTypeParams { pub span: Span, pub def_span: Span, @@ -401,7 +424,7 @@ pub struct MissingTypeParams { // Manual implementation of `Diagnostic` to be able to call `span_to_snippet`. impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for MissingTypeParams { #[track_caller] - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let mut err = Diag::new(dcx, level, fluent::hir_analysis_missing_type_params); err.span(self.span); err.code(E0393); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index b0ae73fcc4be..c7699b0b310f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -18,6 +18,8 @@ use crate::bounds::Bounds; use crate::errors; use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter}; +use super::RegionInferReason; + impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Add a `Sized` bound to the `bounds` if appropriate. /// @@ -166,7 +168,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); } hir::GenericBound::Outlives(lifetime) => { - let region = self.lower_lifetime(lifetime, None); + let region = self.lower_lifetime(lifetime, RegionInferReason::OutlivesBound); bounds.push_region_bound( self.tcx(), ty::Binder::bind_with_vars( @@ -176,6 +178,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { lifetime.ident.span, ); } + hir::GenericBound::Use(..) => { + // We don't actually lower `use` into the type layer. + } } } } @@ -230,32 +235,34 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bounds } - /// Lower an associated item binding from HIR into `bounds`. + /// Lower an associated item constraint from the HIR into `bounds`. /// /// ### A Note on Binders /// /// Given something like `T: for<'a> Iterator`, /// the `trait_ref` here will be `for<'a> T: Iterator`. - /// The `binding` data however is from *inside* the binder + /// The `constraint` data however is from *inside* the binder /// (e.g., `&'a u32`) and hence may reference bound regions. - #[instrument(level = "debug", skip(self, bounds, dup_bindings, path_span))] - pub(super) fn lower_assoc_item_binding( + #[instrument(level = "debug", skip(self, bounds, duplicates, path_span))] + pub(super) fn lower_assoc_item_constraint( &self, hir_ref_id: hir::HirId, trait_ref: ty::PolyTraitRef<'tcx>, - binding: &hir::TypeBinding<'tcx>, + constraint: &hir::AssocItemConstraint<'tcx>, bounds: &mut Bounds<'tcx>, - dup_bindings: &mut FxIndexMap, + duplicates: &mut FxIndexMap, path_span: Span, only_self_bounds: OnlySelfBounds, ) -> Result<(), ErrorGuaranteed> { let tcx = self.tcx(); - let assoc_kind = if binding.gen_args.parenthesized + let assoc_kind = if constraint.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation { ty::AssocKind::Fn - } else if let hir::TypeBindingKind::Equality { term: hir::Term::Const(_) } = binding.kind { + } else if let hir::AssocItemConstraintKind::Equality { term: hir::Term::Const(_) } = + constraint.kind + { ty::AssocKind::Const } else { ty::AssocKind::Type @@ -272,7 +279,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let candidate = if self.probe_trait_that_defines_assoc_item( trait_ref.def_id(), assoc_kind, - binding.ident, + constraint.ident, ) { // Simple case: The assoc item is defined in the current trait. trait_ref @@ -284,48 +291,33 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { trait_ref.skip_binder().print_only_trait_name(), None, assoc_kind, - binding.ident, + constraint.ident, path_span, - Some(binding), + Some(constraint), )? }; - let (assoc_ident, def_scope) = - tcx.adjust_ident_and_get_scope(binding.ident, candidate.def_id(), hir_ref_id); + let assoc_item = self + .probe_assoc_item( + constraint.ident, + assoc_kind, + hir_ref_id, + constraint.span, + candidate.def_id(), + ) + .expect("failed to find associated item"); - // We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()` - // instead of calling `filter_by_name_and_kind` which would needlessly normalize the - // `assoc_ident` again and again. - let assoc_item = tcx - .associated_items(candidate.def_id()) - .filter_by_name_unhygienic(assoc_ident.name) - .find(|i| i.kind == assoc_kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident) - .expect("missing associated item"); - - if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) { - let reported = tcx - .dcx() - .struct_span_err( - binding.span, - format!("{} `{}` is private", assoc_item.kind, binding.ident), - ) - .with_span_label(binding.span, format!("private {}", assoc_item.kind)) - .emit(); - self.set_tainted_by_errors(reported); - } - tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None); - - dup_bindings + duplicates .entry(assoc_item.def_id) .and_modify(|prev_span| { tcx.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified { - span: binding.span, + span: constraint.span, prev_span: *prev_span, - item_name: binding.ident, + item_name: constraint.ident, def_path: tcx.def_path_str(assoc_item.container_id(tcx)), }); }) - .or_insert(binding.span); + .or_insert(constraint.span); let projection_term = if let ty::AssocKind::Fn = assoc_kind { let mut emitted_bad_param_err = None; @@ -364,11 +356,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }, ) }); - let ty = tcx - .type_of(param.def_id) - .no_bound_vars() - .expect("ct params cannot have early bound vars"); - ty::Const::new_error(tcx, guar, ty).into() + ty::Const::new_error(tcx, guar).into() } }; num_bound_vars += 1; @@ -384,7 +372,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { alias_ty.into() } else { return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit { - span: binding.span, + span: constraint.span, ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output), fn_span: tcx.hir().span_if_local(assoc_item.def_id), note: (), @@ -398,19 +386,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let shifted_output = tcx.shift_bound_var_indices(num_bound_vars, output); let instantiation_output = ty::EarlyBinder::bind(shifted_output).instantiate(tcx, args); - let bound_vars = tcx.late_bound_vars(binding.hir_id); + let bound_vars = tcx.late_bound_vars(constraint.hir_id); ty::Binder::bind_with_vars(instantiation_output, bound_vars) } else { // Create the generic arguments for the associated type or constant by joining the // parent arguments (the arguments of the trait) and the own arguments (the ones of // the associated item itself) and construct an alias type using them. - let alias_ty = candidate.map_bound(|trait_ref| { - let ident = Ident::new(assoc_item.name, binding.ident.span); + let alias_term = candidate.map_bound(|trait_ref| { let item_segment = hir::PathSegment { - ident, - hir_id: binding.hir_id, + ident: constraint.ident, + hir_id: constraint.hir_id, res: Res::Err, - args: Some(binding.gen_args), + args: Some(constraint.gen_args), infer_args: false, }; @@ -426,26 +413,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }); // Provide the resolved type of the associated constant to `type_of(AnonConst)`. - if let hir::TypeBindingKind::Equality { term: hir::Term::Const(anon_const) } = - binding.kind - { - let ty = alias_ty.map_bound(|ty| tcx.type_of(ty.def_id).instantiate(tcx, ty.args)); - let ty = check_assoc_const_binding_type(tcx, assoc_ident, ty, binding.hir_id); + if let Some(anon_const) = constraint.ct() { + let ty = alias_term + .map_bound(|alias| tcx.type_of(alias.def_id).instantiate(tcx, alias.args)); + let ty = + check_assoc_const_binding_type(tcx, constraint.ident, ty, constraint.hir_id); tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty)); } - alias_ty + alias_term }; - match binding.kind { - hir::TypeBindingKind::Equality { .. } if let ty::AssocKind::Fn = assoc_kind => { + match constraint.kind { + hir::AssocItemConstraintKind::Equality { .. } if let ty::AssocKind::Fn = assoc_kind => { return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationEqualityBound { - span: binding.span, + span: constraint.span, })); } // Lower an equality constraint like `Item = u32` as found in HIR bound `T: Iterator` // to a projection predicate: `::Item = u32`. - hir::TypeBindingKind::Equality { term } => { + hir::AssocItemConstraintKind::Equality { term } => { let term = match term { hir::Term::Ty(ty) => self.lower_ty(ty).into(), hir::Term::Const(ct) => ty::Const::from_anon_const(tcx, ct.def_id).into(), @@ -469,18 +456,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // struct S1 Fn(&i32, &i32) -> &'a i32>(F); // ---- ---- ^^^^^^^ // NOTE(associated_const_equality): This error should be impossible to trigger - // with associated const equality bounds. + // with associated const equality constraints. self.validate_late_bound_regions( late_bound_in_projection_ty, late_bound_in_term, |br_name| { struct_span_code_err!( tcx.dcx(), - binding.span, + constraint.span, E0582, "binding for associated type `{}` references {}, \ - which does not appear in the trait input types", - binding.ident, + which does not appear in the trait input types", + constraint.ident, br_name ) }, @@ -492,12 +479,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { projection_term, term, }), - binding.span, + constraint.span, ); } // Lower a constraint like `Item: Debug` as found in HIR bound `T: Iterator` // to a bound involving a projection: `::Item: Debug`. - hir::TypeBindingKind::Constraint { bounds: hir_bounds } => { + hir::AssocItemConstraintKind::Bound { bounds: hir_bounds } => { // NOTE: If `only_self_bounds` is true, do NOT expand this associated type bound into // a trait predicate, since we only want to add predicates for the `Self` type. if !only_self_bounds.0 { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index f19492e00b47..2d240699105d 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -1,5 +1,5 @@ use crate::errors::{ - self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams, + self, AssocItemConstraintsNotAllowedHere, ManualImplementation, MissingTypeParams, ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits, }; use crate::fluent_generated as fluent; @@ -15,10 +15,9 @@ use rustc_errors::{ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_infer::traits::FulfillmentError; use rustc_middle::bug; use rustc_middle::query::Key; -use rustc_middle::ty::print::PrintTraitRefExt as _; +use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _}; use rustc_middle::ty::GenericParamDefKind; use rustc_middle::ty::{self, suggest_constraining_type_param}; use rustc_middle::ty::{AdtDef, Ty, TyCtxt, TypeVisitableExt}; @@ -28,6 +27,7 @@ use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::BytePos; use rustc_span::{Span, Symbol, DUMMY_SP}; +use rustc_trait_selection::traits::FulfillmentError; use rustc_trait_selection::traits::{ object_safety_violations_for_assoc_item, TraitAliasExpansionInfo, }; @@ -121,7 +121,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assoc_kind: ty::AssocKind, assoc_name: Ident, span: Span, - binding: Option<&hir::TypeBinding<'tcx>>, + constraint: Option<&hir::AssocItemConstraint<'tcx>>, ) -> ErrorGuaranteed where I: Iterator>, @@ -135,7 +135,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .find(|item| tcx.hygienic_eq(assoc_name, item.ident(tcx), r.def_id())) }) { return self.complain_about_assoc_kind_mismatch( - assoc_item, assoc_kind, assoc_name, span, binding, + assoc_item, assoc_kind, assoc_name, span, constraint, ); } @@ -300,18 +300,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assoc_kind: ty::AssocKind, ident: Ident, span: Span, - binding: Option<&hir::TypeBinding<'tcx>>, + constraint: Option<&hir::AssocItemConstraint<'tcx>>, ) -> ErrorGuaranteed { let tcx = self.tcx(); let bound_on_assoc_const_label = if let ty::AssocKind::Const = assoc_item.kind - && let Some(binding) = binding - && let hir::TypeBindingKind::Constraint { .. } = binding.kind + && let Some(constraint) = constraint + && let hir::AssocItemConstraintKind::Bound { .. } = constraint.kind { - let lo = if binding.gen_args.span_ext.is_dummy() { + let lo = if constraint.gen_args.span_ext.is_dummy() { ident.span } else { - binding.gen_args.span_ext + constraint.gen_args.span_ext }; Some(lo.between(span.shrink_to_hi())) } else { @@ -319,8 +319,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; // FIXME(associated_const_equality): This has quite a few false positives and negatives. - let wrap_in_braces_sugg = if let Some(binding) = binding - && let hir::TypeBindingKind::Equality { term: hir::Term::Ty(hir_ty) } = binding.kind + let wrap_in_braces_sugg = if let Some(constraint) = constraint + && let Some(hir_ty) = constraint.ty() && let ty = self.lower_ty(hir_ty) && (ty.is_enum() || ty.references_error()) && tcx.features().associated_const_equality @@ -333,10 +333,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { None }; - // For equality bounds, we want to blame the term (RHS) instead of the item (LHS) since + // For equality constraints, we want to blame the term (RHS) instead of the item (LHS) since // one can argue that that's more “intuitive” to the user. - let (span, expected_because_label, expected, got) = if let Some(binding) = binding - && let hir::TypeBindingKind::Equality { term } = binding.kind + let (span, expected_because_label, expected, got) = if let Some(constraint) = constraint + && let hir::AssocItemConstraintKind::Equality { term } = constraint.kind { let span = match term { hir::Term::Ty(ty) => ty.span, @@ -702,7 +702,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub(crate) fn complain_about_missing_assoc_tys( &self, associated_types: FxIndexMap>, - potential_assoc_types: Vec, + potential_assoc_types: Vec, trait_bounds: &[hir::PolyTraitRef<'_>], ) { if associated_types.values().all(|v| v.is_empty()) { @@ -791,8 +791,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let path = poly_trait_ref.trait_ref.path.segments.last()?; let args = path.args?; - Some(args.bindings.iter().filter_map(|binding| { - let ident = binding.ident; + Some(args.constraints.iter().filter_map(|constraint| { + let ident = constraint.ident; let trait_def = path.res.def_id(); let assoc_item = tcx.associated_items(trait_def).find_by_name_and_kind( tcx, @@ -1192,14 +1192,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } -/// Emits an error regarding forbidden type binding associations -pub fn prohibit_assoc_item_binding( +/// Emit an error for the given associated item constraint. +pub fn prohibit_assoc_item_constraint( tcx: TyCtxt<'_>, - binding: &hir::TypeBinding<'_>, + constraint: &hir::AssocItemConstraint<'_>, segment: Option<(DefId, &hir::PathSegment<'_>, Span)>, ) -> ErrorGuaranteed { - let mut err = tcx.dcx().create_err(AssocTypeBindingNotAllowed { - span: binding.span, + let mut err = tcx.dcx().create_err(AssocItemConstraintsNotAllowedHere { + span: constraint.span, fn_trait_expansion: if let Some((_, segment, span)) = segment && segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar { @@ -1217,13 +1217,11 @@ pub fn prohibit_assoc_item_binding( // otherwise suggest the removal of the binding. if let Some((def_id, segment, _)) = segment && segment.args().parenthesized == hir::GenericArgsParentheses::No - && let hir::TypeBindingKind::Equality { term } = binding.kind { // Suggests removal of the offending binding let suggest_removal = |e: &mut Diag<'_>| { - let bindings = segment.args().bindings; + let constraints = segment.args().constraints; let args = segment.args().args; - let binding_span = binding.span; // Compute the span to remove based on the position // of the binding. We do that as follows: @@ -1236,26 +1234,21 @@ pub fn prohibit_assoc_item_binding( // the start of the next span or will simply be the // span encomassing everything within the generics brackets - let Some(binding_index) = bindings.iter().position(|b| b.hir_id == binding.hir_id) - else { + let Some(index) = constraints.iter().position(|b| b.hir_id == constraint.hir_id) else { bug!("a type binding exists but its HIR ID not found in generics"); }; - let preceding_span = if binding_index > 0 { - Some(bindings[binding_index - 1].span) + let preceding_span = if index > 0 { + Some(constraints[index - 1].span) } else { args.last().map(|a| a.span()) }; - let next_span = if binding_index < bindings.len() - 1 { - Some(bindings[binding_index + 1].span) - } else { - None - }; + let next_span = constraints.get(index + 1).map(|constraint| constraint.span); let removal_span = match (preceding_span, next_span) { - (Some(prec), _) => binding_span.with_lo(prec.hi()), - (None, Some(next)) => binding_span.with_hi(next.lo()), + (Some(prec), _) => constraint.span.with_lo(prec.hi()), + (None, Some(next)) => constraint.span.with_hi(next.lo()), (None, None) => { let Some(generics_span) = segment.args().span_ext() else { bug!("a type binding exists but generic span is empty"); @@ -1269,7 +1262,7 @@ pub fn prohibit_assoc_item_binding( if let Ok(suggestion) = tcx.sess.source_map().span_to_snippet(removal_span) { e.span_suggestion_verbose( removal_span, - "consider removing this type binding", + format!("consider removing this associated item {}", constraint.kind.descr()), suggestion, Applicability::MaybeIncorrect, ); @@ -1281,7 +1274,7 @@ pub fn prohibit_assoc_item_binding( let suggest_direct_use = |e: &mut Diag<'_>, sp: Span| { if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(sp) { e.span_suggestion_verbose( - binding.span, + constraint.span, format!("to use `{snippet}` as a generic argument specify it directly"), snippet, Applicability::MaybeIncorrect, @@ -1289,22 +1282,76 @@ pub fn prohibit_assoc_item_binding( } }; - // Check if the type has a generic param with the - // same name as the assoc type name in type binding + // Check if the type has a generic param with the same name + // as the assoc type name in the associated item binding. let generics = tcx.generics_of(def_id); - let matching_param = - generics.own_params.iter().find(|p| p.name.as_str() == binding.ident.as_str()); + let matching_param = generics.own_params.iter().find(|p| p.name == constraint.ident.name); // Now emit the appropriate suggestion if let Some(matching_param) = matching_param { - match (&matching_param.kind, term) { - (GenericParamDefKind::Type { .. }, hir::Term::Ty(ty)) => { - suggest_direct_use(&mut err, ty.span); - } - (GenericParamDefKind::Const { .. }, hir::Term::Const(c)) => { + match (constraint.kind, &matching_param.kind) { + ( + hir::AssocItemConstraintKind::Equality { term: hir::Term::Ty(ty) }, + GenericParamDefKind::Type { .. }, + ) => suggest_direct_use(&mut err, ty.span), + ( + hir::AssocItemConstraintKind::Equality { term: hir::Term::Const(c) }, + GenericParamDefKind::Const { .. }, + ) => { let span = tcx.hir().span(c.hir_id); suggest_direct_use(&mut err, span); } + (hir::AssocItemConstraintKind::Bound { bounds }, _) => { + // Suggest `impl Trait for Foo` when finding + // `impl Trait for Foo` + + // Get the parent impl block based on the binding we have + // and the trait DefId + let impl_block = tcx + .hir() + .parent_iter(constraint.hir_id) + .find_map(|(_, node)| node.impl_block_of_trait(def_id)); + + let type_with_constraints = + tcx.sess.source_map().span_to_snippet(constraint.span); + + if let Some(impl_block) = impl_block + && let Ok(type_with_constraints) = type_with_constraints + { + // Filter out the lifetime parameters because + // they should be declared before the type parameter + let lifetimes: String = bounds + .iter() + .filter_map(|bound| { + if let hir::GenericBound::Outlives(lifetime) = bound { + Some(format!("{lifetime}, ")) + } else { + None + } + }) + .collect(); + // Figure out a span and suggestion string based on + // whether there are any existing parameters + let param_decl = if let Some(param_span) = + impl_block.generics.span_for_param_suggestion() + { + (param_span, format!(", {lifetimes}{type_with_constraints}")) + } else { + ( + impl_block.generics.span.shrink_to_lo(), + format!("<{lifetimes}{type_with_constraints}>"), + ) + }; + let suggestions = + vec![param_decl, (constraint.span, format!("{}", matching_param.name))]; + + err.multipart_suggestion_verbose( + format!("declare the type parameter right after the `impl` keyword"), + suggestions, + Applicability::MaybeIncorrect, + ); + } + } _ => suggest_removal(&mut err), } } else { @@ -1322,8 +1369,7 @@ pub(crate) fn fn_trait_to_string( ) -> String { let args = trait_segment .args - .as_ref() - .and_then(|args| args.args.get(0)) + .and_then(|args| args.args.first()) .and_then(|arg| match arg { hir::GenericArg::Type(ty) => match ty.kind { hir::TyKind::Tup(t) => t @@ -1334,7 +1380,7 @@ pub(crate) fn fn_trait_to_string( _ => tcx.sess.source_map().span_to_snippet(ty.span), } .map(|s| { - // `s.empty()` checks to see if the type is the unit tuple, if so we don't want a comma + // `is_empty()` checks to see if the type is the unit tuple, if so we don't want a comma if parenthesized || s.is_empty() { format!("({s})") } else { format!("({s},)") } }) .ok(), @@ -1344,20 +1390,17 @@ pub(crate) fn fn_trait_to_string( let ret = trait_segment .args() - .bindings + .constraints .iter() - .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { - (true, hir::TypeBindingKind::Equality { term }) => { - let span = match term { - hir::Term::Ty(ty) => ty.span, - hir::Term::Const(c) => tcx.hir().span(c.hir_id), - }; - - (span != tcx.hir().span(trait_segment.hir_id)) - .then_some(tcx.sess.source_map().span_to_snippet(span).ok()) - .flatten() + .find_map(|c| { + if c.ident.name == sym::Output + && let Some(ty) = c.ty() + && ty.span != tcx.hir().span(trait_segment.hir_id) + { + tcx.sess.source_map().span_to_snippet(ty.span).ok() + } else { + None } - _ => None, }) .unwrap_or_else(|| "()".to_string()); @@ -1382,7 +1425,7 @@ pub enum GenericsArgsErrExtend<'tcx> { span: Span, }, SelfTyParam(Span), - TyParam(DefId), + Param(DefId), DefVariant, None, } @@ -1409,7 +1452,7 @@ fn generics_args_err_extend<'a>( // it was done based on the end of assoc segment but that sometimes // led to impossible spans and caused issues like #116473 let args_span = args.span_ext.with_lo(args.span_ext.lo() - BytePos(2)); - if tcx.generics_of(adt_def.did()).count() == 0 { + if tcx.generics_of(adt_def.did()).is_empty() { // FIXME(estebank): we could also verify that the arguments being // work for the `enum`, instead of just looking if it takes *any*. err.span_suggestion_verbose( @@ -1498,11 +1541,11 @@ fn generics_args_err_extend<'a>( GenericsArgsErrExtend::DefVariant => { err.note("enum variants can't have type parameters"); } - GenericsArgsErrExtend::TyParam(def_id) => { - if let Some(span) = tcx.def_ident_span(def_id) { - let name = tcx.item_name(def_id); - err.span_note(span, format!("type parameter `{name}` defined here")); - } + GenericsArgsErrExtend::Param(def_id) => { + let span = tcx.def_ident_span(def_id).unwrap(); + let kind = tcx.def_descr(def_id); + let name = tcx.item_name(def_id); + err.span_note(span, format!("{kind} `{name}` defined here")); } GenericsArgsErrExtend::SelfTyParam(span) => { err.span_suggestion_verbose( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index 749f78e79201..3f888c4e2722 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -1,6 +1,6 @@ use super::IsMethodCall; use crate::hir_ty_lowering::{ - errors::prohibit_assoc_item_binding, ExplicitLateBound, GenericArgCountMismatch, + errors::prohibit_assoc_item_constraint, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, GenericArgsLowerer, }; use crate::structured_errors::{GenericArgsInfo, StructuredDiag, WrongNumberOfGenericArgs}; @@ -214,10 +214,11 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( if let Some(¶m) = params.peek() { if param.index == 0 { if let GenericParamDefKind::Type { .. } = param.kind { + assert_eq!(&args[..], &[]); args.push( self_ty .map(|ty| ty.into()) - .unwrap_or_else(|| ctx.inferred_kind(None, param, true)), + .unwrap_or_else(|| ctx.inferred_kind(&args, param, true)), ); params.next(); } @@ -267,7 +268,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( // Since this is a const impl, we need to insert a host arg at the end of // `PartialEq`'s generics, but this errors since `Rhs` isn't specified. // To work around this, we infer all arguments until we reach the host param. - args.push(ctx.inferred_kind(Some(&args), param, infer_args)); + args.push(ctx.inferred_kind(&args, param, infer_args)); params.next(); } (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) @@ -281,7 +282,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( GenericParamDefKind::Const { .. }, _, ) => { - args.push(ctx.provided_kind(param, arg)); + args.push(ctx.provided_kind(&args, param, arg)); args_iter.next(); params.next(); } @@ -292,7 +293,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( ) => { // We expected a lifetime argument, but got a type or const // argument. That means we're inferring the lifetimes. - args.push(ctx.inferred_kind(None, param, infer_args)); + args.push(ctx.inferred_kind(&args, param, infer_args)); force_infer_lt = Some((arg, param)); params.next(); } @@ -388,7 +389,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( (None, Some(¶m)) => { // If there are fewer arguments than parameters, it means // we're inferring the remaining arguments. - args.push(ctx.inferred_kind(Some(&args), param, infer_args)); + args.push(ctx.inferred_kind(&args, param, infer_args)); params.next(); } @@ -452,9 +453,9 @@ pub(crate) fn check_generic_arg_count( (gen_pos != GenericArgPosition::Type || seg.infer_args) && !gen_args.has_lifetime_params(); if gen_pos != GenericArgPosition::Type - && let Some(b) = gen_args.bindings.first() + && let Some(c) = gen_args.constraints.first() { - prohibit_assoc_item_binding(tcx, b, None); + prohibit_assoc_item_constraint(tcx, c, None); } let explicit_late_bound = @@ -474,16 +475,9 @@ pub(crate) fn check_generic_arg_count( return Ok(()); } - if provided_args > max_expected_args { - invalid_args.extend( - gen_args.args[max_expected_args..provided_args].iter().map(|arg| arg.span()), - ); - }; + invalid_args.extend(min_expected_args..provided_args); let gen_args_info = if provided_args > min_expected_args { - invalid_args.extend( - gen_args.args[min_expected_args..provided_args].iter().map(|arg| arg.span()), - ); let num_redundant_args = provided_args - min_expected_args; GenericArgsInfo::ExcessLifetimes { num_redundant_args } } else { @@ -537,12 +531,9 @@ pub(crate) fn check_generic_arg_count( let num_default_params = expected_max - expected_min; + let mut all_params_are_binded = false; let gen_args_info = if provided > expected_max { - invalid_args.extend( - gen_args.args[args_offset + expected_max..args_offset + provided] - .iter() - .map(|arg| arg.span()), - ); + invalid_args.extend((expected_max..provided).map(|i| i + args_offset)); let num_redundant_args = provided - expected_max; // Provide extra note if synthetic arguments like `impl Trait` are specified. @@ -557,6 +548,20 @@ pub(crate) fn check_generic_arg_count( } else { let num_missing_args = expected_max - provided; + let constraint_names: Vec<_> = + gen_args.constraints.iter().map(|b| b.ident.name).collect(); + let param_names: Vec<_> = gen_params + .own_params + .iter() + .filter(|param| !has_self || param.index != 0) // Assumes `Self` will always be the first parameter + .map(|param| param.name) + .collect(); + if constraint_names == param_names { + // We set this to true and delay emitting `WrongNumberOfGenericArgs` + // to provide a succinct error for cases like issue #113073 + all_params_are_binded = true; + }; + GenericArgsInfo::MissingTypesOrConsts { num_missing_args, num_default_params, @@ -566,17 +571,19 @@ pub(crate) fn check_generic_arg_count( debug!(?gen_args_info); - let reported = WrongNumberOfGenericArgs::new( - tcx, - gen_args_info, - seg, - gen_params, - params_offset, - gen_args, - def_id, - ) - .diagnostic() - .emit_unless(gen_args.has_err()); + let reported = gen_args.has_err().unwrap_or_else(|| { + WrongNumberOfGenericArgs::new( + tcx, + gen_args_info, + seg, + gen_params, + params_offset, + gen_args, + def_id, + ) + .diagnostic() + .emit_unless(all_params_are_binded) + }); Err(reported) }; @@ -608,7 +615,7 @@ pub(crate) fn check_generic_arg_count( explicit_late_bound, correct: lifetimes_correct .and(args_correct) - .map_err(|reported| GenericArgCountMismatch { reported: Some(reported), invalid_args }), + .map_err(|reported| GenericArgCountMismatch { reported, invalid_args }), } } @@ -646,8 +653,9 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes( LATE_BOUND_LIFETIME_ARGUMENTS, args.args[0].hir_id(), multispan, - msg, - |_| {}, + |lint| { + lint.primary_message(msg); + }, ); } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index d9d36f5299b5..240a749de96a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -72,8 +72,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.maybe_suggest_assoc_ty_bound(self_ty, &mut diag); diag.stash(self_ty.span, StashKey::TraitMissingMethod); } else { - let msg = "trait objects without an explicit `dyn` are deprecated"; - tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, msg, |lint| { + tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, |lint| { + lint.primary_message("trait objects without an explicit `dyn` are deprecated"); if self_ty.span.can_be_used_for_suggestions() { lint.multipart_suggestion_verbose( "if this is an object-safe trait, use `dyn`", @@ -182,7 +182,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // For recursive traits, don't downgrade the error. (#119652) is_downgradable = false; } - tcx.check_is_object_safe(id) + tcx.is_object_safe(id) } _ => false, }) @@ -268,8 +268,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn maybe_suggest_assoc_ty_bound(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) { let mut parents = self.tcx().hir().parent_iter(self_ty.hir_id); - if let Some((_, hir::Node::TypeBinding(binding))) = parents.next() - && let hir::TypeBindingKind::Equality { term: hir::Term::Ty(obj_ty) } = binding.kind + if let Some((_, hir::Node::AssocItemConstraint(constraint))) = parents.next() + && let Some(obj_ty) = constraint.ty() { if let Some((_, hir::Node::TraitRef(..))) = parents.next() && let Some((_, hir::Node::Ty(ty))) = parents.next() @@ -279,10 +279,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { return; } - let lo = if binding.gen_args.span_ext.is_dummy() { - binding.ident.span + let lo = if constraint.gen_args.span_ext.is_dummy() { + constraint.ident.span } else { - binding.gen_args.span_ext + constraint.gen_args.span_ext }; let hi = obj_ty.span; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 39016d15236f..5911d5bb4e1e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -20,9 +20,8 @@ mod lint; mod object_safety; use crate::bounds::Bounds; -use crate::collect::HirPlaceholderCollector; use crate::errors::{AmbiguousLifetimeBound, WildPatTy}; -use crate::hir_ty_lowering::errors::{prohibit_assoc_item_binding, GenericsArgsErrExtend}; +use crate::hir_ty_lowering::errors::{prohibit_assoc_item_constraint, GenericsArgsErrExtend}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; use crate::require_c_abi_if_c_variadic; @@ -34,12 +33,12 @@ use rustc_errors::{ use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{walk_generics, Visitor as _}; use rustc_hir::{GenericArg, GenericArgs, HirId}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::ty::print::PrintPolyTraitRefExt as _; use rustc_middle::ty::{ self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, TyCtxt, TypeVisitableExt, @@ -81,6 +80,20 @@ pub enum PredicateFilter { SelfAndAssociatedTypeBounds, } +#[derive(Debug)] +pub enum RegionInferReason<'a> { + /// Lifetime on a trait object behind a reference. + /// This allows inferring information from the reference. + BorrowedObjectLifetimeDefault, + /// A trait object's lifetime. + ObjectLifetimeDefault, + /// Generic lifetime parameter + Param(&'a ty::GenericParamDef), + RegionPredicate, + Reference, + OutlivesBound, +} + /// A context which can lower type-system entities from the [HIR][hir] to /// the [`rustc_middle::ty`] representation. /// @@ -88,26 +101,17 @@ pub enum PredicateFilter { pub trait HirTyLowerer<'tcx> { fn tcx(&self) -> TyCtxt<'tcx>; - /// Returns the [`DefId`] of the overarching item whose constituents get lowered. - fn item_def_id(&self) -> DefId; - - /// Returns `true` if the current context allows the use of inference variables. - fn allow_infer(&self) -> bool; + /// Returns the [`LocalDefId`] of the overarching item whose constituents get lowered. + fn item_def_id(&self) -> LocalDefId; /// Returns the region to use when a lifetime is omitted (and not elided). - fn re_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) - -> Option>; + fn re_infer(&self, span: Span, reason: RegionInferReason<'_>) -> ty::Region<'tcx>; /// Returns the type to use when a type is omitted. fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx>; /// Returns the const to use when a const is omitted. - fn ct_infer( - &self, - ty: Ty<'tcx>, - param: Option<&ty::GenericParamDef>, - span: Span, - ) -> Const<'tcx>; + fn ct_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx>; /// Probe bounds in scope where the bounded type coincides with the given type parameter. /// @@ -150,6 +154,14 @@ pub trait HirTyLowerer<'tcx> { poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx>; + fn lower_fn_sig( + &self, + decl: &hir::FnDecl<'tcx>, + generics: Option<&hir::Generics<'_>>, + hir_id: HirId, + hir_ty: Option<&hir::Ty<'_>>, + ) -> (Vec>, Ty<'tcx>); + /// Returns `AdtDef` if `ty` is an ADT. /// /// Note that `ty` might be a alias type that needs normalization. @@ -214,12 +226,11 @@ pub(crate) enum GenericArgPosition { /// A marker denoting that the generic arguments that were /// provided did not match the respective generic parameters. -#[derive(Clone, Default, Debug)] +#[derive(Clone, Debug)] pub struct GenericArgCountMismatch { - /// Indicates whether a fatal error was reported (`Some`), or just a lint (`None`). - pub reported: Option, - /// A list of spans of arguments provided that were not valid. - pub invalid_args: Vec, + pub reported: ErrorGuaranteed, + /// A list of indices of arguments provided that were not valid. + pub invalid_args: Vec, } /// Decorates the result of a generic argument count mismatch @@ -239,13 +250,14 @@ pub trait GenericArgsLowerer<'a, 'tcx> { fn provided_kind( &mut self, + preceding_args: &[ty::GenericArg<'tcx>], param: &ty::GenericParamDef, arg: &GenericArg<'tcx>, ) -> ty::GenericArg<'tcx>; fn inferred_kind( &mut self, - args: Option<&[ty::GenericArg<'tcx>]>, + preceding_args: &[ty::GenericArg<'tcx>], param: &ty::GenericParamDef, infer_args: bool, ) -> ty::GenericArg<'tcx>; @@ -257,7 +269,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub fn lower_lifetime( &self, lifetime: &hir::Lifetime, - def: Option<&ty::GenericParamDef>, + reason: RegionInferReason<'_>, ) -> ty::Region<'tcx> { let tcx = self.tcx(); let lifetime_name = |def_id| tcx.hir().name(tcx.local_def_id_to_hir_id(def_id)); @@ -279,7 +291,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local()); let generics = tcx.generics_of(item_def_id); let index = generics.param_def_id_to_index[&def_id]; - ty::Region::new_early_param(tcx, ty::EarlyParamRegion { def_id, index, name }) + ty::Region::new_early_param(tcx, ty::EarlyParamRegion { index, name }) } Some(rbv::ResolvedArg::Free(scope, id)) => { @@ -291,21 +303,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Some(rbv::ResolvedArg::Error(guar)) => ty::Region::new_error(tcx, guar), - None => { - self.re_infer(def, lifetime.ident.span).unwrap_or_else(|| { - debug!(?lifetime, "unelided lifetime in signature"); - - // This indicates an illegal lifetime - // elision. `resolve_lifetime` should have - // reported an error in this case -- but if - // not, let's error out. - ty::Region::new_error_with_message( - tcx, - lifetime.ident.span, - "unelided lifetime in signature", - ) - }) - } + None => self.re_infer(lifetime.ident.span, reason), } } @@ -323,8 +321,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { None, ty::BoundConstness::NotConst, ); - if let Some(b) = item_segment.args().bindings.first() { - prohibit_assoc_item_binding(self.tcx(), b, Some((def_id, item_segment, span))); + if let Some(c) = item_segment.args().constraints.first() { + prohibit_assoc_item_constraint(self.tcx(), c, Some((def_id, item_segment, span))); } args } @@ -334,7 +332,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// If this is a trait reference, you also need to pass the self type `self_ty`. /// The lowering process may involve applying defaulted type parameters. /// - /// Associated item bindings are not handled here! + /// Associated item constraints are not handled here! They are either lowered via + /// `lower_assoc_item_constraint` or rejected via `prohibit_assoc_item_constraint`. /// /// ### Example /// @@ -348,7 +347,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// which will have been resolved to a `def_id` /// 3. The `generic_args` contains info on the `<...>` contents. The `usize` type /// parameters are returned in the `GenericArgsRef` - /// 4. Associated type bindings like `Output = u32` are contained in `generic_args.bindings`. + /// 4. Associated item constraints like `Output = u32` are contained in `generic_args.constraints`. /// /// Note that the type listing given here is *exactly* what the user provided. /// @@ -402,17 +401,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty.is_some(), ); - if let Err(err) = &arg_count.correct - && let Some(reported) = err.reported - { - self.set_tainted_by_errors(reported); + if let Err(err) = &arg_count.correct { + self.set_tainted_by_errors(err.reported); } // Skip processing if type has no generic parameters. // Traits always have `Self` as a generic parameter, which means they will not return early - // here and so associated type bindings will be handled regardless of whether there are any - // non-`Self` generic parameters. - if generics.own_params.is_empty() { + // here and so associated item constraints will be handled regardless of whether there are + // any non-`Self` generic parameters. + if generics.is_own_empty() { return (tcx.mk_args(parent_args), arg_count); } @@ -421,8 +418,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { def_id: DefId, generic_args: &'a GenericArgs<'tcx>, span: Span, - inferred_params: Vec, infer_args: bool, + incorrect_args: &'a Result<(), GenericArgCountMismatch>, } impl<'a, 'tcx> GenericArgsLowerer<'a, 'tcx> for GenericArgsCtxt<'a, 'tcx> { @@ -437,12 +434,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn provided_kind( &mut self, + _preceding_args: &[ty::GenericArg<'tcx>], param: &ty::GenericParamDef, arg: &GenericArg<'tcx>, ) -> ty::GenericArg<'tcx> { let tcx = self.lowerer.tcx(); - let mut handle_ty_args = |has_default, ty: &hir::Ty<'tcx>| { + if let Err(incorrect) = self.incorrect_args { + if incorrect.invalid_args.contains(&(param.index as usize)) { + return param.to_error(tcx); + } + } + + let handle_ty_args = |has_default, ty: &hir::Ty<'tcx>| { if has_default { tcx.check_optional_stability( param.def_id, @@ -459,17 +463,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }, ); } - if let (hir::TyKind::Infer, false) = (&ty.kind, self.lowerer.allow_infer()) { - self.inferred_params.push(ty.span); - Ty::new_misc_error(tcx).into() - } else { - self.lowerer.lower_ty(ty).into() - } + self.lowerer.lower_ty(ty).into() }; match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - self.lowerer.lower_lifetime(lt, Some(param)).into() + self.lowerer.lower_lifetime(lt, RegionInferReason::Param(param)).into() } (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => { handle_ty_args(has_default, ty) @@ -483,17 +482,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Const::from_anon_const(tcx, did).into() } (&GenericParamDefKind::Const { .. }, hir::GenericArg::Infer(inf)) => { - let ty = tcx - .at(self.span) - .type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"); - if self.lowerer.allow_infer() { - self.lowerer.ct_infer(ty, Some(param), inf.span).into() - } else { - self.inferred_params.push(inf.span); - ty::Const::new_misc_error(tcx, ty).into() - } + self.lowerer.ct_infer(Some(param), inf.span).into() } (kind, arg) => span_bug!( self.span, @@ -504,38 +493,37 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn inferred_kind( &mut self, - args: Option<&[ty::GenericArg<'tcx>]>, + preceding_args: &[ty::GenericArg<'tcx>], param: &ty::GenericParamDef, infer_args: bool, ) -> ty::GenericArg<'tcx> { let tcx = self.lowerer.tcx(); - match param.kind { - GenericParamDefKind::Lifetime => self - .lowerer - .re_infer(Some(param), self.span) - .unwrap_or_else(|| { - debug!(?param, "unelided lifetime in signature"); - // This indicates an illegal lifetime in a non-assoc-trait position - ty::Region::new_error_with_message( - tcx, - self.span, - "unelided lifetime in signature", - ) - }) - .into(), + if let Err(incorrect) = self.incorrect_args { + if incorrect.invalid_args.contains(&(param.index as usize)) { + return param.to_error(tcx); + } + } + match param.kind { + GenericParamDefKind::Lifetime => { + self.lowerer.re_infer(self.span, RegionInferReason::Param(param)).into() + } GenericParamDefKind::Type { has_default, .. } => { if !infer_args && has_default { // No type parameter provided, but a default exists. - let args = args.unwrap(); - if args.iter().any(|arg| match arg.unpack() { - GenericArgKind::Type(ty) => ty.references_error(), - _ => false, - }) { + if let Some(prev) = + preceding_args.iter().find_map(|arg| match arg.unpack() { + GenericArgKind::Type(ty) => ty.error_reported().err(), + _ => None, + }) + { // Avoid ICE #86756 when type error recovery goes awry. - return Ty::new_misc_error(tcx).into(); + return Ty::new_error(tcx, prev).into(); } - tcx.at(self.span).type_of(param.def_id).instantiate(tcx, args).into() + tcx.at(self.span) + .type_of(param.def_id) + .instantiate(tcx, preceding_args) + .into() } else if infer_args { self.lowerer.ty_infer(Some(param), self.span).into() } else { @@ -550,46 +538,45 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .no_bound_vars() .expect("const parameter types cannot be generic"); if let Err(guar) = ty.error_reported() { - return ty::Const::new_error(tcx, guar, ty).into(); + return ty::Const::new_error(tcx, guar).into(); } // FIXME(effects) see if we should special case effect params here if !infer_args && has_default { tcx.const_param_default(param.def_id) - .instantiate(tcx, args.unwrap()) + .instantiate(tcx, preceding_args) .into() } else { if infer_args { - self.lowerer.ct_infer(ty, Some(param), self.span).into() + self.lowerer.ct_infer(Some(param), self.span).into() } else { // We've already errored above about the mismatch. - ty::Const::new_misc_error(tcx, ty).into() + ty::Const::new_misc_error(tcx).into() } } } } } } + if let ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst = constness + && generics.has_self + && !tcx.has_attr(def_id, sym::const_trait) + { + let reported = tcx.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait { + span, + modifier: constness.as_str(), + }); + self.set_tainted_by_errors(reported); + arg_count.correct = Err(GenericArgCountMismatch { reported, invalid_args: vec![] }); + } let mut args_ctx = GenericArgsCtxt { lowerer: self, def_id, span, generic_args: segment.args(), - inferred_params: vec![], infer_args: segment.infer_args, + incorrect_args: &arg_count.correct, }; - if let ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst = constness - && generics.has_self - && !tcx.has_attr(def_id, sym::const_trait) - { - let e = tcx.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait { - span, - modifier: constness.as_str(), - }); - self.set_tainted_by_errors(e); - arg_count.correct = - Err(GenericArgCountMismatch { reported: Some(e), invalid_args: vec![] }); - } let args = lower_generic_args( tcx, def_id, @@ -620,8 +607,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { None, ty::BoundConstness::NotConst, ); - if let Some(b) = item_segment.args().bindings.first() { - prohibit_assoc_item_binding(self.tcx(), b, Some((item_def_id, item_segment, span))); + if let Some(c) = item_segment.args().constraints.first() { + prohibit_assoc_item_constraint(self.tcx(), c, Some((item_def_id, item_segment, span))); } args } @@ -653,13 +640,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// /// *Polymorphic* in the sense that it may bind late-bound vars. /// - /// This may generate auxiliary bounds if the trait reference contains associated item bindings. + /// This may generate auxiliary bounds iff the trait reference contains associated item constraints. /// /// ### Example /// /// Given the trait ref `Iterator` and the self type `Ty`, this will add the /// - /// 1. *trait predicate* `` (known as `Foo: Iterator` in surface syntax) and the + /// 1. *trait predicate* `` (known as `Ty: Iterator` in the surface syntax) and the /// 2. *projection predicate* `::Item = u32` /// /// to `bounds`. @@ -713,27 +700,27 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { debug!(?poly_trait_ref); bounds.push_trait_bound(tcx, poly_trait_ref, span, polarity); - let mut dup_bindings = FxIndexMap::default(); - for binding in trait_segment.args().bindings { - // Don't register additional associated type bounds for negative bounds, - // since we should have emitten an error for them earlier, and they will - // not be well-formed! + let mut dup_constraints = FxIndexMap::default(); + for constraint in trait_segment.args().constraints { + // Don't register any associated item constraints for negative bounds, + // since we should have emitted an error for them earlier, and they + // would not be well-formed! if polarity != ty::PredicatePolarity::Positive { assert!( self.tcx().dcx().has_errors().is_some(), - "negative trait bounds should not have bindings", + "negative trait bounds should not have assoc item constraints", ); continue; } // Specify type to assert that error was already reported in `Err` case. - let _: Result<_, ErrorGuaranteed> = self.lower_assoc_item_binding( + let _: Result<_, ErrorGuaranteed> = self.lower_assoc_item_constraint( trait_ref.hir_ref_id, poly_trait_ref, - binding, + constraint, bounds, - &mut dup_bindings, - binding.span, + &mut dup_constraints, + constraint.span, only_self_bounds, ); // Okay to ignore `Err` because of `ErrorGuaranteed` (see above). @@ -765,8 +752,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Some(self_ty), constness, ); - if let Some(b) = trait_segment.args().bindings.first() { - prohibit_assoc_item_binding(self.tcx(), b, Some((trait_def_id, trait_segment, span))); + if let Some(c) = trait_segment.args().constraints.first() { + prohibit_assoc_item_constraint( + self.tcx(), + c, + Some((trait_def_id, trait_segment, span)), + ); } ty::TraitRef::new(self.tcx(), trait_def_id, generic_args) } @@ -848,7 +839,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// /// This fails if there is no such bound in the list of candidates or if there are multiple /// candidates in which case it reports ambiguity. - #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, binding), ret)] + #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, constraint), ret)] fn probe_single_bound_for_assoc_item( &self, all_candidates: impl Fn() -> I, @@ -857,7 +848,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assoc_kind: ty::AssocKind, assoc_name: Ident, span: Span, - binding: Option<&hir::TypeBinding<'tcx>>, + constraint: Option<&hir::AssocItemConstraint<'tcx>>, ) -> Result, ErrorGuaranteed> where I: Iterator>, @@ -876,7 +867,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assoc_kind, assoc_name, span, - binding, + constraint, ); self.set_tainted_by_errors(reported); return Err(reported); @@ -896,8 +887,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }); // Provide a more specific error code index entry for equality bindings. err.code( - if let Some(binding) = binding - && let hir::TypeBindingKind::Equality { .. } = binding.kind + if let Some(constraint) = constraint + && let hir::AssocItemConstraintKind::Equality { .. } = constraint.kind { E0222 } else { @@ -905,7 +896,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }, ); - // FIXME(#97583): Resugar equality bounds to type/const bindings. + // FIXME(#97583): Print associated item bindings properly (i.e., not as equality predicates!). // FIXME: Turn this into a structured, translateable & more actionable suggestion. let mut where_bounds = vec![]; for bound in [bound, bound2].into_iter().chain(matching_candidates) { @@ -920,9 +911,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bound_span, format!("ambiguous `{assoc_name}` from `{}`", bound.print_trait_sugared(),), ); - if let Some(binding) = binding { - match binding.kind { - hir::TypeBindingKind::Equality { term } => { + if let Some(constraint) = constraint { + match constraint.kind { + hir::AssocItemConstraintKind::Equality { term } => { let term: ty::Term<'_> = match term { hir::Term::Ty(ty) => self.lower_ty(ty).into(), hir::Term::Const(ct) => { @@ -936,7 +927,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { )); } // FIXME: Provide a suggestion. - hir::TypeBindingKind::Constraint { bounds: _ } => {} + hir::AssocItemConstraintKind::Bound { bounds: _ } => {} } } else { err.span_suggestion_verbose( @@ -1160,39 +1151,36 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; let trait_did = bound.def_id(); - let assoc_ty_did = self.probe_assoc_ty(assoc_ident, hir_ref_id, span, trait_did).unwrap(); - let ty = self.lower_assoc_ty(span, assoc_ty_did, assoc_segment, bound); + let assoc_ty = self + .probe_assoc_item(assoc_ident, ty::AssocKind::Type, hir_ref_id, span, trait_did) + .expect("failed to find associated type"); + let ty = self.lower_assoc_ty(span, assoc_ty.def_id, assoc_segment, bound); if let Some(variant_def_id) = variant_resolution { - tcx.node_span_lint( - AMBIGUOUS_ASSOCIATED_ITEMS, - hir_ref_id, - span, - "ambiguous associated item", - |lint| { - let mut could_refer_to = |kind: DefKind, def_id, also| { - let note_msg = format!( - "`{}` could{} refer to the {} defined here", - assoc_ident, - also, - tcx.def_kind_descr(kind, def_id) - ); - lint.span_note(tcx.def_span(def_id), note_msg); - }; - - could_refer_to(DefKind::Variant, variant_def_id, ""); - could_refer_to(DefKind::AssocTy, assoc_ty_did, " also"); - - lint.span_suggestion( - span, - "use fully-qualified syntax", - format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident), - Applicability::MachineApplicable, + tcx.node_span_lint(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| { + lint.primary_message("ambiguous associated item"); + let mut could_refer_to = |kind: DefKind, def_id, also| { + let note_msg = format!( + "`{}` could{} refer to the {} defined here", + assoc_ident, + also, + tcx.def_kind_descr(kind, def_id) ); - }, - ); + lint.span_note(tcx.def_span(def_id), note_msg); + }; + + could_refer_to(DefKind::Variant, variant_def_id, ""); + could_refer_to(DefKind::AssocTy, assoc_ty.def_id, " also"); + + lint.span_suggestion( + span, + "use fully-qualified syntax", + format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident), + Applicability::MachineApplicable, + ); + }); } - Ok((ty, DefKind::AssocTy, assoc_ty_did)) + Ok((ty, DefKind::AssocTy, assoc_ty.def_id)) } fn probe_inherent_assoc_ty( @@ -1219,7 +1207,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let candidates: Vec<_> = tcx .inherent_impls(adt_did)? .iter() - .filter_map(|&impl_| Some((impl_, self.probe_assoc_ty_unchecked(name, block, impl_)?))) + .filter_map(|&impl_| { + let (item, scope) = + self.probe_assoc_item_unchecked(name, ty::AssocKind::Type, block, impl_)?; + Some((impl_, (item.def_id, scope))) + }) .collect(); if candidates.is_empty() { @@ -1263,7 +1255,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }, )?; - self.check_assoc_ty(assoc_item, name, def_scope, block, span); + self.check_assoc_item(assoc_item, name, def_scope, block, span); // FIXME(fmease): Currently creating throwaway `parent_args` to please // `lower_generic_args_of_assoc_item`. Modify the latter instead (or sth. similar) to @@ -1297,7 +1289,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .copied() .filter(|&(impl_, _)| { infcx.probe(|_| { - let ocx = ObligationCtxt::new(infcx); + let ocx = ObligationCtxt::new_with_diagnostics(infcx); let self_ty = ocx.normalize(&ObligationCause::dummy(), param_env, self_ty); let impl_args = infcx.fresh_args_for_item(span, impl_); @@ -1350,50 +1342,69 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - fn probe_assoc_ty(&self, name: Ident, block: HirId, span: Span, scope: DefId) -> Option { - let (item, def_scope) = self.probe_assoc_ty_unchecked(name, block, scope)?; - self.check_assoc_ty(item, name, def_scope, block, span); + /// Given name and kind search for the assoc item in the provided scope and check if it's accessible[^1]. + /// + /// [^1]: I.e., accessible in the provided scope wrt. visibility and stability. + fn probe_assoc_item( + &self, + ident: Ident, + kind: ty::AssocKind, + block: HirId, + span: Span, + scope: DefId, + ) -> Option { + let (item, scope) = self.probe_assoc_item_unchecked(ident, kind, block, scope)?; + self.check_assoc_item(item.def_id, ident, scope, block, span); Some(item) } - fn probe_assoc_ty_unchecked( + /// Given name and kind search for the assoc item in the provided scope + /// *without* checking if it's accessible[^1]. + /// + /// [^1]: I.e., accessible in the provided scope wrt. visibility and stability. + fn probe_assoc_item_unchecked( &self, - name: Ident, + ident: Ident, + kind: ty::AssocKind, block: HirId, scope: DefId, - ) -> Option<(DefId, DefId)> { + ) -> Option<(ty::AssocItem, /*scope*/ DefId)> { let tcx = self.tcx(); - let (ident, def_scope) = tcx.adjust_ident_and_get_scope(name, scope, block); + let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, block); // We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()` // instead of calling `filter_by_name_and_kind` which would needlessly normalize the // `ident` again and again. - let item = tcx.associated_items(scope).in_definition_order().find(|i| { - i.kind.namespace() == Namespace::TypeNS - && i.ident(tcx).normalize_to_macros_2_0() == ident - })?; + let item = tcx + .associated_items(scope) + .filter_by_name_unhygienic(ident.name) + .find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == ident)?; - Some((item.def_id, def_scope)) + Some((*item, def_scope)) } - fn check_assoc_ty(&self, item: DefId, name: Ident, def_scope: DefId, block: HirId, span: Span) { + /// Check if the given assoc item is accessible in the provided scope wrt. visibility and stability. + fn check_assoc_item( + &self, + item_def_id: DefId, + ident: Ident, + scope: DefId, + block: HirId, + span: Span, + ) { let tcx = self.tcx(); - let kind = DefKind::AssocTy; - if !tcx.visibility(item).is_accessible_from(def_scope, tcx) { - let kind = tcx.def_kind_descr(kind, item); - let msg = format!("{kind} `{name}` is private"); - let def_span = tcx.def_span(item); - let reported = tcx - .dcx() - .struct_span_err(span, msg) - .with_code(E0624) - .with_span_label(span, format!("private {kind}")) - .with_span_label(def_span, format!("{kind} defined here")) - .emit(); + if !tcx.visibility(item_def_id).is_accessible_from(scope, tcx) { + let reported = tcx.dcx().emit_err(crate::errors::AssocItemIsPrivate { + span, + kind: tcx.def_descr(item_def_id), + name: ident, + defined_here_label: tcx.def_span(item_def_id), + }); self.set_tainted_by_errors(reported); } - tcx.check_stability(item, Some(block), span, None); + + tcx.check_stability(item_def_id, Some(block), span, None); } fn probe_traits_that_match_assoc_ty( @@ -1476,16 +1487,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let def_id = self.item_def_id(); debug!(item_def_id = ?def_id); - let parent_def_id = def_id - .as_local() - .map(|def_id| tcx.local_def_id_to_hir_id(def_id)) - .map(|hir_id| tcx.hir().get_parent_item(hir_id).to_def_id()); + // FIXME: document why/how this is different from `tcx.local_parent(def_id)` + let parent_def_id = + tcx.hir().get_parent_item(tcx.local_def_id_to_hir_id(def_id)).to_def_id(); debug!(?parent_def_id); // If the trait in segment is the same as the trait defining the item, // use the `` syntax in the error. - let is_part_of_self_trait_constraints = def_id == trait_def_id; - let is_part_of_fn_in_self_trait = parent_def_id == Some(trait_def_id); + let is_part_of_self_trait_constraints = def_id.to_def_id() == trait_def_id; + let is_part_of_fn_in_self_trait = parent_def_id == trait_def_id; let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait { vec!["Self".to_string()] @@ -1544,8 +1554,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { for segment in segments { // Only emit the first error to avoid overloading the user with error messages. - if let Some(b) = segment.args().bindings.first() { - return Err(prohibit_assoc_item_binding(self.tcx(), b, None)); + if let Some(c) = segment.args().constraints.first() { + return Err(prohibit_assoc_item_constraint(self.tcx(), c, None)); } } @@ -1757,7 +1767,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assert_eq!(opt_self_ty, None); let _ = self.prohibit_generic_args( path.segments.iter(), - GenericsArgsErrExtend::TyParam(def_id), + GenericsArgsErrExtend::Param(def_id), ); self.lower_ty_param(hir_id) } @@ -1913,7 +1923,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// /// Early-bound const parameters get lowered to [`ty::ConstKind::Param`] /// and late-bound ones to [`ty::ConstKind::Bound`]. - pub(crate) fn lower_const_param(&self, hir_id: HirId, param_ty: Ty<'tcx>) -> Const<'tcx> { + pub(crate) fn lower_const_param(&self, hir_id: HirId) -> Const<'tcx> { let tcx = self.tcx(); match tcx.named_bound_var(hir_id) { Some(rbv::ResolvedArg::EarlyBound(def_id)) => { @@ -1923,12 +1933,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let generics = tcx.generics_of(item_def_id); let index = generics.param_def_id_to_index[&def_id]; let name = tcx.item_name(def_id); - ty::Const::new_param(tcx, ty::ParamConst::new(index, name), param_ty) + ty::Const::new_param(tcx, ty::ParamConst::new(index, name)) } Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => { - ty::Const::new_bound(tcx, debruijn, ty::BoundVar::from_u32(index), param_ty) + ty::Const::new_bound(tcx, debruijn, ty::BoundVar::from_u32(index)) } - Some(rbv::ResolvedArg::Error(guar)) => ty::Const::new_error(tcx, guar, param_ty), + Some(rbv::ResolvedArg::Error(guar)) => ty::Const::new_error(tcx, guar), arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", hir_id), } } @@ -1966,7 +1976,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } let sig_generics = self.tcx().generics_of(sig_id); - let parent = self.tcx().parent(self.item_def_id()); + let parent = self.tcx().local_parent(self.item_def_id()); let parent_generics = self.tcx().generics_of(parent); let parent_is_trait = (self.tcx().def_kind(parent) == DefKind::Trait) as usize; @@ -2005,7 +2015,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let sig = self.tcx().fn_sig(sig_id); let sig_generics = self.tcx().generics_of(sig_id); - let parent = self.tcx().parent(self.item_def_id()); + let parent = self.tcx().local_parent(self.item_def_id()); let parent_def_kind = self.tcx().def_kind(parent); let sig = if let DefKind::Impl { .. } = parent_def_kind @@ -2053,7 +2063,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::TyKind::Slice(ty) => Ty::new_slice(tcx, self.lower_ty(ty)), hir::TyKind::Ptr(mt) => Ty::new_ptr(tcx, self.lower_ty(mt.ty), mt.mutbl), hir::TyKind::Ref(region, mt) => { - let r = self.lower_lifetime(region, None); + let r = self.lower_lifetime(region, RegionInferReason::Reference); debug!(?r); let t = self.lower_ty_common(mt.ty, true, false); Ty::new_ref(tcx, r, t, mt.mutbl) @@ -2144,7 +2154,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } hir::TyKind::Array(ty, length) => { let length = match length { - hir::ArrayLen::Infer(inf) => self.ct_infer(tcx.types.usize, None, inf.span), + hir::ArrayLen::Infer(inf) => self.ct_infer(None, inf.span), hir::ArrayLen::Body(constant) => { ty::Const::from_anon_const(tcx, constant.def_id) } @@ -2175,42 +2185,49 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } _ => (expr, None), }; - let c = match &expr.kind { + let (c, c_ty) = match &expr.kind { hir::ExprKind::Lit(lit) => { let lit_input = LitToConstInput { lit: &lit.node, ty, neg: neg.is_some() }; - match tcx.lit_to_const(lit_input) { + let ct = match tcx.lit_to_const(lit_input) { Ok(c) => c, Err(LitToConstError::Reported(err)) => { - ty::Const::new_error(tcx, err, ty) + ty::Const::new_error(tcx, err) } Err(LitToConstError::TypeError) => todo!(), - } + }; + (ct, ty) } hir::ExprKind::Path(hir::QPath::Resolved( _, - &hir::Path { - res: Res::Def(DefKind::ConstParam, def_id), .. + path @ &hir::Path { + res: Res::Def(DefKind::ConstParam, def_id), + .. }, )) => { + let _ = self.prohibit_generic_args( + path.segments.iter(), + GenericsArgsErrExtend::Param(def_id), + ); let ty = tcx .type_of(def_id) .no_bound_vars() .expect("const parameter types cannot be generic"); - self.lower_const_param(expr.hir_id, ty) + let ct = self.lower_const_param(expr.hir_id); + (ct, ty) } _ => { let err = tcx .dcx() .emit_err(crate::errors::NonConstRange { span: expr.span }); - ty::Const::new_error(tcx, err, ty) + (ty::Const::new_error(tcx, err), Ty::new_error(tcx, err)) } }; - self.record_ty(expr.hir_id, c.ty(), expr.span); + self.record_ty(expr.hir_id, c_ty, expr.span); if let Some((id, span)) = neg { - self.record_ty(id, c.ty(), span); + self.record_ty(id, c_ty, span); } c }; @@ -2277,7 +2294,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &lifetimes[i] ) }; - self.lower_lifetime(lifetime, None).into() + self.lower_lifetime(lifetime, RegionInferReason::Param(¶m)).into() } else { tcx.mk_param_from_def(param) } @@ -2316,92 +2333,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let bound_vars = tcx.late_bound_vars(hir_id); debug!(?bound_vars); - // We proactively collect all the inferred type params to emit a single error per fn def. - let mut visitor = HirPlaceholderCollector::default(); - let mut infer_replacements = vec![]; - - if let Some(generics) = generics { - walk_generics(&mut visitor, generics); - } - - let input_tys: Vec<_> = decl - .inputs - .iter() - .enumerate() - .map(|(i, a)| { - if let hir::TyKind::Infer = a.kind - && !self.allow_infer() - { - if let Some(suggested_ty) = - self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i)) - { - infer_replacements.push((a.span, suggested_ty.to_string())); - return Ty::new_error_with_message( - self.tcx(), - a.span, - suggested_ty.to_string(), - ); - } - } - - // Only visit the type looking for `_` if we didn't fix the type above - visitor.visit_ty(a); - self.lower_arg_ty(a, None) - }) - .collect(); - - let output_ty = match decl.output { - hir::FnRetTy::Return(output) => { - if let hir::TyKind::Infer = output.kind - && !self.allow_infer() - && let Some(suggested_ty) = - self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None) - { - infer_replacements.push((output.span, suggested_ty.to_string())); - Ty::new_error_with_message(self.tcx(), output.span, suggested_ty.to_string()) - } else { - visitor.visit_ty(output); - self.lower_ty(output) - } - } - hir::FnRetTy::DefaultReturn(..) => tcx.types.unit, - }; + let (input_tys, output_ty) = self.lower_fn_sig(decl, generics, hir_id, hir_ty); debug!(?output_ty); let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, safety, abi); let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); - if !self.allow_infer() && !(visitor.0.is_empty() && infer_replacements.is_empty()) { - // We always collect the spans for placeholder types when evaluating `fn`s, but we - // only want to emit an error complaining about them if infer types (`_`) are not - // allowed. `allow_infer` gates this behavior. We check for the presence of - // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`. - - let mut diag = crate::collect::placeholder_type_error_diag( - tcx, - generics, - visitor.0, - infer_replacements.iter().map(|(s, _)| *s).collect(), - true, - hir_ty, - "function", - ); - - if !infer_replacements.is_empty() { - diag.multipart_suggestion( - format!( - "try replacing `_` with the type{} in the corresponding trait method signature", - rustc_errors::pluralize!(infer_replacements.len()), - ), - infer_replacements, - Applicability::MachineApplicable, - ); - } - - self.set_tainted_by_errors(diag.emit()); - } - // Find any late-bound regions declared in return type that do // not appear in the arguments. These are not well-formed. // @@ -2431,7 +2369,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// corresponding function in the trait that the impl implements, if it exists. /// If arg_idx is Some, then it corresponds to an input type index, otherwise it /// corresponds to the return type. - fn suggest_trait_fn_ty_for_impl_fn_infer( + pub(super) fn suggest_trait_fn_ty_for_impl_fn_infer( &self, fn_hir_id: HirId, arg_idx: Option, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs index 4f7a39d02503..34924f09d09e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs @@ -1,5 +1,7 @@ use crate::bounds::Bounds; -use crate::hir_ty_lowering::{GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds}; +use crate::hir_ty_lowering::{ + GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds, RegionInferReason, +}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::{codes::*, struct_span_code_err}; use rustc_hir as hir; @@ -141,9 +143,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // `trait_object_dummy_self`, so check for that. let references_self = match pred.skip_binder().term.unpack() { ty::TermKind::Ty(ty) => ty.walk().any(|arg| arg == dummy_self.into()), - ty::TermKind::Const(c) => { - c.ty().walk().any(|arg| arg == dummy_self.into()) - } + // FIXME(associated_const_equality): We should walk the const instead of not doing anything + ty::TermKind::Const(_) => false, }; // If the projection output contains `Self`, force the user to @@ -321,30 +322,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Use explicitly-specified region bound. let region_bound = if !lifetime.is_elided() { - self.lower_lifetime(lifetime, None) + self.lower_lifetime(lifetime, RegionInferReason::ObjectLifetimeDefault) } else { self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| { if tcx.named_bound_var(lifetime.hir_id).is_some() { - self.lower_lifetime(lifetime, None) + self.lower_lifetime(lifetime, RegionInferReason::ObjectLifetimeDefault) } else { - self.re_infer(None, span).unwrap_or_else(|| { - let err = struct_span_code_err!( - tcx.dcx(), - span, - E0228, - "the lifetime bound for this object type cannot be deduced \ - from context; please supply an explicit bound" - ); - let e = if borrowed { - // We will have already emitted an error E0106 complaining about a - // missing named lifetime in `&dyn Trait`, so we elide this one. - err.delay_as_bug() + self.re_infer( + span, + if borrowed { + RegionInferReason::ObjectLifetimeDefault } else { - err.emit() - }; - self.set_tainted_by_errors(e); - ty::Region::new_error(tcx, e) - }) + RegionInferReason::BorrowedObjectLifetimeDefault + }, + ) } }) }; diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index 10101aa046e5..13993a1992b7 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -67,7 +67,7 @@ fn diagnostic_hir_wf_check<'tcx>( impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> { fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { let infcx = self.tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(&infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let tcx_ty = self.icx.lower_ty(ty); // This visitor can walk into binders, resulting in the `tcx_ty` to @@ -158,7 +158,7 @@ fn diagnostic_hir_wf_check<'tcx>( }, hir::Node::Field(field) => vec![field.ty], hir::Node::ForeignItem(ForeignItem { - kind: ForeignItemKind::Static(ty, _), .. + kind: ForeignItemKind::Static(ty, _, _), .. }) => vec![*ty], hir::Node::GenericParam(hir::GenericParam { kind: hir::GenericParamKind::Type { default: Some(ty), .. }, diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index 002be61196aa..5cc1ec71757b 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -86,6 +86,8 @@ fn enforce_impl_params_are_constrained( let impl_predicates = tcx.predicates_of(impl_def_id); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).map(ty::EarlyBinder::instantiate_identity); + impl_trait_ref.error_reported()?; + let mut input_parameters = cgp::parameters_for_impl(tcx, impl_self_ty, impl_trait_ref); cgp::identify_constrained_generic_params( tcx, diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 6967cb4d9d0b..f3ce3ab66553 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -196,7 +196,7 @@ fn get_impl_args( impl2_node: Node, ) -> Result<(GenericArgsRef<'_>, GenericArgsRef<'_>), ErrorGuaranteed> { let infcx = &tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(infcx); + let ocx = ObligationCtxt::new_with_diagnostics(infcx); let param_env = tcx.param_env(impl1_def_id); let impl1_span = tcx.def_span(impl1_def_id); let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl1_def_id)?; diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 654ef4baeb39..1927359421d0 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -55,22 +55,23 @@ This API is completely unstable and subject to change. */ +// tidy-alphabetical-start +#![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::potential_query_instability)] #![allow(rustc::untranslatable_diagnostic)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(rustdoc_internals)] -#![allow(internal_features)] #![feature(control_flow_enum)] #![feature(if_let_guard)] #![feature(is_sorted)] #![feature(iter_intersperse)] #![feature(let_chains)] #![feature(never_type)] -#![feature(lazy_cell)] +#![feature(rustdoc_internals)] #![feature(slice_partition_dedup)] #![feature(try_blocks)] +// tidy-alphabetical-end #[macro_use] extern crate tracing; @@ -181,7 +182,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) { let def_kind = tcx.def_kind(item_def_id); match def_kind { DefKind::Static { .. } => tcx.ensure().eval_static_initializer(item_def_id), - DefKind::Const if tcx.generics_of(item_def_id).own_params.is_empty() => { + DefKind::Const if tcx.generics_of(item_def_id).is_empty() => { let instance = ty::Instance::new(item_def_id.into(), ty::GenericArgs::empty()); let cid = GlobalId { instance, promoted: None }; let param_env = ty::ParamEnv::reveal_all(); diff --git a/compiler/rustc_hir_analysis/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs index 1de7a0f7bc73..bbfadbb5c308 100644 --- a/compiler/rustc_hir_analysis/src/outlives/explicit.rs +++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs @@ -6,7 +6,7 @@ use super::utils::*; #[derive(Debug)] pub struct ExplicitPredicatesMap<'tcx> { - map: FxIndexMap>>, + map: FxIndexMap>>, } impl<'tcx> ExplicitPredicatesMap<'tcx> { @@ -18,7 +18,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { &mut self, tcx: TyCtxt<'tcx>, def_id: DefId, - ) -> &ty::EarlyBinder> { + ) -> &ty::EarlyBinder<'tcx, RequiredPredicates<'tcx>> { self.map.entry(def_id).or_insert_with(|| { let predicates = if def_id.is_local() { tcx.explicit_predicates_of(def_id) diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index e6c582667ba5..af08f50f6559 100644 --- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -15,7 +15,7 @@ use super::utils::*; /// now be filled with inferred predicates. pub(super) fn infer_predicates( tcx: TyCtxt<'_>, -) -> FxIndexMap>> { +) -> FxIndexMap>> { debug!("infer_predicates"); let mut explicit_map = ExplicitPredicatesMap::new(); @@ -101,7 +101,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, - global_inferred_outlives: &FxIndexMap>>, + global_inferred_outlives: &FxIndexMap>>, required_predicates: &mut RequiredPredicates<'tcx>, explicit_map: &mut ExplicitPredicatesMap<'tcx>, ) { @@ -322,7 +322,7 @@ fn check_inferred_predicates<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, args: ty::GenericArgsRef<'tcx>, - global_inferred_outlives: &FxIndexMap>>, + global_inferred_outlives: &FxIndexMap>>, required_predicates: &mut RequiredPredicates<'tcx>, ) { // Load the current set of inferred and explicit predicates from `global_inferred_outlives` diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index 95290bbecf2c..5086c2af3f65 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -9,7 +9,7 @@ use smallvec::smallvec; /// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred /// must be added to the struct header. pub(crate) type RequiredPredicates<'tcx> = - FxIndexMap, ty::Region<'tcx>>, Span>; + FxIndexMap>, Span>; /// Given a requirement `T: 'a` or `'b: 'a`, deduce the /// outlives_component and add it to `required_predicates` diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index 9ddba7a6e7a9..5d435a8edf9f 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -489,7 +489,11 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { .in_definition_order() .filter(|item| item.kind == AssocKind::Type) .filter(|item| { - !self.gen_args.bindings.iter().any(|binding| binding.ident.name == item.name) + !self + .gen_args + .constraints + .iter() + .any(|constraint| constraint.ident.name == item.name) }) .map(|item| item.name.to_ident_string()) .collect() @@ -679,11 +683,11 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { (last_lt.span().shrink_to_hi(), false) }; let has_non_lt_args = self.num_provided_type_or_const_args() != 0; - let has_bindings = !self.gen_args.bindings.is_empty(); + let has_constraints = !self.gen_args.constraints.is_empty(); let sugg_prefix = if is_first { "" } else { ", " }; let sugg_suffix = - if is_first && (has_non_lt_args || has_bindings) { ", " } else { "" }; + if is_first && (has_non_lt_args || has_constraints) { ", " } else { "" }; let sugg = format!("{sugg_prefix}{suggested_args}{sugg_suffix}"); debug!("sugg: {:?}", sugg); @@ -741,7 +745,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let sugg_prefix = if is_first { "" } else { ", " }; let sugg_suffix = - if is_first && !self.gen_args.bindings.is_empty() { ", " } else { "" }; + if is_first && !self.gen_args.constraints.is_empty() { ", " } else { "" }; let sugg = format!("{sugg_prefix}{suggested_args}{sugg_suffix}"); debug!("sugg: {:?}", sugg); diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 730e989edaed..0c436e21c16d 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -99,7 +99,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { debug!("build_constraints_for_item({})", tcx.def_path_str(def_id)); // Skip items with no generics - there's nothing to infer in them. - if tcx.generics_of(def_id).count() == 0 { + if tcx.generics_of(def_id).is_empty() { return; } diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index cefd8fb1ac5f..1977451f39e0 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -41,7 +41,7 @@ fn crate_variances(tcx: TyCtxt<'_>, (): ()) -> CrateVariancesMap<'_> { fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] { // Skip items with no generics - there's nothing to infer in them. - if tcx.generics_of(item_def_id).count() == 0 { + if tcx.generics_of(item_def_id).is_empty() { return &[]; } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 29c7e576d2bb..b21f1eadfb7a 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1,7 +1,9 @@ //! HIR pretty-printing is layered on top of AST pretty-printing. A number of //! the definitions in this file have equivalents in `rustc_ast_pretty`. +// tidy-alphabetical-start #![recursion_limit = "256"] +// tidy-alphabetical-end use rustc_ast as ast; use rustc_ast::util::parser::{self, AssocOp, Fixity}; @@ -11,7 +13,7 @@ use rustc_ast_pretty::pprust::{Comments, PrintState}; use rustc_hir as hir; use rustc_hir::{ BindingMode, ByRef, GenericArg, GenericBound, GenericParam, GenericParamKind, HirId, - LifetimeParamKind, Node, PatKind, RangeEnd, Term, TraitBoundModifier, + LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, TraitBoundModifier, }; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, Ident, Symbol}; @@ -90,7 +92,7 @@ impl<'a> State<'a> { Node::Stmt(a) => self.print_stmt(a), Node::PathSegment(a) => self.print_path_segment(a), Node::Ty(a) => self.print_type(a), - Node::TypeBinding(a) => self.print_type_binding(a), + Node::AssocItemConstraint(a) => self.print_assoc_item_constraint(a), Node::TraitRef(a) => self.print_trait_ref(a), Node::Pat(a) => self.print_pat(a), Node::PatField(a) => self.print_patfield(a), @@ -346,12 +348,12 @@ impl<'a> State<'a> { self.maybe_print_comment(item.span.lo()); self.print_outer_attributes(self.attrs(item.hir_id())); match item.kind { - hir::ForeignItemKind::Fn(decl, arg_names, generics) => { + hir::ForeignItemKind::Fn(decl, arg_names, generics, safety) => { self.head(""); self.print_fn( decl, hir::FnHeader { - safety: hir::Safety::Safe, + safety, constness: hir::Constness::NotConst, abi: Abi::Rust, asyncness: hir::IsAsync::NotAsync, @@ -365,7 +367,8 @@ impl<'a> State<'a> { self.word(";"); self.end() // end the outer fn box } - hir::ForeignItemKind::Static(t, m) => { + hir::ForeignItemKind::Static(t, m, safety) => { + self.print_safety(safety); self.head("static"); if m.is_mut() { self.word_space("mut"); @@ -1136,7 +1139,7 @@ impl<'a> State<'a> { self.print_ident(segment.ident); let generic_args = segment.args(); - if !generic_args.args.is_empty() || !generic_args.bindings.is_empty() { + if !generic_args.args.is_empty() || !generic_args.constraints.is_empty() { self.print_generic_args(generic_args, true); } @@ -1454,7 +1457,7 @@ impl<'a> State<'a> { self.word_space(":"); } // containing cbox, will be closed by print-block at `}` - self.cbox(INDENT_UNIT); + self.cbox(0); // head-box, will be closed by print-block after `{` self.ibox(0); self.print_block(blk); @@ -1677,9 +1680,9 @@ impl<'a> State<'a> { }); } - for binding in generic_args.bindings { + for constraint in generic_args.constraints { start_or_comma(self); - self.print_type_binding(binding); + self.print_assoc_item_constraint(constraint); } if !empty.get() { @@ -1687,13 +1690,15 @@ impl<'a> State<'a> { } } hir::GenericArgsParentheses::ParenSugar => { + let (inputs, output) = generic_args.paren_sugar_inputs_output().unwrap(); + self.word("("); - self.commasep(Inconsistent, generic_args.inputs(), |s, ty| s.print_type(ty)); + self.commasep(Inconsistent, inputs, |s, ty| s.print_type(ty)); self.word(")"); self.space_if_not_bol(); self.word_space("->"); - self.print_type(generic_args.bindings[0].ty()); + self.print_type(output); } hir::GenericArgsParentheses::ReturnTypeNotation => { self.word("(..)"); @@ -1701,19 +1706,19 @@ impl<'a> State<'a> { } } - fn print_type_binding(&mut self, binding: &hir::TypeBinding<'_>) { - self.print_ident(binding.ident); - self.print_generic_args(binding.gen_args, false); + fn print_assoc_item_constraint(&mut self, constraint: &hir::AssocItemConstraint<'_>) { + self.print_ident(constraint.ident); + self.print_generic_args(constraint.gen_args, false); self.space(); - match binding.kind { - hir::TypeBindingKind::Equality { ref term } => { + match constraint.kind { + hir::AssocItemConstraintKind::Equality { ref term } => { self.word_space("="); match term { Term::Ty(ty) => self.print_type(ty), Term::Const(ref c) => self.print_anon_const(c), } } - hir::TypeBindingKind::Constraint { bounds } => { + hir::AssocItemConstraintKind::Bound { bounds } => { self.print_bounds(":", bounds); } } @@ -2095,10 +2100,24 @@ impl<'a> State<'a> { GenericBound::Outlives(lt) => { self.print_lifetime(lt); } + GenericBound::Use(args, _) => { + self.word("use <"); + + self.commasep(Inconsistent, args, |s, arg| s.print_precise_capturing_arg(*arg)); + + self.word(">"); + } } } } + fn print_precise_capturing_arg(&mut self, arg: PreciseCapturingArg<'_>) { + match arg { + PreciseCapturingArg::Lifetime(lt) => self.print_lifetime(lt), + PreciseCapturingArg::Param(arg) => self.print_ident(arg.ident), + } + } + fn print_generic_params(&mut self, generic_params: &[GenericParam<'_>]) { if !generic_params.is_empty() { self.word("<"); diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 72b95a9603dd..d6f3f4d640bd 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -44,6 +44,10 @@ hir_typeck_convert_using_method = try using `{$sugg}` to convert `{$found}` to ` hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private +hir_typeck_dependency_on_unit_never_type_fallback = this function depends on never type fallback being `()` + .note = in edition 2024, the requirement `{$obligation}` will fail + .help = specify the types explicitly + hir_typeck_deref_is_empty = this expression `Deref`s to `{$deref_ty}` which implements `is_empty` hir_typeck_expected_default_return_type = expected `()` because of default return type @@ -137,6 +141,10 @@ hir_typeck_rpit_change_return_type = you could change the return type to be a bo hir_typeck_rustcall_incorrect_args = functions with the "rust-call" ABI must take a single non-self tuple argument +hir_typeck_self_ctor_from_outer_item = can't reference `Self` constructor from outer item + .label = the inner item doesn't inherit generics from this impl, so `Self` is invalid to reference + .suggestion = replace `Self` with the actual type + hir_typeck_struct_expr_non_exhaustive = cannot create non-exhaustive {$what} using struct expression diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index c2e62e4c0035..4e2104ff5615 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -207,10 +207,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir = self.tcx.hir(); // First, check that we're actually in the tail of a function. - let Some(body_id) = hir.maybe_body_owned_by(self.body_id) else { + let Some(body) = hir.maybe_body_owned_by(self.body_id) else { return; }; - let body = hir.body(body_id); let hir::ExprKind::Block(block, _) = body.value.kind else { return; }; @@ -235,7 +234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ret_ty = ret_coercion.borrow().expected_ty(); let ret_ty = self.infcx.shallow_resolve(ret_ty); self.can_coerce(arm_ty, ret_ty) - && prior_arm.map_or(true, |(_, ty, _)| self.can_coerce(ty, ret_ty)) + && prior_arm.is_none_or(|(_, ty, _)| self.can_coerce(ty, ret_ty)) // The match arms need to unify for the case of `impl Trait`. && !matches!(ret_ty.kind(), ty::Alias(ty::Opaque, ..)) } @@ -247,7 +246,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let semi = expr.span.shrink_to_hi().with_hi(semi_span.hi()); let sugg = crate::errors::RemoveSemiForCoerce { expr: expr.span, ret, semi }; - diag.subdiagnostic(self.dcx(), sugg); + diag.subdiagnostic(sugg); } /// When the previously checked expression (the scrutinee) diverges, diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 9736c8b89204..46c855155753 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -5,9 +5,9 @@ use super::{Expectation, FnCtxt, TupleArgumentsFlag}; use crate::errors; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey}; -use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, LangItem}; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::traits::ObligationCauseCode; use rustc_infer::{ @@ -41,7 +41,7 @@ pub fn check_legal_trait_for_method_call( trait_id: DefId, body_id: DefId, ) -> Result<(), ErrorGuaranteed> { - if tcx.lang_items().drop_trait() == Some(trait_id) + if tcx.is_lang_item(trait_id, LangItem::Drop) && tcx.lang_items().fallback_surface_drop_fn() != Some(body_id) { let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) { @@ -628,7 +628,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; }; - let pick = self.confirm_method( + let pick = self.confirm_method_for_diagnostic( call_expr.span, callee_expr, call_expr, diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 9e9a1f678edd..587085102821 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -1005,25 +1005,19 @@ impl<'a, 'tcx> CastCheck<'tcx> { if let Some((deref_ty, _)) = derefed { // Give a note about what the expr derefs to. if deref_ty != self.expr_ty.peel_refs() { - err.subdiagnostic( - fcx.dcx(), - errors::DerefImplsIsEmpty { - span: self.expr_span, - deref_ty: fcx.ty_to_string(deref_ty), - }, - ); + err.subdiagnostic(errors::DerefImplsIsEmpty { + span: self.expr_span, + deref_ty: fcx.ty_to_string(deref_ty), + }); } // Create a multipart suggestion: add `!` and `.is_empty()` in // place of the cast. - err.subdiagnostic( - fcx.dcx(), - errors::UseIsEmpty { - lo: self.expr_span.shrink_to_lo(), - hi: self.span.with_lo(self.expr_span.hi()), - expr_ty: fcx.ty_to_string(self.expr_ty), - }, - ); + err.subdiagnostic(errors::UseIsEmpty { + lo: self.expr_span.shrink_to_lo(), + hi: self.span.with_lo(self.expr_span.hi()), + expr_ty: fcx.ty_to_string(self.expr_ty), + }); } } } diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 14a6177141c9..ac7ed3e26f97 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -485,7 +485,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // Since this is a return parameter type it is safe to unwrap. - let ret_param_ty = projection.skip_binder().term.ty().unwrap(); + let ret_param_ty = projection.skip_binder().term.expect_type(); let ret_param_ty = self.resolve_vars_if_possible(ret_param_ty); debug!(?ret_param_ty); @@ -956,7 +956,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let output_ty = self.resolve_vars_if_possible(predicate.term); debug!("deduce_future_output_from_projection: output_ty={:?}", output_ty); // This is a projection on a Fn trait so will always be a type. - Some(output_ty.ty().unwrap()) + Some(output_ty.expect_type()) } /// Converts the types that the user supplied, in case that doing diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index d81dab2222a9..31f85e21d713 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -41,6 +41,7 @@ use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; +use rustc_infer::infer::relate::RelateResult; use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult}; use rustc_infer::traits::{IfExpressionCause, MatchExpressionArmCause}; use rustc_infer::traits::{Obligation, PredicateObligation}; @@ -51,7 +52,6 @@ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, }; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt}; use rustc_session::parse::feature_err; @@ -113,7 +113,7 @@ fn simple<'tcx>(kind: Adjust<'tcx>) -> impl FnOnce(Ty<'tcx>) -> Vec( adj: Vec>, target: Ty<'tcx>, - obligations: traits::PredicateObligations<'tcx>, + obligations: Vec>, ) -> CoerceResult<'tcx> { Ok(InferOk { value: (adj, target), obligations }) } @@ -913,7 +913,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { if self .tcx .upvars_mentioned(closure_def_id_a.expect_local()) - .map_or(true, |u| u.is_empty()) => + .is_none_or(|u| u.is_empty()) => { // We coerce the closure, which has fn type // `extern "rust-call" fn((arg0,arg1,...)) -> _` @@ -1158,8 +1158,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig)); let sig = self .at(cause, self.param_env) - .trace(prev_ty, new_ty) - .lub(DefineOpaqueTypes::No, a_sig, b_sig) + .lub(DefineOpaqueTypes::Yes, a_sig, b_sig) .map(|ok| self.register_infer_ok_obligations(ok))?; // Reify both sides and return the reified fn pointer type. @@ -1591,31 +1590,28 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { err.span_label(cause.span, "return type is not `()`"); } ObligationCauseCode::BlockTailExpression(blk_id, ..) => { - let parent_id = fcx.tcx.parent_hir_id(blk_id); err = self.report_return_mismatched_types( cause, expected, found, coercion_error, fcx, - parent_id, + blk_id, expression, - Some(blk_id), ); if !fcx.tcx.features().unsized_locals { unsized_return = self.is_return_ty_definitely_unsized(fcx); } } - ObligationCauseCode::ReturnValue(id) => { + ObligationCauseCode::ReturnValue(return_expr_id) => { err = self.report_return_mismatched_types( cause, expected, found, coercion_error, fcx, - id, + return_expr_id, expression, - None, ); if !fcx.tcx.features().unsized_locals { unsized_return = self.is_return_ty_definitely_unsized(fcx); @@ -1786,20 +1782,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } let rpid_def_span = fcx.tcx.def_span(rpit_def_id); - err.subdiagnostic( - fcx.tcx.dcx(), - SuggestBoxingForReturnImplTrait::ChangeReturnType { - start_sp: rpid_def_span.with_hi(rpid_def_span.lo() + BytePos(4)), - end_sp: rpid_def_span.shrink_to_hi(), - }, - ); + err.subdiagnostic(SuggestBoxingForReturnImplTrait::ChangeReturnType { + start_sp: rpid_def_span.with_hi(rpid_def_span.lo() + BytePos(4)), + end_sp: rpid_def_span.shrink_to_hi(), + }); let (starts, ends) = arm_spans.map(|span| (span.shrink_to_lo(), span.shrink_to_hi())).unzip(); - err.subdiagnostic( - fcx.tcx.dcx(), - SuggestBoxingForReturnImplTrait::BoxReturnExpr { starts, ends }, - ); + err.subdiagnostic(SuggestBoxingForReturnImplTrait::BoxReturnExpr { starts, ends }); } fn report_return_mismatched_types<'a>( @@ -1809,13 +1799,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { found: Ty<'tcx>, ty_err: TypeError<'tcx>, fcx: &FnCtxt<'a, 'tcx>, - id: hir::HirId, + block_or_return_id: hir::HirId, expression: Option<&'tcx hir::Expr<'tcx>>, - blk_id: Option, ) -> Diag<'a> { let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err); - let parent_id = fcx.tcx.parent_hir_id(id); + let due_to_block = matches!(fcx.tcx.hir_node(block_or_return_id), hir::Node::Block(..)); + + let parent_id = fcx.tcx.parent_hir_id(block_or_return_id); let parent = fcx.tcx.hir_node(parent_id); if let Some(expr) = expression && let hir::Node::Expr(hir::Expr { @@ -1829,72 +1820,61 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // Verify that this is a tail expression of a function, otherwise the // label pointing out the cause for the type coercion will be wrong // as prior return coercions would not be relevant (#57664). - let fn_decl = if let (Some(expr), Some(blk_id)) = (expression, blk_id) { + if let Some(expr) = expression + && due_to_block + { fcx.suggest_missing_semicolon(&mut err, expr, expected, false); - let pointing_at_return_type = - fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id); - if let (Some(cond_expr), true, false) = ( - fcx.tcx.hir().get_if_cause(expr.hir_id), - expected.is_unit(), - pointing_at_return_type, - ) + let pointing_at_return_type = fcx.suggest_mismatched_types_on_tail( + &mut err, + expr, + expected, + found, + block_or_return_id, + ); + if let Some(cond_expr) = fcx.tcx.hir().get_if_cause(expr.hir_id) + && expected.is_unit() + && !pointing_at_return_type // If the block is from an external macro or try (`?`) desugaring, then // do not suggest adding a semicolon, because there's nowhere to put it. // See issues #81943 and #87051. && matches!( cond_expr.span.desugaring_kind(), None | Some(DesugaringKind::WhileLoop) - ) && !in_external_macro(fcx.tcx.sess, cond_expr.span) - && !matches!( - cond_expr.kind, - hir::ExprKind::Match(.., hir::MatchSource::TryDesugar(_)) - ) + ) + && !in_external_macro(fcx.tcx.sess, cond_expr.span) + && !matches!( + cond_expr.kind, + hir::ExprKind::Match(.., hir::MatchSource::TryDesugar(_)) + ) { err.span_label(cond_expr.span, "expected this to be `()`"); if expr.can_have_side_effects() { fcx.suggest_semicolon_at_end(cond_expr.span, &mut err); } } - fcx.get_node_fn_decl(parent) - .map(|(fn_id, fn_decl, _, is_main)| (fn_id, fn_decl, is_main)) - } else { - fcx.get_fn_decl(parent_id) }; - if let Some((fn_id, fn_decl, can_suggest)) = fn_decl { - if blk_id.is_none() { - fcx.suggest_missing_return_type( - &mut err, - fn_decl, - expected, - found, - can_suggest, - fn_id, - ); - } + // If this is due to an explicit `return`, suggest adding a return type. + if let Some((fn_id, fn_decl, can_suggest)) = fcx.get_fn_decl(parent_id) + && !due_to_block + { + fcx.suggest_missing_return_type(&mut err, fn_decl, expected, found, can_suggest, fn_id); } - let mut parent_id = fcx.tcx.hir().get_parent_item(id).def_id; - let mut parent_item = fcx.tcx.hir_node_by_def_id(parent_id); - // When suggesting return, we need to account for closures and async blocks, not just items. - for (_, node) in fcx.tcx.hir().parent_iter(id) { - match node { - hir::Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure(hir::Closure { def_id, .. }), - .. - }) => { - parent_item = node; - parent_id = *def_id; - break; - } - hir::Node::Item(_) | hir::Node::TraitItem(_) | hir::Node::ImplItem(_) => break, - _ => {} - } - } - - if let (Some(expr), Some(_), Some(fn_decl)) = (expression, blk_id, parent_item.fn_decl()) { + // If this is due to a block, then maybe we forgot a `return`/`break`. + if due_to_block + && let Some(expr) = expression + && let Some(parent_fn_decl) = + fcx.tcx.hir().fn_decl_by_hir_id(fcx.tcx.local_def_id_to_hir_id(fcx.body_id)) + { fcx.suggest_missing_break_or_return_expr( - &mut err, expr, fn_decl, expected, found, id, parent_id, + &mut err, + expr, + parent_fn_decl, + expected, + found, + block_or_return_id, + fcx.body_id, ); } diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 6da5adc7a6e9..f9720c9c3079 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -4,7 +4,7 @@ use rustc_errors::{Applicability, Diag}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::intravisit::Visitor; -use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; +use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::bug; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -166,7 +166,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Requires that the two types unify, and prints an error message if /// they don't. pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { - if let Some(e) = self.demand_suptype_diag(sp, expected, actual) { + if let Err(e) = self.demand_suptype_diag(sp, expected, actual) { e.emit(); } } @@ -176,7 +176,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>, - ) -> Option> { + ) -> Result<(), Diag<'tcx>> { self.demand_suptype_with_origin(&self.misc(sp), expected, actual) } @@ -186,18 +186,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { cause: &ObligationCause<'tcx>, expected: Ty<'tcx>, actual: Ty<'tcx>, - ) -> Option> { - match self.at(cause, self.param_env).sup(DefineOpaqueTypes::Yes, expected, actual) { - Ok(InferOk { obligations, value: () }) => { - self.register_predicates(obligations); - None - } - Err(e) => Some(self.err_ctxt().report_mismatched_types(cause, expected, actual, e)), - } + ) -> Result<(), Diag<'tcx>> { + self.at(cause, self.param_env) + .sup(DefineOpaqueTypes::Yes, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + .map_err(|e| self.err_ctxt().report_mismatched_types(cause, expected, actual, e)) } pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { - if let Some(err) = self.demand_eqtype_diag(sp, expected, actual) { + if let Err(err) = self.demand_eqtype_diag(sp, expected, actual) { err.emit(); } } @@ -207,7 +204,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>, - ) -> Option> { + ) -> Result<(), Diag<'tcx>> { self.demand_eqtype_with_origin(&self.misc(sp), expected, actual) } @@ -216,14 +213,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { cause: &ObligationCause<'tcx>, expected: Ty<'tcx>, actual: Ty<'tcx>, - ) -> Option> { - match self.at(cause, self.param_env).eq(DefineOpaqueTypes::Yes, expected, actual) { - Ok(InferOk { obligations, value: () }) => { - self.register_predicates(obligations); - None - } - Err(e) => Some(self.err_ctxt().report_mismatched_types(cause, expected, actual, e)), - } + ) -> Result<(), Diag<'tcx>> { + self.at(cause, self.param_env) + .eq(DefineOpaqueTypes::Yes, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + .map_err(|e| self.err_ctxt().report_mismatched_types(cause, expected, actual, e)) } pub fn demand_coerce( @@ -234,12 +228,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, allow_two_phase: AllowTwoPhase, ) -> Ty<'tcx> { - let (ty, err) = - self.demand_coerce_diag(expr, checked_ty, expected, expected_ty_expr, allow_two_phase); - if let Some(err) = err { - err.emit(); + match self.demand_coerce_diag(expr, checked_ty, expected, expected_ty_expr, allow_two_phase) + { + Ok(ty) => ty, + Err(err) => { + err.emit(); + // Return the original type instead of an error type here, otherwise the type of `x` in + // `let x: u32 = ();` will be a type error, causing all subsequent usages of `x` to not + // report errors, even though `x` is definitely `u32`. + expected + } } - ty } /// Checks that the type of `expr` can be coerced to `expected`. @@ -254,11 +253,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, mut expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, allow_two_phase: AllowTwoPhase, - ) -> (Ty<'tcx>, Option>) { + ) -> Result, Diag<'tcx>> { let expected = self.resolve_vars_with_obligations(expected); let e = match self.coerce(expr, checked_ty, expected, allow_two_phase, None) { - Ok(ty) => return (ty, None), + Ok(ty) => return Ok(ty), Err(e) => e, }; @@ -275,7 +274,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected, expected_ty_expr, Some(e)); - (expected, Some(err)) + Err(err) } /// Notes the point at which a variable is constrained to some type incompatible @@ -327,8 +326,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let mut expr_finder = FindExprs { hir_id: local_hir_id, uses: init.into_iter().collect() }; - let body = - hir.body(hir.maybe_body_owned_by(self.body_id).expect("expected item to have body")); + let body = hir.body_owned_by(self.body_id); expr_finder.visit_expr(body.value); // Replaces all of the variables in the given type with a fresh inference variable. @@ -351,7 +349,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lt_op: |_| self.tcx.lifetimes.re_erased, ct_op: |ct| { if let ty::ConstKind::Infer(_) = ct.kind() { - self.next_const_var(ct.ty(), DUMMY_SP) + self.next_const_var(DUMMY_SP) } else { ct } @@ -373,8 +371,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(arg_ty) = self.node_ty_opt(args[idx].hir_id) else { return false; }; - let possible_rcvr_ty = expr_finder.uses.iter().find_map(|binding| { + let possible_rcvr_ty = expr_finder.uses.iter().rev().find_map(|binding| { let possible_rcvr_ty = self.node_ty_opt(binding.hir_id)?; + if possible_rcvr_ty.is_ty_var() { + return None; + } // Fudge the receiver, so we can do new inference on it. let possible_rcvr_ty = possible_rcvr_ty.fold_with(&mut fudger); let method = self @@ -386,6 +387,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { binding, ) .ok()?; + // Make sure we select the same method that we started with... + if Some(method.def_id) + != self.typeck_results.borrow().type_dependent_def_id(call_expr.hir_id) + { + return None; + } // Unify the method signature with our incompatible arg, to // do inference in the *opposite* direction and to find out // what our ideal rcvr ty would look like. @@ -456,6 +463,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) else { continue; }; + // Make sure we select the same method that we started with... + if Some(method.def_id) + != self.typeck_results.borrow().type_dependent_def_id(parent_expr.hir_id) + { + continue; + } let ideal_rcvr_ty = rcvr_ty.fold_with(&mut fudger); let ideal_method = self @@ -505,13 +518,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // blame arg, if possible. Don't do this if we're coming from // arg mismatch code, because we'll possibly suggest a mutually // incompatible fix at the original mismatch site. + // HACK(compiler-errors): We don't actually consider the implications + // of our inference guesses in `emit_type_mismatch_suggestions`, so + // only suggest things when we know our type error is precisely due to + // a type mismatch, and not via some projection or something. See #116155. if matches!(source, TypeMismatchSource::Ty(_)) && let Some(ideal_method) = ideal_method - && let ideal_arg_ty = self.resolve_vars_if_possible(ideal_method.sig.inputs()[idx + 1]) - // HACK(compiler-errors): We don't actually consider the implications - // of our inference guesses in `emit_type_mismatch_suggestions`, so - // only suggest things when we know our type error is precisely due to - // a type mismatch, and not via some projection or something. See #116155. + && Some(ideal_method.def_id) + == self + .typeck_results + .borrow() + .type_dependent_def_id(parent_expr.hir_id) + && let ideal_arg_ty = + self.resolve_vars_if_possible(ideal_method.sig.inputs()[idx + 1]) && !ideal_arg_ty.has_non_region_infer() { self.emit_type_mismatch_suggestions( @@ -807,7 +826,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) else { return; }; - let in_scope_methods = self.probe_for_name_many( + + let Ok(in_scope_methods) = self.probe_for_name_many( probe::Mode::MethodCall, path.ident, Some(expected), @@ -815,11 +835,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty, deref.hir_id, probe::ProbeScope::TraitsInScope, - ); + ) else { + return; + }; + let other_methods_in_scope: Vec<_> = in_scope_methods.iter().filter(|c| c.item.def_id != pick.item.def_id).collect(); - let all_methods = self.probe_for_name_many( + let Ok(all_methods) = self.probe_for_name_many( probe::Mode::MethodCall, path.ident, Some(expected), @@ -827,7 +850,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty, deref.hir_id, probe::ProbeScope::AllTraits, - ); + ) else { + return; + }; + let suggestions: Vec<_> = all_methods .into_iter() .filter(|c| c.item.def_id != pick.item.def_id) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index f250b909596e..24f039b8e90b 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -7,7 +7,7 @@ use rustc_errors::{ SubdiagMessageOp, Subdiagnostic, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_span::{ edition::{Edition, LATEST_STABLE_EDITION}, symbol::Ident, @@ -183,6 +183,15 @@ pub enum NeverTypeFallbackFlowingIntoUnsafe { Deref, } +#[derive(LintDiagnostic)] +#[help] +#[diag(hir_typeck_dependency_on_unit_never_type_fallback)] +pub struct DependencyOnUnitNeverTypeFallback<'tcx> { + #[note] + pub obligation_span: Span, + pub obligation: ty::Predicate<'tcx>, +} + #[derive(Subdiagnostic)] #[multipart_suggestion( hir_typeck_add_missing_parentheses_in_range, @@ -651,3 +660,31 @@ pub enum SuggestBoxingForReturnImplTrait { ends: Vec, }, } + +#[derive(Diagnostic)] +#[diag(hir_typeck_self_ctor_from_outer_item, code = E0401)] +pub struct SelfCtorFromOuterItem { + #[primary_span] + pub span: Span, + #[label] + pub impl_span: Span, + #[subdiagnostic] + pub sugg: Option, +} + +#[derive(LintDiagnostic)] +#[diag(hir_typeck_self_ctor_from_outer_item)] +pub struct SelfCtorFromOuterItemLint { + #[label] + pub impl_span: Span, + #[subdiagnostic] + pub sugg: Option, +} + +#[derive(Subdiagnostic)] +#[suggestion(hir_typeck_suggestion, code = "{name}", applicability = "machine-applicable")] +pub struct ReplaceWithName { + #[primary_span] + pub span: Span, + pub name: String, +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 5a9eab1ffeaf..233dc2afa9b2 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -13,7 +13,6 @@ use crate::errors::{ YieldExprOutsideOfCoroutine, }; use crate::fatally_break_rust; -use crate::method::SelfSource; use crate::type_error_struct; use crate::CoroutineTypes; use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; @@ -42,7 +41,7 @@ use rustc_infer::infer::InferOk; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; -use rustc_middle::ty::error::{ExpectedFound, TypeError::Sorts}; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; @@ -88,7 +87,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty = adj_ty; } - if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) { + if let Err(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) { let _ = self.emit_type_mismatch_suggestions( &mut err, expr.peel_drop_temps(), @@ -224,7 +223,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = ensure_sufficient_stack(|| match &expr.kind { hir::ExprKind::Path( qpath @ (hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)), - ) => self.check_expr_path(qpath, expr, args, call), + ) => self.check_expr_path(qpath, expr, Some(args), call), _ => self.check_expr_kind(expr, expected), }); let ty = self.resolve_vars_if_possible(ty); @@ -291,7 +290,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Path(QPath::LangItem(lang_item, _)) => { self.check_lang_item_path(lang_item, expr) } - ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr, &[], None), + ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr, None, None), ExprKind::InlineAsm(asm) => { // We defer some asm checks as we may not have resolved the input and output types yet (they may still be infer vars). self.deferred_asm_checks.borrow_mut().push((asm, expr.hir_id)); @@ -385,7 +384,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(sp) = tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp) { - err.subdiagnostic(self.dcx(), ExprParenthesesNeeded::surrounding(*sp)); + err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } oprnd_t = Ty::new_error(tcx, err.emit()); } @@ -503,12 +502,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, qpath: &'tcx hir::QPath<'tcx>, expr: &'tcx hir::Expr<'tcx>, - args: &'tcx [hir::Expr<'tcx>], + args: Option<&'tcx [hir::Expr<'tcx>]>, call: Option<&'tcx hir::Expr<'tcx>>, ) -> Ty<'tcx> { let tcx = self.tcx; let (res, opt_ty, segs) = - self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span, Some(args)); + self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span); let ty = match res { Res::Err => { self.suggest_assoc_method_call(segs); @@ -565,7 +564,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We just want to check sizedness, so instead of introducing // placeholder lifetimes with probing, we just replace higher lifetimes // with fresh vars. - let span = args.get(i).map(|a| a.span).unwrap_or(expr.span); + let span = args.and_then(|args| args.get(i)).map_or(expr.span, |arg| arg.span); let input = self.instantiate_binder_with_fresh_vars( span, infer::BoundRegionConversionTime::FnCall, @@ -684,7 +683,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_mismatched_types_on_tail( &mut err, expr, ty, e_ty, target_id, ); - let error = Some(Sorts(ExpectedFound { expected: ty, found: e_ty })); + let error = + Some(TypeError::Sorts(ExpectedFound { expected: ty, found: e_ty })); self.annotate_loop_expected_due_to_inference(err, expr, error); if let Some(val) = self.err_ctxt().ty_kind_suggestion(self.param_env, ty) @@ -910,8 +910,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the first place. assert_ne!(encl_item_id.def_id, encl_body_owner_id); - let encl_body_id = self.tcx.hir().body_owned_by(encl_body_owner_id); - let encl_body = self.tcx.hir().body(encl_body_id); + let encl_body = self.tcx.hir().body_owned_by(encl_body_owner_id); err.encl_body_span = Some(encl_body.value.span); err.encl_fn_span = Some(*encl_fn_span); @@ -1133,7 +1132,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // say that the user intended to write `lhs == rhs` instead of `lhs = rhs`. // The likely cause of this is `if foo = bar { .. }`. let actual_ty = self.tcx.types.unit; - let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap(); + let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap_err(); let lhs_ty = self.check_expr(lhs); let rhs_ty = self.check_expr(rhs); let refs_can_coerce = |lhs: Ty<'tcx>, rhs: Ty<'tcx>| { @@ -1237,7 +1236,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This is (basically) inlined `check_expr_coercible_to_type`, but we want // to suggest an additional fixup here in `suggest_deref_binop`. let rhs_ty = self.check_expr_with_hint(rhs, lhs_ty); - if let (_, Some(mut diag)) = + if let Err(mut diag) = self.demand_coerce_diag(rhs, rhs_ty, lhs_ty, Some(lhs), AllowTwoPhase::No) { suggest_deref_binop(&mut diag, rhs_ty); @@ -1333,9 +1332,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let rcvr_t = self.check_expr(rcvr); // no need to check for bot/err -- callee does that let rcvr_t = self.structurally_resolve_type(rcvr.span, rcvr_t); - let span = segment.ident.span; - let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr, args) { + let method = match self.lookup_method(rcvr_t, segment, segment.ident.span, expr, rcvr, args) + { Ok(method) => { // We could add a "consider `foo::`" suggestion here, but I wasn't able to // trigger this codepath causing `structurally_resolve_type` to emit an error. @@ -1343,26 +1342,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(method) } Err(error) => { - if segment.ident.name != kw::Empty { - if let Some(err) = self.report_method_error( - span, - rcvr_t, - segment.ident, - SelfSource::MethodCall(rcvr), - error, - Some(args), - expected, - false, - ) { - err.emit(); - } + if segment.ident.name == kw::Empty { + span_bug!(rcvr.span, "empty method name") + } else { + Err(self.report_method_error(expr.hir_id, rcvr_t, error, expected, false)) } - Err(()) } }; // Call the generic checker. - self.check_method_argument_types(span, expr, method, args, DontTupleArguments, expected) + self.check_method_argument_types( + segment.ident.span, + expr, + method, + args, + DontTupleArguments, + expected, + ) } fn check_expr_cast( @@ -1561,7 +1557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the length is 0, we don't create any elements, so we don't copy any. If the length is 1, we // don't copy that one element, we move it. Only check for Copy if the length is larger. - if count.try_eval_target_usize(tcx, self.param_env).map_or(true, |len| len > 1) { + if count.try_eval_target_usize(tcx, self.param_env).is_none_or(|len| len > 1) { let lang_item = self.tcx.require_lang_item(LangItem::Copy, None); let code = traits::ObligationCauseCode::RepeatElementCopy { is_constable, @@ -1692,6 +1688,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut error_happened = false; + if variant.fields.len() != remaining_fields.len() { + // Some field is defined more than once. Make sure we don't try to + // instantiate this struct in static/const context. + let guar = + self.dcx().span_delayed_bug(expr.span, "struct fields have non-unique names"); + self.set_tainted_by_errors(guar); + error_happened = true; + } + // Type-check each field. for (idx, field) in hir_fields.iter().enumerate() { let ident = tcx.adjust_ident(field.ident, variant.def_id); @@ -1733,10 +1738,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Make sure to give a type to the field even if there's // an error, so we can continue type-checking. let ty = self.check_expr_with_hint(field.expr, field_type); - let (_, diag) = - self.demand_coerce_diag(field.expr, ty, field_type, None, AllowTwoPhase::No); + let diag = self.demand_coerce_diag(field.expr, ty, field_type, None, AllowTwoPhase::No); - if let Some(diag) = diag { + if let Err(diag) = diag { if idx == hir_fields.len() - 1 { if remaining_fields.is_empty() { self.suggest_fru_from_range_and_emit(field, variant, args, diag); @@ -2014,10 +2018,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .shrink_to_hi() .to(range_end.span); - err.subdiagnostic( - self.dcx(), - TypeMismatchFruTypo { expr_span: range_start.span, fru_span, expr }, - ); + err.subdiagnostic(TypeMismatchFruTypo { expr_span: range_start.span, fru_span, expr }); // Suppress any range expr type mismatches self.dcx().try_steal_replace_and_emit_err( @@ -3067,7 +3068,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.commit_if_ok(|snapshot| { let outer_universe = self.universe(); - let ocx = ObligationCtxt::new(self); + let ocx = ObligationCtxt::new_with_diagnostics(self); let impl_args = self.fresh_args_for_item(base_expr.span, impl_def_id); let impl_trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(self.tcx, impl_args); @@ -3112,7 +3113,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let true_errors = ocx.select_where_possible(); - // Do a leak check -- we can't really report report a useful error here, + // Do a leak check -- we can't really report a useful error here, // but it at least avoids an ICE when the error has to do with higher-ranked // lifetimes. self.leak_check(outer_universe, Some(snapshot))?; @@ -3146,7 +3147,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error.obligation.predicate.kind().skip_binder(), ) { (ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)), _) - if self.tcx.lang_items().index_trait() == Some(predicate.trait_ref.def_id) => + if self.tcx.is_lang_item(predicate.trait_ref.def_id, LangItem::Index) => { seen_preds.insert(error.obligation.predicate.kind().skip_binder()); } @@ -3355,7 +3356,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let field_ty = self.field_ty(expr.span, field, args); - // FIXME: DSTs with static alignment should be allowed + // Enums are anyway always sized. But just to safeguard against future + // language extensions, let's double-check. self.require_type_is_sized(field_ty, expr.span, ObligationCauseCode::Misc); if field.vis.is_accessible_from(sub_def_scope, self.tcx) { @@ -3383,8 +3385,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let field_ty = self.field_ty(expr.span, field, args); - // FIXME: DSTs with static alignment should be allowed - self.require_type_is_sized(field_ty, expr.span, ObligationCauseCode::Misc); + if self.tcx.features().offset_of_slice { + self.require_type_has_static_alignment( + field_ty, + expr.span, + ObligationCauseCode::Misc, + ); + } else { + self.require_type_is_sized( + field_ty, + expr.span, + ObligationCauseCode::Misc, + ); + } if field.vis.is_accessible_from(def_scope, self.tcx) { self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None); @@ -3404,10 +3417,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Ok(index) = field.as_str().parse::() && field.name == sym::integer(index) { - for ty in tys.iter().take(index + 1) { - self.require_type_is_sized(ty, expr.span, ObligationCauseCode::Misc); - } if let Some(&field_ty) = tys.get(index) { + if self.tcx.features().offset_of_slice { + self.require_type_has_static_alignment( + field_ty, + expr.span, + ObligationCauseCode::Misc, + ); + } else { + self.require_type_is_sized( + field_ty, + expr.span, + ObligationCauseCode::Misc, + ); + } + field_indices.push((FIRST_VARIANT, index.into())); current_container = field_ty; diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 89f62577506f..0ba4bd090f5e 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -1741,7 +1741,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } PatKind::Slice(before, ref slice, after) => { - let Some(element_ty) = place_with_id.place.ty().builtin_index() else { + let Some(element_ty) = self + .cx + .try_structurally_resolve_type(pat.span, place_with_id.place.ty()) + .builtin_index() + else { debug!("explicit index of non-indexable type {:?}", place_with_id); return Err(self .cx diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index e456bd7fd4ab..3cecbfd42757 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -14,16 +14,17 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; use rustc_session::lint; use rustc_span::DUMMY_SP; use rustc_span::{def_id::LocalDefId, Span}; +use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; #[derive(Copy, Clone)] pub enum DivergingFallbackBehavior { /// Always fallback to `()` (aka "always spontaneous decay") - FallbackToUnit, + ToUnit, /// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken. - FallbackToNiko, + ContextDependent, /// Always fallback to `!` (which should be equivalent to never falling back + not making /// never-to-any coercions unless necessary) - FallbackToNever, + ToNever, /// Don't fallback at all NoFallback, } @@ -344,6 +345,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // `!`. let mut diverging_fallback = UnordMap::with_capacity(diverging_vids.len()); let unsafe_infer_vars = OnceCell::new(); + + self.lint_obligations_broken_by_never_type_fallback_change(behavior, &diverging_vids); + for &diverging_vid in &diverging_vids { let diverging_ty = Ty::new_var(self.tcx, diverging_vid); let root_vid = self.root_var(diverging_vid); @@ -364,52 +368,21 @@ impl<'tcx> FnCtxt<'_, 'tcx> { }; let mut fallback_to = |ty| { - let unsafe_infer_vars = unsafe_infer_vars.get_or_init(|| { - let unsafe_infer_vars = compute_unsafe_infer_vars(self.root_ctxt, self.body_id); - debug!(?unsafe_infer_vars); - unsafe_infer_vars - }); - - let affected_unsafe_infer_vars = - graph::depth_first_search_as_undirected(&coercion_graph, root_vid) - .filter_map(|x| unsafe_infer_vars.get(&x).copied()) - .collect::>(); - - for (hir_id, span, reason) in affected_unsafe_infer_vars { - self.tcx.emit_node_span_lint( - lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, - hir_id, - span, - match reason { - UnsafeUseReason::Call => { - errors::NeverTypeFallbackFlowingIntoUnsafe::Call - } - UnsafeUseReason::Method => { - errors::NeverTypeFallbackFlowingIntoUnsafe::Method - } - UnsafeUseReason::Path => { - errors::NeverTypeFallbackFlowingIntoUnsafe::Path - } - UnsafeUseReason::UnionField => { - errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField - } - UnsafeUseReason::Deref => { - errors::NeverTypeFallbackFlowingIntoUnsafe::Deref - } - }, - ); - } + self.lint_never_type_fallback_flowing_into_unsafe_code( + &unsafe_infer_vars, + &coercion_graph, + root_vid, + ); diverging_fallback.insert(diverging_ty, ty); }; - use DivergingFallbackBehavior::*; match behavior { - FallbackToUnit => { + DivergingFallbackBehavior::ToUnit => { debug!("fallback to () - legacy: {:?}", diverging_vid); fallback_to(self.tcx.types.unit); } - FallbackToNiko => { + DivergingFallbackBehavior::ContextDependent => { if found_infer_var_info.self_in_trait && found_infer_var_info.output { // This case falls back to () to ensure that the code pattern in // tests/ui/never_type/fallback-closure-ret.rs continues to @@ -445,14 +418,14 @@ impl<'tcx> FnCtxt<'_, 'tcx> { fallback_to(self.tcx.types.never); } } - FallbackToNever => { + DivergingFallbackBehavior::ToNever => { debug!( "fallback to ! - `rustc_never_type_mode = \"fallback_to_never\")`: {:?}", diverging_vid ); fallback_to(self.tcx.types.never); } - NoFallback => { + DivergingFallbackBehavior::NoFallback => { debug!( "no fallback - `rustc_never_type_mode = \"no_fallback\"`: {:?}", diverging_vid @@ -464,6 +437,91 @@ impl<'tcx> FnCtxt<'_, 'tcx> { diverging_fallback } + fn lint_never_type_fallback_flowing_into_unsafe_code( + &self, + unsafe_infer_vars: &OnceCell>, + coercion_graph: &VecGraph, + root_vid: ty::TyVid, + ) { + let unsafe_infer_vars = unsafe_infer_vars.get_or_init(|| { + let unsafe_infer_vars = compute_unsafe_infer_vars(self.root_ctxt, self.body_id); + debug!(?unsafe_infer_vars); + unsafe_infer_vars + }); + + let affected_unsafe_infer_vars = + graph::depth_first_search_as_undirected(&coercion_graph, root_vid) + .filter_map(|x| unsafe_infer_vars.get(&x).copied()) + .collect::>(); + + for (hir_id, span, reason) in affected_unsafe_infer_vars { + self.tcx.emit_node_span_lint( + lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, + hir_id, + span, + match reason { + UnsafeUseReason::Call => errors::NeverTypeFallbackFlowingIntoUnsafe::Call, + UnsafeUseReason::Method => errors::NeverTypeFallbackFlowingIntoUnsafe::Method, + UnsafeUseReason::Path => errors::NeverTypeFallbackFlowingIntoUnsafe::Path, + UnsafeUseReason::UnionField => { + errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField + } + UnsafeUseReason::Deref => errors::NeverTypeFallbackFlowingIntoUnsafe::Deref, + }, + ); + } + } + + fn lint_obligations_broken_by_never_type_fallback_change( + &self, + behavior: DivergingFallbackBehavior, + diverging_vids: &[ty::TyVid], + ) { + let DivergingFallbackBehavior::ToUnit = behavior else { return }; + + // Fallback happens if and only if there are diverging variables + if diverging_vids.is_empty() { + return; + } + + // Returns errors which happen if fallback is set to `fallback` + let remaining_errors_if_fallback_to = |fallback| { + self.probe(|_| { + let obligations = self.fulfillment_cx.borrow().pending_obligations(); + let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx); + ocx.register_obligations(obligations.iter().cloned()); + + for &diverging_vid in diverging_vids { + let diverging_ty = Ty::new_var(self.tcx, diverging_vid); + + ocx.eq(&ObligationCause::dummy(), self.param_env, diverging_ty, fallback) + .expect("expected diverging var to be unconstrained"); + } + + ocx.select_where_possible() + }) + }; + + // If we have no errors with `fallback = ()`, but *do* have errors with `fallback = !`, + // then this code will be broken by the never type fallback change.qba + let unit_errors = remaining_errors_if_fallback_to(self.tcx.types.unit); + if unit_errors.is_empty() + && let mut never_errors = remaining_errors_if_fallback_to(self.tcx.types.never) + && let [ref mut never_error, ..] = never_errors.as_mut_slice() + { + self.adjust_fulfillment_error_for_expr_obligation(never_error); + self.tcx.emit_node_span_lint( + lint::builtin::DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, + self.tcx.local_def_id_to_hir_id(self.body_id), + self.tcx.def_span(self.body_id), + errors::DependencyOnUnitNeverTypeFallback { + obligation_span: never_error.obligation.cause.span, + obligation: never_error.obligation.predicate, + }, + ) + } + } + /// Returns a graph whose nodes are (unresolved) inference variables and where /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`. fn create_coercion_graph(&self) -> VecGraph { @@ -539,9 +597,8 @@ fn compute_unsafe_infer_vars<'a, 'tcx>( root_ctxt: &'a TypeckRootCtxt<'tcx>, body_id: LocalDefId, ) -> UnordMap { - let body_id = + let body = root_ctxt.tcx.hir().maybe_body_owned_by(body_id).expect("body id must have an owner"); - let body = root_ctxt.tcx.hir().body(body_id); let mut res = UnordMap::default(); struct UnsafeInferVarsVisitor<'a, 'tcx, 'r> { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 6e8ef0444521..061afd030623 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1,6 +1,6 @@ use crate::callee::{self, DeferredCallResolution}; -use crate::errors::CtorIsPrivate; -use crate::method::{self, MethodCallee, SelfSource}; +use crate::errors::{self, CtorIsPrivate}; +use crate::method::{self, MethodCallee}; use crate::rvalue_scopes; use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy}; use rustc_data_structures::fx::FxHashSet; @@ -16,11 +16,12 @@ use rustc_hir_analysis::hir_ty_lowering::generics::{ }; use rustc_hir_analysis::hir_ty_lowering::{ ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgsLowerer, - GenericPathSegment, HirTyLowerer, IsMethodCall, + GenericPathSegment, HirTyLowerer, IsMethodCall, RegionInferReason, }; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; +use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; @@ -33,7 +34,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; -use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::symbol::{kw, sym}; use rustc_span::Span; use rustc_target::abi::FieldIdx; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; @@ -63,19 +64,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); let msg = format!("unreachable {kind}"); - self.tcx().node_span_lint( - lint::builtin::UNREACHABLE_CODE, - id, - span, - msg.clone(), - |lint| { - lint.span_label(span, msg).span_label( - orig_span, - custom_note - .unwrap_or("any code following this expression is unreachable"), - ); - }, - ) + self.tcx().node_span_lint(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { + lint.primary_message(msg.clone()); + lint.span_label(span, msg).span_label( + orig_span, + custom_note.unwrap_or("any code following this expression is unreachable"), + ); + }) } } } @@ -391,6 +386,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + pub fn require_type_has_static_alignment( + &self, + ty: Ty<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>, + ) { + if !ty.references_error() { + let tail = + self.tcx.struct_tail_with_normalize(ty, |ty| self.normalize(span, ty), || {}); + // Sized types have static alignment, and so do slices. + if tail.is_trivially_sized(self.tcx) || matches!(tail.kind(), ty::Slice(..)) { + // Nothing else is required here. + } else { + // We can't be sure, let's required full `Sized`. + let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); + self.require_type_meets(ty, span, code, lang_item); + } + } + } + pub fn register_bound( &self, ty: Ty<'tcx>, @@ -441,7 +456,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn lower_array_length(&self, length: &hir::ArrayLen<'tcx>) -> ty::Const<'tcx> { match length { - hir::ArrayLen::Infer(inf) => self.ct_infer(self.tcx.types.usize, None, inf.span), + hir::ArrayLen::Infer(inf) => self.ct_infer(None, inf.span), hir::ArrayLen::Body(anon_const) => { let span = self.tcx.def_span(anon_const.def_id); let c = ty::Const::from_anon_const(self.tcx, anon_const.def_id); @@ -645,8 +660,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) } - pub(crate) fn err_args(&self, len: usize) -> Vec> { - let ty_error = Ty::new_misc_error(self.tcx); + pub(crate) fn err_args(&self, len: usize, guar: ErrorGuaranteed) -> Vec> { + let ty_error = Ty::new_error(self.tcx, guar); vec![ty_error; len] } @@ -735,17 +750,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Resolves an associated value path into a base type and associated constant, or method /// resolution. The newly resolved definition is written into `type_dependent_defs`. + #[instrument(level = "trace", skip(self), ret)] pub fn resolve_ty_and_res_fully_qualified_call( &self, qpath: &'tcx QPath<'tcx>, hir_id: HirId, span: Span, - args: Option<&'tcx [hir::Expr<'tcx>]>, ) -> (Res, Option>, &'tcx [hir::PathSegment<'tcx>]) { - debug!( - "resolve_ty_and_res_fully_qualified_call: qpath={:?} hir_id={:?} span={:?}", - qpath, hir_id, span - ); let (ty, qself, item_segment) = match *qpath { QPath::Resolved(ref opt_qself, path) => { return ( @@ -832,18 +843,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if item_name.name != kw::Empty { - if let Some(e) = self.report_method_error( - span, + self.report_method_error( + hir_id, ty.normalized, - item_name, - SelfSource::QPath(qself), error, - args, Expectation::NoExpectation, trait_missing_method && span.edition().at_least_rust_2021(), // emits missing method for trait only after edition 2021 - ) { - e.emit(); - } + ); } result @@ -866,76 +872,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } - /// Given a function `Node`, return its `HirId` and `FnDecl` if it exists. Given a closure - /// that is the child of a function, return that function's `HirId` and `FnDecl` instead. - /// This may seem confusing at first, but this is used in diagnostics for `async fn`, - /// for example, where most of the type checking actually happens within a nested closure, - /// but we often want access to the parent function's signature. - /// - /// Otherwise, return false. - pub(crate) fn get_node_fn_decl( - &self, - node: Node<'tcx>, - ) -> Option<(LocalDefId, &'tcx hir::FnDecl<'tcx>, Ident, bool)> { - match node { - Node::Item(&hir::Item { - ident, - kind: hir::ItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => { - // This is less than ideal, it will not suggest a return type span on any - // method called `main`, regardless of whether it is actually the entry point, - // but it will still present it as the reason for the expected type. - Some((owner_id.def_id, &sig.decl, ident, ident.name != sym::main)) - } - Node::TraitItem(&hir::TraitItem { - ident, - kind: hir::TraitItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => Some((owner_id.def_id, &sig.decl, ident, true)), - Node::ImplItem(&hir::ImplItem { - ident, - kind: hir::ImplItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => Some((owner_id.def_id, &sig.decl, ident, false)), - Node::Expr(&hir::Expr { - hir_id, - kind: - hir::ExprKind::Closure(hir::Closure { - kind: hir::ClosureKind::Coroutine(..), .. - }), - .. - }) => { - let (ident, sig, owner_id) = match self.tcx.parent_hir_node(hir_id) { - Node::Item(&hir::Item { - ident, - kind: hir::ItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => (ident, sig, owner_id), - Node::TraitItem(&hir::TraitItem { - ident, - kind: hir::TraitItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => (ident, sig, owner_id), - Node::ImplItem(&hir::ImplItem { - ident, - kind: hir::ImplItemKind::Fn(ref sig, ..), - owner_id, - .. - }) => (ident, sig, owner_id), - _ => return None, - }; - Some((owner_id.def_id, &sig.decl, ident, ident.name != sym::main)) - } - _ => None, - } - } - /// Given a `HirId`, return the `HirId` of the enclosing function, its `FnDecl`, and whether a /// suggestion can be made, `None` otherwise. pub fn get_fn_decl( @@ -944,10 +880,73 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Option<(LocalDefId, &'tcx hir::FnDecl<'tcx>, bool)> { // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or // `while` before reaching it, as block tail returns are not available in them. - self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| { - let parent = self.tcx.hir_node(blk_id); - self.get_node_fn_decl(parent) - .map(|(fn_id, fn_decl, _, is_main)| (fn_id, fn_decl, is_main)) + self.tcx.hir().get_fn_id_for_return_block(blk_id).and_then(|item_id| { + match self.tcx.hir_node(item_id) { + Node::Item(&hir::Item { + ident, + kind: hir::ItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => { + // This is less than ideal, it will not suggest a return type span on any + // method called `main`, regardless of whether it is actually the entry point, + // but it will still present it as the reason for the expected type. + Some((owner_id.def_id, sig.decl, ident.name != sym::main)) + } + Node::TraitItem(&hir::TraitItem { + kind: hir::TraitItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => Some((owner_id.def_id, sig.decl, true)), + // FIXME: Suggestable if this is not a trait implementation + Node::ImplItem(&hir::ImplItem { + kind: hir::ImplItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => Some((owner_id.def_id, sig.decl, false)), + Node::Expr(&hir::Expr { + hir_id, + kind: hir::ExprKind::Closure(&hir::Closure { def_id, kind, fn_decl, .. }), + .. + }) => { + match kind { + hir::ClosureKind::CoroutineClosure(_) => { + // FIXME(async_closures): Implement this. + return None; + } + hir::ClosureKind::Closure => Some((def_id, fn_decl, true)), + hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared( + _, + hir::CoroutineSource::Fn, + )) => { + let (ident, sig, owner_id) = match self.tcx.parent_hir_node(hir_id) { + Node::Item(&hir::Item { + ident, + kind: hir::ItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => (ident, sig, owner_id), + Node::TraitItem(&hir::TraitItem { + ident, + kind: hir::TraitItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => (ident, sig, owner_id), + Node::ImplItem(&hir::ImplItem { + ident, + kind: hir::ImplItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => (ident, sig, owner_id), + _ => return None, + }; + Some((owner_id.def_id, sig.decl, ident.name != sym::main)) + } + _ => None, + } + } + _ => None, + } }) } @@ -1082,7 +1081,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::ImplContainer => { if segments.len() == 1 { // `::assoc` will end up here, and so - // can `T::assoc`. It this came from an + // can `T::assoc`. If this came from an // inherent impl, we need to record the // `T` for posterity (see `UserSelfTy` for // details). @@ -1128,7 +1127,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // to add defaults. If the user provided *too many* types, that's // a problem. - let mut infer_args_for_err = FxHashSet::default(); + let mut infer_args_for_err = None; let mut explicit_late_bound = ExplicitLateBound::No; for &GenericPathSegment(def_id, index) in &generic_segments { @@ -1146,9 +1145,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { explicit_late_bound = ExplicitLateBound::Yes; } - if let Err(GenericArgCountMismatch { reported: Some(e), .. }) = arg_count.correct { - infer_args_for_err.insert(index); - self.set_tainted_by_errors(e); // See issue #53251. + if let Err(GenericArgCountMismatch { reported, .. }) = arg_count.correct { + infer_args_for_err + .get_or_insert_with(|| (reported, FxHashSet::default())) + .1 + .insert(index); + self.set_tainted_by_errors(reported); // See issue #53251. } } @@ -1162,6 +1164,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, tcx.at(span).type_of(impl_def_id).instantiate_identity(), ); + + // Firstly, check that this SelfCtor even comes from the item we're currently + // typechecking. This can happen because we never validated the resolution of + // SelfCtors, and when we started doing so, we noticed regressions. After + // sufficiently long time, we can remove this check and turn it into a hard + // error in `validate_res_from_ribs` -- it's just difficult to tell whether the + // self type has any generic types during rustc_resolve, which is what we use + // to determine if this is a hard error or warning. + if std::iter::successors(Some(self.body_id.to_def_id()), |def_id| { + self.tcx.generics_of(def_id).parent + }) + .all(|def_id| def_id != impl_def_id) + { + let sugg = ty.normalized.ty_adt_def().map(|def| errors::ReplaceWithName { + span: path_span, + name: self.tcx.item_name(def.did()).to_ident_string(), + }); + if ty.raw.has_param() { + let guar = self.tcx.dcx().emit_err(errors::SelfCtorFromOuterItem { + span: path_span, + impl_span: tcx.def_span(impl_def_id), + sugg, + }); + return (Ty::new_error(self.tcx, guar), res); + } else { + self.tcx.emit_node_span_lint( + SELF_CONSTRUCTOR_FROM_OUTER_ITEM, + hir_id, + path_span, + errors::SelfCtorFromOuterItemLint { + impl_span: tcx.def_span(impl_def_id), + sugg, + }, + ); + } + } + match ty.normalized.ty_adt_def() { Some(adt_def) if adt_def.has_ctor() => { let (ctor_kind, ctor_def_id) = adt_def.non_enum_variant().ctor.unwrap(); @@ -1205,15 +1244,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let def_id = res.def_id(); - let arg_count = GenericArgCountResult { - explicit_late_bound, - correct: if infer_args_for_err.is_empty() { - Ok(()) - } else { - Err(GenericArgCountMismatch::default()) - }, + let (correct, infer_args_for_err) = match infer_args_for_err { + Some((reported, args)) => { + (Err(GenericArgCountMismatch { reported, invalid_args: vec![] }), args) + } + None => (Ok(()), Default::default()), }; + let arg_count = GenericArgCountResult { explicit_late_bound, correct }; + struct CtorGenericArgsCtxt<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, span: Span, @@ -1245,13 +1284,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn provided_kind( &mut self, + _preceding_args: &[ty::GenericArg<'tcx>], param: &ty::GenericParamDef, arg: &GenericArg<'tcx>, ) -> ty::GenericArg<'tcx> { match (¶m.kind, arg) { - (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - self.fcx.lowerer().lower_lifetime(lt, Some(param)).into() - } + (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => self + .fcx + .lowerer() + .lower_lifetime(lt, RegionInferReason::Param(param)) + .into(), (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { self.fcx.lower_ty(ty).raw.into() } @@ -1265,20 +1307,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &GenericParamDefKind::Const { has_default, is_host_effect }, GenericArg::Infer(inf), ) => { - let tcx = self.fcx.tcx(); - if has_default && is_host_effect { self.fcx.var_for_effect(param) } else { - self.fcx - .ct_infer( - tcx.type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"), - Some(param), - inf.span, - ) - .into() + self.fcx.ct_infer(Some(param), inf.span).into() } } _ => unreachable!(), @@ -1287,21 +1319,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn inferred_kind( &mut self, - args: Option<&[ty::GenericArg<'tcx>]>, + preceding_args: &[ty::GenericArg<'tcx>], param: &ty::GenericParamDef, infer_args: bool, ) -> ty::GenericArg<'tcx> { let tcx = self.fcx.tcx(); match param.kind { - GenericParamDefKind::Lifetime => { - self.fcx.re_infer(Some(param), self.span).unwrap().into() - } + GenericParamDefKind::Lifetime => self + .fcx + .re_infer( + self.span, + rustc_hir_analysis::hir_ty_lowering::RegionInferReason::Param(param), + ) + .into(), GenericParamDefKind::Type { has_default, .. } => { if !infer_args && has_default { // If we have a default, then it doesn't matter that we're not // inferring the type arguments: we provide the default where any // is missing. - tcx.type_of(param.def_id).instantiate(tcx, args.unwrap()).into() + tcx.type_of(param.def_id).instantiate(tcx, preceding_args).into() } else { // If no type arguments were provided, we have to infer them. // This case also occurs as a result of some malformed input, e.g. @@ -1326,7 +1362,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if !infer_args { return tcx .const_param_default(param.def_id) - .instantiate(tcx, args.unwrap()) + .instantiate(tcx, preceding_args) .into(); } } @@ -1378,18 +1414,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This also occurs for an enum variant on a type alias. let impl_ty = self.normalize(span, tcx.type_of(impl_def_id).instantiate(tcx, args)); let self_ty = self.normalize(span, self_ty); - match self.at(&self.misc(span), self.param_env).eq( - DefineOpaqueTypes::No, - impl_ty, + match self.at(&self.misc(span), self.param_env).sub( + DefineOpaqueTypes::Yes, self_ty, + impl_ty, ) { Ok(ok) => self.register_infer_ok_obligations(ok), Err(_) => { - self.dcx().span_delayed_bug( + self.dcx().span_bug( span, format!( - "instantiate_value_path: (UFCS) {self_ty:?} was a subtype of {impl_ty:?} but now is not?", - ), + "instantiate_value_path: (UFCS) {self_ty:?} was a subtype of {impl_ty:?} but now is not?", + ), ); } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index aea34407a2d5..1138642c56d6 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -113,17 +113,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, sp: Span, expr: &'tcx hir::Expr<'tcx>, - method: Result, ()>, + method: Result, ErrorGuaranteed>, args_no_rcvr: &'tcx [hir::Expr<'tcx>], tuple_arguments: TupleArgumentsFlag, expected: Expectation<'tcx>, ) -> Ty<'tcx> { let has_error = match method { - Ok(method) => method.args.references_error() || method.sig.references_error(), - Err(_) => true, + Ok(method) => method.args.error_reported().and(method.sig.error_reported()), + Err(guar) => Err(guar), }; - if has_error { - let err_inputs = self.err_args(args_no_rcvr.len()); + if let Err(guar) = has_error { + let err_inputs = self.err_args(args_no_rcvr.len(), guar); let err_inputs = match tuple_arguments { DontTupleArguments => err_inputs, @@ -140,7 +140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_arguments, method.ok().map(|method| method.def_id), ); - return Ty::new_misc_error(self.tcx); + return Ty::new_error(self.tcx, guar); } let method = method.unwrap(); @@ -237,7 +237,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => { // Otherwise, there's a mismatch, so clear out what we're expecting, and set // our input types to err_args so we don't blow up the error messages - struct_span_code_err!( + let guar = struct_span_code_err!( tcx.dcx(), call_span, E0059, @@ -245,7 +245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for the function trait is neither a tuple nor unit" ) .emit(); - (self.err_args(provided_args.len()), None) + (self.err_args(provided_args.len(), guar), None) } } } else { @@ -1435,7 +1435,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { // The user provided `ptr::null()`, but the function expects // `ptr::null_mut()`. - err.subdiagnostic(self.dcx(), SuggestPtrNullMut { span: arg.span }); + err.subdiagnostic(SuggestPtrNullMut { span: arg.span }); } } @@ -1578,7 +1578,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // type of the place it is referencing, and not some // supertype thereof. let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); - if let Some(mut diag) = self.demand_eqtype_diag(init.span, local_ty, init_ty) { + if let Err(mut diag) = self.demand_eqtype_diag(init.span, local_ty, init_ty) { self.emit_type_mismatch_suggestions( &mut diag, init.peel_drop_temps(), @@ -1624,7 +1624,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let previous_diverges = self.diverges.get(); let else_ty = self.check_block_with_expected(blk, NoExpectation); let cause = self.cause(blk.span, ObligationCauseCode::LetElse); - if let Some(err) = self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) + if let Err(err) = self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) { err.emit(); } @@ -1774,7 +1774,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // that highlight errors inline. let mut sp = blk.span; let mut fn_span = None; - if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) { + if let Some((fn_def_id, decl, _)) = self.get_fn_decl(blk.hir_id) { let ret_sp = decl.output.span(); if let Some(block_sp) = self.parent_item_span(blk.hir_id) { // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the @@ -1782,7 +1782,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the span we're aiming at correspond to a `fn` body. if block_sp == blk.span { sp = ret_sp; - fn_span = Some(ident.span); + fn_span = self.tcx.def_ident_span(fn_def_id); } } } @@ -1897,15 +1897,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } - /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. - pub(crate) fn get_parent_fn_decl( - &self, - blk_id: HirId, - ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { - let parent = self.tcx.hir_node_by_def_id(self.tcx.hir().get_parent_item(blk_id).def_id); - self.get_node_fn_decl(parent).map(|(_, fn_decl, ident, _)| (fn_decl, ident)) - } - /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors /// when given code like the following: @@ -2249,7 +2240,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (idx, (generic_param, param)) in params_with_generics.iter().enumerate().filter(|(idx, _)| { check_for_matched_generics - || expected_idx.map_or(true, |expected_idx| expected_idx == *idx) + || expected_idx.is_none_or(|expected_idx| expected_idx == *idx) }) { let Some(generic_param) = generic_param else { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index afba812a8e7b..1713d75092e2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -5,17 +5,16 @@ mod checks; mod inspect_obligations; mod suggestions; -use rustc_errors::ErrorGuaranteed; +use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed}; use crate::coercion::DynamicCoerceMany; use crate::fallback::DivergingFallbackBehavior; use crate::fn_ctxt::checks::DivergingBlockBehavior; use crate::{CoroutineTypes, Diverges, EnclosingBreakables, TypeckRootCtxt}; use hir::def_id::CRATE_DEF_ID; -use rustc_errors::DiagCtxt; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; +use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, RegionInferReason}; use rustc_infer::infer; use rustc_infer::infer::error_reporting::sub_relations::SubRelations; use rustc_infer::infer::error_reporting::TypeErrCtxt; @@ -124,7 +123,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { body_id: LocalDefId, ) -> FnCtxt<'a, 'tcx> { let (diverging_fallback_behavior, diverging_block_behavior) = - parse_never_type_options_attr(root_ctxt.tcx); + never_type_behavior(root_ctxt.tcx); FnCtxt { body_id, param_env, @@ -145,8 +144,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub(crate) fn dcx(&self) -> &'tcx DiagCtxt { - self.tcx.dcx() + pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> { + self.infcx.dcx() } pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> { @@ -213,25 +212,21 @@ impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { } } -impl<'a, 'tcx> HirTyLowerer<'tcx> for FnCtxt<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { +impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } - fn item_def_id(&self) -> DefId { - self.body_id.to_def_id() + fn item_def_id(&self) -> LocalDefId { + self.body_id } - fn allow_infer(&self) -> bool { - true - } - - fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option> { - let v = match def { - Some(def) => infer::RegionParameterDefinition(span, def.name), - None => infer::MiscVariable(span), + fn re_infer(&self, span: Span, reason: RegionInferReason<'_>) -> ty::Region<'tcx> { + let v = match reason { + RegionInferReason::Param(def) => infer::RegionParameterDefinition(span, def.name), + _ => infer::MiscVariable(span), }; - Some(self.next_region_var(v)) + self.next_region_var(v) } fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { @@ -241,12 +236,7 @@ impl<'a, 'tcx> HirTyLowerer<'tcx> for FnCtxt<'a, 'tcx> { } } - fn ct_infer( - &self, - ty: Ty<'tcx>, - param: Option<&ty::GenericParamDef>, - span: Span, - ) -> Const<'tcx> { + fn ct_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx> { // FIXME ideally this shouldn't use unwrap match param { Some( @@ -256,7 +246,7 @@ impl<'a, 'tcx> HirTyLowerer<'tcx> for FnCtxt<'a, 'tcx> { }, ) => self.var_for_effect(param).as_const().unwrap(), Some(param) => self.var_for_def(span, param).as_const().unwrap(), - None => self.next_const_var(ty, span), + None => self.next_const_var(span), } } @@ -350,6 +340,22 @@ impl<'a, 'tcx> HirTyLowerer<'tcx> for FnCtxt<'a, 'tcx> { fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { self.infcx.set_tainted_by_errors(e) } + + fn lower_fn_sig( + &self, + decl: &rustc_hir::FnDecl<'tcx>, + _generics: Option<&rustc_hir::Generics<'_>>, + _hir_id: rustc_hir::HirId, + _hir_ty: Option<&hir::Ty<'_>>, + ) -> (Vec>, Ty<'tcx>) { + let input_tys = decl.inputs.iter().map(|a| self.lowerer().lower_arg_ty(a, None)).collect(); + + let output_ty = match decl.output { + hir::FnRetTy::Return(output) => self.lowerer().lower_ty(output), + hir::FnRetTy::DefaultReturn(..) => self.tcx().types.unit, + }; + (input_tys, output_ty) + } } /// The `ty` representation of a user-provided type. Depending on the use-site @@ -380,11 +386,33 @@ impl<'tcx> LoweredTy<'tcx> { } } +fn never_type_behavior(tcx: TyCtxt<'_>) -> (DivergingFallbackBehavior, DivergingBlockBehavior) { + let (fallback, block) = parse_never_type_options_attr(tcx); + let fallback = fallback.unwrap_or_else(|| default_fallback(tcx)); + let block = block.unwrap_or_default(); + + (fallback, block) +} + +/// Returns the default fallback which is used when there is no explicit override via `#![never_type_options(...)]`. +fn default_fallback(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior { + // Edition 2024: fallback to `!` + if tcx.sess.edition().at_least_rust_2024() { + return DivergingFallbackBehavior::ToNever; + } + + // `feature(never_type_fallback)`: fallback to `!` or `()` trying to not break stuff + if tcx.features().never_type_fallback { + return DivergingFallbackBehavior::ContextDependent; + } + + // Otherwise: fallback to `()` + DivergingFallbackBehavior::ToUnit +} + fn parse_never_type_options_attr( tcx: TyCtxt<'_>, -) -> (DivergingFallbackBehavior, DivergingBlockBehavior) { - use DivergingFallbackBehavior::*; - +) -> (Option, Option) { // Error handling is dubious here (unwraps), but that's probably fine for an internal attribute. // Just don't write incorrect attributes <3 @@ -400,10 +428,10 @@ fn parse_never_type_options_attr( if item.has_name(sym::fallback) && fallback.is_none() { let mode = item.value_str().unwrap(); match mode { - sym::unit => fallback = Some(FallbackToUnit), - sym::niko => fallback = Some(FallbackToNiko), - sym::never => fallback = Some(FallbackToNever), - sym::no => fallback = Some(NoFallback), + sym::unit => fallback = Some(DivergingFallbackBehavior::ToUnit), + sym::niko => fallback = Some(DivergingFallbackBehavior::ContextDependent), + sym::never => fallback = Some(DivergingFallbackBehavior::ToNever), + sym::no => fallback = Some(DivergingFallbackBehavior::NoFallback), _ => { tcx.dcx().span_err(item.span(), format!("unknown never type fallback mode: `{mode}` (supported: `unit`, `niko`, `never` and `no`)")); } @@ -432,11 +460,5 @@ fn parse_never_type_options_attr( ); } - let fallback = fallback.unwrap_or_else(|| { - if tcx.features().never_type_fallback { FallbackToNiko } else { FallbackToUnit } - }); - - let block = block.unwrap_or_default(); - (fallback, block) } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 56e13cd679d4..9dd82868adc5 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -22,7 +22,6 @@ use rustc_hir::{ }; use rustc_hir_analysis::collect::suggest_impl_trait; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; -use rustc_infer::traits; use rustc_middle::lint::in_external_macro; use rustc_middle::middle::stability::EvalResult; use rustc_middle::span_bug; @@ -36,6 +35,7 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -440,7 +440,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // Given `Result<_, E>`, check our expected ty is `Result<_, &E>` for // `as_ref` and `as_deref` compatibility. - let error_tys_equate_as_ref = error_tys.map_or(true, |(found, expected)| { + let error_tys_equate_as_ref = error_tys.is_none_or(|(found, expected)| { self.can_eq( self.param_env, Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, found), @@ -460,16 +460,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // but those checks need to be a bit more delicate and the benefit is diminishing. if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref { let sugg = prefix_wrap(".as_ref()"); - err.subdiagnostic( - self.dcx(), - errors::SuggestConvertViaMethod { - span: expr.span.shrink_to_hi(), - sugg, - expected, - found, - borrow_removal_span, - }, - ); + err.subdiagnostic(errors::SuggestConvertViaMethod { + span: expr.span.shrink_to_hi(), + sugg, + expected, + found, + borrow_removal_span, + }); return true; } else if let Some((deref_ty, _)) = self.autoderef(expr.span, found_ty_inner).silence_errors().nth(1) @@ -477,22 +474,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && error_tys_equate_as_ref { let sugg = prefix_wrap(".as_deref()"); - err.subdiagnostic( - self.dcx(), - errors::SuggestConvertViaMethod { - span: expr.span.shrink_to_hi(), - sugg, - expected, - found, - borrow_removal_span, - }, - ); + err.subdiagnostic(errors::SuggestConvertViaMethod { + span: expr.span.shrink_to_hi(), + sugg, + expected, + found, + borrow_removal_span, + }); return true; } else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind() - && Some(adt.did()) == self.tcx.lang_items().string() + && self.tcx.is_lang_item(adt.did(), LangItem::String) && peeled.is_str() // `Result::map`, conversely, does not take ref of the error type. - && error_tys.map_or(true, |(found, expected)| { + && error_tys.is_none_or(|(found, expected)| { self.can_eq(self.param_env, found, expected) }) { @@ -573,7 +567,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { end: span.shrink_to_hi(), }, }; - err.subdiagnostic(self.dcx(), suggest_boxing); + err.subdiagnostic(suggest_boxing); true } else { @@ -800,6 +794,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { can_suggest: bool, fn_id: LocalDefId, ) -> bool { + // Can't suggest `->` on a block-like coroutine + if let Some(hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Block)) = + self.tcx.coroutine_kind(fn_id) + { + return false; + } + let found = self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found)); // Only suggest changing the return type for methods that @@ -807,28 +808,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match &fn_decl.output { &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => { // `fn main()` must return `()`, do not suggest changing return type - err.subdiagnostic(self.dcx(), errors::ExpectedReturnTypeLabel::Unit { span }); + err.subdiagnostic(errors::ExpectedReturnTypeLabel::Unit { span }); return true; } &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => { if let Some(found) = found.make_suggestable(self.tcx, false, None) { - err.subdiagnostic( - self.dcx(), - errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() }, - ); + err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { + span, + found: found.to_string(), + }); return true; } else if let Some(sugg) = suggest_impl_trait(self, self.param_env, found) { - err.subdiagnostic( - self.dcx(), - errors::AddReturnTypeSuggestion::Add { span, found: sugg }, - ); + err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: sugg }); return true; } else { // FIXME: if `found` could be `impl Iterator` we should suggest that. - err.subdiagnostic( - self.dcx(), - errors::AddReturnTypeSuggestion::MissingHere { span }, - ); + err.subdiagnostic(errors::AddReturnTypeSuggestion::MissingHere { span }); return true; } } @@ -841,51 +836,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let [hir::GenericBound::Trait(trait_ref, _)] = op_ty.bounds && let Some(hir::PathSegment { args: Some(generic_args), .. }) = trait_ref.trait_ref.path.segments.last() - && let hir::GenericArgs { bindings: [ty_binding], .. } = generic_args - && let hir::TypeBindingKind::Equality { term: hir::Term::Ty(term) } = - ty_binding.kind + && let [constraint] = generic_args.constraints + && let Some(ty) = constraint.ty() { // Check if async function's return type was omitted. // Don't emit suggestions if the found type is `impl Future<...>`. debug!(?found); if found.is_suggestable(self.tcx, false) { - if term.span.is_empty() { - err.subdiagnostic( - self.dcx(), - errors::AddReturnTypeSuggestion::Add { - span: term.span, - found: found.to_string(), - }, - ); + if ty.span.is_empty() { + err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { + span: ty.span, + found: found.to_string(), + }); return true; } else { - err.subdiagnostic( - self.dcx(), - errors::ExpectedReturnTypeLabel::Other { - span: term.span, - expected, - }, - ); + err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other { + span: ty.span, + expected, + }); } } } else { // Only point to return type if the expected type is the return type, as if they // are not, the expectation must have been caused by something else. - debug!("return type {:?}", hir_ty); + debug!(?hir_ty, "return type"); let ty = self.lowerer().lower_ty(hir_ty); - debug!("return type {:?}", ty); - debug!("expected type {:?}", expected); + debug!(?ty, "return type (lowered)"); + debug!(?expected, "expected type"); let bound_vars = self.tcx.late_bound_vars(hir_ty.hir_id.owner.into()); let ty = Binder::bind_with_vars(ty, bound_vars); let ty = self.normalize(hir_ty.span, ty); let ty = self.tcx.instantiate_bound_regions_with_erased(ty); if self.can_coerce(expected, ty) { - err.subdiagnostic( - self.dcx(), - errors::ExpectedReturnTypeLabel::Other { span: hir_ty.span, expected }, - ); + err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other { + span: hir_ty.span, + expected, + }); self.try_suggest_return_impl_trait(err, expected, found, fn_id); - self.note_caller_chooses_ty_for_ty_param(err, expected, found); + self.try_note_caller_chooses_ty_for_ty_param(err, expected, found); return true; } } @@ -895,21 +883,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - fn note_caller_chooses_ty_for_ty_param( + fn try_note_caller_chooses_ty_for_ty_param( &self, diag: &mut Diag<'_>, expected: Ty<'tcx>, found: Ty<'tcx>, ) { - if let ty::Param(expected_ty_as_param) = expected.kind() { - diag.subdiagnostic( - self.dcx(), - errors::NoteCallerChoosesTyForTyParam { - ty_param_name: expected_ty_as_param.name, - found_ty: found, - }, - ); + // Only show the note if: + // 1. `expected` ty is a type parameter; + // 2. The `expected` type parameter does *not* occur in the return expression type. This can + // happen for e.g. `fn foo(t: &T) -> T { t }`, where `expected` is `T` but `found` is + // `&T`. Saying "the caller chooses a type for `T` which can be different from `&T`" is + // "well duh" and is only confusing and not helpful. + let ty::Param(expected_ty_as_param) = expected.kind() else { + return; + }; + + if found.contains(expected) { + return; } + + diag.subdiagnostic(errors::NoteCallerChoosesTyForTyParam { + ty_param_name: expected_ty_as_param.name, + found_ty: found, + }); } /// check whether the return type is a generic type with a trait bound @@ -1133,7 +1130,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let sp = self.tcx.sess.source_map().start_point(expr.span).with_parent(None); if let Some(sp) = self.tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp) { // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` - err.subdiagnostic(self.dcx(), ExprParenthesesNeeded::surrounding(*sp)); + err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); true } else { false @@ -1247,7 +1244,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { return false; }; - diag.subdiagnostic(self.dcx(), subdiag); + diag.subdiagnostic(subdiag); return true; } } @@ -1909,7 +1906,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let returned = matches!( self.tcx.parent_hir_node(expr.hir_id), hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. }) - ) || map.get_return_block(expr.hir_id).is_some(); + ) || map.get_fn_id_for_return_block(expr.hir_id).is_some(); if returned && let ty::Adt(e, args_e) = expected.kind() && let ty::Adt(f, args_f) = found.kind() @@ -1962,8 +1959,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { *expr } else { let body_def_id = hir.enclosing_body_owner(expr.hir_id); - let body_id = hir.body_owned_by(body_def_id); - let body = hir.body(body_id); + let body = hir.body_owned_by(body_def_id); // Get tail expr of the body match body.value.kind { @@ -3145,7 +3141,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } if let ty::Adt(adt, _) = expected_ty.kind() - && self.tcx.lang_items().range_struct() == Some(adt.did()) + && self.tcx.is_lang_item(adt.did(), LangItem::Range) { return; } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index b9a99fbec3cd..a87ee7b45548 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -1,11 +1,14 @@ +// tidy-alphabetical-start #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -#![feature(if_let_guard)] -#![feature(let_chains)] -#![feature(try_blocks)] -#![feature(never_type)] #![feature(box_patterns)] #![feature(control_flow_enum)] +#![feature(if_let_guard)] +#![feature(is_none_or)] +#![feature(let_chains)] +#![feature(never_type)] +#![feature(try_blocks)] +// tidy-alphabetical-end #[macro_use] extern crate tracing; @@ -235,11 +238,17 @@ fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Opti if let Some(item) = tcx.opt_associated_item(def_id.into()) && let ty::AssocKind::Const = item.kind && let ty::ImplContainer = item.container - && let Some(trait_item) = item.trait_item_def_id + && let Some(trait_item_def_id) = item.trait_item_def_id { - let args = - tcx.impl_trait_ref(item.container_id(tcx)).unwrap().instantiate_identity().args; - Some(tcx.type_of(trait_item).instantiate(tcx, args)) + let impl_def_id = item.container_id(tcx); + let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(); + let args = ty::GenericArgs::identity_for_item(tcx, def_id).rebase_onto( + tcx, + impl_def_id, + impl_trait_ref.args, + ); + tcx.check_args_compatible(trait_item_def_id, args) + .then(|| tcx.type_of(trait_item_def_id).instantiate(tcx, args)) } else { Some(fcx.next_ty_var(span)) } diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 742540585348..120e3239d1f1 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -7,7 +7,9 @@ use rustc_hir::GenericArg; use rustc_hir_analysis::hir_ty_lowering::generics::{ check_generic_arg_count_for_call, lower_generic_args, }; -use rustc_hir_analysis::hir_ty_lowering::{GenericArgsLowerer, HirTyLowerer, IsMethodCall}; +use rustc_hir_analysis::hir_ty_lowering::{ + GenericArgsLowerer, HirTyLowerer, IsMethodCall, RegionInferReason, +}; use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk}; use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; @@ -383,13 +385,17 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { fn provided_kind( &mut self, + _preceding_args: &[ty::GenericArg<'tcx>], param: &ty::GenericParamDef, arg: &GenericArg<'tcx>, ) -> ty::GenericArg<'tcx> { match (¶m.kind, arg) { - (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - self.cfcx.fcx.lowerer().lower_lifetime(lt, Some(param)).into() - } + (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => self + .cfcx + .fcx + .lowerer() + .lower_lifetime(lt, RegionInferReason::Param(param)) + .into(), (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { self.cfcx.lower_ty(ty).raw.into() } @@ -400,16 +406,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.cfcx.ty_infer(Some(param), inf.span).into() } (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { - let tcx = self.cfcx.tcx(); - self.cfcx - .ct_infer( - tcx.type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"), - Some(param), - inf.span, - ) - .into() + self.cfcx.ct_infer(Some(param), inf.span).into() } (kind, arg) => { bug!("mismatched method arg kind {kind:?} in turbofish: {arg:?}") @@ -419,7 +416,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { fn inferred_kind( &mut self, - _args: Option<&[ty::GenericArg<'tcx>]>, + _preceding_args: &[ty::GenericArg<'tcx>], param: &ty::GenericParamDef, _infer_args: bool, ) -> ty::GenericArg<'tcx> { @@ -451,7 +448,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // `foo.bar::(...)` -- the `Self` type here will be the // type of `foo` (possibly adjusted), but we don't want to // include that. We want just the `[_, u32]` part. - if !args.is_empty() && !generics.own_params.is_empty() { + if !args.is_empty() && !generics.is_own_empty() { let user_type_annotation = self.probe(|_| { let user_args = UserArgs { args: GenericArgs::for_item(self.tcx, pick.item.def_id, |param, _| { @@ -500,7 +497,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { args, })), ); - match self.at(&cause, self.param_env).sup(DefineOpaqueTypes::No, method_self_ty, self_ty) { + match self.at(&cause, self.param_env).sup(DefineOpaqueTypes::Yes, method_self_ty, self_ty) { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); } diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index a40fa600c19e..1f90d5e4c88a 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -3,11 +3,10 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/method-lookup.html mod confirm; -mod prelude2021; +mod prelude_edition_lints; pub mod probe; mod suggest; -pub use self::suggest::SelfSource; pub use self::MethodError::*; use crate::FnCtxt; @@ -186,7 +185,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pick = self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; - self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args); + self.lint_edition_dependent_dot_call( + self_ty, segment, span, call_expr, self_expr, &pick, args, + ); for &import_id in &pick.import_ids { debug!("used_trait_import: {:?}", import_id); diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs similarity index 75% rename from compiler/rustc_hir_typeck/src/method/prelude2021.rs rename to compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs index a305461344d4..3ee10f74d98a 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude2021.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs @@ -1,12 +1,12 @@ -use crate::{ - method::probe::{self, Pick}, - FnCtxt, -}; +use crate::method::probe::{self, Pick}; +use crate::FnCtxt; + use hir::def_id::DefId; use hir::HirId; use hir::ItemKind; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_lint::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER}; use rustc_middle::span_bug; use rustc_middle::ty::{self, Ty}; use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS; @@ -17,7 +17,7 @@ use rustc_trait_selection::infer::InferCtxtExt; use std::fmt::Write; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub(super) fn lint_dot_call_from_2018( + pub(super) fn lint_edition_dependent_dot_call( &self, self_ty: Ty<'tcx>, segment: &hir::PathSegment<'_>, @@ -32,22 +32,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { segment.ident, self_ty, call_expr, self_expr ); - // Rust 2021 and later is already using the new prelude - if span.at_least_rust_2021() { - return; - } - - let prelude_or_array_lint = match segment.ident.name { + let (prelude_or_array_lint, edition) = match segment.ident.name { // `try_into` was added to the prelude in Rust 2021. - sym::try_into => RUST_2021_PRELUDE_COLLISIONS, + sym::try_into if !span.at_least_rust_2021() => (RUST_2021_PRELUDE_COLLISIONS, "2021"), // `into_iter` wasn't added to the prelude, // but `[T; N].into_iter()` doesn't resolve to IntoIterator::into_iter // before Rust 2021, which results in the same problem. // It is only a problem for arrays. - sym::into_iter if let ty::Array(..) = self_ty.kind() => { - // In this case, it wasn't really a prelude addition that was the problem. - // Instead, the problem is that the array-into_iter hack will no longer apply in Rust 2021. - rustc_lint::ARRAY_INTO_ITER + sym::into_iter => { + if let ty::Array(..) = self_ty.kind() + && !span.at_least_rust_2021() + { + // In this case, it wasn't really a prelude addition that was the problem. + // Instead, the problem is that the array-into_iter hack will no longer + // apply in Rust 2021. + (ARRAY_INTO_ITER, "2021") + } else if self_ty.is_box() + && self_ty.boxed_ty().is_slice() + && !span.at_least_rust_2024() + { + // In this case, it wasn't really a prelude addition that was the problem. + // Instead, the problem is that the boxed-slice-into_iter hack will no + // longer apply in Rust 2024. + (BOXED_SLICE_INTO_ITER, "2024") + } else { + return; + } } _ => return, }; @@ -81,8 +91,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { prelude_or_array_lint, self_expr.hir_id, self_expr.span, - format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name), |lint| { + lint.primary_message(format!( + "trait method `{}` will become ambiguous in Rust {edition}", + segment.ident.name + )); + let sp = self_expr.span; let derefs = "*".repeat(pick.autoderefs); @@ -131,8 +145,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { prelude_or_array_lint, call_expr.hir_id, call_expr.span, - format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name), |lint| { + lint.primary_message(format!( + "trait method `{}` will become ambiguous in Rust {edition}", + segment.ident.name + )); + let sp = call_expr.span; let trait_name = self.trait_path_or_bare_name( span, @@ -235,73 +253,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } - self.tcx.node_span_lint( - RUST_2021_PRELUDE_COLLISIONS, - expr_id, - span, - format!( + self.tcx.node_span_lint(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| { + lint.primary_message(format!( "trait-associated function `{}` will become ambiguous in Rust 2021", method_name.name - ), - |lint| { - // "type" refers to either a type or, more likely, a trait from which - // the associated function or method is from. - let container_id = pick.item.container_id(self.tcx); - let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id); - let trait_generics = self.tcx.generics_of(container_id); + )); - let trait_name = - if trait_generics.own_params.len() <= trait_generics.has_self as usize { - trait_path - } else { - let counts = trait_generics.own_counts(); - format!( - "{}<{}>", - trait_path, + // "type" refers to either a type or, more likely, a trait from which + // the associated function or method is from. + let container_id = pick.item.container_id(self.tcx); + let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id); + let trait_generics = self.tcx.generics_of(container_id); + + let trait_name = + if trait_generics.own_params.len() <= trait_generics.has_self as usize { + trait_path + } else { + let counts = trait_generics.own_counts(); + format!( + "{}<{}>", + trait_path, + std::iter::repeat("'_") + .take(counts.lifetimes) + .chain(std::iter::repeat("_").take( + counts.types + counts.consts - trait_generics.has_self as usize + )) + .collect::>() + .join(", ") + ) + }; + + let mut self_ty_name = self_ty_span + .find_ancestor_inside(span) + .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) + .unwrap_or_else(|| self_ty.to_string()); + + // Get the number of generics the self type has (if an Adt) unless we can determine that + // the user has written the self type with generics already which we (naively) do by looking + // for a "<" in `self_ty_name`. + if !self_ty_name.contains('<') { + if let ty::Adt(def, _) = self_ty.kind() { + let generics = self.tcx.generics_of(def.did()); + if !generics.is_own_empty() { + let counts = generics.own_counts(); + self_ty_name += &format!( + "<{}>", std::iter::repeat("'_") .take(counts.lifetimes) - .chain(std::iter::repeat("_").take( - counts.types + counts.consts - trait_generics.has_self as usize - )) + .chain(std::iter::repeat("_").take(counts.types + counts.consts)) .collect::>() .join(", ") - ) - }; - - let mut self_ty_name = self_ty_span - .find_ancestor_inside(span) - .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) - .unwrap_or_else(|| self_ty.to_string()); - - // Get the number of generics the self type has (if an Adt) unless we can determine that - // the user has written the self type with generics already which we (naively) do by looking - // for a "<" in `self_ty_name`. - if !self_ty_name.contains('<') { - if let ty::Adt(def, _) = self_ty.kind() { - let generics = self.tcx.generics_of(def.did()); - if !generics.own_params.is_empty() { - let counts = generics.own_counts(); - self_ty_name += &format!( - "<{}>", - std::iter::repeat("'_") - .take(counts.lifetimes) - .chain( - std::iter::repeat("_").take(counts.types + counts.consts) - ) - .collect::>() - .join(", ") - ); - } + ); } } - lint.span_suggestion( - span, - "disambiguate the associated function", - format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,), - Applicability::MachineApplicable, - ); - }, - ); + } + lint.span_suggestion( + span, + "disambiguate the associated function", + format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,), + Applicability::MachineApplicable, + ); + }); } fn trait_path_or_bare_name( diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index fc652490a40a..47ea221d1a11 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -41,6 +41,7 @@ use rustc_trait_selection::traits::query::method_autoderef::{ use rustc_trait_selection::traits::query::CanonicalTyGoal; use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{self, ObligationCause}; +use std::cell::Cell; use std::cell::RefCell; use std::cmp::max; use std::iter; @@ -76,8 +77,12 @@ pub(crate) struct ProbeContext<'a, 'tcx> { /// requested name (by edit distance) allow_similar_names: bool, + /// List of potential private candidates. Will be trimmed to ones that + /// actually apply and then the result inserted into `private_candidate` + private_candidates: Vec>, + /// Some(candidate) if there is a private candidate - private_candidate: Option<(DefKind, DefId)>, + private_candidate: Cell>, /// Collects near misses when the candidate functions are missing a `self` keyword and is only /// used for error reporting @@ -301,7 +306,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty: Ty<'tcx>, scope_expr_id: HirId, scope: ProbeScope, - ) -> Vec> { + ) -> Result>, MethodError<'tcx>> { self.probe_op( item_name.span, mode, @@ -319,7 +324,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect()) }, ) - .unwrap() } pub(crate) fn probe_op( @@ -395,8 +399,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // ambiguous. if let Some(bad_ty) = &steps.opt_bad_ty { if is_suggestion.0 { - // Ambiguity was encountered during a suggestion. Just keep going. - debug!("ProbeContext: encountered ambiguity in suggestion"); + // Ambiguity was encountered during a suggestion. There's really + // not much use in suggesting methods in this case. + return Err(MethodError::NoMatch(NoMatchData { + static_candidates: Vec::new(), + unsatisfied_predicates: Vec::new(), + out_of_scope_traits: Vec::new(), + similar_candidate: None, + mode, + })); } else if bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types && !self.tcx.sess.at_least_rust_2018() @@ -408,8 +419,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lint::builtin::TYVAR_BEHIND_RAW_POINTER, scope_expr_id, span, - "type annotations needed", - |_| {}, + |lint| { + lint.primary_message("type annotations needed"); + }, ); } else { // Ended up encountering a type variable when doing autoderef, @@ -573,7 +585,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { orig_steps_var_values, steps, allow_similar_names: false, - private_candidate: None, + private_candidates: Vec::new(), + private_candidate: Cell::new(None), static_candidates: RefCell::new(Vec::new()), unsatisfied_predicates: RefCell::new(Vec::new()), scope_expr_id, @@ -585,7 +598,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.inherent_candidates.clear(); self.extension_candidates.clear(); self.impl_dups.clear(); - self.private_candidate = None; + self.private_candidates.clear(); + self.private_candidate.set(None); self.static_candidates.borrow_mut().clear(); self.unsatisfied_predicates.borrow_mut().clear(); } @@ -609,9 +623,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } else { self.extension_candidates.push(candidate); } - } else if self.private_candidate.is_none() { - self.private_candidate = - Some((candidate.item.kind.as_def_kind(), candidate.item.def_id)); + } else { + self.private_candidates.push(candidate); } } @@ -621,8 +634,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self))] fn assemble_probe(&mut self, self_ty: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>) { - debug!("assemble_probe: self_ty={:?}", self_ty); let raw_self_ty = self_ty.value.value; match *raw_self_ty.kind() { ty::Dynamic(data, ..) if let Some(p) = data.principal() => { @@ -700,13 +713,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self))] fn assemble_inherent_impl_probe(&mut self, impl_def_id: DefId) { if !self.impl_dups.insert(impl_def_id) { return; // already visited } - debug!("assemble_inherent_impl_probe {:?}", impl_def_id); - for item in self.impl_or_trait_item(impl_def_id) { if !self.has_applicable_self(&item) { // No receiver declared. Not a candidate. @@ -724,9 +736,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self))] fn assemble_inherent_candidates_from_object(&mut self, self_ty: Ty<'tcx>) { - debug!("assemble_inherent_candidates_from_object(self_ty={:?})", self_ty); - let principal = match self_ty.kind() { ty::Dynamic(ref data, ..) => Some(data), _ => None, @@ -755,9 +766,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }); } + #[instrument(level = "debug", skip(self))] fn assemble_inherent_candidates_from_param(&mut self, param_ty: ty::ParamTy) { // FIXME: do we want to commit to this behavior for param bounds? - debug!("assemble_inherent_candidates_from_param(param_ty={:?})", param_ty); let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| { let bound_predicate = predicate.kind(); @@ -813,6 +824,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self))] fn assemble_extension_candidates_for_traits_in_scope(&mut self) { let mut duplicates = FxHashSet::default(); let opt_applicable_traits = self.tcx.in_scope_traits(self.scope_expr_id); @@ -829,6 +841,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self))] fn assemble_extension_candidates_for_all_traits(&mut self) { let mut duplicates = FxHashSet::default(); for trait_info in suggest::all_traits(self.tcx) { @@ -850,12 +863,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self))] fn assemble_extension_candidates_for_trait( &mut self, import_ids: &SmallVec<[LocalDefId; 1]>, trait_def_id: DefId, ) { - debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id); let trait_args = self.fresh_args_for_item(self.span, trait_def_id); let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, trait_args); @@ -945,6 +958,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /////////////////////////////////////////////////////////////////////////// // THE ACTUAL SEARCH + #[instrument(level = "debug", skip(self))] fn pick(mut self) -> PickResult<'tcx> { assert!(self.method_name.is_some()); @@ -1163,7 +1177,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let mut possibly_unsatisfied_predicates = Vec::new(); for (kind, candidates) in - &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] + [("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] { debug!("searching {} candidates", kind); let res = self.consider_candidates( @@ -1177,6 +1191,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + if self.private_candidate.get().is_none() { + if let Some(Ok(pick)) = + self.consider_candidates(self_ty, &self.private_candidates, &mut vec![], None) + { + self.private_candidate.set(Some((pick.item.kind.as_def_kind(), pick.item.def_id))); + } + } + // `pick_method` may be called twice for the same self_ty if no stable methods // match. Only extend once. if unstable_candidates.is_some() { @@ -1279,53 +1301,49 @@ impl<'tcx> Pick<'tcx> { return; } let def_kind = self.item.kind.as_def_kind(); - tcx.node_span_lint( - lint::builtin::UNSTABLE_NAME_COLLISIONS, - scope_expr_id, - span, - format!( + tcx.node_span_lint(lint::builtin::UNSTABLE_NAME_COLLISIONS, scope_expr_id, span, |lint| { + lint.primary_message(format!( "{} {} with this name may be added to the standard library in the future", tcx.def_kind_descr_article(def_kind, self.item.def_id), tcx.def_kind_descr(def_kind, self.item.def_id), - ), - |lint| { - match (self.item.kind, self.item.container) { - (ty::AssocKind::Fn, _) => { - // FIXME: This should be a `span_suggestion` instead of `help` - // However `self.span` only - // highlights the method name, so we can't use it. Also consider reusing - // the code from `report_method_error()`. - lint.help(format!( - "call with fully qualified syntax `{}(...)` to keep using the current \ + )); + + match (self.item.kind, self.item.container) { + (ty::AssocKind::Fn, _) => { + // FIXME: This should be a `span_suggestion` instead of `help` + // However `self.span` only + // highlights the method name, so we can't use it. Also consider reusing + // the code from `report_method_error()`. + lint.help(format!( + "call with fully qualified syntax `{}(...)` to keep using the current \ method", - tcx.def_path_str(self.item.def_id), - )); - } - (ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer) => { - let def_id = self.item.container_id(tcx); - lint.span_suggestion( - span, - "use the fully qualified path to the associated const", - format!( - "<{} as {}>::{}", - self.self_ty, - tcx.def_path_str(def_id), - self.item.name - ), - Applicability::MachineApplicable, - ); - } - _ => {} + tcx.def_path_str(self.item.def_id), + )); } - tcx.disabled_nightly_features( - lint, - Some(scope_expr_id), - self.unstable_candidates.iter().map(|(candidate, feature)| { - (format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature) - }), - ); - }, - ); + (ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer) => { + let def_id = self.item.container_id(tcx); + lint.span_suggestion( + span, + "use the fully qualified path to the associated const", + format!( + "<{} as {}>::{}", + self.self_ty, + tcx.def_path_str(def_id), + self.item.name + ), + Applicability::MachineApplicable, + ); + } + _ => {} + } + tcx.disabled_nightly_features( + lint, + Some(scope_expr_id), + self.unstable_candidates.iter().map(|(candidate, feature)| { + (format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature) + }), + ); + }); } } @@ -1339,6 +1357,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { traits::SelectionContext::new(self).select(&obligation) } + /// Used for ambiguous method call error reporting. Uses probing that throws away the result internally, + /// so do not use to make a decision that may lead to a successful compilation. fn candidate_source(&self, candidate: &Candidate<'tcx>, self_ty: Ty<'tcx>) -> CandidateSource { match candidate.kind { InherentImplCandidate(_) => { @@ -1352,8 +1372,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.instantiate_binder_with_fresh_vars(self.span, infer::FnCall, trait_ref); let (xform_self_ty, _) = self.xform_self_ty(candidate.item, trait_ref.self_ty(), trait_ref.args); + // Guide the trait selection to show impls that have methods whose type matches + // up with the `self` parameter of the method. let _ = self.at(&ObligationCause::dummy(), self.param_env).sup( - DefineOpaqueTypes::No, + DefineOpaqueTypes::Yes, xform_self_ty, self_ty, ); @@ -1369,6 +1391,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + #[instrument(level = "trace", skip(self, possibly_unsatisfied_predicates), ret)] fn consider_probe( &self, self_ty: Ty<'tcx>, @@ -1386,7 +1409,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let mut result = ProbeResult::Match; let cause = &self.misc(self.span); - let ocx = ObligationCtxt::new(self); + let ocx = ObligationCtxt::new_with_diagnostics(self); let mut trait_predicate = None; let (mut xform_self_ty, mut xform_ret_ty); @@ -1398,15 +1421,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { (xform_self_ty, xform_ret_ty) = self.xform_self_ty(probe.item, impl_ty, impl_args); xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty); - // FIXME: Make this `ocx.sup` once we define opaques more eagerly. - match self.at(cause, self.param_env).sup( - DefineOpaqueTypes::No, - xform_self_ty, - self_ty, - ) { - Ok(infer_ok) => { - ocx.register_infer_ok_obligations(infer_ok); - } + match ocx.sup(cause, self.param_env, xform_self_ty, self_ty) { + Ok(()) => {} Err(err) => { debug!("--> cannot relate self-types {:?}", err); return ProbeResult::NoMatch; @@ -1444,6 +1460,18 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { return ProbeResult::NoMatch; } } + + // Some trait methods are excluded for boxed slices before 2024. + // (`boxed_slice.into_iter()` wants a slice iterator for compatibility.) + if self_ty.is_box() + && self_ty.boxed_ty().is_slice() + && !method_name.span.at_least_rust_2024() + { + let trait_def = self.tcx.trait_def(poly_trait_ref.def_id()); + if trait_def.skip_boxed_slice_during_method_dispatch { + return ProbeResult::NoMatch; + } + } } let trait_ref = self.instantiate_binder_with_fresh_vars( @@ -1455,19 +1483,23 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { (xform_self_ty, xform_ret_ty) = self.xform_self_ty(probe.item, trait_ref.self_ty(), trait_ref.args); xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty); - // FIXME: Make this `ocx.sup` once we define opaques more eagerly. - match self.at(cause, self.param_env).sup( - DefineOpaqueTypes::No, - xform_self_ty, - self_ty, - ) { - Ok(infer_ok) => { - ocx.register_infer_ok_obligations(infer_ok); - } - Err(err) => { - debug!("--> cannot relate self-types {:?}", err); + match self_ty.kind() { + // HACK: opaque types will match anything for which their bounds hold. + // Thus we need to prevent them from trying to match the `&_` autoref + // candidates that get created for `&self` trait methods. + ty::Alias(ty::Opaque, alias_ty) + if self.infcx.can_define_opaque_ty(alias_ty.def_id) + && !xform_self_ty.is_ty_var() => + { return ProbeResult::NoMatch; } + _ => match ocx.sup(cause, self.param_env, xform_self_ty, self_ty) { + Ok(()) => {} + Err(err) => { + debug!("--> cannot relate self-types {:?}", err); + return ProbeResult::NoMatch; + } + }, } let obligation = traits::Obligation::new( self.tcx, @@ -1507,15 +1539,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { (xform_self_ty, xform_ret_ty) = self.xform_self_ty(probe.item, trait_ref.self_ty(), trait_ref.args); xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty); - // FIXME: Make this `ocx.sup` once we define opaques more eagerly. - match self.at(cause, self.param_env).sup( - DefineOpaqueTypes::No, - xform_self_ty, - self_ty, - ) { - Ok(infer_ok) => { - ocx.register_infer_ok_obligations(infer_ok); - } + match ocx.sup(cause, self.param_env, xform_self_ty, self_ty) { + Ok(()) => {} Err(err) => { debug!("--> cannot relate self-types {:?}", err); return ProbeResult::NoMatch; @@ -1636,6 +1661,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// Similarly to `probe_for_return_type`, this method attempts to find the best matching /// candidate method where the method name may have been misspelled. Similarly to other /// edit distance based suggestions, we provide at most one such suggestion. + #[instrument(level = "debug", skip(self))] pub(crate) fn probe_for_similar_candidate( &mut self, ) -> Result, MethodError<'tcx>> { @@ -1754,7 +1780,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let generics = self.tcx.generics_of(method); assert_eq!(args.len(), generics.parent_count); - let xform_fn_sig = if generics.own_params.is_empty() { + let xform_fn_sig = if generics.is_own_empty() { fn_sig.instantiate(self.tcx, args) } else { let args = GenericArgs::for_item(self.tcx, method, |param, _| { diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index db510d44392c..a5e955a986b9 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -7,6 +7,7 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem}; use crate::Expectation; use crate::FnCtxt; use core::ops::ControlFlow; +use hir::Expr; use rustc_ast::ast::Mutability; use rustc_attr::parse_confusables; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; @@ -15,12 +16,11 @@ use rustc_data_structures::unord::UnordSet; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, MultiSpan, StashKey, }; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_hir::PatKind::Binding; use rustc_hir::PathSegment; +use rustc_hir::{self as hir, HirId}; use rustc_hir::{ExprKind, Node, QPath}; use rustc_infer::infer::{self, RegionVariableOrigin}; use rustc_middle::bug; @@ -33,7 +33,7 @@ use rustc_middle::ty::IsSuggestable; use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::def_id::DefIdSet; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{edit_distance, ExpnKind, FileName, MacroKind, Span}; +use rustc_span::{edit_distance, ErrorGuaranteed, ExpnKind, FileName, MacroKind, Span}; use rustc_span::{Symbol, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote; @@ -46,7 +46,7 @@ use std::borrow::Cow; use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; use super::{CandidateSource, MethodError, NoMatchData}; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::{self, Visitor}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { @@ -187,33 +187,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self))] pub fn report_method_error( &self, - span: Span, + call_id: HirId, rcvr_ty: Ty<'tcx>, - item_name: Ident, - source: SelfSource<'tcx>, error: MethodError<'tcx>, - args: Option<&'tcx [hir::Expr<'tcx>]>, expected: Expectation<'tcx>, trait_missing_method: bool, - ) -> Option> { - // Avoid suggestions when we don't know what's going on. - if rcvr_ty.references_error() { - return None; - } - - let sugg_span = if let SelfSource::MethodCall(expr) = source { - // Given `foo.bar(baz)`, `expr` is `bar`, but we want to point to the whole thing. - self.tcx.hir().expect_expr(self.tcx.parent_hir_id(expr.hir_id)).span - } else { - span + ) -> ErrorGuaranteed { + let (span, sugg_span, source, item_name, args) = match self.tcx.hir_node(call_id) { + hir::Node::Expr(&hir::Expr { + kind: hir::ExprKind::MethodCall(segment, rcvr, args, _), + span, + .. + }) => { + (segment.ident.span, span, SelfSource::MethodCall(rcvr), segment.ident, Some(args)) + } + hir::Node::Expr(&hir::Expr { + kind: hir::ExprKind::Path(QPath::TypeRelative(rcvr, segment)), + span, + .. + }) + | hir::Node::Pat(&hir::Pat { + kind: + hir::PatKind::Path(QPath::TypeRelative(rcvr, segment)) + | hir::PatKind::Struct(QPath::TypeRelative(rcvr, segment), ..) + | hir::PatKind::TupleStruct(QPath::TypeRelative(rcvr, segment), ..), + span, + .. + }) => { + let args = match self.tcx.parent_hir_node(call_id) { + hir::Node::Expr(&hir::Expr { + kind: hir::ExprKind::Call(callee, args), .. + }) if callee.hir_id == call_id => Some(args), + _ => None, + }; + (segment.ident.span, span, SelfSource::QPath(rcvr), segment.ident, args) + } + node => unreachable!("{node:?}"), }; + // Avoid suggestions when we don't know what's going on. + if let Err(guar) = rcvr_ty.error_reported() { + return guar; + } + match error { MethodError::NoMatch(mut no_match_data) => { return self.report_no_match_method_error( span, rcvr_ty, item_name, + call_id, source, args, sugg_span, @@ -242,7 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &mut sources, Some(sugg_span), ); - err.emit(); + return err.emit(); } MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => { @@ -263,7 +286,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap_or_else(|| self.tcx.def_span(def_id)); err.span_label(sp, format!("private {kind} defined here")); self.suggest_valid_traits(&mut err, item_name, out_of_scope_traits, true); - err.emit(); + return err.emit(); } MethodError::IllegalSizedBound { candidates, needs_mut, bound_span, self_expr } => { @@ -280,14 +303,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if !candidates.is_empty() { let help = format!( - "{an}other candidate{s} {were} found in the following trait{s}, perhaps \ - add a `use` for {one_of_them}:", + "{an}other candidate{s} {were} found in the following trait{s}", an = if candidates.len() == 1 { "an" } else { "" }, s = pluralize!(candidates.len()), were = pluralize!("was", candidates.len()), - one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" }, ); - self.suggest_use_candidates(&mut err, help, candidates); + self.suggest_use_candidates( + candidates, + |accessible_sugg, inaccessible_sugg, span| { + let suggest_for_access = + |err: &mut Diag<'_>, mut msg: String, sugg: Vec<_>| { + msg += &format!( + ", perhaps add a `use` for {one_of_them}:", + one_of_them = + if sugg.len() == 1 { "it" } else { "one_of_them" }, + ); + err.span_suggestions( + span, + msg, + sugg, + Applicability::MaybeIncorrect, + ); + }; + let suggest_for_privacy = + |err: &mut Diag<'_>, mut msg: String, sugg: Vec| { + if sugg.len() == 1 { + let msg = format!("\ + trait `{}` provides `{item_name}` is implemented but not reachable", + sugg[0].trim() + ); + err.help(msg); + } else { + msg += &format!(" but {} not reachable", pluralize!("is", sugg.len())); + err.span_suggestions( + span, + msg, + sugg, + Applicability::MaybeIncorrect, + ); + } + }; + if accessible_sugg.is_empty() { + // `inaccessible_sugg` must not be empty + suggest_for_privacy(&mut err, help, inaccessible_sugg); + } else if inaccessible_sugg.is_empty() { + suggest_for_access(&mut err, help, accessible_sugg); + } else { + suggest_for_access(&mut err, help.clone(), accessible_sugg); + suggest_for_privacy(&mut err, help, inaccessible_sugg); + } + }, + ); } if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() { if needs_mut { @@ -320,12 +386,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - err.emit(); + return err.emit(); } MethodError::BadReturnType => bug!("no return type expectations but got BadReturnType"), } - None } fn suggest_missing_writer(&self, rcvr_ty: Ty<'tcx>, rcvr_expr: &hir::Expr<'tcx>) -> Diag<'_> { @@ -356,18 +421,204 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err } - pub fn report_no_match_method_error( + pub fn suggest_use_shadowed_binding_with_method( + &self, + self_source: SelfSource<'tcx>, + method_name: Ident, + ty_str_reported: &str, + err: &mut Diag<'_>, + ) { + #[derive(Debug)] + struct LetStmt { + ty_hir_id_opt: Option, + binding_id: hir::HirId, + span: Span, + init_hir_id: hir::HirId, + } + + // Used for finding suggest binding. + // ```rust + // earlier binding for suggesting: + // let y = vec![1, 2]; + // now binding: + // if let Some(y) = x { + // y.push(y); + // } + // ``` + struct LetVisitor<'a, 'tcx> { + // Error binding which don't have `method_name`. + binding_name: Symbol, + binding_id: hir::HirId, + // Used for check if the suggest binding has `method_name`. + fcx: &'a FnCtxt<'a, 'tcx>, + call_expr: &'tcx Expr<'tcx>, + method_name: Ident, + // Suggest the binding which is shallowed. + sugg_let: Option, + } + + impl<'a, 'tcx> LetVisitor<'a, 'tcx> { + // Check scope of binding. + fn is_sub_scope(&self, sub_id: hir::ItemLocalId, super_id: hir::ItemLocalId) -> bool { + let scope_tree = self.fcx.tcx.region_scope_tree(self.fcx.body_id); + if let Some(sub_var_scope) = scope_tree.var_scope(sub_id) + && let Some(super_var_scope) = scope_tree.var_scope(super_id) + && scope_tree.is_subscope_of(sub_var_scope, super_var_scope) + { + return true; + } + false + } + + // Check if an earlier shadowed binding make `the receiver` of a MethodCall has the method. + // If it does, record the earlier binding for subsequent notes. + fn check_and_add_sugg_binding(&mut self, binding: LetStmt) -> bool { + if !self.is_sub_scope(self.binding_id.local_id, binding.binding_id.local_id) { + return false; + } + + // Get the earlier shadowed binding'ty and use it to check the method. + if let Some(ty_hir_id) = binding.ty_hir_id_opt + && let Some(tyck_ty) = self.fcx.node_ty_opt(ty_hir_id) + { + if self + .fcx + .lookup_probe_for_diagnostic( + self.method_name, + tyck_ty, + self.call_expr, + ProbeScope::TraitsInScope, + None, + ) + .is_ok() + { + self.sugg_let = Some(binding); + return true; + } else { + return false; + } + } + + // If the shadowed binding has an an itializer expression, + // use the initializer expression'ty to try to find the method again. + // For example like: `let mut x = Vec::new();`, + // `Vec::new()` is the itializer expression. + if let Some(self_ty) = self.fcx.node_ty_opt(binding.init_hir_id) + && self + .fcx + .lookup_probe_for_diagnostic( + self.method_name, + self_ty, + self.call_expr, + ProbeScope::TraitsInScope, + None, + ) + .is_ok() + { + self.sugg_let = Some(binding); + return true; + } + return false; + } + } + + impl<'v> Visitor<'v> for LetVisitor<'_, '_> { + type Result = ControlFlow<()>; + fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { + if let hir::StmtKind::Let(&hir::LetStmt { pat, ty, init, .. }) = ex.kind + && let hir::PatKind::Binding(_, binding_id, binding_name, ..) = pat.kind + && let Some(init) = init + && binding_name.name == self.binding_name + && binding_id != self.binding_id + { + if self.check_and_add_sugg_binding(LetStmt { + ty_hir_id_opt: if let Some(ty) = ty { Some(ty.hir_id) } else { None }, + binding_id: binding_id, + span: pat.span, + init_hir_id: init.hir_id, + }) { + return ControlFlow::Break(()); + } + ControlFlow::Continue(()) + } else { + hir::intravisit::walk_stmt(self, ex) + } + } + + // Used for find the error binding. + // When the visitor reaches this point, all the shadowed bindings + // have been found, so the visitor ends. + fn visit_pat(&mut self, p: &'v hir::Pat<'v>) -> Self::Result { + match p.kind { + hir::PatKind::Binding(_, binding_id, binding_name, _) => { + if binding_name.name == self.binding_name && binding_id == self.binding_id { + return ControlFlow::Break(()); + } + } + _ => { + intravisit::walk_pat(self, p); + } + } + ControlFlow::Continue(()) + } + } + + if let SelfSource::MethodCall(rcvr) = self_source + && let hir::ExprKind::Path(QPath::Resolved(_, path)) = rcvr.kind + && let hir::def::Res::Local(recv_id) = path.res + && let Some(segment) = path.segments.first() + { + let body = self.tcx.hir().body_owned_by(self.body_id); + + if let Node::Expr(call_expr) = self.tcx.parent_hir_node(rcvr.hir_id) { + let mut let_visitor = LetVisitor { + fcx: self, + call_expr, + binding_name: segment.ident.name, + binding_id: recv_id, + method_name, + sugg_let: None, + }; + let_visitor.visit_body(&body); + if let Some(sugg_let) = let_visitor.sugg_let + && let Some(self_ty) = self.node_ty_opt(sugg_let.init_hir_id) + { + let _sm = self.infcx.tcx.sess.source_map(); + let rcvr_name = segment.ident.name; + let mut span = MultiSpan::from_span(sugg_let.span); + span.push_span_label(sugg_let.span, + format!("`{rcvr_name}` of type `{self_ty}` that has method `{method_name}` defined earlier here")); + span.push_span_label( + self.tcx.hir().span(recv_id), + format!( + "earlier `{rcvr_name}` shadowed here with type `{ty_str_reported}`" + ), + ); + err.span_note( + span, + format!( + "there's an earlier shadowed binding `{rcvr_name}` of type `{self_ty}` \ + that has method `{method_name}` available" + ), + ); + } + } + } + } + + fn report_no_match_method_error( &self, mut span: Span, rcvr_ty: Ty<'tcx>, item_name: Ident, + expr_id: hir::HirId, source: SelfSource<'tcx>, args: Option<&'tcx [hir::Expr<'tcx>]>, sugg_span: Span, no_match_data: &mut NoMatchData<'tcx>, expected: Expectation<'tcx>, trait_missing_method: bool, - ) -> Option> { + ) -> ErrorGuaranteed { let mode = no_match_data.mode; let tcx = self.tcx; let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty); @@ -399,14 +650,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We could pass the file for long types into these two, but it isn't strictly necessary // given how targeted they are. - if self.suggest_wrapping_range_with_parens( + if let Err(guar) = self.report_failed_method_call_on_range_end( tcx, rcvr_ty, source, span, item_name, &short_ty_str, - ) || self.suggest_constraining_numerical_ty( + ) { + return guar; + } + if let Err(guar) = self.report_failed_method_call_on_numerical_infer_var( tcx, rcvr_ty, source, @@ -415,7 +669,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name, &short_ty_str, ) { - return None; + return guar; } span = item_name.span; @@ -451,7 +705,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source { self.suggest_missing_writer(rcvr_ty, rcvr_expr) } else { - tcx.dcx().create_err(NoAssociatedItem { + let mut err = tcx.dcx().create_err(NoAssociatedItem { span, item_kind, item_name, @@ -461,9 +715,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { rcvr_ty.prefix_string(self.tcx) }, - ty_str: ty_str_reported, + ty_str: ty_str_reported.clone(), trait_missing_method, - }) + }); + + if is_method { + self.suggest_use_shadowed_binding_with_method( + source, + item_name, + &ty_str_reported, + &mut err, + ); + } + + err }; if tcx.sess.source_map().is_multiline(sugg_span) { err.span_label(sugg_span.with_hi(span.lo()), ""); @@ -482,7 +747,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if matches!(source, SelfSource::QPath(_)) && args.is_some() { - self.find_builder_fn(&mut err, rcvr_ty); + self.find_builder_fn(&mut err, rcvr_ty, expr_id); } if tcx.ty_is_opaque_future(rcvr_ty) && item_name.name == sym::poll { @@ -661,7 +926,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { vec![(span.shrink_to_lo(), format!("into_iter()."))], Applicability::MaybeIncorrect, ); - return Some(err); + return err.emit(); } else if !unsatisfied_predicates.is_empty() && matches!(rcvr_ty.kind(), ty::Param(_)) { // We special case the situation where we are looking for `_` in // `::method` because otherwise the machinery will look for blanket @@ -880,7 +1145,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { unsatisfied_predicates.iter().any(|(pred, _, _)| { match pred.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { - Some(pred.def_id()) == self.tcx.lang_items().sized_trait() + self.tcx.is_lang_item(pred.def_id(), LangItem::Sized) && pred.polarity == ty::PredicatePolarity::Positive } _ => false, @@ -1153,10 +1418,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty.is_str() || matches!( ty.kind(), - ty::Adt(adt, _) if Some(adt.did()) == self.tcx.lang_items().string() + ty::Adt(adt, _) if self.tcx.is_lang_item(adt.did(), LangItem::String) ) } - ty::Adt(adt, _) => Some(adt.did()) == self.tcx.lang_items().string(), + ty::Adt(adt, _) => self.tcx.is_lang_item(adt.did(), LangItem::String), _ => false, }; if is_string_or_ref_str && item_name.name == sym::iter { @@ -1386,7 +1651,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_name, expected); - Some(err) + err.emit() } /// If an appropriate error source is not found, check method chain for possible candidates @@ -1420,10 +1685,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap_or(Ty::new_misc_error(self.tcx)), ); - // FIXME: `probe_for_name_many` searches for methods in inherent implementations, - // so it may return a candidate that doesn't belong to this `revr_ty`. We need to - // check whether the instantiated type matches the received one. - for _matched_method in self.probe_for_name_many( + let Ok(candidates) = self.probe_for_name_many( Mode::MethodCall, item_name, None, @@ -1431,7 +1693,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty, source_expr.hir_id, ProbeScope::TraitsInScope, - ) { + ) else { + return; + }; + + // FIXME: `probe_for_name_many` searches for methods in inherent implementations, + // so it may return a candidate that doesn't belong to this `revr_ty`. We need to + // check whether the instantiated type matches the received one. + for _matched_method in candidates { // found a match, push to stack stack_methods.push(rcvr_ty); } @@ -1743,7 +2012,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Look at all the associated functions without receivers in the type's inherent impls /// to look for builders that return `Self`, `Option` or `Result`. - fn find_builder_fn(&self, err: &mut Diag<'_>, rcvr_ty: Ty<'tcx>) { + fn find_builder_fn(&self, err: &mut Diag<'_>, rcvr_ty: Ty<'tcx>, expr_id: hir::HirId) { let ty::Adt(adt_def, _) = rcvr_ty.kind() else { return; }; @@ -1752,8 +2021,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut items = impls .iter() .flat_map(|i| self.tcx.associated_items(i).in_definition_order()) - // Only assoc fn with no receivers. - .filter(|item| matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter) + // Only assoc fn with no receivers and only if + // they are resolvable + .filter(|item| { + matches!(item.kind, ty::AssocKind::Fn) + && !item.fn_has_self_parameter + && self + .probe_for_name( + Mode::Path, + item.ident(self.tcx), + None, + IsSuggestion(true), + rcvr_ty, + expr_id, + ProbeScope::TraitsInScope, + ) + .is_ok() + }) .filter_map(|item| { // Only assoc fns that return `Self`, `Option` or `Result`. let ret_ty = self @@ -1861,9 +2145,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP)) .into(), GenericArgKind::Type(_) => self.next_ty_var(DUMMY_SP).into(), - GenericArgKind::Const(arg) => { - self.next_const_var(arg.ty(), DUMMY_SP).into() - } + GenericArgKind::Const(_) => self.next_const_var(DUMMY_SP).into(), } } else { arg @@ -2014,7 +2296,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Suggest possible range with adding parentheses, for example: /// when encountering `0..1.map(|i| i + 1)` suggest `(0..1).map(|i| i + 1)`. - fn suggest_wrapping_range_with_parens( + fn report_failed_method_call_on_range_end( &self, tcx: TyCtxt<'tcx>, actual: Ty<'tcx>, @@ -2022,7 +2304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, item_name: Ident, ty_str: &str, - ) -> bool { + ) -> Result<(), ErrorGuaranteed> { if let SelfSource::MethodCall(expr) = source { for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) { if let Node::Expr(parent_expr) = parent { @@ -2079,7 +2361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); if pick.is_ok() { let range_span = parent_expr.span.with_hi(expr.span.hi()); - tcx.dcx().emit_err(errors::MissingParenthesesInRange { + return Err(tcx.dcx().emit_err(errors::MissingParenthesesInRange { span, ty_str: ty_str.to_string(), method_name: item_name.as_str().to_string(), @@ -2088,16 +2370,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { left: range_span.shrink_to_lo(), right: range_span.shrink_to_hi(), }), - }); - return true; + })); } } } } - false + Ok(()) } - fn suggest_constraining_numerical_ty( + fn report_failed_method_call_on_numerical_infer_var( &self, tcx: TyCtxt<'tcx>, actual: Ty<'tcx>, @@ -2106,7 +2387,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_kind: &str, item_name: Ident, ty_str: &str, - ) -> bool { + ) -> Result<(), ErrorGuaranteed> { let found_candidate = all_traits(self.tcx) .into_iter() .any(|info| self.associated_value(info.def_id, item_name).is_some()); @@ -2210,10 +2491,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => {} } - err.emit(); - return true; + return Err(err.emit()); } - false + Ok(()) } /// For code `rect::area(...)`, @@ -2228,9 +2508,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { seg1.ident.span, StashKey::CallAssocMethod, |err| { - let map = self.infcx.tcx.hir(); - let body_id = self.tcx.hir().body_owned_by(self.body_id); - let body = map.body(body_id); + let body = self.tcx.hir().body_owned_by(self.body_id); struct LetVisitor { ident_name: Symbol, } @@ -2240,7 +2518,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { type Result = ControlFlow>>; fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { if let hir::StmtKind::Let(&hir::LetStmt { pat, init, .. }) = ex.kind - && let Binding(_, _, ident, ..) = pat.kind + && let hir::PatKind::Binding(_, _, ident, ..) = pat.kind && ident.name == self.ident_name { ControlFlow::Break(init) @@ -2252,7 +2530,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id) && let ControlFlow::Break(Some(expr)) = - (LetVisitor { ident_name: seg1.ident.name }).visit_body(body) + (LetVisitor { ident_name: seg1.ident.name }).visit_body(&body) && let Some(self_ty) = self.node_ty_opt(expr.hir_id) { let probe = self.lookup_probe_for_diagnostic( @@ -2488,7 +2766,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if tcx.is_diagnostic_item(sym::LocalKey, inner_id) { err.help("use `with` or `try_with` to access thread local storage"); - } else if Some(kind.did()) == tcx.lang_items().maybe_uninit() { + } else if tcx.is_lang_item(kind.did(), LangItem::MaybeUninit) { err.help(format!( "if this `{name}` has been initialized, \ use one of the `assume_init` methods to access the inner value" @@ -2854,49 +3132,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn suggest_use_candidates(&self, err: &mut Diag<'_>, msg: String, candidates: Vec) { + fn suggest_use_candidates(&self, candidates: Vec, handle_candidates: F) + where + F: FnOnce(Vec, Vec, Span), + { let parent_map = self.tcx.visible_parent_map(()); - // Separate out candidates that must be imported with a glob, because they are named `_` - // and cannot be referred with their identifier. - let (candidates, globs): (Vec<_>, Vec<_>) = candidates.into_iter().partition(|trait_did| { - if let Some(parent_did) = parent_map.get(trait_did) { - // If the item is re-exported as `_`, we should suggest a glob-import instead. - if *parent_did != self.tcx.parent(*trait_did) - && self - .tcx - .module_children(*parent_did) - .iter() - .filter(|child| child.res.opt_def_id() == Some(*trait_did)) - .all(|child| child.ident.name == kw::Underscore) - { - return false; - } - } + let scope = self.tcx.parent_module_from_def_id(self.body_id); + let (accessible_candidates, inaccessible_candidates): (Vec<_>, Vec<_>) = + candidates.into_iter().partition(|id| { + let vis = self.tcx.visibility(*id); + vis.is_accessible_from(scope, self.tcx) + }); - true - }); + let sugg = |candidates: Vec<_>, visible| { + // Separate out candidates that must be imported with a glob, because they are named `_` + // and cannot be referred with their identifier. + let (candidates, globs): (Vec<_>, Vec<_>) = + candidates.into_iter().partition(|trait_did| { + if let Some(parent_did) = parent_map.get(trait_did) { + // If the item is re-exported as `_`, we should suggest a glob-import instead. + if *parent_did != self.tcx.parent(*trait_did) + && self + .tcx + .module_children(*parent_did) + .iter() + .filter(|child| child.res.opt_def_id() == Some(*trait_did)) + .all(|child| child.ident.name == kw::Underscore) + { + return false; + } + } - let module_did = self.tcx.parent_module_from_def_id(self.body_id); - let (module, _, _) = self.tcx.hir().get_module(module_did); + true + }); + + let prefix = if visible { "use " } else { "" }; + let postfix = if visible { ";" } else { "" }; + let path_strings = candidates.iter().map(|trait_did| { + format!( + "{prefix}{}{postfix}\n", + with_crate_prefix!(self.tcx.def_path_str(*trait_did)), + ) + }); + + let glob_path_strings = globs.iter().map(|trait_did| { + let parent_did = parent_map.get(trait_did).unwrap(); + format!( + "{prefix}{}::*{postfix} // trait {}\n", + with_crate_prefix!(self.tcx.def_path_str(*parent_did)), + self.tcx.item_name(*trait_did), + ) + }); + let mut sugg: Vec<_> = path_strings.chain(glob_path_strings).collect(); + sugg.sort(); + sugg + }; + + let accessible_sugg = sugg(accessible_candidates, true); + let inaccessible_sugg = sugg(inaccessible_candidates, false); + + let (module, _, _) = self.tcx.hir().get_module(scope); let span = module.spans.inject_use_span; - - let path_strings = candidates.iter().map(|trait_did| { - format!("use {};\n", with_crate_prefix!(self.tcx.def_path_str(*trait_did)),) - }); - - let glob_path_strings = globs.iter().map(|trait_did| { - let parent_did = parent_map.get(trait_did).unwrap(); - format!( - "use {}::*; // trait {}\n", - with_crate_prefix!(self.tcx.def_path_str(*parent_did)), - self.tcx.item_name(*trait_did), - ) - }); - let mut sugg: Vec<_> = path_strings.chain(glob_path_strings).collect(); - sugg.sort(); - - err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect); + handle_candidates(accessible_sugg, inaccessible_sugg, span); } fn suggest_valid_traits( @@ -2920,9 +3218,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if explain { err.help("items from traits can only be used if the trait is in scope"); } + let msg = format!( - "{this_trait_is} implemented but not in scope; perhaps you want to import \ - {one_of_them}", + "{this_trait_is} implemented but not in scope", this_trait_is = if candidates.len() == 1 { format!( "trait `{}` which provides `{item_name}` is", @@ -2930,11 +3228,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } else { format!("the following traits which provide `{item_name}` are") - }, - one_of_them = if candidates.len() == 1 { "it" } else { "one of them" }, + } ); - self.suggest_use_candidates(err, msg, candidates); + self.suggest_use_candidates(candidates, |accessible_sugg, inaccessible_sugg, span| { + let suggest_for_access = |err: &mut Diag<'_>, mut msg: String, sugg: Vec<_>| { + msg += &format!( + "; perhaps you want to import {one_of}", + one_of = if sugg.len() == 1 { "it" } else { "one of them" }, + ); + err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect); + }; + let suggest_for_privacy = |err: &mut Diag<'_>, sugg: Vec| { + let msg = format!( + "{this_trait_is} implemented but not reachable", + this_trait_is = if sugg.len() == 1 { + format!("trait `{}` which provides `{item_name}` is", sugg[0].trim()) + } else { + format!("the following traits which provide `{item_name}` are") + } + ); + if sugg.len() == 1 { + err.help(msg); + } else { + err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect); + } + }; + if accessible_sugg.is_empty() { + // `inaccessible_sugg` must not be empty + suggest_for_privacy(err, inaccessible_sugg); + } else if inaccessible_sugg.is_empty() { + suggest_for_access(err, msg, accessible_sugg); + } else { + suggest_for_access(err, msg, accessible_sugg); + suggest_for_privacy(err, inaccessible_sugg); + } + }); + if let Some(did) = edition_fix { err.note(format!( "'{}' is included in the prelude starting in Edition 2021", @@ -3125,14 +3455,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .source_map() .indentation_before(rcvr.span) .unwrap_or_else(|| " ".to_string()); - err.multipart_suggestion( - "consider pinning the expression", - vec![ - (rcvr.span.shrink_to_lo(), format!("let mut pinned = std::pin::pin!(")), - (rcvr.span.shrink_to_hi(), format!(");\n{indent}pinned.{pin_call}()")), - ], - Applicability::MaybeIncorrect, - ); + let mut expr = rcvr; + while let Node::Expr(call_expr) = self.tcx.parent_hir_node(expr.hir_id) + && let hir::ExprKind::MethodCall(hir::PathSegment { .. }, ..) = + call_expr.kind + { + expr = call_expr; + } + match self.tcx.parent_hir_node(expr.hir_id) { + Node::LetStmt(stmt) + if let Some(init) = stmt.init + && let Ok(code) = + self.tcx.sess.source_map().span_to_snippet(rcvr.span) => + { + // We need to take care to account for the existing binding when we + // suggest the code. + err.multipart_suggestion( + "consider pinning the expression", + vec![ + ( + stmt.span.shrink_to_lo(), + format!( + "let mut pinned = std::pin::pin!({code});\n{indent}" + ), + ), + ( + init.span.until(rcvr.span.shrink_to_hi()), + format!("pinned.{pin_call}()"), + ), + ], + Applicability::MaybeIncorrect, + ); + } + Node::Block(_) | Node::Stmt(_) => { + // There's no binding, so we can provide a slightly nicer looking + // suggestion. + err.multipart_suggestion( + "consider pinning the expression", + vec![ + ( + rcvr.span.shrink_to_lo(), + format!("let mut pinned = std::pin::pin!("), + ), + ( + rcvr.span.shrink_to_hi(), + format!(");\n{indent}pinned.{pin_call}()"), + ), + ], + Applicability::MaybeIncorrect, + ); + } + _ => { + // We don't quite know what the users' code looks like, so we don't + // provide a pinning suggestion. + err.span_help( + rcvr.span, + "consider pinning the expression with `std::pin::pin!()` and \ + assigning that to a new binding", + ); + } + } // We don't care about the other suggestions. alt_rcvr_sugg = true; } @@ -3442,22 +3824,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if impls_trait(trait_info.def_id) { self.suggest_valid_traits(err, item_name, vec![trait_info.def_id], false); } else { - err.subdiagnostic( - self.dcx(), - CandidateTraitNote { - span: self.tcx.def_span(trait_info.def_id), - trait_name: self.tcx.def_path_str(trait_info.def_id), - item_name, - action_or_ty: if trait_missing_method { - "NONE".to_string() - } else { - param_type.map_or_else( - || "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented. - |p| p.to_string(), - ) - }, + err.subdiagnostic(CandidateTraitNote { + span: self.tcx.def_span(trait_info.def_id), + trait_name: self.tcx.def_path_str(trait_info.def_id), + item_name, + action_or_ty: if trait_missing_method { + "NONE".to_string() + } else { + param_type.map_or_else( + || "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented. + |p| p.to_string(), + ) }, - ); + }); } } trait_infos => { diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 87b76b978b93..5a11cb7096f3 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -381,10 +381,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let maybe_missing_semi = self.check_for_missing_semi(expr, &mut err); // We defer to the later error produced by `check_lhs_assignable`. - // We only downgrade this if it's the LHS, though. + // We only downgrade this if it's the LHS, though, and if this is a + // valid assignment statement. if maybe_missing_semi && let hir::Node::Expr(parent) = self.tcx.parent_hir_node(expr.hir_id) && let hir::ExprKind::Assign(lhs, _, _) = parent.kind + && let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(parent.hir_id) + && let hir::StmtKind::Expr(_) | hir::StmtKind::Semi(_) = stmt.kind && lhs.hir_id == expr.hir_id { err.downgrade_to_delayed_bug(); @@ -817,7 +820,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the previous expression was a block expression, suggest parentheses // (turning this into a binary subtraction operation instead.) // for example, `{2} - 2` -> `({2}) - 2` (see src\test\ui\parser\expr-as-stmt.rs) - err.subdiagnostic(self.dcx(), ExprParenthesesNeeded::surrounding(*sp)); + err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } else { match actual.kind() { Uint(_) if op == hir::UnOp::Neg => { @@ -925,7 +928,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (obligation, _) = self.obligation_for_method(cause, trait_did, lhs_ty, Some(input_types)); // FIXME: This should potentially just add the obligation to the `FnCtxt` - let ocx = ObligationCtxt::new(&self.infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx); ocx.register_obligation(obligation); Err(ocx.select_all_or_error()) } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index b9b220d5af8e..aaf3d3ec34d0 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -7,12 +7,12 @@ use rustc_errors::{ }; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, Pat, PatKind}; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, LangItem, Mutability, Pat, PatKind}; use rustc_infer::infer; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; -use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; +use rustc_session::{lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS, parse::feature_err}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::Spanned; @@ -105,15 +105,16 @@ impl<'tcx> FnCtxt<'_, 'tcx> { expected: Ty<'tcx>, actual: Ty<'tcx>, ti: &TopInfo<'tcx>, - ) -> Option> { - let mut diag = - self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)?; - if let Some(expr) = ti.origin_expr { - self.suggest_fn_call(&mut diag, expr, expected, |output| { - self.can_eq(self.param_env, output, actual) - }); - } - Some(diag) + ) -> Result<(), Diag<'tcx>> { + self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual) + .map_err(|mut diag| { + if let Some(expr) = ti.origin_expr { + self.suggest_fn_call(&mut diag, expr, expected, |output| { + self.can_eq(self.param_env, output, actual) + }); + } + diag + }) } fn demand_eqtype_pat( @@ -122,10 +123,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> { expected: Ty<'tcx>, actual: Ty<'tcx>, ti: &TopInfo<'tcx>, - ) { - if let Some(err) = self.demand_eqtype_pat_diag(cause_span, expected, actual, ti) { - err.emit(); - } + ) -> Result<(), ErrorGuaranteed> { + self.demand_eqtype_pat_diag(cause_span, expected, actual, ti).map_err(|err| err.emit()) } } @@ -223,9 +222,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info; let path_res = match &pat.kind { - PatKind::Path(qpath) => Some( - self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span, None), - ), + PatKind::Path(qpath) => { + Some(self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span)) + } _ => None, }; let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); @@ -335,9 +334,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match adjust_mode { AdjustMode::Pass => (expected, def_br, max_ref_mutbl), AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut), - AdjustMode::Peel => { - self.peel_off_references(pat, expected, def_br, Mutability::Mut, max_ref_mutbl) - } + AdjustMode::Peel => self.peel_off_references(pat, expected, def_br, max_ref_mutbl), } } @@ -408,8 +405,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, mut def_br: ByRef, - max_peelable_mutability: Mutability, - mut max_ref_mutability: MutblCap, + mut max_ref_mutbl: MutblCap, ) -> (Ty<'tcx>, ByRef, MutblCap) { let mut expected = self.try_structurally_resolve_type(pat.span, expected); // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example, @@ -421,9 +417,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // See the examples in `ui/match-defbm*.rs`. let mut pat_adjustments = vec![]; - while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() - && inner_mutability <= max_peelable_mutability - { + while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() { debug!("inspecting {:?}", expected); debug!("current discriminant is Ref, inserting implicit deref"); @@ -443,10 +437,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); } - if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { - def_br = def_br.cap_ref_mutability(max_ref_mutability.as_mutbl()); + if self.tcx.features().ref_pat_eat_one_layer_2024 { + def_br = def_br.cap_ref_mutability(max_ref_mutbl.as_mutbl()); if def_br == ByRef::Yes(Mutability::Not) { - max_ref_mutability = MutblCap::Not; + max_ref_mutbl = MutblCap::Not; } } @@ -458,7 +452,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .insert(pat.hir_id, pat_adjustments); } - (expected, def_br, max_ref_mutability) + (expected, def_br, max_ref_mutbl) } fn check_pat_lit( @@ -497,7 +491,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; let expected = self.resolve_vars_if_possible(expected); pat_ty = match expected.kind() { - ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => expected, + ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => expected, ty::Str => Ty::new_static_str(tcx), _ => pat_ty, }; @@ -514,7 +508,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // then that's equivalent to there existing a LUB. let cause = self.pattern_cause(ti, span); - if let Some(err) = self.demand_suptype_with_origin(&cause, expected, pat_ty) { + if let Err(err) = self.demand_suptype_with_origin(&cause, expected, pat_ty) { err.emit_unless( ti.span .filter(|&s| { @@ -567,7 +561,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Subtyping doesn't matter here, as the value is some kind of scalar. let demand_eqtype = |x: &mut _, y| { if let Some((ref mut fail, x_ty, x_span)) = *x - && let Some(mut err) = self.demand_eqtype_pat_diag(x_span, expected, x_ty, ti) + && let Err(mut err) = self.demand_eqtype_pat_diag(x_span, expected, x_ty, ti) { if let Some((_, y_ty, y_span)) = y { self.endpoint_has_type(&mut err, y_span, y_ty); @@ -674,17 +668,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Determine the binding mode... let bm = match user_bind_annot { - // `mut` resets binding mode on edition <= 2021 - BindingMode(ByRef::No, Mutability::Mut) - if !(pat.span.at_least_rust_2024() - && self.tcx.features().mut_preserve_binding_mode_2024) - && matches!(def_br, ByRef::Yes(_)) => - { - self.typeck_results - .borrow_mut() - .rust_2024_migration_desugared_pats_mut() - .insert(pat_info.top_info.hir_id); - BindingMode(ByRef::No, Mutability::Mut) + BindingMode(ByRef::No, Mutability::Mut) if matches!(def_br, ByRef::Yes(_)) => { + if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { + if !self.tcx.features().mut_ref { + feature_err( + &self.tcx.sess, + sym::mut_ref, + pat.span.until(ident.span), + "binding cannot be both mutable and by-reference", + ) + .emit(); + } + + BindingMode(def_br, Mutability::Mut) + } else { + // `mut` resets binding mode on edition <= 2021 + self.typeck_results + .borrow_mut() + .rust_2024_migration_desugared_pats_mut() + .insert(pat_info.top_info.hir_id); + BindingMode(ByRef::No, Mutability::Mut) + } } BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl), BindingMode(ByRef::Yes(_), _) => user_bind_annot, @@ -731,7 +735,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Otherwise, the type of x is the expected type `T`. ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). }; - self.demand_eqtype_pat(pat.span, eq_ty, local_ty, ti); + + // We have a concrete type for the local, so we do not need to taint it and hide follow up errors *using* the local. + let _ = self.demand_eqtype_pat(pat.span, eq_ty, local_ty, ti); // If there are multiple arms, make sure they all agree on // what the type of the binding `x` ought to be. @@ -758,7 +764,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ti: &TopInfo<'tcx>, ) { let var_ty = self.local_ty(span, var_id); - if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) { + if let Err(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) { let hir = self.tcx.hir(); let var_ty = self.resolve_vars_if_possible(var_ty); let msg = format!("first introduced with type `{var_ty}` here"); @@ -981,13 +987,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // Type-check the path. - self.demand_eqtype_pat(pat.span, expected, pat_ty, pat_info.top_info); + let _ = self.demand_eqtype_pat(pat.span, expected, pat_ty, pat_info.top_info); // Type-check subpatterns. - if self.check_struct_pat_fields(pat_ty, pat, variant, fields, has_rest_pat, pat_info) { - pat_ty - } else { - Ty::new_misc_error(self.tcx) + match self.check_struct_pat_fields(pat_ty, pat, variant, fields, has_rest_pat, pat_info) { + Ok(()) => pat_ty, + Err(guar) => Ty::new_error(self.tcx, guar), } } @@ -1045,7 +1050,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type-check the path. let (pat_ty, pat_res) = self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.span, pat.hir_id); - if let Some(err) = + if let Err(err) = self.demand_suptype_with_origin(&self.pattern_cause(ti, pat.span), expected, pat_ty) { self.emit_bad_pat_path(err, pat, res, pat_res, pat_ty, segments); @@ -1184,7 +1189,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Resolve the path and check the definition for errors. let (res, opt_ty, segments) = - self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span, None); + self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span); if res == Res::Err { let e = tcx.dcx().span_delayed_bug(pat.span, "`Res::Err` but no error emitted"); self.set_tainted_by_errors(e); @@ -1218,12 +1223,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type-check the tuple struct pattern against the expected type. let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, pat_info.top_info); - let had_err = if let Some(err) = diag { - err.emit(); - true - } else { - false - }; + let had_err = diag.map_err(|diag| diag.emit()); // Type-check subpatterns. if subpats.len() == variant.fields.len() @@ -1244,6 +1244,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None, ); } + if let Err(e) = had_err { + on_error(e); + return Ty::new_error(tcx, e); + } } else { let e = self.emit_err_pat_wrong_number_of_fields( pat.span, @@ -1268,7 +1272,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { subpats: &'tcx [Pat<'tcx>], fields: &'tcx [ty::FieldDef], expected: Ty<'tcx>, - had_err: bool, + had_err: Result<(), ErrorGuaranteed>, ) -> ErrorGuaranteed { let subpats_ending = pluralize!(subpats.len()); let fields_ending = pluralize!(fields.len()); @@ -1325,7 +1329,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // #67037: only do this if we could successfully type-check the expected type against // the tuple struct pattern. Otherwise the args could get out of range on e.g., // `let P() = U;` where `P != U` with `struct P(T);`. - (ty::Adt(_, args), [field], false) => { + (ty::Adt(_, args), [field], Ok(())) => { let field_ty = self.field_ty(pat_span, field, args); match field_ty.kind() { ty::Tuple(fields) => fields.len() == subpats.len(), @@ -1440,8 +1444,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let element_tys_iter = (0..max_len).map(|_| self.next_ty_var(span)); let element_tys = tcx.mk_type_list_from_iter(element_tys_iter); let pat_ty = Ty::new_tup(tcx, element_tys); - if let Some(err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, pat_info.top_info) { - let reported = err.emit(); + if let Err(reported) = self.demand_eqtype_pat(span, expected, pat_ty, pat_info.top_info) { // Walk subpatterns with an expected type of `err` in this case to silence // further errors being emitted when using the bindings. #50333 let element_tys_iter = (0..max_len).map(|_| Ty::new_error(tcx, reported)); @@ -1465,7 +1468,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &'tcx [hir::PatField<'tcx>], has_rest_pat: bool, pat_info: PatInfo<'tcx, '_>, - ) -> bool { + ) -> Result<(), ErrorGuaranteed> { let tcx = self.tcx; let ty::Adt(adt, args) = adt_ty.kind() else { @@ -1481,7 +1484,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Keep track of which fields have already appeared in the pattern. let mut used_fields = FxHashMap::default(); - let mut no_field_errors = true; + let mut result = Ok(()); let mut inexistent_fields = vec![]; // Typecheck each field. @@ -1490,8 +1493,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ident = tcx.adjust_ident(field.ident, variant.def_id); let field_ty = match used_fields.entry(ident) { Occupied(occupied) => { - no_field_errors = false; let guar = self.error_field_already_bound(span, field.ident, *occupied.get()); + result = Err(guar); Ty::new_error(tcx, guar) } Vacant(vacant) => { @@ -1506,7 +1509,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .unwrap_or_else(|| { inexistent_fields.push(field); - no_field_errors = false; Ty::new_misc_error(tcx) }) } @@ -1580,37 +1582,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } match (inexistent_fields_err, unmentioned_err) { (Some(i), Some(u)) => { - if let Some(e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) { + if let Err(e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) { // We don't want to show the nonexistent fields error when this was // `Foo { a, b }` when it should have been `Foo(a, b)`. i.delay_as_bug(); u.delay_as_bug(); - e.emit(); + Err(e) } else { i.emit(); - u.emit(); + Err(u.emit()) } } (None, Some(u)) => { - if let Some(e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) { + if let Err(e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) { u.delay_as_bug(); - e.emit(); + Err(e) } else { - u.emit(); + Err(u.emit()) } } - (Some(err), None) => { - err.emit(); + (Some(err), None) => Err(err.emit()), + (None, None) => { + self.error_tuple_variant_index_shorthand(variant, pat, fields)?; + result } - (None, None) - if let Some(err) = - self.error_tuple_variant_index_shorthand(variant, pat, fields) => - { - err.emit(); - } - (None, None) => {} } - no_field_errors } fn error_tuple_variant_index_shorthand( @@ -1618,7 +1614,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant: &VariantDef, pat: &'_ Pat<'_>, fields: &[hir::PatField<'_>], - ) -> Option> { + ) -> Result<(), ErrorGuaranteed> { // if this is a tuple struct, then all field names will be numbers // so if any fields in a struct pattern use shorthand syntax, they will // be invalid identifiers (for example, Foo { 0, 1 }). @@ -1640,10 +1636,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("({})", self.get_suggested_tuple_struct_pattern(fields, variant)), Applicability::MaybeIncorrect, ); - return Some(err); + return Err(err.emit()); } } - None + Ok(()) } fn error_foreign_non_exhaustive_spat(&self, pat: &Pat<'_>, descr: &str, no_fields: bool) { @@ -1799,14 +1795,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &Pat<'_>, fields: &'tcx [hir::PatField<'tcx>], variant: &ty::VariantDef, - ) -> Option> { + ) -> Result<(), ErrorGuaranteed> { if let (Some(CtorKind::Fn), PatKind::Struct(qpath, pattern_fields, ..)) = (variant.ctor_kind(), &pat.kind) { let is_tuple_struct_match = !pattern_fields.is_empty() && pattern_fields.iter().map(|field| field.ident.name.as_str()).all(is_number); if is_tuple_struct_match { - return None; + return Ok(()); } let path = rustc_hir_pretty::qpath_to_string(&self.tcx, qpath); @@ -1834,9 +1830,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("({sugg})"), appl, ); - return Some(err); + return Err(err.emit()); } - None + Ok(()) } fn get_suggested_tuple_struct_pattern( @@ -1950,15 +1946,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &unmentioned_fields.iter().map(|(_, i)| i).collect::>(), ); - self.tcx.node_span_lint(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, "some fields are not explicitly listed", |lint| { - lint.span_label(pat.span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns)); - lint.help( - "ensure that all fields are mentioned explicitly by adding the suggested fields", - ); - lint.note(format!( - "the pattern is of type `{ty}` and the `non_exhaustive_omitted_patterns` attribute was found", - )); - }); + self.tcx.node_span_lint(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, |lint| { + lint.primary_message("some fields are not explicitly listed"); + lint.span_label(pat.span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns)); + lint.help( + "ensure that all fields are mentioned explicitly by adding the suggested fields", + ); + lint.note(format!( + "the pattern is of type `{ty}` and the `non_exhaustive_omitted_patterns` attribute was found", + )); + }); } /// Returns a diagnostic reporting a struct pattern which does not mention some fields. @@ -2059,20 +2056,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { let tcx = self.tcx; - let (box_ty, inner_ty) = match self.check_dereferenceable(span, expected, inner) { - Ok(()) => { + let (box_ty, inner_ty) = self + .check_dereferenceable(span, expected, inner) + .and_then(|()| { // Here, `demand::subtype` is good enough, but I don't // think any errors can be introduced by using `demand::eqtype`. let inner_ty = self.next_ty_var(inner.span); let box_ty = Ty::new_box(tcx, inner_ty); - self.demand_eqtype_pat(span, expected, box_ty, pat_info.top_info); - (box_ty, inner_ty) - } - Err(guar) => { + self.demand_eqtype_pat(span, expected, box_ty, pat_info.top_info)?; + Ok((box_ty, inner_ty)) + }) + .unwrap_or_else(|guar| { let err = Ty::new_error(tcx, guar); (err, err) - } - }; + }); self.check_pat(inner, inner_ty, pat_info); box_ty } @@ -2125,57 +2122,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mut expected: Ty<'tcx>, mut pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { - // FIXME: repace with `bool` once final decision on 1 vs 2 layers is made - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - enum MatchErgonomicsMode { - EatOneLayer, - EatTwoLayers, - Legacy, - } + let no_ref_mut_behind_and = self.tcx.features().ref_pat_eat_one_layer_2024; + let new_match_ergonomics = pat.span.at_least_rust_2024() && no_ref_mut_behind_and; - let match_ergonomics_mode = - if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { - MatchErgonomicsMode::EatOneLayer - } else if self.tcx.features().ref_pat_everywhere { - MatchErgonomicsMode::EatTwoLayers - } else { - MatchErgonomicsMode::Legacy - }; + let pat_prefix_span = + inner.span.find_ancestor_inside(pat.span).map(|end| pat.span.until(end)); - let mut inherited_ref_mutbl_match = false; - if match_ergonomics_mode != MatchErgonomicsMode::Legacy { + if no_ref_mut_behind_and { if pat_mutbl == Mutability::Not { // Prevent the inner pattern from binding with `ref mut`. - pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not( - inner.span.find_ancestor_inside(pat.span).map(|end| pat.span.until(end)), - ); + pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not(pat_prefix_span); } + } else { + pat_info.max_ref_mutbl = MutblCap::Mut; + } + if new_match_ergonomics { if let ByRef::Yes(inh_mut) = pat_info.binding_mode { - inherited_ref_mutbl_match = pat_mutbl <= inh_mut; - } + // ref pattern consumes inherited reference - if inherited_ref_mutbl_match { - pat_info.binding_mode = ByRef::No; - if match_ergonomics_mode == MatchErgonomicsMode::EatOneLayer { - self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); - self.check_pat(inner, expected, pat_info); - return expected; + if pat_mutbl > inh_mut { + // Tried to match inherited `ref` with `&mut`, which is an error + let err_msg = "cannot match inherited `&` with `&mut` pattern"; + let err = if let Some(span) = pat_prefix_span { + let mut err = self.dcx().struct_span_err(span, err_msg); + err.span_suggestion_verbose( + span, + "replace this `&mut` pattern with `&`", + "&", + Applicability::MachineApplicable, + ); + err + } else { + self.dcx().struct_span_err(pat.span, err_msg) + }; + err.emit(); } - } else if match_ergonomics_mode == MatchErgonomicsMode::EatOneLayer - && pat_mutbl == Mutability::Mut - { - // `&mut` patterns pell off `&` references - let (new_expected, new_bm, max_ref_mutbl) = self.peel_off_references( - pat, - expected, - pat_info.binding_mode, - Mutability::Not, - pat_info.max_ref_mutbl, - ); - expected = new_expected; - pat_info.binding_mode = new_bm; - pat_info.max_ref_mutbl = max_ref_mutbl; + + pat_info.binding_mode = ByRef::No; + self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); + self.check_pat(inner, expected, pat_info); + return expected; } } else { // Reset binding mode on old editions @@ -2188,8 +2175,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .rust_2024_migration_desugared_pats_mut() .insert(pat_info.top_info.hir_id); } - - pat_info.max_ref_mutbl = MutblCap::Mut; } let tcx = self.tcx; @@ -2204,34 +2189,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the bad interactions of the given hack detailed in (note_1). debug!("check_pat_ref: expected={:?}", expected); match *expected.kind() { - ty::Ref(_, r_ty, r_mutbl) if r_mutbl == pat_mutbl => { - if r_mutbl == Mutability::Not - && match_ergonomics_mode != MatchErgonomicsMode::Legacy - { + ty::Ref(_, r_ty, r_mutbl) + if (new_match_ergonomics && r_mutbl >= pat_mutbl) + || r_mutbl == pat_mutbl => + { + if no_ref_mut_behind_and && r_mutbl == Mutability::Not { pat_info.max_ref_mutbl = MutblCap::Not; } (expected, r_ty) } - // `&` pattern eats `&mut` reference - ty::Ref(_, r_ty, Mutability::Mut) - if pat_mutbl == Mutability::Not - && match_ergonomics_mode != MatchErgonomicsMode::Legacy => - { - (expected, r_ty) - } - - _ if inherited_ref_mutbl_match - && match_ergonomics_mode == MatchErgonomicsMode::EatTwoLayers => - { - // We already matched against a match-ergonmics inserted reference, - // so we don't need to match against a reference from the original type. - // Save this info for use in lowering later - self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); - (expected, expected) - } - _ => { let inner_ty = self.next_ty_var(inner.span); let ref_ty = self.new_ref_ty(pat.span, pat_mutbl, inner_ty); @@ -2245,7 +2213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Look for a case like `fn foo(&foo: u32)` and suggest // `fn foo(foo: &u32)` - if let Some(mut err) = err { + if let Err(mut err) = err { self.borrow_pat_suggestion(&mut err, pat); err.emit(); } @@ -2350,7 +2318,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.try_resolve_slice_ty_to_array_ty(before, slice, span) { debug!(?resolved_arr_ty); - self.demand_eqtype(span, expected, resolved_arr_ty); + let _ = self.demand_eqtype(span, expected, resolved_arr_ty); } } @@ -2408,10 +2376,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { min_len: u64, ) -> (Option>, Ty<'tcx>) { let len = match len.eval(self.tcx, self.param_env, span) { - Ok(val) => val + Ok((_, val)) => val .try_to_scalar() - .and_then(|scalar| scalar.try_to_int().ok()) - .and_then(|int| int.try_to_target_usize(self.tcx).ok()), + .and_then(|scalar| scalar.try_to_scalar_int().ok()) + .map(|int| int.to_target_usize(self.tcx)), Err(ErrorHandled::Reported(..)) => { let guar = self.error_scrutinee_unfixed_length(span); return (Some(Ty::new_error(self.tcx, guar)), arr_ty); diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs index 19d6481cc1b8..b6e9000ef950 100644 --- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -11,7 +11,9 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefIdMap; use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use rustc_trait_selection::traits::{self, PredicateObligation, TraitEngine, TraitEngineExt as _}; +use rustc_trait_selection::traits::{ + self, FulfillmentError, PredicateObligation, TraitEngine, TraitEngineExt as _, +}; use std::cell::RefCell; use std::ops::Deref; @@ -20,7 +22,7 @@ use std::ops::Deref; /// e.g. closures defined within the function. For example: /// ```ignore (illustrative) /// fn foo() { -/// bar(move|| { ... }) +/// bar(move || { ... }) /// } /// ``` /// Here, the function `foo()` and the closure passed to @@ -34,7 +36,7 @@ pub(crate) struct TypeckRootCtxt<'tcx> { pub(super) locals: RefCell>>, - pub(super) fulfillment_cx: RefCell>>, + pub(super) fulfillment_cx: RefCell>>>, /// Some additional `Sized` obligations badly affect type inference. /// These obligations are added in a later stage of typeck. @@ -83,7 +85,7 @@ impl<'tcx> TypeckRootCtxt<'tcx> { TypeckRootCtxt { typeck_results, - fulfillment_cx: RefCell::new(>::new(&infcx)), + fulfillment_cx: RefCell::new(>::new(&infcx)), infcx, locals: RefCell::new(Default::default()), deferred_sized_obligations: RefCell::new(Vec::new()), @@ -158,7 +160,7 @@ impl<'tcx> TypeckRootCtxt<'tcx> { { // If the projection predicate (Foo::Bar == X) has X as a non-TyVid, // we need to make it into one. - if let Some(vid) = predicate.term.ty().and_then(|ty| ty.ty_vid()) { + if let Some(vid) = predicate.term.as_type().and_then(|ty| ty.ty_vid()) { debug!("infer_var_info: {:?}.output = true", vid); infer_var_info.entry(vid).or_default().output = true; } diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 9d16f0d48159..466397817dae 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -204,6 +204,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fake_reads: Default::default(), }; + let _ = euv::ExprUseVisitor::new( + &FnCtxt::new(self, self.tcx.param_env(closure_def_id), closure_def_id), + &mut delegate, + ) + .consume_body(body); + + // There are several curious situations with coroutine-closures where + // analysis is too aggressive with borrows when the coroutine-closure is + // marked `move`. Specifically: + // + // 1. If the coroutine-closure was inferred to be `FnOnce` during signature + // inference, then it's still possible that we try to borrow upvars from + // the coroutine-closure because they are not used by the coroutine body + // in a way that forces a move. See the test: + // `async-await/async-closures/force-move-due-to-inferred-kind.rs`. + // + // 2. If the coroutine-closure is forced to be `FnOnce` due to the way it + // uses its upvars, but not *all* upvars would force the closure to `FnOnce`. + // See the test: `async-await/async-closures/force-move-due-to-actually-fnonce.rs`. + // + // This would lead to an impossible to satisfy situation, since `AsyncFnOnce` + // coroutine bodies can't borrow from their parent closure. To fix this, + // we force the inner coroutine to also be `move`. This only matters for + // coroutine-closures that are `move` since otherwise they themselves will + // be borrowing from the outer environment, so there's no self-borrows occuring. + // + // One *important* note is that we do a call to `process_collected_capture_information` + // to eagerly test whether the coroutine would end up `FnOnce`, but we do this + // *before* capturing all the closure args by-value below, since that would always + // cause the analysis to return `FnOnce`. + if let UpvarArgs::Coroutine(..) = args + && let hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) = + self.tcx.coroutine_kind(closure_def_id).expect("coroutine should have kind") + && let parent_hir_id = + self.tcx.local_def_id_to_hir_id(self.tcx.local_parent(closure_def_id)) + && let parent_ty = self.node_ty(parent_hir_id) + && let hir::CaptureBy::Value { move_kw } = + self.tcx.hir_node(parent_hir_id).expect_closure().capture_clause + { + // (1.) Closure signature inference forced this closure to `FnOnce`. + if let Some(ty::ClosureKind::FnOnce) = self.closure_kind(parent_ty) { + capture_clause = hir::CaptureBy::Value { move_kw }; + } + // (2.) The way that the closure uses its upvars means it's `FnOnce`. + else if let (_, ty::ClosureKind::FnOnce, _) = self + .process_collected_capture_information( + capture_clause, + &delegate.capture_information, + ) + { + capture_clause = hir::CaptureBy::Value { move_kw }; + } + } + // As noted in `lower_coroutine_body_with_moved_arguments`, we default the capture mode // to `ByRef` for the `async {}` block internal to async fns/closure. This means // that we would *not* be moving all of the parameters into the async block by default. @@ -253,34 +307,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let _ = euv::ExprUseVisitor::new( - &FnCtxt::new(self, self.tcx.param_env(closure_def_id), closure_def_id), - &mut delegate, - ) - .consume_body(body); - - // If a coroutine is comes from a coroutine-closure that is `move`, but - // the coroutine-closure was inferred to be `FnOnce` during signature - // inference, then it's still possible that we try to borrow upvars from - // the coroutine-closure because they are not used by the coroutine body - // in a way that forces a move. - // - // This would lead to an impossible to satisfy situation, since `AsyncFnOnce` - // coroutine bodies can't borrow from their parent closure. To fix this, - // we force the inner coroutine to also be `move`. This only matters for - // coroutine-closures that are `move` since otherwise they themselves will - // be borrowing from the outer environment, so there's no self-borrows occuring. - if let UpvarArgs::Coroutine(..) = args - && let hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) = - self.tcx.coroutine_kind(closure_def_id).expect("coroutine should have kind") - && let parent_hir_id = - self.tcx.local_def_id_to_hir_id(self.tcx.local_parent(closure_def_id)) - && let parent_ty = self.node_ty(parent_hir_id) - && let Some(ty::ClosureKind::FnOnce) = self.closure_kind(parent_ty) - { - capture_clause = self.tcx.hir_node(parent_hir_id).expect_closure().capture_clause; - } - debug!( "For closure={:?}, capture_information={:#?}", closure_def_id, delegate.capture_information @@ -289,7 +315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span); let (capture_information, closure_kind, origin) = self - .process_collected_capture_information(capture_clause, delegate.capture_information); + .process_collected_capture_information(capture_clause, &delegate.capture_information); self.compute_min_captures(closure_def_id, capture_information, span); @@ -545,13 +571,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn process_collected_capture_information( &self, capture_clause: hir::CaptureBy, - capture_information: InferredCaptureInformation<'tcx>, + capture_information: &InferredCaptureInformation<'tcx>, ) -> (InferredCaptureInformation<'tcx>, ty::ClosureKind, Option<(Span, Place<'tcx>)>) { let mut closure_kind = ty::ClosureKind::LATTICE_BOTTOM; let mut origin: Option<(Span, Place<'tcx>)> = None; let processed = capture_information - .into_iter() + .iter() + .cloned() .map(|(place, mut capture_info)| { // Apply rules for safety before inferring closure kind let (place, capture_kind) = @@ -928,8 +955,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_hir_id, closure_head_span, - reasons.migration_message(), |lint| { + lint.primary_message(reasons.migration_message()); + for NeededMigration { var_hir_id, diagnostics_info } in &need_migrations { // Labels all the usage of the captured variable and why they are responsible // for migration being needed diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index f798deea207a..b67d29fce921 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -862,8 +862,9 @@ impl<'cx, 'tcx> TypeFolder> for Resolver<'cx, 'tcx> { fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { self.handle_term(ct, ty::Const::outer_exclusive_binder, |tcx, guar| { - ty::Const::new_error(tcx, guar, ct.ty()) + ty::Const::new_error(tcx, guar) }) + .super_fold_with(self) } fn fold_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { diff --git a/compiler/rustc_incremental/messages.ftl b/compiler/rustc_incremental/messages.ftl index e74173b24a97..de2177ebb6e0 100644 --- a/compiler/rustc_incremental/messages.ftl +++ b/compiler/rustc_incremental/messages.ftl @@ -21,6 +21,8 @@ incremental_cargo_help_2 = incremental_copy_workproduct_to_cache = error copying object file `{$from}` to incremental directory as `{$to}`: {$err} +incremental_corrupt_file = corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant. + incremental_create_dep_graph = failed to create dependency graph at `{$path}`: {$err} incremental_create_incr_comp_dir = diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 8c66f239f8ee..41caa5d4765b 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -49,10 +49,10 @@ use rustc_middle::ty::TyCtxt; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; - use std::env; use std::fs::{self, File}; use std::io::{BufWriter, Write}; +use tracing::debug; #[allow(missing_docs)] pub fn assert_dep_graph(tcx: TyCtxt<'_>) { diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs index 61bb0353a9f4..e94a7fb876bb 100644 --- a/compiler/rustc_incremental/src/errors.rs +++ b/compiler/rustc_incremental/src/errors.rs @@ -306,3 +306,9 @@ pub struct DeleteWorkProduct<'a> { pub path: &'a Path, pub err: std::io::Error, } + +#[derive(Diagnostic)] +#[diag(incremental_corrupt_file)] +pub struct CorruptFile<'a> { + pub path: &'a Path, +} diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index 79402c88de47..76e3c0682deb 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -1,13 +1,12 @@ //! Support for serializing the dep-graph and reloading it. +// tidy-alphabetical-start +#![allow(internal_features)] #![deny(missing_docs)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![allow(internal_features)] - -#[macro_use] -extern crate tracing; +// tidy-alphabetical-end mod assert_dep_graph; mod errors; diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index e901ca36dade..2a0d681fa37e 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -33,6 +33,7 @@ use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use thin_vec::ThinVec; +use tracing::debug; const LOADED_FROM_DISK: Symbol = sym::loaded_from_disk; const EXCEPT: Symbol = sym::except; diff --git a/compiler/rustc_incremental/src/persist/file_format.rs b/compiler/rustc_incremental/src/persist/file_format.rs index b459f82f23e3..303785bdb220 100644 --- a/compiler/rustc_incremental/src/persist/file_format.rs +++ b/compiler/rustc_incremental/src/persist/file_format.rs @@ -19,6 +19,7 @@ use std::env; use std::fs; use std::io::{self, Read}; use std::path::{Path, PathBuf}; +use tracing::debug; /// The first few bytes of files generated by incremental compilation. const FILE_MAGIC: &[u8] = b"RSIC"; diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 193042b8cdf2..9afea3d66b0d 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -125,6 +125,7 @@ use std::path::{Path, PathBuf}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use rand::{thread_rng, RngCore}; +use tracing::debug; #[cfg(test)] mod tests; diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 26aaa24771fe..af667a57ce12 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -12,6 +12,7 @@ use rustc_session::Session; use rustc_span::ErrorGuaranteed; use std::path::{Path, PathBuf}; use std::sync::Arc; +use tracing::{debug, warn}; use super::data::*; use super::file_format; @@ -115,7 +116,11 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc, WorkPr if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result { // Decode the list of work_products - let mut work_product_decoder = MemDecoder::new(&work_products_data[..], start_pos); + let Ok(mut work_product_decoder) = MemDecoder::new(&work_products_data[..], start_pos) + else { + sess.dcx().emit_warn(errors::CorruptFile { path: &work_products_path }); + return LoadResult::DataOutOfDate; + }; let work_products: Vec = Decodable::decode(&mut work_product_decoder); @@ -145,7 +150,10 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc, WorkPr LoadResult::DataOutOfDate => LoadResult::DataOutOfDate, LoadResult::LoadDepGraph(path, err) => LoadResult::LoadDepGraph(path, err), LoadResult::Ok { data: (bytes, start_pos) } => { - let mut decoder = MemDecoder::new(&bytes, start_pos); + let Ok(mut decoder) = MemDecoder::new(&bytes, start_pos) else { + sess.dcx().emit_warn(errors::CorruptFile { path: &path }); + return LoadResult::DataOutOfDate; + }; let prev_commandline_args_hash = u64::decode(&mut decoder); if prev_commandline_args_hash != expected_hash { @@ -181,9 +189,14 @@ pub fn load_query_result_cache(sess: &Session) -> Option> { let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache"); - match load_data(&query_cache_path(sess), sess) { + let path = query_cache_path(sess); + match load_data(&path, sess) { LoadResult::Ok { data: (bytes, start_pos) } => { - Some(OnDiskCache::new(sess, bytes, start_pos)) + let cache = OnDiskCache::new(sess, bytes, start_pos).unwrap_or_else(|()| { + sess.dcx().emit_warn(errors::CorruptFile { path: &path }); + OnDiskCache::new_empty(sess.source_map()) + }); + Some(cache) } _ => Some(OnDiskCache::new_empty(sess.source_map())), } diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 9777f7692809..3bf582bd26c6 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -11,6 +11,7 @@ use rustc_serialize::Encodable as RustcEncodable; use rustc_session::Session; use std::fs; use std::sync::Arc; +use tracing::debug; use super::data::*; use super::dirty_clean; diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index 906233ef53ec..e230da9dfb12 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -10,6 +10,7 @@ use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; use std::fs as std_fs; use std::path::Path; +use tracing::debug; /// Copies a CGU work product to the incremental compilation directory, so next compilation can /// find and reuse it. diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index 6fcb3a024ab6..db6b250467e5 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -1,9 +1,11 @@ +// tidy-alphabetical-start +#![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))] #![cfg_attr( feature = "nightly", feature(extend_one, min_specialization, new_uninit, step_trait, test) )] -#![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))] #![cfg_attr(feature = "nightly", allow(internal_features))] +// tidy-alphabetical-end pub mod bit_set; #[cfg(feature = "nightly")] diff --git a/compiler/rustc_index_macros/Cargo.toml b/compiler/rustc_index_macros/Cargo.toml index c4ca29db3c2d..07ee81788ce4 100644 --- a/compiler/rustc_index_macros/Cargo.toml +++ b/compiler/rustc_index_macros/Cargo.toml @@ -7,11 +7,10 @@ edition = "2021" proc-macro = true [dependencies] -synstructure = "0.13.0" syn = { version = "2.0.9", features = ["full"] } proc-macro2 = "1" quote = "1" [features] default = ["nightly"] -nightly = [] \ No newline at end of file +nightly = [] diff --git a/compiler/rustc_index_macros/src/lib.rs b/compiler/rustc_index_macros/src/lib.rs index 015518ae4d68..3e55dd82a6e8 100644 --- a/compiler/rustc_index_macros/src/lib.rs +++ b/compiler/rustc_index_macros/src/lib.rs @@ -1,5 +1,7 @@ -#![cfg_attr(feature = "nightly", feature(allow_internal_unstable))] +// tidy-alphabetical-start #![cfg_attr(feature = "nightly", allow(internal_features))] +#![cfg_attr(feature = "nightly", feature(allow_internal_unstable))] +// tidy-alphabetical-end use proc_macro::TokenStream; diff --git a/compiler/rustc_index_macros/src/newtype.rs b/compiler/rustc_index_macros/src/newtype.rs index fe9a048734fc..41863f7b15f9 100644 --- a/compiler/rustc_index_macros/src/newtype.rs +++ b/compiler/rustc_index_macros/src/newtype.rs @@ -205,6 +205,21 @@ impl Parse for Newtype { } } + /// Creates a new index from a given `u16`. + /// + /// # Panics + /// + /// Will panic if `value` exceeds `MAX`. + #[inline] + #vis const fn from_u16(value: u16) -> Self { + let value = value as u32; + assert!(value <= #max); + // SAFETY: We just checked that `value <= max`. + unsafe { + Self::from_u32_unchecked(value) + } + } + /// Creates a new index from a given `u32`. /// /// # Safety diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index 64f52ea7ac19..fbe8d31370cc 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -104,10 +104,10 @@ infer_compare_impl_item_obligation = ...so that the definition in impl matches t infer_consider_specifying_length = consider specifying the actual array length infer_data_flows = ...but data{$label_var1_exists -> [true] {" "}from `{$label_var1}` - *[false] -> {""} + *[false] {""} } flows{$label_var2_exists -> [true] {" "}into `{$label_var2}` - *[false] -> {""} + *[false] {""} } here infer_data_lifetime_flow = ...but data with one lifetime flows into the other here @@ -164,7 +164,10 @@ infer_label_bad = {$bad_kind -> infer_lf_bound_not_satisfied = lifetime bound not satisfied infer_lifetime_mismatch = lifetime mismatch -infer_lifetime_param_suggestion = consider introducing a named lifetime parameter{$is_impl -> +infer_lifetime_param_suggestion = consider {$is_reuse -> + [true] reusing + *[false] introducing +} a named lifetime parameter{$is_impl -> [true] {" "}and update trait if needed *[false] {""} } diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 2acaeac24398..a801001eaf98 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1,9 +1,12 @@ use hir::GenericParamKind; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ codes::*, Applicability, Diag, DiagMessage, DiagStyledString, EmissionGuarantee, IntoDiagArg, MultiSpan, SubdiagMessageOp, Subdiagnostic, }; use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::FnRetTy; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::print::TraitRefPrintOnlyTraitPath; @@ -342,6 +345,7 @@ impl Subdiagnostic for LifetimeMismatchLabels { pub struct AddLifetimeParamsSuggestion<'a> { pub tcx: TyCtxt<'a>, + pub generic_param_scope: LocalDefId, pub sub: Region<'a>, pub ty_sup: &'a hir::Ty<'a>, pub ty_sub: &'a hir::Ty<'a>, @@ -355,31 +359,34 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { _f: &F, ) { let mut mk_suggestion = || { - let ( - hir::Ty { kind: hir::TyKind::Ref(lifetime_sub, _), .. }, - hir::Ty { kind: hir::TyKind::Ref(lifetime_sup, _), .. }, - ) = (self.ty_sub, self.ty_sup) + let Some(anon_reg) = self.tcx.is_suitable_region(self.generic_param_scope, self.sub) else { return false; }; - if !lifetime_sub.is_anonymous() || !lifetime_sup.is_anonymous() { - return false; - }; - - let Some(anon_reg) = self.tcx.is_suitable_region(self.sub) else { - return false; - }; - let node = self.tcx.hir_node_by_def_id(anon_reg.def_id); let is_impl = matches!(&node, hir::Node::ImplItem(_)); - let generics = match node { + let (generics, parent_generics) = match node { hir::Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, ref generics, ..), .. }) | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. }) - | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics, + | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => ( + generics, + match self.tcx.parent_hir_node(self.tcx.local_def_id_to_hir_id(anon_reg.def_id)) + { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Trait(_, _, ref generics, ..), + .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { ref generics, .. }), + .. + }) => Some(generics), + _ => None, + }, + ), _ => return false, }; @@ -390,24 +397,112 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { .map(|p| p.name.ident().name) .find(|i| *i != kw::UnderscoreLifetime); let introduce_new = suggestion_param_name.is_none(); + + let mut default = "'a".to_string(); + if let Some(parent_generics) = parent_generics { + let used: FxHashSet<_> = parent_generics + .params + .iter() + .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) + .map(|p| p.name.ident().name) + .filter(|i| *i != kw::UnderscoreLifetime) + .map(|l| l.to_string()) + .collect(); + if let Some(lt) = + ('a'..='z').map(|it| format!("'{it}")).find(|it| !used.contains(it)) + { + // We want a lifetime that *isn't* present in the `trait` or `impl` that assoc + // `fn` belongs to. We could suggest reusing one of their lifetimes, but it is + // likely to be an over-constraining lifetime requirement, so we always add a + // lifetime to the `fn`. + default = lt; + } + } let suggestion_param_name = - suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned()); + suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| default); - debug!(?lifetime_sup.ident.span); - debug!(?lifetime_sub.ident.span); - let make_suggestion = |ident: Ident| { - let sugg = if ident.name == kw::Empty { - format!("{suggestion_param_name}, ") - } else if ident.name == kw::UnderscoreLifetime && ident.span.is_empty() { - format!("{suggestion_param_name} ") - } else { - suggestion_param_name.clone() - }; - (ident.span, sugg) + struct ImplicitLifetimeFinder { + suggestions: Vec<(Span, String)>, + suggestion_param_name: String, + } + + impl<'v> Visitor<'v> for ImplicitLifetimeFinder { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + let make_suggestion = |ident: Ident| { + if ident.name == kw::Empty && ident.span.is_empty() { + format!("{}, ", self.suggestion_param_name) + } else if ident.name == kw::UnderscoreLifetime && ident.span.is_empty() { + format!("{} ", self.suggestion_param_name) + } else { + self.suggestion_param_name.clone() + } + }; + match ty.kind { + hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { + for segment in path.segments { + if let Some(args) = segment.args { + if args.args.iter().all(|arg| { + matches!( + arg, + hir::GenericArg::Lifetime(lifetime) + if lifetime.ident.name == kw::Empty + ) + }) { + self.suggestions.push(( + segment.ident.span.shrink_to_hi(), + format!( + "<{}>", + args.args + .iter() + .map(|_| self.suggestion_param_name.clone()) + .collect::>() + .join(", ") + ), + )); + } else { + for arg in args.args { + if let hir::GenericArg::Lifetime(lifetime) = arg + && lifetime.is_anonymous() + { + self.suggestions.push(( + lifetime.ident.span, + make_suggestion(lifetime.ident), + )); + } + } + } + } + } + } + hir::TyKind::Ref(lifetime, ..) if lifetime.is_anonymous() => { + self.suggestions + .push((lifetime.ident.span, make_suggestion(lifetime.ident))); + } + _ => {} + } + walk_ty(self, ty); + } + } + let mut visitor = ImplicitLifetimeFinder { + suggestions: vec![], + suggestion_param_name: suggestion_param_name.clone(), }; - let mut suggestions = - vec![make_suggestion(lifetime_sub.ident), make_suggestion(lifetime_sup.ident)]; - + if let Some(fn_decl) = node.fn_decl() + && let hir::FnRetTy::Return(ty) = fn_decl.output + { + visitor.visit_ty(ty); + } + if visitor.suggestions.is_empty() { + // Do not suggest constraining the `&self` param, but rather the return type. + // If that is wrong (because it is not sufficient), a follow up error will tell the + // user to fix it. This way we lower the chances of *over* constraining, but still + // get the cake of "correctly" contrained in two steps. + visitor.visit_ty(self.ty_sup); + } + visitor.visit_ty(self.ty_sub); + if visitor.suggestions.is_empty() { + return false; + } if introduce_new { let new_param_suggestion = if let Some(first) = generics.params.iter().find(|p| !p.name.ident().span.is_empty()) @@ -417,15 +512,16 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { (generics.span, format!("<{suggestion_param_name}>")) }; - suggestions.push(new_param_suggestion); + visitor.suggestions.push(new_param_suggestion); } - - diag.multipart_suggestion( + diag.multipart_suggestion_verbose( fluent::infer_lifetime_param_suggestion, - suggestions, + visitor.suggestions, Applicability::MaybeIncorrect, ); diag.arg("is_impl", is_impl); + diag.arg("is_reuse", !introduce_new); + true }; if mk_suggestion() && self.add_note { diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index f0b336ca0461..4fbeb0ec1024 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -1,6 +1,7 @@ use crate::fluent_generated as fluent; use crate::infer::error_reporting::nice_region_error::find_anon_type; use rustc_errors::{Diag, EmissionGuarantee, IntoDiagArg, SubdiagMessageOp, Subdiagnostic}; +use rustc_hir::def_id::LocalDefId; use rustc_middle::bug; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{symbol::kw, Span}; @@ -14,12 +15,15 @@ struct DescriptionCtx<'a> { impl<'a> DescriptionCtx<'a> { fn new<'tcx>( tcx: TyCtxt<'tcx>, + generic_param_scope: LocalDefId, region: ty::Region<'tcx>, alt_span: Option, ) -> Option { let (span, kind, arg) = match *region { - ty::ReEarlyParam(ref br) => { - let scope = region.free_region_binding_scope(tcx).expect_local(); + ty::ReEarlyParam(br) => { + let scope = tcx + .parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id) + .expect_local(); let span = if let Some(param) = tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) { @@ -35,11 +39,12 @@ impl<'a> DescriptionCtx<'a> { } ty::ReLateParam(ref fr) => { if !fr.bound_region.is_named() - && let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region) + && let Some((ty, _)) = + find_anon_type(tcx, generic_param_scope, region, &fr.bound_region) { (Some(ty.span), "defined_here", String::new()) } else { - let scope = region.free_region_binding_scope(tcx).expect_local(); + let scope = fr.scope.expect_local(); match fr.bound_region { ty::BoundRegionKind::BrNamed(_, name) => { let span = if let Some(param) = tcx @@ -143,12 +148,17 @@ pub struct RegionExplanation<'a> { impl RegionExplanation<'_> { pub fn new<'tcx>( tcx: TyCtxt<'tcx>, + generic_param_scope: LocalDefId, region: ty::Region<'tcx>, alt_span: Option, prefix: PrefixKind, suffix: SuffixKind, ) -> Option { - Some(Self { desc: DescriptionCtx::new(tcx, region, alt_span)?, prefix, suffix }) + Some(Self { + desc: DescriptionCtx::new(tcx, generic_param_scope, region, alt_span)?, + prefix, + suffix, + }) } } diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 16057b6ad9dc..fc7a22833ff0 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -27,10 +27,12 @@ use super::*; +use crate::infer::relate::{Relate, StructurallyRelateAliases, TypeRelation}; use rustc_middle::bug; -use rustc_middle::ty::relate::{Relate, TypeRelation}; use rustc_middle::ty::{Const, ImplSubject}; +use crate::traits::Obligation; + /// Whether we should define opaque types or just treat them opaquely. /// /// Currently only used to prevent predicate matching from matching anything @@ -48,11 +50,6 @@ pub struct At<'a, 'tcx> { pub param_env: ty::ParamEnv<'tcx>, } -pub struct Trace<'a, 'tcx> { - at: At<'a, 'tcx>, - trace: TypeTrace<'tcx>, -} - impl<'tcx> InferCtxt<'tcx> { #[inline] pub fn at<'a>( @@ -95,7 +92,7 @@ impl<'tcx> InferCtxt<'tcx> { } } -pub trait ToTrace<'tcx>: Relate<'tcx> + Copy { +pub trait ToTrace<'tcx>: Relate> + Copy { fn to_trace( cause: &ObligationCause<'tcx>, a_is_expected: bool, @@ -109,9 +106,6 @@ impl<'a, 'tcx> At<'a, 'tcx> { /// call like `foo(x)`, where `foo: fn(i32)`, you might have /// `sup(i32, x)`, since the "expected" type is the type that /// appears in the signature. - /// - /// See [`At::trace`] and [`Trace::sub`] for a version of - /// this method that only requires `T: Relate<'tcx>` pub fn sup( self, define_opaque_types: DefineOpaqueTypes, @@ -121,13 +115,17 @@ impl<'a, 'tcx> At<'a, 'tcx> { where T: ToTrace<'tcx>, { - self.trace(expected, actual).sup(define_opaque_types, expected, actual) + let mut fields = CombineFields::new( + self.infcx, + ToTrace::to_trace(self.cause, true, expected, actual), + self.param_env, + define_opaque_types, + ); + fields.sup().relate(expected, actual)?; + Ok(InferOk { value: (), obligations: fields.into_obligations() }) } /// Makes `expected <: actual`. - /// - /// See [`At::trace`] and [`Trace::sub`] for a version of - /// this method that only requires `T: Relate<'tcx>` pub fn sub( self, define_opaque_types: DefineOpaqueTypes, @@ -137,13 +135,17 @@ impl<'a, 'tcx> At<'a, 'tcx> { where T: ToTrace<'tcx>, { - self.trace(expected, actual).sub(define_opaque_types, expected, actual) + let mut fields = CombineFields::new( + self.infcx, + ToTrace::to_trace(self.cause, true, expected, actual), + self.param_env, + define_opaque_types, + ); + fields.sub().relate(expected, actual)?; + Ok(InferOk { value: (), obligations: fields.into_obligations() }) } - /// Makes `expected <: actual`. - /// - /// See [`At::trace`] and [`Trace::eq`] for a version of - /// this method that only requires `T: Relate<'tcx>` + /// Makes `expected == actual`. pub fn eq( self, define_opaque_types: DefineOpaqueTypes, @@ -153,7 +155,50 @@ impl<'a, 'tcx> At<'a, 'tcx> { where T: ToTrace<'tcx>, { - self.trace(expected, actual).eq(define_opaque_types, expected, actual) + let mut fields = CombineFields::new( + self.infcx, + ToTrace::to_trace(self.cause, true, expected, actual), + self.param_env, + define_opaque_types, + ); + fields.equate(StructurallyRelateAliases::No).relate(expected, actual)?; + Ok(InferOk { + value: (), + obligations: fields + .goals + .into_iter() + .map(|goal| { + Obligation::new( + self.infcx.tcx, + fields.trace.cause.clone(), + goal.param_env, + goal.predicate, + ) + }) + .collect(), + }) + } + + /// Equates `expected` and `found` while structurally relating aliases. + /// This should only be used inside of the next generation trait solver + /// when relating rigid aliases. + pub fn eq_structurally_relating_aliases( + self, + expected: T, + actual: T, + ) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + assert!(self.infcx.next_trait_solver()); + let mut fields = CombineFields::new( + self.infcx, + ToTrace::to_trace(self.cause, true, expected, actual), + self.param_env, + DefineOpaqueTypes::Yes, + ); + fields.equate(StructurallyRelateAliases::Yes).relate(expected, actual)?; + Ok(InferOk { value: (), obligations: fields.into_obligations() }) } pub fn relate( @@ -167,27 +212,68 @@ impl<'a, 'tcx> At<'a, 'tcx> { T: ToTrace<'tcx>, { match variance { - ty::Variance::Covariant => self.sub(define_opaque_types, expected, actual), - ty::Variance::Invariant => self.eq(define_opaque_types, expected, actual), - ty::Variance::Contravariant => self.sup(define_opaque_types, expected, actual), + ty::Covariant => self.sub(define_opaque_types, expected, actual), + ty::Invariant => self.eq(define_opaque_types, expected, actual), + ty::Contravariant => self.sup(define_opaque_types, expected, actual), // We could make this make sense but it's not readily // exposed and I don't feel like dealing with it. Note // that bivariance in general does a bit more than just // *nothing*, it checks that the types are the same // "modulo variance" basically. - ty::Variance::Bivariant => panic!("Bivariant given to `relate()`"), + ty::Bivariant => panic!("Bivariant given to `relate()`"), } } + /// Used in the new solver since we don't care about tracking an `ObligationCause`. + pub fn relate_no_trace( + self, + expected: T, + variance: ty::Variance, + actual: T, + ) -> Result>>, NoSolution> + where + T: Relate>, + { + let mut fields = CombineFields::new( + self.infcx, + TypeTrace::dummy(self.cause), + self.param_env, + DefineOpaqueTypes::Yes, + ); + fields.sub().relate_with_variance( + variance, + ty::VarianceDiagInfo::default(), + expected, + actual, + )?; + Ok(fields.goals) + } + + /// Used in the new solver since we don't care about tracking an `ObligationCause`. + pub fn eq_structurally_relating_aliases_no_trace( + self, + expected: T, + actual: T, + ) -> Result>>, NoSolution> + where + T: Relate>, + { + let mut fields = CombineFields::new( + self.infcx, + TypeTrace::dummy(self.cause), + self.param_env, + DefineOpaqueTypes::Yes, + ); + fields.equate(StructurallyRelateAliases::Yes).relate(expected, actual)?; + Ok(fields.goals) + } + /// Computes the least-upper-bound, or mutual supertype, of two /// values. The order of the arguments doesn't matter, but since /// this can result in an error (e.g., if asked to compute LUB of /// u32 and i32), it is meaningful to call one of them the /// "expected type". - /// - /// See [`At::trace`] and [`Trace::lub`] for a version of - /// this method that only requires `T: Relate<'tcx>` pub fn lub( self, define_opaque_types: DefineOpaqueTypes, @@ -197,15 +283,19 @@ impl<'a, 'tcx> At<'a, 'tcx> { where T: ToTrace<'tcx>, { - self.trace(expected, actual).lub(define_opaque_types, expected, actual) + let mut fields = CombineFields::new( + self.infcx, + ToTrace::to_trace(self.cause, true, expected, actual), + self.param_env, + define_opaque_types, + ); + let value = fields.lub().relate(expected, actual)?; + Ok(InferOk { value, obligations: fields.into_obligations() }) } /// Computes the greatest-lower-bound, or mutual subtype, of two /// values. As with `lub` order doesn't matter, except for error /// cases. - /// - /// See [`At::trace`] and [`Trace::glb`] for a version of - /// this method that only requires `T: Relate<'tcx>` pub fn glb( self, define_opaque_types: DefineOpaqueTypes, @@ -215,105 +305,14 @@ impl<'a, 'tcx> At<'a, 'tcx> { where T: ToTrace<'tcx>, { - self.trace(expected, actual).glb(define_opaque_types, expected, actual) - } - - /// Sets the "trace" values that will be used for - /// error-reporting, but doesn't actually perform any operation - /// yet (this is useful when you want to set the trace using - /// distinct values from those you wish to operate upon). - pub fn trace(self, expected: T, actual: T) -> Trace<'a, 'tcx> - where - T: ToTrace<'tcx>, - { - let trace = ToTrace::to_trace(self.cause, true, expected, actual); - Trace { at: self, trace } - } -} - -impl<'a, 'tcx> Trace<'a, 'tcx> { - /// Makes `a <: b`. - #[instrument(skip(self), level = "debug")] - pub fn sub(self, define_opaque_types: DefineOpaqueTypes, a: T, b: T) -> InferResult<'tcx, ()> - where - T: Relate<'tcx>, - { - let Trace { at, trace } = self; - let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types); - fields - .sub() - .relate(a, b) - .map(move |_| InferOk { value: (), obligations: fields.obligations }) - } - - /// Makes `a :> b`. - #[instrument(skip(self), level = "debug")] - pub fn sup(self, define_opaque_types: DefineOpaqueTypes, a: T, b: T) -> InferResult<'tcx, ()> - where - T: Relate<'tcx>, - { - let Trace { at, trace } = self; - let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types); - fields - .sup() - .relate(a, b) - .map(move |_| InferOk { value: (), obligations: fields.obligations }) - } - - /// Makes `a == b`. - #[instrument(skip(self), level = "debug")] - pub fn eq(self, define_opaque_types: DefineOpaqueTypes, a: T, b: T) -> InferResult<'tcx, ()> - where - T: Relate<'tcx>, - { - let Trace { at, trace } = self; - let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types); - fields - .equate(StructurallyRelateAliases::No) - .relate(a, b) - .map(move |_| InferOk { value: (), obligations: fields.obligations }) - } - - /// Equates `a` and `b` while structurally relating aliases. This should only - /// be used inside of the next generation trait solver when relating rigid aliases. - #[instrument(skip(self), level = "debug")] - pub fn eq_structurally_relating_aliases(self, a: T, b: T) -> InferResult<'tcx, ()> - where - T: Relate<'tcx>, - { - let Trace { at, trace } = self; - debug_assert!(at.infcx.next_trait_solver()); - let mut fields = at.infcx.combine_fields(trace, at.param_env, DefineOpaqueTypes::Yes); - fields - .equate(StructurallyRelateAliases::Yes) - .relate(a, b) - .map(move |_| InferOk { value: (), obligations: fields.obligations }) - } - - #[instrument(skip(self), level = "debug")] - pub fn lub(self, define_opaque_types: DefineOpaqueTypes, a: T, b: T) -> InferResult<'tcx, T> - where - T: Relate<'tcx>, - { - let Trace { at, trace } = self; - let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types); - fields - .lub() - .relate(a, b) - .map(move |t| InferOk { value: t, obligations: fields.obligations }) - } - - #[instrument(skip(self), level = "debug")] - pub fn glb(self, define_opaque_types: DefineOpaqueTypes, a: T, b: T) -> InferResult<'tcx, T> - where - T: Relate<'tcx>, - { - let Trace { at, trace } = self; - let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types); - fields - .glb() - .relate(a, b) - .map(move |t| InferOk { value: t, obligations: fields.obligations }) + let mut fields = CombineFields::new( + self.infcx, + ToTrace::to_trace(self.cause, true, expected, actual), + self.param_env, + define_opaque_types, + ); + let value = fields.glb().relate(expected, actual)?; + Ok(InferOk { value, obligations: fields.into_obligations() }) } } @@ -348,7 +347,7 @@ impl<'tcx> ToTrace<'tcx> for Ty<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), } } } @@ -360,7 +359,10 @@ impl<'tcx> ToTrace<'tcx> for ty::Region<'tcx> { a: Self, b: Self, ) -> TypeTrace<'tcx> { - TypeTrace { cause: cause.clone(), values: Regions(ExpectedFound::new(a_is_expected, a, b)) } + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Regions(ExpectedFound::new(a_is_expected, a, b)), + } } } @@ -373,7 +375,7 @@ impl<'tcx> ToTrace<'tcx> for Const<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), } } } @@ -389,13 +391,13 @@ impl<'tcx> ToTrace<'tcx> for ty::GenericArg<'tcx> { cause: cause.clone(), values: match (a.unpack(), b.unpack()) { (GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => { - Regions(ExpectedFound::new(a_is_expected, a, b)) + ValuePairs::Regions(ExpectedFound::new(a_is_expected, a, b)) } (GenericArgKind::Type(a), GenericArgKind::Type(b)) => { - Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())) + ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())) } (GenericArgKind::Const(a), GenericArgKind::Const(b)) => { - Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())) + ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())) } ( @@ -424,7 +426,10 @@ impl<'tcx> ToTrace<'tcx> for ty::Term<'tcx> { a: Self, b: Self, ) -> TypeTrace<'tcx> { - TypeTrace { cause: cause.clone(), values: Terms(ExpectedFound::new(a_is_expected, a, b)) } + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a, b)), + } } } @@ -437,7 +442,7 @@ impl<'tcx> ToTrace<'tcx> for ty::TraitRef<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: TraitRefs(ExpectedFound::new(a_is_expected, a, b)), + values: ValuePairs::TraitRefs(ExpectedFound::new(a_is_expected, a, b)), } } } @@ -451,7 +456,7 @@ impl<'tcx> ToTrace<'tcx> for ty::AliasTy<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: Aliases(ExpectedFound::new(a_is_expected, a.into(), b.into())), + values: ValuePairs::Aliases(ExpectedFound::new(a_is_expected, a.into(), b.into())), } } } @@ -463,7 +468,10 @@ impl<'tcx> ToTrace<'tcx> for ty::AliasTerm<'tcx> { a: Self, b: Self, ) -> TypeTrace<'tcx> { - TypeTrace { cause: cause.clone(), values: Aliases(ExpectedFound::new(a_is_expected, a, b)) } + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Aliases(ExpectedFound::new(a_is_expected, a, b)), + } } } @@ -476,7 +484,7 @@ impl<'tcx> ToTrace<'tcx> for ty::FnSig<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: PolySigs(ExpectedFound::new( + values: ValuePairs::PolySigs(ExpectedFound::new( a_is_expected, ty::Binder::dummy(a), ty::Binder::dummy(b), @@ -494,7 +502,7 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyFnSig<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: PolySigs(ExpectedFound::new(a_is_expected, a, b)), + values: ValuePairs::PolySigs(ExpectedFound::new(a_is_expected, a, b)), } } } @@ -508,7 +516,7 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialTraitRef<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: ExistentialTraitRef(ExpectedFound::new(a_is_expected, a, b)), + values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(a_is_expected, a, b)), } } } @@ -522,7 +530,7 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialProjection<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: ExistentialProjection(ExpectedFound::new(a_is_expected, a, b)), + values: ValuePairs::ExistentialProjection(ExpectedFound::new(a_is_expected, a, b)), } } } diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 27b06c4b73e9..bc2592b43f3d 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -462,7 +462,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { // any equated inference vars correctly! let root_vid = self.infcx.unwrap().root_const_var(vid); if root_vid != vid { - ct = ty::Const::new_var(self.tcx, root_vid, ct.ty()); + ct = ty::Const::new_var(self.tcx, root_vid); vid = root_vid; } @@ -481,7 +481,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { ui = ty::UniverseIndex::ROOT; } return self.canonicalize_const_var( - CanonicalVarInfo { kind: CanonicalVarKind::Const(ui, ct.ty()) }, + CanonicalVarInfo { kind: CanonicalVarKind::Const(ui) }, ct, ); } @@ -510,9 +510,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { } ty::ConstKind::Placeholder(placeholder) => { return self.canonicalize_const_var( - CanonicalVarInfo { - kind: CanonicalVarKind::PlaceholderConst(placeholder, ct.ty()), - }, + CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderConst(placeholder) }, ct, ); } @@ -719,9 +717,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { CanonicalVarKind::Region(u) => { CanonicalVarKind::Region(reverse_universe_map[&u]) } - CanonicalVarKind::Const(u, t) => { - CanonicalVarKind::Const(reverse_universe_map[&u], t) - } + CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]), CanonicalVarKind::PlaceholderTy(placeholder) => { CanonicalVarKind::PlaceholderTy(ty::Placeholder { universe: reverse_universe_map[&placeholder.universe], @@ -734,14 +730,11 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { ..placeholder }) } - CanonicalVarKind::PlaceholderConst(placeholder, t) => { - CanonicalVarKind::PlaceholderConst( - ty::Placeholder { - universe: reverse_universe_map[&placeholder.universe], - ..placeholder - }, - t, - ) + CanonicalVarKind::PlaceholderConst(placeholder) => { + CanonicalVarKind::PlaceholderConst(ty::Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) } }, }) @@ -806,6 +799,6 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { !self.infcx.is_some_and(|infcx| const_var != infcx.shallow_resolve_const(const_var)) ); let var = self.canonical_var(info, const_var.into()); - ty::Const::new_bound(self.tcx, self.binder_index, var, self.fold_ty(const_var.ty())) + ty::Const::new_bound(self.tcx, self.binder_index, var) } } diff --git a/compiler/rustc_infer/src/infer/canonical/instantiate.rs b/compiler/rustc_infer/src/infer/canonical/instantiate.rs index de0e15ef3def..153de3d4c09d 100644 --- a/compiler/rustc_infer/src/infer/canonical/instantiate.rs +++ b/compiler/rustc_infer/src/infer/canonical/instantiate.rs @@ -70,7 +70,7 @@ where GenericArgKind::Type(ty) => ty, r => bug!("{:?} is a type but value is {:?}", bound_ty, r), }, - consts: &mut |bound_ct: ty::BoundVar, _| match var_values[bound_ct].unpack() { + consts: &mut |bound_ct: ty::BoundVar| match var_values[bound_ct].unpack() { GenericArgKind::Const(ct) => ct, c => bug!("{:?} is a const but value is {:?}", bound_ct, c), }, diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index 1abb8086d41c..8ad4f7926cad 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -143,8 +143,8 @@ impl<'tcx> InferCtxt<'tcx> { ty::Region::new_placeholder(self.tcx, placeholder_mapped).into() } - CanonicalVarKind::Const(ui, ty) => { - self.next_const_var_in_universe(ty, span, universe_map(ui)).into() + CanonicalVarKind::Const(ui) => { + self.next_const_var_in_universe(span, universe_map(ui)).into() } CanonicalVarKind::Effect => { let vid = self @@ -153,13 +153,12 @@ impl<'tcx> InferCtxt<'tcx> { .effect_unification_table() .new_key(EffectVarValue::Unknown) .vid; - ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(vid), self.tcx.types.bool) - .into() + ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(vid)).into() } - CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst { universe, bound }, ty) => { + CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst { universe, bound }) => { let universe_mapped = universe_map(universe); let placeholder_mapped = ty::PlaceholderConst { universe: universe_mapped, bound }; - ty::Const::new_placeholder(self.tcx, placeholder_mapped, ty).into() + ty::Const::new_placeholder(self.tcx, placeholder_mapped).into() } } } diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 1732913e1915..d7dd6a1e7cf5 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -16,7 +16,7 @@ use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::infer::{DefineOpaqueTypes, InferCtxt, InferOk, InferResult}; use crate::traits::query::NoSolution; use crate::traits::{Obligation, ObligationCause, PredicateObligation}; -use crate::traits::{TraitEngine, TraitEngineExt}; +use crate::traits::{ScrubbedTraitError, TraitEngine}; use rustc_data_structures::captures::Captures; use rustc_index::Idx; use rustc_index::IndexVec; @@ -54,7 +54,7 @@ impl<'tcx> InferCtxt<'tcx> { &self, inference_vars: CanonicalVarValues<'tcx>, answer: T, - fulfill_cx: &mut dyn TraitEngine<'tcx>, + fulfill_cx: &mut dyn TraitEngine<'tcx, ScrubbedTraitError<'tcx>>, ) -> Result, NoSolution> where T: Debug + TypeFoldable>, @@ -101,7 +101,7 @@ impl<'tcx> InferCtxt<'tcx> { &self, inference_vars: CanonicalVarValues<'tcx>, answer: T, - fulfill_cx: &mut dyn TraitEngine<'tcx>, + fulfill_cx: &mut dyn TraitEngine<'tcx, ScrubbedTraitError<'tcx>>, ) -> Result, NoSolution> where T: Debug + TypeFoldable>, @@ -109,19 +109,13 @@ impl<'tcx> InferCtxt<'tcx> { let tcx = self.tcx; // Select everything, returning errors. - let true_errors = fulfill_cx.select_where_possible(self); - debug!("true_errors = {:#?}", true_errors); + let errors = fulfill_cx.select_all_or_error(self); - if !true_errors.is_empty() { - // FIXME -- we don't indicate *why* we failed to solve - debug!("make_query_response: true_errors={:#?}", true_errors); + // True error! + if errors.iter().any(|e| e.is_true_error()) { return Err(NoSolution); } - // Anything left unselected *now* must be an ambiguity. - let ambig_errors = fulfill_cx.select_all_or_error(self); - debug!("ambig_errors = {:#?}", ambig_errors); - let region_obligations = self.take_registered_region_obligations(); debug!(?region_obligations); let region_constraints = self.with_region_constraints(|region_constraints| { @@ -135,8 +129,7 @@ impl<'tcx> InferCtxt<'tcx> { }); debug!(?region_constraints); - let certainty = - if ambig_errors.is_empty() { Certainty::Proven } else { Certainty::Ambiguous }; + let certainty = if errors.is_empty() { Certainty::Proven } else { Certainty::Ambiguous }; let opaque_types = self.take_opaque_types_for_query_response(); diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 46a7e7b23996..227691d09943 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -58,21 +58,22 @@ use crate::traits::{ PredicateObligation, }; +use crate::infer::relate::{self, RelateResult, TypeRelation}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxt, DiagStyledString, - ErrorGuaranteed, IntoDiagArg, StringPart, + codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxtHandle, + DiagStyledString, ErrorGuaranteed, IntoDiagArg, StringPart, }; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; +use rustc_hir::{self as hir, ParamName}; use rustc_macros::extension; use rustc_middle::bug; use rustc_middle::dep_graph::DepContext; +use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError, PrintTraitRefExt as _}; -use rustc_middle::ty::relate::{self, RelateResult, TypeRelation}; use rustc_middle::ty::Upcast; use rustc_middle::ty::{ self, error::TypeError, IsSuggestable, List, Region, Ty, TyCtxt, TypeFoldable, @@ -138,8 +139,8 @@ pub struct TypeErrCtxt<'a, 'tcx> { } impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - pub fn dcx(&self) -> &'tcx DiagCtxt { - self.infcx.tcx.dcx() + pub fn dcx(&self) -> DiagCtxtHandle<'tcx> { + self.infcx.dcx() } /// This is just to avoid a potential footgun of accidentally @@ -161,6 +162,7 @@ impl<'tcx> Deref for TypeErrCtxt<'_, 'tcx> { pub(super) fn note_and_explain_region<'tcx>( tcx: TyCtxt<'tcx>, err: &mut Diag<'_>, + generic_param_scope: LocalDefId, prefix: &str, region: ty::Region<'tcx>, suffix: &str, @@ -168,12 +170,15 @@ pub(super) fn note_and_explain_region<'tcx>( ) { let (description, span) = match *region { ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReStatic => { - msg_span_from_named_region(tcx, region, alt_span) + msg_span_from_named_region(tcx, generic_param_scope, region, alt_span) } ty::ReError(_) => return, - ty::ReVar(_) | ty::ReBound(..) | ty::ReErased => { + // FIXME(#125431): `ReVar` shouldn't reach here. + ty::ReVar(_) => (format!("lifetime `{region}`"), alt_span), + + ty::ReBound(..) | ty::ReErased => { bug!("unexpected region for note_and_explain_region: {:?}", region); } }; @@ -184,23 +189,27 @@ pub(super) fn note_and_explain_region<'tcx>( fn explain_free_region<'tcx>( tcx: TyCtxt<'tcx>, err: &mut Diag<'_>, + generic_param_scope: LocalDefId, prefix: &str, region: ty::Region<'tcx>, suffix: &str, ) { - let (description, span) = msg_span_from_named_region(tcx, region, None); + let (description, span) = msg_span_from_named_region(tcx, generic_param_scope, region, None); label_msg_span(err, prefix, description, span, suffix); } fn msg_span_from_named_region<'tcx>( tcx: TyCtxt<'tcx>, + generic_param_scope: LocalDefId, region: ty::Region<'tcx>, alt_span: Option, ) -> (String, Option) { match *region { - ty::ReEarlyParam(ref br) => { - let scope = region.free_region_binding_scope(tcx).expect_local(); + ty::ReEarlyParam(br) => { + let scope = tcx + .parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id) + .expect_local(); let span = if let Some(param) = tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) { @@ -217,21 +226,21 @@ fn msg_span_from_named_region<'tcx>( } ty::ReLateParam(ref fr) => { if !fr.bound_region.is_named() - && let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region) + && let Some((ty, _)) = + find_anon_type(tcx, generic_param_scope, region, &fr.bound_region) { ("the anonymous lifetime defined here".to_string(), Some(ty.span)) } else { - let scope = region.free_region_binding_scope(tcx).expect_local(); match fr.bound_region { ty::BoundRegionKind::BrNamed(_, name) => { let span = if let Some(param) = tcx .hir() - .get_generics(scope) + .get_generics(generic_param_scope) .and_then(|generics| generics.get_named(name)) { param.span } else { - tcx.def_span(scope) + tcx.def_span(generic_param_scope) }; let text = if name == kw::UnderscoreLifetime { "the anonymous lifetime as defined here".to_string() @@ -242,11 +251,11 @@ fn msg_span_from_named_region<'tcx>( } ty::BrAnon => ( "the anonymous lifetime as defined here".to_string(), - Some(tcx.def_span(scope)), + Some(tcx.def_span(generic_param_scope)), ), _ => ( format!("the lifetime `{region}` as defined here"), - Some(tcx.def_span(scope)), + Some(tcx.def_span(generic_param_scope)), ), } } @@ -299,6 +308,7 @@ fn label_msg_span( #[instrument(level = "trace", skip(tcx))] pub fn unexpected_hidden_region_diagnostic<'tcx>( tcx: TyCtxt<'tcx>, + generic_param_scope: LocalDefId, span: Span, hidden_ty: Ty<'tcx>, hidden_region: ty::Region<'tcx>, @@ -324,11 +334,12 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( explain_free_region( tcx, &mut err, + generic_param_scope, &format!("hidden type `{hidden_ty}` captures "), hidden_region, "", ); - if let Some(reg_info) = tcx.is_suitable_region(hidden_region) { + if let Some(reg_info) = tcx.is_suitable_region(generic_param_scope, hidden_region) { let fn_returns = tcx.return_type_impl_or_dyn_traits(reg_info.def_id); nice_region_error::suggest_new_region_bound( tcx, @@ -346,6 +357,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( explain_free_region( tcx, &mut err, + generic_param_scope, &format!("hidden type `{}` captures ", hidden_ty), hidden_region, "", @@ -373,6 +385,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( note_and_explain_region( tcx, &mut err, + generic_param_scope, &format!("hidden type `{hidden_ty}` captures "), hidden_region, "", @@ -413,7 +426,7 @@ impl<'tcx> InferCtxt<'tcx> { ty::ClauseKind::Projection(projection_predicate) if projection_predicate.projection_term.def_id == item_def_id => { - projection_predicate.term.ty() + projection_predicate.term.as_type() } _ => None, }) @@ -447,7 +460,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { for error in errors { debug!("report_region_errors: error = {:?}", error); - guar = Some(if let Some(guar) = self.try_report_nice_region_error(&error) { + let e = if let Some(guar) = + self.try_report_nice_region_error(generic_param_scope, &error) + { guar } else { match error.clone() { @@ -460,9 +475,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // general bit of code that displays the error information RegionResolutionError::ConcreteFailure(origin, sub, sup) => { if sub.is_placeholder() || sup.is_placeholder() { - self.report_placeholder_failure(origin, sub, sup).emit() + self.report_placeholder_failure(generic_param_scope, origin, sub, sup) + .emit() } else { - self.report_concrete_failure(origin, sub, sup).emit() + self.report_concrete_failure(generic_param_scope, origin, sub, sup) + .emit() } } @@ -485,12 +502,29 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { _, ) => { if sub_r.is_placeholder() { - self.report_placeholder_failure(sub_origin, sub_r, sup_r).emit() + self.report_placeholder_failure( + generic_param_scope, + sub_origin, + sub_r, + sup_r, + ) + .emit() } else if sup_r.is_placeholder() { - self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit() + self.report_placeholder_failure( + generic_param_scope, + sup_origin, + sub_r, + sup_r, + ) + .emit() } else { self.report_sub_sup_conflict( - var_origin, sub_origin, sub_r, sup_origin, sup_r, + generic_param_scope, + var_origin, + sub_origin, + sub_r, + sup_origin, + sup_r, ) } } @@ -511,7 +545,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // value. let sub_r = self.tcx.lifetimes.re_erased; - self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit() + self.report_placeholder_failure( + generic_param_scope, + sup_origin, + sub_r, + sup_r, + ) + .emit() } RegionResolutionError::CannotNormalize(clause, origin) => { @@ -523,7 +563,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .emit() } } - }) + }; + + guar = Some(e) } guar.unwrap() @@ -850,7 +892,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { arm_ty, arm_span, ) { - err.subdiagnostic(self.dcx(), subdiag); + err.subdiagnostic(subdiag); } } }, @@ -876,7 +918,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { else_ty, else_span, ) { - err.subdiagnostic(self.dcx(), subdiag); + err.subdiagnostic(subdiag); } } ObligationCauseCode::LetElse => { @@ -1665,6 +1707,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ValuePairs::ExistentialProjection(_) => { (false, Mismatch::Fixed("existential projection")) } + ValuePairs::Dummy => { + bug!("do not expect to report a type error from a ValuePairs::Dummy") + } }; let Some(vals) = self.values_str(values) else { // Derived error. Cancel the emitter. @@ -2061,10 +2106,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // If a string was expected and the found expression is a character literal, // perhaps the user meant to write `"s"` to specify a string literal. (ty::Ref(_, r, _), ty::Char) if r.is_str() => { - suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { - start: span.with_hi(span.lo() + BytePos(1)), - end: span.with_lo(span.hi() - BytePos(1)), - }) + if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) + && code.starts_with("'") + && code.ends_with("'") + { + suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { + start: span.with_hi(span.lo() + BytePos(1)), + end: span.with_lo(span.hi() - BytePos(1)), + }); + } } // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`, // we try to suggest to add the missing `let` for `if let Some(..) = expr` @@ -2203,12 +2253,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { values: ValuePairs<'tcx>, ) -> Option<(DiagStyledString, DiagStyledString, Option)> { match values { - infer::Regions(exp_found) => self.expected_found_str(exp_found), - infer::Terms(exp_found) => self.expected_found_str_term(exp_found), - infer::Aliases(exp_found) => self.expected_found_str(exp_found), - infer::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found), - infer::ExistentialProjection(exp_found) => self.expected_found_str(exp_found), - infer::TraitRefs(exp_found) => { + ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found), + ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found), + ValuePairs::Aliases(exp_found) => self.expected_found_str(exp_found), + ValuePairs::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found), + ValuePairs::ExistentialProjection(exp_found) => self.expected_found_str(exp_found), + ValuePairs::TraitRefs(exp_found) => { let pretty_exp_found = ty::error::ExpectedFound { expected: exp_found.expected.print_trait_sugared(), found: exp_found.found.print_trait_sugared(), @@ -2220,7 +2270,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ret => ret, } } - infer::PolySigs(exp_found) => { + ValuePairs::PolySigs(exp_found) => { let exp_found = self.resolve_vars_if_possible(exp_found); if exp_found.references_error() { return None; @@ -2228,6 +2278,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let (exp, fnd) = self.cmp_fn_sig(&exp_found.expected, &exp_found.found); Some((exp, fnd, None)) } + ValuePairs::Dummy => { + bug!("do not expect to report a type error from a ValuePairs::Dummy") + } } } @@ -2344,7 +2397,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { '_explain: { let (description, span) = match sub.kind() { ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => { - msg_span_from_named_region(self.tcx, sub, Some(span)) + msg_span_from_named_region(self.tcx, generic_param_scope, sub, Some(span)) } _ => (format!("lifetime `{sub}`"), Some(span)), }; @@ -2376,7 +2429,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let (type_scope, type_param_sugg_span) = match bound_kind { GenericKind::Param(param) => { let generics = self.tcx.generics_of(generic_param_scope); - let def_id = generics.type_param(param, self.tcx).def_id.expect_local(); + let type_param = generics.type_param(param, self.tcx); + let def_id = type_param.def_id.expect_local(); let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id; // Get the `hir::Param` to verify whether it already has any bounds. // We do this to avoid suggesting code that ends up as `T: 'a'b`, @@ -2386,7 +2440,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { Some((span, open_paren_sp)) => Some((span, true, open_paren_sp)), // If `param` corresponds to `Self`, no usable suggestion span. None if generics.has_self && param.index == 0 => None, - None => Some((self.tcx.def_span(def_id).shrink_to_hi(), false, None)), + None => { + let span = if let Some(param) = + hir_generics.params.iter().find(|param| param.def_id == def_id) + && let ParamName::Plain(ident) = param.name + { + ident.span.shrink_to_hi() + } else { + let span = self.tcx.def_span(def_id); + span.shrink_to_hi() + }; + Some((span, false, None)) + } }; (scope, sugg_span) } @@ -2395,7 +2460,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let suggestion_scope = { let lifetime_scope = match sub.kind() { ty::ReStatic => hir::def_id::CRATE_DEF_ID, - _ => match self.tcx.is_suitable_region(sub) { + _ => match self.tcx.is_suitable_region(generic_param_scope, sub) { Some(info) => info.def_id, None => generic_param_scope, }, @@ -2407,7 +2472,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; let mut suggs = vec![]; - let lt_name = self.suggest_name_region(sub, &mut suggs); + let lt_name = self.suggest_name_region(generic_param_scope, sub, &mut suggs); if let Some((sp, has_lifetimes, open_paren_sp)) = type_param_sugg_span && suggestion_scope == type_scope @@ -2452,6 +2517,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { pub fn suggest_name_region( &self, + generic_param_scope: LocalDefId, lifetime: Region<'tcx>, add_lt_suggs: &mut Vec<(Span, String)>, ) -> String { @@ -2498,12 +2564,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - let (lifetime_def_id, lifetime_scope) = match self.tcx.is_suitable_region(lifetime) { - Some(info) if !lifetime.has_name() => { - (info.bound_region.get_id().unwrap().expect_local(), info.def_id) - } - _ => return lifetime.get_name_or_anon().to_string(), - }; + let (lifetime_def_id, lifetime_scope) = + match self.tcx.is_suitable_region(generic_param_scope, lifetime) { + Some(info) if !lifetime.has_name() => { + (info.bound_region.get_id().unwrap().expect_local(), info.def_id) + } + _ => return lifetime.get_name_or_anon().to_string(), + }; let new_lt = { let generics = self.tcx.generics_of(lifetime_scope); @@ -2554,6 +2621,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn report_sub_sup_conflict( &self, + generic_param_scope: LocalDefId, var_origin: RegionVariableOrigin, sub_origin: SubregionOrigin<'tcx>, sub_region: Region<'tcx>, @@ -2565,6 +2633,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { note_and_explain_region( self.tcx, &mut err, + generic_param_scope, "first, the lifetime cannot outlive ", sup_region, "...", @@ -2587,6 +2656,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { note_and_explain_region( self.tcx, &mut err, + generic_param_scope, "...but the lifetime must also be valid for ", sub_region, "...", @@ -2610,6 +2680,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { note_and_explain_region( self.tcx, &mut err, + generic_param_scope, "but, the lifetime must be valid for ", sub_region, "...", @@ -2634,7 +2705,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// with the other type. A TyVar inference type is compatible with any type, and an IntVar or /// FloatVar inference type are compatible with themselves or their concrete types (Int and /// Float types, respectively). When comparing two ADTs, these rules apply recursively. - pub fn same_type_modulo_infer>(&self, a: T, b: T) -> bool { + pub fn same_type_modulo_infer>>(&self, a: T, b: T) -> bool { let (a, b) = self.resolve_vars_if_possible((a, b)); SameTypeModuloInfer(self).relate(a, b).is_ok() } @@ -2642,7 +2713,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { struct SameTypeModuloInfer<'a, 'tcx>(&'a InferCtxt<'tcx>); -impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> { +impl<'tcx> TypeRelation> for SameTypeModuloInfer<'_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.0.tcx } @@ -2651,10 +2722,10 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> { "SameTypeModuloInfer" } - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, _variance: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, + _info: ty::VarianceDiagInfo>, a: T, b: T, ) -> relate::RelateResult<'tcx, T> { @@ -2702,7 +2773,7 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> { b: ty::Binder<'tcx, T>, ) -> relate::RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: relate::Relate<'tcx>, + T: relate::Relate>, { Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) } diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 98fd7906e700..cb0e13652e80 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -457,10 +457,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, arg); - if let Some(body_id) = self.tcx.hir().maybe_body_owned_by( + if let Some(body) = self.tcx.hir().maybe_body_owned_by( self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(), ) { - let expr = self.tcx.hir().body(body_id).value; + let expr = body.value; local_visitor.visit_expr(expr); } @@ -543,9 +543,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { match arg.unpack() { GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), GenericArgKind::Type(_) => self.next_ty_var(DUMMY_SP).into(), - GenericArgKind::Const(arg) => { - self.next_const_var(arg.ty(), DUMMY_SP).into() - } + GenericArgKind::Const(_) => self.next_const_var(DUMMY_SP).into(), } })) .unwrap(); @@ -1163,7 +1161,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { /// For closures, we first visit the parameters and then the content, /// as we prefer those. - fn visit_body(&mut self, body: &'tcx Body<'tcx>) { + fn visit_body(&mut self, body: &Body<'tcx>) { for param in body.params { debug!( "param: span {:?}, ty_span {:?}, pat.span {:?}", diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs index 84bfa5b5af7f..cbeec591960b 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -13,6 +13,7 @@ use crate::infer::TyCtxt; use rustc_errors::Subdiagnostic; use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_hir::def_id::LocalDefId; use rustc_hir::Ty; use rustc_middle::ty::Region; @@ -66,17 +67,17 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } // Determine whether the sub and sup consist of both anonymous (elided) regions. - let anon_reg_sup = self.tcx().is_suitable_region(sup)?; + let anon_reg_sup = self.tcx().is_suitable_region(self.generic_param_scope, sup)?; - let anon_reg_sub = self.tcx().is_suitable_region(sub)?; + let anon_reg_sub = self.tcx().is_suitable_region(self.generic_param_scope, sub)?; let scope_def_id_sup = anon_reg_sup.def_id; let bregion_sup = anon_reg_sup.bound_region; let scope_def_id_sub = anon_reg_sub.def_id; let bregion_sub = anon_reg_sub.bound_region; - let ty_sup = find_anon_type(self.tcx(), sup, &bregion_sup)?; + let ty_sup = find_anon_type(self.tcx(), self.generic_param_scope, sup, &bregion_sup)?; - let ty_sub = find_anon_type(self.tcx(), sub, &bregion_sub)?; + let ty_sub = find_anon_type(self.tcx(), self.generic_param_scope, sub, &bregion_sub)?; debug!( "try_report_anon_anon_conflict: found_param1={:?} sup={:?} br1={:?}", @@ -127,8 +128,14 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { }, }; - let suggestion = - AddLifetimeParamsSuggestion { tcx: self.tcx(), sub, ty_sup, ty_sub, add_note: true }; + let suggestion = AddLifetimeParamsSuggestion { + tcx: self.tcx(), + sub, + ty_sup, + ty_sub, + add_note: true, + generic_param_scope: self.generic_param_scope, + }; let err = LifetimeMismatch { span, labels, suggestion }; let reported = self.tcx().dcx().emit_err(err); Some(reported) @@ -139,11 +146,19 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// removed in favour of public_errors::AddLifetimeParamsSuggestion pub fn suggest_adding_lifetime_params<'tcx>( tcx: TyCtxt<'tcx>, + err: &mut Diag<'_>, + generic_param_scope: LocalDefId, sub: Region<'tcx>, ty_sup: &'tcx Ty<'_>, ty_sub: &'tcx Ty<'_>, - err: &mut Diag<'_>, ) { - let suggestion = AddLifetimeParamsSuggestion { tcx, sub, ty_sup, ty_sub, add_note: false }; + let suggestion = AddLifetimeParamsSuggestion { + tcx, + sub, + ty_sup, + ty_sub, + add_note: false, + generic_param_scope, + }; suggestion.add_to_diag(err); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs index 265a315a5597..b91b755d6837 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -1,5 +1,6 @@ use core::ops::ControlFlow; use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; @@ -23,10 +24,11 @@ use rustc_middle::ty::{self, Region, TyCtxt}; /// for e.g., `&u8` and `Vec<&u8>`. pub fn find_anon_type<'tcx>( tcx: TyCtxt<'tcx>, + generic_param_scope: LocalDefId, region: Region<'tcx>, br: &ty::BoundRegionKind, ) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnSig<'tcx>)> { - let anon_reg = tcx.is_suitable_region(region)?; + let anon_reg = tcx.is_suitable_region(generic_param_scope, region)?; let fn_sig = tcx.hir_node_by_def_id(anon_reg.def_id).fn_sig()?; fn_sig diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs index 45dce0a0e330..7996b4bf65b4 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs @@ -57,6 +57,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let expl = note_and_explain::RegionExplanation::new( self.tcx(), + self.generic_param_scope, sup, Some(binding_span), note_and_explain::PrefixKind::Empty, diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs index 62c163f0b7f6..cffdfa887523 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs @@ -2,6 +2,7 @@ use crate::infer::error_reporting::TypeErrCtxt; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::lexical_region_resolve::RegionResolutionError::*; use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::Span; @@ -23,30 +24,39 @@ pub use util::find_param_with_region; impl<'cx, 'tcx> TypeErrCtxt<'cx, 'tcx> { pub fn try_report_nice_region_error( &'cx self, + generic_param_scope: LocalDefId, error: &RegionResolutionError<'tcx>, ) -> Option { - NiceRegionError::new(self, error.clone()).try_report() + NiceRegionError::new(self, generic_param_scope, error.clone()).try_report() } } pub struct NiceRegionError<'cx, 'tcx> { cx: &'cx TypeErrCtxt<'cx, 'tcx>, + /// The innermost definition that introduces generic parameters that may be involved in + /// the region errors we are dealing with. + generic_param_scope: LocalDefId, error: Option>, regions: Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)>, } impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> { - pub fn new(cx: &'cx TypeErrCtxt<'cx, 'tcx>, error: RegionResolutionError<'tcx>) -> Self { - Self { cx, error: Some(error), regions: None } + pub fn new( + cx: &'cx TypeErrCtxt<'cx, 'tcx>, + generic_param_scope: LocalDefId, + error: RegionResolutionError<'tcx>, + ) -> Self { + Self { cx, error: Some(error), regions: None, generic_param_scope } } pub fn new_from_span( cx: &'cx TypeErrCtxt<'cx, 'tcx>, + generic_param_scope: LocalDefId, span: Span, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>, ) -> Self { - Self { cx, error: None, regions: Some((span, sub, sup)) } + Self { cx, error: None, regions: Some((span, sub, sup)), generic_param_scope } } fn tcx(&self) -> TyCtxt<'tcx> { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs index 239c2db30e2d..29da12e7d151 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs @@ -27,12 +27,12 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // version new_ty of its type where the anonymous region is replaced // with the named one. let (named, anon, anon_param_info, region_info) = if sub.has_name() - && let Some(region_info) = self.tcx().is_suitable_region(sup) + && let Some(region_info) = self.tcx().is_suitable_region(self.generic_param_scope, sup) && let Some(anon_param_info) = self.find_param_with_region(sup, sub) { (sub, sup, anon_param_info, region_info) } else if sup.has_name() - && let Some(region_info) = self.tcx().is_suitable_region(sub) + && let Some(region_info) = self.tcx().is_suitable_region(self.generic_param_scope, sub) && let Some(anon_param_info) = self.find_param_with_region(sub, sup) { (sup, sub, anon_param_info, region_info) @@ -72,7 +72,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { return None; } - if find_anon_type(self.tcx(), anon, &br).is_some() + if find_anon_type(self.tcx(), self.generic_param_scope, anon, &br).is_some() && self.is_self_anon(is_first, scope_def_id) { return None; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index 8a1e3a7ac71b..71a86683c212 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -49,7 +49,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() { // This may have a closure and it would cause ICE // through `find_param_with_region` (#78262). - let anon_reg_sup = tcx.is_suitable_region(*sup_r)?; + let anon_reg_sup = tcx.is_suitable_region(self.generic_param_scope, *sup_r)?; let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); if fn_returns.is_empty() { return None; @@ -92,7 +92,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})", var_origin, sub_origin, sub_r, sup_origin, sup_r ); - let anon_reg_sup = tcx.is_suitable_region(*sup_r)?; + let anon_reg_sup = tcx.is_suitable_region(self.generic_param_scope, *sup_r)?; debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup); let sp = var_origin.span(); let return_sp = sub_origin.span(); diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs index f1f8314661f3..83145e4f7b27 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -37,13 +37,15 @@ pub struct AnonymousParamInfo<'tcx> { #[instrument(skip(tcx), level = "debug")] pub fn find_param_with_region<'tcx>( tcx: TyCtxt<'tcx>, + generic_param_scope: LocalDefId, anon_region: Region<'tcx>, replace_region: Region<'tcx>, ) -> Option> { let (id, bound_region) = match *anon_region { ty::ReLateParam(late_param) => (late_param.scope, late_param.bound_region), ty::ReEarlyParam(ebr) => { - (tcx.parent(ebr.def_id), ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name)) + let region_def = tcx.generics_of(generic_param_scope).region_param(ebr, tcx).def_id; + (tcx.parent(region_def), ty::BoundRegionKind::BrNamed(region_def, ebr.name)) } _ => return None, // not a free region }; @@ -53,21 +55,20 @@ pub fn find_param_with_region<'tcx>( // FIXME: use def_kind // Don't perform this on closures - match tcx.hir_node_by_def_id(def_id) { + match tcx.hir_node_by_def_id(generic_param_scope) { hir::Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => { return None; } _ => {} } - let body_id = hir.maybe_body_owned_by(def_id)?; + let body = hir.maybe_body_owned_by(def_id)?; - let owner_id = hir.body_owner(body_id); + let owner_id = hir.body_owner(body.id()); let fn_decl = hir.fn_decl_by_hir_id(owner_id)?; let poly_fn_sig = tcx.fn_sig(id).instantiate_identity(); let fn_sig = tcx.liberate_late_bound_regions(id, poly_fn_sig); - let body = hir.body(body_id); body.params .iter() .take(if fn_sig.c_variadic { @@ -110,7 +111,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { anon_region: Region<'tcx>, replace_region: Region<'tcx>, ) -> Option> { - find_param_with_region(self.tcx(), anon_region, replace_region) + find_param_with_region(self.tcx(), self.generic_param_scope, anon_region, replace_region) } // Here, we check for the case where the anonymous region diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index 00dd20a2cc27..8fd19563c305 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -75,6 +75,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { pub(super) fn report_concrete_failure( &self, + generic_param_scope: LocalDefId, origin: SubregionOrigin<'tcx>, sub: Region<'tcx>, sup: Region<'tcx>, @@ -89,6 +90,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { note_and_explain_region( self.tcx, &mut err, + generic_param_scope, "", sup, " doesn't meet the lifetime requirements", @@ -99,6 +101,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { note_and_explain_region( self.tcx, &mut err, + generic_param_scope, "the required lifetime does not necessarily outlive ", sub, "", @@ -106,10 +109,19 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); } _ => { - note_and_explain_region(self.tcx, &mut err, "", sup, "...", None); note_and_explain_region( self.tcx, &mut err, + generic_param_scope, + "", + sup, + "...", + None, + ); + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, "...does not necessarily outlive ", sub, "", @@ -122,6 +134,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { infer::Reborrow(span) => { let reference_valid = note_and_explain::RegionExplanation::new( self.tcx, + generic_param_scope, sub, None, note_and_explain::PrefixKind::RefValidFor, @@ -129,6 +142,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); let content_valid = note_and_explain::RegionExplanation::new( self.tcx, + generic_param_scope, sup, None, note_and_explain::PrefixKind::ContentValidFor, @@ -142,6 +156,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { infer::RelateObjectBound(span) => { let object_valid = note_and_explain::RegionExplanation::new( self.tcx, + generic_param_scope, sub, None, note_and_explain::PrefixKind::TypeObjValidFor, @@ -149,6 +164,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); let pointer_valid = note_and_explain::RegionExplanation::new( self.tcx, + generic_param_scope, sup, None, note_and_explain::PrefixKind::SourcePointerValidFor, @@ -170,7 +186,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { note_and_explain::SuffixKind::Empty }; let note = note_and_explain::RegionExplanation::new( - self.tcx, sub, opt_span, prefix, suffix, + self.tcx, + generic_param_scope, + sub, + opt_span, + prefix, + suffix, ); self.dcx().create_err(FulfillReqLifetime { span, @@ -181,6 +202,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { infer::RelateRegionParamBound(span) => { let param_instantiated = note_and_explain::RegionExplanation::new( self.tcx, + generic_param_scope, sup, None, note_and_explain::PrefixKind::LfParamInstantiatedWith, @@ -188,6 +210,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); let param_must_outlive = note_and_explain::RegionExplanation::new( self.tcx, + generic_param_scope, sub, None, note_and_explain::PrefixKind::LfParamMustOutlive, @@ -201,6 +224,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { infer::ReferenceOutlivesReferent(ty, span) => { let pointer_valid = note_and_explain::RegionExplanation::new( self.tcx, + generic_param_scope, sub, None, note_and_explain::PrefixKind::PointerValidFor, @@ -208,6 +232,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); let data_valid = note_and_explain::RegionExplanation::new( self.tcx, + generic_param_scope, sup, None, note_and_explain::PrefixKind::DataValidFor, @@ -239,7 +264,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { err } infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => { - let mut err = self.report_concrete_failure(*parent, sub, sup); + let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup); // Don't mention the item name if it's an RPITIT, since that'll just confuse // folks. @@ -262,6 +287,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { infer::AscribeUserTypeProvePredicate(span) => { let instantiated = note_and_explain::RegionExplanation::new( self.tcx, + generic_param_scope, sup, None, note_and_explain::PrefixKind::LfInstantiatedWith, @@ -269,6 +295,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); let must_outlive = note_and_explain::RegionExplanation::new( self.tcx, + generic_param_scope, sub, None, note_and_explain::PrefixKind::LfMustOutlive, @@ -342,11 +369,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { trait_predicates: trait_predicates.join(", "), } }; - err.subdiagnostic(self.dcx(), suggestion); + err.subdiagnostic(suggestion); } pub(super) fn report_placeholder_failure( &self, + generic_param_scope: LocalDefId, placeholder_origin: SubregionOrigin<'tcx>, sub: Region<'tcx>, sup: Region<'tcx>, @@ -368,7 +396,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { && !span.is_dummy() { let span = *span; - self.report_concrete_failure(placeholder_origin, sub, sup) + self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup) .with_span_note(span, "the lifetime requirement is introduced here") } else { unreachable!( @@ -380,7 +408,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let terr = TypeError::RegionsPlaceholderMismatch; return self.report_and_explain_type_error(trace, terr); } - _ => return self.report_concrete_failure(placeholder_origin, sub, sup), + _ => { + return self.report_concrete_failure( + generic_param_scope, + placeholder_origin, + sub, + sup, + ); + } } } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index f2fe43380b80..bc59b5e033ba 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -21,18 +21,26 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { sp: Span, body_owner_def_id: DefId, ) { - use ty::error::TypeError::*; debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause); let tcx = self.tcx; match err { - ArgumentSorts(values, _) | Sorts(values) => { + TypeError::ArgumentSorts(values, _) | TypeError::Sorts(values) => { match (*values.expected.kind(), *values.found.kind()) { (ty::Closure(..), ty::Closure(..)) => { diag.note("no two closures, even if identical, have the same type"); diag.help("consider boxing your closure and/or using it as a trait object"); } + (ty::Coroutine(def_id1, ..), ty::Coroutine(def_id2, ..)) + if self.tcx.coroutine_is_async(def_id1) + && self.tcx.coroutine_is_async(def_id2) => + { + diag.note("no two async blocks, even if identical, have the same type"); + diag.help( + "consider pinning your async block and casting it to a trait object", + ); + } (ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => { // Issue #63167 diag.note("distinct uses of `impl Trait` result in different opaque types"); @@ -54,17 +62,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } (ty::Param(expected), ty::Param(found)) => { let generics = tcx.generics_of(body_owner_def_id); - if let Some(param) = generics.opt_type_param(expected, tcx) { - let e_span = tcx.def_span(param.def_id); - if !sp.contains(e_span) { - diag.span_label(e_span, "expected type parameter"); - } + let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id); + if !sp.contains(e_span) { + diag.span_label(e_span, "expected type parameter"); } - if let Some(param) = generics.opt_type_param(found, tcx) { - let f_span = tcx.def_span(param.def_id); - if !sp.contains(f_span) { - diag.span_label(f_span, "found type parameter"); - } + let f_span = tcx.def_span(generics.type_param(found, tcx).def_id); + if !sp.contains(f_span) { + diag.span_label(f_span, "found type parameter"); } diag.note( "a type parameter was expected, but a different one was found; \ @@ -87,29 +91,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { | (ty::Alias(ty::Projection, proj), ty::Param(p)) if !tcx.is_impl_trait_in_trait(proj.def_id) => { - let parent = tcx - .generics_of(body_owner_def_id) - .opt_type_param(p, tcx) - .and_then(|param| { - let p_def_id = param.def_id; - let p_span = tcx.def_span(p_def_id); - let expected = match (values.expected.kind(), values.found.kind()) { - (ty::Param(_), _) => "expected ", - (_, ty::Param(_)) => "found ", - _ => "", - }; - if !sp.contains(p_span) { - diag.span_label( - p_span, - format!("{expected}this type parameter"), - ); - } - p_def_id.as_local().and_then(|id| { - let local_id = tcx.local_def_id_to_hir_id(id); - let generics = tcx.parent_hir_node(local_id).generics()?; - Some((id, generics)) - }) - }); + let param = tcx.generics_of(body_owner_def_id).type_param(p, tcx); + let p_def_id = param.def_id; + let p_span = tcx.def_span(p_def_id); + let expected = match (values.expected.kind(), values.found.kind()) { + (ty::Param(_), _) => "expected ", + (_, ty::Param(_)) => "found ", + _ => "", + }; + if !sp.contains(p_span) { + diag.span_label(p_span, format!("{expected}this type parameter")); + } + let parent = p_def_id.as_local().and_then(|id| { + let local_id = tcx.local_def_id_to_hir_id(id); + let generics = tcx.parent_hir_node(local_id).generics()?; + Some((id, generics)) + }); let mut note = true; if let Some((local_id, generics)) = parent { // Synthesize the associated type restriction `Add`. @@ -183,19 +180,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { (ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..)) | (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => { let generics = tcx.generics_of(body_owner_def_id); - if let Some(param) = generics.opt_type_param(p, tcx) { - let p_span = tcx.def_span(param.def_id); - let expected = match (values.expected.kind(), values.found.kind()) { - (ty::Param(_), _) => "expected ", - (_, ty::Param(_)) => "found ", - _ => "", - }; - if !sp.contains(p_span) { - diag.span_label(p_span, format!("{expected}this type parameter")); - } + let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); + let expected = match (values.expected.kind(), values.found.kind()) { + (ty::Param(_), _) => "expected ", + (_, ty::Param(_)) => "found ", + _ => "", + }; + if !sp.contains(p_span) { + diag.span_label(p_span, format!("{expected}this type parameter")); } diag.help("type parameters must be constrained to match other types"); - if tcx.sess.teach(diag.code.unwrap()) { + if diag.code.is_some_and(|code| tcx.sess.teach(code)) { diag.help( "given a type parameter `T` and a method `foo`: ``` @@ -233,11 +228,9 @@ impl Trait for X { ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..), ) => { let generics = tcx.generics_of(body_owner_def_id); - if let Some(param) = generics.opt_type_param(p, tcx) { - let p_span = tcx.def_span(param.def_id); - if !sp.contains(p_span) { - diag.span_label(p_span, "expected this type parameter"); - } + let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); + if !sp.contains(p_span) { + diag.span_label(p_span, "expected this type parameter"); } diag.help(format!( "every closure has a distinct type and so could not always match the \ @@ -246,16 +239,14 @@ impl Trait for X { } (ty::Param(p), _) | (_, ty::Param(p)) => { let generics = tcx.generics_of(body_owner_def_id); - if let Some(param) = generics.opt_type_param(p, tcx) { - let p_span = tcx.def_span(param.def_id); - let expected = match (values.expected.kind(), values.found.kind()) { - (ty::Param(_), _) => "expected ", - (_, ty::Param(_)) => "found ", - _ => "", - }; - if !sp.contains(p_span) { - diag.span_label(p_span, format!("{expected}this type parameter")); - } + let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); + let expected = match (values.expected.kind(), values.found.kind()) { + (ty::Param(_), _) => "expected ", + (_, ty::Param(_)) => "found ", + _ => "", + }; + if !sp.contains(p_span) { + diag.span_label(p_span, format!("{expected}this type parameter")); } } (ty::Alias(ty::Projection | ty::Inherent, proj_ty), _) @@ -500,7 +491,7 @@ impl Trait for X { values.found.kind(), ); } - CyclicTy(ty) => { + TypeError::CyclicTy(ty) => { // Watch out for various cases of cyclic types and try to explain. if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() { diag.note( @@ -511,7 +502,7 @@ impl Trait for X { ); } } - TargetFeatureCast(def_id) => { + TypeError::TargetFeatureCast(def_id) => { let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span); diag.note( "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" @@ -545,10 +536,8 @@ impl Trait for X { return false; }; let generics = tcx.generics_of(body_owner_def_id); - let Some(param) = generics.opt_type_param(param_ty, tcx) else { - return false; - }; - let Some(def_id) = param.def_id.as_local() else { + let def_id = generics.type_param(param_ty, tcx).def_id; + let Some(def_id) = def_id.as_local() else { return false; }; @@ -557,7 +546,7 @@ impl Trait for X { for pred in hir_generics.bounds_for_param(def_id) { if self.constrain_generic_bound_associated_type_structured_suggestion( diag, - &trait_ref, + trait_ref, pred.bounds, assoc, assoc_args, @@ -682,7 +671,7 @@ impl Trait for X { https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", ); } - if tcx.sess.teach(diag.code.unwrap()) { + if diag.code.is_some_and(|code| tcx.sess.teach(code)) { diag.help( "given an associated type `T` and a method `foo`: ``` @@ -726,7 +715,7 @@ fn foo(&self) -> Self::T { String::new() } self.constrain_generic_bound_associated_type_structured_suggestion( diag, - &trait_ref, + trait_ref, opaque_hir_ty.bounds, assoc, assoc_args, @@ -880,7 +869,7 @@ fn foo(&self) -> Self::T { String::new() } fn constrain_generic_bound_associated_type_structured_suggestion( &self, diag: &mut Diag<'_>, - trait_ref: &ty::TraitRef<'tcx>, + trait_ref: ty::TraitRef<'tcx>, bounds: hir::GenericBounds<'_>, assoc: ty::AssocItem, assoc_args: &[ty::GenericArg<'tcx>], diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 75479decebcd..74c65e93616e 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -121,7 +121,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { span_low: cause.span.shrink_to_lo(), span_high: cause.span.shrink_to_hi(), }; - diag.subdiagnostic(self.dcx(), sugg); + diag.subdiagnostic(sugg); } _ => { // More than one matching variant. @@ -130,7 +130,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { cause_span: cause.span, compatible_variants, }; - diag.subdiagnostic(self.dcx(), sugg); + diag.subdiagnostic(sugg); } } } @@ -202,10 +202,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }, (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => { // FIXME: Seems like we can't have a suggestion and a note with different spans in a single subdiagnostic - diag.subdiagnostic( - self.dcx(), - ConsiderAddingAwait::FutureSugg { span: exp_span.shrink_to_hi() }, - ); + diag.subdiagnostic(ConsiderAddingAwait::FutureSugg { + span: exp_span.shrink_to_hi(), + }); Some(ConsiderAddingAwait::FutureSuggNote { span: exp_span }) } (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code() @@ -233,7 +232,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { _ => None, }; if let Some(subdiag) = subdiag { - diag.subdiagnostic(self.dcx(), subdiag); + diag.subdiagnostic(subdiag); } } @@ -269,7 +268,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } else { return; }; - diag.subdiagnostic(self.dcx(), suggestion); + diag.subdiagnostic(suggestion); } } } @@ -401,15 +400,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { (true, false) => FunctionPointerSuggestion::UseRef { span, fn_name }, (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name }, (true, true) => { - diag.subdiagnostic(self.dcx(), FnItemsAreDistinct); + diag.subdiagnostic(FnItemsAreDistinct); FunctionPointerSuggestion::CastRef { span, fn_name, sig: *sig } } (false, false) => { - diag.subdiagnostic(self.dcx(), FnItemsAreDistinct); + diag.subdiagnostic(FnItemsAreDistinct); FunctionPointerSuggestion::Cast { span, fn_name, sig: *sig } } }; - diag.subdiagnostic(self.dcx(), sugg); + diag.subdiagnostic(sugg); } (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => { let expected_sig = @@ -418,7 +417,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2)); if self.same_type_modulo_infer(*expected_sig, *found_sig) { - diag.subdiagnostic(self.dcx(), FnUniqTypes); + diag.subdiagnostic(FnUniqTypes); } if !self.same_type_modulo_infer(*found_sig, *expected_sig) @@ -447,7 +446,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } }; - diag.subdiagnostic(self.dcx(), sug); + diag.subdiagnostic(sug); } (ty::FnDef(did, args), ty::FnPtr(sig)) => { let expected_sig = @@ -466,7 +465,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { format!("{fn_name} as {found_sig}") }; - diag.subdiagnostic(self.dcx(), FnConsiderCasting { casting }); + diag.subdiagnostic(FnConsiderCasting { casting }); } _ => { return; @@ -578,16 +577,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { walk_stmt(self, ex) } } - - fn visit_body(&mut self, body: &'v hir::Body<'v>) -> Self::Result { - hir::intravisit::walk_body(self, body) - } } - self.tcx.hir().maybe_body_owned_by(cause.body_id).and_then(|body_id| { - let body = self.tcx.hir().body(body_id); + self.tcx.hir().maybe_body_owned_by(cause.body_id).and_then(|body| { IfVisitor { err_span: span, found_if: false } - .visit_body(body) + .visit_body(&body) .is_break() .then(|| TypeErrorAdditionalDiags::AddLetForLetChains { span: span.shrink_to_lo() }) }) @@ -894,7 +888,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let diag = self.consider_returning_binding_diag(blk, expected_ty); match diag { Some(diag) => { - err.subdiagnostic(self.dcx(), diag); + err.subdiagnostic(diag); true } None => false, diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index b2d89523ea84..4bb59bd9037c 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -33,7 +33,6 @@ use super::InferCtxt; use rustc_data_structures::fx::FxHashMap; use rustc_middle::bug; -use rustc_middle::infer::unify_key::ToType; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitableExt}; use std::collections::hash_map::Entry; @@ -80,7 +79,6 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { &mut self, input: Result, ty::InferConst>, freshener: F, - ty: Ty<'tcx>, ) -> ty::Const<'tcx> where F: FnOnce(u32) -> ty::InferConst, @@ -92,7 +90,7 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { Entry::Vacant(entry) => { let index = self.const_freshen_count; self.const_freshen_count += 1; - let ct = ty::Const::new_infer(self.infcx.tcx, freshener(index), ty); + let ct = ty::Const::new_infer(self.infcx.tcx, freshener(index)); entry.insert(ct); ct } @@ -150,7 +148,7 @@ impl<'a, 'tcx> TypeFolder> for TypeFreshener<'a, 'tcx> { ty::InferConst::Var(inner.const_unification_table().find(v).vid) }); drop(inner); - self.freshen_const(input, ty::InferConst::Fresh, ct.ty()) + self.freshen_const(input, ty::InferConst::Fresh) } ty::ConstKind::Infer(ty::InferConst::EffectVar(v)) => { let mut inner = self.infcx.inner.borrow_mut(); @@ -159,7 +157,7 @@ impl<'a, 'tcx> TypeFolder> for TypeFreshener<'a, 'tcx> { ty::InferConst::EffectVar(inner.effect_unification_table().find(v).vid) }); drop(inner); - self.freshen_const(input, ty::InferConst::Fresh, ct.ty()) + self.freshen_const(input, ty::InferConst::Fresh) } ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => { if i >= self.const_freshen_count { @@ -178,7 +176,7 @@ impl<'a, 'tcx> TypeFolder> for TypeFreshener<'a, 'tcx> { } ty::ConstKind::Param(_) - | ty::ConstKind::Value(_) + | ty::ConstKind::Value(_, _) | ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) | ty::ConstKind::Error(_) => ct.super_fold_with(self), @@ -204,22 +202,27 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { ty::IntVar(v) => { let mut inner = self.infcx.inner.borrow_mut(); - let input = inner - .int_unification_table() - .probe_value(v) - .map(|v| v.to_type(self.infcx.tcx)) - .ok_or_else(|| ty::IntVar(inner.int_unification_table().find(v))); + let value = inner.int_unification_table().probe_value(v); + let input = match value { + ty::IntVarValue::IntType(ty) => Ok(Ty::new_int(self.infcx.tcx, ty)), + ty::IntVarValue::UintType(ty) => Ok(Ty::new_uint(self.infcx.tcx, ty)), + ty::IntVarValue::Unknown => { + Err(ty::IntVar(inner.int_unification_table().find(v))) + } + }; drop(inner); Some(self.freshen_ty(input, |n| Ty::new_fresh_int(self.infcx.tcx, n))) } ty::FloatVar(v) => { let mut inner = self.infcx.inner.borrow_mut(); - let input = inner - .float_unification_table() - .probe_value(v) - .map(|v| v.to_type(self.infcx.tcx)) - .ok_or_else(|| ty::FloatVar(inner.float_unification_table().find(v))); + let value = inner.float_unification_table().probe_value(v); + let input = match value { + ty::FloatVarValue::Known(ty) => Ok(Ty::new_float(self.infcx.tcx, ty)), + ty::FloatVarValue::Unknown => { + Err(ty::FloatVar(inner.float_unification_table().find(v))) + } + }; drop(inner); Some(self.freshen_ty(input, |n| Ty::new_fresh_float(self.infcx.tcx, n))) } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 723f4c81ca52..4d6ddd7ba66a 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -2,18 +2,17 @@ pub use at::DefineOpaqueTypes; pub use freshen::TypeFreshener; pub use lexical_region_resolve::RegionResolutionError; pub use relate::combine::CombineFields; -pub use relate::combine::ObligationEmittingRelation; +pub use relate::combine::PredicateEmittingRelation; pub use relate::StructurallyRelateAliases; +use rustc_errors::DiagCtxtHandle; pub use rustc_macros::{TypeFoldable, TypeVisitable}; pub use rustc_middle::ty::IntVarValue; pub use BoundRegionConversionTime::*; pub use RegionVariableOrigin::*; pub use SubregionOrigin::*; -pub use ValuePairs::*; -use crate::traits::{ - self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine, TraitEngineExt, -}; +use crate::infer::relate::RelateResult; +use crate::traits::{self, ObligationCause, ObligationInspector, PredicateObligation, TraitEngine}; use error_reporting::TypeErrCtxt; use free_regions::RegionRelations; use lexical_region_resolve::LexicalRegionResolutions; @@ -25,23 +24,23 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::Rollback; use rustc_data_structures::unify as ut; -use rustc_errors::{Diag, DiagCtxt, ErrorGuaranteed}; +use rustc_errors::{Diag, ErrorGuaranteed}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_macros::extension; use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; +use rustc_middle::infer::unify_key::ConstVariableOrigin; use rustc_middle::infer::unify_key::ConstVariableValue; use rustc_middle::infer::unify_key::EffectVarValue; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ToType}; use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey}; use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::select; +use rustc_middle::traits::solve::{Goal, NoSolution}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BoundVarReplacerDelegate; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, GenericParamDefKind, InferConst, InferTy, Ty, TyCtxt}; +use rustc_middle::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt}; use rustc_middle::ty::{ConstVid, EffectVid, FloatVid, IntVid, TyVid}; use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgs, GenericArgsRef}; use rustc_middle::{bug, span_bug}; @@ -62,7 +61,7 @@ pub mod opaque_types; pub mod outlives; mod projection; pub mod region_constraints; -mod relate; +pub mod relate; pub mod resolve; pub(crate) mod snapshot; pub mod type_variable; @@ -71,7 +70,7 @@ pub mod type_variable; #[derive(Debug)] pub struct InferOk<'tcx, T> { pub value: T, - pub obligations: PredicateObligations<'tcx>, + pub obligations: Vec>, } pub type InferResult<'tcx, T> = Result, TypeError<'tcx>>; @@ -336,69 +335,6 @@ pub struct InferCtxt<'tcx> { pub obligation_inspector: Cell>>, } -impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> { - type Interner = TyCtxt<'tcx>; - - fn interner(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn universe_of_ty(&self, vid: TyVid) -> Option { - // FIXME(BoxyUwU): this is kind of jank and means that printing unresolved - // ty infers will give you the universe of the var it resolved to not the universe - // it actually had. It also means that if you have a `?0.1` and infer it to `u8` then - // try to print out `?0.1` it will just print `?0`. - match self.probe_ty_var(vid) { - Err(universe) => Some(universe), - Ok(_) => None, - } - } - - fn universe_of_ct(&self, ct: ConstVid) -> Option { - // Same issue as with `universe_of_ty` - match self.probe_const_var(ct) { - Err(universe) => Some(universe), - Ok(_) => None, - } - } - - fn universe_of_lt(&self, lt: ty::RegionVid) -> Option { - match self.inner.borrow_mut().unwrap_region_constraints().probe_value(lt) { - Err(universe) => Some(universe), - Ok(_) => None, - } - } - - fn root_ty_var(&self, vid: TyVid) -> TyVid { - self.root_var(vid) - } - - fn probe_ty_var(&self, vid: TyVid) -> Option> { - self.probe_ty_var(vid).ok() - } - - fn opportunistic_resolve_lt_var(&self, vid: ty::RegionVid) -> Option> { - let re = self - .inner - .borrow_mut() - .unwrap_region_constraints() - .opportunistic_resolve_var(self.tcx, vid); - if *re == ty::ReVar(vid) { None } else { Some(re) } - } - - fn root_ct_var(&self, vid: ConstVid) -> ConstVid { - self.root_const_var(vid) - } - - fn probe_ct_var(&self, vid: ConstVid) -> Option> { - self.probe_const_var(vid).ok() - } - - fn defining_opaque_types(&self) -> &'tcx ty::List { - self.defining_opaque_types - } -} - /// See the `error_reporting` module for more details. #[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable)] pub enum ValuePairs<'tcx> { @@ -409,13 +345,14 @@ pub enum ValuePairs<'tcx> { PolySigs(ExpectedFound>), ExistentialTraitRef(ExpectedFound>), ExistentialProjection(ExpectedFound>), + Dummy, } impl<'tcx> ValuePairs<'tcx> { pub fn ty(&self) -> Option<(Ty<'tcx>, Ty<'tcx>)> { if let ValuePairs::Terms(ExpectedFound { expected, found }) = self - && let Some(expected) = expected.ty() - && let Some(found) = found.ty() + && let Some(expected) = expected.as_type() + && let Some(found) = found.as_type() { Some((expected, found)) } else { @@ -577,6 +514,7 @@ pub enum FixupError { UnresolvedFloatTy(FloatVid), UnresolvedTy(TyVid), UnresolvedConst(ConstVid), + UnresolvedEffect(EffectVid), } /// See the `region_obligations` field for more information. @@ -604,6 +542,7 @@ impl fmt::Display for FixupError { ), UnresolvedTy(_) => write!(f, "unconstrained type"), UnresolvedConst(_) => write!(f, "unconstrained const value"), + UnresolvedEffect(_) => write!(f, "unconstrained effect value"), } } } @@ -727,10 +666,10 @@ impl<'tcx> InferCtxtBuilder<'tcx> { impl<'tcx, T> InferOk<'tcx, T> { /// Extracts `value`, registering any obligations into `fulfill_cx`. - pub fn into_value_registering_obligations( + pub fn into_value_registering_obligations( self, infcx: &InferCtxt<'tcx>, - fulfill_cx: &mut dyn TraitEngine<'tcx>, + fulfill_cx: &mut dyn TraitEngine<'tcx, E>, ) -> T { let InferOk { value, obligations } = self; fulfill_cx.register_predicate_obligations(infcx, obligations); @@ -739,16 +678,20 @@ impl<'tcx, T> InferOk<'tcx, T> { } impl<'tcx> InferOk<'tcx, ()> { - pub fn into_obligations(self) -> PredicateObligations<'tcx> { + pub fn into_obligations(self) -> Vec> { self.obligations } } impl<'tcx> InferCtxt<'tcx> { - pub fn dcx(&self) -> &'tcx DiagCtxt { + pub fn dcx(&self) -> DiagCtxtHandle<'tcx> { self.tcx.dcx() } + pub fn defining_opaque_types(&self) -> &'tcx ty::List { + self.defining_opaque_types + } + pub fn next_trait_solver(&self) -> bool { self.next_trait_solver } @@ -800,14 +743,14 @@ impl<'tcx> InferCtxt<'tcx> { .collect(); vars.extend( (0..inner.int_unification_table().len()) - .map(|i| ty::IntVid::from_u32(i as u32)) - .filter(|&vid| inner.int_unification_table().probe_value(vid).is_none()) + .map(|i| ty::IntVid::from_usize(i)) + .filter(|&vid| inner.int_unification_table().probe_value(vid).is_unknown()) .map(|v| Ty::new_int_var(self.tcx, v)), ); vars.extend( (0..inner.float_unification_table().len()) - .map(|i| ty::FloatVid::from_u32(i as u32)) - .filter(|&vid| inner.float_unification_table().probe_value(vid).is_none()) + .map(|i| ty::FloatVid::from_usize(i)) + .filter(|&vid| inner.float_unification_table().probe_value(vid).is_unknown()) .map(|v| Ty::new_float_var(self.tcx, v)), ); vars @@ -820,27 +763,10 @@ impl<'tcx> InferCtxt<'tcx> { (0..table.len()) .map(|i| ty::EffectVid::from_usize(i)) .filter(|&vid| table.probe_value(vid).is_unknown()) - .map(|v| { - ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(v), self.tcx.types.bool) - }) + .map(|v| ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(v))) .collect() } - fn combine_fields<'a>( - &'a self, - trace: TypeTrace<'tcx>, - param_env: ty::ParamEnv<'tcx>, - define_opaque_types: DefineOpaqueTypes, - ) -> CombineFields<'a, 'tcx> { - CombineFields { - infcx: self, - trace, - param_env, - obligations: PredicateObligations::new(), - define_opaque_types, - } - } - pub fn can_sub(&self, param_env: ty::ParamEnv<'tcx>, expected: T, actual: T) -> bool where T: at::ToTrace<'tcx>, @@ -947,19 +873,6 @@ impl<'tcx> InferCtxt<'tcx> { (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { return Err((a_vid, b_vid)); } - // We don't silently want to constrain hidden types here, so we assert that either one side is - // an infer var, so it'll get constrained to whatever the other side is, or there are no opaque - // types involved. - // We don't expect this to actually get hit, but if it does, we now at least know how to write - // a test for it. - (_, ty::Infer(ty::TyVar(_))) => {} - (ty::Infer(ty::TyVar(_)), _) => {} - _ if r_a != r_b && (r_a, r_b).has_opaque_types() => { - span_bug!( - cause.span(), - "opaque types got hidden types registered from within subtype predicate: {r_a:?} vs {r_b:?}" - ) - } _ => {} } @@ -1009,27 +922,22 @@ impl<'tcx> InferCtxt<'tcx> { Ty::new_var(self.tcx, vid) } - pub fn next_const_var(&self, ty: Ty<'tcx>, span: Span) -> ty::Const<'tcx> { - self.next_const_var_with_origin(ty, ConstVariableOrigin { span, param_def_id: None }) + pub fn next_const_var(&self, span: Span) -> ty::Const<'tcx> { + self.next_const_var_with_origin(ConstVariableOrigin { span, param_def_id: None }) } - pub fn next_const_var_with_origin( - &self, - ty: Ty<'tcx>, - origin: ConstVariableOrigin, - ) -> ty::Const<'tcx> { + pub fn next_const_var_with_origin(&self, origin: ConstVariableOrigin) -> ty::Const<'tcx> { let vid = self .inner .borrow_mut() .const_unification_table() .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() }) .vid; - ty::Const::new_var(self.tcx, vid, ty) + ty::Const::new_var(self.tcx, vid) } pub fn next_const_var_in_universe( &self, - ty: Ty<'tcx>, span: Span, universe: ty::UniverseIndex, ) -> ty::Const<'tcx> { @@ -1040,17 +948,31 @@ impl<'tcx> InferCtxt<'tcx> { .const_unification_table() .new_key(ConstVariableValue::Unknown { origin, universe }) .vid; - ty::Const::new_var(self.tcx, vid, ty) + ty::Const::new_var(self.tcx, vid) + } + + pub fn next_const_var_id(&self, origin: ConstVariableOrigin) -> ConstVid { + self.inner + .borrow_mut() + .const_unification_table() + .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() }) + .vid + } + + fn next_int_var_id(&self) -> IntVid { + self.inner.borrow_mut().int_unification_table().new_key(ty::IntVarValue::Unknown) } pub fn next_int_var(&self) -> Ty<'tcx> { - let vid = self.inner.borrow_mut().int_unification_table().new_key(None); - Ty::new_int_var(self.tcx, vid) + Ty::new_int_var(self.tcx, self.next_int_var_id()) + } + + fn next_float_var_id(&self) -> FloatVid { + self.inner.borrow_mut().float_unification_table().new_key(ty::FloatVarValue::Unknown) } pub fn next_float_var(&self) -> Ty<'tcx> { - let vid = self.inner.borrow_mut().float_unification_table().new_key(None); - Ty::new_float_var(self.tcx, vid) + Ty::new_float_var(self.tcx, self.next_float_var_id()) } /// Creates a fresh region variable with the next available index. @@ -1137,15 +1059,7 @@ impl<'tcx> InferCtxt<'tcx> { .const_unification_table() .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() }) .vid; - ty::Const::new_var( - self.tcx, - const_var_id, - self.tcx - .type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"), - ) - .into() + ty::Const::new_var(self.tcx, const_var_id).into() } } } @@ -1159,7 +1073,7 @@ impl<'tcx> InferCtxt<'tcx> { .no_bound_vars() .expect("const parameter types cannot be generic"); debug_assert_eq!(self.tcx.types.bool, ty); - ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(effect_vid), ty).into() + ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(effect_vid)).into() } /// Given a set of generics defined on a type or impl, returns the generic parameters mapping each @@ -1252,45 +1166,44 @@ impl<'tcx> InferCtxt<'tcx> { } pub fn shallow_resolve(&self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Infer(v) = ty.kind() { self.fold_infer_ty(*v).unwrap_or(ty) } else { ty } - } + if let ty::Infer(v) = *ty.kind() { + match v { + ty::TyVar(v) => { + // Not entirely obvious: if `typ` is a type variable, + // it can be resolved to an int/float variable, which + // can then be recursively resolved, hence the + // recursion. Note though that we prevent type + // variables from unifying to other type variables + // directly (though they may be embedded + // structurally), and we prevent cycles in any case, + // so this recursion should always be of very limited + // depth. + // + // Note: if these two lines are combined into one we get + // dynamic borrow errors on `self.inner`. + let known = self.inner.borrow_mut().type_variables().probe(v).known(); + known.map_or(ty, |t| self.shallow_resolve(t)) + } - // This is separate from `shallow_resolve` to keep that method small and inlinable. - #[inline(never)] - fn fold_infer_ty(&self, v: InferTy) -> Option> { - match v { - ty::TyVar(v) => { - // Not entirely obvious: if `typ` is a type variable, - // it can be resolved to an int/float variable, which - // can then be recursively resolved, hence the - // recursion. Note though that we prevent type - // variables from unifying to other type variables - // directly (though they may be embedded - // structurally), and we prevent cycles in any case, - // so this recursion should always be of very limited - // depth. - // - // Note: if these two lines are combined into one we get - // dynamic borrow errors on `self.inner`. - let known = self.inner.borrow_mut().type_variables().probe(v).known(); - known.map(|t| self.shallow_resolve(t)) + ty::IntVar(v) => { + match self.inner.borrow_mut().int_unification_table().probe_value(v) { + ty::IntVarValue::IntType(ty) => Ty::new_int(self.tcx, ty), + ty::IntVarValue::UintType(ty) => Ty::new_uint(self.tcx, ty), + ty::IntVarValue::Unknown => ty, + } + } + + ty::FloatVar(v) => { + match self.inner.borrow_mut().float_unification_table().probe_value(v) { + ty::FloatVarValue::Known(ty) => Ty::new_float(self.tcx, ty), + ty::FloatVarValue::Unknown => ty, + } + } + + ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => ty, } - - ty::IntVar(v) => self - .inner - .borrow_mut() - .int_unification_table() - .probe_value(v) - .map(|v| v.to_type(self.tcx)), - - ty::FloatVar(v) => self - .inner - .borrow_mut() - .float_unification_table() - .probe_value(v) - .map(|v| v.to_type(self.tcx)), - - ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => None, + } else { + ty } } @@ -1317,7 +1230,7 @@ impl<'tcx> InferCtxt<'tcx> { | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) | ty::ConstKind::Unevaluated(_) - | ty::ConstKind::Value(_) + | ty::ConstKind::Value(_, _) | ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => ct, } @@ -1339,10 +1252,13 @@ impl<'tcx> InferCtxt<'tcx> { /// or else the root int var in the unification table. pub fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> Ty<'tcx> { let mut inner = self.inner.borrow_mut(); - if let Some(value) = inner.int_unification_table().probe_value(vid) { - value.to_type(self.tcx) - } else { - Ty::new_int_var(self.tcx, inner.int_unification_table().find(vid)) + let value = inner.int_unification_table().probe_value(vid); + match value { + ty::IntVarValue::IntType(ty) => Ty::new_int(self.tcx, ty), + ty::IntVarValue::UintType(ty) => Ty::new_uint(self.tcx, ty), + ty::IntVarValue::Unknown => { + Ty::new_int_var(self.tcx, inner.int_unification_table().find(vid)) + } } } @@ -1350,10 +1266,12 @@ impl<'tcx> InferCtxt<'tcx> { /// or else the root float var in the unification table. pub fn opportunistic_resolve_float_var(&self, vid: ty::FloatVid) -> Ty<'tcx> { let mut inner = self.inner.borrow_mut(); - if let Some(value) = inner.float_unification_table().probe_value(vid) { - value.to_type(self.tcx) - } else { - Ty::new_float_var(self.tcx, inner.float_unification_table().find(vid)) + let value = inner.float_unification_table().probe_value(vid); + match value { + ty::FloatVarValue::Known(ty) => Ty::new_float(self.tcx, ty), + ty::FloatVarValue::Unknown => { + Ty::new_float_var(self.tcx, inner.float_unification_table().find(vid)) + } } } @@ -1467,10 +1385,10 @@ impl<'tcx> InferCtxt<'tcx> { .or_insert_with(|| self.infcx.next_ty_var(self.span).into()) .expect_ty() } - fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx> { + fn replace_const(&mut self, bv: ty::BoundVar) -> ty::Const<'tcx> { self.map .entry(bv) - .or_insert_with(|| self.infcx.next_const_var(ty, self.span).into()) + .or_insert_with(|| self.infcx.next_const_var(self.span).into()) .expect_const() } } @@ -1524,11 +1442,14 @@ impl<'tcx> InferCtxt<'tcx> { &self, param_env: ty::ParamEnv<'tcx>, unevaluated: ty::UnevaluatedConst<'tcx>, - ty: Ty<'tcx>, span: Span, ) -> Result, ErrorHandled> { match self.const_eval_resolve(param_env, unevaluated, span) { - Ok(Some(val)) => Ok(ty::Const::new_value(self.tcx, val, ty)), + Ok(Some(val)) => Ok(ty::Const::new_value( + self.tcx, + val, + self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args), + )), Ok(None) => { let tcx = self.tcx; let def_id = unevaluated.def; @@ -1644,7 +1565,7 @@ impl<'tcx> InferCtxt<'tcx> { // If `inlined_probe_value` returns a value it's always a // `ty::Int(_)` or `ty::UInt(_)`, which never matches a // `ty::Infer(_)`. - self.inner.borrow_mut().int_unification_table().inlined_probe_value(v).is_some() + self.inner.borrow_mut().int_unification_table().inlined_probe_value(v).is_known() } TyOrConstInferVar::TyFloat(v) => { @@ -1652,7 +1573,7 @@ impl<'tcx> InferCtxt<'tcx> { // `ty::Float(_)`, which never matches a `ty::Infer(_)`. // // Not `inlined_probe_value(v)` because this call site is colder. - self.inner.borrow_mut().float_unification_table().probe_value(v).is_some() + self.inner.borrow_mut().float_unification_table().probe_value(v).is_known() } TyOrConstInferVar::Const(v) => { @@ -1824,7 +1745,7 @@ impl<'tcx> TypeTrace<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), } } @@ -1836,7 +1757,7 @@ impl<'tcx> TypeTrace<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: TraitRefs(ExpectedFound::new(a_is_expected, a, b)), + values: ValuePairs::TraitRefs(ExpectedFound::new(a_is_expected, a, b)), } } @@ -1848,9 +1769,13 @@ impl<'tcx> TypeTrace<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), } } + + fn dummy(cause: &ObligationCause<'tcx>) -> TypeTrace<'tcx> { + TypeTrace { cause: cause.clone(), values: ValuePairs::Dummy } + } } impl<'tcx> SubregionOrigin<'tcx> { @@ -1962,11 +1887,6 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { if let ty::ConstKind::Infer(_) = c.kind() { - let ty = c.ty(); - // If the type references param or infer then ICE ICE ICE - if ty.has_non_region_param() || ty.has_non_region_infer() { - bug!("const `{c}`'s type should not reference params or types"); - } ty::Const::new_placeholder( self.tcx, ty::PlaceholderConst { @@ -1977,7 +1897,6 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( idx }), }, - ty, ) } else { c.super_fold_with(self) diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index 8eb3185673b4..b8dd501a721b 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -1,11 +1,11 @@ -use super::{DefineOpaqueTypes, InferResult}; use crate::errors::OpaqueHiddenTypeDiag; use crate::infer::{InferCtxt, InferOk}; -use crate::traits::{self, PredicateObligation}; +use crate::traits::{self, Obligation}; use hir::def_id::{DefId, LocalDefId}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; +use rustc_middle::traits::solve::Goal; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BottomUpFolder; @@ -21,6 +21,8 @@ mod table; pub type OpaqueTypeMap<'tcx> = FxIndexMap, OpaqueTypeDecl<'tcx>>; pub use table::{OpaqueTypeStorage, OpaqueTypeTable}; +use super::DefineOpaqueTypes; + /// Information about the opaque types whose values we /// are inferring in this function (these are the `impl Trait` that /// appear in the return type). @@ -62,11 +64,23 @@ impl<'tcx> InferCtxt<'tcx> { { let def_span = self.tcx.def_span(def_id); let span = if span.contains(def_span) { def_span } else { span }; - let code = traits::ObligationCauseCode::OpaqueReturnType(None); - let cause = ObligationCause::new(span, body_id, code); let ty_var = self.next_ty_var(span); obligations.extend( - self.handle_opaque_type(ty, ty_var, &cause, param_env).unwrap().obligations, + self.handle_opaque_type(ty, ty_var, span, param_env) + .unwrap() + .into_iter() + .map(|goal| { + Obligation::new( + self.tcx, + ObligationCause::new( + span, + body_id, + traits::ObligationCauseCode::OpaqueReturnType(None), + ), + goal.param_env, + goal.predicate, + ) + }), ); ty_var } @@ -80,9 +94,9 @@ impl<'tcx> InferCtxt<'tcx> { &self, a: Ty<'tcx>, b: Ty<'tcx>, - cause: &ObligationCause<'tcx>, + span: Span, param_env: ty::ParamEnv<'tcx>, - ) -> InferResult<'tcx, ()> { + ) -> Result>>, TypeError<'tcx>> { let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() { ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) if def_id.is_local() => { let def_id = def_id.expect_local(); @@ -90,7 +104,7 @@ impl<'tcx> InferCtxt<'tcx> { // See comment on `insert_hidden_type` for why this is sufficient in coherence return Some(self.register_hidden_type( OpaqueTypeKey { def_id, args }, - cause.clone(), + span, param_env, b, )); @@ -143,18 +157,13 @@ impl<'tcx> InferCtxt<'tcx> { && self.tcx.is_type_alias_impl_trait(b_def_id) { self.tcx.dcx().emit_err(OpaqueHiddenTypeDiag { - span: cause.span, + span, hidden_type: self.tcx.def_span(b_def_id), opaque_type: self.tcx.def_span(def_id), }); } } - Some(self.register_hidden_type( - OpaqueTypeKey { def_id, args }, - cause.clone(), - param_env, - b, - )) + Some(self.register_hidden_type(OpaqueTypeKey { def_id, args }, span, param_env, b)) } _ => None, }; @@ -336,7 +345,7 @@ impl<'tcx> InferCtxt<'tcx> { .args .iter() .enumerate() - .filter(|(i, _)| variances[*i] == ty::Variance::Invariant) + .filter(|(i, _)| variances[*i] == ty::Invariant) .filter_map(|(_, arg)| match arg.unpack() { GenericArgKind::Lifetime(r) => Some(r), GenericArgKind::Type(_) | GenericArgKind::Const(_) => None, @@ -432,7 +441,7 @@ where let variances = self.tcx.variances_of(*def_id); for (v, s) in std::iter::zip(variances, args.iter()) { - if *v != ty::Variance::Bivariant { + if *v != ty::Bivariant { s.visit_with(self); } } @@ -464,24 +473,23 @@ impl<'tcx> InferCtxt<'tcx> { fn register_hidden_type( &self, opaque_type_key: OpaqueTypeKey<'tcx>, - cause: ObligationCause<'tcx>, + span: Span, param_env: ty::ParamEnv<'tcx>, hidden_ty: Ty<'tcx>, - ) -> InferResult<'tcx, ()> { - let mut obligations = Vec::new(); + ) -> Result>>, TypeError<'tcx>> { + let mut goals = Vec::new(); - self.insert_hidden_type(opaque_type_key, &cause, param_env, hidden_ty, &mut obligations)?; + self.insert_hidden_type(opaque_type_key, span, param_env, hidden_ty, &mut goals)?; self.add_item_bounds_for_hidden_type( opaque_type_key.def_id.to_def_id(), opaque_type_key.args, - cause, param_env, hidden_ty, - &mut obligations, + &mut goals, ); - Ok(InferOk { value: (), obligations }) + Ok(goals) } /// Insert a hidden type into the opaque type storage, making sure @@ -507,27 +515,21 @@ impl<'tcx> InferCtxt<'tcx> { pub fn insert_hidden_type( &self, opaque_type_key: OpaqueTypeKey<'tcx>, - cause: &ObligationCause<'tcx>, + span: Span, param_env: ty::ParamEnv<'tcx>, hidden_ty: Ty<'tcx>, - obligations: &mut Vec>, + goals: &mut Vec>>, ) -> Result<(), TypeError<'tcx>> { // Ideally, we'd get the span where *this specific `ty` came // from*, but right now we just use the span from the overall // value being folded. In simple cases like `-> impl Foo`, // these are the same span, but not in cases like `-> (impl // Foo, impl Bar)`. - let span = cause.span; if self.intercrate { // During intercrate we do not define opaque types but instead always // force ambiguity unless the hidden type is known to not implement // our trait. - obligations.push(traits::Obligation::new( - self.tcx, - cause.clone(), - param_env, - ty::PredicateKind::Ambiguous, - )) + goals.push(Goal::new(self.tcx, param_env, ty::PredicateKind::Ambiguous)) } else { let prev = self .inner @@ -535,10 +537,13 @@ impl<'tcx> InferCtxt<'tcx> { .opaque_types() .register(opaque_type_key, OpaqueHiddenType { ty: hidden_ty, span }); if let Some(prev) = prev { - obligations.extend( - self.at(cause, param_env) + goals.extend( + self.at(&ObligationCause::dummy_with_span(span), param_env) .eq(DefineOpaqueTypes::Yes, prev, hidden_ty)? - .obligations, + .obligations + .into_iter() + // FIXME: Shuttling between obligations and goals is awkward. + .map(Goal::from), ); } }; @@ -550,10 +555,9 @@ impl<'tcx> InferCtxt<'tcx> { &self, def_id: DefId, args: ty::GenericArgsRef<'tcx>, - cause: ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, hidden_ty: Ty<'tcx>, - obligations: &mut Vec>, + goals: &mut Vec>>, ) { let tcx = self.tcx; // Require that the hidden type is well-formed. We have to @@ -567,12 +571,7 @@ impl<'tcx> InferCtxt<'tcx> { // type during MIR borrowck, causing us to infer the wrong // lifetime for its member constraints which then results in // unexpected region errors. - obligations.push(traits::Obligation::new( - tcx, - cause.clone(), - param_env, - ty::ClauseKind::WellFormed(hidden_ty.into()), - )); + goals.push(Goal::new(tcx, param_env, ty::ClauseKind::WellFormed(hidden_ty.into()))); let item_bounds = tcx.explicit_item_bounds(def_id); for (predicate, _) in item_bounds.iter_instantiated_copied(tcx, args) { @@ -588,13 +587,18 @@ impl<'tcx> InferCtxt<'tcx> { && !tcx.is_impl_trait_in_trait(projection_ty.def_id) && !self.next_trait_solver() => { - self.projection_ty_to_infer( + let ty_var = self.next_ty_var(self.tcx.def_span(projection_ty.def_id)); + goals.push(Goal::new( + self.tcx, param_env, - projection_ty, - cause.clone(), - 0, - obligations, - ) + ty::PredicateKind::Clause(ty::ClauseKind::Projection( + ty::ProjectionPredicate { + projection_term: projection_ty.into(), + term: ty_var.into(), + }, + )), + )); + ty_var } // Replace all other mentions of the same opaque type with the hidden type, // as the bounds must hold on the hidden type after all. @@ -611,12 +615,7 @@ impl<'tcx> InferCtxt<'tcx> { // Require that the predicate holds for the concrete type. debug!(?predicate); - obligations.push(traits::Obligation::new( - self.tcx, - cause.clone(), - param_env, - predicate, - )); + goals.push(Goal::new(self.tcx, param_env, predicate)); } } } diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index c44a5082f68b..5bcb4f29364d 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -64,8 +64,7 @@ struct OutlivesEnvironmentBuilder<'tcx> { /// "Region-bound pairs" tracks outlives relations that are known to /// be true, either because of explicit where-clauses like `T: 'a` or /// because of implied bounds. -pub type RegionBoundPairs<'tcx> = - FxIndexSet, Region<'tcx>>>; +pub type RegionBoundPairs<'tcx> = FxIndexSet>>; impl<'tcx> OutlivesEnvironment<'tcx> { /// Create a builder using `ParamEnv` and add explicit outlives bounds into it. diff --git a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs index 488f435994d4..c02ab98b2bae 100644 --- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs +++ b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs @@ -102,9 +102,7 @@ where }; for (idx, s) in args.iter().enumerate() { - if variances.map(|variances| variances[idx]) - != Some(ty::Variance::Bivariant) - { + if variances.map(|variances| variances[idx]) != Some(ty::Bivariant) { s.visit_with(self); } } diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index 29c11d4247d0..978b92fd8980 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -1,15 +1,12 @@ use std::collections::hash_map::Entry; use rustc_data_structures::fx::FxHashMap; +use rustc_middle::ty::error::TypeError; use rustc_middle::ty::TypeVisitableExt; -use rustc_middle::ty::{ - self, - error::TypeError, - relate::{self, Relate, RelateResult, TypeRelation}, - Ty, TyCtxt, -}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use crate::infer::region_constraints::VerifyIfEq; +use crate::infer::relate::{self as relate, Relate, RelateResult, TypeRelation}; /// Given a "verify-if-eq" type test like: /// @@ -135,7 +132,7 @@ impl<'tcx> MatchAgainstHigherRankedOutlives<'tcx> { } } -impl<'tcx> TypeRelation<'tcx> for MatchAgainstHigherRankedOutlives<'tcx> { +impl<'tcx> TypeRelation> for MatchAgainstHigherRankedOutlives<'tcx> { fn tag(&self) -> &'static str { "MatchAgainstHigherRankedOutlives" } @@ -145,10 +142,10 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstHigherRankedOutlives<'tcx> { } #[instrument(level = "trace", skip(self))] - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, variance: ty::Variance, - _: ty::VarianceDiagInfo<'tcx>, + _: ty::VarianceDiagInfo>, a: T, b: T, ) -> RelateResult<'tcx, T> { @@ -208,7 +205,7 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstHigherRankedOutlives<'tcx> { value: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: Relate<'tcx>, + T: Relate>, { self.pattern_depth.shift_in(1); let result = Ok(pattern.rebind(self.relate(pattern.skip_binder(), value.skip_binder())?)); diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index bd981c205677..7e977b9b9545 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -94,7 +94,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { pub fn approx_declared_bounds_from_env( &self, alias_ty: ty::AliasTy<'tcx>, - ) -> Vec, ty::Region<'tcx>>>> { + ) -> Vec> { let erased_alias_ty = self.tcx.erase_regions(alias_ty.to_ty(self.tcx)); self.declared_generic_bounds_from_env_for_erased_ty(erased_alias_ty) } @@ -193,7 +193,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { fn declared_generic_bounds_from_env( &self, generic_ty: Ty<'tcx>, - ) -> Vec, ty::Region<'tcx>>>> { + ) -> Vec> { assert!(matches!(generic_ty.kind(), ty::Param(_) | ty::Placeholder(_))); self.declared_generic_bounds_from_env_for_erased_ty(generic_ty) } @@ -213,7 +213,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { fn declared_generic_bounds_from_env_for_erased_ty( &self, erased_ty: Ty<'tcx>, - ) -> Vec, ty::Region<'tcx>>>> { + ) -> Vec> { let tcx = self.tcx; // To start, collect bounds from user environment. Note that diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs index 167863479806..a1ba43eb1715 100644 --- a/compiler/rustc_infer/src/infer/projection.rs +++ b/compiler/rustc_infer/src/infer/projection.rs @@ -21,11 +21,12 @@ impl<'tcx> InferCtxt<'tcx> { obligations: &mut Vec>, ) -> Ty<'tcx> { debug_assert!(!self.next_trait_solver()); - let def_id = projection_ty.def_id; - let ty_var = self.next_ty_var(self.tcx.def_span(def_id)); - let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::Projection( - ty::ProjectionPredicate { projection_term: projection_ty.into(), term: ty_var.into() }, - ))); + let ty_var = self.next_ty_var(self.tcx.def_span(projection_ty.def_id)); + let projection = + ty::PredicateKind::Clause(ty::ClauseKind::Projection(ty::ProjectionPredicate { + projection_term: projection_ty.into(), + term: ty_var.into(), + })); let obligation = Obligation::with_depth(self.tcx, cause, recursion_depth, param_env, projection); obligations.push(obligation); diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs index 255ca52d3e98..5b159d627312 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -1,11 +1,11 @@ use super::*; +use crate::infer::relate::RelateResult; use crate::infer::snapshot::CombinedSnapshot; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::graph::{scc::Sccs, vec_graph::VecGraph}; use rustc_index::Idx; use rustc_middle::span_bug; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::relate::RelateResult; impl<'tcx> RegionConstraintCollector<'_, 'tcx> { /// Searches new universes created during `snapshot`, looking for @@ -276,7 +276,7 @@ impl<'a, 'b, 'tcx> LeakCheck<'a, 'b, 'tcx> { other_region: ty::Region<'tcx>, ) -> TypeError<'tcx> { debug!("error: placeholder={:?}, other_region={:?}", placeholder, other_region); - TypeError::RegionsInsufficientlyPolymorphic(placeholder.bound.kind, other_region) + TypeError::RegionsInsufficientlyPolymorphic(placeholder.bound, other_region) } } diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs index 101598c59512..0a2e85cc8919 100644 --- a/compiler/rustc_infer/src/infer/relate/combine.rs +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -22,13 +22,14 @@ use super::glb::Glb; use super::lub::Lub; use super::type_relating::TypeRelating; use super::StructurallyRelateAliases; +use super::{RelateResult, TypeRelation}; +use crate::infer::relate; use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace}; -use crate::traits::{Obligation, PredicateObligations}; +use crate::traits::{Obligation, PredicateObligation}; use rustc_middle::bug; -use rustc_middle::infer::canonical::OriginalQueryValues; use rustc_middle::infer::unify_key::EffectVarValue; +use rustc_middle::traits::solve::Goal; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::relate::{RelateResult, TypeRelation}; use rustc_middle::ty::{self, InferConst, Ty, TyCtxt, TypeVisitableExt, Upcast}; use rustc_middle::ty::{IntType, UintType}; use rustc_span::Span; @@ -38,10 +39,35 @@ pub struct CombineFields<'infcx, 'tcx> { pub infcx: &'infcx InferCtxt<'tcx>, pub trace: TypeTrace<'tcx>, pub param_env: ty::ParamEnv<'tcx>, - pub obligations: PredicateObligations<'tcx>, + pub goals: Vec>>, pub define_opaque_types: DefineOpaqueTypes, } +impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { + pub fn new( + infcx: &'infcx InferCtxt<'tcx>, + trace: TypeTrace<'tcx>, + param_env: ty::ParamEnv<'tcx>, + define_opaque_types: DefineOpaqueTypes, + ) -> Self { + Self { infcx, trace, param_env, define_opaque_types, goals: vec![] } + } + + pub(crate) fn into_obligations(self) -> Vec> { + self.goals + .into_iter() + .map(|goal| { + Obligation::new( + self.infcx.tcx, + self.trace.cause.clone(), + goal.param_env, + goal.predicate, + ) + }) + .collect() + } +} + impl<'tcx> InferCtxt<'tcx> { pub fn super_combine_tys( &self, @@ -50,7 +76,7 @@ impl<'tcx> InferCtxt<'tcx> { b: Ty<'tcx>, ) -> RelateResult<'tcx, Ty<'tcx>> where - R: ObligationEmittingRelation<'tcx>, + R: PredicateEmittingRelation<'tcx>, { debug_assert!(!a.has_escaping_bound_vars()); debug_assert!(!b.has_escaping_bound_vars()); @@ -58,40 +84,38 @@ impl<'tcx> InferCtxt<'tcx> { match (a.kind(), b.kind()) { // Relate integral variables to other types (&ty::Infer(ty::IntVar(a_id)), &ty::Infer(ty::IntVar(b_id))) => { - self.inner - .borrow_mut() - .int_unification_table() - .unify_var_var(a_id, b_id) - .map_err(|e| int_unification_error(true, e))?; + self.inner.borrow_mut().int_unification_table().union(a_id, b_id); Ok(a) } (&ty::Infer(ty::IntVar(v_id)), &ty::Int(v)) => { - self.unify_integral_variable(true, v_id, IntType(v)) + self.unify_integral_variable(v_id, IntType(v)); + Ok(b) } (&ty::Int(v), &ty::Infer(ty::IntVar(v_id))) => { - self.unify_integral_variable(false, v_id, IntType(v)) + self.unify_integral_variable(v_id, IntType(v)); + Ok(a) } (&ty::Infer(ty::IntVar(v_id)), &ty::Uint(v)) => { - self.unify_integral_variable(true, v_id, UintType(v)) + self.unify_integral_variable(v_id, UintType(v)); + Ok(b) } (&ty::Uint(v), &ty::Infer(ty::IntVar(v_id))) => { - self.unify_integral_variable(false, v_id, UintType(v)) + self.unify_integral_variable(v_id, UintType(v)); + Ok(a) } // Relate floating-point variables to other types (&ty::Infer(ty::FloatVar(a_id)), &ty::Infer(ty::FloatVar(b_id))) => { - self.inner - .borrow_mut() - .float_unification_table() - .unify_var_var(a_id, b_id) - .map_err(|e| float_unification_error(true, e))?; + self.inner.borrow_mut().float_unification_table().union(a_id, b_id); Ok(a) } (&ty::Infer(ty::FloatVar(v_id)), &ty::Float(v)) => { - self.unify_float_variable(true, v_id, v) + self.unify_float_variable(v_id, ty::FloatVarValue::Known(v)); + Ok(b) } (&ty::Float(v), &ty::Infer(ty::FloatVar(v_id))) => { - self.unify_float_variable(false, v_id, v) + self.unify_float_variable(v_id, ty::FloatVarValue::Known(v)); + Ok(a) } // We don't expect `TyVar` or `Fresh*` vars at this point with lazy norm. @@ -113,10 +137,10 @@ impl<'tcx> InferCtxt<'tcx> { (_, ty::Alias(..)) | (ty::Alias(..), _) if self.next_trait_solver() => { match relation.structurally_relate_aliases() { StructurallyRelateAliases::Yes => { - ty::relate::structurally_relate_tys(relation, a, b) + relate::structurally_relate_tys(relation, a, b) } StructurallyRelateAliases::No => { - relation.register_type_relate_obligation(a, b); + relation.register_alias_relate_predicate(a, b); Ok(a) } } @@ -124,7 +148,7 @@ impl<'tcx> InferCtxt<'tcx> { // All other cases of inference are errors (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { - Err(TypeError::Sorts(ty::relate::expected_found(a, b))) + Err(TypeError::Sorts(ExpectedFound::new(true, a, b))) } // During coherence, opaque types should be treated as *possibly* @@ -136,7 +160,7 @@ impl<'tcx> InferCtxt<'tcx> { Ok(a) } - _ => ty::relate::structurally_relate_tys(relation, a, b), + _ => relate::structurally_relate_tys(relation, a, b), } } @@ -147,7 +171,7 @@ impl<'tcx> InferCtxt<'tcx> { b: ty::Const<'tcx>, ) -> RelateResult<'tcx, ty::Const<'tcx>> where - R: ObligationEmittingRelation<'tcx>, + R: PredicateEmittingRelation<'tcx>, { debug!("{}.consts({:?}, {:?})", relation.tag(), a, b); debug_assert!(!a.has_escaping_bound_vars()); @@ -159,37 +183,6 @@ impl<'tcx> InferCtxt<'tcx> { let a = self.shallow_resolve_const(a); let b = self.shallow_resolve_const(b); - // We should never have to relate the `ty` field on `Const` as it is checked elsewhere that consts have the - // correct type for the generic param they are an argument for. However there have been a number of cases - // historically where asserting that the types are equal has found bugs in the compiler so this is valuable - // to check even if it is a bit nasty impl wise :( - // - // This probe is probably not strictly necessary but it seems better to be safe and not accidentally find - // ourselves with a check to find bugs being required for code to compile because it made inference progress. - self.probe(|_| { - if a.ty() == b.ty() { - return; - } - - // We don't have access to trait solving machinery in `rustc_infer` so the logic for determining if the - // two const param's types are able to be equal has to go through a canonical query with the actual logic - // in `rustc_trait_selection`. - let canonical = self.canonicalize_query( - relation.param_env().and((a.ty(), b.ty())), - &mut OriginalQueryValues::default(), - ); - self.tcx.check_tys_might_be_eq(canonical).unwrap_or_else(|_| { - // The error will only be reported later. If we emit an ErrorGuaranteed - // here, then we will never get to the code that actually emits the error. - self.tcx.dcx().delayed_bug(format!( - "cannot relate consts of different types (a={a:?}, b={b:?})", - )); - // We treat these constants as if they were of the same type, so that any - // such constants being used in impls make these impls match barring other mismatches. - // This helps with diagnostics down the road. - }); - }); - match (a.kind(), b.kind()) { ( ty::ConstKind::Infer(InferConst::Var(a_vid)), @@ -257,43 +250,22 @@ impl<'tcx> InferCtxt<'tcx> { Ok(b) } StructurallyRelateAliases::Yes => { - ty::relate::structurally_relate_consts(relation, a, b) + relate::structurally_relate_consts(relation, a, b) } } } - _ => ty::relate::structurally_relate_consts(relation, a, b), + _ => relate::structurally_relate_consts(relation, a, b), } } - fn unify_integral_variable( - &self, - vid_is_expected: bool, - vid: ty::IntVid, - val: ty::IntVarValue, - ) -> RelateResult<'tcx, Ty<'tcx>> { - self.inner - .borrow_mut() - .int_unification_table() - .unify_var_value(vid, Some(val)) - .map_err(|e| int_unification_error(vid_is_expected, e))?; - match val { - IntType(v) => Ok(Ty::new_int(self.tcx, v)), - UintType(v) => Ok(Ty::new_uint(self.tcx, v)), - } + #[inline(always)] + fn unify_integral_variable(&self, vid: ty::IntVid, val: ty::IntVarValue) { + self.inner.borrow_mut().int_unification_table().union_value(vid, val); } - fn unify_float_variable( - &self, - vid_is_expected: bool, - vid: ty::FloatVid, - val: ty::FloatTy, - ) -> RelateResult<'tcx, Ty<'tcx>> { - self.inner - .borrow_mut() - .float_unification_table() - .unify_var_value(vid, Some(ty::FloatVarValue(val))) - .map_err(|e| float_unification_error(vid_is_expected, e))?; - Ok(Ty::new_float(self.tcx, val)) + #[inline(always)] + fn unify_float_variable(&self, vid: ty::FloatVid, val: ty::FloatVarValue) { + self.inner.borrow_mut().float_unification_table().union_value(vid, val); } fn unify_effect_variable(&self, vid: ty::EffectVid, val: ty::Const<'tcx>) -> ty::Const<'tcx> { @@ -333,21 +305,26 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { Glb::new(self) } - pub fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { - self.obligations.extend(obligations); + pub fn register_obligations( + &mut self, + obligations: impl IntoIterator>>, + ) { + self.goals.extend(obligations); } pub fn register_predicates( &mut self, obligations: impl IntoIterator, ty::Predicate<'tcx>>>, ) { - self.obligations.extend(obligations.into_iter().map(|to_pred| { - Obligation::new(self.infcx.tcx, self.trace.cause.clone(), self.param_env, to_pred) - })) + self.goals.extend( + obligations + .into_iter() + .map(|to_pred| Goal::new(self.infcx.tcx, self.param_env, to_pred)), + ) } } -pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> { +pub trait PredicateEmittingRelation<'tcx>: TypeRelation> { fn span(&self) -> Span; fn param_env(&self) -> ty::ParamEnv<'tcx>; @@ -358,32 +335,18 @@ pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> { fn structurally_relate_aliases(&self) -> StructurallyRelateAliases; /// Register obligations that must hold in order for this relation to hold - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>); + fn register_goals( + &mut self, + obligations: impl IntoIterator>>, + ); - /// Register predicates that must hold in order for this relation to hold. Uses - /// a default obligation cause, [`ObligationEmittingRelation::register_obligations`] should - /// be used if control over the obligation causes is required. + /// Register predicates that must hold in order for this relation to hold. + /// This uses the default `param_env` of the obligation. fn register_predicates( &mut self, obligations: impl IntoIterator, ty::Predicate<'tcx>>>, ); /// Register `AliasRelate` obligation(s) that both types must be related to each other. - fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>); -} - -fn int_unification_error<'tcx>( - a_is_expected: bool, - v: (ty::IntVarValue, ty::IntVarValue), -) -> TypeError<'tcx> { - let (a, b) = v; - TypeError::IntMismatch(ExpectedFound::new(a_is_expected, a, b)) -} - -fn float_unification_error<'tcx>( - a_is_expected: bool, - v: (ty::FloatVarValue, ty::FloatVarValue), -) -> TypeError<'tcx> { - let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v; - TypeError::FloatMismatch(ExpectedFound::new(a_is_expected, a, b)) + fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>); } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index d4c7d752c953..d6e57d853877 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -1,15 +1,16 @@ use std::mem; use super::StructurallyRelateAliases; +use super::{PredicateEmittingRelation, Relate, RelateResult, TypeRelation}; +use crate::infer::relate; use crate::infer::type_variable::TypeVariableValue; -use crate::infer::{InferCtxt, ObligationEmittingRelation, RegionVariableOrigin}; +use crate::infer::{InferCtxt, RegionVariableOrigin}; use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::infer::unify_key::ConstVariableValue; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::visit::MaxUniverse; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{AliasRelationDirection, InferConst, Term, TypeVisitable, TypeVisitableExt}; @@ -29,7 +30,7 @@ impl<'tcx> InferCtxt<'tcx> { /// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all /// other usecases (i.e. setting the value of a type var). #[instrument(level = "debug", skip(self, relation))] - pub fn instantiate_ty_var>( + pub fn instantiate_ty_var>( &self, relation: &mut R, target_is_expected: bool, @@ -82,16 +83,16 @@ impl<'tcx> InferCtxt<'tcx> { // mention `?0`. if self.next_trait_solver() { let (lhs, rhs, direction) = match instantiation_variance { - ty::Variance::Invariant => { + ty::Invariant => { (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Equate) } - ty::Variance::Covariant => { + ty::Covariant => { (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Subtype) } - ty::Variance::Contravariant => { + ty::Contravariant => { (source_ty.into(), generalized_ty.into(), AliasRelationDirection::Subtype) } - ty::Variance::Bivariant => unreachable!("bivariant generalization"), + ty::Bivariant => unreachable!("bivariant generalization"), }; relation.register_predicates([ty::PredicateKind::AliasRelate(lhs, rhs, direction)]); @@ -177,7 +178,7 @@ impl<'tcx> InferCtxt<'tcx> { /// /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant. #[instrument(level = "debug", skip(self, relation))] - pub(super) fn instantiate_const_var>( + pub(super) fn instantiate_const_var>( &self, relation: &mut R, target_is_expected: bool, @@ -191,7 +192,7 @@ impl<'tcx> InferCtxt<'tcx> { relation.span(), relation.structurally_relate_aliases(), target_vid, - ty::Variance::Invariant, + ty::Invariant, source_ct, )?; @@ -209,14 +210,14 @@ impl<'tcx> InferCtxt<'tcx> { // generalized const and the source. if target_is_expected { relation.relate_with_variance( - ty::Variance::Invariant, + ty::Invariant, ty::VarianceDiagInfo::default(), generalized_ct, source_ct, )?; } else { relation.relate_with_variance( - ty::Variance::Invariant, + ty::Invariant, ty::VarianceDiagInfo::default(), source_ct, generalized_ct, @@ -228,7 +229,7 @@ impl<'tcx> InferCtxt<'tcx> { /// Attempts to generalize `source_term` for the type variable `target_vid`. /// This checks for cycles -- that is, whether `source_term` references `target_vid`. - fn generalize> + Relate<'tcx>>( + fn generalize> + Relate>>( &self, span: Span, structurally_relate_aliases: StructurallyRelateAliases, @@ -329,6 +330,14 @@ impl<'tcx> Generalizer<'_, 'tcx> { } } + /// Create a new type variable in the universe of the target when + /// generalizing an alias. This has to set `has_unconstrained_ty_var` + /// if we're currently in a bivariant context. + fn next_ty_var_for_alias(&mut self) -> Ty<'tcx> { + self.has_unconstrained_ty_var |= self.ambient_variance == ty::Bivariant; + self.infcx.next_ty_var_in_universe(self.span, self.for_universe) + } + /// An occurs check failure inside of an alias does not mean /// that the types definitely don't unify. We may be able /// to normalize the alias after all. @@ -358,7 +367,7 @@ impl<'tcx> Generalizer<'_, 'tcx> { // // cc trait-system-refactor-initiative#110 if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() && !self.in_alias { - return Ok(self.infcx.next_ty_var_in_universe(self.span, self.for_universe)); + return Ok(self.next_ty_var_for_alias()); } let is_nested_alias = mem::replace(&mut self.in_alias, true); @@ -378,7 +387,7 @@ impl<'tcx> Generalizer<'_, 'tcx> { } debug!("generalization failure in alias"); - Ok(self.infcx.next_ty_var_in_universe(self.span, self.for_universe)) + Ok(self.next_ty_var_for_alias()) } } }; @@ -387,7 +396,7 @@ impl<'tcx> Generalizer<'_, 'tcx> { } } -impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { +impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } @@ -402,7 +411,7 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { a_arg: ty::GenericArgsRef<'tcx>, b_arg: ty::GenericArgsRef<'tcx>, ) -> RelateResult<'tcx, ty::GenericArgsRef<'tcx>> { - if self.ambient_variance == ty::Variance::Invariant { + if self.ambient_variance == ty::Invariant { // Avoid fetching the variance if we are in an invariant // context; no need, and it can induce dependency cycles // (e.g., #41849). @@ -422,10 +431,10 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { } #[instrument(level = "debug", skip(self, variance, b), ret)] - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, variance: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, + _info: ty::VarianceDiagInfo>, a: T, b: T, ) -> RelateResult<'tcx, T> { @@ -645,7 +654,7 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { { variable_table.union(vid, new_var_id); } - Ok(ty::Const::new_var(self.tcx(), new_var_id, c.ty())) + Ok(ty::Const::new_var(self.tcx(), new_var_id)) } } } @@ -658,16 +667,12 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { // structural. ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => { let args = self.relate_with_variance( - ty::Variance::Invariant, + ty::Invariant, ty::VarianceDiagInfo::default(), args, args, )?; - Ok(ty::Const::new_unevaluated( - self.tcx(), - ty::UnevaluatedConst { def, args }, - c.ty(), - )) + Ok(ty::Const::new_unevaluated(self.tcx(), ty::UnevaluatedConst { def, args })) } ty::ConstKind::Placeholder(placeholder) => { if self.for_universe.can_name(placeholder.universe) { @@ -691,7 +696,7 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { _: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: Relate<'tcx>, + T: Relate>, { let result = self.relate(a.skip_binder(), a.skip_binder())?; Ok(a.rebind(result)) diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs index a224a86492ab..067004ecaebb 100644 --- a/compiler/rustc_infer/src/infer/relate/glb.rs +++ b/compiler/rustc_infer/src/infer/relate/glb.rs @@ -1,14 +1,15 @@ //! Greatest lower bound. See [`lattice`]. +use rustc_middle::traits::solve::Goal; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::Span; -use super::combine::{CombineFields, ObligationEmittingRelation}; +use super::combine::{CombineFields, PredicateEmittingRelation}; use super::lattice::{self, LatticeDir}; use super::StructurallyRelateAliases; use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; -use crate::traits::{ObligationCause, PredicateObligations}; +use crate::traits::ObligationCause; /// "Greatest lower bound" (common subtype) pub struct Glb<'combine, 'infcx, 'tcx> { @@ -21,7 +22,7 @@ impl<'combine, 'infcx, 'tcx> Glb<'combine, 'infcx, 'tcx> { } } -impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> { +impl<'tcx> TypeRelation> for Glb<'_, '_, 'tcx> { fn tag(&self) -> &'static str { "Glb" } @@ -30,10 +31,10 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> { self.fields.tcx() } - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, variance: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, + _info: ty::VarianceDiagInfo>, a: T, b: T, ) -> RelateResult<'tcx, T> { @@ -81,7 +82,7 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> { b: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: Relate<'tcx>, + T: Relate>, { // GLB of a binder and itself is just itself if a == b { @@ -93,12 +94,7 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> { // When higher-ranked types are involved, computing the GLB is // very challenging, switch to invariance. This is obviously // overly conservative but works ok in practice. - self.relate_with_variance( - ty::Variance::Invariant, - ty::VarianceDiagInfo::default(), - a, - b, - )?; + self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?; Ok(a) } else { Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) @@ -127,7 +123,7 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, } } -impl<'tcx> ObligationEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> { +impl<'tcx> PredicateEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> { fn span(&self) -> Span { self.fields.trace.span() } @@ -147,11 +143,14 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> { self.fields.register_predicates(obligations); } - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + fn register_goals( + &mut self, + obligations: impl IntoIterator>>, + ) { self.fields.register_obligations(obligations); } - fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { + fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate( a.into(), b.into(), diff --git a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs index 70ed7cf9af1a..cfce28aca5d6 100644 --- a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs +++ b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs @@ -1,10 +1,10 @@ //! Helper routines for higher-ranked things. See the `doc` module at //! the end of the file for details. +use super::RelateResult; use crate::infer::snapshot::CombinedSnapshot; use crate::infer::InferCtxt; use rustc_middle::ty::fold::FnMutDelegate; -use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; impl<'tcx> InferCtxt<'tcx> { @@ -43,11 +43,10 @@ impl<'tcx> InferCtxt<'tcx> { ty::PlaceholderType { universe: next_universe, bound: bound_ty }, ) }, - consts: &mut |bound_var: ty::BoundVar, ty| { + consts: &mut |bound_var: ty::BoundVar| { ty::Const::new_placeholder( self.tcx, ty::PlaceholderConst { universe: next_universe, bound: bound_var }, - ty, ) }, }; diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index 38e25b0d9b68..6cc8d6d910ad 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -17,7 +17,7 @@ //! //! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) -use super::combine::ObligationEmittingRelation; +use super::combine::PredicateEmittingRelation; use crate::infer::{DefineOpaqueTypes, InferCtxt}; use crate::traits::ObligationCause; @@ -30,7 +30,7 @@ use rustc_middle::ty::{self, Ty}; /// /// GLB moves "down" the lattice (to smaller values); LUB moves /// "up" the lattice (to bigger values). -pub trait LatticeDir<'f, 'tcx>: ObligationEmittingRelation<'tcx> { +pub trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation<'tcx> { fn infcx(&self) -> &'f InferCtxt<'tcx>; fn cause(&self) -> &ObligationCause<'tcx>; @@ -64,8 +64,8 @@ where let infcx = this.infcx(); - let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); - let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); + let a = infcx.shallow_resolve(a); + let b = infcx.shallow_resolve(b); match (a.kind(), b.kind()) { // If one side is known to be a variable and one is not, @@ -108,9 +108,7 @@ where && def_id.is_local() && !this.infcx().next_trait_solver() => { - this.register_obligations( - infcx.handle_opaque_type(a, b, this.cause(), this.param_env())?.obligations, - ); + this.register_goals(infcx.handle_opaque_type(a, b, this.span(), this.param_env())?); Ok(a) } diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs index 83ab77707709..2184416b4cce 100644 --- a/compiler/rustc_infer/src/infer/relate/lub.rs +++ b/compiler/rustc_infer/src/infer/relate/lub.rs @@ -1,11 +1,12 @@ //! Least upper bound. See [`lattice`]. -use super::combine::{CombineFields, ObligationEmittingRelation}; +use super::combine::{CombineFields, PredicateEmittingRelation}; use super::lattice::{self, LatticeDir}; use super::StructurallyRelateAliases; use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin}; -use crate::traits::{ObligationCause, PredicateObligations}; +use crate::traits::ObligationCause; +use rustc_middle::traits::solve::Goal; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::Span; @@ -21,7 +22,7 @@ impl<'combine, 'infcx, 'tcx> Lub<'combine, 'infcx, 'tcx> { } } -impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { +impl<'tcx> TypeRelation> for Lub<'_, '_, 'tcx> { fn tag(&self) -> &'static str { "Lub" } @@ -30,10 +31,10 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { self.fields.tcx() } - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, variance: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, + _info: ty::VarianceDiagInfo>, a: T, b: T, ) -> RelateResult<'tcx, T> { @@ -81,7 +82,7 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { b: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: Relate<'tcx>, + T: Relate>, { // LUB of a binder and itself is just itself if a == b { @@ -93,12 +94,7 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { // When higher-ranked types are involved, computing the LUB is // very challenging, switch to invariance. This is obviously // overly conservative but works ok in practice. - self.relate_with_variance( - ty::Variance::Invariant, - ty::VarianceDiagInfo::default(), - a, - b, - )?; + self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?; Ok(a) } else { Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) @@ -127,7 +123,7 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, } } -impl<'tcx> ObligationEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> { +impl<'tcx> PredicateEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> { fn span(&self) -> Span { self.fields.trace.span() } @@ -147,11 +143,14 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> { self.fields.register_predicates(obligations); } - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + fn register_goals( + &mut self, + obligations: impl IntoIterator>>, + ) { self.fields.register_obligations(obligations) } - fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { + fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasRelate( a.into(), b.into(), diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs index 86a01130167a..41cc945492da 100644 --- a/compiler/rustc_infer/src/infer/relate/mod.rs +++ b/compiler/rustc_infer/src/infer/relate/mod.rs @@ -1,5 +1,11 @@ //! This module contains the definitions of most `TypeRelation`s in the type system //! (except for some relations used for diagnostics and heuristics in the compiler). +//! As well as the implementation of `Relate` for interned things (`Ty`/`Const`/etc). + +pub use rustc_middle::ty::relate::*; + +pub use self::combine::CombineFields; +pub use self::combine::PredicateEmittingRelation; pub(super) mod combine; mod generalize; @@ -8,15 +14,3 @@ mod higher_ranked; mod lattice; mod lub; mod type_relating; - -/// Whether aliases should be related structurally or not. Used -/// to adjust the behavior of generalization and combine. -/// -/// This should always be `No` unless in a few special-cases when -/// instantiating canonical responses and in the new solver. Each -/// such case should have a comment explaining why it is used. -#[derive(Debug, Copy, Clone)] -pub enum StructurallyRelateAliases { - Yes, - No, -} diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index 21064fff97f7..f2bec9392d54 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -1,10 +1,8 @@ use super::combine::CombineFields; +use crate::infer::relate::{PredicateEmittingRelation, StructurallyRelateAliases}; use crate::infer::BoundRegionConversionTime::HigherRankedType; -use crate::infer::{ - DefineOpaqueTypes, ObligationEmittingRelation, StructurallyRelateAliases, SubregionOrigin, -}; -use crate::traits::{Obligation, PredicateObligations}; - +use crate::infer::{DefineOpaqueTypes, SubregionOrigin}; +use rustc_middle::traits::solve::Goal; use rustc_middle::ty::relate::{ relate_args_invariantly, relate_args_with_variances, Relate, RelateResult, TypeRelation, }; @@ -29,7 +27,7 @@ impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> { } } -impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { +impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { fn tag(&self) -> &'static str { "TypeRelating" } @@ -44,7 +42,7 @@ impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { a_arg: ty::GenericArgsRef<'tcx>, b_arg: ty::GenericArgsRef<'tcx>, ) -> RelateResult<'tcx, ty::GenericArgsRef<'tcx>> { - if self.ambient_variance == ty::Variance::Invariant { + if self.ambient_variance == ty::Invariant { // Avoid fetching the variance if we are in an invariant // context; no need, and it can induce dependency cycles // (e.g., #41849). @@ -56,10 +54,10 @@ impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { } } - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, variance: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, + _info: ty::VarianceDiagInfo>, a: T, b: T, ) -> RelateResult<'tcx, T> { @@ -80,8 +78,8 @@ impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { } let infcx = self.fields.infcx; - let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); - let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); + let a = infcx.shallow_resolve(a); + let b = infcx.shallow_resolve(b); match (a.kind(), b.kind()) { (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => { @@ -89,9 +87,8 @@ impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { ty::Covariant => { // can't make progress on `A <: B` if both A and B are // type variables, so record an obligation. - self.fields.obligations.push(Obligation::new( + self.fields.goals.push(Goal::new( self.tcx(), - self.fields.trace.cause.clone(), self.fields.param_env, ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: true, @@ -103,9 +100,8 @@ impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { ty::Contravariant => { // can't make progress on `B <: A` if both A and B are // type variables, so record an obligation. - self.fields.obligations.push(Obligation::new( + self.fields.goals.push(Goal::new( self.tcx(), - self.fields.trace.cause.clone(), self.fields.param_env, ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: false, @@ -154,11 +150,12 @@ impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { && def_id.is_local() && !infcx.next_trait_solver() => { - self.fields.obligations.extend( - infcx - .handle_opaque_type(a, b, &self.fields.trace.cause, self.param_env())? - .obligations, - ); + self.fields.goals.extend(infcx.handle_opaque_type( + a, + b, + self.fields.trace.cause.span, + self.param_env(), + )?); } _ => { @@ -226,7 +223,7 @@ impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { b: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: Relate<'tcx>, + T: Relate>, { if a == b { // Do nothing @@ -299,7 +296,7 @@ impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { } } -impl<'tcx> ObligationEmittingRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { +impl<'tcx> PredicateEmittingRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { fn span(&self) -> Span { self.fields.trace.span() } @@ -319,29 +316,32 @@ impl<'tcx> ObligationEmittingRelation<'tcx> for TypeRelating<'_, '_, 'tcx> { self.fields.register_predicates(obligations); } - fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + fn register_goals( + &mut self, + obligations: impl IntoIterator>>, + ) { self.fields.register_obligations(obligations); } - fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { + fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { self.register_predicates([ty::Binder::dummy(match self.ambient_variance { - ty::Variance::Covariant => ty::PredicateKind::AliasRelate( + ty::Covariant => ty::PredicateKind::AliasRelate( a.into(), b.into(), ty::AliasRelationDirection::Subtype, ), // a :> b is b <: a - ty::Variance::Contravariant => ty::PredicateKind::AliasRelate( + ty::Contravariant => ty::PredicateKind::AliasRelate( b.into(), a.into(), ty::AliasRelationDirection::Subtype, ), - ty::Variance::Invariant => ty::PredicateKind::AliasRelate( + ty::Invariant => ty::PredicateKind::AliasRelate( a.into(), b.into(), ty::AliasRelationDirection::Equate, ), - ty::Variance::Bivariant => { + ty::Bivariant => { unreachable!("Expected bivariance to be handled in relate_with_variance") } })]); diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 61b13dd9a54f..830d79f52b94 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -167,90 +167,12 @@ impl<'a, 'tcx> FallibleTypeFolder> for FullTypeResolver<'a, 'tcx> { ty::ConstKind::Infer(InferConst::Fresh(_)) => { bug!("Unexpected const in full const resolver: {:?}", c); } + ty::ConstKind::Infer(InferConst::EffectVar(evid)) => { + return Err(FixupError::UnresolvedEffect(evid)); + } _ => {} } c.try_super_fold_with(self) } } } - -/////////////////////////////////////////////////////////////////////////// -// EAGER RESOLUTION - -/// Resolves ty, region, and const vars to their inferred values or their root vars. -pub struct EagerResolver<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, -} - -impl<'a, 'tcx> EagerResolver<'a, 'tcx> { - pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { - EagerResolver { infcx } - } -} - -impl<'tcx> TypeFolder> for EagerResolver<'_, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match *t.kind() { - ty::Infer(ty::TyVar(vid)) => match self.infcx.probe_ty_var(vid) { - Ok(t) => t.fold_with(self), - Err(_) => Ty::new_var(self.infcx.tcx, self.infcx.root_var(vid)), - }, - ty::Infer(ty::IntVar(vid)) => self.infcx.opportunistic_resolve_int_var(vid), - ty::Infer(ty::FloatVar(vid)) => self.infcx.opportunistic_resolve_float_var(vid), - _ => { - if t.has_infer() { - t.super_fold_with(self) - } else { - t - } - } - } - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { - ty::ReVar(vid) => self - .infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .opportunistic_resolve_var(self.infcx.tcx, vid), - _ => r, - } - } - - fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { - match c.kind() { - ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { - // FIXME: we need to fold the ty too, I think. - match self.infcx.probe_const_var(vid) { - Ok(c) => c.fold_with(self), - Err(_) => { - ty::Const::new_var(self.infcx.tcx, self.infcx.root_const_var(vid), c.ty()) - } - } - } - ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)) => { - debug_assert_eq!(c.ty(), self.infcx.tcx.types.bool); - self.infcx.probe_effect_var(vid).unwrap_or_else(|| { - ty::Const::new_infer( - self.infcx.tcx, - ty::InferConst::EffectVar(self.infcx.root_effect_var(vid)), - self.infcx.tcx.types.bool, - ) - }) - } - _ => { - if c.has_infer() { - c.super_fold_with(self) - } else { - c - } - } - } - } -} diff --git a/compiler/rustc_infer/src/infer/snapshot/fudge.rs b/compiler/rustc_infer/src/infer/snapshot/fudge.rs index 4408251c99dc..a086c82c92e8 100644 --- a/compiler/rustc_infer/src/infer/snapshot/fudge.rs +++ b/compiler/rustc_infer/src/infer/snapshot/fudge.rs @@ -244,7 +244,7 @@ impl<'a, 'tcx> TypeFolder> for InferenceFudger<'a, 'tcx> { // Recreate it with a fresh variable here. let idx = vid.index() - self.const_vars.0.start.index(); let origin = self.const_vars.1[idx]; - self.infcx.next_const_var_with_origin(ct.ty(), origin) + self.infcx.next_const_var_with_origin(origin) } else { ct } diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 28d908abf83e..b65ac8596675 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -12,22 +12,24 @@ //! //! This API is completely unstable and subject to change. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![doc(rust_logo)] -#![feature(rustdoc_internals)] +// tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![doc(rust_logo)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(extend_one)] -#![feature(let_chains)] #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iterator_try_collect)] +#![feature(let_chains)] +#![feature(rustdoc_internals)] #![feature(try_blocks)] #![feature(yeet_expr)] #![recursion_limit = "512"] // For rustdoc +// tidy-alphabetical-end #[macro_use] extern crate tracing; diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index e27e6a0a4a15..026b2c1b905e 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -1,13 +1,38 @@ +use std::fmt::Debug; + use crate::infer::InferCtxt; use crate::traits::Obligation; use rustc_hir::def_id::DefId; -use rustc_macros::extension; use rustc_middle::ty::{self, Ty, Upcast}; -use super::FulfillmentError; use super::{ObligationCause, PredicateObligation}; -pub trait TraitEngine<'tcx>: 'tcx { +/// A trait error with most of its information removed. This is the error +/// returned by an `ObligationCtxt` by default, and suitable if you just +/// want to see if a predicate holds, and don't particularly care about the +/// error itself (except for if it's an ambiguity or true error). +/// +/// use `ObligationCtxt::new_with_diagnostics` to get a `FulfillmentError`. +#[derive(Clone, Debug)] +pub enum ScrubbedTraitError<'tcx> { + /// A real error. This goal definitely does not hold. + TrueError, + /// An ambiguity. This goal may hold if further inference is done. + Ambiguity, + /// An old-solver-style cycle error, which will fatal. + Cycle(Vec>), +} + +impl<'tcx> ScrubbedTraitError<'tcx> { + pub fn is_true_error(&self) -> bool { + match self { + ScrubbedTraitError::TrueError => true, + ScrubbedTraitError::Ambiguity | ScrubbedTraitError::Cycle(_) => false, + } + } +} + +pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { /// Requires that `ty` must implement the trait with `def_id` in /// the given environment. This trait must not have any type /// parameters (except for `Self`). @@ -37,10 +62,30 @@ pub trait TraitEngine<'tcx>: 'tcx { obligation: PredicateObligation<'tcx>, ); - #[must_use] - fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec>; + fn register_predicate_obligations( + &mut self, + infcx: &InferCtxt<'tcx>, + obligations: Vec>, + ) { + for obligation in obligations { + self.register_predicate_obligation(infcx, obligation); + } + } - fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec>; + #[must_use] + fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec; + + fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec; + + #[must_use] + fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { + let errors = self.select_where_possible(infcx); + if !errors.is_empty() { + return errors; + } + + self.collect_remaining_errors(infcx) + } fn pending_obligations(&self) -> Vec>; @@ -53,25 +98,6 @@ pub trait TraitEngine<'tcx>: 'tcx { ) -> Vec>; } -#[extension(pub trait TraitEngineExt<'tcx>)] -impl<'tcx, T: ?Sized + TraitEngine<'tcx>> T { - fn register_predicate_obligations( - &mut self, - infcx: &InferCtxt<'tcx>, - obligations: impl IntoIterator>, - ) { - for obligation in obligations { - self.register_predicate_obligation(infcx, obligation); - } - } - - #[must_use] - fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec> { - let errors = self.select_where_possible(infcx); - if !errors.is_empty() { - return errors; - } - - self.collect_remaining_errors(infcx) - } +pub trait FromSolverError<'tcx, E>: Debug + 'tcx { + fn from_solver_error(infcx: &InferCtxt<'tcx>, error: E) -> Self; } diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 0ae4340098bc..556b3bd063da 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -15,15 +15,14 @@ use hir::def_id::LocalDefId; use rustc_hir as hir; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::Certainty; -use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::{self, Const, Ty, TyCtxt, Upcast}; +use rustc_middle::ty::{self, Ty, TyCtxt, Upcast}; use rustc_span::Span; pub use self::ImplSource::*; pub use self::SelectionError::*; use crate::infer::InferCtxt; -pub use self::engine::{TraitEngine, TraitEngineExt}; +pub use self::engine::{FromSolverError, ScrubbedTraitError, TraitEngine}; pub use self::project::MismatchedProjectionTypes; pub(crate) use self::project::UndoLog; pub use self::project::{ @@ -115,8 +114,6 @@ impl<'tcx> PolyTraitObligation<'tcx> { #[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(PredicateObligation<'_>, 48); -pub type PredicateObligations<'tcx> = Vec>; - pub type Selection<'tcx> = ImplSource<'tcx, PredicateObligation<'tcx>>; /// A callback that can be provided to `inspect_typeck`. Invoked on evaluation @@ -124,32 +121,6 @@ pub type Selection<'tcx> = ImplSource<'tcx, PredicateObligation<'tcx>>; pub type ObligationInspector<'tcx> = fn(&InferCtxt<'tcx>, &PredicateObligation<'tcx>, Result); -pub struct FulfillmentError<'tcx> { - pub obligation: PredicateObligation<'tcx>, - pub code: FulfillmentErrorCode<'tcx>, - /// Diagnostics only: the 'root' obligation which resulted in - /// the failure to process `obligation`. This is the obligation - /// that was initially passed to `register_predicate_obligation` - pub root_obligation: PredicateObligation<'tcx>, -} - -#[derive(Clone)] -pub enum FulfillmentErrorCode<'tcx> { - /// Inherently impossible to fulfill; this trait is implemented if and only - /// if it is already implemented. - Cycle(Vec>), - Select(SelectionError<'tcx>), - Project(MismatchedProjectionTypes<'tcx>), - Subtype(ExpectedFound>, TypeError<'tcx>), // always comes from a SubtypePredicate - ConstEquate(ExpectedFound>, TypeError<'tcx>), - Ambiguity { - /// Overflow is only `Some(suggest_recursion_limit)` when using the next generation - /// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by - /// emitting a fatal error instead. - overflow: Option, - }, -} - impl<'tcx, O> Obligation<'tcx, O> { pub fn new( tcx: TyCtxt<'tcx>, @@ -198,28 +169,6 @@ impl<'tcx, O> Obligation<'tcx, O> { } } -impl<'tcx> FulfillmentError<'tcx> { - pub fn new( - obligation: PredicateObligation<'tcx>, - code: FulfillmentErrorCode<'tcx>, - root_obligation: PredicateObligation<'tcx>, - ) -> FulfillmentError<'tcx> { - FulfillmentError { obligation, code, root_obligation } - } - - pub fn is_true_error(&self) -> bool { - match self.code { - FulfillmentErrorCode::Select(_) - | FulfillmentErrorCode::Project(_) - | FulfillmentErrorCode::Subtype(_, _) - | FulfillmentErrorCode::ConstEquate(_, _) => true, - FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => { - false - } - } - } -} - impl<'tcx> PolyTraitObligation<'tcx> { pub fn polarity(&self) -> ty::PredicatePolarity { self.predicate.skip_binder().polarity diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs index b616d37e5b5e..b26734a296fd 100644 --- a/compiler/rustc_infer/src/traits/structural_impls.rs +++ b/compiler/rustc_infer/src/traits/structural_impls.rs @@ -29,33 +29,6 @@ impl<'tcx, O: fmt::Debug> fmt::Debug for traits::Obligation<'tcx, O> { } } -impl<'tcx> fmt::Debug for traits::FulfillmentError<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "FulfillmentError({:?},{:?})", self.obligation, self.code) - } -} - -impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use traits::FulfillmentErrorCode::*; - match *self { - Select(ref e) => write!(f, "{e:?}"), - Project(ref e) => write!(f, "{e:?}"), - Subtype(ref a, ref b) => { - write!(f, "CodeSubtypeError({a:?}, {b:?})") - } - ConstEquate(ref a, ref b) => { - write!(f, "CodeConstEquateError({a:?}, {b:?})") - } - Ambiguity { overflow: None } => write!(f, "Ambiguity"), - Ambiguity { overflow: Some(suggest_increasing_limit) } => { - write!(f, "Overflow({suggest_increasing_limit})") - } - Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"), - } - } -} - impl<'tcx> fmt::Debug for traits::MismatchedProjectionTypes<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "MismatchedProjectionTypes({:?})", self.err) diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index cc12a4bf0914..ab4148faaab6 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -285,8 +285,7 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> { let obligations = predicates.predicates.iter().enumerate().map(|(index, &(clause, span))| { elaboratable.child_with_derived_cause( - clause - .instantiate_supertrait(tcx, &bound_clause.rebind(data.trait_ref)), + clause.instantiate_supertrait(tcx, bound_clause.rebind(data.trait_ref)), span, bound_clause.rebind(data), index, diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 55304bbbd922..41c8b941717f 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -9,13 +9,12 @@ use rustc_data_structures::jobserver; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::Lrc; use rustc_errors::registry::Registry; -use rustc_errors::{DiagCtxt, ErrorGuaranteed}; +use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed}; use rustc_lint::LintStore; - use rustc_middle::ty; use rustc_middle::ty::CurrentGcx; use rustc_middle::util::Providers; -use rustc_parse::maybe_new_parser_from_source_str; +use rustc_parse::new_parser_from_source_str; use rustc_query_impl::QueryCtxt; use rustc_query_system::query::print_query_stack; use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName}; @@ -28,6 +27,7 @@ use rustc_span::FileName; use std::path::PathBuf; use std::result; use std::sync::Arc; +use tracing::trace; pub type Result = result::Result; @@ -46,7 +46,7 @@ pub struct Compiler { } /// Converts strings provided as `--cfg [cfgspec]` into a `Cfg`. -pub(crate) fn parse_cfg(dcx: &DiagCtxt, cfgs: Vec) -> Cfg { +pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec) -> Cfg { cfgs.into_iter() .map(|s| { let psess = ParseSess::with_silent_emitter( @@ -67,7 +67,7 @@ pub(crate) fn parse_cfg(dcx: &DiagCtxt, cfgs: Vec) -> Cfg { }; } - match maybe_new_parser_from_source_str(&psess, filename, s.to_string()) { + match new_parser_from_source_str(&psess, filename, s.to_string()) { Ok(mut parser) => match parser.parse_meta_item() { Ok(meta_item) if parser.token == token::Eof => { if meta_item.path.segments.len() != 1 { @@ -105,7 +105,7 @@ pub(crate) fn parse_cfg(dcx: &DiagCtxt, cfgs: Vec) -> Cfg { } /// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`. -pub(crate) fn parse_check_cfg(dcx: &DiagCtxt, specs: Vec) -> CheckCfg { +pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> CheckCfg { // If any --check-cfg is passed then exhaustive_values and exhaustive_names // are enabled by default. let exhaustive_names = !specs.is_empty(); @@ -166,7 +166,7 @@ pub(crate) fn parse_check_cfg(dcx: &DiagCtxt, specs: Vec) -> CheckCfg { error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`") }; - let mut parser = match maybe_new_parser_from_source_str(&psess, filename, s.to_string()) { + let mut parser = match new_parser_from_source_str(&psess, filename, s.to_string()) { Ok(parser) => parser, Err(errs) => { errs.into_iter().for_each(|err| err.cancel()); @@ -389,6 +389,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se let hash_kind = config.opts.unstable_opts.src_hash_algorithm(&target); util::run_in_thread_pool_with_globals( + &early_dcx, config.opts.edition, config.opts.unstable_opts.threads, SourceMapInputs { file_loader, path_mapping, hash_kind }, @@ -450,12 +451,12 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se codegen_backend.init(&sess); - let cfg = parse_cfg(&sess.dcx(), config.crate_cfg); + let cfg = parse_cfg(sess.dcx(), config.crate_cfg); let mut cfg = config::build_configuration(&sess, cfg); util::add_configuration(&mut cfg, &mut sess, &*codegen_backend); sess.psess.config = cfg; - let mut check_cfg = parse_check_cfg(&sess.dcx(), config.crate_check_cfg); + let mut check_cfg = parse_check_cfg(sess.dcx(), config.crate_check_cfg); check_cfg.fill_well_known(&sess.target); sess.psess.check_config = check_cfg; @@ -528,7 +529,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se } pub fn try_print_query_stack( - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, num_frames: Option, file: Option, ) { diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index 75df006a56f1..0c3d4e19ef82 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,11 +1,9 @@ +// tidy-alphabetical-start #![feature(decl_macro)] -#![feature(lazy_cell)] #![feature(let_chains)] #![feature(thread_spawn_unchecked)] #![feature(try_blocks)] - -#[macro_use] -extern crate tracing; +// tidy-alphabetical-end mod callbacks; mod errors; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 76d5d7a3ac2f..f881d53858a2 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -19,7 +19,9 @@ use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepGraph; use rustc_middle::ty::{self, GlobalCtxt, RegisteredTools, TyCtxt}; use rustc_middle::util::Providers; -use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str, validate_attr}; +use rustc_parse::{ + new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal, validate_attr, +}; use rustc_passes::{abi_test, hir_stats, layout_test}; use rustc_resolve::Resolver; use rustc_session::code_stats::VTableSizeInfo; @@ -39,13 +41,17 @@ use std::io::{self, BufWriter, Write}; use std::path::{Path, PathBuf}; use std::sync::LazyLock; use std::{env, fs, iter}; +use tracing::{info, instrument}; pub fn parse<'a>(sess: &'a Session) -> PResult<'a, ast::Crate> { - let krate = sess.time("parse_crate", || match &sess.io.input { - Input::File(file) => parse_crate_from_file(file, &sess.psess), - Input::Str { input, name } => { - parse_crate_from_source_str(name.clone(), input.clone(), &sess.psess) - } + let krate = sess.time("parse_crate", || { + let mut parser = unwrap_or_emit_fatal(match &sess.io.input { + Input::File(file) => new_parser_from_file(&sess.psess, file, None), + Input::Str { input, name } => { + new_parser_from_source_str(&sess.psess, name.clone(), input.clone()) + } + }); + parser.parse_crate_mod() })?; if sess.opts.unstable_opts.input_stats { @@ -750,7 +756,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { || tcx.hir().body_const_context(def_id).is_some() { tcx.ensure().mir_drops_elaborated_and_const_checked(def_id); - tcx.ensure().unused_generic_params(ty::InstanceDef::Item(def_id.to_def_id())); + tcx.ensure().unused_generic_params(ty::InstanceKind::Item(def_id.to_def_id())); } } }); @@ -831,7 +837,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { let traits = tcx.traits(LOCAL_CRATE); for &tr in traits { - if !tcx.check_is_object_safe(tr) { + if !tcx.is_object_safe(tr) { continue; } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 36f9dda7250c..2909f8adfb0e 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -70,7 +70,7 @@ where Arc::default(), Default::default(), ); - let cfg = parse_cfg(&sess.dcx(), matches.opt_strs("cfg")); + let cfg = parse_cfg(sess.dcx(), matches.opt_strs("cfg")); let cfg = build_configuration(&sess, cfg); f(sess, cfg) }); @@ -761,7 +761,7 @@ fn test_unstable_options_tracking_hash() { }) ); tracked!(codegen_backend, Some("abc".to_string())); - tracked!(coverage_options, CoverageOptions { level: CoverageLevel::Mcdc }); + tracked!(coverage_options, CoverageOptions { level: CoverageLevel::Mcdc, no_mir_spans: true }); tracked!(crate_attr, vec!["abc".to_string()]); tracked!(cross_crate_inline_threshold, InliningThreshold::Always); tracked!(debug_info_for_profiling, true); @@ -773,6 +773,7 @@ fn test_unstable_options_tracking_hash() { tracked!(emit_thin_lto, false); tracked!(export_executable_symbols, true); tracked!(fewer_names, Some(true)); + tracked!(fixed_x18, true); tracked!(flatten_format_args, false); tracked!(force_unstable_if_unmarked, true); tracked!(fuel, Some(("abc".to_string(), 99))); @@ -799,10 +800,7 @@ fn test_unstable_options_tracking_hash() { tracked!(mir_opt_level, Some(4)); tracked!(move_size_limit, Some(4096)); tracked!(mutable_noalias, false); - tracked!( - next_solver, - Some(NextSolverConfig { coherence: true, globally: false, dump_tree: Default::default() }) - ); + tracked!(next_solver, Some(NextSolverConfig { coherence: true, globally: false })); tracked!(no_generate_arange_section, true); tracked!(no_jump_tables, true); tracked!(no_link, true); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index ce4d38250152..8dac524bb5bf 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -1,5 +1,4 @@ use crate::errors; -use info; use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; #[cfg(parallel_compiler)] @@ -23,6 +22,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::OnceLock; use std::thread; use std::{env, iter}; +use tracing::info; /// Function pointer type that constructs a new CodegenBackend. pub type MakeBackendFn = fn() -> Box; @@ -51,20 +51,38 @@ pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dy pub static STACK_SIZE: OnceLock = OnceLock::new(); pub const DEFAULT_STACK_SIZE: usize = 8 * 1024 * 1024; -fn init_stack_size() -> usize { +fn init_stack_size(early_dcx: &EarlyDiagCtxt) -> usize { // Obey the environment setting or default *STACK_SIZE.get_or_init(|| { env::var_os("RUST_MIN_STACK") - .map(|os_str| os_str.to_string_lossy().into_owned()) - // ignore if it is set to nothing - .filter(|s| s.trim() != "") - .map(|s| s.trim().parse::().unwrap()) + .as_ref() + .map(|os_str| os_str.to_string_lossy()) + // if someone finds out `export RUST_MIN_STACK=640000` isn't enough stack + // they might try to "unset" it by running `RUST_MIN_STACK= rustc code.rs` + // this is wrong, but std would nonetheless "do what they mean", so let's do likewise + .filter(|s| !s.trim().is_empty()) + // rustc is a batch program, so error early on inputs which are unlikely to be intended + // so no one thinks we parsed them setting `RUST_MIN_STACK="64 megabytes"` + // FIXME: we could accept `RUST_MIN_STACK=64MB`, perhaps? + .map(|s| { + let s = s.trim(); + // FIXME(workingjubilee): add proper diagnostics when we factor out "pre-run" setup + #[allow(rustc::untranslatable_diagnostic, rustc::diagnostic_outside_of_impl)] + s.parse::().unwrap_or_else(|_| { + let mut err = early_dcx.early_struct_fatal(format!( + r#"`RUST_MIN_STACK` should be a number of bytes, but was "{s}""#, + )); + err.note("you can also unset `RUST_MIN_STACK` to use the default stack size"); + err.emit() + }) + }) // otherwise pick a consistent default .unwrap_or(DEFAULT_STACK_SIZE) }) } fn run_in_thread_with_globals R + Send, R: Send>( + thread_stack_size: usize, edition: Edition, sm_inputs: SourceMapInputs, f: F, @@ -75,7 +93,7 @@ fn run_in_thread_with_globals R + Send, R: Send>( // the parallel compiler, in particular to ensure there is no accidental // sharing of data between the main thread and the compilation thread // (which might cause problems for the parallel compiler). - let builder = thread::Builder::new().name("rustc".to_string()).stack_size(init_stack_size()); + let builder = thread::Builder::new().name("rustc".to_string()).stack_size(thread_stack_size); // We build the session globals and run `f` on the spawned thread, because // `SessionGlobals` does not impl `Send` in the non-parallel compiler. @@ -100,16 +118,19 @@ fn run_in_thread_with_globals R + Send, R: Send>( #[cfg(not(parallel_compiler))] pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( + thread_builder_diag: &EarlyDiagCtxt, edition: Edition, _threads: usize, sm_inputs: SourceMapInputs, f: F, ) -> R { - run_in_thread_with_globals(edition, sm_inputs, f) + let thread_stack_size = init_stack_size(thread_builder_diag); + run_in_thread_with_globals(thread_stack_size, edition, sm_inputs, f) } #[cfg(parallel_compiler)] pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( + thread_builder_diag: &EarlyDiagCtxt, edition: Edition, threads: usize, sm_inputs: SourceMapInputs, @@ -121,10 +142,12 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, use rustc_query_system::query::{break_query_cycles, QueryContext}; use std::process; + let thread_stack_size = init_stack_size(thread_builder_diag); + let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap()); if !sync::is_dyn_thread_safe() { - return run_in_thread_with_globals(edition, sm_inputs, |current_gcx| { + return run_in_thread_with_globals(thread_stack_size, edition, sm_inputs, |current_gcx| { // Register the thread for use with the `WorkerLocal` type. registry.register(); @@ -167,7 +190,7 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, }) .unwrap(); }) - .stack_size(init_stack_size()); + .stack_size(thread_stack_size); // We create the session globals on the main thread, then create the thread // pool. Upon creation, each worker thread created gets a copy of the @@ -376,31 +399,17 @@ pub(crate) fn check_attr_crate_type( if let ast::MetaItemKind::NameValue(spanned) = a.meta_kind().unwrap() { let span = spanned.span; - let lev_candidate = find_best_match_for_name( + let candidate = find_best_match_for_name( &CRATE_TYPES.iter().map(|(k, _)| *k).collect::>(), n, None, ); - if let Some(candidate) = lev_candidate { - lint_buffer.buffer_lint_with_diagnostic( - lint::builtin::UNKNOWN_CRATE_TYPES, - ast::CRATE_NODE_ID, - span, - "invalid `crate_type` value", - BuiltinLintDiag::UnknownCrateTypes( - span, - "did you mean".to_string(), - format!("\"{candidate}\""), - ), - ); - } else { - lint_buffer.buffer_lint( - lint::builtin::UNKNOWN_CRATE_TYPES, - ast::CRATE_NODE_ID, - span, - "invalid `crate_type` value", - ); - } + lint_buffer.buffer_lint( + lint::builtin::UNKNOWN_CRATE_TYPES, + ast::CRATE_NODE_ID, + span, + BuiltinLintDiag::UnknownCrateTypes { span, candidate }, + ); } } else { // This is here mainly to check for using a macro, such as diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 6f8a9792b6ce..d4efb41eed08 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -19,9 +19,11 @@ //! //! [`rustc_parse::lexer`]: ../rustc_parse/lexer/index.html +// tidy-alphabetical-start // We want to be able to build this crate with a stable compiler, // so no `#![feature]` attributes should be added. #![deny(unstable_features)] +// tidy-alphabetical-end mod cursor; pub mod unescape; diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index fa1133e7780f..232d4c18fa48 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -13,6 +13,7 @@ rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } +rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } rustc_infer = { path = "../rustc_infer" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 5180fce2eb37..007709e32d87 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -1,13 +1,20 @@ +lint_abs_path_with_module = absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition + .suggestion = use `crate` + +lint_ambiguous_glob_reexport = ambiguous glob re-exports + .label_first_reexport = the name `{$name}` in the {$namespace} namespace is first re-exported here + .label_duplicate_reexport = but the name `{$name}` in the {$namespace} namespace is also re-exported here + lint_ambiguous_wide_pointer_comparisons = ambiguous wide pointer comparison, the comparison includes metadata which may not be expected .addr_metadata_suggestion = use explicit `std::ptr::eq` method to compare metadata and addresses .addr_suggestion = use `std::ptr::addr_eq` or untyped pointers to only compare their addresses -lint_array_into_iter = - this method call resolves to `<&{$target} as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <{$target} as IntoIterator>::into_iter in Rust 2021 - .use_iter_suggestion = use `.iter()` instead of `.into_iter()` to avoid ambiguity - .remove_into_iter_suggestion = or remove `.into_iter()` to iterate by value - .use_explicit_into_iter_suggestion = - or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value +lint_associated_const_elided_lifetime = {$elided -> + [true] `&` without an explicit lifetime name cannot be used here + *[false] `'_` cannot be used here + } + .suggestion = use the `'static` lifetime + .note = cannot automatically infer `'static` because of other lifetimes in scope lint_async_fn_in_trait = use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified .note = you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future` @@ -26,10 +33,19 @@ lint_atomic_ordering_load = atomic loads cannot have `Release` or `AcqRel` order lint_atomic_ordering_store = atomic stores cannot have `Acquire` or `AcqRel` ordering .help = consider using ordering modes `Release`, `SeqCst` or `Relaxed` +lint_avoid_att_syntax = + avoid using `.att_syntax`, prefer using `options(att_syntax)` instead + +lint_avoid_intel_syntax = + avoid using `.intel_syntax`, Intel syntax is the default + lint_bad_attribute_argument = bad attribute argument lint_bad_opt_access = {$msg} +lint_break_with_label_and_loop = this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression + .suggestion = wrap this expression in parentheses + lint_builtin_allow_internal_unsafe = `allow_internal_unsafe` allows defining macros using unsafe without triggering the `unsafe_code` lint at their call site @@ -37,6 +53,8 @@ lint_builtin_anonymous_params = anonymous parameters are deprecated and will be .suggestion = try naming the parameter or explicitly ignoring it lint_builtin_asm_labels = avoid using named labels in inline assembly + .help = only local labels of the form `:` should be used in inline asm + .note = see the asm section of Rust By Example for more information lint_builtin_box_pointers = type uses owned (Box type) pointers: {$ty} @@ -161,6 +179,12 @@ lint_builtin_unused_doc_comment = unused doc comment lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}` .suggestion = use `loop` +lint_byte_slice_in_packed_struct_with_derive = {$ty} slice in a packed struct that derives a built-in trait + .help = consider implementing the trait by hand, or remove the `packed` attribute + +lint_cfg_attr_no_attributes = + `#[cfg_attr]` does not expand to any attributes + lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}` lint_command_line_source = `forbid` lint level was set on command line @@ -169,12 +193,20 @@ lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as i .current_use = this identifier can be confused with `{$existing_sym}` .other_use = other identifier used here +lint_crate_name_in_cfg_attr_deprecated = + `crate_name` within an `#![cfg_attr]` attribute is deprecated + +lint_crate_type_in_cfg_attr_deprecated = + `crate_type` within an `#![cfg_attr]` attribute is deprecated + lint_cstring_ptr = getting the inner pointer of a temporary `CString` .as_ptr_label = this pointer will be invalid .unwrap_label = this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime .note = pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned .help = for more information, see https://doc.rust-lang.org/reference/destructors.html +lint_custom_inner_attribute_unstable = custom inner attributes are unstable + lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance .note = a `use rustc_data_structures::fx::{$preferred}` may be necessary @@ -185,6 +217,11 @@ lint_deprecated_lint_name = .suggestion = change it to .help = change it to {$replace} +lint_deprecated_where_clause_location = where clause not allowed here + .note = see issue #89122 for more information + .suggestion_move_to_end = move it to the end of the type declaration + .suggestion_remove_where = remove this `where` + lint_diag_out_of_impl = diagnostics should only be created in `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls @@ -196,11 +233,14 @@ lint_drop_trait_constraints = lint_dropping_copy_types = calls to `std::mem::drop` with a value that implements `Copy` does nothing .label = argument has type `{$arg_ty}` - .note = use `let _ = ...` to ignore the expression or result lint_dropping_references = calls to `std::mem::drop` with a reference instead of an owned value does nothing .label = argument has type `{$arg_ty}` - .note = use `let _ = ...` to ignore the expression or result + +lint_duplicate_macro_attribute = + duplicated attribute + +lint_duplicate_matcher_binding = duplicate matcher binding lint_enum_intrinsics_mem_discriminant = the return value of `mem::discriminant` is unspecified when called with a non-enum type @@ -214,8 +254,15 @@ lint_expectation = this lint expectation is unfulfilled .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message .rationale = {$rationale} +lint_extern_crate_not_idiomatic = `extern crate` is not idiomatic in the new edition + .suggestion = convert it to a `use` + +lint_extern_without_abi = extern declarations without an explicit ABI are deprecated + .label = ABI should be specified here + .help = the default ABI is {$default_abi} + lint_for_loops_over_fallibles = - for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement + for loop over {$article} `{$ref_prefix}{$ty}`. This is more readably written as an `if let` statement .suggestion = consider using `if let` to clear intent .remove_next = to iterate over `{$recv_snip}` remove the call to `next` .use_while_let = to check pattern in a loop use `while let` @@ -223,10 +270,15 @@ lint_for_loops_over_fallibles = lint_forgetting_copy_types = calls to `std::mem::forget` with a value that implements `Copy` does nothing .label = argument has type `{$arg_ty}` - .note = use `let _ = ...` to ignore the expression or result + lint_forgetting_references = calls to `std::mem::forget` with a reference instead of an owned value does nothing .label = argument has type `{$arg_ty}` - .note = use `let _ = ...` to ignore the expression or result + +lint_hidden_glob_reexport = private item shadows public glob re-export + .note_glob_reexport = the name `{$name}` in the {$namespace} namespace is supposed to be publicly re-exported here + .note_private_item = but the private item here shadows it + +lint_hidden_lifetime_parameters = hidden lifetime parameters in types are deprecated lint_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label} .label = this {$label} contains {$count -> @@ -269,6 +321,11 @@ lint_identifier_uncommon_codepoints = identifier contains {$codepoints_len -> lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level +lint_ill_formed_attribute_input = {$num_suggestions -> + [1] attribute must be of the form {$suggestions} + *[other] valid forms for the attribute are {$suggestions} + } + lint_impl_trait_overcaptures = `{$self_ty}` will capture more lifetimes than possibly intended in edition 2024 .note = specifically, {$num_captured -> [one] this lifetime is @@ -339,6 +396,14 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re lint_improper_ctypes_union_layout_reason = this union has unspecified layout lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive +lint_incomplete_include = + include macro expected single expression in source + +lint_inner_macro_attribute_unstable = inner macro attributes are unstable + +lint_invalid_crate_type_value = invalid `crate_type` value + .suggestion = did you mean + # FIXME: we should ordinalize $valid_up_to when we add support for doing so lint_invalid_from_utf8_checked = calls to `{$method}` with a invalid literal always return an error .label = the literal was valid UTF-8 up to the {$valid_up_to} bytes @@ -367,9 +432,23 @@ lint_invalid_reference_casting_note_book = for more information, visit `, which means the only information carried by the iterator is the number of items .suggestion = you might have meant to use `Iterator::for_each` +lint_metavariable_still_repeating = variable `{$name}` is still repeating at this depth + +lint_metavariable_wrong_operator = meta-variable repeats with different Kleene operator + +lint_missing_fragment_specifier = missing fragment specifier + +lint_missing_unsafe_on_extern = extern blocks should be unsafe + .suggestion = needs `unsafe` before the extern keyword + lint_mixed_script_confusables = the usage of Script Group `{$set}` in this crate consists solely of mixed script confusables .includes_note = the usage includes {$includes} @@ -386,6 +474,11 @@ lint_mixed_script_confusables = lint_multiple_supertrait_upcastable = `{$ident}` is object-safe and has multiple supertraits +lint_named_argument_used_positionally = named argument `{$named_arg_name}` is not used by name + .label_named_arg = this named argument is referred to by position in formatting string + .label_position_arg = this formatting argument uses named argument `{$named_arg_name}` by position + .suggestion = use the named argument by name to avoid ambiguity + lint_node_source = `forbid` level set here .note = {$reason} @@ -451,17 +544,23 @@ lint_non_local_definitions_cargo_update = the {$macro_kind} `{$macro_name}` may lint_non_local_definitions_deprecation = this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -lint_non_local_definitions_impl = non-local `impl` definition, they should be avoided as they go against expectation - .help = - move this `impl` block outside the of the current {$body_kind_descr} {$depth -> - [one] `{$body_name}` - *[other] `{$body_name}` and up {$depth} bodies - } - .non_local = an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - .exception = one exception to the rule are anon-const (`const _: () = {"{"} ... {"}"}`) at top-level module and anon-const at the same nesting as the trait or type +lint_non_local_definitions_impl = non-local `impl` definition, `impl` blocks should be written at the same level as their item + .remove_help = remove `{$may_remove_part}` to make the `impl` local + .without_trait = methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` + .with_trait = an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` + .bounds = `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + .doctest = make this doc-test a standalone test with its own `fn main() {"{"} ... {"}"}` + .exception = items in an anonymous const item (`const _: () = {"{"} ... {"}"}`) are treated as in the same scope as the anonymous const's declaration .const_anon = use a const-anon item to suppress this lint + .macro_to_change = the {$macro_kind} `{$macro_to_change}` defines the non-local `impl`, and may need to be changed -lint_non_local_definitions_macro_rules = non-local `macro_rules!` definition, they should be avoided as they go against expectation +lint_non_local_definitions_impl_move_help = + move the `impl` block outside of this {$body_kind_descr} {$depth -> + [one] `{$body_name}` + *[other] `{$body_name}` and up {$depth} bodies + } + +lint_non_local_definitions_macro_rules = non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module .help = remove the `#[macro_export]` or move this `macro_rules!` outside the of the current {$body_kind_descr} {$depth -> [one] `{$body_name}` @@ -470,7 +569,12 @@ lint_non_local_definitions_macro_rules = non-local `macro_rules!` definition, th .help_doctest = remove the `#[macro_export]` or make this doc-test a standalone test with its own `fn main() {"{"} ... {"}"}` .non_local = a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute - .exception = one exception to the rule are anon-const (`const _: () = {"{"} ... {"}"}`) at top-level module + +lint_non_local_definitions_may_move = may need to be moved as well + +lint_non_local_definitions_of_trait_not_local = `{$of_trait_str}` is not local + +lint_non_local_definitions_self_ty_not_local = `{$self_ty_str}` is not local lint_non_snake_case = {$sort} `{$name}` should have a snake case name .rename_or_convert_suggestion = rename the identifier or convert it to a snake case raw identifier @@ -497,6 +601,9 @@ lint_opaque_hidden_inferred_bound = opaque type `{$ty}` does not satisfy its ass lint_opaque_hidden_inferred_bound_sugg = add this bound +lint_or_patterns_back_compat = the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro + .suggestion = use pat_param to preserve semantics + lint_overflowing_bin_hex = literal out of range for `{$ty}` .negative_note = the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` .negative_becomes_note = and the value `-{$lit}` will become `{$actually}{$ty}` @@ -526,6 +633,18 @@ lint_path_statement_drop = path statement drops value lint_path_statement_no_effect = path statement with no effect +lint_pattern_in_bodiless = patterns aren't allowed in functions without bodies + .label = pattern not allowed in function without body + +lint_pattern_in_foreign = patterns aren't allowed in foreign function declarations + .label = pattern not allowed in foreign function + +lint_private_extern_crate_reexport = extern crate `{$ident}` is private and cannot be re-exported + .suggestion = consider making the `extern crate` item publicly accessible + +lint_proc_macro_derive_resolution_fallback = cannot find {$ns} `{$ident}` in this scope + .label = names from parent modules are not accessible without an explicit import + lint_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking them for null will always return false .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value .label = expression has type `{$orig_ty}` @@ -547,6 +666,16 @@ lint_reason_must_be_string_literal = reason must be a string literal lint_reason_must_come_last = reason in lint attribute must come last +lint_redundant_import = the item `{$ident}` is imported redundantly + .label_imported_here = the item `{ident}` is already imported here + .label_defined_here = the item `{ident}` is already defined here + .label_imported_prelude = the item `{ident}` is already imported by the extern prelude + .label_defined_prelude = the item `{ident}` is already defined by the extern prelude + +lint_redundant_import_visibility = glob import doesn't reexport anything with visibility `{$import_vis}` because no imported item is public enough + .note = the most public imported item is `{$max_vis}` + .help = reduce the glob import's visibility or increase visibility of imported items + lint_redundant_semicolons = unnecessary trailing {$multiple -> [true] semicolons @@ -557,6 +686,8 @@ lint_redundant_semicolons = *[false] this semicolon } +lint_remove_mut_from_pattern = remove `mut` from the parameter + lint_removed_lint = lint `{$name}` has been removed: {$reason} lint_renamed_lint = lint `{$name}` has been renamed to `{$replace}` @@ -565,6 +696,22 @@ lint_renamed_lint = lint `{$name}` has been renamed to `{$replace}` lint_requested_level = requested on the command line with `{$level} {$lint_name}` +lint_reserved_prefix = prefix `{$prefix}` is unknown + .label = unknown prefix + .suggestion = insert whitespace here to avoid this being parsed as a prefix in Rust 2021 + +lint_shadowed_into_iter = + this method call resolves to `<&{$target} as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<{$target} as IntoIterator>::into_iter` in Rust {$edition} + .use_iter_suggestion = use `.iter()` instead of `.into_iter()` to avoid ambiguity + .remove_into_iter_suggestion = or remove `.into_iter()` to iterate by value + .use_explicit_into_iter_suggestion = + or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value + +lint_single_use_lifetime = lifetime parameter `{$ident}` only used once + .label_param = this lifetime... + .label_use = ...is used only here + .suggestion = elide the single-use lifetime + lint_span_use_eq_ctxt = use `.eq_ctxt()` instead of `.ctxt() == .ctxt()` lint_supertrait_as_deref_target = this `Deref` implementation is covered by an implicit supertrait coercion @@ -578,6 +725,10 @@ lint_suspicious_double_ref_clone = lint_suspicious_double_ref_deref = using `.deref()` on a double reference, which returns `{$ty}` instead of dereferencing the inner type +lint_trailing_semi_macro = trailing semicolon in macro used in expression position + .note1 = macro invocations at the end of a block are treated as expressions + .note2 = to ignore the value produced by the macro, add a semicolon after the invocation of `{$name}` + lint_ty_qualified = usage of qualified `ty::{$ty}` .suggestion = try importing it and using it unqualified @@ -591,12 +742,64 @@ lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::Manual .label = argument has type `{$arg_ty}` .suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value +lint_unexpected_cfg_add_build_rs_println = or consider adding `{$build_rs_println}` to the top of the `build.rs` +lint_unexpected_cfg_add_cargo_feature = consider using a Cargo feature instead +lint_unexpected_cfg_add_cargo_toml_lint_cfg = or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg} +lint_unexpected_cfg_add_cmdline_arg = to expect this configuration use `{$cmdline_arg}` +lint_unexpected_cfg_define_features = consider defining some features in `Cargo.toml` +lint_unexpected_cfg_doc_cargo = see for more information about checking conditional configuration +lint_unexpected_cfg_doc_rustc = see for more information about checking conditional configuration + +lint_unexpected_cfg_name = unexpected `cfg` condition name: `{$name}` +lint_unexpected_cfg_name_expected_names = expected names are: {$possibilities}{$and_more -> + [0] {""} + *[other] {" "}and {$and_more} more + } +lint_unexpected_cfg_name_expected_values = expected values for `{$best_match}` are: {$possibilities} +lint_unexpected_cfg_name_similar_name = there is a config with a similar name +lint_unexpected_cfg_name_similar_name_different_values = there is a config with a similar name and different values +lint_unexpected_cfg_name_similar_name_no_value = there is a config with a similar name and no value +lint_unexpected_cfg_name_similar_name_value = there is a config with a similar name and value +lint_unexpected_cfg_name_with_similar_value = found config with similar value + +lint_unexpected_cfg_value = unexpected `cfg` condition value: {$has_value -> + [true] `{$value}` + *[false] (none) + } +lint_unexpected_cfg_value_add_feature = consider adding `{$value}` as a feature in `Cargo.toml` +lint_unexpected_cfg_value_expected_values = expected values for `{$name}` are: {$have_none_possibility -> + [true] {"(none), "} + *[false] {""} + }{$possibilities}{$and_more -> + [0] {""} + *[other] {" "}and {$and_more} more + } +lint_unexpected_cfg_value_no_expected_value = no expected value for `{$name}` +lint_unexpected_cfg_value_no_expected_values = no expected values for `{$name}` +lint_unexpected_cfg_value_remove_condition = remove the condition +lint_unexpected_cfg_value_remove_value = remove the value +lint_unexpected_cfg_value_similar_name = there is a expected value with a similar name +lint_unexpected_cfg_value_specify_value = specify a config value + lint_ungated_async_fn_track_caller = `#[track_caller]` on async functions is a no-op .label = this function will not propagate the caller location +lint_unicode_text_flow = unicode codepoint changing visible direction of text present in comment + .label = {$num_codepoints -> + [1] this comment contains an invisible unicode text flow control codepoint + *[other] this comment contains invisible unicode text flow control codepoints + } + .note = these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + .suggestion = if their presence wasn't intentional, you can remove them + .label_comment_char = {$c_debug} + + lint_unit_bindings = binding has unit type `()` .label = this pattern is inferred to be the unit type `()` +lint_unknown_diagnostic_attribute = unknown diagnostic attribute +lint_unknown_diagnostic_attribute_typo_sugg = an attribute with a similar name exists + lint_unknown_gated_lint = unknown lint: `{$name}` .note = the `{$name}` lint is unstable @@ -612,9 +815,16 @@ lint_unknown_lint = *[false] did you mean: `{$replace}` } +lint_unknown_macro_variable = unknown macro variable `{$name}` + lint_unknown_tool_in_scoped_lint = unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}` .help = add `#![register_tool({$tool_name})]` to the crate root +lint_unnameable_test_items = cannot test inner items + +lint_unnecessary_qualification = unnecessary qualification + .suggestion = remove the unnecessary path segments + lint_unsupported_group = `{$lint_group}` lint group is not supported with ´--force-warn´ lint_untranslatable_diag = diagnostics should be created using translatable messages @@ -622,6 +832,9 @@ lint_untranslatable_diag = diagnostics should be created using translatable mess lint_unused_allocation = unnecessary allocation, use `&` instead lint_unused_allocation_mut = unnecessary allocation, use `&mut` instead +lint_unused_builtin_attribute = unused attribute `{$attr_name}` + .note = the built-in attribute `{$attr_name}` will be ignored, since it's applied to the macro invocation `{$macro_name}` + lint_unused_closure = unused {$pre}{$count -> [one] closure @@ -638,19 +851,54 @@ lint_unused_coroutine = }{$post} that must be used .note = coroutines are lazy and do nothing unless resumed +lint_unused_crate_dependency = extern crate `{$extern_crate}` is unused in crate `{$local_crate}` + .help = remove the dependency or add `use {$extern_crate} as _;` to the crate root + lint_unused_def = unused {$pre}`{$def}`{$post} that must be used .suggestion = use `let _ = ...` to ignore the resulting value lint_unused_delim = unnecessary {$delim} around {$item} .suggestion = remove these {$delim} +lint_unused_doc_comment = unused doc comment + .label = rustdoc does not generate documentation for macro invocations + .help = to document an item produced by a macro, the macro must produce the documentation as part of its expansion + +lint_unused_extern_crate = unused extern crate + .suggestion = remove it + lint_unused_import_braces = braces around {$node} is unnecessary +lint_unused_imports = {$num_snippets -> + [one] unused import: {$span_snippets} + *[other] unused imports: {$span_snippets} + } + .suggestion_remove_whole_use = remove the whole `use` item + .suggestion_remove_imports = {$num_to_remove -> + [one] remove the unused import + *[other] remove the unused imports + } + .help = if this is a test module, consider adding a `#[cfg(test)]` to the containing module + +lint_unused_label = unused label + +lint_unused_lifetime = lifetime parameter `{$ident}` never used + .suggestion = elide the unused lifetime + +lint_unused_macro_definition = unused macro definition: `{$name}` + +lint_unused_macro_use = unused `#[macro_use]` import + lint_unused_op = unused {$op} that must be used .label = the {$op} produces a value .suggestion = use `let _ = ...` to ignore the resulting value lint_unused_result = unused result of type `{$ty}` +lint_use_let_underscore_ignore_suggestion = use `let _ = ...` to ignore the expression or result + lint_variant_size_differences = enum variant is more than three times larger ({$largest} bytes) than the next largest + +lint_wasm_c_abi = + older versions of the `wasm-bindgen` crate will be incompatible with future versions of Rust; please update to `wasm-bindgen` v0.2.88 diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs deleted file mode 100644 index 8f4bae339573..000000000000 --- a/compiler/rustc_lint/src/array_into_iter.rs +++ /dev/null @@ -1,144 +0,0 @@ -use crate::{ - lints::{ArrayIntoIterDiag, ArrayIntoIterDiagSub}, - LateContext, LateLintPass, LintContext, -}; -use rustc_hir as hir; -use rustc_middle::bug; -use rustc_middle::ty; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; -use rustc_session::lint::FutureIncompatibilityReason; -use rustc_session::{declare_lint, impl_lint_pass}; -use rustc_span::edition::Edition; -use rustc_span::symbol::sym; -use rustc_span::Span; - -declare_lint! { - /// The `array_into_iter` lint detects calling `into_iter` on arrays. - /// - /// ### Example - /// - /// ```rust,edition2018 - /// # #![allow(unused)] - /// [1, 2, 3].into_iter().for_each(|n| { *n; }); - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Since Rust 1.53, arrays implement `IntoIterator`. However, to avoid - /// breakage, `array.into_iter()` in Rust 2015 and 2018 code will still - /// behave as `(&array).into_iter()`, returning an iterator over - /// references, just like in Rust 1.52 and earlier. - /// This only applies to the method call syntax `array.into_iter()`, not to - /// any other syntax such as `for _ in array` or `IntoIterator::into_iter(array)`. - pub ARRAY_INTO_ITER, - Warn, - "detects calling `into_iter` on arrays in Rust 2015 and 2018", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021), - reference: "", - }; -} - -#[derive(Copy, Clone, Default)] -pub struct ArrayIntoIter { - for_expr_span: Span, -} - -impl_lint_pass!(ArrayIntoIter => [ARRAY_INTO_ITER]); - -impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - // Save the span of expressions in `for _ in expr` syntax, - // so we can give a better suggestion for those later. - if let hir::ExprKind::Match(arg, [_], hir::MatchSource::ForLoopDesugar) = &expr.kind { - if let hir::ExprKind::Call(path, [arg]) = &arg.kind { - if let hir::ExprKind::Path(hir::QPath::LangItem( - hir::LangItem::IntoIterIntoIter, - .., - )) = &path.kind - { - self.for_expr_span = arg.span; - } - } - } - - // We only care about method call expressions. - if let hir::ExprKind::MethodCall(call, receiver_arg, ..) = &expr.kind { - if call.ident.name != sym::into_iter { - return; - } - - // Check if the method call actually calls the libcore - // `IntoIterator::into_iter`. - let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); - match cx.tcx.trait_of_item(def_id) { - Some(trait_id) if cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_id) => {} - _ => return, - }; - - // As this is a method call expression, we have at least one argument. - let receiver_ty = cx.typeck_results().expr_ty(receiver_arg); - let adjustments = cx.typeck_results().expr_adjustments(receiver_arg); - - let Some(Adjustment { kind: Adjust::Borrow(_), target }) = adjustments.last() else { - return; - }; - - let types = - std::iter::once(receiver_ty).chain(adjustments.iter().map(|adj| adj.target)); - - let mut found_array = false; - - for ty in types { - match ty.kind() { - // If we run into a &[T; N] or &[T] first, there's nothing to warn about. - // It'll resolve to the reference version. - ty::Ref(_, inner_ty, _) if inner_ty.is_array() => return, - ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Slice(..)) => return, - // Found an actual array type without matching a &[T; N] first. - // This is the problematic case. - ty::Array(..) => { - found_array = true; - break; - } - _ => {} - } - } - - if !found_array { - return; - } - - // Emit lint diagnostic. - let target = match *target.kind() { - ty::Ref(_, inner_ty, _) if inner_ty.is_array() => "[T; N]", - ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Slice(..)) => "[T]", - // We know the original first argument type is an array type, - // we know that the first adjustment was an autoref coercion - // and we know that `IntoIterator` is the trait involved. The - // array cannot be coerced to something other than a reference - // to an array or to a slice. - _ => bug!("array type coerced to something other than array or slice"), - }; - let sub = if self.for_expr_span == expr.span { - Some(ArrayIntoIterDiagSub::RemoveIntoIter { - span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), - }) - } else if receiver_ty.is_array() { - Some(ArrayIntoIterDiagSub::UseExplicitIntoIter { - start_span: expr.span.shrink_to_lo(), - end_span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), - }) - } else { - None - }; - cx.emit_span_lint( - ARRAY_INTO_ITER, - call.ident.span, - ArrayIntoIterDiag { target, suggestion: call.ident.span, sub }, - ); - } - } -} diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 6b9f9d1531ea..98318cd14d9d 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -30,10 +30,10 @@ use crate::{ BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures, BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, - BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, - BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds, - BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause, - BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, + BuiltinMutablesTransmutes, BuiltinNamedAsmLabel, BuiltinNoMangleGeneric, + BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, + BuiltinTypeAliasGenericBounds, BuiltinTypeAliasGenericBoundsSuggestion, + BuiltinTypeAliasWhereClause, BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, BuiltinWhileTrue, SuggestChangingAssocTypes, @@ -51,7 +51,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir::intravisit::FnKind as HirFnKind; -use rustc_hir::{Body, FnDecl, GenericParamKind, Node, PatKind, PredicateOrigin}; +use rustc_hir::{Body, FnDecl, GenericParamKind, PatKind, PredicateOrigin}; use rustc_middle::bug; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::LayoutOf; @@ -60,7 +60,7 @@ use rustc_middle::ty::GenericArgKind; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::Upcast; use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; -use rustc_session::lint::{BuiltinLintDiag, FutureIncompatibilityReason}; +use rustc_session::lint::FutureIncompatibilityReason; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; @@ -70,6 +70,7 @@ use rustc_target::abi::Abi; use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{self, misc::type_allowed_to_implement_copy}; +use tracing::debug; use crate::nonstandard_style::{method_context, MethodLateContext}; @@ -141,7 +142,7 @@ declare_lint! { /// ```rust,compile_fail /// #![deny(box_pointers)] /// struct Foo { - /// x: Box, + /// x: Box, /// } /// ``` /// @@ -674,11 +675,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { return; } } - let param_env = ty::ParamEnv::empty(); - if ty.is_copy_modulo_regions(cx.tcx, param_env) { + if ty.is_copy_modulo_regions(cx.tcx, cx.param_env) { return; } - if type_implements_negative_copy_modulo_regions(cx.tcx, ty, param_env) { + if type_implements_negative_copy_modulo_regions(cx.tcx, ty, cx.param_env) { return; } if def.is_variant_list_non_exhaustive() @@ -694,7 +694,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { .tcx .infer_ctxt() .build() - .type_implements_trait(iter_trait, [ty], param_env) + .type_implements_trait(iter_trait, [ty], cx.param_env) .must_apply_modulo_regions() { return; @@ -711,7 +711,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { if type_allowed_to_implement_copy( cx.tcx, - param_env, + cx.param_env, ty, traits::ObligationCause::misc(item.span, item.owner_id.def_id), ) @@ -1423,11 +1423,20 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub { self.perform_lint(cx, "item", foreign_item.owner_id.def_id, foreign_item.vis_span, true); } - fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - if matches!(cx.tcx.parent_hir_node(field.hir_id), Node::Variant(_)) { - return; - } - self.perform_lint(cx, "field", field.def_id, field.vis_span, false); + fn check_field_def(&mut self, _cx: &LateContext<'_>, _field: &hir::FieldDef<'_>) { + // - If an ADT definition is reported then we don't need to check fields + // (as it would add unnecessary complexity to the source code, the struct + // definition is in the immediate proximity to give the "real" visibility). + // - If an ADT is not reported because it's not `pub` - we don't need to + // check fields. + // - If an ADT is not reported because it's reachable - we also don't need + // to check fields because then they are reachable by construction if they + // are pub. + // + // Therefore in no case we check the fields. + // + // cf. https://github.com/rust-lang/rust/pull/126013#issuecomment-2152839205 + // cf. https://github.com/rust-lang/rust/pull/126040#issuecomment-2152944506 } fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { @@ -1494,8 +1503,9 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { let ty = cx.tcx.type_of(item.owner_id).skip_binder(); if ty.has_inherent_projections() { - // Bounds of type aliases that contain opaque types or inherent projections are respected. - // E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`, `type X = Type::Inherent;`. + // Bounds of type aliases that contain opaque types or inherent projections are + // respected. E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`, `type X = + // Type::Inherent;`. return; } @@ -1950,14 +1960,22 @@ declare_lint_pass!(ExplicitOutlivesRequirements => [EXPLICIT_OUTLIVES_REQUIREMEN impl ExplicitOutlivesRequirements { fn lifetimes_outliving_lifetime<'tcx>( + tcx: TyCtxt<'tcx>, inferred_outlives: &'tcx [(ty::Clause<'tcx>, Span)], - def_id: DefId, + item: DefId, + lifetime: DefId, ) -> Vec> { + let item_generics = tcx.generics_of(item); + inferred_outlives .iter() .filter_map(|(clause, _)| match clause.kind().skip_binder() { ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a { - ty::ReEarlyParam(ebr) if ebr.def_id == def_id => Some(b), + ty::ReEarlyParam(ebr) + if item_generics.region_param(ebr, tcx).def_id == lifetime => + { + Some(b) + } _ => None, }, _ => None, @@ -1986,9 +2004,12 @@ impl ExplicitOutlivesRequirements { bounds: &hir::GenericBounds<'_>, inferred_outlives: &[ty::Region<'tcx>], predicate_span: Span, + item: DefId, ) -> Vec<(usize, Span)> { use rustc_middle::middle::resolve_bound_vars::ResolvedArg; + let item_generics = tcx.generics_of(item); + bounds .iter() .enumerate() @@ -2000,7 +2021,7 @@ impl ExplicitOutlivesRequirements { let is_inferred = match tcx.named_bound_var(lifetime.hir_id) { Some(ResolvedArg::EarlyBound(def_id)) => inferred_outlives .iter() - .any(|r| matches!(**r, ty::ReEarlyParam(ebr) if { ebr.def_id == def_id })), + .any(|r| matches!(**r, ty::ReEarlyParam(ebr) if { item_generics.region_param(ebr, tcx).def_id == def_id })), _ => false, }; @@ -2109,7 +2130,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { { ( Self::lifetimes_outliving_lifetime( + cx.tcx, inferred_outlives, + item.owner_id.to_def_id(), region_def_id, ), &predicate.bounds, @@ -2152,6 +2175,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { bounds, &relevant_lifetimes, predicate_span, + item.owner_id.to_def_id(), ); bound_count += bound_spans.len(); @@ -2210,7 +2234,8 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { hir_generics.span.shrink_to_hi().to(where_span) }; - // Due to macro expansions, the `full_where_span` might not actually contain all predicates. + // Due to macro expansions, the `full_where_span` might not actually contain all + // predicates. if where_lint_spans.iter().all(|&sp| full_where_span.contains(sp)) { lint_spans.push(full_where_span); } else { @@ -2587,7 +2612,8 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { }; // So we have at least one potentially inhabited variant. Might we have two? let Some(second_variant) = potential_variants.next() else { - // There is only one potentially inhabited variant. So we can recursively check that variant! + // There is only one potentially inhabited variant. So we can recursively + // check that variant! return variant_find_init_error( cx, ty, @@ -2597,10 +2623,10 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { init, ); }; - // So we have at least two potentially inhabited variants. - // If we can prove that we have at least two *definitely* inhabited variants, - // then we have a tag and hence leaving this uninit is definitely disallowed. - // (Leaving it zeroed could be okay, depending on which variant is encoded as zero tag.) + // So we have at least two potentially inhabited variants. If we can prove that + // we have at least two *definitely* inhabited variants, then we have a tag and + // hence leaving this uninit is definitely disallowed. (Leaving it zeroed could + // be okay, depending on which variant is encoded as zero tag.) if init == InitKind::Uninit { let definitely_inhabited = (first_variant.1 as usize) + (second_variant.1 as usize) @@ -2811,7 +2837,8 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels { let mut found_labels = Vec::new(); - // A semicolon might not actually be specified as a separator for all targets, but it seems like LLVM accepts it always + // A semicolon might not actually be specified as a separator for all targets, but + // it seems like LLVM accepts it always. let statements = template_str.split(|c| matches!(c, '\n' | ';')); for statement in statements { // If there's a comment, trim it from the statement @@ -2824,7 +2851,8 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels { let mut chars = possible_label.chars(); let Some(start) = chars.next() else { - // Empty string means a leading ':' in this section, which is not a label. + // Empty string means a leading ':' in this section, which is not a + // label. break 'label_loop; }; @@ -2841,12 +2869,15 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels { // Labels continue with ASCII alphanumeric characters, _, or $ for c in chars { - // Inside a template format arg, any character is permitted for the puproses of label detection - // because we assume that it can be replaced with some other valid label string later. - // `options(raw)` asm blocks cannot have format args, so they are excluded from this special case. + // Inside a template format arg, any character is permitted for the + // puproses of label detection because we assume that it can be + // replaced with some other valid label string later. `options(raw)` + // asm blocks cannot have format args, so they are excluded from this + // special case. if !raw && in_bracket { if c == '{' { - // Nested brackets are not allowed in format args, this cannot be a label. + // Nested brackets are not allowed in format args, this cannot + // be a label. break 'label_loop; } @@ -2859,7 +2890,8 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels { in_bracket = true; } else { if !(c.is_ascii_alphanumeric() || matches!(c, '_' | '$')) { - // The potential label had an invalid character inside it, it cannot be a label. + // The potential label had an invalid character inside it, it + // cannot be a label. break 'label_loop; } } @@ -2878,20 +2910,12 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels { .into_iter() .filter_map(|label| find_label_span(label)) .collect::>(); - // If there were labels but we couldn't find a span, combine the warnings and use the template span + // If there were labels but we couldn't find a span, combine the warnings and + // use the template span. let target_spans: MultiSpan = if spans.len() > 0 { spans.into() } else { (*template_span).into() }; - cx.span_lint_with_diagnostics( - NAMED_ASM_LABELS, - Some(target_spans), - fluent::lint_builtin_asm_labels, - |_| {}, - BuiltinLintDiag::NamedAsmLabel( - "only local labels of the form `:` should be used in inline asm" - .to_string(), - ), - ); + cx.emit_span_lint(NAMED_ASM_LABELS, target_spans, BuiltinNamedAsmLabel); } } } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 62ba9ef5c113..9f0f116cbd03 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -21,7 +21,7 @@ use crate::passes::{EarlyLintPassObject, LateLintPassObject}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_data_structures::unord::UnordMap; -use rustc_errors::{Diag, DiagMessage, LintDiagnostic, MultiSpan}; +use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; use rustc_feature::Features; use rustc_hir as hir; use rustc_hir::def::Res; @@ -39,10 +39,10 @@ use rustc_span::edit_distance::find_best_match_for_names; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; use rustc_target::abi; - use std::cell::Cell; use std::iter; use std::slice; +use tracing::debug; mod diagnostics; @@ -94,7 +94,8 @@ enum TargetLint { /// A lint name that should give no warnings and have no effect. /// - /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers them as tool lints. + /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers + /// them as tool lints. Ignored, } @@ -126,12 +127,16 @@ pub enum CheckLintNameResult<'a> { Renamed(String), /// The lint has been removed due to the given reason. Removed(String), - /// The lint is from a tool. If the Option is None, then either - /// the lint does not exist in the tool or the code was not - /// compiled with the tool and therefore the lint was never - /// added to the `LintStore`. Otherwise the `LintId` will be - /// returned as if it where a rustc lint. - Tool(Result<&'a [LintId], (Option<&'a [LintId]>, String)>), + + /// The lint is from a tool. The `LintId` will be returned as if it were a + /// rustc lint. The `Option` indicates if the lint has been + /// renamed. + Tool(&'a [LintId], Option), + + /// The lint is from a tool. Either the lint does not exist in the tool or + /// the code was not compiled with the tool and therefore the lint was + /// never added to the `LintStore`. + MissingTool, } impl LintStore { @@ -384,14 +389,14 @@ impl LintStore { } else { // 2. The tool isn't currently running, so no lints will be registered. // To avoid giving a false positive, ignore all unknown lints. - CheckLintNameResult::Tool(Err((None, String::new()))) + CheckLintNameResult::MissingTool }; } Some(LintGroup { lint_ids, .. }) => { - return CheckLintNameResult::Tool(Ok(lint_ids)); + return CheckLintNameResult::Tool(lint_ids, None); } }, - Some(Id(id)) => return CheckLintNameResult::Tool(Ok(slice::from_ref(id))), + Some(Id(id)) => return CheckLintNameResult::Tool(slice::from_ref(id), None), // If the lint was registered as removed or renamed by the lint tool, we don't need // to treat tool_lints and rustc lints different and can use the code below. _ => {} @@ -411,7 +416,7 @@ impl LintStore { return if *silent { CheckLintNameResult::Ok(lint_ids) } else { - CheckLintNameResult::Tool(Err((Some(lint_ids), (*name).to_string()))) + CheckLintNameResult::Tool(lint_ids, Some((*name).to_string())) }; } CheckLintNameResult::Ok(lint_ids) @@ -472,18 +477,17 @@ impl LintStore { // Reaching this would be weird, but let's cover this case anyway if let Some(LintAlias { name, silent }) = depr { let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap(); - return if *silent { - CheckLintNameResult::Tool(Err((Some(lint_ids), complete_name))) + if *silent { + CheckLintNameResult::Tool(lint_ids, Some(complete_name)) } else { - CheckLintNameResult::Tool(Err((Some(lint_ids), (*name).to_string()))) - }; + CheckLintNameResult::Tool(lint_ids, Some((*name).to_string())) + } + } else { + CheckLintNameResult::Tool(lint_ids, Some(complete_name)) } - CheckLintNameResult::Tool(Err((Some(lint_ids), complete_name))) } }, - Some(Id(id)) => { - CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name))) - } + Some(Id(id)) => CheckLintNameResult::Tool(slice::from_ref(id), Some(complete_name)), Some(other) => { debug!("got renamed lint {:?}", other); CheckLintNameResult::NoLint(None) @@ -527,30 +531,26 @@ pub struct EarlyContext<'a> { pub buffered: LintBuffer, } -pub trait LintContext { - fn sess(&self) -> &Session; - +impl EarlyContext<'_> { /// Emit a lint at the appropriate level, with an optional associated span and an existing /// diagnostic. /// /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature #[rustc_lint_diagnostics] - fn span_lint_with_diagnostics( + pub fn span_lint_with_diagnostics( &self, lint: &'static Lint, - span: Option>, - msg: impl Into, - decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), + span: MultiSpan, diagnostic: BuiltinLintDiag, ) { - // We first generate a blank diagnostic. - self.opt_span_lint(lint, span, msg, |db| { - // Now, set up surrounding context. - diagnostics::builtin(self.sess(), diagnostic, db); - // Rewrap `db`, and pass control to the user. - decorate(db) + self.opt_span_lint(lint, Some(span), |diag| { + diagnostics::decorate_lint(self.sess(), diagnostic, diag); }); } +} + +pub trait LintContext { + fn sess(&self) -> &Session; // FIXME: These methods should not take an Into -- instead, callers should need to // set the span in their `decorate` function (preferably using set_span). @@ -562,7 +562,6 @@ pub trait LintContext { &self, lint: &'static Lint, span: Option, - msg: impl Into, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), ); @@ -574,8 +573,8 @@ pub trait LintContext { span: S, decorator: impl for<'a> LintDiagnostic<'a, ()>, ) { - self.opt_span_lint(lint, Some(span), decorator.msg(), |diag| { - decorator.decorate_lint(diag); + self.opt_span_lint(lint, Some(span), |lint| { + decorator.decorate_lint(lint); }); } @@ -587,17 +586,16 @@ pub trait LintContext { &self, lint: &'static Lint, span: S, - msg: impl Into, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), ) { - self.opt_span_lint(lint, Some(span), msg, decorate); + self.opt_span_lint(lint, Some(span), decorate); } /// Emit a lint from a lint struct (some type that implements `LintDiagnostic`, typically /// generated by `#[derive(LintDiagnostic)]`). fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> LintDiagnostic<'a, ()>) { - self.opt_span_lint(lint, None as Option, decorator.msg(), |diag| { - decorator.decorate_lint(diag); + self.opt_span_lint(lint, None as Option, |lint| { + decorator.decorate_lint(lint); }); } @@ -605,13 +603,8 @@ pub trait LintContext { /// /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature #[rustc_lint_diagnostics] - fn lint( - &self, - lint: &'static Lint, - msg: impl Into, - decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), - ) { - self.opt_span_lint(lint, None as Option, msg, decorate); + fn lint(&self, lint: &'static Lint, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>)) { + self.opt_span_lint(lint, None as Option, decorate); } /// This returns the lint level for the given lint at the current location. @@ -674,14 +667,13 @@ impl<'tcx> LintContext for LateContext<'tcx> { &self, lint: &'static Lint, span: Option, - msg: impl Into, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), ) { let hir_id = self.last_node_with_lint_attrs; match span { - Some(s) => self.tcx.node_span_lint(lint, hir_id, s, msg, decorate), - None => self.tcx.node_lint(lint, hir_id, msg, decorate), + Some(s) => self.tcx.node_span_lint(lint, hir_id, s, decorate), + None => self.tcx.node_lint(lint, hir_id, decorate), } } @@ -701,10 +693,9 @@ impl LintContext for EarlyContext<'_> { &self, lint: &'static Lint, span: Option, - msg: impl Into, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), ) { - self.builder.opt_span_lint(lint, span.map(|s| s.into()), msg, decorate) + self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorate) } fn get_lint_level(&self, lint: &'static Lint) -> Level { diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index 5ad3ff71a6d4..290bb5173dbe 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -1,58 +1,51 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +use std::borrow::Cow; + use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; -use rustc_errors::{elided_lifetime_in_path_suggestion, Diag}; -use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_errors::elided_lifetime_in_path_suggestion; +use rustc_errors::{Applicability, Diag, DiagArgValue, LintDiagnostic}; use rustc_middle::middle::stability; use rustc_session::lint::BuiltinLintDiag; use rustc_session::Session; use rustc_span::BytePos; +use tracing::debug; + +use crate::lints; mod check_cfg; -pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Diag<'_, ()>) { +pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Diag<'_, ()>) { match diagnostic { - BuiltinLintDiag::UnicodeTextFlow(span, content) => { + BuiltinLintDiag::UnicodeTextFlow(comment_span, content) => { let spans: Vec<_> = content .char_indices() .filter_map(|(i, c)| { TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { - let lo = span.lo() + BytePos(2 + i as u32); - (c, span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) + let lo = comment_span.lo() + BytePos(2 + i as u32); + (c, comment_span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) }) }) .collect(); - let (an, s) = match spans.len() { - 1 => ("an ", ""), - _ => ("", "s"), - }; - diag.span_label( - span, - format!( - "this comment contains {an}invisible unicode text flow control codepoint{s}", - ), - ); - for (c, span) in &spans { - diag.span_label(*span, format!("{c:?}")); - } - diag.note( - "these kind of unicode codepoints change the way text flows on \ - applications that support them, but can cause confusion because they \ - change the order of characters on the screen", - ); - if !spans.is_empty() { - diag.multipart_suggestion_with_style( - "if their presence wasn't intentional, you can remove them", - spans.into_iter().map(|(_, span)| (span, "".to_string())).collect(), - Applicability::MachineApplicable, - SuggestionStyle::HideCodeAlways, - ); + let characters = spans + .iter() + .map(|&(c, span)| lints::UnicodeCharNoteSub { span, c_debug: format!("{c:?}") }) + .collect(); + let suggestions = (!spans.is_empty()).then_some(lints::UnicodeTextFlowSuggestion { + spans: spans.iter().map(|(_c, span)| *span).collect(), + }); + + lints::UnicodeTextFlow { + comment_span, + characters, + suggestions, + num_codepoints: spans.len(), } + .decorate_lint(diag); } - BuiltinLintDiag::Normal => (), - BuiltinLintDiag::AbsPathWithModule(span) => { - let (sugg, app) = match sess.source_map().span_to_snippet(span) { + BuiltinLintDiag::AbsPathWithModule(mod_span) => { + let (replacement, applicability) = match sess.source_map().span_to_snippet(mod_span) { Ok(ref s) => { // FIXME(Manishearth) ideally the emitting code // can tell us whether or not this is global @@ -62,160 +55,164 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di } Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), }; - diag.span_suggestion(span, "use `crate`", sugg, app); + lints::AbsPathWithModule { + sugg: lints::AbsPathWithModuleSugg { span: mod_span, applicability, replacement }, + } + .decorate_lint(diag); } - BuiltinLintDiag::ProcMacroDeriveResolutionFallback(span) => { - diag.span_label( - span, - "names from parent modules are not accessible without an explicit import", - ); + BuiltinLintDiag::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident } => { + lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident } + .decorate_lint(diag) } BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => { - diag.span_note(span_def, "the macro is defined here"); + lints::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def } + .decorate_lint(diag) } + BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => { - diag.subdiagnostic( - sess.dcx(), - elided_lifetime_in_path_suggestion( + lints::ElidedLifetimesInPaths { + subdiag: elided_lifetime_in_path_suggestion( sess.source_map(), n, path_span, incl_angl_brckt, insertion_span, ), - ); - } - BuiltinLintDiag::UnknownCrateTypes(span, note, sugg) => { - diag.span_suggestion(span, note, sugg, Applicability::MaybeIncorrect); - } - BuiltinLintDiag::UnusedImports(message, replaces, in_test_module) => { - if !replaces.is_empty() { - diag.tool_only_multipart_suggestion( - message, - replaces, - Applicability::MachineApplicable, - ); } + .decorate_lint(diag); + } + BuiltinLintDiag::UnknownCrateTypes { span, candidate } => { + let sugg = candidate.map(|candidate| lints::UnknownCrateTypesSub { span, candidate }); + lints::UnknownCrateTypes { sugg }.decorate_lint(diag); + } + BuiltinLintDiag::UnusedImports { + remove_whole_use, + num_to_remove, + remove_spans, + test_module_span, + span_snippets, + } => { + let sugg = if remove_whole_use { + lints::UnusedImportsSugg::RemoveWholeUse { span: remove_spans[0] } + } else { + lints::UnusedImportsSugg::RemoveImports { remove_spans, num_to_remove } + }; + let test_module_span = + test_module_span.map(|span| sess.source_map().guess_head_span(span)); - if let Some(span) = in_test_module { - diag.span_help( - sess.source_map().guess_head_span(span), - "if this is a test module, consider adding a `#[cfg(test)]` to the containing module", - ); + lints::UnusedImports { + sugg, + test_module_span, + num_snippets: span_snippets.len(), + span_snippets: DiagArgValue::StrListSepByAnd( + span_snippets.into_iter().map(Cow::Owned).collect(), + ), } + .decorate_lint(diag); } BuiltinLintDiag::RedundantImport(spans, ident) => { - for (span, is_imported) in spans { - let introduced = if is_imported { "imported" } else { "defined" }; - let span_msg = if span.is_dummy() { "by the extern prelude" } else { "here" }; - diag.span_label( - span, - format!("the item `{ident}` is already {introduced} {span_msg}"), - ); - } + let subs = spans + .into_iter() + .map(|(span, is_imported)| { + (match (span.is_dummy(), is_imported) { + (false, true) => lints::RedundantImportSub::ImportedHere, + (false, false) => lints::RedundantImportSub::DefinedHere, + (true, true) => lints::RedundantImportSub::ImportedPrelude, + (true, false) => lints::RedundantImportSub::DefinedPrelude, + })(span) + }) + .collect(); + lints::RedundantImport { subs, ident }.decorate_lint(diag); } - BuiltinLintDiag::DeprecatedMacro(suggestion, span) => { - stability::deprecation_suggestion(diag, "macro", suggestion, span) - } - BuiltinLintDiag::UnusedDocComment(span) => { - diag.span_label(span, "rustdoc does not generate documentation for macro invocations"); - diag.help("to document an item produced by a macro, \ - the macro must produce the documentation as part of its expansion"); - } - BuiltinLintDiag::PatternsInFnsWithoutBody(span, ident) => { - diag.span_suggestion( - span, - "remove `mut` from the parameter", - ident, - Applicability::MachineApplicable, - ); - } - BuiltinLintDiag::MissingAbi(span, default_abi) => { - diag.span_label(span, "ABI should be specified here"); - diag.help(format!("the default ABI is {}", default_abi.name())); - } - BuiltinLintDiag::LegacyDeriveHelpers(span) => { - diag.span_label(span, "the attribute is introduced here"); - } - BuiltinLintDiag::ProcMacroBackCompat(note) => { - diag.note(note); - } - BuiltinLintDiag::OrPatternsBackCompat(span, suggestion) => { - diag.span_suggestion( - span, - "use pat_param to preserve semantics", + BuiltinLintDiag::DeprecatedMacro { + suggestion, + suggestion_span, + note, + path, + since_kind, + } => { + let sub = suggestion.map(|suggestion| stability::DeprecationSuggestion { + span: suggestion_span, + kind: "macro".to_owned(), suggestion, - Applicability::MachineApplicable, - ); + }); + + stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind } + .decorate_lint(diag); } - BuiltinLintDiag::ReservedPrefix(span) => { - diag.span_label(span, "unknown prefix"); - diag.span_suggestion_verbose( - span.shrink_to_hi(), - "insert whitespace here to avoid this being parsed as a prefix in Rust 2021", - " ", - Applicability::MachineApplicable, - ); + BuiltinLintDiag::UnusedDocComment(attr_span) => { + lints::UnusedDocComment { span: attr_span }.decorate_lint(diag); + } + BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => { + let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span }; + if is_foreign { + lints::PatternsInFnsWithoutBody::Foreign { sub } + } else { + lints::PatternsInFnsWithoutBody::Bodiless { sub } + } + .decorate_lint(diag); + } + BuiltinLintDiag::MissingAbi(label_span, default_abi) => { + lints::MissingAbi { span: label_span, default_abi: default_abi.name() } + .decorate_lint(diag); + } + BuiltinLintDiag::LegacyDeriveHelpers(label_span) => { + lints::LegacyDeriveHelpers { span: label_span }.decorate_lint(diag); + } + BuiltinLintDiag::OrPatternsBackCompat(suggestion_span, suggestion) => { + lints::OrPatternsBackCompat { span: suggestion_span, suggestion }.decorate_lint(diag); + } + BuiltinLintDiag::ReservedPrefix(label_span, prefix) => { + lints::ReservedPrefix { + label: label_span, + suggestion: label_span.shrink_to_hi(), + prefix, + } + .decorate_lint(diag); } BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => { - diag.span_note( - invoc_span, - format!("the built-in attribute `{attr_name}` will be ignored, since it's applied to the macro invocation `{macro_name}`") - ); + lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }.decorate_lint(diag); } BuiltinLintDiag::TrailingMacro(is_trailing, name) => { - if is_trailing { - diag.note("macro invocations at the end of a block are treated as expressions"); - diag.note(format!("to ignore the value produced by the macro, add a semicolon after the invocation of `{name}`")); + lints::TrailingMacro { is_trailing, name }.decorate_lint(diag); + } + BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => { + lints::BreakWithLabelAndLoop { + sub: lints::BreakWithLabelAndLoopSub { + left: sugg_span.shrink_to_lo(), + right: sugg_span.shrink_to_hi(), + }, } - } - BuiltinLintDiag::BreakWithLabelAndLoop(span) => { - diag.multipart_suggestion( - "wrap this expression in parentheses", - vec![ - (span.shrink_to_lo(), "(".to_string()), - (span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ); - } - BuiltinLintDiag::NamedAsmLabel(help) => { - diag.help(help); - diag.note("see the asm section of Rust By Example for more information"); + .decorate_lint(diag); } BuiltinLintDiag::UnexpectedCfgName(name, value) => { - check_cfg::unexpected_cfg_name(sess, diag, name, value) + check_cfg::unexpected_cfg_name(sess, name, value).decorate_lint(diag); } BuiltinLintDiag::UnexpectedCfgValue(name, value) => { - check_cfg::unexpected_cfg_value(sess, diag, name, value) + check_cfg::unexpected_cfg_value(sess, name, value).decorate_lint(diag); } - BuiltinLintDiag::DeprecatedWhereclauseLocation(sugg) => { - let left_sp = diag.span.primary_span().unwrap(); - match sugg { - Some((right_sp, sugg)) => diag.multipart_suggestion( - "move it to the end of the type declaration", - vec![(left_sp, String::new()), (right_sp, sugg)], - Applicability::MachineApplicable, - ), - None => diag.span_suggestion( - left_sp, - "remove this `where`", - "", - Applicability::MachineApplicable, - ), + BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => { + let suggestion = match sugg { + Some((right_sp, sugg)) => lints::DeprecatedWhereClauseLocationSugg::MoveToEnd { + left: left_sp, + right: right_sp, + sugg, + }, + None => lints::DeprecatedWhereClauseLocationSugg::RemoveWhere { span: left_sp }, }; - diag.note("see issue #89122 for more information"); + lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag); + } + BuiltinLintDiag::MissingUnsafeOnExtern { suggestion } => { + lints::MissingUnsafeOnExtern { suggestion }.decorate_lint(diag); } BuiltinLintDiag::SingleUseLifetime { param_span, use_span: Some((use_span, elide)), deletion_span, + ident, } => { debug!(?param_span, ?use_span, ?deletion_span); - diag.span_label(param_span, "this lifetime..."); - diag.span_label(use_span, "...is used only here"); - if let Some(deletion_span) = deletion_span { - let msg = "elide the single-use lifetime"; + let suggestion = if let Some(deletion_span) = deletion_span { let (use_span, replace_lt) = if elide { let use_span = sess.source_map().span_extend_while_whitespace(use_span); (use_span, String::new()) @@ -226,24 +223,19 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di // issue 107998 for the case such as a wrong function pointer type // `deletion_span` is empty and there is no need to report lifetime uses here - let suggestions = if deletion_span.is_empty() { - vec![(use_span, replace_lt)] - } else { - vec![(deletion_span, String::new()), (use_span, replace_lt)] - }; - diag.multipart_suggestion(msg, suggestions, Applicability::MachineApplicable); - } + let deletion_span = + if deletion_span.is_empty() { None } else { Some(deletion_span) }; + Some(lints::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt }) + } else { + None + }; + + lints::SingleUseLifetime { suggestion, param_span, use_span, ident } + .decorate_lint(diag); } - BuiltinLintDiag::SingleUseLifetime { param_span: _, use_span: None, deletion_span } => { + BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => { debug!(?deletion_span); - if let Some(deletion_span) = deletion_span { - diag.span_suggestion( - deletion_span, - "elide the unused lifetime", - "", - Applicability::MachineApplicable, - ); - } + lints::UnusedLifetime { deletion_span, ident }.decorate_lint(diag); } BuiltinLintDiag::NamedArgumentUsedPositionally { position_sp_to_replace, @@ -252,19 +244,12 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di named_arg_name, is_formatting_arg, } => { - diag.span_label( - named_arg_sp, - "this named argument is referred to by position in formatting string", - ); - if let Some(positional_arg_for_msg) = position_sp_for_msg { - let msg = format!( - "this formatting argument uses named argument `{named_arg_name}` by position" - ); - diag.span_label(positional_arg_for_msg, msg); - } - - if let Some(positional_arg_to_replace) = position_sp_to_replace { - let name = if is_formatting_arg { named_arg_name + "$" } else { named_arg_name }; + let (suggestion, name) = if let Some(positional_arg_to_replace) = position_sp_to_replace + { + let mut name = named_arg_name.clone(); + if is_formatting_arg { + name.push('$') + }; let span_to_replace = if let Ok(positional_arg_content) = sess.source_map().span_to_snippet(positional_arg_to_replace) && positional_arg_content.starts_with(':') @@ -273,31 +258,34 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di } else { positional_arg_to_replace }; - diag.span_suggestion_verbose( - span_to_replace, - "use the named argument by name to avoid ambiguity", - name, - Applicability::MaybeIncorrect, - ); + (Some(span_to_replace), name) + } else { + (None, String::new()) + }; + + lints::NamedArgumentUsedPositionally { + named_arg_sp, + position_label_sp: position_sp_for_msg, + suggestion, + name, + named_arg_name, } + .decorate_lint(diag); } - BuiltinLintDiag::ByteSliceInPackedStructWithDerive => { - diag.help("consider implementing the trait by hand, or remove the `packed` attribute"); + BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => { + lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag); } BuiltinLintDiag::UnusedExternCrate { removal_span } => { - diag.span_suggestion(removal_span, "remove it", "", Applicability::MachineApplicable); + lints::UnusedExternCrate { removal_span }.decorate_lint(diag); } BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => { let suggestion_span = vis_span.between(ident_span); - diag.span_suggestion_verbose( - suggestion_span, - "convert it to a `use`", - if vis_span.is_empty() { "use " } else { " use " }, - Applicability::MachineApplicable, - ); + let code = if vis_span.is_empty() { "use " } else { " use " }; + + lints::ExternCrateNotIdiomatic { span: suggestion_span, code }.decorate_lint(diag); } BuiltinLintDiag::AmbiguousGlobImports { diag: ambiguity } => { - rustc_errors::report_ambiguity_error(diag, ambiguity); + lints::AmbiguousGlobImports { ambiguity }.decorate_lint(diag); } BuiltinLintDiag::AmbiguousGlobReexports { name, @@ -305,16 +293,13 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di first_reexport_span, duplicate_reexport_span, } => { - diag.span_label( - first_reexport_span, - format!("the name `{name}` in the {namespace} namespace is first re-exported here"), - ); - diag.span_label( - duplicate_reexport_span, - format!( - "but the name `{name}` in the {namespace} namespace is also re-exported here" - ), - ); + lints::AmbiguousGlobReexports { + first_reexport: first_reexport_span, + duplicate_reexport: duplicate_reexport_span, + name, + namespace, + } + .decorate_lint(diag); } BuiltinLintDiag::HiddenGlobReexports { name, @@ -322,38 +307,122 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di glob_reexport_span, private_item_span, } => { - diag.span_note(glob_reexport_span, format!("the name `{name}` in the {namespace} namespace is supposed to be publicly re-exported here")); - diag.span_note(private_item_span, "but the private item here shadows it".to_owned()); + lints::HiddenGlobReexports { + glob_reexport: glob_reexport_span, + private_item: private_item_span, + + name, + namespace, + } + .decorate_lint(diag); } BuiltinLintDiag::UnusedQualifications { removal_span } => { - diag.span_suggestion_verbose( - removal_span, - "remove the unnecessary path segments", - "", - Applicability::MachineApplicable, - ); + lints::UnusedQualifications { removal_span }.decorate_lint(diag); } - BuiltinLintDiag::AssociatedConstElidedLifetime { elided, span } => { - diag.span_suggestion_verbose( - if elided { span.shrink_to_hi() } else { span }, - "use the `'static` lifetime", - if elided { "'static " } else { "'static" }, - Applicability::MachineApplicable, - ); + BuiltinLintDiag::AssociatedConstElidedLifetime { + elided, + span: lt_span, + lifetimes_in_scope, + } => { + let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span }; + let code = if elided { "'static " } else { "'static" }; + lints::AssociatedConstElidedLifetime { + span: lt_span, + code, + elided, + lifetimes_in_scope, + } + .decorate_lint(diag); } - BuiltinLintDiag::RedundantImportVisibility { max_vis, span } => { - diag.span_note(span, format!("the most public imported item is `{max_vis}`")); - diag.help( - "reduce the glob import's visibility or increase visibility of imported items", - ); + BuiltinLintDiag::RedundantImportVisibility { max_vis, span: vis_span, import_vis } => { + lints::RedundantImportVisibility { span: vis_span, help: (), max_vis, import_vis } + .decorate_lint(diag); } - BuiltinLintDiag::MaybeTypo { span, name } => { - diag.span_suggestion_verbose( - span, - "an attribute with a similar name exists", - name, - Applicability::MachineApplicable, - ); + BuiltinLintDiag::UnknownDiagnosticAttribute { span: typo_span, typo_name } => { + let typo = typo_name.map(|typo_name| lints::UnknownDiagnosticAttributeTypoSugg { + span: typo_span, + typo_name, + }); + lints::UnknownDiagnosticAttribute { typo }.decorate_lint(diag); } + BuiltinLintDiag::MacroUseDeprecated => { + lints::MacroUseDeprecated.decorate_lint(diag); + } + BuiltinLintDiag::UnusedMacroUse => lints::UnusedMacroUse.decorate_lint(diag), + BuiltinLintDiag::PrivateExternCrateReexport { source: ident, extern_crate_span } => { + lints::PrivateExternCrateReexport { ident, sugg: extern_crate_span.shrink_to_lo() } + .decorate_lint(diag); + } + BuiltinLintDiag::UnusedLabel => lints::UnusedLabel.decorate_lint(diag), + BuiltinLintDiag::MacroIsPrivate(ident) => { + lints::MacroIsPrivate { ident }.decorate_lint(diag); + } + BuiltinLintDiag::UnusedMacroDefinition(name) => { + lints::UnusedMacroDefinition { name }.decorate_lint(diag); + } + BuiltinLintDiag::MacroRuleNeverUsed(n, name) => { + lints::MacroRuleNeverUsed { n: n + 1, name }.decorate_lint(diag); + } + BuiltinLintDiag::UnstableFeature(msg) => { + lints::UnstableFeature { msg }.decorate_lint(diag); + } + BuiltinLintDiag::AvoidUsingIntelSyntax => { + lints::AvoidIntelSyntax.decorate_lint(diag); + } + BuiltinLintDiag::AvoidUsingAttSyntax => { + lints::AvoidAttSyntax.decorate_lint(diag); + } + BuiltinLintDiag::IncompleteInclude => { + lints::IncompleteInclude.decorate_lint(diag); + } + BuiltinLintDiag::UnnameableTestItems => { + lints::UnnameableTestItems.decorate_lint(diag); + } + BuiltinLintDiag::DuplicateMacroAttribute => { + lints::DuplicateMacroAttribute.decorate_lint(diag); + } + BuiltinLintDiag::CfgAttrNoAttributes => { + lints::CfgAttrNoAttributes.decorate_lint(diag); + } + BuiltinLintDiag::CrateTypeInCfgAttr => { + lints::CrateTypeInCfgAttr.decorate_lint(diag); + } + BuiltinLintDiag::CrateNameInCfgAttr => { + lints::CrateNameInCfgAttr.decorate_lint(diag); + } + BuiltinLintDiag::MissingFragmentSpecifier => { + lints::MissingFragmentSpecifier.decorate_lint(diag); + } + BuiltinLintDiag::MetaVariableStillRepeating(name) => { + lints::MetaVariableStillRepeating { name }.decorate_lint(diag); + } + BuiltinLintDiag::MetaVariableWrongOperator => { + lints::MetaVariableWrongOperator.decorate_lint(diag); + } + BuiltinLintDiag::DuplicateMatcherBinding => { + lints::DuplicateMatcherBinding.decorate_lint(diag); + } + BuiltinLintDiag::UnknownMacroVariable(name) => { + lints::UnknownMacroVariable { name }.decorate_lint(diag); + } + BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => { + lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag) + } + BuiltinLintDiag::WasmCAbi => lints::WasmCAbi.decorate_lint(diag), + BuiltinLintDiag::IllFormedAttributeInput { suggestions } => { + lints::IllFormedAttributeInput { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + } + .decorate_lint(diag) + } + BuiltinLintDiag::InnerAttributeUnstable { is_macro } => if is_macro { + lints::InnerAttributeUnstable::InnerMacroAttribute + } else { + lints::InnerAttributeUnstable::CustomInnerAttribute + } + .decorate_lint(diag), } } diff --git a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs index 3c423b4e2aa6..c69e680cb64f 100644 --- a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs +++ b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs @@ -1,52 +1,68 @@ -use rustc_errors::{Applicability, Diag}; use rustc_middle::bug; use rustc_session::{config::ExpectedValues, Session}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{sym, Span, Symbol}; +use crate::lints; + const MAX_CHECK_CFG_NAMES_OR_VALUES: usize = 35; -fn check_cfg_expected_note( +fn sort_and_truncate_possibilities( sess: &Session, - possibilities: &[Symbol], - type_: &str, - name: Option, - suffix: &str, -) -> String { - use std::fmt::Write; - + mut possibilities: Vec, +) -> (Vec, usize) { let n_possibilities = if sess.opts.unstable_opts.check_cfg_all_expected { possibilities.len() } else { std::cmp::min(possibilities.len(), MAX_CHECK_CFG_NAMES_OR_VALUES) }; - let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::>(); - possibilities.sort(); + possibilities.sort_by(|s1, s2| s1.as_str().cmp(s2.as_str())); let and_more = possibilities.len().saturating_sub(n_possibilities); - let possibilities = possibilities[..n_possibilities].join("`, `"); + possibilities.truncate(n_possibilities); + (possibilities, and_more) +} - let mut note = String::with_capacity(50 + possibilities.len()); +enum EscapeQuotes { + Yes, + No, +} - write!(&mut note, "expected {type_}").unwrap(); - if let Some(name) = name { - write!(&mut note, " for `{name}`").unwrap(); - } - write!(&mut note, " are: {suffix}`{possibilities}`").unwrap(); - if and_more > 0 { - write!(&mut note, " and {and_more} more").unwrap(); +fn to_check_cfg_arg(name: Symbol, value: Option, quotes: EscapeQuotes) -> String { + if let Some(value) = value { + let value = str::escape_debug(value.as_str()).to_string(); + let values = match quotes { + EscapeQuotes::Yes => format!("\\\"{}\\\"", value.replace("\"", "\\\\\\\\\"")), + EscapeQuotes::No => format!("\"{value}\""), + }; + format!("cfg({name}, values({values}))") + } else { + format!("cfg({name})") } +} - note +fn cargo_help_sub( + sess: &Session, + inst: &impl Fn(EscapeQuotes) -> String, +) -> lints::UnexpectedCfgCargoHelp { + // We don't want to suggest the `build.rs` way to expected cfgs if we are already in a + // `build.rs`. We therefor do a best effort check (looking if the `--crate-name` is + // `build_script_build`) to try to figure out if we are building a Cargo build script + + let unescaped = &inst(EscapeQuotes::No); + if matches!(&sess.opts.crate_name, Some(crate_name) if crate_name == "build_script_build") { + lints::UnexpectedCfgCargoHelp::lint_cfg(unescaped) + } else { + lints::UnexpectedCfgCargoHelp::lint_cfg_and_build_rs(unescaped, &inst(EscapeQuotes::Yes)) + } } pub(super) fn unexpected_cfg_name( sess: &Session, - diag: &mut Diag<'_, ()>, (name, name_span): (Symbol, Span), value: Option<(Symbol, Span)>, -) { +) -> lints::UnexpectedCfgName { #[allow(rustc::potential_query_instability)] let possibilities: Vec = sess.psess.check_config.expecteds.keys().copied().collect(); @@ -69,116 +85,115 @@ pub(super) fn unexpected_cfg_name( let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); let mut is_feature_cfg = name == sym::feature; - if is_feature_cfg && is_from_cargo { - diag.help("consider defining some features in `Cargo.toml`"); + let code_sugg = if is_feature_cfg && is_from_cargo { + lints::unexpected_cfg_name::CodeSuggestion::DefineFeatures // Suggest the most probable if we found one } else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) { + is_feature_cfg |= best_match == sym::feature; + if let Some(ExpectedValues::Some(best_match_values)) = sess.psess.check_config.expecteds.get(&best_match) { // We will soon sort, so the initial order does not matter. #[allow(rustc::potential_query_instability)] - let mut possibilities = - best_match_values.iter().flatten().map(Symbol::as_str).collect::>(); - possibilities.sort(); + let mut possibilities = best_match_values.iter().flatten().collect::>(); + possibilities.sort_by_key(|s| s.as_str()); + + let get_possibilities_sub = || { + if !possibilities.is_empty() { + let possibilities = + possibilities.iter().copied().cloned().collect::>().into(); + Some(lints::unexpected_cfg_name::ExpectedValues { best_match, possibilities }) + } else { + None + } + }; - let mut should_print_possibilities = true; if let Some((value, value_span)) = value { if best_match_values.contains(&Some(value)) { - diag.span_suggestion( - name_span, - "there is a config with a similar name and value", - best_match, - Applicability::MaybeIncorrect, - ); - should_print_possibilities = false; + lints::unexpected_cfg_name::CodeSuggestion::SimilarNameAndValue { + span: name_span, + code: best_match.to_string(), + } } else if best_match_values.contains(&None) { - diag.span_suggestion( - name_span.to(value_span), - "there is a config with a similar name and no value", - best_match, - Applicability::MaybeIncorrect, - ); - should_print_possibilities = false; + lints::unexpected_cfg_name::CodeSuggestion::SimilarNameNoValue { + span: name_span.to(value_span), + code: best_match.to_string(), + } } else if let Some(first_value) = possibilities.first() { - diag.span_suggestion( - name_span.to(value_span), - "there is a config with a similar name and different values", - format!("{best_match} = \"{first_value}\""), - Applicability::MaybeIncorrect, - ); + lints::unexpected_cfg_name::CodeSuggestion::SimilarNameDifferentValues { + span: name_span.to(value_span), + code: format!("{best_match} = \"{first_value}\""), + expected: get_possibilities_sub(), + } } else { - diag.span_suggestion( - name_span.to(value_span), - "there is a config with a similar name and different values", - best_match, - Applicability::MaybeIncorrect, - ); - }; + lints::unexpected_cfg_name::CodeSuggestion::SimilarNameDifferentValues { + span: name_span.to(value_span), + code: best_match.to_string(), + expected: get_possibilities_sub(), + } + } } else { - diag.span_suggestion( - name_span, - "there is a config with a similar name", - best_match, - Applicability::MaybeIncorrect, - ); - } - - if !possibilities.is_empty() && should_print_possibilities { - let possibilities = possibilities.join("`, `"); - diag.help(format!("expected values for `{best_match}` are: `{possibilities}`")); + lints::unexpected_cfg_name::CodeSuggestion::SimilarName { + span: name_span, + code: best_match.to_string(), + expected: get_possibilities_sub(), + } } } else { - diag.span_suggestion( - name_span, - "there is a config with a similar name", - best_match, - Applicability::MaybeIncorrect, - ); - } - - is_feature_cfg |= best_match == sym::feature; - } else { - if !names_possibilities.is_empty() && names_possibilities.len() <= 3 { - names_possibilities.sort(); - for cfg_name in names_possibilities.iter() { - diag.span_suggestion( - name_span, - "found config with similar value", - format!("{cfg_name} = \"{name}\""), - Applicability::MaybeIncorrect, - ); + lints::unexpected_cfg_name::CodeSuggestion::SimilarName { + span: name_span, + code: best_match.to_string(), + expected: None, } } - if !possibilities.is_empty() { - diag.help_once(check_cfg_expected_note(sess, &possibilities, "names", None, "")); - } - } - - let inst = if let Some((value, _value_span)) = value { - let pre = if is_from_cargo { "\\" } else { "" }; - format!("cfg({name}, values({pre}\"{value}{pre}\"))") } else { - format!("cfg({name})") + let similar_values = if !names_possibilities.is_empty() && names_possibilities.len() <= 3 { + names_possibilities.sort(); + names_possibilities + .iter() + .map(|cfg_name| lints::unexpected_cfg_name::FoundWithSimilarValue { + span: name_span, + code: format!("{cfg_name} = \"{name}\""), + }) + .collect() + } else { + vec![] + }; + let expected_names = if !possibilities.is_empty() { + let (possibilities, and_more) = sort_and_truncate_possibilities(sess, possibilities); + Some(lints::unexpected_cfg_name::ExpectedNames { + possibilities: possibilities.into(), + and_more, + }) + } else { + None + }; + lints::unexpected_cfg_name::CodeSuggestion::SimilarValues { + with_similar_values: similar_values, + expected_names, + } }; - if is_from_cargo { - if !is_feature_cfg { - diag.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo::rustc-check-cfg={inst}\");` to the top of the `build.rs`")); - } - diag.note("see for more information about checking conditional configuration"); + let inst = |escape_quotes| to_check_cfg_arg(name, value.map(|(v, _s)| v), escape_quotes); + + let invocation_help = if is_from_cargo { + let sub = if !is_feature_cfg { Some(cargo_help_sub(sess, &inst)) } else { None }; + lints::unexpected_cfg_name::InvocationHelp::Cargo { sub } } else { - diag.help(format!("to expect this configuration use `--check-cfg={inst}`")); - diag.note("see for more information about checking conditional configuration"); - } + lints::unexpected_cfg_name::InvocationHelp::Rustc(lints::UnexpectedCfgRustcHelp::new( + &inst(EscapeQuotes::No), + )) + }; + + lints::UnexpectedCfgName { code_sugg, invocation_help, name } } pub(super) fn unexpected_cfg_value( sess: &Session, - diag: &mut Diag<'_, ()>, (name, name_span): (Symbol, Span), value: Option<(Symbol, Span)>, -) { +) -> lints::UnexpectedCfgValue { let Some(ExpectedValues::Some(values)) = &sess.psess.check_config.expecteds.get(&name) else { bug!( "it shouldn't be possible to have a diagnostic on a value whose name is not in values" @@ -198,81 +213,87 @@ pub(super) fn unexpected_cfg_value( // Show the full list if all possible values for a given name, but don't do it // for names as the possibilities could be very long - if !possibilities.is_empty() { - diag.note(check_cfg_expected_note( - sess, - &possibilities, - "values", - Some(name), - if have_none_possibility { "(none), " } else { "" }, - )); + let code_sugg = if !possibilities.is_empty() { + let expected_values = { + let (possibilities, and_more) = + sort_and_truncate_possibilities(sess, possibilities.clone()); + lints::unexpected_cfg_value::ExpectedValues { + name, + have_none_possibility, + possibilities: possibilities.into(), + and_more, + } + }; - if let Some((value, value_span)) = value { + let suggestion = if let Some((value, value_span)) = value { // Suggest the most probable if we found one if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) { - diag.span_suggestion( - value_span, - "there is a expected value with a similar name", - format!("\"{best_match}\""), - Applicability::MaybeIncorrect, - ); + Some(lints::unexpected_cfg_value::ChangeValueSuggestion::SimilarName { + span: value_span, + best_match, + }) + } else { + None } } else if let &[first_possibility] = &possibilities[..] { - diag.span_suggestion( - name_span.shrink_to_hi(), - "specify a config value", - format!(" = \"{first_possibility}\""), - Applicability::MaybeIncorrect, - ); - } - } else if have_none_possibility { - diag.note(format!("no expected value for `{name}`")); - if let Some((_value, value_span)) = value { - diag.span_suggestion( - name_span.shrink_to_hi().to(value_span), - "remove the value", - "", - Applicability::MaybeIncorrect, - ); - } - } else { - diag.note(format!("no expected values for `{name}`")); + Some(lints::unexpected_cfg_value::ChangeValueSuggestion::SpecifyValue { + span: name_span.shrink_to_hi(), + first_possibility, + }) + } else { + None + }; - let sp = if let Some((_value, value_span)) = value { + lints::unexpected_cfg_value::CodeSuggestion::ChangeValue { expected_values, suggestion } + } else if have_none_possibility { + let suggestion = + value.map(|(_value, value_span)| lints::unexpected_cfg_value::RemoveValueSuggestion { + span: name_span.shrink_to_hi().to(value_span), + }); + lints::unexpected_cfg_value::CodeSuggestion::RemoveValue { suggestion, name } + } else { + let span = if let Some((_value, value_span)) = value { name_span.to(value_span) } else { name_span }; - diag.span_suggestion(sp, "remove the condition", "", Applicability::MaybeIncorrect); - } + let suggestion = lints::unexpected_cfg_value::RemoveConditionSuggestion { span }; + lints::unexpected_cfg_value::CodeSuggestion::RemoveCondition { suggestion, name } + }; // We don't want to suggest adding values to well known names // since those are defined by rustc it-self. Users can still // do it if they want, but should not encourage them. let is_cfg_a_well_know_name = sess.psess.check_config.well_known_names.contains(&name); - let inst = if let Some((value, _value_span)) = value { - let pre = if is_from_cargo { "\\" } else { "" }; - format!("cfg({name}, values({pre}\"{value}{pre}\"))") - } else { - format!("cfg({name})") - }; + let inst = |escape_quotes| to_check_cfg_arg(name, value.map(|(v, _s)| v), escape_quotes); - if is_from_cargo { - if name == sym::feature { + let invocation_help = if is_from_cargo { + let help = if name == sym::feature { if let Some((value, _value_span)) = value { - diag.help(format!("consider adding `{value}` as a feature in `Cargo.toml`")); + Some(lints::unexpected_cfg_value::CargoHelp::AddFeature { value }) } else { - diag.help("consider defining some features in `Cargo.toml`"); + Some(lints::unexpected_cfg_value::CargoHelp::DefineFeatures) } } else if !is_cfg_a_well_know_name { - diag.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo::rustc-check-cfg={inst}\");` to the top of the `build.rs`")); - } - diag.note("see for more information about checking conditional configuration"); + Some(lints::unexpected_cfg_value::CargoHelp::Other(cargo_help_sub(sess, &inst))) + } else { + None + }; + lints::unexpected_cfg_value::InvocationHelp::Cargo(help) } else { - if !is_cfg_a_well_know_name { - diag.help(format!("to expect this configuration use `--check-cfg={inst}`")); - } - diag.note("see for more information about checking conditional configuration"); + let help = if !is_cfg_a_well_know_name { + Some(lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No))) + } else { + None + }; + lints::unexpected_cfg_value::InvocationHelp::Rustc(help) + }; + + lints::UnexpectedCfgValue { + code_sugg, + invocation_help, + has_value: value.is_some(), + value: value.map_or_else(String::new, |(v, _span)| v.to_string()), } } diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index b671dfaa0d1b..911975f61791 100644 --- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -3,7 +3,7 @@ use crate::{ LateContext, LateLintPass, LintContext, }; -use rustc_hir as hir; +use rustc_hir::{self as hir, LangItem}; use rustc_middle::ty; use rustc_session::lint::FutureIncompatibilityReason; use rustc_session::{declare_lint, declare_lint_pass}; @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { // the trait is a `Deref` implementation && let Some(trait_) = &impl_.of_trait && let Some(did) = trait_.trait_def_id() - && Some(did) == tcx.lang_items().deref_trait() + && tcx.is_lang_item(did, LangItem::Deref) // the self type is `dyn t_principal` && let self_ty = tcx.type_of(item.owner_id).instantiate_identity() && let ty::Dynamic(data, _, ty::Dyn) = self_ty.kind() diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs index 60b799f3c746..eea0898d83fa 100644 --- a/compiler/rustc_lint/src/drop_forget_useless.rs +++ b/compiler/rustc_lint/src/drop_forget_useless.rs @@ -1,4 +1,4 @@ -use rustc_hir::{Arm, Expr, ExprKind, Node}; +use rustc_hir::{Arm, Expr, ExprKind, Node, StmtKind}; use rustc_middle::ty; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; @@ -6,7 +6,7 @@ use rustc_span::sym; use crate::{ lints::{ DropCopyDiag, DropRefDiag, ForgetCopyDiag, ForgetRefDiag, UndroppedManuallyDropsDiag, - UndroppedManuallyDropsSuggestion, + UndroppedManuallyDropsSuggestion, UseLetUnderscoreIgnoreSuggestion, }, LateContext, LateLintPass, LintContext, }; @@ -148,33 +148,59 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetUseless { let arg_ty = cx.typeck_results().expr_ty(arg); let is_copy = arg_ty.is_copy_modulo_regions(cx.tcx, cx.param_env); let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); + let let_underscore_ignore_sugg = || { + if let Some((_, node)) = cx.tcx.hir().parent_iter(expr.hir_id).nth(0) + && let Node::Stmt(stmt) = node + && let StmtKind::Semi(e) = stmt.kind + && e.hir_id == expr.hir_id + { + UseLetUnderscoreIgnoreSuggestion::Suggestion { + start_span: expr.span.shrink_to_lo().until(arg.span), + end_span: arg.span.shrink_to_hi().until(expr.span.shrink_to_hi()), + } + } else { + UseLetUnderscoreIgnoreSuggestion::Note + } + }; match fn_name { sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => { cx.emit_span_lint( DROPPING_REFERENCES, expr.span, - DropRefDiag { arg_ty, label: arg.span }, + DropRefDiag { arg_ty, label: arg.span, sugg: let_underscore_ignore_sugg() }, ); } sym::mem_forget if arg_ty.is_ref() => { cx.emit_span_lint( FORGETTING_REFERENCES, expr.span, - ForgetRefDiag { arg_ty, label: arg.span }, + ForgetRefDiag { + arg_ty, + label: arg.span, + sugg: let_underscore_ignore_sugg(), + }, ); } sym::mem_drop if is_copy && !drop_is_single_call_in_arm => { cx.emit_span_lint( DROPPING_COPY_TYPES, expr.span, - DropCopyDiag { arg_ty, label: arg.span }, + DropCopyDiag { + arg_ty, + label: arg.span, + sugg: let_underscore_ignore_sugg(), + }, ); } sym::mem_forget if is_copy => { cx.emit_span_lint( FORGETTING_COPY_TYPES, expr.span, - ForgetCopyDiag { arg_ty, label: arg.span }, + ForgetCopyDiag { + arg_ty, + label: arg.span, + sugg: let_underscore_ignore_sugg(), + }, ); } sym::mem_drop diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 3f627baf7704..329221612b58 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -14,7 +14,7 @@ //! upon. As the ast is traversed, this keeps track of the current lint level //! for all lint attributes. -use crate::context::{EarlyContext, LintContext, LintStore}; +use crate::context::{EarlyContext, LintStore}; use crate::passes::{EarlyLintPass, EarlyLintPassObject}; use rustc_ast::ptr::P; use rustc_ast::visit::{self as ast_visit, walk_list, Visitor}; @@ -26,6 +26,7 @@ use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::Span; +use tracing::debug; macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({ $cx.pass.$f(&$cx.context, $($args),*); @@ -44,14 +45,8 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { #[allow(rustc::diagnostic_outside_of_impl)] fn inlined_check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { - let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint; - self.context.span_lint_with_diagnostics( - lint_id.lint, - Some(span), - msg, - |_| {}, - diagnostic, - ); + let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint; + self.context.span_lint_with_diagnostics(lint_id.lint, span, diagnostic); } } diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs index c23d1221bc86..46dfaf0b83f9 100644 --- a/compiler/rustc_lint/src/errors.rs +++ b/compiler/rustc_lint/src/errors.rs @@ -16,7 +16,7 @@ pub struct OverruledAttribute<'a> { #[subdiagnostic] pub sub: OverruledAttributeSub, } -// + pub enum OverruledAttributeSub { DefaultSource { id: String }, NodeSource { span: Span, reason: Option }, diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index a6876d8aae79..aa00fb4573d8 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -52,14 +52,29 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { let ty = cx.typeck_results().expr_ty(arg); - let &ty::Adt(adt, args) = ty.kind() else { return }; + let (adt, args, ref_mutability) = match ty.kind() { + &ty::Adt(adt, args) => (adt, args, None), + &ty::Ref(_, ty, mutability) => match ty.kind() { + &ty::Adt(adt, args) => (adt, args, Some(mutability)), + _ => return, + }, + _ => return, + }; let (article, ty, var) = match adt.did() { + did if cx.tcx.is_diagnostic_item(sym::Option, did) && ref_mutability.is_some() => { + ("a", "Option", "Some") + } did if cx.tcx.is_diagnostic_item(sym::Option, did) => ("an", "Option", "Some"), did if cx.tcx.is_diagnostic_item(sym::Result, did) => ("a", "Result", "Ok"), _ => return, }; + let ref_prefix = match ref_mutability { + None => "", + Some(ref_mutability) => ref_mutability.ref_prefix_str(), + }; + let sub = if let Some(recv) = extract_iterator_next_call(cx, arg) && let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span) { @@ -85,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { cx.emit_span_lint( FOR_LOOPS_OVER_FALLIBLES, arg.span, - ForLoopsOverFalliblesDiag { article, ty, sub, question_mark, suggestion }, + ForLoopsOverFalliblesDiag { article, ref_prefix, ty, sub, question_mark, suggestion }, ); } } diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 2c86964feef1..5da1cbc2283b 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -8,6 +8,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::declare_lint; use rustc_span::{sym, Span, Symbol}; use rustc_target::abi::FIRST_VARIANT; +use tracing::{debug, instrument}; use crate::lints::{BuiltinClashingExtern, BuiltinClashingExternSub}; use crate::{types, LintVec}; diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index 30bf80b915b8..d4f6d388d9fe 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -5,13 +5,13 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_macros::LintDiagnostic; +use rustc_middle::bug; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; -use rustc_middle::{bug, span_bug}; use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{sym, Span}; use crate::fluent_generated as fluent; use crate::{LateContext, LateLintPass}; @@ -53,7 +53,7 @@ declare_lint! { /// while the `impl Display` is live. /// /// To fix this, we can explicitly state that the `impl Display` doesn't - /// capture any lifetimes, using `impl use<> Display`. + /// capture any lifetimes, using `impl Display + use<>`. pub IMPL_TRAIT_OVERCAPTURES, Allow, "`impl Trait` will capture more lifetimes than possibly intended in edition 2024", @@ -79,7 +79,7 @@ declare_lint! { /// # #![feature(precise_capturing, lifetime_capture_rules_2024)] /// # #![allow(incomplete_features)] /// # #![deny(impl_trait_redundant_captures)] - /// fn test<'a>(x: &'a i32) -> impl use<'a> Sized { x } + /// fn test<'a>(x: &'a i32) -> impl Sized + use<'a> { x } /// ``` /// /// {{produces}} @@ -249,7 +249,7 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { // If we have uncaptured args, and if the opaque doesn't already have // `use<>` syntax on it, and we're < edition 2024, then warn the user. if !new_capture_rules - && opaque.precise_capturing_args.is_none() + && !opaque.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Use(..))) && !uncaptured_spans.is_empty() { let suggestion = if let Ok(snippet) = @@ -268,8 +268,8 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { // Make sure that we're not trying to name any APITs if generics.iter().all(|name| !name.starts_with("impl ")) { Some(( - format!(" use<{}>", generics.join(", ")), - opaque_span.with_lo(opaque_span.lo() + BytePos(4)).shrink_to_lo(), + format!(" + use<{}>", generics.join(", ")), + opaque_span.shrink_to_hi(), )) } else { None @@ -294,7 +294,11 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { // have no uncaptured args, then we should warn to the user that // it's redundant to capture all args explicitly. else if new_capture_rules - && let Some((captured_args, capturing_span)) = opaque.precise_capturing_args + && let Some((captured_args, capturing_span)) = + opaque.bounds.iter().find_map(|bound| match *bound { + hir::GenericBound::Use(a, s) => Some((a, s)), + _ => None, + }) { let mut explicitly_captured = UnordSet::default(); for arg in captured_args { @@ -303,20 +307,12 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { ResolvedArg::EarlyBound(def_id) | ResolvedArg::LateBound(_, _, def_id), ) => { if self.tcx.def_kind(self.tcx.parent(def_id)) == DefKind::OpaqueTy { - let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) - | ty::ReLateParam(ty::LateParamRegion { - bound_region: ty::BoundRegionKind::BrNamed(def_id, _), - .. - })) = self + let def_id = self .tcx .map_opaque_lifetime_to_parent_lifetime(def_id.expect_local()) - .kind() - else { - span_bug!( - self.tcx.def_span(def_id), - "variable should have been duplicated from a parent" - ); - }; + .opt_param_def_id(self.tcx, self.parent_def_id.to_def_id()) + .expect("variable should have been duplicated from parent"); + explicitly_captured.insert(def_id); } else { explicitly_captured.insert(def_id); @@ -369,6 +365,7 @@ struct ImplTraitOvercapturesLint<'tcx> { impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> { fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { + diag.primary_message(fluent::lint_impl_trait_overcaptures); diag.arg("self_ty", self.self_ty.to_string()) .arg("num_captured", self.num_captured) .span_note(self.uncaptured_spans, fluent::lint_note) @@ -382,10 +379,6 @@ impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> { ); } } - - fn msg(&self) -> rustc_errors::DiagMessage { - fluent::lint_impl_trait_overcaptures - } } #[derive(LintDiagnostic)] diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 153d91ce28cb..9110cccdc46f 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -15,13 +15,14 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; +use tracing::debug; declare_tool_lint! { - /// The `default_hash_type` lint detects use of [`std::collections::HashMap`]/[`std::collections::HashSet`], - /// suggesting the use of `FxHashMap`/`FxHashSet`. + /// The `default_hash_type` lint detects use of [`std::collections::HashMap`] and + /// [`std::collections::HashSet`], suggesting the use of `FxHashMap`/`FxHashSet`. /// - /// This can help as `FxHasher` can perform better than the default hasher. DOS protection is not - /// required as input is assumed to be trusted. + /// This can help as `FxHasher` can perform better than the default hasher. DOS protection is + /// not required as input is assumed to be trusted. pub rustc::DEFAULT_HASH_TYPES, Allow, "forbid HashMap and HashSet and suggest the FxHash* variants", @@ -34,7 +35,7 @@ impl LateLintPass<'_> for DefaultHashTypes { fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) { let Res::Def(rustc_hir::def::DefKind::Struct, def_id) = path.res else { return }; if matches!(cx.tcx.hir_node(hir_id), Node::Item(Item { kind: ItemKind::Use(..), .. })) { - // don't lint imports, only actual usages + // Don't lint imports, only actual usages. return; } let preferred = match cx.tcx.get_diagnostic_name(def_id) { @@ -74,8 +75,8 @@ declare_tool_lint! { /// potential query instability, such as iterating over a `HashMap`. /// /// Due to the [incremental compilation](https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation.html) model, - /// queries must return deterministic, stable results. `HashMap` iteration order can change between compilations, - /// and will introduce instability if query results expose the order. + /// queries must return deterministic, stable results. `HashMap` iteration order can change + /// between compilations, and will introduce instability if query results expose the order. pub rustc::POTENTIAL_QUERY_INSTABILITY, Allow, "require explicit opt-in when using potentially unstable methods or functions", diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index ca188277b9de..aa328fb87b27 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -26,9 +26,9 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::LintPass; use rustc_session::Session; use rustc_span::Span; - use std::any::Any; use std::cell::Cell; +use tracing::debug; /// Extract the [`LintStore`] from [`Session`]. /// @@ -125,7 +125,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas }); } - fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) { + fn visit_body(&mut self, body: &hir::Body<'tcx>) { lint_callback!(self, check_body, body); hir_visit::walk_body(self, body); lint_callback!(self, check_body_post, body); diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index ea82fb9f2625..e6c274ec09a7 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -113,11 +113,11 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { let mut top_level = true; - // We recursively walk through all patterns, so that we can catch cases where the lock is nested in a pattern. - // For the basic `let_underscore_drop` lint, we only look at the top level, since there are many legitimate reasons - // to bind a sub-pattern to an `_`, if we're only interested in the rest. - // But with locks, we prefer having the chance of "false positives" over missing cases, since the effects can be - // quite catastrophic. + // We recursively walk through all patterns, so that we can catch cases where the lock is + // nested in a pattern. For the basic `let_underscore_drop` lint, we only look at the top + // level, since there are many legitimate reasons to bind a sub-pattern to an `_`, if we're + // only interested in the rest. But with locks, we prefer having the chance of "false + // positives" over missing cases, since the effects can be quite catastrophic. local.pat.walk_always(|pat| { let is_top_level = top_level; top_level = false; diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 12b3d1d2f9e4..1317af50a4a8 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -16,7 +16,7 @@ use crate::{ use rustc_ast as ast; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::{Diag, DiagMessage, LintDiagnostic, MultiSpan}; +use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; use rustc_feature::{Features, GateIssue}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; @@ -41,6 +41,7 @@ use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; +use tracing::{debug, instrument}; use crate::errors::{ MalformedAttribute, MalformedAttributeSub, OverruledAttribute, OverruledAttributeSub, @@ -592,7 +593,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { let lint = UnknownLintFromCommandLine { name, suggestion, requested_level }; self.emit_lint(UNKNOWN_LINTS, lint); } - CheckLintNameResult::Tool(Err((Some(_), ref replace))) => { + CheckLintNameResult::Tool(_, Some(ref replace)) => { let name = lint_name.clone(); let requested_level = RequestedLevel { level, lint_name }; let lint = DeprecatedLintNameFromCommandLine { name, replace, requested_level }; @@ -749,7 +750,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { let level = match Level::from_attr(attr) { None => continue, - // This is the only lint level with a `LintExpectationId` that can be created from an attribute + // This is the only lint level with a `LintExpectationId` that can be created from + // an attribute. Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => { let LintExpectationId::Unstable { attr_id, lint_index } = unstable_id else { bug!("stable id Level::from_attr") @@ -759,8 +761,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { hir_id, attr_index: attr_index.try_into().unwrap(), lint_index, - // we pass the previous unstable attr_id such that we can trace the ast id when building a map - // to go from unstable to stable id. + // We pass the previous unstable attr_id such that we can trace the ast id + // when building a map to go from unstable to stable id. attr_id: Some(attr_id), }; @@ -859,13 +861,15 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { self.store.check_lint_name(&name, tool_name, self.registered_tools); match &lint_result { CheckLintNameResult::Ok(ids) => { - // This checks for instances where the user writes `#[expect(unfulfilled_lint_expectations)]` - // in that case we want to avoid overriding the lint level but instead add an expectation that - // can't be fulfilled. The lint message will include an explanation, that the + // This checks for instances where the user writes + // `#[expect(unfulfilled_lint_expectations)]` in that case we want to avoid + // overriding the lint level but instead add an expectation that can't be + // fulfilled. The lint message will include an explanation, that the // `unfulfilled_lint_expectations` lint can't be expected. if let Level::Expect(expect_id) = level { - // The `unfulfilled_lint_expectations` lint is not part of any lint groups. Therefore. we - // only need to check the slice if it contains a single lint. + // The `unfulfilled_lint_expectations` lint is not part of any lint + // groups. Therefore. we only need to check the slice if it contains a + // single lint. let is_unfulfilled_lint_expectations = match ids { [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS), _ => false, @@ -898,32 +902,20 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } } - CheckLintNameResult::Tool(result) => { - match *result { - Ok(ids) => { + CheckLintNameResult::Tool(ids, new_lint_name) => { + let src = match new_lint_name { + None => { let complete_name = &format!("{}::{}", tool_ident.unwrap().name, name); - let src = LintLevelSource::Node { + LintLevelSource::Node { name: Symbol::intern(complete_name), span: sp, reason, - }; - for &id in ids { - if self.check_gated_lint(id, attr.span, false) { - self.insert_spec(id, (level, src)); - } - } - if let Level::Expect(expect_id) = level { - self.provider.push_expectation( - expect_id, - LintExpectation::new(reason, sp, false, tool_name), - ); } } - Err((Some(ids), ref new_lint_name)) => { - let lint = builtin::RENAMED_AND_REMOVED_LINTS; + Some(new_lint_name) => { self.emit_span_lint( - lint, + builtin::RENAMED_AND_REMOVED_LINTS, sp.into(), DeprecatedLintName { name, @@ -931,29 +923,31 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { replace: new_lint_name, }, ); - - let src = LintLevelSource::Node { + LintLevelSource::Node { name: Symbol::intern(new_lint_name), span: sp, reason, - }; - for id in ids { - self.insert_spec(*id, (level, src)); - } - if let Level::Expect(expect_id) = level { - self.provider.push_expectation( - expect_id, - LintExpectation::new(reason, sp, false, tool_name), - ); } } - Err((None, _)) => { - // If Tool(Err(None, _)) is returned, then either the lint does not - // exist in the tool or the code was not compiled with the tool and - // therefore the lint was never added to the `LintStore`. To detect - // this is the responsibility of the lint tool. + }; + for &id in *ids { + if self.check_gated_lint(id, attr.span, false) { + self.insert_spec(id, (level, src)); } } + if let Level::Expect(expect_id) = level { + self.provider.push_expectation( + expect_id, + LintExpectation::new(reason, sp, false, tool_name), + ); + } + } + + CheckLintNameResult::MissingTool => { + // If `MissingTool` is returned, then either the lint does not + // exist in the tool or the code was not compiled with the tool and + // therefore the lint was never added to the `LintStore`. To detect + // this is the responsibility of the lint tool. } &CheckLintNameResult::NoTool => { @@ -996,7 +990,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { // we don't warn about the name change. if let CheckLintNameResult::Renamed(new_name) = lint_result { // Ignore any errors or warnings that happen because the new name is inaccurate - // NOTE: `new_name` already includes the tool name, so we don't have to add it again. + // NOTE: `new_name` already includes the tool name, so we don't have to add it + // again. let CheckLintNameResult::Ok(ids) = self.store.check_lint_name(&new_name, None, self.registered_tools) else { @@ -1063,26 +1058,19 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { // FIXME: make this translatable #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] - lint_level( - self.sess, - lint, - level, - src, - Some(span.into()), - fluent::lint_unknown_gated_lint, - |lint| { - lint.arg("name", lint_id.lint.name_lower()); - lint.note(fluent::lint_note); - rustc_session::parse::add_feature_diagnostics_for_issue( - lint, - &self.sess, - feature, - GateIssue::Language, - lint_from_cli, - None, - ); - }, - ); + lint_level(self.sess, lint, level, src, Some(span.into()), |lint| { + lint.primary_message(fluent::lint_unknown_gated_lint); + lint.arg("name", lint_id.lint.name_lower()); + lint.note(fluent::lint_note); + rustc_session::parse::add_feature_diagnostics_for_issue( + lint, + &self.sess, + feature, + GateIssue::Language, + lint_from_cli, + None, + ); + }); } false @@ -1103,11 +1091,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { &self, lint: &'static Lint, span: Option, - msg: impl Into, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), ) { let (level, src) = self.lint_level(lint); - lint_level(self.sess, lint, level, src, span, msg, decorate) + lint_level(self.sess, lint, level, src, span, decorate) } #[track_caller] @@ -1118,7 +1105,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { decorate: impl for<'a> LintDiagnostic<'a, ()>, ) { let (level, src) = self.lint_level(lint); - lint_level(self.sess, lint, level, src, Some(span), decorate.msg(), |lint| { + lint_level(self.sess, lint, level, src, Some(span), |lint| { decorate.decorate_lint(lint); }); } @@ -1126,7 +1113,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { #[track_caller] pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> LintDiagnostic<'a, ()>) { let (level, src) = self.lint_level(lint); - lint_level(self.sess, lint, level, src, None, decorate.msg(), |lint| { + lint_level(self.sess, lint, level, src, None, |lint| { decorate.decorate_lint(lint); }); } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index d93edadcfbcc..7dae2de7bfb5 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -25,9 +25,10 @@ //! //! This API is completely unstable and subject to change. +// tidy-alphabetical-start +#![allow(internal_features)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(rustdoc_internals)] #![feature(array_windows)] #![feature(box_patterns)] #![feature(control_flow_enum)] @@ -35,14 +36,11 @@ #![feature(if_let_guard)] #![feature(iter_order_by)] #![feature(let_chains)] -#![feature(trait_upcasting)] #![feature(rustc_attrs)] -#![allow(internal_features)] +#![feature(rustdoc_internals)] +#![feature(trait_upcasting)] +// tidy-alphabetical-end -#[macro_use] -extern crate tracing; - -mod array_into_iter; mod async_fn_in_trait; pub mod builtin; mod context; @@ -76,18 +74,18 @@ mod passes; mod ptr_nulls; mod redundant_semicolon; mod reference_casting; +mod shadowed_into_iter; mod traits; mod types; mod unit_bindings; mod unused; -pub use array_into_iter::ARRAY_INTO_ITER; +pub use shadowed_into_iter::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER}; use rustc_hir::def_id::LocalModDefId; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; -use array_into_iter::ArrayIntoIter; use async_fn_in_trait::AsyncFnInTrait; use builtin::*; use deref_into_dyn_supertrait::*; @@ -112,6 +110,7 @@ use pass_by_value::*; use ptr_nulls::*; use redundant_semicolon::*; use reference_casting::*; +use shadowed_into_iter::ShadowedIntoIter; use traits::*; use types::*; use unit_bindings::*; @@ -215,7 +214,7 @@ late_lint_methods!( DerefNullPtr: DerefNullPtr, UnstableFeatures: UnstableFeatures, UngatedAsyncFnTrackCaller: UngatedAsyncFnTrackCaller, - ArrayIntoIter: ArrayIntoIter::default(), + ShadowedIntoIter: ShadowedIntoIter, DropTraitConstraints: DropTraitConstraints, TemporaryCStringAsPtr: TemporaryCStringAsPtr, NonPanicFmt: NonPanicFmt, @@ -542,6 +541,16 @@ fn register_builtins(store: &mut LintStore) { "converted into hard error, see RFC #3535 \ for more information", ); + store.register_removed( + "indirect_structural_match", + "converted into hard error, see RFC #3535 \ + for more information", + ); + store.register_removed( + "pointer_structural_match", + "converted into hard error, see RFC #3535 \ + for more information", + ); } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 7efa5245baa2..b26d04d06180 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -5,16 +5,22 @@ use std::num::NonZero; use crate::errors::RequestedLevel; use crate::fluent_generated as fluent; use rustc_errors::{ - codes::*, Applicability, Diag, DiagMessage, DiagStyledString, EmissionGuarantee, - LintDiagnostic, SubdiagMessageOp, Subdiagnostic, SuggestionStyle, + codes::*, Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, + ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp, + Subdiagnostic, SuggestionStyle, }; -use rustc_hir::def_id::DefId; +use rustc_hir::{def::Namespace, def_id::DefId}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{ inhabitedness::InhabitedPredicate, Clause, PolyExistentialTraitRef, Ty, TyCtxt, }; -use rustc_session::Session; -use rustc_span::{edition::Edition, sym, symbol::Ident, Span, Symbol}; +use rustc_session::{lint::AmbiguityErrorDiag, Session}; +use rustc_span::{ + edition::Edition, + sym, + symbol::{Ident, MacroRulesNormalizedIdent}, + Span, Symbol, +}; use crate::{ builtin::InitError, builtin::TypeAliasBounds, errors::OverruledAttributeSub, LateContext, @@ -22,17 +28,18 @@ use crate::{ // array_into_iter.rs #[derive(LintDiagnostic)] -#[diag(lint_array_into_iter)] -pub struct ArrayIntoIterDiag<'a> { - pub target: &'a str, +#[diag(lint_shadowed_into_iter)] +pub struct ShadowedIntoIterDiag { + pub target: &'static str, + pub edition: &'static str, #[suggestion(lint_use_iter_suggestion, code = "iter", applicability = "machine-applicable")] pub suggestion: Span, #[subdiagnostic] - pub sub: Option, + pub sub: Option, } #[derive(Subdiagnostic)] -pub enum ArrayIntoIterDiagSub { +pub enum ShadowedIntoIterDiagSub { #[suggestion(lint_remove_into_iter_suggestion, code = "", applicability = "maybe-incorrect")] RemoveIntoIter { #[primary_span] @@ -138,12 +145,9 @@ pub struct BuiltinMissingDebugImpl<'a> { // Needed for def_path_str impl<'a> LintDiagnostic<'a, ()> for BuiltinMissingDebugImpl<'_> { fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { + diag.primary_message(fluent::lint_builtin_missing_debug_impl); diag.arg("debug", self.tcx.def_path_str(self.def_id)); } - - fn msg(&self) -> DiagMessage { - fluent::lint_builtin_missing_debug_impl - } } #[derive(LintDiagnostic)] @@ -243,6 +247,7 @@ pub struct BuiltinUngatedAsyncFnTrackCaller<'a> { impl<'a> LintDiagnostic<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { + diag.primary_message(fluent::lint_ungated_async_fn_track_caller); diag.span_label(self.label, fluent::lint_label); rustc_session::parse::add_feature_diagnostics( diag, @@ -250,10 +255,6 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> { sym::async_fn_track_caller, ); } - - fn msg(&self) -> DiagMessage { - fluent::lint_ungated_async_fn_track_caller - } } #[derive(LintDiagnostic)] @@ -425,6 +426,7 @@ pub struct BuiltinUnpermittedTypeInit<'a> { impl<'a> LintDiagnostic<'a, ()> for BuiltinUnpermittedTypeInit<'_> { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { + diag.primary_message(self.msg); diag.arg("ty", self.ty); diag.span_label(self.label, fluent::lint_builtin_unpermitted_type_init_label); if let InhabitedPredicate::True = self.ty.inhabited_predicate(self.tcx) { @@ -436,10 +438,6 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinUnpermittedTypeInit<'_> { } self.sub.add_to_diag(diag); } - - fn msg(&self) -> DiagMessage { - self.msg.clone() - } } // FIXME(davidtwco): make translatable @@ -613,6 +611,7 @@ pub enum PtrNullChecksDiag<'a> { #[diag(lint_for_loops_over_fallibles)] pub struct ForLoopsOverFalliblesDiag<'a> { pub article: &'static str, + pub ref_prefix: &'static str, pub ty: &'static str, #[subdiagnostic] pub sub: ForLoopsOverFalliblesLoopSub<'a>, @@ -657,41 +656,62 @@ pub struct ForLoopsOverFalliblesSuggestion<'a> { pub end_span: Span, } +#[derive(Subdiagnostic)] +pub enum UseLetUnderscoreIgnoreSuggestion { + #[note(lint_use_let_underscore_ignore_suggestion)] + Note, + #[multipart_suggestion( + lint_use_let_underscore_ignore_suggestion, + style = "verbose", + applicability = "maybe-incorrect" + )] + Suggestion { + #[suggestion_part(code = "let _ = ")] + start_span: Span, + #[suggestion_part(code = "")] + end_span: Span, + }, +} + // drop_forget_useless.rs #[derive(LintDiagnostic)] #[diag(lint_dropping_references)] -#[note] pub struct DropRefDiag<'a> { pub arg_ty: Ty<'a>, #[label] pub label: Span, + #[subdiagnostic] + pub sugg: UseLetUnderscoreIgnoreSuggestion, } #[derive(LintDiagnostic)] #[diag(lint_dropping_copy_types)] -#[note] pub struct DropCopyDiag<'a> { pub arg_ty: Ty<'a>, #[label] pub label: Span, + #[subdiagnostic] + pub sugg: UseLetUnderscoreIgnoreSuggestion, } #[derive(LintDiagnostic)] #[diag(lint_forgetting_references)] -#[note] pub struct ForgetRefDiag<'a> { pub arg_ty: Ty<'a>, #[label] pub label: Span, + #[subdiagnostic] + pub sugg: UseLetUnderscoreIgnoreSuggestion, } #[derive(LintDiagnostic)] #[diag(lint_forgetting_copy_types)] -#[note] pub struct ForgetCopyDiag<'a> { pub arg_ty: Ty<'a>, #[label] pub label: Span, + #[subdiagnostic] + pub sugg: UseLetUnderscoreIgnoreSuggestion, } #[derive(LintDiagnostic)] @@ -1161,6 +1181,7 @@ pub struct NonFmtPanicUnused { // Used because of two suggestions based on one Option impl<'a> LintDiagnostic<'a, ()> for NonFmtPanicUnused { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { + diag.primary_message(fluent::lint_non_fmt_panic_unused); diag.arg("count", self.count); diag.note(fluent::lint_note); if let Some(span) = self.suggestion { @@ -1178,10 +1199,6 @@ impl<'a> LintDiagnostic<'a, ()> for NonFmtPanicUnused { ); } } - - fn msg(&self) -> DiagMessage { - fluent::lint_non_fmt_panic_unused - } } #[derive(LintDiagnostic)] @@ -1333,40 +1350,136 @@ pub struct SuspiciousDoubleRefCloneDiag<'a> { } // non_local_defs.rs -#[derive(LintDiagnostic)] pub enum NonLocalDefinitionsDiag { - #[diag(lint_non_local_definitions_impl)] - #[help] - #[note(lint_non_local)] - #[note(lint_exception)] - #[note(lint_non_local_definitions_deprecation)] Impl { depth: u32, body_kind_descr: &'static str, body_name: String, - #[subdiagnostic] cargo_update: Option, - #[suggestion(lint_const_anon, code = "_", applicability = "machine-applicable")] - const_anon: Option, + const_anon: Option>, + move_to: Option<(Span, Vec)>, + doctest: bool, + may_remove: Option<(Span, String)>, + has_trait: bool, + self_ty_str: String, + of_trait_str: Option, + macro_to_change: Option<(String, &'static str)>, }, - #[diag(lint_non_local_definitions_macro_rules)] MacroRules { depth: u32, body_kind_descr: &'static str, body_name: String, - #[help] - help: Option<()>, - #[help(lint_help_doctest)] - doctest_help: Option<()>, - #[note(lint_non_local)] - #[note(lint_exception)] - #[note(lint_non_local_definitions_deprecation)] - notes: (), - #[subdiagnostic] + doctest: bool, cargo_update: Option, }, } +impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag { + fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { + match self { + NonLocalDefinitionsDiag::Impl { + depth, + body_kind_descr, + body_name, + cargo_update, + const_anon, + move_to, + doctest, + may_remove, + has_trait, + self_ty_str, + of_trait_str, + macro_to_change, + } => { + diag.primary_message(fluent::lint_non_local_definitions_impl); + diag.arg("depth", depth); + diag.arg("body_kind_descr", body_kind_descr); + diag.arg("body_name", body_name); + diag.arg("self_ty_str", self_ty_str); + if let Some(of_trait_str) = of_trait_str { + diag.arg("of_trait_str", of_trait_str); + } + + if let Some((macro_to_change, macro_kind)) = macro_to_change { + diag.arg("macro_to_change", macro_to_change); + diag.arg("macro_kind", macro_kind); + diag.note(fluent::lint_macro_to_change); + } + if let Some(cargo_update) = cargo_update { + diag.subdiagnostic(cargo_update); + } + + if has_trait { + diag.note(fluent::lint_bounds); + diag.note(fluent::lint_with_trait); + } else { + diag.note(fluent::lint_without_trait); + } + + if let Some((move_help, may_move)) = move_to { + let mut ms = MultiSpan::from_span(move_help); + for sp in may_move { + ms.push_span_label(sp, fluent::lint_non_local_definitions_may_move); + } + diag.span_help(ms, fluent::lint_non_local_definitions_impl_move_help); + } + if doctest { + diag.help(fluent::lint_doctest); + } + + if let Some((span, part)) = may_remove { + diag.arg("may_remove_part", part); + diag.span_suggestion( + span, + fluent::lint_remove_help, + "", + Applicability::MaybeIncorrect, + ); + } + + if let Some(const_anon) = const_anon { + diag.note(fluent::lint_exception); + if let Some(const_anon) = const_anon { + diag.span_suggestion( + const_anon, + fluent::lint_const_anon, + "_", + Applicability::MachineApplicable, + ); + } + } + + diag.note(fluent::lint_non_local_definitions_deprecation); + } + NonLocalDefinitionsDiag::MacroRules { + depth, + body_kind_descr, + body_name, + doctest, + cargo_update, + } => { + diag.primary_message(fluent::lint_non_local_definitions_macro_rules); + diag.arg("depth", depth); + diag.arg("body_kind_descr", body_kind_descr); + diag.arg("body_name", body_name); + + if doctest { + diag.help(fluent::lint_help_doctest); + } else { + diag.help(fluent::lint_help); + } + + diag.note(fluent::lint_non_local); + diag.note(fluent::lint_non_local_definitions_deprecation); + + if let Some(cargo_update) = cargo_update { + diag.subdiagnostic(cargo_update); + } + } + } + } +} + #[derive(Subdiagnostic)] #[note(lint_non_local_definitions_cargo_update)] pub struct NonLocalDefinitionsCargoUpdateNote { @@ -1403,13 +1516,10 @@ pub struct DropTraitConstraintsDiag<'a> { // Needed for def_path_str impl<'a> LintDiagnostic<'a, ()> for DropTraitConstraintsDiag<'_> { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { + diag.primary_message(fluent::lint_drop_trait_constraints); diag.arg("predicate", self.predicate); diag.arg("needs_drop", self.tcx.def_path_str(self.def_id)); } - - fn msg(&self) -> DiagMessage { - fluent::lint_drop_trait_constraints - } } pub struct DropGlue<'a> { @@ -1420,12 +1530,9 @@ pub struct DropGlue<'a> { // Needed for def_path_str impl<'a> LintDiagnostic<'a, ()> for DropGlue<'_> { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { + diag.primary_message(fluent::lint_drop_glue); diag.arg("needs_drop", self.tcx.def_path_str(self.def_id)); } - - fn msg(&self) -> DiagMessage { - fluent::lint_drop_glue - } } // types.rs @@ -1703,6 +1810,7 @@ pub struct ImproperCTypes<'a> { // Used because of the complexity of Option, DiagMessage, and Option impl<'a> LintDiagnostic<'a, ()> for ImproperCTypes<'_> { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { + diag.primary_message(fluent::lint_improper_ctypes); diag.arg("ty", self.ty); diag.arg("desc", self.desc); diag.span_label(self.label, fluent::lint_label); @@ -1714,10 +1822,6 @@ impl<'a> LintDiagnostic<'a, ()> for ImproperCTypes<'_> { diag.span_note(note, fluent::lint_note); } } - - fn msg(&self) -> DiagMessage { - fluent::lint_improper_ctypes - } } #[derive(LintDiagnostic)] @@ -1846,6 +1950,7 @@ pub enum UnusedDefSuggestion { // Needed because of def_path_str impl<'a> LintDiagnostic<'a, ()> for UnusedDef<'_, '_> { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { + diag.primary_message(fluent::lint_unused_def); diag.arg("pre", self.pre); diag.arg("post", self.post); diag.arg("def", self.cx.tcx.def_path_str(self.def_id)); @@ -1854,13 +1959,9 @@ impl<'a> LintDiagnostic<'a, ()> for UnusedDef<'_, '_> { diag.note(note.to_string()); } if let Some(sugg) = self.suggestion { - diag.subdiagnostic(diag.dcx, sugg); + diag.subdiagnostic(sugg); } } - - fn msg(&self) -> DiagMessage { - fluent::lint_unused_def - } } #[derive(LintDiagnostic)] @@ -1929,15 +2030,12 @@ pub struct AsyncFnInTraitDiag { impl<'a> LintDiagnostic<'a, ()> for AsyncFnInTraitDiag { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { + diag.primary_message(fluent::lint_async_fn_in_trait); diag.note(fluent::lint_note); if let Some(sugg) = self.sugg { diag.multipart_suggestion(fluent::lint_suggestion, sugg, Applicability::MaybeIncorrect); } } - - fn msg(&self) -> DiagMessage { - fluent::lint_async_fn_in_trait - } } #[derive(LintDiagnostic)] @@ -1946,3 +2044,849 @@ pub struct UnitBindingsDiag { #[label] pub label: Span, } + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_asm_labels)] +#[help] +#[note] +pub struct BuiltinNamedAsmLabel; + +#[derive(Subdiagnostic)] +pub enum UnexpectedCfgCargoHelp { + #[help(lint_unexpected_cfg_add_cargo_feature)] + #[help(lint_unexpected_cfg_add_cargo_toml_lint_cfg)] + LintCfg { cargo_toml_lint_cfg: String }, + #[help(lint_unexpected_cfg_add_cargo_feature)] + #[help(lint_unexpected_cfg_add_cargo_toml_lint_cfg)] + #[help(lint_unexpected_cfg_add_build_rs_println)] + LintCfgAndBuildRs { cargo_toml_lint_cfg: String, build_rs_println: String }, +} + +impl UnexpectedCfgCargoHelp { + fn cargo_toml_lint_cfg(unescaped: &str) -> String { + format!( + "\n [lints.rust]\n unexpected_cfgs = {{ level = \"warn\", check-cfg = ['{unescaped}'] }}" + ) + } + + pub fn lint_cfg(unescaped: &str) -> Self { + UnexpectedCfgCargoHelp::LintCfg { + cargo_toml_lint_cfg: Self::cargo_toml_lint_cfg(unescaped), + } + } + + pub fn lint_cfg_and_build_rs(unescaped: &str, escaped: &str) -> Self { + UnexpectedCfgCargoHelp::LintCfgAndBuildRs { + cargo_toml_lint_cfg: Self::cargo_toml_lint_cfg(unescaped), + build_rs_println: format!("println!(\"cargo::rustc-check-cfg={escaped}\");"), + } + } +} + +#[derive(Subdiagnostic)] +#[help(lint_unexpected_cfg_add_cmdline_arg)] +pub struct UnexpectedCfgRustcHelp { + pub cmdline_arg: String, +} + +impl UnexpectedCfgRustcHelp { + pub fn new(unescaped: &str) -> Self { + Self { cmdline_arg: format!("--check-cfg={unescaped}") } + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_unexpected_cfg_name)] +pub struct UnexpectedCfgName { + #[subdiagnostic] + pub code_sugg: unexpected_cfg_name::CodeSuggestion, + #[subdiagnostic] + pub invocation_help: unexpected_cfg_name::InvocationHelp, + + pub name: Symbol, +} + +pub mod unexpected_cfg_name { + use rustc_errors::DiagSymbolList; + use rustc_macros::Subdiagnostic; + use rustc_span::{Span, Symbol}; + + #[derive(Subdiagnostic)] + pub enum CodeSuggestion { + #[help(lint_unexpected_cfg_define_features)] + DefineFeatures, + #[suggestion( + lint_unexpected_cfg_name_similar_name_value, + applicability = "maybe-incorrect", + code = "{code}" + )] + SimilarNameAndValue { + #[primary_span] + span: Span, + code: String, + }, + #[suggestion( + lint_unexpected_cfg_name_similar_name_no_value, + applicability = "maybe-incorrect", + code = "{code}" + )] + SimilarNameNoValue { + #[primary_span] + span: Span, + code: String, + }, + #[suggestion( + lint_unexpected_cfg_name_similar_name_different_values, + applicability = "maybe-incorrect", + code = "{code}" + )] + SimilarNameDifferentValues { + #[primary_span] + span: Span, + code: String, + #[subdiagnostic] + expected: Option, + }, + #[suggestion( + lint_unexpected_cfg_name_similar_name, + applicability = "maybe-incorrect", + code = "{code}" + )] + SimilarName { + #[primary_span] + span: Span, + code: String, + #[subdiagnostic] + expected: Option, + }, + SimilarValues { + #[subdiagnostic] + with_similar_values: Vec, + #[subdiagnostic] + expected_names: Option, + }, + } + + #[derive(Subdiagnostic)] + #[help(lint_unexpected_cfg_name_expected_values)] + pub struct ExpectedValues { + pub best_match: Symbol, + pub possibilities: DiagSymbolList, + } + + #[derive(Subdiagnostic)] + #[suggestion( + lint_unexpected_cfg_name_with_similar_value, + applicability = "maybe-incorrect", + code = "{code}" + )] + pub struct FoundWithSimilarValue { + #[primary_span] + pub span: Span, + pub code: String, + } + + #[derive(Subdiagnostic)] + #[help_once(lint_unexpected_cfg_name_expected_names)] + pub struct ExpectedNames { + pub possibilities: DiagSymbolList, + pub and_more: usize, + } + + #[derive(Subdiagnostic)] + pub enum InvocationHelp { + #[note(lint_unexpected_cfg_doc_cargo)] + Cargo { + #[subdiagnostic] + sub: Option, + }, + #[note(lint_unexpected_cfg_doc_rustc)] + Rustc(#[subdiagnostic] super::UnexpectedCfgRustcHelp), + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_unexpected_cfg_value)] +pub struct UnexpectedCfgValue { + #[subdiagnostic] + pub code_sugg: unexpected_cfg_value::CodeSuggestion, + #[subdiagnostic] + pub invocation_help: unexpected_cfg_value::InvocationHelp, + + pub has_value: bool, + pub value: String, +} + +pub mod unexpected_cfg_value { + use rustc_errors::DiagSymbolList; + use rustc_macros::Subdiagnostic; + use rustc_span::{Span, Symbol}; + + #[derive(Subdiagnostic)] + pub enum CodeSuggestion { + ChangeValue { + #[subdiagnostic] + expected_values: ExpectedValues, + #[subdiagnostic] + suggestion: Option, + }, + #[note(lint_unexpected_cfg_value_no_expected_value)] + RemoveValue { + #[subdiagnostic] + suggestion: Option, + + name: Symbol, + }, + #[note(lint_unexpected_cfg_value_no_expected_values)] + RemoveCondition { + #[subdiagnostic] + suggestion: RemoveConditionSuggestion, + + name: Symbol, + }, + } + + #[derive(Subdiagnostic)] + pub enum ChangeValueSuggestion { + #[suggestion( + lint_unexpected_cfg_value_similar_name, + code = r#""{best_match}""#, + applicability = "maybe-incorrect" + )] + SimilarName { + #[primary_span] + span: Span, + best_match: Symbol, + }, + #[suggestion( + lint_unexpected_cfg_value_specify_value, + code = r#" = "{first_possibility}""#, + applicability = "maybe-incorrect" + )] + SpecifyValue { + #[primary_span] + span: Span, + first_possibility: Symbol, + }, + } + + #[derive(Subdiagnostic)] + #[suggestion( + lint_unexpected_cfg_value_remove_value, + code = "", + applicability = "maybe-incorrect" + )] + pub struct RemoveValueSuggestion { + #[primary_span] + pub span: Span, + } + + #[derive(Subdiagnostic)] + #[suggestion( + lint_unexpected_cfg_value_remove_condition, + code = "", + applicability = "maybe-incorrect" + )] + pub struct RemoveConditionSuggestion { + #[primary_span] + pub span: Span, + } + + #[derive(Subdiagnostic)] + #[note(lint_unexpected_cfg_value_expected_values)] + pub struct ExpectedValues { + pub name: Symbol, + pub have_none_possibility: bool, + pub possibilities: DiagSymbolList, + pub and_more: usize, + } + + #[derive(Subdiagnostic)] + pub enum InvocationHelp { + #[note(lint_unexpected_cfg_doc_cargo)] + Cargo(#[subdiagnostic] Option), + #[note(lint_unexpected_cfg_doc_rustc)] + Rustc(#[subdiagnostic] Option), + } + + #[derive(Subdiagnostic)] + pub enum CargoHelp { + #[help(lint_unexpected_cfg_value_add_feature)] + AddFeature { + value: Symbol, + }, + #[help(lint_unexpected_cfg_define_features)] + DefineFeatures, + Other(#[subdiagnostic] super::UnexpectedCfgCargoHelp), + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_macro_use_deprecated)] +#[help] +pub struct MacroUseDeprecated; + +#[derive(LintDiagnostic)] +#[diag(lint_unused_macro_use)] +pub struct UnusedMacroUse; + +#[derive(LintDiagnostic)] +#[diag(lint_private_extern_crate_reexport, code = E0365)] +pub struct PrivateExternCrateReexport { + pub ident: Ident, + #[suggestion(code = "pub ", style = "verbose", applicability = "maybe-incorrect")] + pub sugg: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_label)] +pub struct UnusedLabel; + +#[derive(LintDiagnostic)] +#[diag(lint_macro_is_private)] +pub struct MacroIsPrivate { + pub ident: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_macro_definition)] +pub struct UnusedMacroDefinition { + pub name: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(lint_macro_rule_never_used)] +pub struct MacroRuleNeverUsed { + pub n: usize, + pub name: Symbol, +} + +pub struct UnstableFeature { + pub msg: DiagMessage, +} + +impl<'a> LintDiagnostic<'a, ()> for UnstableFeature { + fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { + diag.primary_message(self.msg); + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_avoid_intel_syntax)] +pub struct AvoidIntelSyntax; + +#[derive(LintDiagnostic)] +#[diag(lint_avoid_att_syntax)] +pub struct AvoidAttSyntax; + +#[derive(LintDiagnostic)] +#[diag(lint_incomplete_include)] +pub struct IncompleteInclude; + +#[derive(LintDiagnostic)] +#[diag(lint_unnameable_test_items)] +pub struct UnnameableTestItems; + +#[derive(LintDiagnostic)] +#[diag(lint_duplicate_macro_attribute)] +pub struct DuplicateMacroAttribute; + +#[derive(LintDiagnostic)] +#[diag(lint_cfg_attr_no_attributes)] +pub struct CfgAttrNoAttributes; + +#[derive(LintDiagnostic)] +#[diag(lint_crate_type_in_cfg_attr_deprecated)] +pub struct CrateTypeInCfgAttr; + +#[derive(LintDiagnostic)] +#[diag(lint_crate_name_in_cfg_attr_deprecated)] +pub struct CrateNameInCfgAttr; + +#[derive(LintDiagnostic)] +#[diag(lint_missing_fragment_specifier)] +pub struct MissingFragmentSpecifier; + +#[derive(LintDiagnostic)] +#[diag(lint_metavariable_still_repeating)] +pub struct MetaVariableStillRepeating { + pub name: MacroRulesNormalizedIdent, +} + +#[derive(LintDiagnostic)] +#[diag(lint_metavariable_wrong_operator)] +pub struct MetaVariableWrongOperator; + +#[derive(LintDiagnostic)] +#[diag(lint_duplicate_matcher_binding)] +pub struct DuplicateMatcherBinding; + +#[derive(LintDiagnostic)] +#[diag(lint_unknown_macro_variable)] +pub struct UnknownMacroVariable { + pub name: MacroRulesNormalizedIdent, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_crate_dependency)] +#[help] +pub struct UnusedCrateDependency { + pub extern_crate: Symbol, + pub local_crate: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(lint_wasm_c_abi)] +pub struct WasmCAbi; + +#[derive(LintDiagnostic)] +#[diag(lint_ill_formed_attribute_input)] +pub struct IllFormedAttributeInput { + pub num_suggestions: usize, + pub suggestions: DiagArgValue, +} + +#[derive(LintDiagnostic)] +pub enum InnerAttributeUnstable { + #[diag(lint_inner_macro_attribute_unstable)] + InnerMacroAttribute, + #[diag(lint_custom_inner_attribute_unstable)] + CustomInnerAttribute, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unknown_diagnostic_attribute)] +pub struct UnknownDiagnosticAttribute { + #[subdiagnostic] + pub typo: Option, +} + +#[derive(Subdiagnostic)] +#[suggestion( + lint_unknown_diagnostic_attribute_typo_sugg, + style = "verbose", + code = "{typo_name}", + applicability = "machine-applicable" +)] +pub struct UnknownDiagnosticAttributeTypoSugg { + #[primary_span] + pub span: Span, + pub typo_name: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unicode_text_flow)] +#[note] +pub struct UnicodeTextFlow { + #[label] + pub comment_span: Span, + #[subdiagnostic] + pub characters: Vec, + #[subdiagnostic] + pub suggestions: Option, + + pub num_codepoints: usize, +} + +#[derive(Subdiagnostic)] +#[label(lint_label_comment_char)] +pub struct UnicodeCharNoteSub { + #[primary_span] + pub span: Span, + pub c_debug: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable", style = "hidden")] +pub struct UnicodeTextFlowSuggestion { + #[suggestion_part(code = "")] + pub spans: Vec, +} + +#[derive(LintDiagnostic)] +#[diag(lint_abs_path_with_module)] +pub struct AbsPathWithModule { + #[subdiagnostic] + pub sugg: AbsPathWithModuleSugg, +} + +#[derive(Subdiagnostic)] +#[suggestion(lint_suggestion, code = "{replacement}")] +pub struct AbsPathWithModuleSugg { + #[primary_span] + pub span: Span, + #[applicability] + pub applicability: Applicability, + pub replacement: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_proc_macro_derive_resolution_fallback)] +pub struct ProcMacroDeriveResolutionFallback { + #[label] + pub span: Span, + pub ns: Namespace, + pub ident: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(lint_macro_expanded_macro_exports_accessed_by_absolute_paths)] +pub struct MacroExpandedMacroExportsAccessedByAbsolutePaths { + #[note] + pub definition: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_hidden_lifetime_parameters)] +pub struct ElidedLifetimesInPaths { + #[subdiagnostic] + pub subdiag: ElidedLifetimeInPathSubdiag, +} + +#[derive(LintDiagnostic)] +#[diag(lint_invalid_crate_type_value)] +pub struct UnknownCrateTypes { + #[subdiagnostic] + pub sugg: Option, +} + +#[derive(Subdiagnostic)] +#[suggestion(lint_suggestion, code = r#""{candidate}""#, applicability = "maybe-incorrect")] +pub struct UnknownCrateTypesSub { + #[primary_span] + pub span: Span, + pub candidate: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_imports)] +pub struct UnusedImports { + #[subdiagnostic] + pub sugg: UnusedImportsSugg, + #[help] + pub test_module_span: Option, + + pub span_snippets: DiagArgValue, + pub num_snippets: usize, +} + +#[derive(Subdiagnostic)] +pub enum UnusedImportsSugg { + #[suggestion( + lint_suggestion_remove_whole_use, + applicability = "machine-applicable", + code = "", + style = "tool-only" + )] + RemoveWholeUse { + #[primary_span] + span: Span, + }, + #[multipart_suggestion( + lint_suggestion_remove_imports, + applicability = "machine-applicable", + style = "tool-only" + )] + RemoveImports { + #[suggestion_part(code = "")] + remove_spans: Vec, + num_to_remove: usize, + }, +} + +#[derive(LintDiagnostic)] +#[diag(lint_redundant_import)] +pub struct RedundantImport { + #[subdiagnostic] + pub subs: Vec, + + pub ident: Ident, +} + +#[derive(Subdiagnostic)] +pub enum RedundantImportSub { + #[label(lint_label_imported_here)] + ImportedHere(#[primary_span] Span), + #[label(lint_label_defined_here)] + DefinedHere(#[primary_span] Span), + #[label(lint_label_imported_prelude)] + ImportedPrelude(#[primary_span] Span), + #[label(lint_label_defined_prelude)] + DefinedPrelude(#[primary_span] Span), +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_doc_comment)] +#[help] +pub struct UnusedDocComment { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +pub enum PatternsInFnsWithoutBody { + #[diag(lint_pattern_in_foreign)] + Foreign { + #[subdiagnostic] + sub: PatternsInFnsWithoutBodySub, + }, + #[diag(lint_pattern_in_bodiless)] + Bodiless { + #[subdiagnostic] + sub: PatternsInFnsWithoutBodySub, + }, +} + +#[derive(Subdiagnostic)] +#[suggestion(lint_remove_mut_from_pattern, code = "{ident}", applicability = "machine-applicable")] +pub struct PatternsInFnsWithoutBodySub { + #[primary_span] + pub span: Span, + + pub ident: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(lint_extern_without_abi)] +#[help] +pub struct MissingAbi { + #[label] + pub span: Span, + + pub default_abi: &'static str, +} + +#[derive(LintDiagnostic)] +#[diag(lint_legacy_derive_helpers)] +pub struct LegacyDeriveHelpers { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_or_patterns_back_compat)] +pub struct OrPatternsBackCompat { + #[suggestion(code = "{suggestion}", applicability = "machine-applicable")] + pub span: Span, + pub suggestion: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_reserved_prefix)] +pub struct ReservedPrefix { + #[label] + pub label: Span, + #[suggestion(code = " ", applicability = "machine-applicable")] + pub suggestion: Span, + + pub prefix: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_builtin_attribute)] +pub struct UnusedBuiltinAttribute { + #[note] + pub invoc_span: Span, + + pub attr_name: Symbol, + pub macro_name: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_trailing_semi_macro)] +pub struct TrailingMacro { + #[note(lint_note1)] + #[note(lint_note2)] + pub is_trailing: bool, + + pub name: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(lint_break_with_label_and_loop)] +pub struct BreakWithLabelAndLoop { + #[subdiagnostic] + pub sub: BreakWithLabelAndLoopSub, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")] +pub struct BreakWithLabelAndLoopSub { + #[suggestion_part(code = "(")] + pub left: Span, + #[suggestion_part(code = ")")] + pub right: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_deprecated_where_clause_location)] +#[note] +pub struct DeprecatedWhereClauseLocation { + #[subdiagnostic] + pub suggestion: DeprecatedWhereClauseLocationSugg, +} + +#[derive(Subdiagnostic)] +pub enum DeprecatedWhereClauseLocationSugg { + #[multipart_suggestion(lint_suggestion_move_to_end, applicability = "machine-applicable")] + MoveToEnd { + #[suggestion_part(code = "")] + left: Span, + #[suggestion_part(code = "{sugg}")] + right: Span, + + sugg: String, + }, + #[suggestion(lint_suggestion_remove_where, code = "", applicability = "machine-applicable")] + RemoveWhere { + #[primary_span] + span: Span, + }, +} + +#[derive(LintDiagnostic)] +#[diag(lint_missing_unsafe_on_extern)] +pub struct MissingUnsafeOnExtern { + #[suggestion(code = "unsafe ", applicability = "machine-applicable")] + pub suggestion: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_single_use_lifetime)] +pub struct SingleUseLifetime { + #[label(lint_label_param)] + pub param_span: Span, + #[label(lint_label_use)] + pub use_span: Span, + #[subdiagnostic] + pub suggestion: Option, + + pub ident: Ident, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")] +pub struct SingleUseLifetimeSugg { + #[suggestion_part(code = "")] + pub deletion_span: Option, + #[suggestion_part(code = "{replace_lt}")] + pub use_span: Span, + + pub replace_lt: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_lifetime)] +pub struct UnusedLifetime { + #[suggestion(code = "", applicability = "machine-applicable")] + pub deletion_span: Option, + + pub ident: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(lint_named_argument_used_positionally)] +pub struct NamedArgumentUsedPositionally { + #[label(lint_label_named_arg)] + pub named_arg_sp: Span, + #[label(lint_label_position_arg)] + pub position_label_sp: Option, + #[suggestion(style = "verbose", code = "{name}", applicability = "maybe-incorrect")] + pub suggestion: Option, + + pub name: String, + pub named_arg_name: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_byte_slice_in_packed_struct_with_derive)] +#[help] +pub struct ByteSliceInPackedStructWithDerive { + // FIXME: make this translatable + pub ty: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_extern_crate)] +pub struct UnusedExternCrate { + #[suggestion(code = "", applicability = "machine-applicable")] + pub removal_span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_extern_crate_not_idiomatic)] +pub struct ExternCrateNotIdiomatic { + #[suggestion(style = "verbose", code = "{code}", applicability = "machine-applicable")] + pub span: Span, + + pub code: &'static str, +} + +// FIXME: make this translatable +pub struct AmbiguousGlobImports { + pub ambiguity: AmbiguityErrorDiag, +} + +impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for AmbiguousGlobImports { + fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) { + diag.primary_message(self.ambiguity.msg.clone()); + rustc_errors::report_ambiguity_error(diag, self.ambiguity); + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_ambiguous_glob_reexport)] +pub struct AmbiguousGlobReexports { + #[label(lint_label_first_reexport)] + pub first_reexport: Span, + #[label(lint_label_duplicate_reexport)] + pub duplicate_reexport: Span, + + pub name: String, + // FIXME: make this translatable + pub namespace: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_hidden_glob_reexport)] +pub struct HiddenGlobReexports { + #[note(lint_note_glob_reexport)] + pub glob_reexport: Span, + #[note(lint_note_private_item)] + pub private_item: Span, + + pub name: String, + // FIXME: make this translatable + pub namespace: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unnecessary_qualification)] +pub struct UnusedQualifications { + #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] + pub removal_span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_associated_const_elided_lifetime)] +pub struct AssociatedConstElidedLifetime { + #[suggestion(style = "verbose", code = "{code}", applicability = "machine-applicable")] + pub span: Span, + + pub code: &'static str, + pub elided: bool, + #[note] + pub lifetimes_in_scope: MultiSpan, +} + +#[derive(LintDiagnostic)] +#[diag(lint_redundant_import_visibility)] +pub struct RedundantImportVisibility { + #[note] + pub span: Span, + #[help] + pub help: (), + + pub import_vis: String, + pub max_vis: String, +} diff --git a/compiler/rustc_lint/src/methods.rs b/compiler/rustc_lint/src/methods.rs index b93245d58d9d..7a71fec769fe 100644 --- a/compiler/rustc_lint/src/methods.rs +++ b/compiler/rustc_lint/src/methods.rs @@ -25,9 +25,9 @@ declare_lint! { /// /// The inner pointer of a `CString` lives only as long as the `CString` it /// points to. Getting the inner pointer of a *temporary* `CString` allows the `CString` - /// to be dropped at the end of the statement, as it is not being referenced as far as the typesystem - /// is concerned. This means outside of the statement the pointer will point to freed memory, which - /// causes undefined behavior if the pointer is later dereferenced. + /// to be dropped at the end of the statement, as it is not being referenced as far as the + /// typesystem is concerned. This means outside of the statement the pointer will point to + /// freed memory, which causes undefined behavior if the pointer is later dereferenced. pub TEMPORARY_CSTRING_AS_PTR, Warn, "detects getting the inner pointer of a temporary `CString`" diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index 48d140c6b7f3..aa1d94228ea2 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -38,10 +38,10 @@ declare_lint_pass!(MultipleSupertraitUpcastable => [MULTIPLE_SUPERTRAIT_UPCASTAB impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { let def_id = item.owner_id.to_def_id(); - // NOTE(nbdd0121): use `object_safety_violations` instead of `check_is_object_safe` because + // NOTE(nbdd0121): use `object_safety_violations` instead of `is_object_safe` because // the latter will report `where_clause_object_safety` lint. if let hir::ItemKind::Trait(_, _, _, _, _) = item.kind - && cx.tcx.object_safety_violations(def_id).is_empty() + && cx.tcx.is_object_safe(def_id) { let direct_super_traits_iter = cx .tcx diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index a6057afcbd6b..2dc2a0efdf04 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -2,7 +2,7 @@ use crate::lints::{NonFmtPanicBraces, NonFmtPanicUnused}; use crate::{fluent_generated as fluent, LateContext, LateLintPass, LintContext}; use rustc_ast as ast; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, LangItem}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::bug; use rustc_middle::lint::in_external_macro; @@ -53,8 +53,8 @@ impl<'tcx> LateLintPass<'tcx> for NonPanicFmt { if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() { let f_diagnostic_name = cx.tcx.get_diagnostic_name(def_id); - if Some(def_id) == cx.tcx.lang_items().begin_panic_fn() - || Some(def_id) == cx.tcx.lang_items().panic_fn() + if cx.tcx.is_lang_item(def_id, LangItem::BeginPanic) + || cx.tcx.is_lang_item(def_id, LangItem::Panic) || f_diagnostic_name == Some(sym::panic_str_2015) { if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id { @@ -123,7 +123,8 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc } #[allow(rustc::diagnostic_outside_of_impl)] - cx.span_lint(NON_FMT_PANICS, arg_span, fluent::lint_non_fmt_panic, |lint| { + cx.span_lint(NON_FMT_PANICS, arg_span, |lint| { + lint.primary_message(fluent::lint_non_fmt_panic); lint.arg("name", symbol); lint.note(fluent::lint_note); lint.note(fluent::lint_more_info_note); @@ -152,7 +153,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc ty::Ref(_, r, _) if r.is_str(), ) || matches!( ty.ty_adt_def(), - Some(ty_def) if Some(ty_def.did()) == cx.tcx.lang_items().string(), + Some(ty_def) if cx.tcx.is_lang_item(ty_def.did(), LangItem::String), ); let infcx = cx.tcx.infer_ctxt().build(); diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 885c0bb3a89c..24dd337e6995 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -1,3 +1,6 @@ +use rustc_errors::MultiSpan; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::HirId; use rustc_hir::{def::DefKind, Body, Item, ItemKind, Node, TyKind}; use rustc_hir::{Path, QPath}; use rustc_infer::infer::InferCtxt; @@ -7,12 +10,13 @@ use rustc_middle::ty::{EarlyBinder, TraitRef, TypeSuperFoldable}; use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::Span; -use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind}; +use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind, Symbol}; use rustc_trait_selection::infer::TyCtxtInferExt; use rustc_trait_selection::traits::error_reporting::ambiguity::{ compute_applicable_impls_for_diagnostics, CandidateSource, }; +use crate::fluent_generated as fluent; use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; use crate::{LateContext, LateLintPass, LintContext}; @@ -38,7 +42,7 @@ declare_lint! { /// /// Creating non-local definitions go against expectation and can create discrepancies /// in tooling. It should be avoided. It may become deny-by-default in edition 2024 - /// and higher, see see the tracking issue . + /// and higher, see the tracking issue . /// /// An `impl` definition is non-local if it is nested inside an item and neither /// the type nor the trait are at the same nesting level as the `impl` block. @@ -64,11 +68,11 @@ impl_lint_pass!(NonLocalDefinitions => [NON_LOCAL_DEFINITIONS]); // instead check_mod is called after every body has been handled. impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { - fn check_body(&mut self, _cx: &LateContext<'tcx>, _body: &'tcx Body<'tcx>) { + fn check_body(&mut self, _cx: &LateContext<'tcx>, _body: &Body<'tcx>) { self.body_depth += 1; } - fn check_body_post(&mut self, _cx: &LateContext<'tcx>, _body: &'tcx Body<'tcx>) { + fn check_body_post(&mut self, _cx: &LateContext<'tcx>, _body: &Body<'tcx>) { self.body_depth -= 1; } @@ -107,6 +111,12 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { } }; + // determining if we are in a doctest context can't currently be determined + // by the code itself (there are no specific attributes), but fortunately rustdoc + // sets a perma-unstable env var for libtest so we just reuse that for now + let is_at_toplevel_doctest = + || self.body_depth == 2 && std::env::var("UNSTABLE_RUSTDOC_TEST_PATH").is_ok(); + match item.kind { ItemKind::Impl(impl_) => { // The RFC states: @@ -134,35 +144,8 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { }; // Part 1: Is the Self type local? - let self_ty_has_local_parent = match impl_.self_ty.kind { - TyKind::Path(QPath::Resolved(_, ty_path)) => { - path_has_local_parent(ty_path, cx, parent, parent_parent) - } - TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => { - path_has_local_parent( - principle_poly_trait_ref.trait_ref.path, - cx, - parent, - parent_parent, - ) - } - TyKind::TraitObject([], _, _) - | TyKind::InferDelegation(_, _) - | TyKind::Slice(_) - | TyKind::Array(_, _) - | TyKind::Ptr(_) - | TyKind::Ref(_, _) - | TyKind::BareFn(_) - | TyKind::Never - | TyKind::Tup(_) - | TyKind::Path(_) - | TyKind::Pat(..) - | TyKind::AnonAdt(_) - | TyKind::OpaqueDef(_, _, _) - | TyKind::Typeof(_) - | TyKind::Infer - | TyKind::Err(_) => false, - }; + let self_ty_has_local_parent = + ty_has_local_parent(&impl_.self_ty.kind, cx, parent, parent_parent); if self_ty_has_local_parent { return; @@ -202,8 +185,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { // Get the span of the parent const item ident (if it's a not a const anon). // // Used to suggest changing the const item to a const anon. - let span_for_const_anon_suggestion = if self.body_depth == 1 - && parent_def_kind == DefKind::Const + let span_for_const_anon_suggestion = if parent_def_kind == DefKind::Const && parent_opt_item_name != Some(kw::Underscore) && let Some(parent) = parent.as_local() && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent) @@ -215,9 +197,90 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { None }; + let const_anon = matches!(parent_def_kind, DefKind::Const | DefKind::Static { .. }) + .then_some(span_for_const_anon_suggestion); + + let may_remove = match &impl_.self_ty.kind { + TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) + if ty_has_local_parent(&mut_ty.ty.kind, cx, parent, parent_parent) => + { + let type_ = + if matches!(impl_.self_ty.kind, TyKind::Ptr(_)) { "*" } else { "&" }; + let part = format!("{}{}", type_, mut_ty.mutbl.prefix_str()); + Some((impl_.self_ty.span.shrink_to_lo().until(mut_ty.ty.span), part)) + } + _ => None, + }; + + let impl_span = item.span.shrink_to_lo().to(impl_.self_ty.span); + let mut ms = MultiSpan::from_span(impl_span); + + let (self_ty_span, self_ty_str) = + self_ty_kind_for_diagnostic(&impl_.self_ty, cx.tcx); + + ms.push_span_label( + self_ty_span, + fluent::lint_non_local_definitions_self_ty_not_local, + ); + let of_trait_str = if let Some(of_trait) = &impl_.of_trait { + ms.push_span_label( + path_span_without_args(&of_trait.path), + fluent::lint_non_local_definitions_of_trait_not_local, + ); + Some(path_name_to_string(&of_trait.path)) + } else { + None + }; + + let (doctest, move_to) = if is_at_toplevel_doctest() { + (true, None) + } else { + let mut collector = PathCollector { paths: Vec::new() }; + collector.visit_ty(&impl_.self_ty); + if let Some(of_trait) = &impl_.of_trait { + collector.visit_trait_ref(of_trait); + } + collector.visit_generics(&impl_.generics); + + let mut may_move: Vec = collector + .paths + .into_iter() + .filter_map(|path| { + if let Some(did) = path.res.opt_def_id() + && did_has_local_parent(did, cx.tcx, parent, parent_parent) + { + Some(cx.tcx.def_span(did)) + } else { + None + } + }) + .collect(); + may_move.sort(); + may_move.dedup(); + + let move_to = if may_move.is_empty() { + ms.push_span_label( + cx.tcx.def_span(parent), + fluent::lint_non_local_definitions_impl_move_help, + ); + None + } else { + Some((cx.tcx.def_span(parent), may_move)) + }; + + (false, move_to) + }; + + let macro_to_change = + if let ExpnKind::Macro(kind, name) = item.span.ctxt().outer_expn_data().kind { + Some((name.to_string(), kind.descr())) + } else { + None + }; + cx.emit_span_lint( NON_LOCAL_DEFINITIONS, - item.span, + ms, NonLocalDefinitionsDiag::Impl { depth: self.body_depth, body_kind_descr: cx.tcx.def_kind_descr(parent_def_kind, parent), @@ -225,19 +288,20 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { .map(|s| s.to_ident_string()) .unwrap_or_else(|| "".to_string()), cargo_update: cargo_update(), - const_anon: span_for_const_anon_suggestion, + const_anon, + self_ty_str, + of_trait_str, + move_to, + doctest, + may_remove, + has_trait: impl_.of_trait.is_some(), + macro_to_change, }, ) } ItemKind::Macro(_macro, MacroKind::Bang) if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) => { - // determining we if are in a doctest context can't currently be determined - // by the code it-self (no specific attrs), but fortunatly rustdoc sets a - // perma-unstable env for libtest so we just re-use that env for now - let is_at_toplevel_doctest = - self.body_depth == 2 && std::env::var("UNSTABLE_RUSTDOC_TEST_PATH").is_ok(); - cx.emit_span_lint( NON_LOCAL_DEFINITIONS, item.span, @@ -248,9 +312,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { .map(|s| s.to_ident_string()) .unwrap_or_else(|| "".to_string()), cargo_update: cargo_update(), - help: (!is_at_toplevel_doctest).then_some(()), - doctest_help: is_at_toplevel_doctest.then_some(()), - notes: (), + doctest: is_at_toplevel_doctest(), }, ) } @@ -259,7 +321,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { } } -// Detecting if the impl definition is leaking outside of it's defining scope. +// Detecting if the impl definition is leaking outside of its defining scope. // // Rule: for each impl, instantiate all local types with inference vars and // then assemble candidates for that goal, if there are more than 1 (non-private @@ -270,7 +332,7 @@ fn impl_trait_ref_has_enough_non_local_candidates<'tcx>( tcx: TyCtxt<'tcx>, infer_span: Span, trait_def_id: DefId, - binder: EarlyBinder>, + binder: EarlyBinder<'tcx, TraitRef<'tcx>>, mut did_has_local_parent: impl FnMut(DefId) -> bool, ) -> bool { let infcx = tcx @@ -343,6 +405,54 @@ impl<'a, 'tcx, F: FnMut(DefId) -> bool> TypeFolder> } } +/// Simple hir::Path collector +struct PathCollector<'tcx> { + paths: Vec>, +} + +impl<'tcx> Visitor<'tcx> for PathCollector<'tcx> { + fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) { + self.paths.push(path.clone()); // need to clone, bc of the restricted lifetime + intravisit::walk_path(self, path) + } +} + +/// Given a `Ty` we check if the (outermost) type is local. +fn ty_has_local_parent( + ty_kind: &TyKind<'_>, + cx: &LateContext<'_>, + impl_parent: DefId, + impl_parent_parent: Option, +) -> bool { + match ty_kind { + TyKind::Path(QPath::Resolved(_, ty_path)) => { + path_has_local_parent(ty_path, cx, impl_parent, impl_parent_parent) + } + TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => path_has_local_parent( + principle_poly_trait_ref.trait_ref.path, + cx, + impl_parent, + impl_parent_parent, + ), + TyKind::TraitObject([], _, _) + | TyKind::InferDelegation(_, _) + | TyKind::Slice(_) + | TyKind::Array(_, _) + | TyKind::Ptr(_) + | TyKind::Ref(_, _) + | TyKind::BareFn(_) + | TyKind::Never + | TyKind::Tup(_) + | TyKind::Path(_) + | TyKind::Pat(..) + | TyKind::AnonAdt(_) + | TyKind::OpaqueDef(_, _, _) + | TyKind::Typeof(_) + | TyKind::Infer + | TyKind::Err(_) => false, + } +} + /// Given a path and a parent impl def id, this checks if the if parent resolution /// def id correspond to the def id of the parent impl definition. /// @@ -384,3 +494,52 @@ fn did_has_local_parent( false } } + +/// Return for a given `Path` the span until the last args +fn path_span_without_args(path: &Path<'_>) -> Span { + if let Some(args) = &path.segments.last().unwrap().args { + path.span.until(args.span_ext) + } else { + path.span + } +} + +/// Return a "error message-able" ident for the last segment of the `Path` +fn path_name_to_string(path: &Path<'_>) -> String { + path.segments.last().unwrap().ident.name.to_ident_string() +} + +/// Compute the `Span` and visual representation for the `Self` we want to point at; +/// It follows part of the actual logic of non-local, and if possible return the least +/// amount possible for the span and representation. +fn self_ty_kind_for_diagnostic(ty: &rustc_hir::Ty<'_>, tcx: TyCtxt<'_>) -> (Span, String) { + match ty.kind { + TyKind::Path(QPath::Resolved(_, ty_path)) => ( + path_span_without_args(ty_path), + ty_path + .res + .opt_def_id() + .map(|did| tcx.opt_item_name(did)) + .flatten() + .as_ref() + .map(|s| Symbol::as_str(s)) + .unwrap_or("") + .to_string(), + ), + TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => { + let path = &principle_poly_trait_ref.trait_ref.path; + ( + path_span_without_args(path), + path.res + .opt_def_id() + .map(|did| tcx.opt_item_name(did)) + .flatten() + .as_ref() + .map(|s| Symbol::as_str(s)) + .unwrap_or("") + .to_string(), + ) + } + _ => (ty.span, rustc_hir_pretty::ty_to_string(&tcx, ty)), + } +} diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index dbb0644cd63f..d64f44471620 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -297,14 +297,14 @@ impl NonSnakeCase { // We cannot provide meaningful suggestions // if the characters are in the category of "Uppercase Letter". let sub = if name != sc { - // We have a valid span in almost all cases, but we don't have one when linting a crate - // name provided via the command line. + // We have a valid span in almost all cases, but we don't have one when linting a + // crate name provided via the command line. if !span.is_dummy() { let sc_ident = Ident::from_str_and_span(&sc, span); if sc_ident.is_reserved() { - // We shouldn't suggest a reserved identifier to fix non-snake-case identifiers. - // Instead, recommend renaming the identifier entirely or, if permitted, - // escaping it to create a raw identifier. + // We shouldn't suggest a reserved identifier to fix non-snake-case + // identifiers. Instead, recommend renaming the identifier entirely or, if + // permitted, escaping it to create a raw identifier. if sc_ident.name.can_be_raw() { NonSnakeCaseDiagSub::RenameOrConvertSuggestion { span, diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index eda40e4a011a..7aef6321eeb5 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -46,6 +46,8 @@ declare_lint! { /// fn test() -> impl Trait { /// 42 /// } + /// + /// fn main() {} /// ``` /// /// {{produces}} @@ -83,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { }; // Only check types, since those are the only things that may // have opaques in them anyways. - let Some(proj_term) = proj.term.ty() else { return }; + let Some(proj_term) = proj.term.as_type() else { return }; // HACK: `impl Trait` from an RPIT is "ok"... if let ty::Alias(ty::Opaque, opaque_ty) = *proj_term.kind() diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index d8ba84eb7a1a..2a843977990c 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -7,8 +7,8 @@ use rustc_session::lint::LintPass; macro_rules! late_lint_methods { ($macro:path, $args:tt) => ( $macro!($args, [ - fn check_body(a: &'tcx rustc_hir::Body<'tcx>); - fn check_body_post(a: &'tcx rustc_hir::Body<'tcx>); + fn check_body(a: &rustc_hir::Body<'tcx>); + fn check_body_post(a: &rustc_hir::Body<'tcx>); fn check_crate(); fn check_crate_post(); fn check_mod(a: &'tcx rustc_hir::Mod<'tcx>, b: rustc_hir::HirId); @@ -237,5 +237,5 @@ macro_rules! declare_combined_early_lint_pass { } /// A lint pass boxed up as a trait object. -pub type EarlyLintPassObject = Box; -pub type LateLintPassObject<'tcx> = Box + 'tcx>; +pub(crate) type EarlyLintPassObject = Box; +pub(crate) type LateLintPassObject<'tcx> = Box + 'tcx>; diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs new file mode 100644 index 000000000000..da2b5878b19c --- /dev/null +++ b/compiler/rustc_lint/src/shadowed_into_iter.rs @@ -0,0 +1,157 @@ +use crate::lints::{ShadowedIntoIterDiag, ShadowedIntoIterDiagSub}; +use crate::{LateContext, LateLintPass, LintContext}; +use rustc_hir as hir; +use rustc_middle::ty::{self, Ty}; +use rustc_session::lint::FutureIncompatibilityReason; +use rustc_session::{declare_lint, impl_lint_pass}; +use rustc_span::edition::Edition; + +declare_lint! { + /// The `array_into_iter` lint detects calling `into_iter` on arrays. + /// + /// ### Example + /// + /// ```rust,edition2018 + /// # #![allow(unused)] + /// [1, 2, 3].into_iter().for_each(|n| { *n; }); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Since Rust 1.53, arrays implement `IntoIterator`. However, to avoid + /// breakage, `array.into_iter()` in Rust 2015 and 2018 code will still + /// behave as `(&array).into_iter()`, returning an iterator over + /// references, just like in Rust 1.52 and earlier. + /// This only applies to the method call syntax `array.into_iter()`, not to + /// any other syntax such as `for _ in array` or `IntoIterator::into_iter(array)`. + pub ARRAY_INTO_ITER, + Warn, + "detects calling `into_iter` on arrays in Rust 2015 and 2018", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021), + reference: "", + }; +} + +declare_lint! { + /// The `boxed_slice_into_iter` lint detects calling `into_iter` on boxed slices. + /// + /// ### Example + /// + /// ```rust,edition2021 + /// # #![allow(unused)] + /// vec![1, 2, 3].into_boxed_slice().into_iter().for_each(|n| { *n; }); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Since Rust 1.80.0, boxed slices implement `IntoIterator`. However, to avoid + /// breakage, `boxed_slice.into_iter()` in Rust 2015, 2018, and 2021 code will still + /// behave as `(&boxed_slice).into_iter()`, returning an iterator over + /// references, just like in Rust 1.79.0 and earlier. + /// This only applies to the method call syntax `boxed_slice.into_iter()`, not to + /// any other syntax such as `for _ in boxed_slice` or `IntoIterator::into_iter(boxed_slice)`. + pub BOXED_SLICE_INTO_ITER, + Warn, + "detects calling `into_iter` on boxed slices in Rust 2015, 2018, and 2021", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), + }; +} + +#[derive(Copy, Clone)] +pub struct ShadowedIntoIter; + +impl_lint_pass!(ShadowedIntoIter => [ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER]); + +impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let hir::ExprKind::MethodCall(call, receiver_arg, ..) = &expr.kind else { + return; + }; + + // Check if the method call actually calls the libcore + // `IntoIterator::into_iter`. + let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { + return; + }; + if Some(method_def_id) != cx.tcx.lang_items().into_iter_fn() { + return; + } + + // As this is a method call expression, we have at least one argument. + let receiver_ty = cx.typeck_results().expr_ty(receiver_arg); + let adjustments = cx.typeck_results().expr_adjustments(receiver_arg); + + let adjusted_receiver_tys: Vec<_> = + [receiver_ty].into_iter().chain(adjustments.iter().map(|adj| adj.target)).collect(); + + fn is_ref_to_array(ty: Ty<'_>) -> bool { + if let ty::Ref(_, pointee_ty, _) = *ty.kind() { pointee_ty.is_array() } else { false } + } + fn is_boxed_slice(ty: Ty<'_>) -> bool { + ty.is_box() && ty.boxed_ty().is_slice() + } + fn is_ref_to_boxed_slice(ty: Ty<'_>) -> bool { + if let ty::Ref(_, pointee_ty, _) = *ty.kind() { + is_boxed_slice(pointee_ty) + } else { + false + } + } + + let (lint, target, edition, can_suggest_ufcs) = + if is_ref_to_array(*adjusted_receiver_tys.last().unwrap()) + && let Some(idx) = adjusted_receiver_tys + .iter() + .copied() + .take_while(|ty| !is_ref_to_array(*ty)) + .position(|ty| ty.is_array()) + { + (ARRAY_INTO_ITER, "[T; N]", "2021", idx == 0) + } else if is_ref_to_boxed_slice(*adjusted_receiver_tys.last().unwrap()) + && let Some(idx) = adjusted_receiver_tys + .iter() + .copied() + .take_while(|ty| !is_ref_to_boxed_slice(*ty)) + .position(|ty| is_boxed_slice(ty)) + { + (BOXED_SLICE_INTO_ITER, "Box<[T]>", "2024", idx == 0) + } else { + return; + }; + + // If this expression comes from the `IntoIter::into_iter` inside of a for loop, + // we should just suggest removing the `.into_iter()` or changing it to `.iter()` + // to disambiguate if we want to iterate by-value or by-ref. + let sub = if let Some((_, hir::Node::Expr(parent_expr))) = + cx.tcx.hir().parent_iter(expr.hir_id).nth(1) + && let hir::ExprKind::Match(arg, [_], hir::MatchSource::ForLoopDesugar) = + &parent_expr.kind + && let hir::ExprKind::Call(path, [_]) = &arg.kind + && let hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoIterIntoIter, ..)) = + &path.kind + { + Some(ShadowedIntoIterDiagSub::RemoveIntoIter { + span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), + }) + } else if can_suggest_ufcs { + Some(ShadowedIntoIterDiagSub::UseExplicitIntoIter { + start_span: expr.span.shrink_to_lo(), + end_span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), + }) + } else { + None + }; + + cx.emit_span_lint( + lint, + call.ident.span, + ShadowedIntoIterDiag { target, edition, suggestion: call.ident.span, sub }, + ); + } +} diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index 4d1b1dc4fb78..6983e7abbd64 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -2,7 +2,7 @@ use crate::lints::{DropGlue, DropTraitConstraintsDiag}; use crate::LateContext; use crate::LateLintPass; use crate::LintContext; -use rustc_hir as hir; +use rustc_hir::{self as hir, LangItem}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::symbol::sym; @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { continue; }; let def_id = trait_predicate.trait_ref.def_id; - if cx.tcx.lang_items().drop_trait() == Some(def_id) { + if cx.tcx.is_lang_item(def_id, LangItem::Drop) { // Explicitly allow `impl Drop`, a drop-guards-as-unnameable-type pattern. if trait_predicate.trait_ref.self_ty().is_impl_trait() { continue; diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index f9f766f832af..f3a904022e9e 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -31,9 +31,9 @@ use rustc_span::{Span, Symbol}; use rustc_target::abi::{Abi, Size, WrappingRange}; use rustc_target::abi::{Integer, TagEncoding, Variants}; use rustc_target::spec::abi::Abi as SpecAbi; - use std::iter; use std::ops::ControlFlow; +use tracing::debug; declare_lint! { /// The `unused_comparisons` lint detects comparisons made useless by @@ -1741,13 +1741,13 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { let abi = cx.tcx.hir().get_foreign_abi(it.hir_id()); match it.kind { - hir::ForeignItemKind::Fn(decl, _, _) if !vis.is_internal_abi(abi) => { + hir::ForeignItemKind::Fn(decl, _, _, _) if !vis.is_internal_abi(abi) => { vis.check_foreign_fn(it.owner_id.def_id, decl); } - hir::ForeignItemKind::Static(ty, _) if !vis.is_internal_abi(abi) => { + hir::ForeignItemKind::Static(ty, _, _) if !vis.is_internal_abi(abi) => { vis.check_foreign_static(it.owner_id, ty.span); } - hir::ForeignItemKind::Fn(decl, _, _) => vis.check_fn(it.owner_id.def_id, decl), + hir::ForeignItemKind::Fn(decl, _, _, _) => vis.check_fn(it.owner_id.def_id, decl), hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (), } } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 8866b2be0784..195a0f72475f 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -10,9 +10,9 @@ use rustc_ast as ast; use rustc_ast::util::{classify, parser}; use rustc_ast::{ExprKind, StmtKind}; use rustc_errors::{pluralize, MultiSpan}; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, LangItem}; use rustc_infer::traits::util::elaborate; use rustc_middle::ty::adjustment; use rustc_middle::ty::{self, Ty}; @@ -22,6 +22,7 @@ use rustc_span::symbol::{kw, sym}; use rustc_span::{BytePos, Span}; use std::iter; use std::ops::ControlFlow; +use tracing::instrument; declare_lint! { /// The `unused_must_use` lint detects unused result of a type flagged as @@ -288,7 +289,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { is_ty_must_use(cx, boxed_ty, expr, span) .map(|inner| MustUsePath::Boxed(Box::new(inner))) } - ty::Adt(def, args) if cx.tcx.lang_items().pin_type() == Some(def.did()) => { + ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => { let pinned_ty = args.type_at(0); is_ty_must_use(cx, pinned_ty, expr, span) .map(|inner| MustUsePath::Pinned(Box::new(inner))) @@ -386,7 +387,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } } - // Returns whether further errors should be suppressed because either a lint has been emitted or the type should be ignored. + // Returns whether further errors should be suppressed because either a lint has been + // emitted or the type should be ignored. fn check_must_use_def( cx: &LateContext<'_>, def_id: DefId, @@ -676,7 +678,8 @@ trait UnusedDelimLint { return true; } - // Check if LHS needs parens to prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`. + // Check if LHS needs parens to prevent false-positives in cases like + // `fn x() -> u8 { ({ 0 } + 1) }`. // // FIXME: https://github.com/rust-lang/rust/issues/119426 // The syntax tree in this code is from after macro expansion, so the @@ -721,7 +724,8 @@ trait UnusedDelimLint { } } - // Check if RHS needs parens to prevent false-positives in cases like `if (() == return) {}`. + // Check if RHS needs parens to prevent false-positives in cases like `if (() == return) + // {}`. if !followed_by_block { return false; } @@ -1264,7 +1268,7 @@ impl EarlyLintPass for UnusedParens { ast::TyKind::TraitObject(..) => {} ast::TyKind::BareFn(b) if self.with_self_ty_parens && b.generic_params.len() > 0 => {} - ast::TyKind::ImplTrait(_, bounds, _) if bounds.len() > 1 => {} + ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {} _ => { let spans = if !ty.span.from_expansion() { r.span diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 536945457724..1913b9d6a1c3 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -34,9 +34,11 @@ declare_lint_pass! { CONST_EVALUATABLE_UNCHECKED, CONST_ITEM_MUTATION, DEAD_CODE, + DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, DEPRECATED, DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, DEPRECATED_IN_FUTURE, + DEPRECATED_SAFE, DEPRECATED_WHERE_CLAUSE_LOCATION, DUPLICATE_MACRO_ATTRIBUTES, ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, @@ -49,7 +51,6 @@ declare_lint_pass! { HIDDEN_GLOB_REEXPORTS, ILL_FORMED_ATTRIBUTE_INPUT, INCOMPLETE_INCLUDE, - INDIRECT_STRUCTURAL_MATCH, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, INLINE_NO_SANITIZE, INVALID_DOC_ATTRIBUTES, @@ -66,6 +67,7 @@ declare_lint_pass! { META_VARIABLE_MISUSE, MISSING_ABI, MISSING_FRAGMENT_SPECIFIER, + MISSING_UNSAFE_ON_EXTERN, MUST_NOT_SUSPEND, NAMED_ARGUMENTS_USED_POSITIONALLY, NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, @@ -74,10 +76,8 @@ declare_lint_pass! { ORDER_DEPENDENT_TRAIT_OBJECTS, OVERLAPPING_RANGE_ENDPOINTS, PATTERNS_IN_FNS_WITHOUT_BODY, - POINTER_STRUCTURAL_MATCH, PRIVATE_BOUNDS, PRIVATE_INTERFACES, - PROC_MACRO_BACK_COMPAT, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_LIFETIMES, @@ -90,6 +90,7 @@ declare_lint_pass! { RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, RUST_2021_PRELUDE_COLLISIONS, RUST_2024_INCOMPATIBLE_PAT, + SELF_CONSTRUCTOR_FROM_OUTER_ITEM, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, SINGLE_USE_LIFETIMES, SOFT_UNSTABLE, @@ -136,7 +137,6 @@ declare_lint_pass! { USELESS_DEPRECATED, WARNINGS, WASM_C_ABI, - WHERE_CLAUSES_OBJECT_SAFETY, WRITES_THROUGH_IMMUTABLE_POINTER, // tidy-alphabetical-end ] @@ -512,8 +512,9 @@ declare_lint! { /// This will produce: /// /// ```text - /// error: external crate `regex` unused in `lint_example`: remove the dependency or add `use regex as _;` + /// error: extern crate `regex` is unused in crate `lint_example` /// | + /// = help: remove the dependency or add `use regex as _;` to the crate root /// note: the lint level is defined here /// --> src/lib.rs:1:9 /// | @@ -1315,10 +1316,8 @@ declare_lint! { /// * If you are trying to perform a one-time initialization of a global: /// * If the value can be computed at compile-time, consider using /// const-compatible values (see [Constant Evaluation]). - /// * For more complex single-initialization cases, consider using a - /// third-party crate, such as [`lazy_static`] or [`once_cell`]. - /// * If you are using the [nightly channel], consider the new - /// [`lazy`] module in the standard library. + /// * For more complex single-initialization cases, consider using + /// [`std::sync::LazyLock`]. /// * If you truly need a mutable global, consider using a [`static`], /// which has a variety of options: /// * Simple data types can be directly defined and mutated with an @@ -1333,9 +1332,7 @@ declare_lint! { /// [Constant Evaluation]: https://doc.rust-lang.org/reference/const_eval.html /// [`static`]: https://doc.rust-lang.org/reference/items/static-items.html /// [mutable `static`]: https://doc.rust-lang.org/reference/items/static-items.html#mutable-statics - /// [`lazy`]: https://doc.rust-lang.org/nightly/std/lazy/index.html - /// [`lazy_static`]: https://crates.io/crates/lazy_static - /// [`once_cell`]: https://crates.io/crates/once_cell + /// [`std::sync::LazyLock`]: https://doc.rust-lang.org/stable/std/sync/struct.LazyLock.html /// [`atomic`]: https://doc.rust-lang.org/std/sync/atomic/index.html /// [`Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html pub CONST_ITEM_MUTATION, @@ -2097,47 +2094,6 @@ declare_lint! { "detects labels that are never used" } -declare_lint! { - /// The `where_clauses_object_safety` lint detects for [object safety] of - /// [where clauses]. - /// - /// [object safety]: https://doc.rust-lang.org/reference/items/traits.html#object-safety - /// [where clauses]: https://doc.rust-lang.org/reference/items/generics.html#where-clauses - /// - /// ### Example - /// - /// ```rust,no_run - /// trait Trait {} - /// - /// trait X { fn foo(&self) where Self: Trait; } - /// - /// impl X for () { fn foo(&self) {} } - /// - /// impl Trait for dyn X {} - /// - /// // Segfault at opt-level 0, SIGILL otherwise. - /// pub fn main() { ::foo(&()); } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// The compiler previously allowed these object-unsafe bounds, which was - /// incorrect. This is a [future-incompatible] lint to transition this to - /// a hard error in the future. See [issue #51443] for more details. - /// - /// [issue #51443]: https://github.com/rust-lang/rust/issues/51443 - /// [future-incompatible]: ../index.md#future-incompatible-lints - pub WHERE_CLAUSES_OBJECT_SAFETY, - Warn, - "checks the object safety of where clauses", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, - reference: "issue #51443 ", - }; -} - declare_lint! { /// The `proc_macro_derive_resolution_fallback` lint detects proc macro /// derives using inaccessible names from parent modules. @@ -2206,8 +2162,7 @@ declare_lint! { } declare_lint! { - /// The `macro_use_extern_crate` lint detects the use of the - /// [`macro_use` attribute]. + /// The `macro_use_extern_crate` lint detects the use of the [`macro_use` attribute]. /// /// ### Example /// @@ -2225,12 +2180,13 @@ declare_lint! { /// This will produce: /// /// ```text - /// error: deprecated `#[macro_use]` attribute used to import macros should be replaced at use sites with a `use` item to import the macro instead + /// error: applying the `#[macro_use]` attribute to an `extern crate` item is deprecated /// --> src/main.rs:3:1 /// | /// 3 | #[macro_use] /// | ^^^^^^^^^^^^ /// | + /// = help: remove it and import macros at use sites with a `use` item instead /// note: the lint level is defined here /// --> src/main.rs:1:9 /// | @@ -2354,52 +2310,6 @@ declare_lint! { "outlives requirements can be inferred" } -declare_lint! { - /// The `indirect_structural_match` lint detects a `const` in a pattern - /// that manually implements [`PartialEq`] and [`Eq`]. - /// - /// [`PartialEq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html - /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html - /// - /// ### Example - /// - /// ```rust,compile_fail - /// #![deny(indirect_structural_match)] - /// - /// struct NoDerive(i32); - /// impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } - /// impl Eq for NoDerive { } - /// #[derive(PartialEq, Eq)] - /// struct WrapParam(T); - /// const WRAP_INDIRECT_PARAM: & &WrapParam = & &WrapParam(NoDerive(0)); - /// fn main() { - /// match WRAP_INDIRECT_PARAM { - /// WRAP_INDIRECT_PARAM => { } - /// _ => { } - /// } - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// The compiler unintentionally accepted this form in the past. This is a - /// [future-incompatible] lint to transition this to a hard error in the - /// future. See [issue #62411] for a complete description of the problem, - /// and some possible solutions. - /// - /// [issue #62411]: https://github.com/rust-lang/rust/issues/62411 - /// [future-incompatible]: ../index.md#future-incompatible-lints - pub INDIRECT_STRUCTURAL_MATCH, - Warn, - "constant used in pattern contains value of non-structural-match type in a field or a variant", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, - reference: "issue #120362 ", - }; -} - declare_lint! { /// The `deprecated_in_future` lint is internal to rustc and should not be /// used by user code. @@ -2417,45 +2327,6 @@ declare_lint! { report_in_external_macro } -declare_lint! { - /// The `pointer_structural_match` lint detects pointers used in patterns whose behaviour - /// cannot be relied upon across compiler versions and optimization levels. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// #![deny(pointer_structural_match)] - /// fn foo(a: usize, b: usize) -> usize { a + b } - /// const FOO: fn(usize, usize) -> usize = foo; - /// fn main() { - /// match FOO { - /// FOO => {}, - /// _ => {}, - /// } - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Previous versions of Rust allowed function pointers and all raw pointers in patterns. - /// While these work in many cases as expected by users, it is possible that due to - /// optimizations pointers are "not equal to themselves" or pointers to different functions - /// compare as equal during runtime. This is because LLVM optimizations can deduplicate - /// functions if their bodies are the same, thus also making pointers to these functions point - /// to the same location. Additionally functions may get duplicated if they are instantiated - /// in different crates and not deduplicated again via LTO. Pointer identity for memory - /// created by `const` is similarly unreliable. - pub POINTER_STRUCTURAL_MATCH, - Warn, - "pointers are not structural-match", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, - reference: "issue #120362 ", - }; -} - declare_lint! { /// The `ambiguous_associated_items` lint detects ambiguity between /// [associated items] and [enum variants]. @@ -3149,6 +3020,47 @@ declare_lint! { "detects `#[unstable]` on stable trait implementations for stable types" } +declare_lint! { + /// The `self_constructor_from_outer_item` lint detects cases where the `Self` constructor + /// was silently allowed due to a bug in the resolver, and which may produce surprising + /// and unintended behavior. + /// + /// Using a `Self` type alias from an outer item was never intended, but was silently allowed. + /// This is deprecated -- and is a hard error when the `Self` type alias references generics + /// that are not in scope. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(self_constructor_from_outer_item)] + /// + /// struct S0(usize); + /// + /// impl S0 { + /// fn foo() { + /// const C: S0 = Self(0); + /// fn bar() -> S0 { + /// Self(0) + /// } + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The `Self` type alias should not be reachable because nested items are not associated with + /// the scope of the parameters from the parent item. + pub SELF_CONSTRUCTOR_FROM_OUTER_ITEM, + Warn, + "detect unsupported use of `Self` from outer item", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reference: "issue #124186 ", + }; +} + declare_lint! { /// The `semicolon_in_expressions_from_macros` lint detects trailing semicolons /// in macro bodies when the macro is invoked in expression position. @@ -3339,11 +3251,18 @@ declare_lint! { /// /// ### Explanation /// - /// This lint is only active when `--check-cfg` arguments are being passed - /// to the compiler and triggers whenever an unexpected condition name or value is used. + /// This lint is only active when [`--check-cfg`][check-cfg] arguments are being + /// passed to the compiler and triggers whenever an unexpected condition name or value is + /// used. /// - /// The known condition include names or values passed in `--check-cfg`, and some - /// well-knows names and values built into the compiler. + /// See the [Checking Conditional Configurations][check-cfg] section for more + /// details. + /// + /// See the [Cargo Specifics][unexpected_cfgs_lint_config] section for configuring this lint in + /// `Cargo.toml`. + /// + /// [check-cfg]: https://doc.rust-lang.org/nightly/rustc/check-cfg.html + /// [unexpected_cfgs_lint_config]: https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html#check-cfg-in-lintsrust-table pub UNEXPECTED_CFGS, Warn, "detects unexpected names and values in `#[cfg]` conditions", @@ -3751,59 +3670,12 @@ declare_lint! { "detects invalid `#[doc(...)]` attributes", } -declare_lint! { - /// The `proc_macro_back_compat` lint detects uses of old versions of certain - /// proc-macro crates, which have hardcoded workarounds in the compiler. - /// - /// ### Example - /// - /// ```rust,ignore (needs-dependency) - /// - /// use time_macros_impl::impl_macros; - /// struct Foo; - /// impl_macros!(Foo); - /// ``` - /// - /// This will produce: - /// - /// ```text - /// warning: using an old version of `time-macros-impl` - /// ::: $DIR/group-compat-hack.rs:27:5 - /// | - /// LL | impl_macros!(Foo); - /// | ------------------ in this macro invocation - /// | - /// = note: `#[warn(proc_macro_back_compat)]` on by default - /// = warning: 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 #83125 - /// = note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage - /// = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - /// ``` - /// - /// ### Explanation - /// - /// Eventually, the backwards-compatibility hacks present in the compiler will be removed, - /// causing older versions of certain crates to stop compiling. - /// This is a [future-incompatible] lint to ease the transition to an error. - /// See [issue #83125] for more details. - /// - /// [issue #83125]: https://github.com/rust-lang/rust/issues/83125 - /// [future-incompatible]: ../index.md#future-incompatible-lints - pub PROC_MACRO_BACK_COMPAT, - Deny, - "detects usage of old versions of certain proc-macro crates", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, - reference: "issue #83125 ", - }; -} - declare_lint! { /// The `rust_2021_incompatible_or_patterns` lint detects usage of old versions of or-patterns. /// /// ### Example /// - /// ```rust,compile_fail + /// ```rust,edition2018,compile_fail /// #![deny(rust_2021_incompatible_or_patterns)] /// /// macro_rules! match_any { @@ -3843,7 +3715,7 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + /// ```rust,edition2018,compile_fail /// #![deny(rust_2021_prelude_collisions)] /// /// trait Foo { @@ -4263,8 +4135,7 @@ declare_lint! { /// /// // where absurd is a function with the following signature /// // (it's sound, because `!` always marks unreachable code): - /// fn absurd(_: !) -> T { ... } - // FIXME: use `core::convert::absurd` here instead, once it's merged + /// fn absurd(never: !) -> T { ... } /// ``` /// /// While it's convenient to be able to use non-diverging code in one of the branches (like @@ -4321,7 +4192,65 @@ declare_lint! { /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html pub NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, Warn, - "never type fallback affecting unsafe function calls" + "never type fallback affecting unsafe function calls", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange, + reference: "issue #123748 ", + }; + report_in_external_macro +} + +declare_lint! { + /// The `dependency_on_unit_never_type_fallback` lint detects cases where code compiles with + /// [never type fallback] being [`()`], but will stop compiling with fallback being [`!`]. + /// + /// [never type fallback]: https://doc.rust-lang.org/nightly/core/primitive.never.html#never-type-fallback + /// [`!`]: https://doc.rust-lang.org/core/primitive.never.html + /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(dependency_on_unit_never_type_fallback)] + /// fn main() { + /// if true { + /// // return has type `!` which, is some cases, causes never type fallback + /// return + /// } else { + /// // the type produced by this call is not specified explicitly, + /// // so it will be inferred from the previous branch + /// Default::default() + /// }; + /// // depending on the fallback, this may compile (because `()` implements `Default`), + /// // or it may not (because `!` does not implement `Default`) + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Due to historic reasons never type fallback was `()`, meaning that `!` got spontaneously + /// coerced to `()`. There are plans to change that, but they may make the code such as above + /// not compile. Instead of depending on the fallback, you should specify the type explicitly: + /// ``` + /// if true { + /// return + /// } else { + /// // type is explicitly specified, fallback can't hurt us no more + /// <() as Default>::default() + /// }; + /// ``` + /// + /// See [Tracking Issue for making `!` fall back to `!`](https://github.com/rust-lang/rust/issues/123748). + pub DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, + Warn, + "never type fallback affecting unsafe function calls", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reference: "issue #123748 ", + }; + report_in_external_macro } declare_lint! { @@ -4664,16 +4593,18 @@ declare_lint! { declare_lint! { /// The `elided_lifetimes_in_associated_constant` lint detects elided lifetimes - /// that were erroneously allowed in associated constants. + /// in associated constants when there are other lifetimes in scope. This was + /// accidentally supported, and this lint was later relaxed to allow eliding + /// lifetimes to `'static` when there are no lifetimes in scope. /// /// ### Example /// /// ```rust,compile_fail /// #![deny(elided_lifetimes_in_associated_constant)] /// - /// struct Foo; + /// struct Foo<'a>(&'a ()); /// - /// impl Foo { + /// impl<'a> Foo<'a> { /// const STR: &str = "hello, world"; /// } /// ``` @@ -4886,3 +4817,88 @@ declare_lint! { reference: "issue #124559 ", }; } + +declare_lint! { + /// The `deprecated_safe` lint detects unsafe functions being used as safe + /// functions. + /// + /// ### Example + /// + /// ```rust,edition2021,compile_fail + /// #![deny(deprecated_safe)] + /// // edition 2021 + /// use std::env; + /// fn enable_backtrace() { + /// env::set_var("RUST_BACKTRACE", "1"); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Rust [editions] allow the language to evolve without breaking backward + /// compatibility. This lint catches code that uses `unsafe` functions that + /// were declared as safe (non-`unsafe`) in earlier editions. If you switch + /// the compiler to a new edition without updating the code, then it + /// will fail to compile if you are using a function previously marked as + /// safe. + /// + /// You can audit the code to see if it suffices the preconditions of the + /// `unsafe` code, and if it does, you can wrap it in an `unsafe` block. If + /// you can't fulfill the preconditions, you probably need to switch to a + /// different way of doing what you want to achieve. + /// + /// This lint can automatically wrap the calls in `unsafe` blocks, but this + /// obviously cannot verify that the preconditions of the `unsafe` + /// functions are fulfilled, so that is still up to the user. + /// + /// The lint is currently "allow" by default, but that might change in the + /// future. + /// + /// [editions]: https://doc.rust-lang.org/edition-guide/ + pub DEPRECATED_SAFE, + Allow, + "detects unsafe functions being used as safe functions", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), + reference: "issue #27970 ", + }; +} + +declare_lint! { + /// The `missing_unsafe_on_extern` lint detects missing unsafe keyword on extern declarations. + /// + /// ### Example + /// + /// ```rust + /// #![feature(unsafe_extern_blocks)] + /// #![warn(missing_unsafe_on_extern)] + /// #![allow(dead_code)] + /// + /// extern "C" { + /// fn foo(_: i32); + /// } + /// + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Declaring extern items, even without ever using them, can cause Undefined Behavior. We + /// should consider all sources of Undefined Behavior to be unsafe. + /// + /// This is a [future-incompatible] lint to transition this to a + /// hard error in the future. + /// + /// [future-incompatible]: ../index.md#future-incompatible-lints + pub MISSING_UNSAFE_ON_EXTERN, + Allow, + "detects missing unsafe keyword on extern declarations", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), + reference: "issue #123743 ", + }; +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index e06e3e9b805f..70330c445776 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -1,3 +1,4 @@ +// tidy-alphabetical-start pub use self::Level::*; use rustc_ast::node_id::NodeId; use rustc_ast::{AttrId, Attribute}; @@ -6,12 +7,15 @@ use rustc_data_structures::stable_hasher::{ HashStable, StableCompare, StableHasher, ToStableHashKey, }; use rustc_error_messages::{DiagMessage, MultiSpan}; +use rustc_hir::def::Namespace; use rustc_hir::HashStableContext; use rustc_hir::HirId; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::Edition; +use rustc_span::symbol::MacroRulesNormalizedIdent; use rustc_span::{sym, symbol::Ident, Span, Symbol}; use rustc_target::spec::abi::Abi; +// tidy-alphabetical-end use serde::{Deserialize, Serialize}; @@ -565,19 +569,44 @@ pub struct AmbiguityErrorDiag { pub b2_help_msgs: Vec, } +#[derive(Debug, Clone)] +pub enum DeprecatedSinceKind { + InEffect, + InFuture, + InVersion(String), +} + // This could be a closure, but then implementing derive trait // becomes hacky (and it gets allocated). #[derive(Debug)] pub enum BuiltinLintDiag { - Normal, AbsPathWithModule(Span), - ProcMacroDeriveResolutionFallback(Span), + ProcMacroDeriveResolutionFallback { + span: Span, + ns: Namespace, + ident: Ident, + }, MacroExpandedMacroExportsAccessedByAbsolutePaths(Span), ElidedLifetimesInPaths(usize, Span, bool, Span), - UnknownCrateTypes(Span, String, String), - UnusedImports(String, Vec<(Span, String)>, Option), + UnknownCrateTypes { + span: Span, + candidate: Option, + }, + UnusedImports { + remove_whole_use: bool, + num_to_remove: usize, + remove_spans: Vec, + test_module_span: Option, + span_snippets: Vec, + }, RedundantImport(Vec<(Span, bool)>, Ident), - DeprecatedMacro(Option, Span), + DeprecatedMacro { + suggestion: Option, + suggestion_span: Span, + note: Option, + path: String, + since_kind: DeprecatedSinceKind, + }, MissingAbi(Span, Abi), UnusedDocComment(Span), UnusedBuiltinAttribute { @@ -585,18 +614,23 @@ pub enum BuiltinLintDiag { macro_name: String, invoc_span: Span, }, - PatternsInFnsWithoutBody(Span, Ident), + PatternsInFnsWithoutBody { + span: Span, + ident: Ident, + is_foreign: bool, + }, LegacyDeriveHelpers(Span), - ProcMacroBackCompat(String), OrPatternsBackCompat(Span, String), - ReservedPrefix(Span), + ReservedPrefix(Span, String), TrailingMacro(bool, Ident), BreakWithLabelAndLoop(Span), - NamedAsmLabel(String), UnicodeTextFlow(Span, String), UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>), UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>), - DeprecatedWhereclauseLocation(Option<(Span, String)>), + DeprecatedWhereclauseLocation(Span, Option<(Span, String)>), + MissingUnsafeOnExtern { + suggestion: Span, + }, SingleUseLifetime { /// Span of the parameter which declares this lifetime. param_span: Span, @@ -606,6 +640,7 @@ pub enum BuiltinLintDiag { /// Span of the single use, or None if the lifetime is never used. /// If true, the lifetime will be fully elided. use_span: Option<(Span, bool)>, + ident: Ident, }, NamedArgumentUsedPositionally { /// Span where the named argument is used by position and will be replaced with the named @@ -620,7 +655,10 @@ pub enum BuiltinLintDiag { /// Indicates if the named argument is used as a width/precision for formatting is_formatting_arg: bool, }, - ByteSliceInPackedStructWithDerive, + ByteSliceInPackedStructWithDerive { + // FIXME: enum of byte/string + ty: String, + }, UnusedExternCrate { removal_span: Span, }, @@ -658,14 +696,51 @@ pub enum BuiltinLintDiag { AssociatedConstElidedLifetime { elided: bool, span: Span, + lifetimes_in_scope: MultiSpan, }, RedundantImportVisibility { span: Span, max_vis: String, + import_vis: String, }, - MaybeTypo { + UnknownDiagnosticAttribute { span: Span, - name: Symbol, + typo_name: Option, + }, + MacroUseDeprecated, + UnusedMacroUse, + PrivateExternCrateReexport { + source: Ident, + extern_crate_span: Span, + }, + UnusedLabel, + MacroIsPrivate(Ident), + UnusedMacroDefinition(Symbol), + MacroRuleNeverUsed(usize, Symbol), + UnstableFeature(DiagMessage), + AvoidUsingIntelSyntax, + AvoidUsingAttSyntax, + IncompleteInclude, + UnnameableTestItems, + DuplicateMacroAttribute, + CfgAttrNoAttributes, + CrateTypeInCfgAttr, + CrateNameInCfgAttr, + MissingFragmentSpecifier, + MetaVariableStillRepeating(MacroRulesNormalizedIdent), + MetaVariableWrongOperator, + DuplicateMatcherBinding, + UnknownMacroVariable(MacroRulesNormalizedIdent), + UnusedCrateDependency { + extern_crate: Symbol, + local_crate: Symbol, + }, + WasmCAbi, + IllFormedAttributeInput { + suggestions: Vec, + }, + InnerAttributeUnstable { + is_macro: bool, }, } @@ -676,9 +751,6 @@ pub struct BufferedEarlyLint { /// The span of code that we are linting on. pub span: MultiSpan, - /// The lint message. - pub msg: DiagMessage, - /// The `NodeId` of the AST node that generated the lint. pub node_id: NodeId, @@ -697,21 +769,7 @@ pub struct LintBuffer { impl LintBuffer { pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) { - let arr = self.map.entry(early_lint.node_id).or_default(); - arr.push(early_lint); - } - - pub fn add_lint( - &mut self, - lint: &'static Lint, - node_id: NodeId, - span: MultiSpan, - msg: impl Into, - diagnostic: BuiltinLintDiag, - ) { - let lint_id = LintId::of(lint); - let msg = msg.into(); - self.add_early_lint(BufferedEarlyLint { lint_id, node_id, span, msg, diagnostic }); + self.map.entry(early_lint.node_id).or_default().push(early_lint); } pub fn take(&mut self, id: NodeId) -> Vec { @@ -722,22 +780,16 @@ impl LintBuffer { pub fn buffer_lint( &mut self, lint: &'static Lint, - id: NodeId, - sp: impl Into, - msg: impl Into, - ) { - self.add_lint(lint, id, sp.into(), msg, BuiltinLintDiag::Normal) - } - - pub fn buffer_lint_with_diagnostic( - &mut self, - lint: &'static Lint, - id: NodeId, - sp: impl Into, - msg: impl Into, + node_id: NodeId, + span: impl Into, diagnostic: BuiltinLintDiag, ) { - self.add_lint(lint, id, sp.into(), msg, diagnostic) + self.add_early_lint(BufferedEarlyLint { + lint_id: LintId::of(lint), + node_id, + span: span.into(), + diagnostic, + }); } } diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 024f6f89a4b7..cdaabb036c2a 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -23,6 +23,7 @@ const OPTIONAL_COMPONENTS: &[&str] = &[ "nvptx", "hexagon", "riscv", + "xtensa", "bpf", ]; diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 067374c02610..a027ddcc1508 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -155,6 +155,12 @@ extern "C" void LLVMRustTimeTraceProfilerFinish(const char* FileName) { #define SUBTARGET_SPARC #endif +#ifdef LLVM_COMPONENT_XTENSA +#define SUBTARGET_XTENSA SUBTARGET(XTENSA) +#else +#define SUBTARGET_XTENSA +#endif + #ifdef LLVM_COMPONENT_HEXAGON #define SUBTARGET_HEXAGON SUBTARGET(Hexagon) #else @@ -180,6 +186,7 @@ extern "C" void LLVMRustTimeTraceProfilerFinish(const char* FileName) { SUBTARGET_MSP430 \ SUBTARGET_SPARC \ SUBTARGET_HEXAGON \ + SUBTARGET_XTENSA \ SUBTARGET_RISCV \ SUBTARGET_LOONGARCH \ @@ -1488,13 +1495,15 @@ LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M, // a ThinLTO summary attached. struct LLVMRustThinLTOBuffer { std::string data; + std::string thin_link_data; }; extern "C" LLVMRustThinLTOBuffer* -LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin) { +LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin, bool emit_summary) { auto Ret = std::make_unique(); { auto OS = raw_string_ostream(Ret->data); + auto ThinLinkOS = raw_string_ostream(Ret->thin_link_data); { if (is_thin) { PassBuilder PB; @@ -1508,7 +1517,10 @@ LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin) { PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); ModulePassManager MPM; - MPM.addPass(ThinLTOBitcodeWriterPass(OS, nullptr)); + // We only pass ThinLinkOS to be filled in if we want the summary, + // because otherwise LLVM does extra work and may double-emit some + // errors or warnings. + MPM.addPass(ThinLTOBitcodeWriterPass(OS, emit_summary ? &ThinLinkOS : nullptr)); MPM.run(*unwrap(M), MAM); } else { WriteBitcodeToFile(*unwrap(M), OS); @@ -1533,6 +1545,16 @@ LLVMRustThinLTOBufferLen(const LLVMRustThinLTOBuffer *Buffer) { return Buffer->data.length(); } +extern "C" const void* +LLVMRustThinLTOBufferThinLinkDataPtr(const LLVMRustThinLTOBuffer *Buffer) { + return Buffer->thin_link_data.data(); +} + +extern "C" size_t +LLVMRustThinLTOBufferThinLinkDataLen(const LLVMRustThinLTOBuffer *Buffer) { + return Buffer->thin_link_data.length(); +} + // This is what we used to parse upstream bitcode for actual ThinLTO // processing. We'll call this once per module optimized through ThinLTO, and // it'll be called concurrently on many threads. diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index 6a570c97c888..e5366c9b5183 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs @@ -1,7 +1,9 @@ +// tidy-alphabetical-start +#![allow(internal_features)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![allow(internal_features)] +// tidy-alphabetical-end // NOTE: This crate only exists to allow linking on mingw targets. @@ -188,6 +190,13 @@ pub fn initialize_available_targets() { LLVMInitializeHexagonAsmPrinter, LLVMInitializeHexagonAsmParser ); + init_target!( + llvm_component = "xtensa", + LLVMInitializeXtensaTargetInfo, + LLVMInitializeXtensaTarget, + LLVMInitializeXtensaTargetMC, + LLVMInitializeXtensaAsmParser + ); init_target!( llvm_component = "webassembly", LLVMInitializeWebAssemblyTargetInfo, diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml index 3ff86f700a53..fe399bc77e32 100644 --- a/compiler/rustc_log/Cargo.toml +++ b/compiler/rustc_log/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" tracing = "0.1.28" tracing-core = "=0.1.30" # FIXME(Nilstrieb) tracing has a deadlock: https://github.com/tokio-rs/tracing/issues/2635 tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } -tracing-tree = "0.3.0" +tracing-tree = "0.3.1" # tidy-alphabetical-end [dev-dependencies] diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index 81257f9be882..01b6e342df04 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -58,6 +58,7 @@ pub struct LoggerConfig { pub verbose_thread_ids: Result, pub backtrace: Result, pub wraptree: Result, + pub lines: Result, } impl LoggerConfig { @@ -69,6 +70,7 @@ impl LoggerConfig { verbose_thread_ids: env::var(format!("{env}_THREAD_IDS")), backtrace: env::var(format!("{env}_BACKTRACE")), wraptree: env::var(format!("{env}_WRAPTREE")), + lines: env::var(format!("{env}_LINES")), } } } @@ -101,14 +103,19 @@ pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> { Err(_) => false, }; + let lines = match cfg.lines { + Ok(v) => &v == "1", + Err(_) => false, + }; + let mut layer = tracing_tree::HierarchicalLayer::default() .with_writer(io::stderr) - .with_indent_lines(true) .with_ansi(color_logs) .with_targets(true) .with_verbose_exit(verbose_entry_exit) .with_verbose_entry(verbose_entry_exit) .with_indent_amount(2) + .with_indent_lines(lines) .with_thread_ids(verbose_thread_ids) .with_thread_names(verbose_thread_ids); @@ -159,7 +166,9 @@ where if !target.contains(&self.backtrace_target) { return Ok(()); } - let backtrace = std::backtrace::Backtrace::capture(); + // Use Backtrace::force_capture because we don't want to depend on the + // RUST_BACKTRACE environment variable being set. + let backtrace = std::backtrace::Backtrace::force_capture(); writeln!(writer, "stack backtrace: \n{backtrace:?}") } } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 9d21d88165e0..a3abbdcf18ca 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -78,7 +78,7 @@ impl<'a> DiagnosticDerive<'a> { #[track_caller] fn into_diag( self, - dcx: &'_sess rustc_errors::DiagCtxt, + dcx: rustc_errors::DiagCtxtHandle<'_sess>, level: rustc_errors::Level ) -> rustc_errors::Diag<'_sess, G> { #implementation @@ -105,25 +105,12 @@ impl<'a> LintDiagnosticDerive<'a> { pub(crate) fn into_tokens(self) -> TokenStream { let LintDiagnosticDerive { mut structure } = self; let kind = DiagnosticDeriveKind::LintDiagnostic; + let slugs = RefCell::new(Vec::new()); let implementation = kind.each_variant(&mut structure, |mut builder, variant| { let preamble = builder.preamble(variant); let body = builder.body(variant); - let formatting_init = &builder.formatting_init; - quote! { - #preamble - #formatting_init - #body - diag - } - }); - - let slugs = RefCell::new(Vec::new()); - let msg = kind.each_variant(&mut structure, |mut builder, variant| { - // Collect the slug by generating the preamble. - let _ = builder.preamble(variant); - - match builder.slug.value_ref() { + let primary_message = match builder.slug.value_ref() { None => { span_err(builder.span, "diagnostic slug not specified") .help( @@ -146,9 +133,18 @@ impl<'a> LintDiagnosticDerive<'a> { Some(slug) => { slugs.borrow_mut().push(slug.clone()); quote! { - crate::fluent_generated::#slug.into() + diag.primary_message(crate::fluent_generated::#slug); } } + }; + + let formatting_init = &builder.formatting_init; + quote! { + #primary_message + #preamble + #formatting_init + #body + diag } }); @@ -161,10 +157,6 @@ impl<'a> LintDiagnosticDerive<'a> { ) { #implementation; } - - fn msg(&self) -> rustc_errors::DiagMessage { - #msg - } } }); for test in slugs.borrow().iter().map(|s| generate_test(s, &structure)) { diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index ae481efb263d..46bd80c2df64 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -158,7 +158,9 @@ impl DiagnosticDeriveVariantBuilder { let slug = subdiag.slug.unwrap_or_else(|| match subdiag.kind { SubdiagnosticKind::Label => parse_quote! { _subdiag::label }, SubdiagnosticKind::Note => parse_quote! { _subdiag::note }, + SubdiagnosticKind::NoteOnce => parse_quote! { _subdiag::note_once }, SubdiagnosticKind::Help => parse_quote! { _subdiag::help }, + SubdiagnosticKind::HelpOnce => parse_quote! { _subdiag::help_once }, SubdiagnosticKind::Warn => parse_quote! { _subdiag::warn }, SubdiagnosticKind::Suggestion { .. } => parse_quote! { _subdiag::suggestion }, SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), @@ -233,9 +235,11 @@ impl DiagnosticDeriveVariantBuilder { }; let fn_ident = format_ident!("{}", subdiag); match subdiag { - SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => { - Ok(self.add_subdiagnostic(&fn_ident, slug)) - } + SubdiagnosticKind::Note + | SubdiagnosticKind::NoteOnce + | SubdiagnosticKind::Help + | SubdiagnosticKind::HelpOnce + | SubdiagnosticKind::Warn => Ok(self.add_subdiagnostic(&fn_ident, slug)), SubdiagnosticKind::Label | SubdiagnosticKind::Suggestion { .. } => { throw_invalid_attr!(attr, |diag| diag .help("`#[label]` and `#[suggestion]` can only be applied to fields")); @@ -331,7 +335,7 @@ impl DiagnosticDeriveVariantBuilder { } } (Meta::Path(_), "subdiagnostic") => { - return Ok(quote! { diag.subdiagnostic(diag.dcx, #binding); }); + return Ok(quote! { diag.subdiagnostic(#binding); }); } _ => (), } @@ -347,7 +351,11 @@ impl DiagnosticDeriveVariantBuilder { report_error_if_not_applied_to_span(attr, &info)?; Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) } - SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => { + SubdiagnosticKind::Note + | SubdiagnosticKind::NoteOnce + | SubdiagnosticKind::Help + | SubdiagnosticKind::HelpOnce + | SubdiagnosticKind::Warn => { let inner = info.ty.inner_type(); if type_matches_path(inner, &["rustc_span", "Span"]) || type_matches_path(inner, &["rustc_span", "MultiSpan"]) diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index 389d88bebc78..134045d0644c 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -91,7 +91,7 @@ pub fn diagnostic_derive(mut s: Structure<'_>) -> TokenStream { /// Then, later, to emit the error: /// /// ```ignore (rust) -/// cx.span_lint(INVALID_ATOMIC_ORDERING, fail_order_arg_span, AtomicOrderingInvalidLint { +/// cx.emit_span_lint(INVALID_ATOMIC_ORDERING, fail_order_arg_span, AtomicOrderingInvalidLint { /// method, /// success_ordering, /// fail_ordering, diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 45236771bce6..69014f39925a 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -510,11 +510,11 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { .map(|binding| self.generate_field_attr_code(binding, kind_stats)) .collect(); - if kind_slugs.is_empty() { + if kind_slugs.is_empty() && !self.has_subdiagnostic { if self.is_enum { // It's okay for a variant to not be a subdiagnostic at all.. return Ok(quote! {}); - } else if !self.has_subdiagnostic { + } else { // ..but structs should always be _something_. throw_span_err!( self.variant.ast().ident.span().unwrap(), diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 4684306e2359..05a5a32514bb 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -575,8 +575,12 @@ pub(super) enum SubdiagnosticKind { Label, /// `#[note(...)]` Note, + /// `#[note_once(...)]` + NoteOnce, /// `#[help(...)]` Help, + /// `#[help_once(...)]` + HelpOnce, /// `#[warning(...)]` Warn, /// `#[suggestion{,_short,_hidden,_verbose}]` @@ -624,7 +628,9 @@ impl SubdiagnosticVariant { let mut kind = match name { "label" => SubdiagnosticKind::Label, "note" => SubdiagnosticKind::Note, + "note_once" => SubdiagnosticKind::NoteOnce, "help" => SubdiagnosticKind::Help, + "help_once" => SubdiagnosticKind::HelpOnce, "warning" => SubdiagnosticKind::Warn, _ => { // Recover old `#[(multipart_)suggestion_*]` syntaxes @@ -682,7 +688,9 @@ impl SubdiagnosticVariant { match kind { SubdiagnosticKind::Label | SubdiagnosticKind::Note + | SubdiagnosticKind::NoteOnce | SubdiagnosticKind::Help + | SubdiagnosticKind::HelpOnce | SubdiagnosticKind::Warn | SubdiagnosticKind::MultipartSuggestion { .. } => { return Ok(Some(SubdiagnosticVariant { kind, slug: None, no_span: false })); @@ -836,7 +844,9 @@ impl SubdiagnosticVariant { } SubdiagnosticKind::Label | SubdiagnosticKind::Note + | SubdiagnosticKind::NoteOnce | SubdiagnosticKind::Help + | SubdiagnosticKind::HelpOnce | SubdiagnosticKind::Warn => {} } @@ -849,7 +859,9 @@ impl quote::IdentFragment for SubdiagnosticKind { match self { SubdiagnosticKind::Label => write!(f, "label"), SubdiagnosticKind::Note => write!(f, "note"), + SubdiagnosticKind::NoteOnce => write!(f, "note_once"), SubdiagnosticKind::Help => write!(f, "help"), + SubdiagnosticKind::HelpOnce => write!(f, "help_once"), SubdiagnosticKind::Warn => write!(f, "warn"), SubdiagnosticKind::Suggestion { .. } => write!(f, "suggestions_with_style"), SubdiagnosticKind::MultipartSuggestion { .. } => { diff --git a/compiler/rustc_macros/src/extension.rs b/compiler/rustc_macros/src/extension.rs index 5377bbdfeabd..bbaa477237b1 100644 --- a/compiler/rustc_macros/src/extension.rs +++ b/compiler/rustc_macros/src/extension.rs @@ -6,6 +6,7 @@ use syn::spanned::Spanned; use syn::{ braced, parse_macro_input, Attribute, Generics, ImplItem, Pat, PatIdent, Path, Signature, Token, TraitItem, TraitItemConst, TraitItemFn, TraitItemMacro, TraitItemType, Type, Visibility, + WhereClause, }; pub(crate) fn extension( @@ -13,7 +14,7 @@ pub(crate) fn extension( input: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let ExtensionAttr { vis, trait_ } = parse_macro_input!(attr as ExtensionAttr); - let Impl { attrs, generics, self_ty, items } = parse_macro_input!(input as Impl); + let Impl { attrs, generics, self_ty, items, wc } = parse_macro_input!(input as Impl); let headers: Vec<_> = items .iter() .map(|item| match item { @@ -59,7 +60,7 @@ pub(crate) fn extension( #(#headers)* } - impl #generics #trait_ for #self_ty { + impl #generics #trait_ for #self_ty #wc { #(#items)* } } @@ -133,6 +134,7 @@ struct Impl { generics: Generics, self_ty: Type, items: Vec, + wc: Option, } impl Parse for Impl { @@ -141,6 +143,7 @@ impl Parse for Impl { let _: Token![impl] = input.parse()?; let generics = input.parse()?; let self_ty = input.parse()?; + let wc = input.parse()?; let content; let _brace_token = braced!(content in input); @@ -149,6 +152,6 @@ impl Parse for Impl { items.push(content.parse()?); } - Ok(Impl { attrs, generics, self_ty, items }) + Ok(Impl { attrs, generics, self_ty, items, wc }) } } diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index c7b7eadbd9d6..9d7418cd370a 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -1,3 +1,6 @@ +// tidy-alphabetical-start +#![allow(internal_features)] +#![allow(rustc::default_hash_types)] #![feature(allow_internal_unstable)] #![feature(if_let_guard)] #![feature(let_chains)] @@ -5,8 +8,7 @@ #![feature(proc_macro_diagnostic)] #![feature(proc_macro_span)] #![feature(proc_macro_tracked_env)] -#![allow(rustc::default_hash_types)] -#![allow(internal_features)] +// tidy-alphabetical-end use synstructure::decl_derive; @@ -108,7 +110,9 @@ decl_derive!( // struct attributes diag, help, + help_once, note, + note_once, warning, // field attributes skip_arg, @@ -125,7 +129,9 @@ decl_derive!( // struct attributes diag, help, + help_once, note, + note_once, warning, // field attributes skip_arg, @@ -142,7 +148,9 @@ decl_derive!( // struct/variant attributes label, help, + help_once, note, + note_once, warning, subdiagnostic, suggestion, diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 25675e06e38d..ceff1da97636 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -314,6 +314,17 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { let mut query_description_stream = quote! {}; let mut query_cached_stream = quote! {}; let mut feedable_queries = quote! {}; + let mut errors = quote! {}; + + macro_rules! assert { + ( $cond:expr, $span:expr, $( $tt:tt )+ ) => { + if !$cond { + errors.extend( + Error::new($span, format!($($tt)+)).into_compile_error(), + ); + } + } + } for query in queries.0 { let Query { name, arg, modifiers, .. } = &query; @@ -369,10 +380,15 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { [#attribute_stream] fn #name(#arg) #result, }); - if modifiers.feedable.is_some() { - assert!(modifiers.anon.is_none(), "Query {name} cannot be both `feedable` and `anon`."); + if let Some(feedable) = &modifiers.feedable { + assert!( + modifiers.anon.is_none(), + feedable.span(), + "Query {name} cannot be both `feedable` and `anon`." + ); assert!( modifiers.eval_always.is_none(), + feedable.span(), "Query {name} cannot be both `feedable` and `eval_always`." ); feedable_queries.extend(quote! { @@ -407,5 +423,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { use super::*; #query_cached_stream } + #errors }) } diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 5fa11d22f0e7..7b5dd1601c1f 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -13,7 +13,7 @@ pub fn type_decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2: quote! {} }; - s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_type_ir::codec::TyDecoder #bound }); + s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_type_ir::codec::TyDecoder #bound }); s.add_bounds(synstructure::AddBounds::Fields); s.underscore_const(true); @@ -34,7 +34,7 @@ pub fn meta_decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2: pub fn decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { let decoder_ty = quote! { __D }; - s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_span::SpanDecoder}); + s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_span::SpanDecoder }); s.add_bounds(synstructure::AddBounds::Generics); s.underscore_const(true); @@ -43,7 +43,7 @@ pub fn decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke pub fn decodable_generic_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { let decoder_ty = quote! { __D }; - s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_serialize::Decoder}); + s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_serialize::Decoder }); s.add_bounds(synstructure::AddBounds::Generics); s.underscore_const(true); @@ -120,7 +120,7 @@ fn decode_field(field: &syn::Field) -> proc_macro2::TokenStream { let __decoder = quote! { __decoder }; // Use the span of the field for the method call, so // that backtraces will point to the field. - quote_spanned! {field_span=> #decode_inner_method(#__decoder) } + quote_spanned! { field_span=> #decode_inner_method(#__decoder) } } pub fn type_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { @@ -133,7 +133,7 @@ pub fn type_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2: }; let encoder_ty = quote! { __E }; - s.add_impl_generic(parse_quote! {#encoder_ty: ::rustc_type_ir::codec::TyEncoder #bound }); + s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_type_ir::codec::TyEncoder #bound }); s.add_bounds(synstructure::AddBounds::Fields); s.underscore_const(true); @@ -142,7 +142,7 @@ pub fn type_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2: pub fn meta_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { - s.add_impl_generic(parse_quote! {'tcx}); + s.add_impl_generic(parse_quote! { 'tcx }); } s.add_impl_generic(parse_quote! { '__a }); let encoder_ty = quote! { EncodeContext<'__a, 'tcx> }; @@ -154,7 +154,7 @@ pub fn meta_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2: pub fn encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { let encoder_ty = quote! { __E }; - s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_span::SpanEncoder}); + s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_span::SpanEncoder }); s.add_bounds(synstructure::AddBounds::Generics); s.underscore_const(true); @@ -163,7 +163,7 @@ pub fn encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke pub fn encodable_generic_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { let encoder_ty = quote! { __E }; - s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_serialize::Encoder}); + s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_serialize::Encoder }); s.add_bounds(synstructure::AddBounds::Generics); s.underscore_const(true); diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index 3d0846ae6dea..932603cd6b26 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -91,9 +91,6 @@ metadata_found_staticlib = found staticlib `{$crate_name}` instead of rlib or dylib{$add_info} .help = please recompile that crate using --crate-type lib -metadata_framework_only_windows = - link kind `raw-dylib` is only supported on Windows targets - metadata_global_alloc_required = no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait @@ -233,6 +230,9 @@ metadata_profiler_builtins_needs_core = metadata_raw_dylib_no_nul = link name must not contain NUL characters if link kind is `raw-dylib` +metadata_raw_dylib_only_windows = + link kind `raw-dylib` is only supported on Windows targets + metadata_renaming_no_link = renaming of the library `{$lib_name}` was specified, however this crate contains no `#[link(...)]` attributes referencing this library @@ -284,8 +284,6 @@ metadata_unsupported_abi = metadata_unsupported_abi_i686 = ABI not supported by `#[link(kind = "raw-dylib")]` on i686 -metadata_wasm_c_abi = - older versions of the `wasm-bindgen` crate will be incompatible with future versions of Rust; please update to `wasm-bindgen` v0.2.88 metadata_wasm_import_form = wasm import module must be of the form `wasm_import_module = "string"` diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index e3205fc1d30e..749495bc2ef5 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -10,7 +10,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::owned_slice::OwnedSlice; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard}; -use rustc_errors::DiagCtxt; +use rustc_errors::DiagCtxtHandle; use rustc_expand::base::SyntaxExtension; use rustc_fs_util::try_canonicalize; use rustc_hir::def_id::{CrateNum, LocalDefId, StableCrateId, LOCAL_CRATE}; @@ -20,13 +20,14 @@ use rustc_middle::bug; use rustc_middle::ty::{TyCtxt, TyCtxtFeed}; use rustc_session::config::{self, CrateType, ExternLocation}; use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateSource}; -use rustc_session::lint; +use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::output::validate_crate_name; use rustc_session::search_paths::PathKind; use rustc_span::edition::Edition; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::{PanicStrategy, Target, TargetTriple}; +use tracing::{debug, info, trace}; use proc_macro::bridge::client::ProcMacro; use std::error::Error; @@ -90,8 +91,8 @@ impl<'a, 'tcx> std::ops::Deref for CrateLoader<'a, 'tcx> { } impl<'a, 'tcx> CrateLoader<'a, 'tcx> { - fn dcx(&self) -> &'tcx DiagCtxt { - &self.tcx.dcx() + fn dcx(&self) -> DiagCtxtHandle<'tcx> { + self.tcx.dcx() } } @@ -580,7 +581,6 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { self.tcx.crate_types().iter().all(|c| *c == CrateType::Rlib), hash, extra_filename, - false, // is_host path_kind, ); @@ -975,15 +975,14 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { } self.sess.psess.buffer_lint( - lint::builtin::UNUSED_CRATE_DEPENDENCIES, - span, - ast::CRATE_NODE_ID, - format!( - "external crate `{}` unused in `{}`: remove the dependency or add `use {} as _;`", - name, - self.tcx.crate_name(LOCAL_CRATE), - name), - ); + lint::builtin::UNUSED_CRATE_DEPENDENCIES, + span, + ast::CRATE_NODE_ID, + BuiltinLintDiag::UnusedCrateDependency { + extern_crate: name_interned, + local_crate: self.tcx.crate_name(LOCAL_CRATE), + }, + ); } } @@ -1020,7 +1019,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { lint::builtin::WASM_C_ABI, span, ast::CRATE_NODE_ID, - crate::fluent_generated::metadata_wasm_c_abi, + BuiltinLintDiag::WasmCAbi, ); } } diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index 99181f9c8605..9c69ab2344e5 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -65,6 +65,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::config::CrateType; use rustc_session::cstore::CrateDepKind; use rustc_session::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic}; +use tracing::info; pub(crate) fn calculate(tcx: TyCtxt<'_>) -> Dependencies { tcx.crate_types() diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index fb0010f2c5d2..b0d82a0e3b7c 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -3,7 +3,7 @@ use std::{ path::{Path, PathBuf}, }; -use rustc_errors::{codes::*, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level}; +use rustc_errors::{codes::*, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{sym, Span, Symbol}; use rustc_target::spec::{PanicStrategy, TargetTriple}; @@ -142,8 +142,8 @@ pub struct LinkFrameworkApple { } #[derive(Diagnostic)] -#[diag(metadata_framework_only_windows, code = E0455)] -pub struct FrameworkOnlyWindows { +#[diag(metadata_raw_dylib_only_windows, code = E0455)] +pub struct RawDylibOnlyWindows { #[primary_span] pub span: Span, } @@ -503,7 +503,7 @@ pub(crate) struct MultipleCandidates { } impl Diagnostic<'_, G> for MultipleCandidates { - fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let mut diag = Diag::new(dcx, level, fluent::metadata_multiple_candidates); diag.arg("crate_name", self.crate_name); diag.arg("flavor", self.flavor); @@ -602,7 +602,7 @@ pub struct InvalidMetadataFiles { impl Diagnostic<'_, G> for InvalidMetadataFiles { #[track_caller] - fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let mut diag = Diag::new(dcx, level, fluent::metadata_invalid_meta_files); diag.arg("crate_name", self.crate_name); diag.arg("add_info", self.add_info); @@ -631,7 +631,7 @@ pub struct CannotFindCrate { impl Diagnostic<'_, G> for CannotFindCrate { #[track_caller] - fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let mut diag = Diag::new(dcx, level, fluent::metadata_cannot_find_crate); diag.arg("crate_name", self.crate_name); diag.arg("current_crate", self.current_crate); diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 99584845e49a..acaf9fb0fc32 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -1,27 +1,25 @@ +// tidy-alphabetical-start +#![allow(internal_features)] +#![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(rustdoc_internals)] -#![allow(internal_features)] +#![feature(coroutines)] #![feature(decl_macro)] #![feature(error_iter)] #![feature(extract_if)] -#![feature(coroutines)] +#![feature(if_let_guard)] #![feature(iter_from_coroutine)] #![feature(let_chains)] -#![feature(if_let_guard)] -#![feature(proc_macro_internals)] #![feature(macro_metavar_expr)] #![feature(min_specialization)] -#![feature(trusted_len)] -#![feature(try_blocks)] #![feature(never_type)] -#![allow(rustc::potential_query_instability)] +#![feature(proc_macro_internals)] +#![feature(rustdoc_internals)] +#![feature(trusted_len)] +// tidy-alphabetical-end extern crate proc_macro; -#[macro_use] -extern crate tracing; - pub use rmeta::provide; mod dependency_format; diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 7de03be6da62..90fe52a34385 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -222,7 +222,6 @@ use rustc_data_structures::owned_slice::slice_owned; use rustc_data_structures::svh::Svh; use rustc_errors::{DiagArgValue, IntoDiagArg}; use rustc_fs_util::try_canonicalize; -use rustc_session::config; use rustc_session::cstore::CrateSource; use rustc_session::filesearch::FileSearch; use rustc_session::search_paths::PathKind; @@ -231,6 +230,7 @@ use rustc_session::Session; use rustc_span::symbol::Symbol; use rustc_span::Span; use rustc_target::spec::{Target, TargetTriple}; +use tracing::{debug, info}; use snap::read::FrameDecoder; use std::borrow::Cow; @@ -308,7 +308,6 @@ impl<'a> CrateLocator<'a> { is_rlib: bool, hash: Option, extra_filename: Option<&'a str>, - is_host: bool, path_kind: PathKind, ) -> CrateLocator<'a> { let needs_object_code = sess.opts.output_types.should_codegen(); @@ -339,17 +338,9 @@ impl<'a> CrateLocator<'a> { }, hash, extra_filename, - target: if is_host { &sess.host } else { &sess.target }, - triple: if is_host { - TargetTriple::from_triple(config::host_triple()) - } else { - sess.opts.target_triple.clone() - }, - filesearch: if is_host { - sess.host_filesearch(path_kind) - } else { - sess.target_filesearch(path_kind) - }, + target: &sess.target, + triple: sess.opts.target_triple.clone(), + filesearch: sess.target_filesearch(path_kind), is_proc_macro: false, crate_rejections: CrateRejections::default(), } @@ -423,12 +414,18 @@ impl<'a> CrateLocator<'a> { debug!("testing {}", spf.path.display()); let f = &spf.file_name_str; - let (hash, kind) = if f.starts_with(rlib_prefix) && f.ends_with(rlib_suffix) { - (&f[rlib_prefix.len()..(f.len() - rlib_suffix.len())], CrateFlavor::Rlib) - } else if f.starts_with(rmeta_prefix) && f.ends_with(rmeta_suffix) { - (&f[rmeta_prefix.len()..(f.len() - rmeta_suffix.len())], CrateFlavor::Rmeta) - } else if f.starts_with(dylib_prefix) && f.ends_with(dylib_suffix.as_ref()) { - (&f[dylib_prefix.len()..(f.len() - dylib_suffix.len())], CrateFlavor::Dylib) + let (hash, kind) = if let Some(f) = f.strip_prefix(rlib_prefix) + && let Some(f) = f.strip_suffix(rlib_suffix) + { + (f, CrateFlavor::Rlib) + } else if let Some(f) = f.strip_prefix(rmeta_prefix) + && let Some(f) = f.strip_suffix(rmeta_suffix) + { + (f, CrateFlavor::Rmeta) + } else if let Some(f) = f.strip_prefix(dylib_prefix) + && let Some(f) = f.strip_suffix(dylib_suffix.as_ref()) + { + (f, CrateFlavor::Dylib) } else { if f.starts_with(staticlib_prefix) && f.ends_with(staticlib_suffix.as_ref()) { self.crate_rejections.via_kind.push(CrateMismatch { @@ -853,7 +850,12 @@ fn get_metadata_section<'p>( slice_owned(mmap, Deref::deref) } }; - let blob = MetadataBlob(raw_bytes); + let Ok(blob) = MetadataBlob::new(raw_bytes) else { + return Err(MetadataError::LoadFailure(format!( + "corrupt metadata encountered in {}", + filename.display() + ))); + }; match blob.check_compatibility(cfg_version) { Ok(()) => Ok(blob), Err(None) => Err(MetadataError::LoadFailure(format!( diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 58760be921a6..1254ebead072 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -151,7 +151,7 @@ impl<'tcx> Collector<'tcx> { } "raw-dylib" => { if !sess.target.is_like_windows { - sess.dcx().emit_err(errors::FrameworkOnlyWindows { span }); + sess.dcx().emit_err(errors::RawDylibOnlyWindows { span }); } NativeLibKind::RawDylib } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index bb68c6eaf092..ea7037740f17 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -28,6 +28,7 @@ use rustc_session::cstore::{CrateSource, ExternCrate}; use rustc_session::Session; use rustc_span::symbol::kw; use rustc_span::{BytePos, Pos, SpanData, SpanDecoder, SyntaxContext, DUMMY_SP}; +use tracing::debug; use proc_macro::bridge::client::ProcMacro; use std::iter::TrustedLen; @@ -40,10 +41,9 @@ use rustc_span::hygiene::HygieneDecodeContext; mod cstore_impl; /// A reference to the raw binary version of crate metadata. -/// A `MetadataBlob` internally is just a reference counted pointer to -/// the actual data, so cloning it is cheap. -#[derive(Clone)] -pub(crate) struct MetadataBlob(pub(crate) OwnedSlice); +/// This struct applies [`MemDecoder`]'s validation when constructed +/// so that later constructions are guaranteed to succeed. +pub(crate) struct MetadataBlob(OwnedSlice); impl std::ops::Deref for MetadataBlob { type Target = [u8]; @@ -54,6 +54,19 @@ impl std::ops::Deref for MetadataBlob { } } +impl MetadataBlob { + /// Runs the [`MemDecoder`] validation and if it passes, constructs a new [`MetadataBlob`]. + pub fn new(slice: OwnedSlice) -> Result { + if MemDecoder::new(&slice, 0).is_ok() { Ok(Self(slice)) } else { Err(()) } + } + + /// Since this has passed the validation of [`MetadataBlob::new`], this returns bytes which are + /// known to pass the [`MemDecoder`] validation. + pub fn bytes(&self) -> &OwnedSlice { + &self.0 + } +} + /// A map from external crate numbers (as decoded from some crate file) to /// local crate numbers (as generated during this session). Each external /// crate may refer to types in other external crates, and each has their @@ -165,7 +178,14 @@ pub(super) trait Metadata<'a, 'tcx>: Copy { fn decoder(self, pos: usize) -> DecodeContext<'a, 'tcx> { let tcx = self.tcx(); DecodeContext { - opaque: MemDecoder::new(self.blob(), pos), + // FIXME: This unwrap should never panic because we check that it won't when creating + // `MetadataBlob`. Ideally we'd just have a `MetadataDecoder` and hand out subslices of + // it as we do elsewhere in the compiler using `MetadataDecoder::split_at`. But we own + // the data for the decoder so holding onto the `MemDecoder` too would make us a + // self-referential struct which is downright goofy because `MetadataBlob` is already + // self-referential. Probably `MemDecoder` should contain an `OwnedSlice`, but that + // demands a significant refactoring due to our crate graph. + opaque: MemDecoder::new(self.blob(), pos).unwrap(), cdata: self.cdata(), blob: self.blob(), sess: self.sess().or(tcx.map(|tcx| tcx.sess)), @@ -393,7 +413,7 @@ impl<'a, 'tcx> TyDecoder for DecodeContext<'a, 'tcx> { where F: FnOnce(&mut Self) -> R, { - let new_opaque = MemDecoder::new(self.opaque.data(), pos); + let new_opaque = self.opaque.split_at(pos); let old_opaque = mem::replace(&mut self.opaque, new_opaque); let old_state = mem::replace(&mut self.lazy_state, LazyState::NoNode); let r = f(self); @@ -519,7 +539,7 @@ impl<'a, 'tcx> SpanDecoder for DecodeContext<'a, 'tcx> { } else { SpanData::decode(self) }; - Span::new(data.lo, data.hi, data.ctxt, data.parent) + data.span() } fn decode_symbol(&mut self) -> Symbol { @@ -649,7 +669,7 @@ impl<'a, 'tcx> Decodable> for SpanData { let lo = lo + source_file.translated_source_file.start_pos; let hi = hi + source_file.translated_source_file.start_pos; - // Do not try to decode parent for foreign spans. + // Do not try to decode parent for foreign spans (it wasn't encoded in the first place). SpanData { lo, hi, ctxt, parent: None } } } @@ -1054,7 +1074,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self, index: DefIndex, tcx: TyCtxt<'tcx>, - ) -> ty::EarlyBinder<&'tcx [(ty::Clause<'tcx>, Span)]> { + ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { let lazy = self.root.tables.explicit_item_bounds.get(self, index); let output = if lazy.is_default() { &mut [] @@ -1068,7 +1088,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self, index: DefIndex, tcx: TyCtxt<'tcx>, - ) -> ty::EarlyBinder<&'tcx [(ty::Clause<'tcx>, Span)]> { + ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { let lazy = self.root.tables.explicit_item_super_predicates.get(self, index); let output = if lazy.is_default() { &mut [] diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index c783149a6951..c9450142cd3b 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -191,7 +191,7 @@ impl IntoArgs for (CrateNum, DefId) { } } -impl<'tcx> IntoArgs for ty::InstanceDef<'tcx> { +impl<'tcx> IntoArgs for ty::InstanceKind<'tcx> { type Other = (); fn into_args(self) -> (DefId, ()) { (self.def_id(), ()) @@ -287,6 +287,10 @@ provide! { tcx, def_id, other, cdata, let _ = cdata; tcx.calculate_dtor(def_id, |_,_| Ok(())) } + adt_async_destructor => { + let _ = cdata; + tcx.calculate_async_dtor(def_id, |_,_| Ok(())) + } associated_item_def_ids => { tcx.arena.alloc_from_iter(cdata.get_associated_item_or_field_def_ids(def_id.index)) } @@ -310,6 +314,7 @@ provide! { tcx, def_id, other, cdata, extern_crate => { cdata.extern_crate.map(|c| &*tcx.arena.alloc(c)) } is_no_builtins => { cdata.root.no_builtins } symbol_mangling_version => { cdata.root.symbol_mangling_version } + specialization_enabled_in => { cdata.root.specialization_enabled_in } reachable_non_generics => { let reachable_non_generics = tcx .exported_symbols(cdata.cnum) diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs index 9950bc1c31f7..861bf6b2769e 100644 --- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs +++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs @@ -48,7 +48,7 @@ impl<'a, 'tcx> Decodable> for DefPathHashMapRef<'static> fn decode(d: &mut DecodeContext<'a, 'tcx>) -> DefPathHashMapRef<'static> { let len = d.read_usize(); let pos = d.position(); - let o = d.blob().clone().0.slice(|blob| &blob[pos..pos + len]); + let o = d.blob().bytes().clone().slice(|blob| &blob[pos..pos + len]); // Although we already have the data we need via the `OwnedSlice`, we still need // to advance the `DecodeContext`'s position so it's in a valid state after diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index db0dc6d9064b..4bd2ec09a6e6 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -33,6 +33,7 @@ use std::collections::hash_map::Entry; use std::fs::File; use std::io::{Read, Seek, Write}; use std::path::{Path, PathBuf}; +use tracing::{debug, instrument, trace}; pub(super) struct EncodeContext<'a, 'tcx> { opaque: opaque::FileEncoder, @@ -740,6 +741,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { expn_data, expn_hashes, def_path_hash_map, + specialization_enabled_in: tcx.specialization_enabled_in(LOCAL_CRATE), }) }); @@ -1676,9 +1678,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if should_encode_const(tcx.def_kind(def_id)) { let qualifs = tcx.mir_const_qualif(def_id); record!(self.tables.mir_const_qualif[def_id.to_def_id()] <- qualifs); - let body_id = tcx.hir().maybe_body_owned_by(def_id); - if let Some(body_id) = body_id { - let const_data = rendered_const(self.tcx, body_id); + let body = tcx.hir().maybe_body_owned_by(def_id); + if let Some(body) = body { + let const_data = rendered_const(self.tcx, &body, def_id); record!(self.tables.rendered_const[def_id.to_def_id()] <- const_data); } } @@ -1691,7 +1693,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); } - let instance = ty::InstanceDef::Item(def_id.to_def_id()); + let instance = ty::InstanceKind::Item(def_id.to_def_id()); let unused = tcx.unused_generic_params(instance); self.tables.unused_generic_params.set(def_id.local_def_index, unused); } @@ -2018,7 +2020,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // if this is an impl of `CoerceUnsized`, create its // "unsized info", else just store None - if Some(trait_ref.def_id) == tcx.lang_items().coerce_unsized_trait() { + if tcx.is_lang_item(trait_ref.def_id, LangItem::CoerceUnsized) { let coerce_unsized_info = tcx.coerce_unsized_info(def_id).unwrap(); record!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info); } @@ -2367,9 +2369,9 @@ pub fn provide(providers: &mut Providers) { /// Whenever possible, prefer to evaluate the constant first and try to /// use a different method for pretty-printing. Ideally this function /// should only ever be used as a fallback. -pub fn rendered_const<'tcx>(tcx: TyCtxt<'tcx>, body: hir::BodyId) -> String { +pub fn rendered_const<'tcx>(tcx: TyCtxt<'tcx>, body: &hir::Body<'_>, def_id: LocalDefId) -> String { let hir = tcx.hir(); - let value = &hir.body(body).value; + let value = body.value; #[derive(PartialEq, Eq)] enum Classification { @@ -2425,13 +2427,13 @@ pub fn rendered_const<'tcx>(tcx: TyCtxt<'tcx>, body: hir::BodyId) -> String { // Otherwise we prefer pretty-printing to get rid of extraneous whitespace, comments and // other formatting artifacts. - Literal | Simple => id_to_string(&hir, body.hir_id), + Literal | Simple => id_to_string(&hir, body.id().hir_id), // FIXME: Omit the curly braces if the enclosing expression is an array literal // with a repeated element (an `ExprKind::Repeat`) as in such case it // would not actually need any disambiguation. Complex => { - if tcx.def_kind(hir.body_owner_def_id(body).to_def_id()) == DefKind::AnonConst { + if tcx.def_kind(def_id) == DefKind::AnonConst { "{ _ }".to_owned() } else { "_".to_owned() diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 79e4ff81093b..87900c23d8da 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -1,15 +1,13 @@ use crate::creader::CrateMetadataRef; -use decoder::Metadata; +pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob}; +use decoder::{DecodeContext, Metadata}; use def_path_hash_map::DefPathHashMapRef; -use rustc_data_structures::fx::FxHashMap; -use rustc_macros::{Decodable, Encodable, TyDecodable, TyEncodable}; -use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; -use rustc_middle::middle::lib_features::FeatureStability; -use table::TableBuilder; - +use encoder::EncodeContext; +pub use encoder::{encode_metadata, rendered_const, EncodedMetadata}; use rustc_ast as ast; use rustc_ast::expand::StrippedCfgItem; use rustc_attr as attr; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::svh::Svh; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, DocLinkResMap}; @@ -18,10 +16,13 @@ use rustc_hir::definitions::DefKey; use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; +use rustc_macros::{Decodable, Encodable, TyDecodable, TyEncodable}; use rustc_macros::{MetadataDecodable, MetadataEncodable}; use rustc_middle::metadata::ModChild; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; +use rustc_middle::middle::lib_features::FeatureStability; use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::mir; use rustc_middle::trivially_parameterized_over_tcx; @@ -33,20 +34,14 @@ use rustc_serialize::opaque::FileEncoder; use rustc_session::config::SymbolManglingVersion; use rustc_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib}; use rustc_span::edition::Edition; -use rustc_span::hygiene::{ExpnIndex, MacroKind}; +use rustc_span::hygiene::{ExpnIndex, MacroKind, SyntaxContextData}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Span}; use rustc_target::abi::{FieldIdx, VariantIdx}; use rustc_target::spec::{PanicStrategy, TargetTriple}; - use std::marker::PhantomData; use std::num::NonZero; - -use decoder::DecodeContext; -pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob}; -use encoder::EncodeContext; -pub use encoder::{encode_metadata, rendered_const, EncodedMetadata}; -use rustc_span::hygiene::SyntaxContextData; +use table::TableBuilder; mod decoder; mod def_path_hash_map; @@ -295,6 +290,8 @@ pub(crate) struct CrateRoot { panic_runtime: bool, profiler_runtime: bool, symbol_mangling_version: SymbolManglingVersion, + + specialization_enabled_in: bool, } /// On-disk representation of `DefId`. @@ -423,19 +420,19 @@ define_tables! { // As an optimization, we only store this for trait aliases, // since it's identical to super_predicates_of for traits. implied_predicates_of: Table>>, - type_of: Table>>>, + type_of: Table>>>, variances_of: Table>, - fn_sig: Table>>>, + fn_sig: Table>>>, codegen_fn_attrs: Table>, impl_trait_header: Table>>, - const_param_default: Table>>>, + const_param_default: Table>>>, object_lifetime_default: Table>, optimized_mir: Table>>, mir_for_ctfe: Table>>, closure_saved_names_of_captured_variables: Table>>, mir_coroutine_witnesses: Table>>, promoted_mir: Table>>>, - thir_abstract_const: Table>>>, + thir_abstract_const: Table>>>, impl_parent: Table, constness: Table, defaultness: Table, @@ -464,7 +461,7 @@ define_tables! { macro_definition: Table>, proc_macro: Table, deduced_param_attrs: Table>, - trait_impl_trait_tys: Table>>>>, + trait_impl_trait_tys: Table>>>>, doc_link_resolutions: Table>, doc_link_traits_in_scope: Table>, assumed_wf_types_for_rpitit: Table, Span)>>, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 019cb91c765e..dcbddad2dbca 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -2,6 +2,7 @@ use crate::rmeta::*; use rustc_hir::def::CtorOf; use rustc_index::Idx; +use tracing::trace; pub(super) trait IsDefault: Default { fn is_default(&self) -> bool; @@ -155,10 +156,14 @@ fixed_size_enum! { ( Impl { of_trait: false } ) ( Impl { of_trait: true } ) ( Closure ) - ( Static { mutability: ast::Mutability::Not, nested: false } ) - ( Static { mutability: ast::Mutability::Mut, nested: false } ) - ( Static { mutability: ast::Mutability::Not, nested: true } ) - ( Static { mutability: ast::Mutability::Mut, nested: true } ) + ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Not, nested: false } ) + ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Not, nested: false } ) + ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Mut, nested: false } ) + ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Mut, nested: false } ) + ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Not, nested: true } ) + ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Not, nested: true } ) + ( Static { safety: hir::Safety::Unsafe, mutability: ast::Mutability::Mut, nested: true } ) + ( Static { safety: hir::Safety::Safe, mutability: ast::Mutability::Mut, nested: true } ) ( Ctor(CtorOf::Struct, CtorKind::Fn) ) ( Ctor(CtorOf::Struct, CtorKind::Const) ) ( Ctor(CtorOf::Variant, CtorKind::Fn) ) diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index ab0c598ea0c1..3dc592980fdb 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -11,7 +11,6 @@ either = "1.5.0" field-offset = "0.3.5" gsgdt = "0.1.2" polonius-engine = "0.13.0" -rustc-rayon = { version = "0.5.0", optional = true } rustc-rayon-core = { version = "0.5.0", optional = true } rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } @@ -28,7 +27,6 @@ rustc_hir = { path = "../rustc_hir" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } -rustc_next_trait_solver = { path = "../rustc_next_trait_solver" } rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } @@ -42,5 +40,5 @@ tracing = "0.1" [features] # tidy-alphabetical-start -rustc_use_parallel_compiler = ["rustc-rayon", "rustc-rayon-core"] +rustc_use_parallel_compiler = ["rustc-rayon-core"] # tidy-alphabetical-end diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 27d555d7e26c..f4d619329eb9 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -50,6 +50,20 @@ middle_const_not_used_in_type_alias = middle_cycle = a cycle occurred during layout computation +middle_deprecated = use of deprecated {$kind} `{$path}`{$has_note -> + [true] : {$note} + *[other] {""} + } +middle_deprecated_in_future = use of {$kind} `{$path}` that will be deprecated in a future Rust version{$has_note -> + [true] : {$note} + *[other] {""} + } +middle_deprecated_in_version = use of {$kind} `{$path}` that will be deprecated in future version {$version}{$has_note -> + [true] : {$note} + *[other] {""} + } +middle_deprecated_suggestion = replace the use of the deprecated {$kind} + middle_drop_check_overflow = overflow while adding drop-check rules for {$ty} .note = overflowed on {$overflow_ty} diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 7392eb6c2bb4..e3d7dff3c66b 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -61,7 +61,10 @@ macro_rules! arena_types { [] dtorck_constraint: rustc_middle::traits::query::DropckConstraint<'tcx>, [] candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>, [] autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>, - [] canonical_goal_evaluation: rustc_next_trait_solver::solve::inspect::GoalEvaluationStep>, + [] canonical_goal_evaluation: + rustc_type_ir::solve::inspect::CanonicalGoalEvaluationStep< + rustc_middle::ty::TyCtxt<'tcx> + >, [] query_region_constraints: rustc_middle::infer::canonical::QueryRegionConstraints<'tcx>, [] type_op_subtype: rustc_middle::infer::canonical::Canonical<'tcx, @@ -105,10 +108,10 @@ macro_rules! arena_types { [decode] trait_impl_trait_tys: rustc_data_structures::unord::UnordMap< rustc_hir::def_id::DefId, - rustc_middle::ty::EarlyBinder> + rustc_middle::ty::EarlyBinder<'tcx, rustc_middle::ty::Ty<'tcx>> >, - [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData<'tcx>, - [] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData<'tcx>, + [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData>, + [] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData>, [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap, [] stripped_cfg_items: rustc_ast::expand::StrippedCfgItem, [] mod_child: rustc_middle::metadata::ModChild, diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index c7aea137b684..305ba1ef3bbc 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -254,13 +254,13 @@ impl<'hir> Map<'hir> { /// Given a `LocalDefId`, returns the `BodyId` associated with it, /// if the node is a body owner, otherwise returns `None`. - pub fn maybe_body_owned_by(self, id: LocalDefId) -> Option { - self.tcx.hir_node_by_def_id(id).body_id() + pub fn maybe_body_owned_by(self, id: LocalDefId) -> Option<&'hir Body<'hir>> { + Some(self.body(self.tcx.hir_node_by_def_id(id).body_id()?)) } /// Given a body owner's id, returns the `BodyId` associated with it. #[track_caller] - pub fn body_owned_by(self, id: LocalDefId) -> BodyId { + pub fn body_owned_by(self, id: LocalDefId) -> &'hir Body<'hir> { self.maybe_body_owned_by(id).unwrap_or_else(|| { let hir_id = self.tcx.local_def_id_to_hir_id(id); span_bug!( @@ -290,7 +290,9 @@ impl<'hir> Map<'hir> { DefKind::InlineConst => BodyOwnerKind::Const { inline: true }, DefKind::Ctor(..) | DefKind::Fn | DefKind::AssocFn => BodyOwnerKind::Fn, DefKind::Closure => BodyOwnerKind::Closure, - DefKind::Static { mutability, nested: false } => BodyOwnerKind::Static(mutability), + DefKind::Static { safety: _, mutability, nested: false } => { + BodyOwnerKind::Static(mutability) + } dk => bug!("{:?} is not a body node: {:?}", def_id, dk), } } @@ -511,14 +513,14 @@ impl<'hir> Map<'hir> { self.body_const_context(self.enclosing_body_owner(hir_id)).is_some() } - /// 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. + /// Retrieves the `HirId` for `id`'s enclosing function *if* the `id` block or return is + /// in the "tail" position of the function, in other words if it's likely to correspond + /// to the return type of the function. /// /// ``` /// fn foo(x: usize) -> bool { /// if x == 1 { - /// true // If `get_return_block` gets passed the `id` corresponding + /// true // If `get_fn_id_for_return_block` gets passed the `id` corresponding /// } else { // to this, it will return `foo`'s `HirId`. /// false /// } @@ -528,12 +530,12 @@ impl<'hir> Map<'hir> { /// ```compile_fail,E0308 /// fn foo(x: usize) -> bool { /// loop { - /// true // If `get_return_block` gets passed the `id` corresponding + /// true // If `get_fn_id_for_return_block` gets passed the `id` corresponding /// } // to this, it will return `None`. /// false /// } /// ``` - pub fn get_return_block(self, id: HirId) -> Option { + pub fn get_fn_id_for_return_block(self, id: HirId) -> Option { let mut iter = self.parent_iter(id).peekable(); let mut ignore_tail = false; if let Node::Expr(Expr { kind: ExprKind::Ret(_), .. }) = self.tcx.hir_node(id) { @@ -549,6 +551,11 @@ impl<'hir> Map<'hir> { Node::Block(Block { expr: None, .. }) => return None, // The current node is not the tail expression of its parent. Node::Block(Block { expr: Some(e), .. }) if hir_id != e.hir_id => return None, + Node::Block(Block { expr: Some(e), .. }) + if matches!(e.kind, ExprKind::If(_, _, None)) => + { + return None; + } _ => {} } } @@ -856,7 +863,7 @@ impl<'hir> Map<'hir> { Node::Variant(variant) => named_span(variant.span, variant.ident, None), Node::ImplItem(item) => named_span(item.span, item.ident, Some(item.generics)), Node::ForeignItem(item) => match item.kind { - ForeignItemKind::Fn(decl, _, _) => until_within(item.span, decl.output.span()), + ForeignItemKind::Fn(decl, _, _, _) => until_within(item.span, decl.output.span()), _ => named_span(item.span, item.ident, None), }, Node::Ctor(_) => return self.span(self.tcx.parent_hir_id(hir_id)), @@ -896,7 +903,7 @@ impl<'hir> Map<'hir> { .with_hi(seg.args.map_or_else(|| ident_span.hi(), |args| args.span_ext.hi())) } Node::Ty(ty) => ty.span, - Node::TypeBinding(tb) => tb.span, + Node::AssocItemConstraint(constraint) => constraint.span, Node::TraitRef(tr) => tr.path.span, Node::Pat(pat) => pat.span, Node::PatField(field) => field.span, @@ -1162,7 +1169,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { Node::Stmt(_) => node_str("stmt"), Node::PathSegment(_) => node_str("path segment"), Node::Ty(_) => node_str("type"), - Node::TypeBinding(_) => node_str("type binding"), + Node::AssocItemConstraint(_) => node_str("assoc item constraint"), Node::TraitRef(_) => node_str("trait ref"), Node::Pat(_) => node_str("pat"), Node::PatField(_) => node_str("pattern field"), diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 34748afa863d..57c8ba96a20a 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -121,7 +121,7 @@ impl<'tcx> TyCtxt<'tcx> { LocalModDefId::new_unchecked(id) } - pub fn impl_subject(self, def_id: DefId) -> EarlyBinder> { + pub fn impl_subject(self, def_id: DefId) -> EarlyBinder<'tcx, ImplSubject<'tcx>> { match self.impl_trait_ref(def_id) { Some(t) => t.map_bound(ImplSubject::Trait), None => self.type_of(def_id).map_bound(ImplSubject::Inherent), @@ -194,21 +194,24 @@ pub fn provide(providers: &mut Providers) { }; providers.fn_arg_names = |tcx, def_id| { let hir = tcx.hir(); - let hir_id = tcx.local_def_id_to_hir_id(def_id); - if let Some(body_id) = hir.maybe_body_owned_by(def_id) { + if let Some(body_id) = tcx.hir_node_by_def_id(def_id).body_id() { tcx.arena.alloc_from_iter(hir.body_param_names(body_id)) } else if let Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(_, TraitFn::Required(idents)), .. }) | Node::ForeignItem(&ForeignItem { - kind: ForeignItemKind::Fn(_, idents, _), + kind: ForeignItemKind::Fn(_, idents, _, _), .. - }) = tcx.hir_node(hir_id) + }) = tcx.hir_node(tcx.local_def_id_to_hir_id(def_id)) { idents } else { - span_bug!(hir.span(hir_id), "fn_arg_names: unexpected item {:?}", def_id); + span_bug!( + hir.span(tcx.local_def_id_to_hir_id(def_id)), + "fn_arg_names: unexpected item {:?}", + def_id + ); } }; providers.all_local_trait_impls = |tcx, ()| &tcx.resolutions(()).trait_impls; diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index d35b9fc46e4d..bf10a71dbaec 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -10,6 +10,7 @@ use rustc_hir::def_id::{DefId, DefPathHash}; use rustc_session::StableCrateId; use rustc_span::def_id::{CrateNum, LocalDefId}; use rustc_span::{ExpnHash, ExpnId, DUMMY_SP}; +use tracing::instrument; macro_rules! declare_hooks { ($($(#[$attr:meta])*hook $name:ident($($arg:ident: $K:ty),*) -> $V:ty;)*) => { diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 49bf03e9c754..dba71d88f404 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -32,7 +32,7 @@ use std::collections::hash_map::Entry; use crate::infer::MemberConstraint; use crate::mir::ConstraintCategory; use crate::ty::GenericArg; -use crate::ty::{self, List, Region, Ty, TyCtxt, TypeFlags, TypeVisitableExt}; +use crate::ty::{self, List, Ty, TyCtxt, TypeFlags, TypeVisitableExt}; pub type Canonical<'tcx, V> = ir::Canonical, V>; pub type CanonicalVarInfo<'tcx> = ir::CanonicalVarInfo>; @@ -141,7 +141,7 @@ impl<'tcx, R> QueryResponse<'tcx, R> { } pub type QueryOutlivesConstraint<'tcx> = - (ty::OutlivesPredicate, Region<'tcx>>, ConstraintCategory<'tcx>); + (ty::OutlivesPredicate<'tcx, GenericArg<'tcx>>, ConstraintCategory<'tcx>); TrivialTypeTraversalImpls! { crate::infer::canonical::Certainty, diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs index 105be21f2727..a5da7e7739e4 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_middle/src/infer/unify_key.rs @@ -86,21 +86,6 @@ impl<'tcx> UnifyValue for RegionVariableValue<'tcx> { } } -impl ToType for ty::IntVarValue { - fn to_type<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match *self { - ty::IntType(i) => Ty::new_int(tcx, i), - ty::UintType(i) => Ty::new_uint(tcx, i), - } - } -} - -impl ToType for ty::FloatVarValue { - fn to_type<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - Ty::new_float(tcx, self.0) - } -} - // Generic consts. #[derive(Copy, Clone, Debug)] @@ -211,6 +196,7 @@ impl<'tcx> EffectVarValue<'tcx> { impl<'tcx> UnifyValue for EffectVarValue<'tcx> { type Error = NoError; + fn unify_values(value1: &Self, value2: &Self) -> Result { match (*value1, *value2) { (EffectVarValue::Unknown, EffectVarValue::Unknown) => Ok(EffectVarValue::Unknown), diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 04fd4c8d0f7b..b499604df87e 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -4,15 +4,16 @@ //! has their own README with further details). //! //! - **HIR.** The "high-level (H) intermediate representation (IR)" is -//! defined in the `hir` module. +//! defined in the [`hir`] module. //! - **MIR.** The "mid-level (M) intermediate representation (IR)" is -//! defined in the `mir` module. This module contains only the +//! defined in the [`mir`] module. This module contains only the //! *definition* of the MIR; the passes that transform and operate //! on MIR are found in `rustc_const_eval` crate. //! - **Types.** The internal representation of types used in rustc is -//! defined in the `ty` module. This includes the **type context** -//! (or `tcx`), which is the central context during most of -//! compilation, containing the interners and other things. +//! defined in the [`ty`] module. This includes the +//! [**type context**][ty::TyCtxt] (or `tcx`), which is the central +//! context during most of compilation, containing the interners and +//! other things. //! //! For more information about how rustc works, see the [rustc dev guide]. //! @@ -22,49 +23,46 @@ //! //! This API is completely unstable and subject to change. +// tidy-alphabetical-start +#![allow(internal_features)] +#![allow(rustc::diagnostic_outside_of_impl)] +#![allow(rustc::potential_query_instability)] +#![allow(rustc::untranslatable_diagnostic)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(min_exhaustive_patterns)] -#![feature(rustdoc_internals)] #![feature(allocator_api)] #![feature(array_windows)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(closure_track_caller)] -#![feature(core_intrinsics)] +#![feature(const_option)] #![feature(const_type_name)] -#![feature(discriminant_kind)] +#![feature(core_intrinsics)] #![feature(coroutines)] -#![feature(stmt_expr_attributes)] +#![feature(decl_macro)] +#![feature(discriminant_kind)] +#![feature(extern_types)] +#![feature(extract_if)] #![feature(if_let_guard)] +#![feature(intra_doc_pointers)] #![feature(iter_from_coroutine)] +#![feature(let_chains)] +#![feature(macro_metavar_expr)] +#![feature(min_exhaustive_patterns)] +#![feature(min_specialization)] #![feature(negative_impls)] #![feature(never_type)] -#![feature(extern_types)] #![feature(new_uninit)] -#![feature(let_chains)] -#![feature(min_specialization)] -#![feature(trusted_len)] -#![feature(type_alias_impl_trait)] -#![feature(strict_provenance)] -#![feature(rustc_attrs)] -#![feature(control_flow_enum)] -#![feature(trait_upcasting)] -#![feature(try_blocks)] -#![feature(decl_macro)] -#![feature(extract_if)] -#![feature(intra_doc_pointers)] -#![feature(yeet_expr)] -#![feature(const_option)] #![feature(ptr_alignment_type)] -#![feature(macro_metavar_expr)] -#![allow(internal_features)] -#![allow(rustc::potential_query_instability)] -#![allow(rustc::diagnostic_outside_of_impl)] -#![allow(rustc::untranslatable_diagnostic)] - -#[macro_use] -extern crate tracing; +#![feature(rustc_attrs)] +#![feature(rustdoc_internals)] +#![feature(strict_provenance)] +#![feature(trait_upcasting)] +#![feature(trusted_len)] +#![feature(try_blocks)] +#![feature(type_alias_impl_trait)] +#![feature(yeet_expr)] +// tidy-alphabetical-end #[cfg(test)] mod tests; diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 086582e60a3c..4e655ca2027f 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -2,7 +2,7 @@ use std::cmp; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sorted_map::SortedMap; -use rustc_errors::{Diag, DiagMessage, MultiSpan}; +use rustc_errors::{Diag, MultiSpan}; use rustc_hir::{HirId, ItemLocalId}; use rustc_macros::HashStable; use rustc_session::lint::{ @@ -12,6 +12,7 @@ use rustc_session::lint::{ use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::{symbol, DesugaringKind, Span, Symbol, DUMMY_SP}; +use tracing::instrument; use crate::ty::TyCtxt; @@ -269,7 +270,6 @@ pub fn lint_level( level: Level, src: LintLevelSource, span: Option, - msg: impl Into, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), ) { // Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to @@ -281,7 +281,6 @@ pub fn lint_level( level: Level, src: LintLevelSource, span: Option, - msg: impl Into, decorate: Box FnOnce(&'b mut Diag<'a, ()>)>, ) { // Check for future incompatibility lints and issue a stronger warning. @@ -350,10 +349,6 @@ pub fn lint_level( } } - // Delay evaluating and setting the primary message until after we've - // suppressed the lint due to macros. - err.primary_message(msg); - err.is_lint(lint.name_lower(), has_future_breakage); // Lint diagnostics that are covered by the expect level will not be emitted outside @@ -418,7 +413,7 @@ pub fn lint_level( explain_lint_level_source(lint, level, src, &mut err); err.emit() } - lint_level_impl(sess, lint, level, src, span, msg, Box::new(decorate)) + lint_level_impl(sess, lint, level, src, span, Box::new(decorate)) } /// Returns whether `span` originates in a foreign crate's external macro. diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index 817ac5946276..fcea1ea81a7c 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -4,10 +4,10 @@ /// /// If you have a span available, you should use [`span_bug`] instead. /// -/// If the bug should only be emitted when compilation didn't fail, [`DiagCtxt::span_delayed_bug`] +/// If the bug should only be emitted when compilation didn't fail, [`DiagCtxtHandle::span_delayed_bug`] /// may be useful. /// -/// [`DiagCtxt::span_delayed_bug`]: rustc_errors::DiagCtxt::span_delayed_bug +/// [`DiagCtxtHandle::span_delayed_bug`]: rustc_errors::DiagCtxtHandle::span_delayed_bug /// [`span_bug`]: crate::span_bug #[macro_export] macro_rules! bug { @@ -30,10 +30,10 @@ macro_rules! bug { /// at the code the compiler was compiling when it ICEd. This is the preferred way to trigger /// ICEs. /// -/// If the bug should only be emitted when compilation didn't fail, [`DiagCtxt::span_delayed_bug`] +/// If the bug should only be emitted when compilation didn't fail, [`DiagCtxtHandle::span_delayed_bug`] /// may be useful. /// -/// [`DiagCtxt::span_delayed_bug`]: rustc_errors::DiagCtxt::span_delayed_bug +/// [`DiagCtxtHandle::span_delayed_bug`]: rustc_errors::DiagCtxtHandle::span_delayed_bug #[macro_export] macro_rules! span_bug { ($span:expr, $msg:expr) => ( diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs index 3b6eecb7fa04..b35cc83cb8e8 100644 --- a/compiler/rustc_middle/src/middle/exported_symbols.rs +++ b/compiler/rustc_middle/src/middle/exported_symbols.rs @@ -64,7 +64,7 @@ impl<'tcx> ExportedSymbol<'tcx> { tcx.symbol_name(ty::Instance::resolve_async_drop_in_place(tcx, ty)) } ExportedSymbol::ThreadLocalShim(def_id) => tcx.symbol_name(ty::Instance { - def: ty::InstanceDef::ThreadLocalShim(def_id), + def: ty::InstanceKind::ThreadLocalShim(def_id), args: ty::GenericArgs::empty(), }), ExportedSymbol::NoDefId(symbol_name) => symbol_name, diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs index 4fd1c1f4a1b7..e76d7af6e4ab 100644 --- a/compiler/rustc_middle/src/middle/lang_items.rs +++ b/compiler/rustc_middle/src/middle/lang_items.rs @@ -23,6 +23,10 @@ impl<'tcx> TyCtxt<'tcx> { }) } + pub fn is_lang_item(self, def_id: DefId, lang_item: LangItem) -> bool { + self.lang_items().get(lang_item) == Some(def_id) + } + /// Given a [`DefId`] of one of the [`Fn`], [`FnMut`] or [`FnOnce`] traits, /// returns a corresponding [`ty::ClosureKind`]. /// For any other [`DefId`] return `None`. diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index de07ba9700ac..6e89dc494fa5 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -13,6 +13,7 @@ use rustc_hir as hir; use rustc_hir::{HirId, HirIdMap, Node}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::{Span, DUMMY_SP}; +use tracing::debug; use std::fmt; use std::ops::Deref; diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 67bd53f53dae..d1ccd158cf93 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -9,20 +9,21 @@ use rustc_attr::{ self as attr, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability, }; use rustc_data_structures::unord::UnordMap; -use rustc_errors::{Applicability, Diag}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee}; use rustc_feature::GateIssue; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap}; use rustc_hir::{self as hir, HirId}; -use rustc_macros::{Decodable, Encodable, HashStable}; +use rustc_macros::{Decodable, Encodable, HashStable, Subdiagnostic}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; -use rustc_session::lint::{BuiltinLintDiag, Level, Lint, LintBuffer}; +use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint, LintBuffer}; use rustc_session::parse::feature_err_issue; use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use std::num::NonZero; +use tracing::debug; #[derive(PartialEq, Clone, Copy, Debug)] pub enum StabilityLevel { @@ -125,90 +126,104 @@ pub fn report_unstable( } } -pub fn deprecation_suggestion( - diag: &mut Diag<'_, ()>, - kind: &str, - suggestion: Option, - span: Span, -) { - if let Some(suggestion) = suggestion { - diag.span_suggestion_verbose( - span, - format!("replace the use of the deprecated {kind}"), - suggestion, - Applicability::MachineApplicable, - ); - } -} - fn deprecation_lint(is_in_effect: bool) -> &'static Lint { if is_in_effect { DEPRECATED } else { DEPRECATED_IN_FUTURE } } -fn deprecation_message( - is_in_effect: bool, - since: DeprecatedSince, - note: Option, - kind: &str, - path: &str, -) -> String { - let message = if is_in_effect { - format!("use of deprecated {kind} `{path}`") +#[derive(Subdiagnostic)] +#[suggestion( + middle_deprecated_suggestion, + code = "{suggestion}", + style = "verbose", + applicability = "machine-applicable" +)] +pub struct DeprecationSuggestion { + #[primary_span] + pub span: Span, + + pub kind: String, + pub suggestion: Symbol, +} + +pub struct Deprecated { + pub sub: Option, + + // FIXME: make this translatable + pub kind: String, + pub path: String, + pub note: Option, + pub since_kind: DeprecatedSinceKind, +} + +impl<'a, G: EmissionGuarantee> rustc_errors::LintDiagnostic<'a, G> for Deprecated { + fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) { + diag.primary_message(match &self.since_kind { + DeprecatedSinceKind::InEffect => crate::fluent_generated::middle_deprecated, + DeprecatedSinceKind::InFuture => crate::fluent_generated::middle_deprecated_in_future, + DeprecatedSinceKind::InVersion(_) => { + crate::fluent_generated::middle_deprecated_in_version + } + }); + diag.arg("kind", self.kind); + diag.arg("path", self.path); + if let DeprecatedSinceKind::InVersion(version) = self.since_kind { + diag.arg("version", version); + } + if let Some(note) = self.note { + diag.arg("has_note", true); + diag.arg("note", note); + } else { + diag.arg("has_note", false); + } + if let Some(sub) = self.sub { + diag.subdiagnostic(sub); + } + } +} + +fn deprecated_since_kind(is_in_effect: bool, since: DeprecatedSince) -> DeprecatedSinceKind { + if is_in_effect { + DeprecatedSinceKind::InEffect } else { match since { - DeprecatedSince::RustcVersion(version) => format!( - "use of {kind} `{path}` that will be deprecated in future version {version}" - ), - DeprecatedSince::Future => { - format!("use of {kind} `{path}` that will be deprecated in a future Rust version") + DeprecatedSince::RustcVersion(version) => { + DeprecatedSinceKind::InVersion(version.to_string()) } + DeprecatedSince::Future => DeprecatedSinceKind::InFuture, DeprecatedSince::NonStandard(_) | DeprecatedSince::Unspecified | DeprecatedSince::Err => { unreachable!("this deprecation is always in effect; {since:?}") } } - }; - - match note { - Some(reason) => format!("{message}: {reason}"), - None => message, } } -pub fn deprecation_message_and_lint( - depr: &Deprecation, - kind: &str, - path: &str, -) -> (String, &'static Lint) { - let is_in_effect = depr.is_in_effect(); - ( - deprecation_message(is_in_effect, depr.since, depr.note, kind, path), - deprecation_lint(is_in_effect), - ) -} - -pub fn early_report_deprecation( +pub fn early_report_macro_deprecation( lint_buffer: &mut LintBuffer, - message: String, - suggestion: Option, - lint: &'static Lint, + depr: &Deprecation, span: Span, node_id: NodeId, + path: String, ) { if span.in_derive_expansion() { return; } - let diag = BuiltinLintDiag::DeprecatedMacro(suggestion, span); - lint_buffer.buffer_lint_with_diagnostic(lint, node_id, span, message, diag); + let is_in_effect = depr.is_in_effect(); + let diag = BuiltinLintDiag::DeprecatedMacro { + suggestion: depr.suggestion, + suggestion_span: span, + note: depr.note, + path, + since_kind: deprecated_since_kind(is_in_effect, depr.since.clone()), + }; + lint_buffer.buffer_lint(deprecation_lint(is_in_effect), node_id, span, diag); } fn late_report_deprecation( tcx: TyCtxt<'_>, - message: String, - suggestion: Option, - lint: &'static Lint, + depr: &Deprecation, span: Span, method_span: Option, hir_id: HirId, @@ -217,13 +232,26 @@ fn late_report_deprecation( if span.in_derive_expansion() { return; } + + let def_path = with_no_trimmed_paths!(tcx.def_path_str(def_id)); + let def_kind = tcx.def_descr(def_id); + let is_in_effect = depr.is_in_effect(); + let method_span = method_span.unwrap_or(span); - tcx.node_span_lint(lint, hir_id, method_span, message, |diag| { - if let hir::Node::Expr(_) = tcx.hir_node(hir_id) { - let kind = tcx.def_descr(def_id); - deprecation_suggestion(diag, kind, suggestion, method_span); - } - }); + let suggestion = + if let hir::Node::Expr(_) = tcx.hir_node(hir_id) { depr.suggestion } else { None }; + let diag = Deprecated { + sub: suggestion.map(|suggestion| DeprecationSuggestion { + span: method_span, + kind: def_kind.to_owned(), + suggestion, + }), + kind: def_kind.to_owned(), + path: def_path, + note: depr.note, + since_kind: deprecated_since_kind(is_in_effect, depr.since), + }; + tcx.emit_node_span_lint(deprecation_lint(is_in_effect), hir_id, method_span, diag); } /// Result of `TyCtxt::eval_stability`. @@ -352,28 +380,9 @@ impl<'tcx> TyCtxt<'tcx> { // Calculating message for lint involves calling `self.def_path_str`. // Which by default to calculate visible path will invoke expensive `visible_parent_map` query. // So we skip message calculation altogether, if lint is allowed. - let is_in_effect = depr_attr.is_in_effect(); - let lint = deprecation_lint(is_in_effect); + let lint = deprecation_lint(depr_attr.is_in_effect()); if self.lint_level_at_node(lint, id).0 != Level::Allow { - let def_path = with_no_trimmed_paths!(self.def_path_str(def_id)); - let def_kind = self.def_descr(def_id); - - late_report_deprecation( - self, - deprecation_message( - is_in_effect, - depr_attr.since, - depr_attr.note, - def_kind, - &def_path, - ), - depr_attr.suggestion, - lint, - span, - method_span, - id, - def_id, - ); + late_report_deprecation(self, depr_attr, span, method_span, id, def_id); } } }; @@ -586,7 +595,9 @@ impl<'tcx> TyCtxt<'tcx> { unmarked: impl FnOnce(Span, DefId), ) -> bool { let soft_handler = |lint, span, msg: String| { - self.node_span_lint(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, msg, |_| {}) + self.node_span_lint(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| { + lint.primary_message(msg); + }) }; let eval_result = self.eval_stability_allow_unstable(def_id, id, span, method_span, allow_unstable); diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index d025dc360a24..89f5acacf9d1 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -84,11 +84,11 @@ impl<'tcx> ConstValue<'tcx> { } pub fn try_to_scalar_int(&self) -> Option { - self.try_to_scalar()?.try_to_int().ok() + self.try_to_scalar()?.try_to_scalar_int().ok() } pub fn try_to_bits(&self, size: Size) -> Option { - self.try_to_scalar_int()?.try_to_bits(size).ok() + Some(self.try_to_scalar_int()?.to_bits(size)) } pub fn try_to_bool(&self) -> Option { @@ -96,7 +96,7 @@ impl<'tcx> ConstValue<'tcx> { } pub fn try_to_target_usize(&self, tcx: TyCtxt<'tcx>) -> Option { - self.try_to_scalar_int()?.try_to_target_usize(tcx).ok() + Some(self.try_to_scalar_int()?.to_target_usize(tcx)) } pub fn try_to_bits_for_ty( @@ -204,7 +204,9 @@ pub enum Const<'tcx> { /// Any way of turning `ty::Const` into `ConstValue` should go through `valtree_to_const_val`; /// this ensures that we consistently produce "clean" values without data in the padding or /// anything like that. - Ty(ty::Const<'tcx>), + /// + /// FIXME(BoxyUwU): We should remove this `Ty` and look up the type for params via `ParamEnv` + Ty(Ty<'tcx>, ty::Const<'tcx>), /// An unevaluated mir constant which is not part of the type system. /// @@ -220,7 +222,10 @@ pub enum Const<'tcx> { } impl<'tcx> Const<'tcx> { - pub fn identity_unevaluated(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::EarlyBinder> { + pub fn identity_unevaluated( + tcx: TyCtxt<'tcx>, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, Const<'tcx>> { ty::EarlyBinder::bind(Const::Unevaluated( UnevaluatedConst { def: def_id, @@ -234,7 +239,15 @@ impl<'tcx> Const<'tcx> { #[inline(always)] pub fn ty(&self) -> Ty<'tcx> { match self { - Const::Ty(c) => c.ty(), + Const::Ty(ty, ct) => { + match ct.kind() { + // Dont use the outter ty as on invalid code we can wind up with them not being the same. + // this then results in allowing const eval to add `1_i64 + 1_usize` in cases where the mir + // was originally `({N: usize} + 1_usize)` under `generic_const_exprs`. + ty::ConstKind::Value(ty, _) => ty, + _ => *ty, + } + } Const::Val(_, ty) | Const::Unevaluated(_, ty) => *ty, } } @@ -244,8 +257,8 @@ impl<'tcx> Const<'tcx> { #[inline] pub fn is_required_const(&self) -> bool { match self { - Const::Ty(c) => match c.kind() { - ty::ConstKind::Value(_) => false, // already a value, cannot error + Const::Ty(_, c) => match c.kind() { + ty::ConstKind::Value(_, _) => false, // already a value, cannot error _ => true, }, Const::Val(..) => false, // already a value, cannot error @@ -256,8 +269,8 @@ impl<'tcx> Const<'tcx> { #[inline] pub fn try_to_scalar(self) -> Option { match self { - Const::Ty(c) => match c.kind() { - ty::ConstKind::Value(valtree) if c.ty().is_primitive() => { + Const::Ty(_, c) => match c.kind() { + ty::ConstKind::Value(ty, valtree) if ty.is_primitive() => { // A valtree of a type where leaves directly represent the scalar const value. // Just checking whether it is a leaf is insufficient as e.g. references are leafs // but the leaf value is the value they point to, not the reference itself! @@ -275,8 +288,8 @@ impl<'tcx> Const<'tcx> { // This is equivalent to `self.try_to_scalar()?.try_to_int().ok()`, but measurably faster. match self { Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x), - Const::Ty(c) => match c.kind() { - ty::ConstKind::Value(valtree) if c.ty().is_primitive() => { + Const::Ty(_, c) => match c.kind() { + ty::ConstKind::Value(ty, valtree) if ty.is_primitive() => { Some(valtree.unwrap_leaf()) } _ => None, @@ -287,7 +300,7 @@ impl<'tcx> Const<'tcx> { #[inline] pub fn try_to_bits(self, size: Size) -> Option { - self.try_to_scalar_int()?.try_to_bits(size).ok() + Some(self.try_to_scalar_int()?.to_bits(size)) } #[inline] @@ -303,11 +316,11 @@ impl<'tcx> Const<'tcx> { span: Span, ) -> Result, ErrorHandled> { match self { - Const::Ty(c) => { + Const::Ty(_, c) => { // We want to consistently have a "clean" value for type system constants (i.e., no // data hidden in the padding), so we always go through a valtree here. - let val = c.eval(tcx, param_env, span)?; - Ok(tcx.valtree_to_const_val((self.ty(), val))) + let (ty, val) = c.eval(tcx, param_env, span)?; + Ok(tcx.valtree_to_const_val((ty, val))) } Const::Unevaluated(uneval, _) => { // FIXME: We might want to have a `try_eval`-like function on `Unevaluated` @@ -323,7 +336,7 @@ impl<'tcx> Const<'tcx> { match self.eval(tcx, param_env, DUMMY_SP) { Ok(val) => Self::Val(val, self.ty()), Err(ErrorHandled::Reported(guar, _span)) => { - Self::Ty(ty::Const::new_error(tcx, guar.into(), self.ty())) + Self::Ty(Ty::new_error(tcx, guar.into()), ty::Const::new_error(tcx, guar.into())) } Err(ErrorHandled::TooGeneric(_span)) => self, } @@ -335,15 +348,16 @@ impl<'tcx> Const<'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Option { - match self { - Const::Ty(c) if c.ty().is_primitive() => { - // Avoid the `valtree_to_const_val` query. Can only be done on primitive types that - // are valtree leaves, and *not* on references. (References should return the - // pointer here, which valtrees don't represent.) - let val = c.eval(tcx, param_env, DUMMY_SP).ok()?; - Some(val.unwrap_leaf().into()) - } - _ => self.eval(tcx, param_env, DUMMY_SP).ok()?.try_to_scalar(), + if let Const::Ty(_, c) = self + && let ty::ConstKind::Value(ty, val) = c.kind() + && ty.is_primitive() + { + // Avoid the `valtree_to_const_val` query. Can only be done on primitive types that + // are valtree leaves, and *not* on references. (References should return the + // pointer here, which valtrees don't represent.) + Some(val.unwrap_leaf().into()) + } else { + self.eval(tcx, param_env, DUMMY_SP).ok()?.try_to_scalar() } } @@ -353,7 +367,7 @@ impl<'tcx> Const<'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Option { - self.try_eval_scalar(tcx, param_env)?.try_to_int().ok() + self.try_eval_scalar(tcx, param_env)?.try_to_scalar_int().ok() } #[inline] @@ -361,7 +375,7 @@ impl<'tcx> Const<'tcx> { let int = self.try_eval_scalar_int(tcx, param_env)?; let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(self.ty())).ok()?.size; - int.try_to_bits(size).ok() + Some(int.to_bits(size)) } /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type. @@ -377,7 +391,7 @@ impl<'tcx> Const<'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Option { - self.try_eval_scalar_int(tcx, param_env)?.try_to_target_usize(tcx).ok() + Some(self.try_eval_scalar_int(tcx, param_env)?.to_target_usize(tcx)) } #[inline] @@ -436,14 +450,14 @@ impl<'tcx> Const<'tcx> { Self::Val(val, ty) } - pub fn from_ty_const(c: ty::Const<'tcx>, tcx: TyCtxt<'tcx>) -> Self { + pub fn from_ty_const(c: ty::Const<'tcx>, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Self { match c.kind() { - ty::ConstKind::Value(valtree) => { + ty::ConstKind::Value(ty, valtree) => { // Make sure that if `c` is normalized, then the return value is normalized. - let const_val = tcx.valtree_to_const_val((c.ty(), valtree)); - Self::Val(const_val, c.ty()) + let const_val = tcx.valtree_to_const_val((ty, valtree)); + Self::Val(const_val, ty) } - _ => Self::Ty(c), + _ => Self::Ty(ty, c), } } @@ -455,12 +469,12 @@ impl<'tcx> Const<'tcx> { // - valtrees purposefully generate new allocations // - ConstValue::Slice also generate new allocations match self { - Const::Ty(c) => match c.kind() { + Const::Ty(_, c) => match c.kind() { ty::ConstKind::Param(..) => true, // A valtree may be a reference. Valtree references correspond to a // different allocation each time they are evaluated. Valtrees for primitive // types are fine though. - ty::ConstKind::Value(_) => c.ty().is_primitive(), + ty::ConstKind::Value(ty, _) => ty.is_primitive(), ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false, // This can happen if evaluation of a constant failed. The result does not matter // much since compilation is doomed. @@ -514,7 +528,7 @@ impl<'tcx> UnevaluatedConst<'tcx> { impl<'tcx> Display for Const<'tcx> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { match *self { - Const::Ty(c) => pretty_print_const(c, fmt, true), + Const::Ty(_, c) => pretty_print_const(c, fmt, true), Const::Val(val, ty) => pretty_print_const_value(val, ty, fmt), // FIXME(valtrees): Correctly print mir constants. Const::Unevaluated(c, _ty) => { diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 7f9a5a366d74..da25fbb0a82b 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -181,8 +181,8 @@ impl Debug for CodeRegion { } } -#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] -#[derive(TypeFoldable, TypeVisitable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] +#[derive(TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)] pub enum Op { Subtract, Add, @@ -329,14 +329,14 @@ pub struct MCDCBranchSpan { #[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] pub struct DecisionInfo { pub bitmap_idx: u32, - pub conditions_num: u16, + pub num_conditions: u16, } #[derive(Clone, Debug)] #[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] pub struct MCDCDecisionSpan { pub span: Span, - pub conditions_num: usize, + pub num_conditions: usize, pub end_markers: Vec, pub decision_depth: u16, } diff --git a/compiler/rustc_middle/src/mir/graphviz.rs b/compiler/rustc_middle/src/mir/graphviz.rs index 96bef40dac51..2eadc4d553cc 100644 --- a/compiler/rustc_middle/src/mir/graphviz.rs +++ b/compiler/rustc_middle/src/mir/graphviz.rs @@ -19,7 +19,7 @@ where if tcx.is_const_fn_raw(*def_id) { vec![tcx.optimized_mir(*def_id), tcx.mir_for_ctfe(*def_id)] } else { - vec![tcx.instance_mir(ty::InstanceDef::Item(*def_id))] + vec![tcx.instance_mir(ty::InstanceKind::Item(*def_id))] } }) .collect::>(); diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 791e87735f40..2fc466c0e7e9 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -52,7 +52,7 @@ impl AllocBytes for Box<[u8]> { } fn zeroed(size: Size, _align: Align) -> Option { - let bytes = Box::<[u8]>::try_new_zeroed_slice(size.bytes_usize()).ok()?; + let bytes = Box::<[u8]>::try_new_zeroed_slice(size.bytes().try_into().ok()?).ok()?; // SAFETY: the box was zero-allocated, which is a valid initial value for Box<[u8]> let bytes = unsafe { bytes.assume_init() }; Some(bytes) @@ -266,19 +266,6 @@ impl AllocRange { // The constructors are all without extra; the extra gets added by a machine hook later. impl Allocation { - /// Creates an allocation from an existing `Bytes` value - this is needed for miri FFI support - pub fn from_raw_bytes(bytes: Bytes, align: Align, mutability: Mutability) -> Self { - let size = Size::from_bytes(bytes.len()); - Self { - bytes, - provenance: ProvenanceMap::new(), - init_mask: InitMask::new(size, true), - align, - mutability, - extra: (), - } - } - /// Creates an allocation initialized by the given bytes pub fn from_bytes<'a>( slice: impl Into>, @@ -336,24 +323,39 @@ impl Allocation { /// first call this function and then call write_scalar to fill in the right data. pub fn uninit(size: Size, align: Align) -> Self { match Self::uninit_inner(size, align, || { - panic!("Allocation::uninit called with panic_on_fail had allocation failure"); + panic!( + "interpreter ran out of memory: cannot create allocation of {} bytes", + size.bytes() + ); }) { Ok(x) => x, Err(x) => x, } } + + /// Add the extra. + pub fn with_extra(self, extra: Extra) -> Allocation { + Allocation { + bytes: self.bytes, + provenance: self.provenance, + init_mask: self.init_mask, + align: self.align, + mutability: self.mutability, + extra, + } + } } impl Allocation { /// Adjust allocation from the ones in `tcx` to a custom Machine instance - /// with a different `Provenance`, `Extra` and `Byte` type. - pub fn adjust_from_tcx( - self, + /// with a different `Provenance` and `Byte` type. + pub fn adjust_from_tcx( + &self, cx: &impl HasDataLayout, - extra: Extra, mut adjust_ptr: impl FnMut(Pointer) -> Result, Err>, - ) -> Result, Err> { - let mut bytes = self.bytes; + ) -> Result, Err> { + // Copy the data. + let mut bytes = Bytes::from_bytes(Cow::Borrowed(&*self.bytes), self.align); // Adjust provenance of pointers stored in this allocation. let mut new_provenance = Vec::with_capacity(self.provenance.ptrs().len()); let ptr_size = cx.data_layout().pointer_size.bytes_usize(); @@ -369,12 +371,12 @@ impl Allocation { } // Create allocation. Ok(Allocation { - bytes: AllocBytes::from_bytes(Cow::Owned(Vec::from(bytes)), self.align), + bytes, provenance: ProvenanceMap::from_presorted_ptrs(new_provenance), - init_mask: self.init_mask, + init_mask: self.init_mask.clone(), align: self.align, mutability: self.mutability, - extra, + extra: self.extra, }) } } diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index e974279f1917..4e37295a571b 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -3,12 +3,12 @@ use std::cmp; +use super::{alloc_range, AllocError, AllocRange, AllocResult, CtfeProvenance, Provenance}; use rustc_data_structures::sorted_map::SortedMap; use rustc_macros::HashStable; -use rustc_target::abi::{HasDataLayout, Size}; - -use super::{alloc_range, AllocError, AllocRange, AllocResult, CtfeProvenance, Provenance}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; +use rustc_target::abi::{HasDataLayout, Size}; +use tracing::trace; /// Stores the provenance information of pointers stored in memory. #[derive(Clone, PartialEq, Eq, Hash, Debug)] diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 6e152cbcb657..23680f143970 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -1,19 +1,22 @@ -use super::{AllocId, AllocRange, ConstAllocation, Pointer, Scalar}; +use std::borrow::Cow; +use std::{any::Any, backtrace::Backtrace, fmt}; -use crate::error; -use crate::mir::{ConstAlloc, ConstValue}; -use crate::ty::{self, layout, tls, Ty, TyCtxt, ValTree}; +use either::Either; use rustc_ast_ir::Mutability; use rustc_data_structures::sync::Lock; use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagArg}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_session::CtfeBacktrace; +use rustc_span::Symbol; use rustc_span::{def_id::DefId, Span, DUMMY_SP}; use rustc_target::abi::{call, Align, Size, VariantIdx, WrappingRange}; -use std::borrow::Cow; -use std::{any::Any, backtrace::Backtrace, fmt}; +use super::{AllocId, AllocRange, ConstAllocation, Pointer, Scalar}; + +use crate::error; +use crate::mir::{ConstAlloc, ConstValue}; +use crate::ty::{self, layout, tls, Ty, TyCtxt, ValTree}; #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] pub enum ErrorHandled { @@ -63,6 +66,9 @@ impl ReportedErrorInfo { pub fn tainted_by_errors(error: ErrorGuaranteed) -> ReportedErrorInfo { ReportedErrorInfo { is_tainted_by_errors: true, error } } + pub fn is_tainted_by_errors(&self) -> bool { + self.is_tainted_by_errors + } } impl From for ReportedErrorInfo { @@ -310,6 +316,10 @@ pub enum UndefinedBehaviorInfo<'tcx> { RemainderOverflow, /// Overflowing inbounds pointer arithmetic. PointerArithOverflow, + /// Overflow in arithmetic that may not overflow. + ArithOverflow { intrinsic: Symbol }, + /// Shift by too much. + ShiftOverflow { intrinsic: Symbol, shift_amount: Either }, /// Invalid metadata in a wide pointer InvalidMeta(InvalidMetaKind), /// Reading a C string that does not end within its allocation. @@ -428,9 +438,6 @@ pub enum ValidationErrorKind<'tcx> { ptr_kind: PointerKind, ty: Ty<'tcx>, }, - PtrToStatic { - ptr_kind: PointerKind, - }, ConstRefToMutable, ConstRefToExtern, MutableRefToImmutable, diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 739b1410e6db..16093cfca6ad 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -15,6 +15,7 @@ use std::num::NonZero; use std::sync::atomic::{AtomicU32, Ordering}; use smallvec::{smallvec, SmallVec}; +use tracing::{debug, trace}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashMap; diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 04d6301116ee..95857e8579d9 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -1,4 +1,6 @@ -use super::{ErrorHandled, EvalToConstValueResult, EvalToValTreeResult, GlobalId}; +use super::{ + ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, GlobalId, +}; use crate::mir; use crate::query::TyCtxtEnsure; @@ -9,10 +11,11 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_session::lint; use rustc_span::{Span, DUMMY_SP}; +use tracing::{debug, instrument}; impl<'tcx> TyCtxt<'tcx> { /// Evaluates a constant without providing any generic parameters. This is useful to evaluate consts - /// that can't take any generic arguments like statics, const items or enum discriminants. If a + /// that can't take any generic arguments like const items or enum discriminants. If a /// generic parameter is used within the constant `ErrorHandled::ToGeneric` will be returned. #[instrument(skip(self), level = "debug")] pub fn const_eval_poly(self, def_id: DefId) -> EvalToConstValueResult<'tcx> { @@ -26,6 +29,24 @@ impl<'tcx> TyCtxt<'tcx> { let param_env = self.param_env(def_id).with_reveal_all_normalized(self); self.const_eval_global_id(param_env, cid, DUMMY_SP) } + + /// Evaluates a constant without providing any generic parameters. This is useful to evaluate consts + /// that can't take any generic arguments like const items or enum discriminants. If a + /// generic parameter is used within the constant `ErrorHandled::ToGeneric` will be returned. + #[instrument(skip(self), level = "debug")] + pub fn const_eval_poly_to_alloc(self, def_id: DefId) -> EvalToAllocationRawResult<'tcx> { + // In some situations def_id will have generic parameters within scope, but they aren't allowed + // to be used. So we can't use `Instance::mono`, instead we feed unresolved generic parameters + // into `const_eval` which will return `ErrorHandled::ToGeneric` if any of them are + // encountered. + let args = GenericArgs::identity_for_item(self, def_id); + let instance = ty::Instance::new(def_id, args); + let cid = GlobalId { instance, promoted: None }; + let param_env = self.param_env(def_id).with_reveal_all_normalized(self); + let inputs = self.erase_regions(param_env.and(cid)); + self.eval_to_allocation_raw(inputs) + } + /// Resolves and evaluates a constant. /// /// The constant can be located on a trait like `::C`, in which case the given @@ -112,8 +133,7 @@ impl<'tcx> TyCtxt<'tcx> { lint::builtin::CONST_EVALUATABLE_UNCHECKED, self.local_def_id_to_hir_id(local_def_id), self.def_span(ct.def), - "cannot use constants which depend on generic parameters in types", - |_| {}, + |lint| { lint.primary_message("cannot use constants which depend on generic parameters in types"); }, ) } } @@ -177,7 +197,7 @@ impl<'tcx> TyCtxt<'tcx> { impl<'tcx> TyCtxtEnsure<'tcx> { /// Evaluates a constant without providing any generic parameters. This is useful to evaluate consts - /// that can't take any generic arguments like statics, const items or enum discriminants. If a + /// that can't take any generic arguments like const items or enum discriminants. If a /// generic parameter is used within the constant `ErrorHandled::ToGeneric` will be returned. #[instrument(skip(self), level = "debug")] pub fn const_eval_poly(self, def_id: DefId) { diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index c5c87c506b77..a84a4c583edd 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -69,6 +69,13 @@ impl fmt::LowerHex for Scalar { } } +impl From for Scalar { + #[inline(always)] + fn from(f: Half) -> Self { + Scalar::from_f16(f) + } +} + impl From for Scalar { #[inline(always)] fn from(f: Single) -> Self { @@ -83,6 +90,13 @@ impl From for Scalar { } } +impl From for Scalar { + #[inline(always)] + fn from(f: Quad) -> Self { + Scalar::from_f128(f) + } +} + impl From for Scalar { #[inline(always)] fn from(ptr: ScalarInt) -> Self { @@ -122,16 +136,12 @@ impl Scalar { Scalar::Int(c.into()) } - #[inline] - pub fn try_from_uint(i: impl Into, size: Size) -> Option { - ScalarInt::try_from_uint(i, size).map(Scalar::Int) - } - #[inline] pub fn from_uint(i: impl Into, size: Size) -> Self { let i = i.into(); - Self::try_from_uint(i, size) + ScalarInt::try_from_uint(i, size) .unwrap_or_else(|| bug!("Unsigned value {:#x} does not fit in {} bits", i, size.bits())) + .into() } #[inline] @@ -164,16 +174,12 @@ impl Scalar { Self::from_uint(i, cx.data_layout().pointer_size) } - #[inline] - pub fn try_from_int(i: impl Into, size: Size) -> Option { - ScalarInt::try_from_int(i, size).map(Scalar::Int) - } - #[inline] pub fn from_int(i: impl Into, size: Size) -> Self { let i = i.into(); - Self::try_from_int(i, size) + ScalarInt::try_from_int(i, size) .unwrap_or_else(|| bug!("Signed value {:#x} does not fit in {} bits", i, size.bits())) + .into() } #[inline] @@ -196,6 +202,11 @@ impl Scalar { Self::from_int(i, Size::from_bits(64)) } + #[inline] + pub fn from_i128(i: i128) -> Self { + Self::from_int(i, Size::from_bits(128)) + } + #[inline] pub fn from_target_isize(i: i64, cx: &impl HasDataLayout) -> Self { Self::from_int(i, cx.data_layout().pointer_size) @@ -222,7 +233,7 @@ impl Scalar { } /// This is almost certainly not the method you want! You should dispatch on the type - /// and use `to_{u8,u16,...}`/`scalar_to_ptr` to perform ptr-to-int / int-to-ptr casts as needed. + /// and use `to_{u8,u16,...}`/`to_pointer` to perform ptr-to-int / int-to-ptr casts as needed. /// /// This method only exists for the benefit of low-level operations that truly need to treat the /// scalar in whatever form it is. @@ -284,7 +295,7 @@ impl<'tcx, Prov: Provenance> Scalar { /// The error type is `AllocId`, not `CtfeProvenance`, since `AllocId` is the "minimal" /// component all provenance types must have. #[inline] - pub fn try_to_int(self) -> Result> { + pub fn try_to_scalar_int(self) -> Result> { match self { Scalar::Int(int) => Ok(int), Scalar::Ptr(ptr, sz) => { @@ -302,13 +313,13 @@ impl<'tcx, Prov: Provenance> Scalar { #[inline(always)] pub fn to_scalar_int(self) -> InterpResult<'tcx, ScalarInt> { - self.try_to_int().map_err(|_| err_unsup!(ReadPointerAsInt(None)).into()) + self.try_to_scalar_int().map_err(|_| err_unsup!(ReadPointerAsInt(None)).into()) } #[inline(always)] #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) - pub fn assert_int(self) -> ScalarInt { - self.try_to_int().unwrap() + pub fn assert_scalar_int(self) -> ScalarInt { + self.try_to_scalar_int().expect("got a pointer where a ScalarInt was expected") } /// This throws UB (instead of ICEing) on a size mismatch since size mismatches can arise in @@ -325,13 +336,6 @@ impl<'tcx, Prov: Provenance> Scalar { }) } - #[inline(always)] - #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) - pub fn assert_bits(self, target_size: Size) -> u128 { - self.to_bits(target_size) - .unwrap_or_else(|_| panic!("assertion failed: {self:?} fits {target_size:?}")) - } - pub fn to_bool(self) -> InterpResult<'tcx, bool> { let val = self.to_u8()?; match val { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 25cc9ac47c83..01cefc75194f 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -10,7 +10,7 @@ use crate::ty::print::{pretty_print_const, with_no_trimmed_paths}; use crate::ty::print::{FmtPrinter, Printer}; use crate::ty::visit::TypeVisitableExt; use crate::ty::{self, List, Ty, TyCtxt}; -use crate::ty::{AdtDef, Instance, InstanceDef, UserTypeAnnotationIndex}; +use crate::ty::{AdtDef, Instance, InstanceKind, UserTypeAnnotationIndex}; use crate::ty::{GenericArg, GenericArgsRef}; use rustc_data_structures::captures::Captures; @@ -37,6 +37,7 @@ use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; use either::Either; +use tracing::trace; use std::borrow::Cow; use std::cell::RefCell; @@ -232,7 +233,7 @@ impl RuntimePhase { #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)] pub struct MirSource<'tcx> { - pub instance: InstanceDef<'tcx>, + pub instance: InstanceKind<'tcx>, /// If `Some`, this is a promoted rvalue within the parent function. pub promoted: Option, @@ -240,10 +241,10 @@ pub struct MirSource<'tcx> { impl<'tcx> MirSource<'tcx> { pub fn item(def_id: DefId) -> Self { - MirSource { instance: InstanceDef::Item(def_id), promoted: None } + MirSource { instance: InstanceKind::Item(def_id), promoted: None } } - pub fn from_instance(instance: InstanceDef<'tcx>) -> Self { + pub fn from_instance(instance: InstanceKind<'tcx>) -> Self { MirSource { instance, promoted: None } } @@ -623,7 +624,7 @@ impl<'tcx> Body<'tcx> { /// Returns the return type; it always return first element from `local_decls` array. #[inline] - pub fn bound_return_ty(&self) -> ty::EarlyBinder> { + pub fn bound_return_ty(&self) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { ty::EarlyBinder::bind(self.local_decls[RETURN_PLACE].ty) } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index daab6c855819..146cd6dfbeb7 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -1,13 +1,13 @@ use crate::dep_graph::{DepNode, WorkProduct, WorkProductId}; -use crate::ty::{GenericArgs, Instance, InstanceDef, SymbolName, TyCtxt}; +use crate::ty::{GenericArgs, Instance, InstanceKind, SymbolName, TyCtxt}; use rustc_attr::InlineAttr; use rustc_data_structures::base_n::BaseNString; use rustc_data_structures::base_n::ToBaseN; use rustc_data_structures::base_n::CASE_INSENSITIVE; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher}; +use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher, ToStableHashKey}; +use rustc_data_structures::unord::UnordMap; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::ItemId; use rustc_index::Idx; @@ -18,6 +18,7 @@ use rustc_span::symbol::Symbol; use rustc_span::Span; use std::fmt; use std::hash::Hash; +use tracing::debug; /// Describes how a monomorphization will be instantiated in object files. #[derive(PartialEq)] @@ -55,7 +56,7 @@ impl<'tcx> MonoItem<'tcx> { /// Returns `true` if the mono item is user-defined (i.e. not compiler-generated, like shims). pub fn is_user_defined(&self) -> bool { match *self { - MonoItem::Fn(instance) => matches!(instance.def, InstanceDef::Item(..)), + MonoItem::Fn(instance) => matches!(instance.def, InstanceKind::Item(..)), MonoItem::Static(..) | MonoItem::GlobalAsm(..) => true, } } @@ -68,9 +69,9 @@ impl<'tcx> MonoItem<'tcx> { match instance.def { // "Normal" functions size estimate: the number of // statements, plus one for the terminator. - InstanceDef::Item(..) - | InstanceDef::DropGlue(..) - | InstanceDef::AsyncDropGlueCtorShim(..) => { + InstanceKind::Item(..) + | InstanceKind::DropGlue(..) + | InstanceKind::AsyncDropGlueCtorShim(..) => { let mir = tcx.instance_mir(instance.def); mir.basic_blocks.iter().map(|bb| bb.statements.len() + 1).sum() } @@ -240,7 +241,17 @@ impl<'tcx> fmt::Display for MonoItem<'tcx> { } } -#[derive(Debug)] +impl ToStableHashKey> for MonoItem<'_> { + type KeyType = Fingerprint; + + fn to_stable_hash_key(&self, hcx: &StableHashingContext<'_>) -> Self::KeyType { + let mut hasher = StableHasher::new(); + self.hash_stable(&mut hcx.clone(), &mut hasher); + hasher.finish() + } +} + +#[derive(Debug, HashStable)] pub struct CodegenUnit<'tcx> { /// A name for this CGU. Incremental compilation requires that /// name be unique amongst **all** crates. Therefore, it should @@ -396,20 +407,20 @@ impl<'tcx> CodegenUnit<'tcx> { // instances into account. The others don't matter for // the codegen tests and can even make item order // unstable. - InstanceDef::Item(def) => def.as_local().map(Idx::index), - InstanceDef::VTableShim(..) - | InstanceDef::ReifyShim(..) - | InstanceDef::Intrinsic(..) - | InstanceDef::FnPtrShim(..) - | InstanceDef::Virtual(..) - | InstanceDef::ClosureOnceShim { .. } - | InstanceDef::ConstructCoroutineInClosureShim { .. } - | InstanceDef::CoroutineKindShim { .. } - | InstanceDef::DropGlue(..) - | InstanceDef::CloneShim(..) - | InstanceDef::ThreadLocalShim(..) - | InstanceDef::FnPtrAddrShim(..) - | InstanceDef::AsyncDropGlueCtorShim(..) => None, + InstanceKind::Item(def) => def.as_local().map(Idx::index), + InstanceKind::VTableShim(..) + | InstanceKind::ReifyShim(..) + | InstanceKind::Intrinsic(..) + | InstanceKind::FnPtrShim(..) + | InstanceKind::Virtual(..) + | InstanceKind::ClosureOnceShim { .. } + | InstanceKind::ConstructCoroutineInClosureShim { .. } + | InstanceKind::CoroutineKindShim { .. } + | InstanceKind::DropGlue(..) + | InstanceKind::CloneShim(..) + | InstanceKind::ThreadLocalShim(..) + | InstanceKind::FnPtrAddrShim(..) + | InstanceKind::AsyncDropGlueCtorShim(..) => None, } } MonoItem::Static(def_id) => def_id.as_local().map(Idx::index), @@ -429,38 +440,19 @@ impl<'tcx> CodegenUnit<'tcx> { } } -impl<'a, 'tcx> HashStable> for CodegenUnit<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let CodegenUnit { - ref items, - name, - // The size estimate is not relevant to the hash - size_estimate: _, - primary: _, - is_code_coverage_dead_code_cgu, - } = *self; +impl ToStableHashKey> for CodegenUnit<'_> { + type KeyType = String; - name.hash_stable(hcx, hasher); - is_code_coverage_dead_code_cgu.hash_stable(hcx, hasher); - - let mut items: Vec<(Fingerprint, _)> = items - .iter() - .map(|(mono_item, &attrs)| { - let mut hasher = StableHasher::new(); - mono_item.hash_stable(hcx, &mut hasher); - let mono_item_fingerprint = hasher.finish(); - (mono_item_fingerprint, attrs) - }) - .collect(); - - items.sort_unstable_by_key(|i| i.0); - items.hash_stable(hcx, hasher); + fn to_stable_hash_key(&self, _: &StableHashingContext<'_>) -> Self::KeyType { + // Codegen unit names are conceptually required to be stable across + // compilation session so that object file names match up. + self.name.to_string() } } pub struct CodegenUnitNameBuilder<'tcx> { tcx: TyCtxt<'tcx>, - cache: FxHashMap, + cache: UnordMap, } impl<'tcx> CodegenUnitNameBuilder<'tcx> { diff --git a/compiler/rustc_middle/src/mir/patch.rs b/compiler/rustc_middle/src/mir/patch.rs index 334122970178..18c48d99b81c 100644 --- a/compiler/rustc_middle/src/mir/patch.rs +++ b/compiler/rustc_middle/src/mir/patch.rs @@ -1,4 +1,5 @@ use rustc_middle::mir::*; +use tracing::debug; /// This struct represents a patch to MIR, which can add /// new statements and basic blocks and patch over block diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 5aaa1c30cade..4657f4dcf813 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -15,6 +15,7 @@ use rustc_middle::mir::interpret::{ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_target::abi::Size; +use tracing::trace; const INDENT: &str = " "; /// Alignment for lining up comments following MIR statements @@ -176,7 +177,7 @@ fn dump_path<'tcx>( // All drop shims have the same DefId, so we have to add the type // to get unique file names. let shim_disambiguator = match source.instance { - ty::InstanceDef::DropGlue(_, Some(ty)) => { + ty::InstanceKind::DropGlue(_, Some(ty)) => { // Unfortunately, pretty-printed typed are not very filename-friendly. // We dome some filtering. let mut s = ".".to_owned(); @@ -187,7 +188,7 @@ fn dump_path<'tcx>( })); s } - ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) => { + ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)) => { // Unfortunately, pretty-printed typed are not very filename-friendly. // We dome some filtering. let mut s = ".".to_owned(); @@ -279,7 +280,7 @@ pub fn write_mir_pretty<'tcx>( // are shared between mir_for_ctfe and optimized_mir write_mir_fn(tcx, tcx.mir_for_ctfe(def_id), &mut |_, _| Ok(()), w)?; } else { - let instance_mir = tcx.instance_mir(ty::InstanceDef::Item(def_id)); + let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id)); render_body(w, instance_mir)?; } } @@ -511,12 +512,12 @@ fn write_coverage_branch_info( )?; } - for coverage::MCDCDecisionSpan { span, conditions_num, end_markers, decision_depth } in + for coverage::MCDCDecisionSpan { span, num_conditions, end_markers, decision_depth } in mcdc_decision_spans { writeln!( w, - "{INDENT}coverage mcdc decision {{ conditions_num: {conditions_num:?}, end: {end_markers:?}, depth: {decision_depth:?} }} => {span:?}" + "{INDENT}coverage mcdc decision {{ num_conditions: {num_conditions:?}, end: {end_markers:?}, depth: {decision_depth:?} }} => {span:?}" )?; } @@ -558,10 +559,10 @@ fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io: match (kind, body.source.promoted) { (_, Some(_)) => write!(w, "const ")?, // promoteds are the closest to consts (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?, - (DefKind::Static { mutability: hir::Mutability::Not, nested: false }, _) => { + (DefKind::Static { safety: _, mutability: hir::Mutability::Not, nested: false }, _) => { write!(w, "static ")? } - (DefKind::Static { mutability: hir::Mutability::Mut, nested: false }, _) => { + (DefKind::Static { safety: _, mutability: hir::Mutability::Mut, nested: false }, _) => { write!(w, "static mut ")? } (_, _) if is_function => write!(w, "fn ")?, @@ -971,9 +972,6 @@ impl<'tcx> Debug for Rvalue<'tcx> { with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})")) } BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"), - CheckedBinaryOp(ref op, box (ref a, ref b)) => { - write!(fmt, "Checked{op:?}({a:?}, {b:?})") - } UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"), Discriminant(ref place) => write!(fmt, "discriminant({place:?})"), NullaryOp(ref op, ref t) => { @@ -1289,7 +1287,7 @@ fn use_verbose(ty: Ty<'_>, fn_def: bool) -> bool { } impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { - fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _location: Location) { + fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, _location: Location) { let ConstOperand { span, user_ty, const_ } = constant; if use_verbose(const_.ty(), true) { self.push("mir::ConstOperand"); @@ -1315,12 +1313,12 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { }; let val = match const_ { - Const::Ty(ct) => match ct.kind() { + Const::Ty(_, ct) => match ct.kind() { ty::ConstKind::Param(p) => format!("ty::Param({p})"), ty::ConstKind::Unevaluated(uv) => { format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,) } - ty::ConstKind::Value(val) => format!("ty::Valtree({})", fmt_valtree(&val)), + ty::ConstKind::Value(_, val) => format!("ty::Valtree({})", fmt_valtree(&val)), // No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`. ty::ConstKind::Error(_) => "Error".to_string(), // These variants shouldn't exist in the MIR. @@ -1417,9 +1415,9 @@ pub fn write_allocations<'tcx>( struct CollectAllocIds(BTreeSet); impl<'tcx> Visitor<'tcx> for CollectAllocIds { - fn visit_constant(&mut self, c: &ConstOperand<'tcx>, _: Location) { + fn visit_const_operand(&mut self, c: &ConstOperand<'tcx>, _: Location) { match c.const_ { - Const::Ty(_) | Const::Unevaluated(..) => {} + Const::Ty(_, _) | Const::Unevaluated(..) => {} Const::Val(val, _) => { self.0.extend(alloc_ids_from_const_val(val)); } diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 9d70231be3b0..46b38e4a6a60 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -1,7 +1,7 @@ //! Values computed by queries that use MIR. use crate::mir; -use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt}; +use crate::ty::{self, CoroutineArgsExt, OpaqueHiddenType, Ty, TyCtxt}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::LocalDefId; diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 375f1f15a39e..4d9a931d6979 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -438,7 +438,6 @@ impl<'tcx> Rvalue<'tcx> { _, ) | Rvalue::BinaryOp(_, _) - | Rvalue::CheckedBinaryOp(_, _) | Rvalue::NullaryOp(_, _) | Rvalue::UnaryOp(_, _) | Rvalue::Discriminant(_) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index e124b478f419..5957a25f0f2b 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -127,6 +127,9 @@ pub enum AnalysisPhase { /// * [`StatementKind::AscribeUserType`] /// * [`StatementKind::Coverage`] with [`CoverageKind::BlockMarker`] or [`CoverageKind::SpanMarker`] /// * [`Rvalue::Ref`] with `BorrowKind::Fake` + /// * [`CastKind::PointerCoercion`] with any of the following: + /// * [`PointerCoercion::ArrayToPointer`] + /// * [`PointerCoercion::MutToConstPointer`] /// /// Furthermore, `Deref` projections must be the first projection within any place (if they /// appear at all) @@ -361,16 +364,19 @@ pub enum StatementKind<'tcx> { /// At any point during the execution of a function, each local is either allocated or /// unallocated. Except as noted below, all locals except function parameters are initially /// unallocated. `StorageLive` statements cause memory to be allocated for the local while - /// `StorageDead` statements cause the memory to be freed. Using a local in any way (not only - /// reading/writing from it) while it is unallocated is UB. + /// `StorageDead` statements cause the memory to be freed. In other words, + /// `StorageLive`/`StorageDead` act like the heap operations `allocate`/`deallocate`, but for + /// stack-allocated local variables. Using a local in any way (not only reading/writing from it) + /// while it is unallocated is UB. /// /// Some locals have no `StorageLive` or `StorageDead` statements within the entire MIR body. /// These locals are implicitly allocated for the full duration of the function. There is a /// convenience method at `rustc_mir_dataflow::storage::always_storage_live_locals` for /// computing these locals. /// - /// If the local is already allocated, calling `StorageLive` again is UB. However, for an - /// unallocated local an additional `StorageDead` all is simply a nop. + /// If the local is already allocated, calling `StorageLive` again will implicitly free the + /// local and then allocate fresh uninitilized memory. If a local is already deallocated, + /// calling `StorageDead` again is a NOP. StorageLive(Local), /// See `StorageLive` above. @@ -1008,8 +1014,8 @@ pub type AssertMessage<'tcx> = AssertKind>; /// element: /// /// - [`Downcast`](ProjectionElem::Downcast): This projection sets the place's variant index to the -/// given one, and makes no other changes. A `Downcast` projection on a place with its variant -/// index already set is not well-formed. +/// given one, and makes no other changes. A `Downcast` projection must always be followed +/// immediately by a `Field` projection. /// - [`Field`](ProjectionElem::Field): `Field` projections take their parent place and create a /// place referring to one of the fields of the type. The resulting address is the parent /// address, plus the offset of the field. The type becomes the type of the field. If the parent @@ -1281,8 +1287,7 @@ pub enum Rvalue<'tcx> { /// /// This allows for casts from/to a variety of types. /// - /// **FIXME**: Document exactly which `CastKind`s allow which types of casts. Figure out why - /// `ArrayToPointer` and `MutToConstPointer` are special. + /// **FIXME**: Document exactly which `CastKind`s allow which types of casts. Cast(CastKind, Operand<'tcx>, Ty<'tcx>), /// * `Offset` has the same semantics as [`offset`](pointer::offset), except that the second @@ -1295,18 +1300,12 @@ pub enum Rvalue<'tcx> { /// truncated as needed. /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching /// types and return a value of that type. + /// * The `FooWithOverflow` are like the `Foo`, but returning `(T, bool)` instead of just `T`, + /// where the `bool` is true if the result is not equal to the infinite-precision result. /// * The remaining operations accept signed integers, unsigned integers, or floats with /// matching types and return a value of that type. BinaryOp(BinOp, Box<(Operand<'tcx>, Operand<'tcx>)>), - /// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition. - /// - /// For addition, subtraction, and multiplication on integers the error condition is set when - /// the infinite precision result would not be equal to the actual result. - /// - /// Other combinations of types and operators are unsupported. - CheckedBinaryOp(BinOp, Box<(Operand<'tcx>, Operand<'tcx>)>), - /// Computes a value as described by the operation. NullaryOp(NullOp<'tcx>, Ty<'tcx>), @@ -1368,6 +1367,13 @@ pub enum CastKind { PointerWithExposedProvenance, /// Pointer related casts that are done by coercions. Note that reference-to-raw-ptr casts are /// translated into `&raw mut/const *r`, i.e., they are not actually casts. + /// + /// The following are allowed in [`AnalysisPhase::Initial`] as they're needed for borrowck, + /// but after that are forbidden (including in all phases of runtime MIR): + /// * [`PointerCoercion::ArrayToPointer`] + /// * [`PointerCoercion::MutToConstPointer`] + /// + /// Both are runtime nops, so should be [`CastKind::PtrToPtr`] instead in runtime MIR. PointerCoercion(PointerCoercion), /// Cast into a dyn* object. DynStar, @@ -1440,6 +1446,13 @@ pub enum UnOp { Not, /// The `-` operator for negation Neg, + /// Get the metadata `M` from a `*const/mut impl Pointee`. + /// + /// For example, this will give a `()` from `*const i32`, a `usize` from + /// `*mut [u8]`, or a pointer to a vtable from a `*const dyn Foo`. + /// + /// Allowed only in [`MirPhase::Runtime`]; earlier it's an intrinsic. + PtrMetadata, } #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] @@ -1449,14 +1462,23 @@ pub enum BinOp { Add, /// Like `Add`, but with UB on overflow. (Integers only.) AddUnchecked, + /// Like `Add`, but returns `(T, bool)` of both the wrapped result + /// and a bool indicating whether it overflowed. + AddWithOverflow, /// The `-` operator (subtraction) Sub, /// Like `Sub`, but with UB on overflow. (Integers only.) SubUnchecked, + /// Like `Sub`, but returns `(T, bool)` of both the wrapped result + /// and a bool indicating whether it overflowed. + SubWithOverflow, /// The `*` operator (multiplication) Mul, /// Like `Mul`, but with UB on overflow. (Integers only.) MulUnchecked, + /// Like `Mul`, but returns `(T, bool)` of both the wrapped result + /// and a bool indicating whether it overflowed. + MulWithOverflow, /// The `/` operator (division) /// /// For integer types, division by zero is UB, as is `MIN / -1` for signed. @@ -1480,7 +1502,8 @@ pub enum BinOp { BitOr, /// The `<<` operator (shift left) /// - /// The offset is (uniquely) determined as follows: + /// The offset is given by `RHS.rem_euclid(LHS::BITS)`. + /// In other words, it is (uniquely) determined as follows: /// - it is "equal modulo LHS::BITS" to the RHS /// - it is in the range `0..LHS::BITS` Shl, @@ -1488,7 +1511,8 @@ pub enum BinOp { ShlUnchecked, /// The `>>` operator (shift right) /// - /// The offset is (uniquely) determined as follows: + /// The offset is given by `RHS.rem_euclid(LHS::BITS)`. + /// In other words, it is (uniquely) determined as follows: /// - it is "equal modulo LHS::BITS" to the RHS /// - it is in the range `0..LHS::BITS` /// diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 4994679ad5da..126387db1d9c 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -5,6 +5,7 @@ use crate::mir::*; use rustc_hir as hir; +use tracing::{debug, instrument}; #[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)] pub struct PlaceTy<'tcx> { @@ -179,13 +180,10 @@ impl<'tcx> Rvalue<'tcx> { let rhs_ty = rhs.ty(local_decls, tcx); op.ty(tcx, lhs_ty, rhs_ty) } - Rvalue::CheckedBinaryOp(op, box (ref lhs, ref rhs)) => { - let lhs_ty = lhs.ty(local_decls, tcx); - let rhs_ty = rhs.ty(local_decls, tcx); - let ty = op.ty(tcx, lhs_ty, rhs_ty); - Ty::new_tup(tcx, &[ty, tcx.types.bool]) + Rvalue::UnaryOp(op, ref operand) => { + let arg_ty = operand.ty(local_decls, tcx); + op.ty(tcx, arg_ty) } - Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, ref operand) => operand.ty(local_decls, tcx), Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx), Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { tcx.types.usize @@ -263,6 +261,11 @@ impl<'tcx> BinOp { assert_eq!(lhs_ty, rhs_ty); lhs_ty } + &BinOp::AddWithOverflow | &BinOp::SubWithOverflow | &BinOp::MulWithOverflow => { + // these should be integers of the same size. + assert_eq!(lhs_ty, rhs_ty); + Ty::new_tup(tcx, &[lhs_ty, tcx.types.bool]) + } &BinOp::Shl | &BinOp::ShlUnchecked | &BinOp::Shr @@ -282,6 +285,27 @@ impl<'tcx> BinOp { } } +impl<'tcx> UnOp { + pub fn ty(&self, tcx: TyCtxt<'tcx>, arg_ty: Ty<'tcx>) -> Ty<'tcx> { + match self { + UnOp::Not | UnOp::Neg => arg_ty, + UnOp::PtrMetadata => { + let pointee_ty = arg_ty + .builtin_deref(true) + .unwrap_or_else(|| bug!("PtrMetadata of non-dereferenceable ty {arg_ty:?}")); + if pointee_ty.is_trivially_sized(tcx) { + tcx.types.unit + } else { + let Some(metadata_def_id) = tcx.lang_items().metadata_type() else { + bug!("No metadata_type lang item while looking at {arg_ty:?}") + }; + Ty::new_projection(tcx, metadata_def_id, [pointee_ty]) + } + } + } + } +} + impl BorrowKind { pub fn to_mutbl_lossy(self) -> hir::Mutability { match self { @@ -296,11 +320,13 @@ impl BorrowKind { } impl BinOp { - pub fn to_hir_binop(self) -> hir::BinOpKind { + pub(crate) fn to_hir_binop(self) -> hir::BinOpKind { match self { - BinOp::Add => hir::BinOpKind::Add, - BinOp::Sub => hir::BinOpKind::Sub, - BinOp::Mul => hir::BinOpKind::Mul, + // HIR `+`/`-`/`*` can map to either of these MIR BinOp, depending + // on whether overflow checks are enabled or not. + BinOp::Add | BinOp::AddWithOverflow => hir::BinOpKind::Add, + BinOp::Sub | BinOp::SubWithOverflow => hir::BinOpKind::Sub, + BinOp::Mul | BinOp::MulWithOverflow => hir::BinOpKind::Mul, BinOp::Div => hir::BinOpKind::Div, BinOp::Rem => hir::BinOpKind::Rem, BinOp::BitXor => hir::BinOpKind::BitXor, @@ -314,6 +340,7 @@ impl BinOp { BinOp::Gt => hir::BinOpKind::Gt, BinOp::Le => hir::BinOpKind::Le, BinOp::Ge => hir::BinOpKind::Ge, + // We don't have HIR syntax for these. BinOp::Cmp | BinOp::AddUnchecked | BinOp::SubUnchecked @@ -325,4 +352,29 @@ impl BinOp { } } } + + /// If this is a `FooWithOverflow`, return `Some(Foo)`. + pub fn overflowing_to_wrapping(self) -> Option { + Some(match self { + BinOp::AddWithOverflow => BinOp::Add, + BinOp::SubWithOverflow => BinOp::Sub, + BinOp::MulWithOverflow => BinOp::Mul, + _ => return None, + }) + } + + /// Returns whether this is a `FooWithOverflow` + pub fn is_overflowing(self) -> bool { + self.overflowing_to_wrapping().is_some() + } + + /// If this is a `Foo`, return `Some(FooWithOverflow)`. + pub fn wrapping_to_overflowing(self) -> Option { + Some(match self { + BinOp::Add => BinOp::AddWithOverflow, + BinOp::Sub => BinOp::SubWithOverflow, + BinOp::Mul => BinOp::MulWithOverflow, + _ => return None, + }) + } } diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index f95afb199f7f..c8c12e205e11 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -377,9 +377,6 @@ pub struct Terminator<'tcx> { pub kind: TerminatorKind<'tcx>, } -pub type Successors<'a> = impl DoubleEndedIterator + 'a; -pub type SuccessorsMut<'a> = impl DoubleEndedIterator + 'a; - impl<'tcx> Terminator<'tcx> { #[inline] pub fn successors(&self) -> Successors<'_> { @@ -407,81 +404,95 @@ impl<'tcx> TerminatorKind<'tcx> { pub fn if_(cond: Operand<'tcx>, t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> { TerminatorKind::SwitchInt { discr: cond, targets: SwitchTargets::static_if(0, f, t) } } +} - #[inline] - pub fn successors(&self) -> Successors<'_> { - use self::TerminatorKind::*; - match *self { - Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. } - | Yield { resume: ref t, drop: Some(u), .. } - | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), .. } - | Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. } - | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => { - slice::from_ref(t).into_iter().copied().chain(Some(u)) +pub use helper::*; + +mod helper { + use super::*; + pub type Successors<'a> = impl DoubleEndedIterator + 'a; + pub type SuccessorsMut<'a> = impl DoubleEndedIterator + 'a; + impl<'tcx> TerminatorKind<'tcx> { + #[inline] + pub fn successors(&self) -> Successors<'_> { + use self::TerminatorKind::*; + match *self { + Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. } + | Yield { resume: ref t, drop: Some(u), .. } + | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), .. } + | Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. } + | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => { + slice::from_ref(t).into_iter().copied().chain(Some(u)) + } + Goto { target: ref t } + | Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. } + | Call { target: Some(ref t), unwind: _, .. } + | Yield { resume: ref t, drop: None, .. } + | Drop { target: ref t, unwind: _, .. } + | Assert { target: ref t, unwind: _, .. } + | FalseUnwind { real_target: ref t, unwind: _ } => { + slice::from_ref(t).into_iter().copied().chain(None) + } + UnwindResume + | UnwindTerminate(_) + | CoroutineDrop + | Return + | Unreachable + | Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None), + InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => { + targets.iter().copied().chain(Some(u)) + } + InlineAsm { ref targets, unwind: _, .. } => targets.iter().copied().chain(None), + SwitchInt { ref targets, .. } => targets.targets.iter().copied().chain(None), + FalseEdge { ref real_target, imaginary_target } => { + slice::from_ref(real_target).into_iter().copied().chain(Some(imaginary_target)) + } } - Goto { target: ref t } - | Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. } - | Call { target: Some(ref t), unwind: _, .. } - | Yield { resume: ref t, drop: None, .. } - | Drop { target: ref t, unwind: _, .. } - | Assert { target: ref t, unwind: _, .. } - | FalseUnwind { real_target: ref t, unwind: _ } => { - slice::from_ref(t).into_iter().copied().chain(None) - } - UnwindResume - | UnwindTerminate(_) - | CoroutineDrop - | Return - | Unreachable - | Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None), - InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => { - targets.iter().copied().chain(Some(u)) - } - InlineAsm { ref targets, unwind: _, .. } => targets.iter().copied().chain(None), - SwitchInt { ref targets, .. } => targets.targets.iter().copied().chain(None), - FalseEdge { ref real_target, imaginary_target } => { - slice::from_ref(real_target).into_iter().copied().chain(Some(imaginary_target)) - } - } - } - - #[inline] - pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { - use self::TerminatorKind::*; - match *self { - Call { target: Some(ref mut t), unwind: UnwindAction::Cleanup(ref mut u), .. } - | Yield { resume: ref mut t, drop: Some(ref mut u), .. } - | Drop { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. } - | Assert { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. } - | FalseUnwind { real_target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u) } => { - slice::from_mut(t).into_iter().chain(Some(u)) - } - Goto { target: ref mut t } - | Call { target: None, unwind: UnwindAction::Cleanup(ref mut t), .. } - | Call { target: Some(ref mut t), unwind: _, .. } - | Yield { resume: ref mut t, drop: None, .. } - | Drop { target: ref mut t, unwind: _, .. } - | Assert { target: ref mut t, unwind: _, .. } - | FalseUnwind { real_target: ref mut t, unwind: _ } => { - slice::from_mut(t).into_iter().chain(None) - } - UnwindResume - | UnwindTerminate(_) - | CoroutineDrop - | Return - | Unreachable - | Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None), - InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => { - targets.iter_mut().chain(Some(u)) - } - InlineAsm { ref mut targets, unwind: _, .. } => targets.iter_mut().chain(None), - SwitchInt { ref mut targets, .. } => targets.targets.iter_mut().chain(None), - FalseEdge { ref mut real_target, ref mut imaginary_target } => { - slice::from_mut(real_target).into_iter().chain(Some(imaginary_target)) + } + + #[inline] + pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { + use self::TerminatorKind::*; + match *self { + Call { + target: Some(ref mut t), unwind: UnwindAction::Cleanup(ref mut u), .. + } + | Yield { resume: ref mut t, drop: Some(ref mut u), .. } + | Drop { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. } + | Assert { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. } + | FalseUnwind { + real_target: ref mut t, + unwind: UnwindAction::Cleanup(ref mut u), + } => slice::from_mut(t).into_iter().chain(Some(u)), + Goto { target: ref mut t } + | Call { target: None, unwind: UnwindAction::Cleanup(ref mut t), .. } + | Call { target: Some(ref mut t), unwind: _, .. } + | Yield { resume: ref mut t, drop: None, .. } + | Drop { target: ref mut t, unwind: _, .. } + | Assert { target: ref mut t, unwind: _, .. } + | FalseUnwind { real_target: ref mut t, unwind: _ } => { + slice::from_mut(t).into_iter().chain(None) + } + UnwindResume + | UnwindTerminate(_) + | CoroutineDrop + | Return + | Unreachable + | Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None), + InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => { + targets.iter_mut().chain(Some(u)) + } + InlineAsm { ref mut targets, unwind: _, .. } => targets.iter_mut().chain(None), + SwitchInt { ref mut targets, .. } => targets.targets.iter_mut().chain(None), + FalseEdge { ref mut real_target, ref mut imaginary_target } => { + slice::from_mut(real_target).into_iter().chain(Some(imaginary_target)) + } } } } +} +impl<'tcx> TerminatorKind<'tcx> { #[inline] pub fn unwind(&self) -> Option<&UnwindAction> { match *self { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index d97abc3f1904..7628a1ed2fe2 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -184,12 +184,12 @@ macro_rules! make_mir_visitor { /// This is called for every constant in the MIR body and every `required_consts` /// (i.e., including consts that have been dead-code-eliminated). - fn visit_constant( + fn visit_const_operand( &mut self, constant: & $($mutability)? ConstOperand<'tcx>, location: Location, ) { - self.super_constant(constant, location); + self.super_const_operand(constant, location); } fn visit_ty_const( @@ -337,27 +337,27 @@ macro_rules! make_mir_visitor { let ty::Instance { def: callee_def, args: callee_args } = callee; match callee_def { - ty::InstanceDef::Item(_def_id) => {} + ty::InstanceKind::Item(_def_id) => {} - ty::InstanceDef::Intrinsic(_def_id) | - ty::InstanceDef::VTableShim(_def_id) | - ty::InstanceDef::ReifyShim(_def_id, _) | - ty::InstanceDef::Virtual(_def_id, _) | - ty::InstanceDef::ThreadLocalShim(_def_id) | - ty::InstanceDef::ClosureOnceShim { call_once: _def_id, track_caller: _ } | - ty::InstanceDef::ConstructCoroutineInClosureShim { + ty::InstanceKind::Intrinsic(_def_id) | + ty::InstanceKind::VTableShim(_def_id) | + ty::InstanceKind::ReifyShim(_def_id, _) | + ty::InstanceKind::Virtual(_def_id, _) | + ty::InstanceKind::ThreadLocalShim(_def_id) | + ty::InstanceKind::ClosureOnceShim { call_once: _def_id, track_caller: _ } | + ty::InstanceKind::ConstructCoroutineInClosureShim { coroutine_closure_def_id: _def_id, receiver_by_ref: _, } | - ty::InstanceDef::CoroutineKindShim { coroutine_def_id: _def_id } | - ty::InstanceDef::AsyncDropGlueCtorShim(_def_id, None) | - ty::InstanceDef::DropGlue(_def_id, None) => {} + ty::InstanceKind::CoroutineKindShim { coroutine_def_id: _def_id } | + ty::InstanceKind::AsyncDropGlueCtorShim(_def_id, None) | + ty::InstanceKind::DropGlue(_def_id, None) => {} - ty::InstanceDef::FnPtrShim(_def_id, ty) | - ty::InstanceDef::DropGlue(_def_id, Some(ty)) | - ty::InstanceDef::CloneShim(_def_id, ty) | - ty::InstanceDef::FnPtrAddrShim(_def_id, ty) | - ty::InstanceDef::AsyncDropGlueCtorShim(_def_id, Some(ty)) => { + ty::InstanceKind::FnPtrShim(_def_id, ty) | + ty::InstanceKind::DropGlue(_def_id, Some(ty)) | + ty::InstanceKind::CloneShim(_def_id, ty) | + ty::InstanceKind::FnPtrAddrShim(_def_id, ty) | + ty::InstanceKind::AsyncDropGlueCtorShim(_def_id, Some(ty)) => { // FIXME(eddyb) use a better `TyContext` here. self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } @@ -597,7 +597,7 @@ macro_rules! make_mir_visitor { } InlineAsmOperand::Const { value } | InlineAsmOperand::SymFn { value } => { - self.visit_constant(value, location); + self.visit_const_operand(value, location); } InlineAsmOperand::Out { place: None, .. } | InlineAsmOperand::SymStatic { def_id: _ } @@ -696,8 +696,7 @@ macro_rules! make_mir_visitor { self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } - Rvalue::BinaryOp(_bin_op, box(lhs, rhs)) - | Rvalue::CheckedBinaryOp(_bin_op, box(lhs, rhs)) => { + Rvalue::BinaryOp(_bin_op, box(lhs, rhs)) => { self.visit_operand(lhs, location); self.visit_operand(rhs, location); } @@ -789,7 +788,7 @@ macro_rules! make_mir_visitor { ); } Operand::Constant(constant) => { - self.visit_constant(constant, location); + self.visit_const_operand(constant, location); } } } @@ -868,7 +867,7 @@ macro_rules! make_mir_visitor { } } match value { - VarDebugInfoContents::Const(c) => self.visit_constant(c, location), + VarDebugInfoContents::Const(c) => self.visit_const_operand(c, location), VarDebugInfoContents::Place(place) => self.visit_place( place, @@ -883,7 +882,7 @@ macro_rules! make_mir_visitor { _scope: $(& $mutability)? SourceScope ) {} - fn super_constant( + fn super_const_operand( &mut self, constant: & $($mutability)? ConstOperand<'tcx>, location: Location @@ -896,7 +895,7 @@ macro_rules! make_mir_visitor { self.visit_span($(& $mutability)? *span); match const_ { - Const::Ty(ct) => self.visit_ty_const($(&$mutability)? *ct, location), + Const::Ty(_, ct) => self.visit_ty_const($(&$mutability)? *ct, location), Const::Val(_, ty) => self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)), Const::Unevaluated(_, ty) => self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)), } @@ -1058,7 +1057,7 @@ macro_rules! super_body { for const_ in &$($mutability)? $body.required_consts { let location = Location::START; - $self.visit_constant(const_, location); + $self.visit_const_operand(const_, location); } } } diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 320d49ea6467..301c9911b44c 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -4,7 +4,7 @@ use crate::traits; use crate::ty::adjustment::CoerceUnsizedInfo; use crate::ty::{self, Ty}; use std::intrinsics::transmute_unchecked; -use std::mem::{size_of, MaybeUninit}; +use std::mem::MaybeUninit; #[derive(Copy, Clone)] pub struct Erased { @@ -114,9 +114,11 @@ impl EraseType for Result { type Result = [u8; size_of::>()]; } -impl EraseType for Result>>, rustc_errors::ErrorGuaranteed> { +impl EraseType + for Result>>, rustc_errors::ErrorGuaranteed> +{ type Result = [u8; size_of::< - Result>>, rustc_errors::ErrorGuaranteed>, + Result>>, rustc_errors::ErrorGuaranteed>, >()]; } @@ -165,8 +167,8 @@ impl EraseType for Result<&'_ ty::List>, ty::util::AlwaysRequiresDrop> { [u8; size_of::>, ty::util::AlwaysRequiresDrop>>()]; } -impl EraseType for Result>, CyclePlaceholder> { - type Result = [u8; size_of::>, CyclePlaceholder>>()]; +impl EraseType for Result>, CyclePlaceholder> { + type Result = [u8; size_of::>, CyclePlaceholder>>()]; } impl EraseType for Option<&'_ T> { @@ -185,15 +187,15 @@ impl EraseType for Option> { type Result = [u8; size_of::>>()]; } -impl EraseType for Option>> { - type Result = [u8; size_of::>>>()]; +impl EraseType for Option>> { + type Result = [u8; size_of::>>>()]; } impl EraseType for rustc_hir::MaybeOwner<'_> { type Result = [u8; size_of::>()]; } -impl EraseType for ty::EarlyBinder { +impl EraseType for ty::EarlyBinder<'_, T> { type Result = T::Result; } @@ -236,6 +238,7 @@ trivial! { Option, Option, Option, + Option, Option, Option, Option, @@ -293,6 +296,7 @@ trivial! { rustc_middle::ty::AssocItem, rustc_middle::ty::AssocItemContainer, rustc_middle::ty::Asyncness, + rustc_middle::ty::AsyncDestructor, rustc_middle::ty::BoundVariableKind, rustc_middle::ty::DeducedParamAttrs, rustc_middle::ty::Destructor, @@ -361,7 +365,7 @@ tcx_lifetime! { rustc_middle::ty::GenericPredicates, rustc_middle::ty::inhabitedness::InhabitedPredicate, rustc_middle::ty::Instance, - rustc_middle::ty::InstanceDef, + rustc_middle::ty::InstanceKind, rustc_middle::ty::layout::FnAbiError, rustc_middle::ty::layout::LayoutError, rustc_middle::ty::ParamEnv, diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index faa137019cb9..add88491f846 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -62,7 +62,7 @@ impl Key for () { } } -impl<'tcx> Key for ty::InstanceDef<'tcx> { +impl<'tcx> Key for ty::InstanceKind<'tcx> { type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { @@ -70,7 +70,7 @@ impl<'tcx> Key for ty::InstanceDef<'tcx> { } } -impl<'tcx> AsLocalKey for ty::InstanceDef<'tcx> { +impl<'tcx> AsLocalKey for ty::InstanceKind<'tcx> { type LocalKey = Self; #[inline(always)] @@ -360,6 +360,14 @@ impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::TraitRef<'tcx>) { } } +impl<'tcx> Key for ty::TraitRef<'tcx> { + type Cache = DefaultCache; + + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + tcx.def_span(self.def_id) + } +} + impl<'tcx> Key for ty::PolyTraitRef<'tcx> { type Cache = DefaultCache; diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index c2f7a227f666..c5afecffb07a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -46,6 +46,7 @@ use crate::traits::{ }; use crate::ty::fast_reject::SimplifiedType; use crate::ty::layout::ValidityRequirement; +use crate::ty::print::PrintTraitRefExt; use crate::ty::util::AlwaysRequiresDrop; use crate::ty::TyCtxtFeed; use crate::ty::{ @@ -209,7 +210,7 @@ rustc_queries! { /// Given the def_id of a const-generic parameter, computes the associated default const /// parameter. e.g. `fn example` called on `N` would return `3`. - query const_param_default(param: DefId) -> ty::EarlyBinder> { + query const_param_default(param: DefId) -> ty::EarlyBinder<'tcx, ty::Const<'tcx>> { desc { |tcx| "computing const default for a given parameter `{}`", tcx.def_path_str(param) } cache_on_disk_if { param.is_local() } separate_provide_extern @@ -219,7 +220,7 @@ rustc_queries! { /// to an alias, it will "skip" this alias to return the aliased type. /// /// [`DefId`]: rustc_hir::def_id::DefId - query type_of(key: DefId) -> ty::EarlyBinder> { + query type_of(key: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { desc { |tcx| "{action} `{path}`", action = { @@ -240,7 +241,7 @@ rustc_queries! { /// Specialized instance of `type_of` that detects cycles that are due to /// revealing opaque because of an auto trait bound. Unless `CyclePlaceholder` needs /// to be handled separately, call `type_of` instead. - query type_of_opaque(key: DefId) -> Result>, CyclePlaceholder> { + query type_of_opaque(key: DefId) -> Result>, CyclePlaceholder> { desc { |tcx| "computing type of opaque `{path}`", path = tcx.def_path_str(key), @@ -257,7 +258,7 @@ rustc_queries! { } query collect_return_position_impl_trait_in_trait_tys(key: DefId) - -> Result<&'tcx DefIdMap>>, ErrorGuaranteed> + -> Result<&'tcx DefIdMap>>, ErrorGuaranteed> { desc { "comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process" } cache_on_disk_if { key.is_local() } @@ -363,7 +364,7 @@ rustc_queries! { /// `key` is the `DefId` of the associated type or opaque type. /// /// Bounds from the parent (e.g. with nested impl trait) are not included. - query explicit_item_bounds(key: DefId) -> ty::EarlyBinder<&'tcx [(ty::Clause<'tcx>, Span)]> { + query explicit_item_bounds(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern @@ -373,7 +374,7 @@ rustc_queries! { /// share the `Self` type of the item. These are a subset of the bounds /// that may explicitly be used for things like closure signature /// deduction. - query explicit_item_super_predicates(key: DefId) -> ty::EarlyBinder<&'tcx [(ty::Clause<'tcx>, Span)]> { + query explicit_item_super_predicates(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern @@ -399,15 +400,15 @@ rustc_queries! { /// ``` /// /// Bounds from the parent (e.g. with nested impl trait) are not included. - query item_bounds(key: DefId) -> ty::EarlyBinder> { + query item_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) } } - query item_super_predicates(key: DefId) -> ty::EarlyBinder> { + query item_super_predicates(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } } - query item_non_self_assumptions(key: DefId) -> ty::EarlyBinder> { + query item_non_self_assumptions(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } } @@ -504,7 +505,7 @@ rustc_queries! { /// Try to build an abstract representation of the given constant. query thir_abstract_const( key: DefId - ) -> Result>>, ErrorGuaranteed> { + ) -> Result>>, ErrorGuaranteed> { desc { |tcx| "building an abstract representation for `{}`", tcx.def_path_str(key), } @@ -574,7 +575,7 @@ rustc_queries! { /// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass /// (for compiler option `-Cinstrument-coverage`), after MIR optimizations /// have had a chance to potentially remove some of them. - query coverage_ids_info(key: ty::InstanceDef<'tcx>) -> &'tcx mir::CoverageIdsInfo { + query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> &'tcx mir::CoverageIdsInfo { desc { |tcx| "retrieving coverage IDs info from MIR for `{}`", tcx.def_path_str(key.def_id()) } arena_cache } @@ -703,8 +704,13 @@ rustc_queries! { cache_on_disk_if { key.is_local() } separate_provide_extern } + query adt_async_destructor(key: DefId) -> Option { + desc { |tcx| "computing `AsyncDrop` impl for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } - query adt_sized_constraint(key: DefId) -> Option>> { + query adt_sized_constraint(key: DefId) -> Option>> { desc { |tcx| "computing the `Sized` constraint for `{}`", tcx.def_path_str(key) } } @@ -780,7 +786,7 @@ rustc_queries! { separate_provide_extern } - /// Maps from a trait item to the trait item "descriptor". + /// Maps from a trait/impl item to the trait/impl item "descriptor". query associated_item(key: DefId) -> ty::AssocItem { desc { |tcx| "computing associated item data for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } @@ -849,7 +855,7 @@ rustc_queries! { query self_ty_of_trait_impl_enabling_order_dep_trait_object_hack( key: DefId - ) -> Option>> { + ) -> Option>> { desc { |tcx| "computing self type wrt issue #33140 `{}`", tcx.def_path_str(key) } } @@ -888,7 +894,7 @@ rustc_queries! { } /// Computes the signature of the function. - query fn_sig(key: DefId) -> ty::EarlyBinder> { + query fn_sig(key: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> { desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern @@ -1027,7 +1033,7 @@ rustc_queries! { } /// Obtain all the calls into other local functions - query mir_inliner_callees(key: ty::InstanceDef<'tcx>) -> &'tcx [(DefId, GenericArgsRef<'tcx>)] { + query mir_inliner_callees(key: ty::InstanceKind<'tcx>) -> &'tcx [(DefId, GenericArgsRef<'tcx>)] { fatal_cycle desc { |tcx| "computing all local function calls in `{}`", @@ -1134,7 +1140,7 @@ rustc_queries! { } /// Generates a MIR body for the shim. - query mir_shims(key: ty::InstanceDef<'tcx>) -> &'tcx mir::Body<'tcx> { + query mir_shims(key: ty::InstanceKind<'tcx>) -> &'tcx mir::Body<'tcx> { arena_cache desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) } } @@ -1266,7 +1272,11 @@ rustc_queries! { desc { |tcx| "finding all vtable entries for trait `{}`", tcx.def_path_str(key.def_id()) } } - query vtable_trait_upcasting_coercion_new_vptr_slot(key: (Ty<'tcx>, Ty<'tcx>)) -> Option { + query first_method_vtable_slot(key: ty::TraitRef<'tcx>) -> usize { + desc { |tcx| "finding the slot within the vtable of `{}` for the implementation of `{}`", key.self_ty(), key.print_only_trait_name() } + } + + query supertrait_vtable_slot(key: (Ty<'tcx>, Ty<'tcx>)) -> Option { desc { |tcx| "finding the slot within vtable for trait object `{}` vtable ptr during trait upcasting coercion from `{}` vtable", key.1, key.0 } } @@ -1304,7 +1314,7 @@ rustc_queries! { query object_safety_violations(trait_id: DefId) -> &'tcx [ObjectSafetyViolation] { desc { |tcx| "determining object safety of trait `{}`", tcx.def_path_str(trait_id) } } - query check_is_object_safe(trait_id: DefId) -> bool { + query is_object_safe(trait_id: DefId) -> bool { desc { |tcx| "checking if trait `{}` is object safe", tcx.def_path_str(trait_id) } } @@ -1343,18 +1353,14 @@ rustc_queries! { query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is `Unpin`", env.value } } - /// Query backing `Ty::has_surface_async_drop`. - query has_surface_async_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` has `AsyncDrop` implementation", env.value } - } - /// Query backing `Ty::has_surface_drop`. - query has_surface_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` has `Drop` implementation", env.value } - } /// Query backing `Ty::needs_drop`. query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` needs drop", env.value } } + /// Query backing `Ty::needs_async_drop`. + query needs_async_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` needs async drop", env.value } + } /// Query backing `Ty::has_significant_drop_raw`. query has_significant_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` has a significant drop", env.value } @@ -1404,7 +1410,7 @@ rustc_queries! { /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. /// /// NB: this doesn't handle virtual calls - those should use `fn_abi_of_instance` - /// instead, where the instance is an `InstanceDef::Virtual`. + /// instead, where the instance is an `InstanceKind::Virtual`. query fn_abi_of_fn_ptr( key: ty::ParamEnvAnd<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List>)> ) -> Result<&'tcx abi::call::FnAbi<'tcx, Ty<'tcx>>, &'tcx ty::layout::FnAbiError<'tcx>> { @@ -1415,7 +1421,7 @@ rustc_queries! { /// direct calls to an `fn`. /// /// NB: that includes virtual calls, which are represented by "direct calls" - /// to an `InstanceDef::Virtual` instance (of `::fn`). + /// to an `InstanceKind::Virtual` instance (of `::fn`). query fn_abi_of_instance( key: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)> ) -> Result<&'tcx abi::call::FnAbi<'tcx, Ty<'tcx>>, &'tcx ty::layout::FnAbiError<'tcx>> { @@ -1493,6 +1499,11 @@ rustc_queries! { separate_provide_extern } + query specialization_enabled_in(cnum: CrateNum) -> bool { + desc { "checking whether the crate enabled `specialization`/`min_specialization`" } + separate_provide_extern + } + query specializes(_: (DefId, DefId)) -> bool { desc { "computing whether impls specialize one another" } } @@ -1543,12 +1554,13 @@ rustc_queries! { } } - /// The entire set of monomorphizations the local crate can safely link - /// to because they are exported from upstream crates. Do not depend on - /// this directly, as its value changes anytime a monomorphization gets - /// added or removed in any upstream crate. Instead use the narrower - /// `upstream_monomorphizations_for`, `upstream_drop_glue_for`, or, even - /// better, `Instance::upstream_monomorphization()`. + /// The entire set of monomorphizations the local crate can safely + /// link to because they are exported from upstream crates. Do + /// not depend on this directly, as its value changes anytime + /// a monomorphization gets added or removed in any upstream + /// crate. Instead use the narrower `upstream_monomorphizations_for`, + /// `upstream_drop_glue_for`, `upstream_async_drop_glue_for`, or, + /// even better, `Instance::upstream_monomorphization()`. query upstream_monomorphizations(_: ()) -> &'tcx DefIdMap, CrateNum>> { arena_cache desc { "collecting available upstream monomorphizations" } @@ -1590,6 +1602,26 @@ rustc_queries! { desc { "available upstream drop-glue for `{:?}`", args } } + /// Returns the upstream crate that exports async-drop-glue for + /// the given type (`args` is expected to be a single-item list + /// containing the type one wants async-drop-glue for). + /// + /// This is a subset of `upstream_monomorphizations_for` in order + /// to increase dep-tracking granularity. Otherwise adding or + /// removing any type with async-drop-glue in any upstream crate + /// would invalidate all functions calling async-drop-glue of an + /// upstream type. + /// + /// You likely want to call `Instance::upstream_monomorphization()` + /// instead of invoking this query directly. + /// + /// NOTE: This query could easily be extended to also support other + /// common functions that have are large set of monomorphizations + /// (like `Clone::clone` for example). + query upstream_async_drop_glue_for(args: GenericArgsRef<'tcx>) -> Option { + desc { "available upstream async-drop-glue for `{:?}`", args } + } + /// Returns a list of all `extern` blocks of a crate. query foreign_modules(_: CrateNum) -> &'tcx FxIndexMap { arena_cache @@ -1907,7 +1939,7 @@ rustc_queries! { desc { "getting codegen unit `{sym}`" } } - query unused_generic_params(key: ty::InstanceDef<'tcx>) -> UnusedGenericParams { + query unused_generic_params(key: ty::InstanceKind<'tcx>) -> UnusedGenericParams { cache_on_disk_if { key.def_id().is_local() } desc { |tcx| "determining which generic parameters are unused by `{}`", @@ -2209,15 +2241,6 @@ rustc_queries! { separate_provide_extern } - /// Used in `super_combine_consts` to ICE if the type of the two consts are definitely not going to end up being - /// equal to eachother. This might return `Ok` even if the types are not equal, but will never return `Err` if - /// the types might be equal. - query check_tys_might_be_eq( - arg: Canonical<'tcx, ty::ParamEnvAnd<'tcx, (Ty<'tcx>, Ty<'tcx>)>> - ) -> Result<(), NoSolution> { - desc { "check whether two const param are definitely not equal to eachother"} - } - /// Get all item paths that were stripped by a `#[cfg]` in a particular crate. /// Should not be called for the local crate before the resolver outputs are created, as it /// is only fed there. diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 2dcb58729ff9..ccd0c7cb10c4 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -154,24 +154,25 @@ impl EncodedSourceFileId { impl<'sess> OnDiskCache<'sess> { /// Creates a new `OnDiskCache` instance from the serialized data in `data`. - pub fn new(sess: &'sess Session, data: Mmap, start_pos: usize) -> Self { - debug_assert!(sess.opts.incremental.is_some()); + /// + /// The serialized cache has some basic integrity checks, if those checks indicate that the + /// on-disk data is corrupt, an error is returned. + pub fn new(sess: &'sess Session, data: Mmap, start_pos: usize) -> Result { + assert!(sess.opts.incremental.is_some()); - // Wrap in a scope so we can borrow `data`. - let footer: Footer = { - let mut decoder = MemDecoder::new(&data, start_pos); + let mut decoder = MemDecoder::new(&data, start_pos)?; - // Decode the *position* of the footer, which can be found in the - // last 8 bytes of the file. - let footer_pos = decoder - .with_position(decoder.len() - IntEncodedWithFixedSize::ENCODED_SIZE, |decoder| { - IntEncodedWithFixedSize::decode(decoder).0 as usize - }); - // Decode the file footer, which contains all the lookup tables, etc. - decoder.with_position(footer_pos, |decoder| decode_tagged(decoder, TAG_FILE_FOOTER)) - }; + // Decode the *position* of the footer, which can be found in the + // last 8 bytes of the file. + let footer_pos = decoder + .with_position(decoder.len() - IntEncodedWithFixedSize::ENCODED_SIZE, |decoder| { + IntEncodedWithFixedSize::decode(decoder).0 as usize + }); + // Decode the file footer, which contains all the lookup tables, etc. + let footer: Footer = + decoder.with_position(footer_pos, |decoder| decode_tagged(decoder, TAG_FILE_FOOTER)); - Self { + Ok(Self { serialized_data: RwLock::new(Some(data)), file_index_to_stable_id: footer.file_index_to_stable_id, file_index_to_file: Default::default(), @@ -184,7 +185,7 @@ impl<'sess> OnDiskCache<'sess> { expn_data: footer.expn_data, foreign_expn_data: footer.foreign_expn_data, hygiene_context: Default::default(), - } + }) } pub fn new_empty(source_map: &'sess SourceMap) -> Self { @@ -437,7 +438,8 @@ impl<'sess> OnDiskCache<'sess> { let serialized_data = self.serialized_data.read(); let mut decoder = CacheDecoder { tcx, - opaque: MemDecoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize()), + opaque: MemDecoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize()) + .unwrap(), source_map: self.source_map, file_index_to_file: &self.file_index_to_file, file_index_to_stable_id: &self.file_index_to_stable_id, @@ -558,7 +560,7 @@ impl<'a, 'tcx> TyDecoder for CacheDecoder<'a, 'tcx> { { debug_assert!(pos < self.opaque.len()); - let new_opaque = MemDecoder::new(self.opaque.data(), pos); + let new_opaque = self.opaque.split_at(pos); let old_opaque = mem::replace(&mut self.opaque, new_opaque); let r = f(self); self.opaque = old_opaque; @@ -750,7 +752,7 @@ impl<'a, 'tcx> Decodable> for &'tcx UnordSet } impl<'a, 'tcx> Decodable> - for &'tcx UnordMap>> + for &'tcx UnordMap>> { #[inline] fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index c68b7a6c9eb3..7c8b0ec671a4 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -32,6 +32,7 @@ use rustc_target::asm::InlineAsmRegOrRegClass; use std::cmp::Ordering; use std::fmt; use std::ops::Index; +use tracing::instrument; pub mod visit; @@ -1032,8 +1033,8 @@ impl<'tcx> PatRangeBoundary<'tcx> { if let (Some(a), Some(b)) = (a.try_to_scalar_int(), b.try_to_scalar_int()) { let sz = ty.primitive_size(tcx); let cmp = match ty.kind() { - ty::Uint(_) | ty::Char => a.assert_uint(sz).cmp(&b.assert_uint(sz)), - ty::Int(_) => a.assert_int(sz).cmp(&b.assert_int(sz)), + ty::Uint(_) | ty::Char => a.to_uint(sz).cmp(&b.to_uint(sz)), + ty::Int(_) => a.to_int(sz).cmp(&b.to_int(sz)), _ => unreachable!(), }; return Some(cmp); diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 62e71c4db11f..b4e3fae1b43b 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -32,54 +32,7 @@ use std::hash::{Hash, Hasher}; pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache}; // FIXME: Remove this import and import via `solve::` -pub use rustc_next_trait_solver::solve::BuiltinImplSource; - -/// Depending on the stage of compilation, we want projection to be -/// more or less conservative. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable, Encodable, Decodable)] -pub enum Reveal { - /// At type-checking time, we refuse to project any associated - /// type that is marked `default`. Non-`default` ("final") types - /// are always projected. This is necessary in general for - /// soundness of specialization. However, we *could* allow - /// projections in fully-monomorphic cases. We choose not to, - /// because we prefer for `default type` to force the type - /// definition to be treated abstractly by any consumers of the - /// impl. Concretely, that means that the following example will - /// fail to compile: - /// - /// ```compile_fail,E0308 - /// #![feature(specialization)] - /// trait Assoc { - /// type Output; - /// } - /// - /// impl Assoc for T { - /// default type Output = bool; - /// } - /// - /// fn main() { - /// let x: <() as Assoc>::Output = true; - /// } - /// ``` - /// - /// We also do not reveal the hidden type of opaque types during - /// type-checking. - UserFacing, - - /// At codegen time, all monomorphic projections will succeed. - /// Also, `impl Trait` is normalized to the concrete type, - /// which has to be already collected by type-checking. - /// - /// NOTE: as `impl Trait`'s concrete type should *never* - /// be observable directly by the user, `Reveal::All` - /// should not be used by checks which may expose - /// type equality or type contents to the user. - /// There are some exceptions, e.g., around auto traits and - /// transmute-checking, which expose some details, but - /// not the whole concrete type of the `impl Trait`. - All, -} +pub use rustc_type_ir::solve::{BuiltinImplSource, Reveal}; /// The reason why we incurred this obligation; used for error reporting. /// @@ -616,6 +569,8 @@ pub enum SelectionError<'tcx> { /// We can thus not know whether the hidden type implements an auto trait, so /// we should not presume anything about it. OpaqueTypeAutoTraitLeakageUnknown(DefId), + /// Error for a `ConstArgHasType` goal + ConstArgHasWrongType { ct: ty::Const<'tcx>, ct_ty: Ty<'tcx>, expected_ty: Ty<'tcx> }, } #[derive(Clone, Debug, TypeVisitable)] diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 66e50307733d..4fad721ce984 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -7,13 +7,12 @@ use crate::error::DropCheckOverflow; use crate::infer::canonical::{Canonical, QueryResponse}; -use crate::ty::error::TypeError; use crate::ty::GenericArg; use crate::ty::{self, Ty, TyCtxt}; use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; use rustc_span::Span; // FIXME: Remove this import and import via `traits::solve`. -pub use rustc_next_trait_solver::solve::NoSolution; +pub use rustc_type_ir::solve::NoSolution; pub mod type_op { use crate::ty::fold::TypeFoldable; @@ -91,12 +90,6 @@ pub type CanonicalTypeOpProvePredicateGoal<'tcx> = pub type CanonicalTypeOpNormalizeGoal<'tcx, T> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize>>; -impl<'tcx> From> for NoSolution { - fn from(_: TypeError<'tcx>) -> NoSolution { - NoSolution - } -} - #[derive(Clone, Debug, Default, HashStable, TypeFoldable, TypeVisitable)] pub struct DropckOutlivesResult<'tcx> { pub kinds: Vec>, @@ -163,7 +156,7 @@ pub struct CandidateStep<'tcx> { #[derive(Copy, Clone, Debug, HashStable)] pub struct MethodAutoderefStepsResult<'tcx> { - /// The valid autoderef steps that could be find. + /// The valid autoderef steps that could be found. pub steps: &'tcx [CandidateStep<'tcx>], /// If Some(T), a type autoderef reported an error on. pub opt_bad_ty: Option<&'tcx MethodAutoderefBadTy<'tcx>>, diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index c8c16ec1e2ce..90f80f90767d 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -1,17 +1,16 @@ use rustc_ast_ir::try_visit; use rustc_data_structures::intern::Interned; -use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; -use rustc_next_trait_solver as ir; -pub use rustc_next_trait_solver::solve::*; +use rustc_macros::HashStable; +use rustc_type_ir as ir; +pub use rustc_type_ir::solve::*; -use crate::infer::canonical::QueryRegionConstraints; use crate::ty::{ - self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, + self, FallibleTypeFolder, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, }; mod cache; -pub use cache::{CacheData, EvaluationCache}; +pub use cache::EvaluationCache; pub type Goal<'tcx, P> = ir::solve::Goal, P>; pub type QueryInput<'tcx, P> = ir::solve::QueryInput, P>; @@ -20,17 +19,11 @@ pub type CandidateSource<'tcx> = ir::solve::CandidateSource>; pub type CanonicalInput<'tcx, P = ty::Predicate<'tcx>> = ir::solve::CanonicalInput, P>; pub type CanonicalResponse<'tcx> = ir::solve::CanonicalResponse>; -/// Additional constraints returned on success. -#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default)] -pub struct PredefinedOpaquesData<'tcx> { - pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>, -} - #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)] -pub struct PredefinedOpaques<'tcx>(pub(crate) Interned<'tcx, PredefinedOpaquesData<'tcx>>); +pub struct PredefinedOpaques<'tcx>(pub(crate) Interned<'tcx, PredefinedOpaquesData>>); impl<'tcx> std::ops::Deref for PredefinedOpaques<'tcx> { - type Target = PredefinedOpaquesData<'tcx>; + type Target = PredefinedOpaquesData>; fn deref(&self) -> &Self::Target { &self.0 @@ -38,37 +31,18 @@ impl<'tcx> std::ops::Deref for PredefinedOpaques<'tcx> { } #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)] -pub struct ExternalConstraints<'tcx>(pub(crate) Interned<'tcx, ExternalConstraintsData<'tcx>>); +pub struct ExternalConstraints<'tcx>( + pub(crate) Interned<'tcx, ExternalConstraintsData>>, +); impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> { - type Target = ExternalConstraintsData<'tcx>; + type Target = ExternalConstraintsData>; fn deref(&self) -> &Self::Target { &self.0 } } -/// Additional constraints returned on success. -#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default, TypeVisitable, TypeFoldable)] -pub struct ExternalConstraintsData<'tcx> { - // FIXME: implement this. - pub region_constraints: QueryRegionConstraints<'tcx>, - pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>, - pub normalization_nested_goals: NestedNormalizationGoals<'tcx>, -} - -#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default, TypeVisitable, TypeFoldable)] -pub struct NestedNormalizationGoals<'tcx>(pub Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>); -impl<'tcx> NestedNormalizationGoals<'tcx> { - pub fn empty() -> Self { - NestedNormalizationGoals(vec![]) - } - - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } -} - // FIXME: Having to clone `region_constraints` for folding feels bad and // probably isn't great wrt performance. // diff --git a/compiler/rustc_middle/src/traits/solve/cache.rs b/compiler/rustc_middle/src/traits/solve/cache.rs index 03ce7cf98cf7..72a8d4eb4050 100644 --- a/compiler/rustc_middle/src/traits/solve/cache.rs +++ b/compiler/rustc_middle/src/traits/solve/cache.rs @@ -5,6 +5,8 @@ use rustc_data_structures::sync::Lock; use rustc_query_system::cache::WithDepNode; use rustc_query_system::dep_graph::DepNodeIndex; use rustc_session::Limit; +use rustc_type_ir::solve::CacheData; + /// The trait solver cache used by `-Znext-solver`. /// /// FIXME(@lcnr): link to some official documentation of how @@ -14,22 +16,14 @@ pub struct EvaluationCache<'tcx> { map: Lock, CacheEntry<'tcx>>>, } -#[derive(PartialEq, Eq)] -pub struct CacheData<'tcx> { - pub result: QueryResult<'tcx>, - pub proof_tree: Option<&'tcx [inspect::GoalEvaluationStep>]>, - pub reached_depth: usize, - pub encountered_overflow: bool, -} - -impl<'tcx> EvaluationCache<'tcx> { +impl<'tcx> rustc_type_ir::inherent::EvaluationCache> for &'tcx EvaluationCache<'tcx> { /// Insert a final result into the global cache. - pub fn insert( + fn insert( &self, tcx: TyCtxt<'tcx>, key: CanonicalInput<'tcx>, - proof_tree: Option<&'tcx [inspect::GoalEvaluationStep>]>, - reached_depth: usize, + proof_tree: Option<&'tcx inspect::CanonicalGoalEvaluationStep>>, + additional_depth: usize, encountered_overflow: bool, cycle_participants: FxHashSet>, dep_node: DepNodeIndex, @@ -40,17 +34,17 @@ impl<'tcx> EvaluationCache<'tcx> { let data = WithDepNode::new(dep_node, QueryData { result, proof_tree }); entry.cycle_participants.extend(cycle_participants); if encountered_overflow { - entry.with_overflow.insert(reached_depth, data); + entry.with_overflow.insert(additional_depth, data); } else { - entry.success = Some(Success { data, reached_depth }); + entry.success = Some(Success { data, additional_depth }); } if cfg!(debug_assertions) { drop(map); - if Some(CacheData { result, proof_tree, reached_depth, encountered_overflow }) - != self.get(tcx, key, |_| false, Limit(reached_depth)) - { - bug!("unable to retrieve inserted element from cache: {key:?}"); + let expected = CacheData { result, proof_tree, additional_depth, encountered_overflow }; + let actual = self.get(tcx, key, [], additional_depth); + if !actual.as_ref().is_some_and(|actual| expected == *actual) { + bug!("failed to lookup inserted element for {key:?}: {expected:?} != {actual:?}"); } } } @@ -59,38 +53,40 @@ impl<'tcx> EvaluationCache<'tcx> { /// and handling root goals of coinductive cycles. /// /// If this returns `Some` the cache result can be used. - pub fn get( + fn get( &self, tcx: TyCtxt<'tcx>, key: CanonicalInput<'tcx>, - cycle_participant_in_stack: impl FnOnce(&FxHashSet>) -> bool, - available_depth: Limit, - ) -> Option> { + stack_entries: impl IntoIterator>, + available_depth: usize, + ) -> Option>> { let map = self.map.borrow(); let entry = map.get(&key)?; - if cycle_participant_in_stack(&entry.cycle_participants) { - return None; + for stack_entry in stack_entries { + if entry.cycle_participants.contains(&stack_entry) { + return None; + } } if let Some(ref success) = entry.success { - if available_depth.value_within_limit(success.reached_depth) { + if Limit(available_depth).value_within_limit(success.additional_depth) { let QueryData { result, proof_tree } = success.data.get(tcx); return Some(CacheData { result, proof_tree, - reached_depth: success.reached_depth, + additional_depth: success.additional_depth, encountered_overflow: false, }); } } - entry.with_overflow.get(&available_depth.0).map(|e| { + entry.with_overflow.get(&available_depth).map(|e| { let QueryData { result, proof_tree } = e.get(tcx); CacheData { result, proof_tree, - reached_depth: available_depth.0, + additional_depth: available_depth, encountered_overflow: true, } }) @@ -99,13 +95,13 @@ impl<'tcx> EvaluationCache<'tcx> { struct Success<'tcx> { data: WithDepNode>, - reached_depth: usize, + additional_depth: usize, } #[derive(Clone, Copy)] pub struct QueryData<'tcx> { pub result: QueryResult<'tcx>, - pub proof_tree: Option<&'tcx [inspect::GoalEvaluationStep>]>, + pub proof_tree: Option<&'tcx inspect::CanonicalGoalEvaluationStep>>, } /// The cache entry for a goal `CanonicalInput`. diff --git a/compiler/rustc_middle/src/traits/util.rs b/compiler/rustc_middle/src/traits/util.rs index 707e076921bc..adbb6cf2ddc9 100644 --- a/compiler/rustc_middle/src/traits/util.rs +++ b/compiler/rustc_middle/src/traits/util.rs @@ -37,7 +37,7 @@ impl<'tcx> Elaborator<'tcx> { let super_predicates = self.tcx.super_predicates_of(trait_ref.def_id()).predicates.iter().filter_map( |&(pred, _)| { - let clause = pred.instantiate_supertrait(self.tcx, &trait_ref); + let clause = pred.instantiate_supertrait(self.tcx, trait_ref); self.visited.insert(clause).then_some(clause) }, ); diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index dc46b470b6f4..254e1b544819 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -30,7 +30,8 @@ impl From for NotConstEvaluatable { TrivialTypeTraversalImpls! { NotConstEvaluatable } -pub type BoundAbstractConst<'tcx> = Result>>, ErrorGuaranteed>; +pub type BoundAbstractConst<'tcx> = + Result>>, ErrorGuaranteed>; impl<'tcx> TyCtxt<'tcx> { pub fn expand_abstract_consts>>(self, ac: T) -> T { @@ -52,7 +53,7 @@ impl<'tcx> TyCtxt<'tcx> { fn fold_const(&mut self, c: Const<'tcx>) -> Const<'tcx> { let ct = match c.kind() { ty::ConstKind::Unevaluated(uv) => match self.tcx.thir_abstract_const(uv.def) { - Err(e) => ty::Const::new_error(self.tcx, e, c.ty()), + Err(e) => ty::Const::new_error(self.tcx, e), Ok(Some(bac)) => { let args = self.tcx.erase_regions(uv.args); let bac = bac.instantiate(self.tcx, args); diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 77da3fbe1d79..8e221cdc603b 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -8,22 +8,25 @@ use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::HashingControls; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, LangItem}; use rustc_index::{IndexSlice, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::ich::StableHashingContext; use rustc_session::DataTypeKind; use rustc_span::symbol::sym; use rustc_target::abi::{ReprOptions, VariantIdx, FIRST_VARIANT}; +use tracing::{debug, info, trace}; use std::cell::RefCell; use std::hash::{Hash, Hasher}; use std::ops::Range; use std::str; -use super::{Destructor, FieldDef, GenericPredicates, Ty, TyCtxt, VariantDef, VariantDiscr}; +use super::{ + AsyncDestructor, Destructor, FieldDef, GenericPredicates, Ty, TyCtxt, VariantDef, VariantDiscr, +}; #[derive(Clone, Copy, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)] pub struct AdtFlags(u16); @@ -197,6 +200,37 @@ impl<'tcx> AdtDef<'tcx> { } } +impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { + fn def_id(self) -> DefId { + self.did() + } + + fn is_struct(self) -> bool { + self.is_struct() + } + + fn struct_tail_ty(self, interner: TyCtxt<'tcx>) -> Option>> { + Some(interner.type_of(self.non_enum_variant().tail_opt()?.did)) + } + + fn is_phantom_data(self) -> bool { + self.is_phantom_data() + } + + fn all_field_tys( + self, + tcx: TyCtxt<'tcx>, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + ty::EarlyBinder::bind( + self.all_fields().map(move |field| tcx.type_of(field.did).skip_binder()), + ) + } + + fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option>> { + self.sized_constraint(tcx) + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable, TyEncodable, TyDecodable)] pub enum AdtKind { Struct, @@ -248,16 +282,16 @@ impl AdtDefData { if tcx.has_attr(did, sym::fundamental) { flags |= AdtFlags::IS_FUNDAMENTAL; } - if Some(did) == tcx.lang_items().phantom_data() { + if tcx.is_lang_item(did, LangItem::PhantomData) { flags |= AdtFlags::IS_PHANTOM_DATA; } - if Some(did) == tcx.lang_items().owned_box() { + if tcx.is_lang_item(did, LangItem::OwnedBox) { flags |= AdtFlags::IS_BOX; } - if Some(did) == tcx.lang_items().manually_drop() { + if tcx.is_lang_item(did, LangItem::ManuallyDrop) { flags |= AdtFlags::IS_MANUALLY_DROP; } - if Some(did) == tcx.lang_items().unsafe_cell_type() { + if tcx.is_lang_item(did, LangItem::UnsafeCell) { flags |= AdtFlags::IS_UNSAFE_CELL; } if is_anonymous { @@ -576,9 +610,15 @@ impl<'tcx> AdtDef<'tcx> { tcx.adt_destructor(self.did()) } + // FIXME: consider combining this method with `AdtDef::destructor` and removing + // this version + pub fn async_destructor(self, tcx: TyCtxt<'tcx>) -> Option { + tcx.adt_async_destructor(self.did()) + } + /// Returns a type such that `Self: Sized` if and only if that type is `Sized`, /// or `None` if the type is always sized. - pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option>> { + pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option>> { if self.is_struct() { tcx.adt_sized_constraint(self.did()) } else { None } } } diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index c0effe9804cd..33f564e9b59c 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -115,18 +115,11 @@ impl<'tcx, E: TyEncoder>> Encodable for Ty<'tcx> { } } -impl<'tcx, E: TyEncoder>> Encodable - for ty::Binder<'tcx, ty::PredicateKind<'tcx>> -{ - fn encode(&self, e: &mut E) { - self.bound_vars().encode(e); - encode_with_shorthand(e, &self.skip_binder(), TyEncoder::predicate_shorthands); - } -} - impl<'tcx, E: TyEncoder>> Encodable for ty::Predicate<'tcx> { fn encode(&self, e: &mut E) { - self.kind().encode(e); + let kind = self.kind(); + kind.bound_vars().encode(e); + encode_with_shorthand(e, &kind.skip_binder(), TyEncoder::predicate_shorthands); } } @@ -233,13 +226,11 @@ impl<'tcx, D: TyDecoder>> Decodable for Ty<'tcx> { } } -impl<'tcx, D: TyDecoder>> Decodable - for ty::Binder<'tcx, ty::PredicateKind<'tcx>> -{ - fn decode(decoder: &mut D) -> ty::Binder<'tcx, ty::PredicateKind<'tcx>> { +impl<'tcx, D: TyDecoder>> Decodable for ty::Predicate<'tcx> { + fn decode(decoder: &mut D) -> ty::Predicate<'tcx> { let bound_vars = Decodable::decode(decoder); // Handle shorthands first, if we have a usize > 0x80. - ty::Binder::bind_with_vars( + let predicate_kind = ty::Binder::bind_with_vars( if decoder.positioned_at_shorthand() { let pos = decoder.read_usize(); assert!(pos >= SHORTHAND_OFFSET); @@ -250,13 +241,7 @@ impl<'tcx, D: TyDecoder>> Decodable as Decodable>::decode(decoder) }, bound_vars, - ) - } -} - -impl<'tcx, D: TyDecoder>> Decodable for ty::Predicate<'tcx> { - fn decode(decoder: &mut D) -> ty::Predicate<'tcx> { - let predicate_kind = Decodable::decode(decoder); + ); decoder.interner().mk_predicate(predicate_kind) } } @@ -365,8 +350,8 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> impl<'tcx, D: TyDecoder>> Decodable for ty::Const<'tcx> { fn decode(decoder: &mut D) -> Self { - let consts: ty::ConstData<'tcx> = Decodable::decode(decoder); - decoder.interner().mk_ct_from_kind(consts.kind, consts.ty) + let kind: ty::ConstKind<'tcx> = Decodable::decode(decoder); + decoder.interner().mk_ct_from_kind(kind) } } @@ -599,32 +584,3 @@ macro_rules! implement_ty_decoder { } } } - -macro_rules! impl_binder_encode_decode { - ($($t:ty),+ $(,)?) => { - $( - impl<'tcx, E: TyEncoder>> Encodable for ty::Binder<'tcx, $t> { - fn encode(&self, e: &mut E) { - self.bound_vars().encode(e); - self.as_ref().skip_binder().encode(e); - } - } - impl<'tcx, D: TyDecoder>> Decodable for ty::Binder<'tcx, $t> { - fn decode(decoder: &mut D) -> Self { - let bound_vars = Decodable::decode(decoder); - ty::Binder::bind_with_vars(Decodable::decode(decoder), bound_vars) - } - } - )* - } -} - -impl_binder_encode_decode! { - &'tcx ty::List>, - ty::FnSig<'tcx>, - ty::Predicate<'tcx>, - ty::TraitPredicate<'tcx>, - ty::ExistentialPredicate<'tcx>, - ty::TraitRef<'tcx>, - ty::ExistentialTraitRef<'tcx>, -} diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 9dc30447f0e5..32d01d07c17e 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -6,8 +6,9 @@ use rustc_error_messages::MultiSpan; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::LocalDefId; -use rustc_macros::{HashStable, TyDecodable, TyEncodable}; +use rustc_macros::HashStable; use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo}; +use tracing::{debug, instrument}; mod int; mod kind; @@ -15,8 +16,8 @@ mod valtree; pub use int::*; pub use kind::*; -use rustc_span::Span; use rustc_span::DUMMY_SP; +use rustc_span::{ErrorGuaranteed, Span}; pub use valtree::*; pub type ConstKind<'tcx> = ir::ConstKind>; @@ -25,10 +26,9 @@ pub type UnevaluatedConst<'tcx> = ir::UnevaluatedConst>; #[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(ConstKind<'_>, 32); -/// Use this rather than `ConstData`, whenever possible. #[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable)] #[rustc_pass_by_value] -pub struct Const<'tcx>(pub(super) Interned<'tcx, WithCachedTypeInfo>>); +pub struct Const<'tcx>(pub(super) Interned<'tcx, WithCachedTypeInfo>>); impl<'tcx> rustc_type_ir::inherent::IntoKind for Const<'tcx> { type Kind = ConstKind<'tcx>; @@ -48,26 +48,11 @@ impl<'tcx> rustc_type_ir::visit::Flags for Const<'tcx> { } } -/// Typed constant value. -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -#[derive(HashStable, TyEncodable, TyDecodable)] -pub struct ConstData<'tcx> { - pub ty: Ty<'tcx>, - pub kind: ConstKind<'tcx>, -} - -#[cfg(target_pointer_width = "64")] -rustc_data_structures::static_assert_size!(ConstData<'_>, 40); - impl<'tcx> Const<'tcx> { - #[inline] - pub fn ty(self) -> Ty<'tcx> { - self.0.ty - } - #[inline] pub fn kind(self) -> ConstKind<'tcx> { - self.0.kind + let a: &ConstKind<'tcx> = self.0.0; + *a } // FIXME(compiler-errors): Think about removing this. @@ -83,28 +68,28 @@ impl<'tcx> Const<'tcx> { } #[inline] - pub fn new(tcx: TyCtxt<'tcx>, kind: ty::ConstKind<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> { - tcx.mk_ct_from_kind(kind, ty) + pub fn new(tcx: TyCtxt<'tcx>, kind: ty::ConstKind<'tcx>) -> Const<'tcx> { + tcx.mk_ct_from_kind(kind) } #[inline] - pub fn new_param(tcx: TyCtxt<'tcx>, param: ty::ParamConst, ty: Ty<'tcx>) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Param(param), ty) + pub fn new_param(tcx: TyCtxt<'tcx>, param: ty::ParamConst) -> Const<'tcx> { + Const::new(tcx, ty::ConstKind::Param(param)) } #[inline] - pub fn new_var(tcx: TyCtxt<'tcx>, infer: ty::ConstVid, ty: Ty<'tcx>) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Infer(ty::InferConst::Var(infer)), ty) + pub fn new_var(tcx: TyCtxt<'tcx>, infer: ty::ConstVid) -> Const<'tcx> { + Const::new(tcx, ty::ConstKind::Infer(ty::InferConst::Var(infer))) } #[inline] - pub fn new_fresh(tcx: TyCtxt<'tcx>, fresh: u32, ty: Ty<'tcx>) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Infer(ty::InferConst::Fresh(fresh)), ty) + pub fn new_fresh(tcx: TyCtxt<'tcx>, fresh: u32) -> Const<'tcx> { + Const::new(tcx, ty::ConstKind::Infer(ty::InferConst::Fresh(fresh))) } #[inline] - pub fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferConst, ty: Ty<'tcx>) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Infer(infer), ty) + pub fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferConst) -> Const<'tcx> { + Const::new(tcx, ty::ConstKind::Infer(infer)) } #[inline] @@ -112,50 +97,40 @@ impl<'tcx> Const<'tcx> { tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar, - ty: Ty<'tcx>, ) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Bound(debruijn, var), ty) + Const::new(tcx, ty::ConstKind::Bound(debruijn, var)) } #[inline] - pub fn new_placeholder( - tcx: TyCtxt<'tcx>, - placeholder: ty::PlaceholderConst, - ty: Ty<'tcx>, - ) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Placeholder(placeholder), ty) + pub fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderConst) -> Const<'tcx> { + Const::new(tcx, ty::ConstKind::Placeholder(placeholder)) } #[inline] - pub fn new_unevaluated( - tcx: TyCtxt<'tcx>, - uv: ty::UnevaluatedConst<'tcx>, - ty: Ty<'tcx>, - ) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Unevaluated(uv), ty) + pub fn new_unevaluated(tcx: TyCtxt<'tcx>, uv: ty::UnevaluatedConst<'tcx>) -> Const<'tcx> { + Const::new(tcx, ty::ConstKind::Unevaluated(uv)) } #[inline] pub fn new_value(tcx: TyCtxt<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Value(val), ty) + Const::new(tcx, ty::ConstKind::Value(ty, val)) } #[inline] - pub fn new_expr(tcx: TyCtxt<'tcx>, expr: ty::Expr<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Expr(expr), ty) + pub fn new_expr(tcx: TyCtxt<'tcx>, expr: ty::Expr<'tcx>) -> Const<'tcx> { + Const::new(tcx, ty::ConstKind::Expr(expr)) } #[inline] - pub fn new_error(tcx: TyCtxt<'tcx>, e: ty::ErrorGuaranteed, ty: Ty<'tcx>) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Error(e), ty) + pub fn new_error(tcx: TyCtxt<'tcx>, e: ty::ErrorGuaranteed) -> Const<'tcx> { + Const::new(tcx, ty::ConstKind::Error(e)) } /// Like [Ty::new_error] but for constants. #[track_caller] - pub fn new_misc_error(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> { + pub fn new_misc_error(tcx: TyCtxt<'tcx>) -> Const<'tcx> { Const::new_error_with_message( tcx, - ty, DUMMY_SP, "ty::ConstKind::Error constructed but no error reported", ) @@ -165,35 +140,45 @@ impl<'tcx> Const<'tcx> { #[track_caller] pub fn new_error_with_message>( tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, span: S, msg: &'static str, ) -> Const<'tcx> { let reported = tcx.dcx().span_delayed_bug(span, msg); - Const::new_error(tcx, reported, ty) + Const::new_error(tcx, reported) } } impl<'tcx> rustc_type_ir::inherent::Const> for Const<'tcx> { - fn new_anon_bound( - tcx: TyCtxt<'tcx>, - debruijn: ty::DebruijnIndex, - var: ty::BoundVar, - ty: Ty<'tcx>, - ) -> Self { - Const::new_bound(tcx, debruijn, var, ty) + fn try_to_target_usize(self, interner: TyCtxt<'tcx>) -> Option { + self.try_to_target_usize(interner) } - fn new_unevaluated( - interner: TyCtxt<'tcx>, - uv: ty::UnevaluatedConst<'tcx>, - ty: Ty<'tcx>, - ) -> Self { - Const::new_unevaluated(interner, uv, ty) + fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferConst) -> Self { + Const::new_infer(tcx, infer) } - fn ty(self) -> Ty<'tcx> { - self.ty() + fn new_var(tcx: TyCtxt<'tcx>, vid: ty::ConstVid) -> Self { + Const::new_var(tcx, vid) + } + + fn new_bound(interner: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self { + Const::new_bound(interner, debruijn, var) + } + + fn new_anon_bound(tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self { + Const::new_bound(tcx, debruijn, var) + } + + fn new_unevaluated(interner: TyCtxt<'tcx>, uv: ty::UnevaluatedConst<'tcx>) -> Self { + Const::new_unevaluated(interner, uv) + } + + fn new_expr(interner: TyCtxt<'tcx>, expr: ty::Expr<'tcx>) -> Self { + Const::new_expr(interner, expr) + } + + fn new_error(interner: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self { + Const::new_error(interner, guar) } } @@ -223,7 +208,6 @@ impl<'tcx> Const<'tcx> { def: def.to_def_id(), args: GenericArgs::identity_for_item(tcx, def.to_def_id()), }, - ty, ), } } @@ -275,9 +259,6 @@ impl<'tcx> Const<'tcx> { _, &hir::Path { res: Res::Def(DefKind::ConstParam, def_id), .. }, )) => { - // Use the type from the param's definition, since we can resolve it, - // not the expected parameter type from WithOptConstParam. - let param_ty = tcx.type_of(def_id).instantiate_identity(); match tcx.named_bound_var(expr.hir_id) { Some(rbv::ResolvedArg::EarlyBound(_)) => { // Find the name and index of the const parameter by indexing the generics of @@ -286,19 +267,12 @@ impl<'tcx> Const<'tcx> { let generics = tcx.generics_of(item_def_id); let index = generics.param_def_id_to_index[&def_id]; let name = tcx.item_name(def_id); - Some(ty::Const::new_param(tcx, ty::ParamConst::new(index, name), param_ty)) + Some(ty::Const::new_param(tcx, ty::ParamConst::new(index, name))) } Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => { - Some(ty::Const::new_bound( - tcx, - debruijn, - ty::BoundVar::from_u32(index), - param_ty, - )) - } - Some(rbv::ResolvedArg::Error(guar)) => { - Some(ty::Const::new_error(tcx, guar, param_ty)) + Some(ty::Const::new_bound(tcx, debruijn, ty::BoundVar::from_u32(index))) } + Some(rbv::ResolvedArg::Error(guar)) => Some(ty::Const::new_error(tcx, guar)), arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", expr.hir_id), } } @@ -345,7 +319,7 @@ impl<'tcx> Const<'tcx> { tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, span: Span, - ) -> Result, ErrorHandled> { + ) -> Result<(Ty<'tcx>, ValTree<'tcx>), ErrorHandled> { assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}"); match self.kind() { ConstKind::Unevaluated(unevaluated) => { @@ -363,9 +337,9 @@ impl<'tcx> Const<'tcx> { ); return Err(e.into()); }; - Ok(c) + Ok((tcx.type_of(unevaluated.def).instantiate(tcx, unevaluated.args), c)) } - ConstKind::Value(val) => Ok(val), + ConstKind::Value(ty, val) => Ok((ty, val)), ConstKind::Error(g) => Err(g.into()), ConstKind::Param(_) | ConstKind::Infer(_) @@ -379,8 +353,8 @@ impl<'tcx> Const<'tcx> { #[inline] pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { match self.eval(tcx, param_env, DUMMY_SP) { - Ok(val) => Self::new_value(tcx, val, self.ty()), - Err(ErrorHandled::Reported(r, _span)) => Self::new_error(tcx, r.into(), self.ty()), + Ok((ty, val)) => Self::new_value(tcx, val, ty), + Err(ErrorHandled::Reported(r, _span)) => Self::new_error(tcx, r.into()), Err(ErrorHandled::TooGeneric(_span)) => self, } } @@ -390,8 +364,10 @@ impl<'tcx> Const<'tcx> { self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - ) -> Option { - self.eval(tcx, param_env, DUMMY_SP).ok()?.try_to_scalar() + ) -> Option<(Ty<'tcx>, Scalar)> { + let (ty, val) = self.eval(tcx, param_env, DUMMY_SP).ok()?; + let val = val.try_to_scalar()?; + Some((ty, val)) } #[inline] @@ -402,8 +378,10 @@ impl<'tcx> Const<'tcx> { self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, - ) -> Option { - self.try_eval_scalar(tcx, param_env)?.try_to_int().ok() + ) -> Option<(Ty<'tcx>, ScalarInt)> { + let (ty, scalar) = self.try_eval_scalar(tcx, param_env)?; + let val = scalar.try_to_scalar_int().ok()?; + Some((ty, val)) } #[inline] @@ -411,18 +389,17 @@ impl<'tcx> Const<'tcx> { /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it /// contains const generic parameters or pointers). pub fn try_eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { - let int = self.try_eval_scalar_int(tcx, param_env)?; - let size = - tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(self.ty())).ok()?.size; + let (ty, scalar) = self.try_eval_scalar_int(tcx, param_env)?; + let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size; // if `ty` does not depend on generic parameters, use an empty param_env - int.try_to_bits(size).ok() + Some(scalar.to_bits(size)) } #[inline] /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type. pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u128 { self.try_eval_bits(tcx, param_env) - .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", self.ty(), self)) + .unwrap_or_else(|| bug!("failed to evalate {:#?} to bits", self)) } #[inline] @@ -431,12 +408,14 @@ impl<'tcx> Const<'tcx> { tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ) -> Option { - self.try_eval_scalar_int(tcx, param_env)?.try_to_target_usize(tcx).ok() + let (_, scalar) = self.try_eval_scalar_int(tcx, param_env)?; + Some(scalar.to_target_usize(tcx)) } #[inline] pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option { - self.try_eval_scalar_int(tcx, param_env)?.try_into().ok() + let (_, scalar) = self.try_eval_scalar_int(tcx, param_env)?; + scalar.try_into().ok() } #[inline] @@ -449,7 +428,7 @@ impl<'tcx> Const<'tcx> { /// Panics if self.kind != ty::ConstKind::Value pub fn to_valtree(self) -> ty::ValTree<'tcx> { match self.kind() { - ty::ConstKind::Value(valtree) => valtree, + ty::ConstKind::Value(_, valtree) => valtree, _ => bug!("expected ConstKind::Value, got {:?}", self.kind()), } } @@ -457,7 +436,7 @@ impl<'tcx> Const<'tcx> { /// Attempts to convert to a `ValTree` pub fn try_to_valtree(self) -> Option> { match self.kind() { - ty::ConstKind::Value(valtree) => Some(valtree), + ty::ConstKind::Value(_, valtree) => Some(valtree), _ => None, } } @@ -477,7 +456,10 @@ impl<'tcx> Const<'tcx> { } } -pub fn const_param_default(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder> { +pub fn const_param_default<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> ty::EarlyBinder<'tcx, Const<'tcx>> { let default_def_id = match tcx.hir_node_by_def_id(def_id) { hir::Node::GenericParam(hir::GenericParam { kind: hir::GenericParamKind::Const { default: Some(ac), .. }, diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index 40ac87873a0a..13691b61941b 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -209,8 +209,8 @@ impl ScalarInt { #[inline] pub fn try_from_uint(i: impl Into, size: Size) -> Option { - let data = i.into(); - if size.truncate(data) == data { Some(Self::raw(data, size)) } else { None } + let (r, overflow) = Self::truncate_from_uint(i, size); + if overflow { None } else { Some(r) } } /// Returns the truncated result, and whether truncation changed the value. @@ -223,20 +223,15 @@ impl ScalarInt { #[inline] pub fn try_from_int(i: impl Into, size: Size) -> Option { - let i = i.into(); - // `into` performed sign extension, we have to truncate - let truncated = size.truncate(i as u128); - if size.sign_extend(truncated) as i128 == i { - Some(Self::raw(truncated, size)) - } else { - None - } + let (r, overflow) = Self::truncate_from_int(i, size); + if overflow { None } else { Some(r) } } /// Returns the truncated result, and whether truncation changed the value. #[inline] pub fn truncate_from_int(i: impl Into, size: Size) -> (Self, bool) { let data = i.into(); + // `into` performed sign extension, we have to truncate let r = Self::raw(size.truncate(data as u128), size); (r, size.sign_extend(r.data) as i128 != data) } @@ -246,6 +241,10 @@ impl ScalarInt { Self::try_from_uint(i, tcx.data_layout.pointer_size) } + /// Try to convert this ScalarInt to the raw underlying bits. + /// Fails if the size is wrong. Generally a wrong size should lead to a panic, + /// but Miri sometimes wants to be resilient to size mismatches, + /// so the interpreter will generally use this `try` method. #[inline] pub fn try_to_bits(self, target_size: Size) -> Result { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); @@ -258,165 +257,149 @@ impl ScalarInt { } #[inline] - pub fn assert_bits(self, target_size: Size) -> u128 { + pub fn to_bits(self, target_size: Size) -> u128 { self.try_to_bits(target_size).unwrap_or_else(|size| { bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes()) }) } - /// Tries to convert the `ScalarInt` to an unsigned integer of the given size. - /// Fails if the size of the `ScalarInt` is not equal to `size` and returns the - /// `ScalarInt`s size in that case. + /// Extracts the bits from the scalar without checking the size. #[inline] - pub fn try_to_uint(self, size: Size) -> Result { - self.try_to_bits(size) + pub fn to_bits_unchecked(self) -> u128 { + self.check_data(); + self.data + } + + /// Converts the `ScalarInt` to an unsigned integer of the given size. + /// Panics if the size of the `ScalarInt` is not equal to `size`. + #[inline] + pub fn to_uint(self, size: Size) -> u128 { + self.to_bits(size) + } + + /// Converts the `ScalarInt` to `u8`. + /// Panics if the `size` of the `ScalarInt`in not equal to 1 byte. + #[inline] + pub fn to_u8(self) -> u8 { + self.to_uint(Size::from_bits(8)).try_into().unwrap() + } + + /// Converts the `ScalarInt` to `u16`. + /// Panics if the size of the `ScalarInt` in not equal to 2 bytes. + #[inline] + pub fn to_u16(self) -> u16 { + self.to_uint(Size::from_bits(16)).try_into().unwrap() + } + + /// Converts the `ScalarInt` to `u32`. + /// Panics if the `size` of the `ScalarInt` in not equal to 4 bytes. + #[inline] + pub fn to_u32(self) -> u32 { + self.to_uint(Size::from_bits(32)).try_into().unwrap() + } + + /// Converts the `ScalarInt` to `u64`. + /// Panics if the `size` of the `ScalarInt` in not equal to 8 bytes. + #[inline] + pub fn to_u64(self) -> u64 { + self.to_uint(Size::from_bits(64)).try_into().unwrap() + } + + /// Converts the `ScalarInt` to `u128`. + /// Panics if the `size` of the `ScalarInt` in not equal to 16 bytes. + #[inline] + pub fn to_u128(self) -> u128 { + self.to_uint(Size::from_bits(128)) } #[inline] - pub fn assert_uint(self, size: Size) -> u128 { - self.assert_bits(size) + pub fn to_target_usize(&self, tcx: TyCtxt<'_>) -> u64 { + self.to_uint(tcx.data_layout.pointer_size).try_into().unwrap() } - // Tries to convert the `ScalarInt` to `u8`. Fails if the `size` of the `ScalarInt` - // in not equal to 1 byte and returns the `size` value of the `ScalarInt` in - // that case. + /// Converts the `ScalarInt` to `bool`. + /// Panics if the `size` of the `ScalarInt` is not equal to 1 byte. + /// Errors if it is not a valid `bool`. #[inline] - pub fn try_to_u8(self) -> Result { - self.try_to_uint(Size::from_bits(8)).map(|v| u8::try_from(v).unwrap()) - } - - /// Tries to convert the `ScalarInt` to `u16`. Fails if the size of the `ScalarInt` - /// in not equal to 2 bytes and returns the `size` value of the `ScalarInt` in - /// that case. - #[inline] - pub fn try_to_u16(self) -> Result { - self.try_to_uint(Size::from_bits(16)).map(|v| u16::try_from(v).unwrap()) - } - - /// Tries to convert the `ScalarInt` to `u32`. Fails if the `size` of the `ScalarInt` - /// in not equal to 4 bytes and returns the `size` value of the `ScalarInt` in - /// that case. - #[inline] - pub fn try_to_u32(self) -> Result { - self.try_to_uint(Size::from_bits(32)).map(|v| u32::try_from(v).unwrap()) - } - - /// Tries to convert the `ScalarInt` to `u64`. Fails if the `size` of the `ScalarInt` - /// in not equal to 8 bytes and returns the `size` value of the `ScalarInt` in - /// that case. - #[inline] - pub fn try_to_u64(self) -> Result { - self.try_to_uint(Size::from_bits(64)).map(|v| u64::try_from(v).unwrap()) - } - - /// Tries to convert the `ScalarInt` to `u128`. Fails if the `size` of the `ScalarInt` - /// in not equal to 16 bytes and returns the `size` value of the `ScalarInt` in - /// that case. - #[inline] - pub fn try_to_u128(self) -> Result { - self.try_to_uint(Size::from_bits(128)) - } - - #[inline] - pub fn try_to_target_usize(&self, tcx: TyCtxt<'_>) -> Result { - self.try_to_uint(tcx.data_layout.pointer_size).map(|v| u64::try_from(v).unwrap()) - } - - // Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt` - // in not equal to 1 byte or if the value is not 0 or 1 and returns the `size` - // value of the `ScalarInt` in that case. - #[inline] - pub fn try_to_bool(self) -> Result { - match self.try_to_u8()? { + pub fn try_to_bool(self) -> Result { + match self.to_u8() { 0 => Ok(false), 1 => Ok(true), - _ => Err(self.size()), + _ => Err(()), } } - /// Tries to convert the `ScalarInt` to a signed integer of the given size. - /// Fails if the size of the `ScalarInt` is not equal to `size` and returns the - /// `ScalarInt`s size in that case. + /// Converts the `ScalarInt` to a signed integer of the given size. + /// Panics if the size of the `ScalarInt` is not equal to `size`. #[inline] - pub fn try_to_int(self, size: Size) -> Result { - let b = self.try_to_bits(size)?; - Ok(size.sign_extend(b) as i128) - } - - #[inline] - pub fn assert_int(self, size: Size) -> i128 { - let b = self.assert_bits(size); + pub fn to_int(self, size: Size) -> i128 { + let b = self.to_bits(size); size.sign_extend(b) as i128 } - /// Tries to convert the `ScalarInt` to i8. - /// Fails if the size of the `ScalarInt` is not equal to 1 byte - /// and returns the `ScalarInt`s size in that case. - pub fn try_to_i8(self) -> Result { - self.try_to_int(Size::from_bits(8)).map(|v| i8::try_from(v).unwrap()) + /// Converts the `ScalarInt` to i8. + /// Panics if the size of the `ScalarInt` is not equal to 1 byte. + pub fn to_i8(self) -> i8 { + self.to_int(Size::from_bits(8)).try_into().unwrap() } - /// Tries to convert the `ScalarInt` to i16. - /// Fails if the size of the `ScalarInt` is not equal to 2 bytes - /// and returns the `ScalarInt`s size in that case. - pub fn try_to_i16(self) -> Result { - self.try_to_int(Size::from_bits(16)).map(|v| i16::try_from(v).unwrap()) + /// Converts the `ScalarInt` to i16. + /// Panics if the size of the `ScalarInt` is not equal to 2 bytes. + pub fn to_i16(self) -> i16 { + self.to_int(Size::from_bits(16)).try_into().unwrap() } - /// Tries to convert the `ScalarInt` to i32. - /// Fails if the size of the `ScalarInt` is not equal to 4 bytes - /// and returns the `ScalarInt`s size in that case. - pub fn try_to_i32(self) -> Result { - self.try_to_int(Size::from_bits(32)).map(|v| i32::try_from(v).unwrap()) + /// Converts the `ScalarInt` to i32. + /// Panics if the size of the `ScalarInt` is not equal to 4 bytes. + pub fn to_i32(self) -> i32 { + self.to_int(Size::from_bits(32)).try_into().unwrap() } - /// Tries to convert the `ScalarInt` to i64. - /// Fails if the size of the `ScalarInt` is not equal to 8 bytes - /// and returns the `ScalarInt`s size in that case. - pub fn try_to_i64(self) -> Result { - self.try_to_int(Size::from_bits(64)).map(|v| i64::try_from(v).unwrap()) + /// Converts the `ScalarInt` to i64. + /// Panics if the size of the `ScalarInt` is not equal to 8 bytes. + pub fn to_i64(self) -> i64 { + self.to_int(Size::from_bits(64)).try_into().unwrap() } - /// Tries to convert the `ScalarInt` to i128. - /// Fails if the size of the `ScalarInt` is not equal to 16 bytes - /// and returns the `ScalarInt`s size in that case. - pub fn try_to_i128(self) -> Result { - self.try_to_int(Size::from_bits(128)) + /// Converts the `ScalarInt` to i128. + /// Panics if the size of the `ScalarInt` is not equal to 16 bytes. + pub fn to_i128(self) -> i128 { + self.to_int(Size::from_bits(128)) } #[inline] - pub fn try_to_target_isize(&self, tcx: TyCtxt<'_>) -> Result { - self.try_to_int(tcx.data_layout.pointer_size).map(|v| i64::try_from(v).unwrap()) + pub fn to_target_isize(&self, tcx: TyCtxt<'_>) -> i64 { + self.to_int(tcx.data_layout.pointer_size).try_into().unwrap() } #[inline] - pub fn try_to_float(self) -> Result { + pub fn to_float(self) -> F { // Going through `to_uint` to check size and truncation. - Ok(F::from_bits(self.try_to_bits(Size::from_bits(F::BITS))?)) + F::from_bits(self.to_bits(Size::from_bits(F::BITS))) } #[inline] - pub fn try_to_f16(self) -> Result { - self.try_to_float() + pub fn to_f16(self) -> Half { + self.to_float() } #[inline] - pub fn try_to_f32(self) -> Result { - self.try_to_float() + pub fn to_f32(self) -> Single { + self.to_float() } #[inline] - pub fn try_to_f64(self) -> Result { - self.try_to_float() + pub fn to_f64(self) -> Double { + self.to_float() } #[inline] - pub fn try_to_f128(self) -> Result { - self.try_to_float() + pub fn to_f128(self) -> Quad { + self.to_float() } } -macro_rules! from { +macro_rules! from_x_for_scalar_int { ($($ty:ty),*) => { $( impl From<$ty> for ScalarInt { @@ -432,30 +415,29 @@ macro_rules! from { } } -macro_rules! try_from { +macro_rules! from_scalar_int_for_x { ($($ty:ty),*) => { $( - impl TryFrom for $ty { - type Error = Size; + impl From for $ty { #[inline] - fn try_from(int: ScalarInt) -> Result { + fn from(int: ScalarInt) -> Self { // The `unwrap` cannot fail because to_bits (if it succeeds) // is guaranteed to return a value that fits into the size. - int.try_to_bits(Size::from_bytes(std::mem::size_of::<$ty>())) - .map(|u| u.try_into().unwrap()) + int.to_bits(Size::from_bytes(std::mem::size_of::<$ty>())) + .try_into().unwrap() } } )* } } -from!(u8, u16, u32, u64, u128, bool); -try_from!(u8, u16, u32, u64, u128); +from_x_for_scalar_int!(u8, u16, u32, u64, u128, bool); +from_scalar_int_for_x!(u8, u16, u32, u64, u128); impl TryFrom for bool { - type Error = Size; + type Error = (); #[inline] - fn try_from(int: ScalarInt) -> Result { + fn try_from(int: ScalarInt) -> Result { int.try_to_bool() } } @@ -463,7 +445,7 @@ impl TryFrom for bool { impl From for ScalarInt { #[inline] fn from(c: char) -> Self { - Self { data: c as u128, size: NonZero::new(std::mem::size_of::() as u8).unwrap() } + (c as u32).into() } } @@ -476,10 +458,7 @@ impl TryFrom for char { #[inline] fn try_from(int: ScalarInt) -> Result { - let Ok(bits) = int.try_to_bits(Size::from_bytes(std::mem::size_of::())) else { - return Err(CharTryFromScalarInt); - }; - match char::from_u32(bits.try_into().unwrap()) { + match char::from_u32(int.to_u32()) { Some(c) => Ok(c), None => Err(CharTryFromScalarInt), } @@ -494,11 +473,10 @@ impl From for ScalarInt { } } -impl TryFrom for Half { - type Error = Size; +impl From for Half { #[inline] - fn try_from(int: ScalarInt) -> Result { - int.try_to_bits(Size::from_bytes(2)).map(Self::from_bits) + fn from(int: ScalarInt) -> Self { + Self::from_bits(int.to_bits(Size::from_bytes(2))) } } @@ -510,11 +488,10 @@ impl From for ScalarInt { } } -impl TryFrom for Single { - type Error = Size; +impl From for Single { #[inline] - fn try_from(int: ScalarInt) -> Result { - int.try_to_bits(Size::from_bytes(4)).map(Self::from_bits) + fn from(int: ScalarInt) -> Self { + Self::from_bits(int.to_bits(Size::from_bytes(4))) } } @@ -526,11 +503,10 @@ impl From for ScalarInt { } } -impl TryFrom for Double { - type Error = Size; +impl From for Double { #[inline] - fn try_from(int: ScalarInt) -> Result { - int.try_to_bits(Size::from_bytes(8)).map(Self::from_bits) + fn from(int: ScalarInt) -> Self { + Self::from_bits(int.to_bits(Size::from_bytes(8))) } } @@ -542,11 +518,10 @@ impl From for ScalarInt { } } -impl TryFrom for Quad { - type Error = Size; +impl From for Quad { #[inline] - fn try_from(int: ScalarInt) -> Result { - int.try_to_bits(Size::from_bytes(16)).map(Self::from_bits) + fn from(int: ScalarInt) -> Self { + Self::from_bits(int.to_bits(Size::from_bytes(16))) } } diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index d7ae050ed4d0..bf834ef76074 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -1,7 +1,7 @@ use super::Const; use crate::mir; use crate::ty::abstract_const::CastKind; -use crate::ty::{self, visit::TypeVisitableExt as _, List, Ty, TyCtxt}; +use crate::ty::{self, visit::TypeVisitableExt as _, Ty, TyCtxt}; use rustc_macros::{extension, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; #[extension(pub(crate) trait UnevaluatedConstEvalExt<'tcx>)] @@ -40,14 +40,127 @@ impl<'tcx> ty::UnevaluatedConst<'tcx> { } } +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)] +pub enum ExprKind { + Binop(mir::BinOp), + UnOp(mir::UnOp), + FunctionCall, + Cast(CastKind), +} #[derive(Copy, Clone, Eq, PartialEq, Hash)] #[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)] -pub enum Expr<'tcx> { - Binop(mir::BinOp, Const<'tcx>, Const<'tcx>), - UnOp(mir::UnOp, Const<'tcx>), - FunctionCall(Const<'tcx>, &'tcx List>), - Cast(CastKind, Const<'tcx>, Ty<'tcx>), +pub struct Expr<'tcx> { + pub kind: ExprKind, + args: ty::GenericArgsRef<'tcx>, +} +impl<'tcx> Expr<'tcx> { + pub fn new_binop( + tcx: TyCtxt<'tcx>, + binop: mir::BinOp, + lhs_ty: Ty<'tcx>, + rhs_ty: Ty<'tcx>, + lhs_ct: Const<'tcx>, + rhs_ct: Const<'tcx>, + ) -> Self { + let args = tcx.mk_args_from_iter::<_, ty::GenericArg<'tcx>>( + [lhs_ty.into(), rhs_ty.into(), lhs_ct.into(), rhs_ct.into()].into_iter(), + ); + + Self { kind: ExprKind::Binop(binop), args } + } + + pub fn binop_args(self) -> (Ty<'tcx>, Ty<'tcx>, Const<'tcx>, Const<'tcx>) { + assert!(matches!(self.kind, ExprKind::Binop(_))); + + match self.args().as_slice() { + [lhs_ty, rhs_ty, lhs_ct, rhs_ct] => ( + lhs_ty.expect_ty(), + rhs_ty.expect_ty(), + lhs_ct.expect_const(), + rhs_ct.expect_const(), + ), + _ => bug!("Invalid args for `Binop` expr {self:?}"), + } + } + + pub fn new_unop(tcx: TyCtxt<'tcx>, unop: mir::UnOp, ty: Ty<'tcx>, ct: Const<'tcx>) -> Self { + let args = + tcx.mk_args_from_iter::<_, ty::GenericArg<'tcx>>([ty.into(), ct.into()].into_iter()); + + Self { kind: ExprKind::UnOp(unop), args } + } + + pub fn unop_args(self) -> (Ty<'tcx>, Const<'tcx>) { + assert!(matches!(self.kind, ExprKind::UnOp(_))); + + match self.args().as_slice() { + [ty, ct] => (ty.expect_ty(), ct.expect_const()), + _ => bug!("Invalid args for `UnOp` expr {self:?}"), + } + } + + pub fn new_call( + tcx: TyCtxt<'tcx>, + func_ty: Ty<'tcx>, + func_expr: Const<'tcx>, + arguments: impl IntoIterator>, + ) -> Self { + let args = tcx.mk_args_from_iter::<_, ty::GenericArg<'tcx>>( + [func_ty.into(), func_expr.into()] + .into_iter() + .chain(arguments.into_iter().map(|ct| ct.into())), + ); + + Self { kind: ExprKind::FunctionCall, args } + } + + pub fn call_args(self) -> (Ty<'tcx>, Const<'tcx>, impl Iterator>) { + assert!(matches!(self.kind, ExprKind::FunctionCall)); + + match self.args().as_slice() { + [func_ty, func, rest @ ..] => ( + func_ty.expect_ty(), + func.expect_const(), + rest.iter().map(|arg| arg.expect_const()), + ), + _ => bug!("Invalid args for `Call` expr {self:?}"), + } + } + + pub fn new_cast( + tcx: TyCtxt<'tcx>, + cast: CastKind, + value_ty: Ty<'tcx>, + value: Const<'tcx>, + to_ty: Ty<'tcx>, + ) -> Self { + let args = tcx.mk_args_from_iter::<_, ty::GenericArg<'tcx>>( + [value_ty.into(), value.into(), to_ty.into()].into_iter(), + ); + + Self { kind: ExprKind::Cast(cast), args } + } + + pub fn cast_args(self) -> (Ty<'tcx>, Const<'tcx>, Ty<'tcx>) { + assert!(matches!(self.kind, ExprKind::Cast(_))); + + match self.args().as_slice() { + [value_ty, value, to_ty] => { + (value_ty.expect_ty(), value.expect_const(), to_ty.expect_ty()) + } + _ => bug!("Invalid args for `Cast` expr {self:?}"), + } + } + + pub fn new(kind: ExprKind, args: ty::GenericArgsRef<'tcx>) -> Self { + Self { kind, args } + } + + pub fn args(self) -> ty::GenericArgsRef<'tcx> { + self.args + } } #[cfg(target_pointer_width = "64")] -rustc_data_structures::static_assert_size!(Expr<'_>, 24); +rustc_data_structures::static_assert_size!(Expr<'_>, 16); diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index 96bc5515a569..efc91357af8c 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -79,7 +79,7 @@ impl<'tcx> ValTree<'tcx> { } pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option { - self.try_to_scalar_int().and_then(|s| s.try_to_target_usize(tcx).ok()) + self.try_to_scalar_int().map(|s| s.to_target_usize(tcx)) } /// Get the values inside the ValTree as a slice of bytes. This only works for @@ -100,8 +100,9 @@ impl<'tcx> ValTree<'tcx> { _ => return None, } - Some(tcx.arena.alloc_from_iter( - self.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().try_to_u8().unwrap()), - )) + Some( + tcx.arena + .alloc_from_iter(self.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().to_u8())), + ) } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 69681930be65..6d64c1d50aea 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -28,11 +28,10 @@ use crate::traits::solve::{ }; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ - self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, ConstData, - GenericParamDefKind, ImplPolarity, List, ListWithCachedTypeInfo, ParamConst, ParamTy, Pattern, - PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, PredicatePolarity, - Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, TypeVisitable, - Visibility, + self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, GenericParamDefKind, + ImplPolarity, List, ListWithCachedTypeInfo, ParamConst, ParamTy, Pattern, PatternKind, + PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, PredicatePolarity, Region, + RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, Visibility, }; use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use rustc_ast::{self as ast, attr}; @@ -49,7 +48,7 @@ use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, Lrc, RwLock, Work use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_data_structures::unord::UnordSet; use rustc_errors::{ - Applicability, Diag, DiagCtxt, DiagMessage, ErrorGuaranteed, LintDiagnostic, MultiSpan, + Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan, }; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -72,9 +71,12 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx}; use rustc_target::spec::abi; +use rustc_type_ir::fold::TypeFoldable; +use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::solve::SolverMode; use rustc_type_ir::TyKind::*; -use rustc_type_ir::WithCachedTypeInfo; -use rustc_type_ir::{CollectAndApply, Interner, TypeFlags}; +use rustc_type_ir::{CollectAndApply, Interner, TypeFlags, WithCachedTypeInfo}; +use tracing::{debug, instrument}; use std::assert_matches::assert_matches; use std::borrow::Borrow; @@ -89,70 +91,88 @@ use std::ops::{Bound, Deref}; #[allow(rustc::usage_of_ty_tykind)] impl<'tcx> Interner for TyCtxt<'tcx> { type DefId = DefId; - type AdtDef = ty::AdtDef<'tcx>; - + type LocalDefId = LocalDefId; type GenericArgs = ty::GenericArgsRef<'tcx>; - type OwnItemArgs = &'tcx [ty::GenericArg<'tcx>]; + + type GenericArgsSlice = &'tcx [ty::GenericArg<'tcx>]; type GenericArg = ty::GenericArg<'tcx>; type Term = ty::Term<'tcx>; + type BoundVarKinds = &'tcx List; - type Binder>> = Binder<'tcx, T>; - type BoundVars = &'tcx List; - type BoundVar = ty::BoundVariableKind; - - type CanonicalVars = CanonicalVarInfos<'tcx>; + type BoundVarKind = ty::BoundVariableKind; type PredefinedOpaques = solve::PredefinedOpaques<'tcx>; - type DefiningOpaqueTypes = &'tcx ty::List; - type ExternalConstraints = ExternalConstraints<'tcx>; - type GoalEvaluationSteps = &'tcx [solve::inspect::GoalEvaluationStep>]; + fn mk_predefined_opaques_in_body( + self, + data: PredefinedOpaquesData, + ) -> Self::PredefinedOpaques { + self.mk_predefined_opaques_in_body(data) + } + type DefiningOpaqueTypes = &'tcx ty::List; + type CanonicalGoalEvaluationStepRef = + &'tcx solve::inspect::CanonicalGoalEvaluationStep>; + type CanonicalVars = CanonicalVarInfos<'tcx>; + fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo]) -> Self::CanonicalVars { + self.mk_canonical_var_infos(infos) + } + + type ExternalConstraints = ExternalConstraints<'tcx>; + fn mk_external_constraints( + self, + data: ExternalConstraintsData, + ) -> ExternalConstraints<'tcx> { + self.mk_external_constraints(data) + } + type DepNodeIndex = DepNodeIndex; + fn with_cached_task(self, task: impl FnOnce() -> T) -> (T, DepNodeIndex) { + self.dep_graph.with_anon_task(self, crate::dep_graph::dep_kinds::TraitSelect, task) + } type Ty = Ty<'tcx>; type Tys = &'tcx List>; + type FnInputTys = &'tcx [Ty<'tcx>]; type ParamTy = ParamTy; type BoundTy = ty::BoundTy; - type PlaceholderTy = ty::PlaceholderType; + type PlaceholderTy = ty::PlaceholderType; type ErrorGuaranteed = ErrorGuaranteed; type BoundExistentialPredicates = &'tcx List>; - type PolyFnSig = PolyFnSig<'tcx>; - type AllocId = crate::mir::interpret::AllocId; + type AllocId = crate::mir::interpret::AllocId; type Pat = Pattern<'tcx>; type Safety = hir::Safety; type Abi = abi::Abi; - type Const = ty::Const<'tcx>; - type AliasConst = ty::UnevaluatedConst<'tcx>; type PlaceholderConst = ty::PlaceholderConst; + type ParamConst = ty::ParamConst; type BoundConst = ty::BoundVar; type ValueConst = ty::ValTree<'tcx>; type ExprConst = ty::Expr<'tcx>; - type Region = Region<'tcx>; + type EarlyParamRegion = ty::EarlyParamRegion; type LateParamRegion = ty::LateParamRegion; type BoundRegion = ty::BoundRegion; - type InferRegion = ty::RegionVid; type PlaceholderRegion = ty::PlaceholderRegion; type ParamEnv = ty::ParamEnv<'tcx>; type Predicate = Predicate<'tcx>; - type TraitPredicate = ty::TraitPredicate<'tcx>; - type RegionOutlivesPredicate = ty::RegionOutlivesPredicate<'tcx>; - type TypeOutlivesPredicate = ty::TypeOutlivesPredicate<'tcx>; - type ProjectionPredicate = ty::ProjectionPredicate<'tcx>; - type NormalizesTo = ty::NormalizesTo<'tcx>; - type SubtypePredicate = ty::SubtypePredicate<'tcx>; - - type CoercePredicate = ty::CoercePredicate<'tcx>; - type ClosureKind = ty::ClosureKind; + type Clause = Clause<'tcx>; type Clauses = ty::Clauses<'tcx>; - fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo]) -> Self::CanonicalVars { - self.mk_canonical_var_infos(infos) + type EvaluationCache = &'tcx solve::EvaluationCache<'tcx>; + + fn evaluation_cache(self, mode: SolverMode) -> &'tcx solve::EvaluationCache<'tcx> { + match mode { + SolverMode::Normal => &self.new_solver_evaluation_cache, + SolverMode::Coherence => &self.new_solver_coherence_evaluation_cache, + } + } + + fn expand_abstract_consts>>(self, t: T) -> T { + self.expand_abstract_consts(t) } type GenericsOf = &'tcx ty::Generics; @@ -161,8 +181,19 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.generics_of(def_id) } - fn type_of_instantiated(self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> Ty<'tcx> { - self.type_of(def_id).instantiate(self, args) + type VariancesOf = &'tcx [ty::Variance]; + + fn variances_of(self, def_id: DefId) -> Self::VariancesOf { + self.variances_of(def_id) + } + + fn type_of(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { + self.type_of(def_id) + } + + type AdtDef = ty::AdtDef<'tcx>; + fn adt_def(self, adt_def_id: DefId) -> Self::AdtDef { + self.adt_def(adt_def_id) } fn alias_ty_kind(self, alias: ty::AliasTy<'tcx>) -> ty::AliasTyKind { @@ -201,9 +232,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> { fn trait_ref_and_own_args_for_alias( self, - def_id: Self::DefId, - args: Self::GenericArgs, - ) -> (rustc_type_ir::TraitRef, Self::OwnItemArgs) { + def_id: DefId, + args: ty::GenericArgsRef<'tcx>, + ) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) { assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst); let trait_def_id = self.parent(def_id); assert_matches!(self.def_kind(trait_def_id), DefKind::Trait); @@ -214,14 +245,22 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) } - fn mk_args(self, args: &[Self::GenericArg]) -> Self::GenericArgs { + fn mk_args(self, args: &[Self::GenericArg]) -> ty::GenericArgsRef<'tcx> { self.mk_args(args) } - fn mk_args_from_iter(self, args: impl Iterator) -> Self::GenericArgs { + fn mk_args_from_iter(self, args: I) -> T::Output + where + I: Iterator, + T: CollectAndApply>, + { self.mk_args_from_iter(args) } + fn check_args_compatible(self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> bool { + self.check_args_compatible(def_id, args) + } + fn check_and_mk_args( self, def_id: DefId, @@ -230,23 +269,401 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.check_and_mk_args(def_id, args) } - fn parent(self, def_id: Self::DefId) -> Self::DefId { + fn intern_canonical_goal_evaluation_step( + self, + step: solve::inspect::CanonicalGoalEvaluationStep>, + ) -> &'tcx solve::inspect::CanonicalGoalEvaluationStep> { + self.arena.alloc(step) + } + + fn mk_type_list_from_iter(self, args: I) -> T::Output + where + I: Iterator, + T: CollectAndApply, &'tcx List>>, + { + self.mk_type_list_from_iter(args) + } + + fn parent(self, def_id: DefId) -> DefId { self.parent(def_id) } + + fn recursion_limit(self) -> usize { + self.recursion_limit().0 + } + + type Features = &'tcx rustc_feature::Features; + + fn features(self) -> Self::Features { + self.features() + } + + fn bound_coroutine_hidden_types( + self, + def_id: DefId, + ) -> impl IntoIterator>>> { + self.bound_coroutine_hidden_types(def_id) + } + + fn fn_sig(self, def_id: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> { + self.fn_sig(def_id) + } + + fn coroutine_movability(self, def_id: DefId) -> rustc_ast::Movability { + self.coroutine_movability(def_id) + } + + fn coroutine_for_closure(self, def_id: DefId) -> DefId { + self.coroutine_for_closure(def_id) + } + + fn generics_require_sized_self(self, def_id: DefId) -> bool { + self.generics_require_sized_self(def_id) + } + + fn item_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + self.item_bounds(def_id).map_bound(IntoIterator::into_iter) + } + + fn predicates_of( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + ty::EarlyBinder::bind( + self.predicates_of(def_id).instantiate_identity(self).predicates.into_iter(), + ) + } + + fn own_predicates_of( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + ty::EarlyBinder::bind( + self.predicates_of(def_id).instantiate_own_identity().map(|(clause, _)| clause), + ) + } + + fn super_predicates_of( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + ty::EarlyBinder::bind( + self.super_predicates_of(def_id).instantiate_identity(self).predicates.into_iter(), + ) + } + + fn has_target_features(self, def_id: DefId) -> bool { + !self.codegen_fn_attrs(def_id).target_features.is_empty() + } + + fn require_lang_item(self, lang_item: TraitSolverLangItem) -> DefId { + self.require_lang_item(trait_lang_item_to_lang_item(lang_item), None) + } + + fn is_lang_item(self, def_id: DefId, lang_item: TraitSolverLangItem) -> bool { + self.is_lang_item(def_id, trait_lang_item_to_lang_item(lang_item)) + } + + fn associated_type_def_ids(self, def_id: DefId) -> impl IntoIterator { + self.associated_items(def_id) + .in_definition_order() + .filter(|assoc_item| matches!(assoc_item.kind, ty::AssocKind::Type)) + .map(|assoc_item| assoc_item.def_id) + } + + fn args_may_unify_deep( + self, + obligation_args: ty::GenericArgsRef<'tcx>, + impl_args: ty::GenericArgsRef<'tcx>, + ) -> bool { + ty::fast_reject::DeepRejectCtxt { + treat_obligation_params: ty::fast_reject::TreatParams::ForLookup, + } + .args_may_unify(obligation_args, impl_args) + } + + // This implementation is a bit different from `TyCtxt::for_each_relevant_impl`, + // since we want to skip over blanket impls for non-rigid aliases, and also we + // only want to consider types that *actually* unify with float/int vars. + fn for_each_relevant_impl( + self, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + mut f: impl FnMut(DefId), + ) { + let tcx = self; + let trait_impls = tcx.trait_impls_of(trait_def_id); + let mut consider_impls_for_simplified_type = |simp| { + if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) { + for &impl_def_id in impls_for_type { + f(impl_def_id); + } + } + }; + + match self_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Pat(_, _) + | ty::Slice(_) + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(_, _, _) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(_, _) + | ty::Never + | ty::Tuple(_) => { + let simp = ty::fast_reject::simplify_type( + tcx, + self_ty, + ty::fast_reject::TreatParams::ForLookup, + ) + .unwrap(); + consider_impls_for_simplified_type(simp); + } + + // HACK: For integer and float variables we have to manually look at all impls + // which have some integer or float as a self type. + ty::Infer(ty::IntVar(_)) => { + use ty::IntTy::*; + use ty::UintTy::*; + // This causes a compiler error if any new integer kinds are added. + let (I8 | I16 | I32 | I64 | I128 | Isize): ty::IntTy; + let (U8 | U16 | U32 | U64 | U128 | Usize): ty::UintTy; + let possible_integers = [ + // signed integers + ty::SimplifiedType::Int(I8), + ty::SimplifiedType::Int(I16), + ty::SimplifiedType::Int(I32), + ty::SimplifiedType::Int(I64), + ty::SimplifiedType::Int(I128), + ty::SimplifiedType::Int(Isize), + // unsigned integers + ty::SimplifiedType::Uint(U8), + ty::SimplifiedType::Uint(U16), + ty::SimplifiedType::Uint(U32), + ty::SimplifiedType::Uint(U64), + ty::SimplifiedType::Uint(U128), + ty::SimplifiedType::Uint(Usize), + ]; + for simp in possible_integers { + consider_impls_for_simplified_type(simp); + } + } + + ty::Infer(ty::FloatVar(_)) => { + // This causes a compiler error if any new float kinds are added. + let (ty::FloatTy::F16 | ty::FloatTy::F32 | ty::FloatTy::F64 | ty::FloatTy::F128); + let possible_floats = [ + ty::SimplifiedType::Float(ty::FloatTy::F16), + ty::SimplifiedType::Float(ty::FloatTy::F32), + ty::SimplifiedType::Float(ty::FloatTy::F64), + ty::SimplifiedType::Float(ty::FloatTy::F128), + ]; + + for simp in possible_floats { + consider_impls_for_simplified_type(simp); + } + } + + // The only traits applying to aliases and placeholders are blanket impls. + // + // Impls which apply to an alias after normalization are handled by + // `assemble_candidates_after_normalizing_self_ty`. + ty::Alias(_, _) | ty::Placeholder(..) | ty::Error(_) => (), + + // FIXME: These should ideally not exist as a self type. It would be nice for + // the builtin auto trait impls of coroutines to instead directly recurse + // into the witness. + ty::CoroutineWitness(..) => (), + + // These variants should not exist as a self type. + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Param(_) + | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"), + } + + let trait_impls = tcx.trait_impls_of(trait_def_id); + for &impl_def_id in trait_impls.blanket_impls() { + f(impl_def_id); + } + } + + fn has_item_definition(self, def_id: DefId) -> bool { + self.defaultness(def_id).has_value() + } + + fn impl_is_default(self, impl_def_id: DefId) -> bool { + self.defaultness(impl_def_id).is_default() + } + + fn impl_trait_ref(self, impl_def_id: DefId) -> ty::EarlyBinder<'tcx, ty::TraitRef<'tcx>> { + self.impl_trait_ref(impl_def_id).unwrap() + } + + fn impl_polarity(self, impl_def_id: DefId) -> ty::ImplPolarity { + self.impl_polarity(impl_def_id) + } + + fn trait_is_auto(self, trait_def_id: DefId) -> bool { + self.trait_is_auto(trait_def_id) + } + + fn trait_is_alias(self, trait_def_id: DefId) -> bool { + self.trait_is_alias(trait_def_id) + } + + fn trait_is_object_safe(self, trait_def_id: DefId) -> bool { + self.is_object_safe(trait_def_id) + } + + fn trait_may_be_implemented_via_object(self, trait_def_id: DefId) -> bool { + self.trait_def(trait_def_id).implement_via_object + } + + fn fn_trait_kind_from_def_id(self, trait_def_id: DefId) -> Option { + self.fn_trait_kind_from_def_id(trait_def_id) + } + + fn async_fn_trait_kind_from_def_id(self, trait_def_id: DefId) -> Option { + self.async_fn_trait_kind_from_def_id(trait_def_id) + } + + fn supertrait_def_ids(self, trait_def_id: DefId) -> impl IntoIterator { + self.supertrait_def_ids(trait_def_id) + } + + fn delay_bug(self, msg: impl ToString) -> ErrorGuaranteed { + self.dcx().span_delayed_bug(DUMMY_SP, msg.to_string()) + } + + fn is_general_coroutine(self, coroutine_def_id: DefId) -> bool { + self.is_general_coroutine(coroutine_def_id) + } + + fn coroutine_is_async(self, coroutine_def_id: DefId) -> bool { + self.coroutine_is_async(coroutine_def_id) + } + + fn coroutine_is_gen(self, coroutine_def_id: DefId) -> bool { + self.coroutine_is_gen(coroutine_def_id) + } + + fn coroutine_is_async_gen(self, coroutine_def_id: DefId) -> bool { + self.coroutine_is_async_gen(coroutine_def_id) + } + + fn layout_is_pointer_like(self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { + self.layout_of(self.erase_regions(param_env.and(ty))) + .is_ok_and(|layout| layout.layout.is_pointer_like(&self.data_layout)) + } + + type UnsizingParams = &'tcx rustc_index::bit_set::BitSet; + fn unsizing_params_for_adt(self, adt_def_id: DefId) -> Self::UnsizingParams { + self.unsizing_params_for_adt(adt_def_id) + } + + fn find_const_ty_from_env( + self, + param_env: ty::ParamEnv<'tcx>, + placeholder: Self::PlaceholderConst, + ) -> Ty<'tcx> { + placeholder.find_const_ty_from_env(param_env) + } +} + +fn trait_lang_item_to_lang_item(lang_item: TraitSolverLangItem) -> LangItem { + match lang_item { + TraitSolverLangItem::AsyncDestruct => LangItem::AsyncDestruct, + TraitSolverLangItem::AsyncFnKindHelper => LangItem::AsyncFnKindHelper, + TraitSolverLangItem::AsyncFnKindUpvars => LangItem::AsyncFnKindUpvars, + TraitSolverLangItem::AsyncFnOnceOutput => LangItem::AsyncFnOnceOutput, + TraitSolverLangItem::AsyncIterator => LangItem::AsyncIterator, + TraitSolverLangItem::CallOnceFuture => LangItem::CallOnceFuture, + TraitSolverLangItem::CallRefFuture => LangItem::CallRefFuture, + TraitSolverLangItem::Clone => LangItem::Clone, + TraitSolverLangItem::Copy => LangItem::Copy, + TraitSolverLangItem::Coroutine => LangItem::Coroutine, + TraitSolverLangItem::CoroutineReturn => LangItem::CoroutineReturn, + TraitSolverLangItem::CoroutineYield => LangItem::CoroutineYield, + TraitSolverLangItem::Destruct => LangItem::Destruct, + TraitSolverLangItem::DiscriminantKind => LangItem::DiscriminantKind, + TraitSolverLangItem::DynMetadata => LangItem::DynMetadata, + TraitSolverLangItem::FnPtrTrait => LangItem::FnPtrTrait, + TraitSolverLangItem::FusedIterator => LangItem::FusedIterator, + TraitSolverLangItem::Future => LangItem::Future, + TraitSolverLangItem::FutureOutput => LangItem::FutureOutput, + TraitSolverLangItem::Iterator => LangItem::Iterator, + TraitSolverLangItem::Metadata => LangItem::Metadata, + TraitSolverLangItem::Option => LangItem::Option, + TraitSolverLangItem::PointeeTrait => LangItem::PointeeTrait, + TraitSolverLangItem::PointerLike => LangItem::PointerLike, + TraitSolverLangItem::Poll => LangItem::Poll, + TraitSolverLangItem::Sized => LangItem::Sized, + TraitSolverLangItem::TransmuteTrait => LangItem::TransmuteTrait, + TraitSolverLangItem::Tuple => LangItem::Tuple, + TraitSolverLangItem::Unpin => LangItem::Unpin, + TraitSolverLangItem::Unsize => LangItem::Unsize, + } +} + +impl<'tcx> rustc_type_ir::inherent::DefId> for DefId { + fn as_local(self) -> Option { + self.as_local() + } } impl<'tcx> rustc_type_ir::inherent::Abi> for abi::Abi { + fn rust() -> Self { + abi::Abi::Rust + } + fn is_rust(self) -> bool { matches!(self, abi::Abi::Rust) } } impl<'tcx> rustc_type_ir::inherent::Safety> for hir::Safety { + fn safe() -> Self { + hir::Safety::Safe + } + + fn is_safe(self) -> bool { + matches!(self, hir::Safety::Safe) + } + fn prefix_str(self) -> &'static str { self.prefix_str() } } +impl<'tcx> rustc_type_ir::inherent::Features> for &'tcx rustc_feature::Features { + fn generic_const_exprs(self) -> bool { + self.generic_const_exprs + } + + fn coroutine_clone(self) -> bool { + self.coroutine_clone + } + + fn associated_const_equality(self) -> bool { + self.associated_const_equality + } +} + type InternedSet<'tcx, T> = ShardedHashMap, ()>; pub struct CtxtInterners<'tcx> { @@ -266,14 +683,14 @@ pub struct CtxtInterners<'tcx> { clauses: InternedSet<'tcx, ListWithCachedTypeInfo>>, projs: InternedSet<'tcx, List>, place_elems: InternedSet<'tcx, List>>, - const_: InternedSet<'tcx, WithCachedTypeInfo>>, + const_: InternedSet<'tcx, WithCachedTypeInfo>>, pat: InternedSet<'tcx, PatternKind<'tcx>>, const_allocation: InternedSet<'tcx, Allocation>, bound_variable_kinds: InternedSet<'tcx, List>, layout: InternedSet<'tcx, LayoutS>, adt_def: InternedSet<'tcx, AdtDefData>, - external_constraints: InternedSet<'tcx, ExternalConstraintsData<'tcx>>, - predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<'tcx>>, + external_constraints: InternedSet<'tcx, ExternalConstraintsData>>, + predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData>>, fields: InternedSet<'tcx, List>, local_def_ids: InternedSet<'tcx, List>, captures: InternedSet<'tcx, List<&'tcx ty::CapturedPlace<'tcx>>>, @@ -336,18 +753,18 @@ impl<'tcx> CtxtInterners<'tcx> { #[inline(never)] fn intern_const( &self, - data: ty::ConstData<'tcx>, + kind: ty::ConstKind<'tcx>, sess: &Session, untracked: &Untracked, ) -> Const<'tcx> { Const(Interned::new_unchecked( self.const_ - .intern(data, |data: ConstData<'_>| { - let flags = super::flags::FlagComputation::for_const(&data.kind, data.ty); - let stable_hash = self.stable_hash(&flags, sess, untracked, &data); + .intern(kind, |kind: ty::ConstKind<'_>| { + let flags = super::flags::FlagComputation::for_const_kind(&kind); + let stable_hash = self.stable_hash(&flags, sess, untracked, &kind); InternedInSet(self.arena.alloc(WithCachedTypeInfo { - internee: data, + internee: kind, stable_hash, flags: flags.flags, outer_exclusive_binder: flags.outer_exclusive_binder, @@ -599,18 +1016,15 @@ impl<'tcx> CommonConsts<'tcx> { }; CommonConsts { - unit: mk_const(ty::ConstData { - kind: ty::ConstKind::Value(ty::ValTree::zst()), - ty: types.unit, - }), - true_: mk_const(ty::ConstData { - kind: ty::ConstKind::Value(ty::ValTree::Leaf(ty::ScalarInt::TRUE)), - ty: types.bool, - }), - false_: mk_const(ty::ConstData { - kind: ty::ConstKind::Value(ty::ValTree::Leaf(ty::ScalarInt::FALSE)), - ty: types.bool, - }), + unit: mk_const(ty::ConstKind::Value(types.unit, ty::ValTree::zst())), + true_: mk_const(ty::ConstKind::Value( + types.bool, + ty::ValTree::Leaf(ty::ScalarInt::TRUE), + )), + false_: mk_const(ty::ConstKind::Value( + types.bool, + ty::ValTree::Leaf(ty::ScalarInt::FALSE), + )), } } } @@ -683,7 +1097,7 @@ impl<'tcx> TyCtxt<'tcx> { /// In order to break cycles involving `AnonConst`, we need to set the expected type by side /// effect. However, we do not want this as a general capability, so this interface restricts /// to the only allowed case. - pub fn feed_anon_const_type(self, key: LocalDefId, value: ty::EarlyBinder>) { + pub fn feed_anon_const_type(self, key: LocalDefId, value: ty::EarlyBinder<'tcx, Ty<'tcx>>) { debug_assert_eq!(self.def_kind(key), DefKind::AnonConst); TyCtxtFeed { tcx: self, key }.type_of(value) } @@ -1307,7 +1721,7 @@ impl<'tcx> TyCtxt<'tcx> { ) } - pub fn dcx(self) -> &'tcx DiagCtxt { + pub fn dcx(self) -> DiagCtxtHandle<'tcx> { self.sess.dcx() } } @@ -1491,13 +1905,14 @@ impl<'tcx> TyCtxt<'tcx> { } /// Returns the `DefId` and the `BoundRegionKind` corresponding to the given region. - pub fn is_suitable_region(self, mut region: Region<'tcx>) -> Option { + pub fn is_suitable_region( + self, + generic_param_scope: LocalDefId, + mut region: Region<'tcx>, + ) -> Option { let (suitable_region_binding_scope, bound_region) = loop { - let def_id = match region.kind() { - ty::ReLateParam(fr) => fr.bound_region.get_id()?.as_local()?, - ty::ReEarlyParam(ebr) => ebr.def_id.as_local()?, - _ => return None, // not a free region - }; + let def_id = + region.opt_param_def_id(self, generic_param_scope.to_def_id())?.as_local()?; let scope = self.local_parent(def_id); if self.def_kind(scope) == DefKind::OpaqueTy { // Lifetime params of opaque types are synthetic and thus irrelevant to @@ -1985,9 +2400,9 @@ direct_interners! { const_allocation: pub mk_const_alloc(Allocation): ConstAllocation -> ConstAllocation<'tcx>, layout: pub mk_layout(LayoutS): Layout -> Layout<'tcx>, adt_def: pub mk_adt_def_from_data(AdtDefData): AdtDef -> AdtDef<'tcx>, - external_constraints: pub mk_external_constraints(ExternalConstraintsData<'tcx>): + external_constraints: pub mk_external_constraints(ExternalConstraintsData>): ExternalConstraints -> ExternalConstraints<'tcx>, - predefined_opaques_in_body: pub mk_predefined_opaques_in_body(PredefinedOpaquesData<'tcx>): + predefined_opaques_in_body: pub mk_predefined_opaques_in_body(PredefinedOpaquesData>): PredefinedOpaques -> PredefinedOpaques<'tcx>, } @@ -2221,9 +2636,9 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] - pub fn mk_ct_from_kind(self, kind: ty::ConstKind<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> { + pub fn mk_ct_from_kind(self, kind: ty::ConstKind<'tcx>) -> Const<'tcx> { self.interners.intern_const( - ty::ConstData { kind, ty }, + kind, self.sess, // This is only used to create a stable hashing context. &self.untracked, @@ -2248,14 +2663,10 @@ impl<'tcx> TyCtxt<'tcx> { ty::Region::new_early_param(self, param.to_early_bound_region_data()).into() } GenericParamDefKind::Type { .. } => Ty::new_param(self, param.index, param.name).into(), - GenericParamDefKind::Const { .. } => ty::Const::new_param( - self, - ParamConst { index: param.index, name: param.name }, - self.type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"), - ) - .into(), + GenericParamDefKind::Const { .. } => { + ty::Const::new_param(self, ParamConst { index: param.index, name: param.name }) + .into() + } } } @@ -2472,10 +2883,9 @@ impl<'tcx> TyCtxt<'tcx> { span: impl Into, decorator: impl for<'a> LintDiagnostic<'a, ()>, ) { - let msg = decorator.msg(); let (level, src) = self.lint_level_at_node(lint, hir_id); - lint_level(self.sess, lint, level, src, Some(span.into()), msg, |diag| { - decorator.decorate_lint(diag); + lint_level(self.sess, lint, level, src, Some(span.into()), |lint| { + decorator.decorate_lint(lint); }) } @@ -2489,11 +2899,10 @@ impl<'tcx> TyCtxt<'tcx> { lint: &'static Lint, hir_id: HirId, span: impl Into, - msg: impl Into, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), ) { let (level, src) = self.lint_level_at_node(lint, hir_id); - lint_level(self.sess, lint, level, src, Some(span.into()), msg, decorate); + lint_level(self.sess, lint, level, src, Some(span.into()), decorate); } /// Find the crate root and the appropriate span where `use` and outer attributes can be @@ -2544,8 +2953,8 @@ impl<'tcx> TyCtxt<'tcx> { id: HirId, decorator: impl for<'a> LintDiagnostic<'a, ()>, ) { - self.node_lint(lint, id, decorator.msg(), |diag| { - decorator.decorate_lint(diag); + self.node_lint(lint, id, |lint| { + decorator.decorate_lint(lint); }) } @@ -2558,11 +2967,10 @@ impl<'tcx> TyCtxt<'tcx> { self, lint: &'static Lint, id: HirId, - msg: impl Into, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), ) { let (level, src) = self.lint_level_at_node(lint, id); - lint_level(self.sess, lint, level, src, None, msg, decorate); + lint_level(self.sess, lint, level, src, None, decorate); } pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx [TraitCandidate]> { @@ -2636,7 +3044,6 @@ impl<'tcx> TyCtxt<'tcx> { return ty::Region::new_early_param( self, ty::EarlyParamRegion { - def_id: ebv, index: generics .param_def_id_to_index(self, ebv) .expect("early-bound var should be present in fn generics"), @@ -2748,7 +3155,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn impl_trait_ref( self, def_id: impl IntoQueryParam, - ) -> Option>> { + ) -> Option>> { Some(self.impl_trait_header(def_id)?.trait_ref) } diff --git a/compiler/rustc_middle/src/ty/erase_regions.rs b/compiler/rustc_middle/src/ty/erase_regions.rs index cfd36fd8c7c3..cd6e7df31f7c 100644 --- a/compiler/rustc_middle/src/ty/erase_regions.rs +++ b/compiler/rustc_middle/src/ty/erase_regions.rs @@ -1,6 +1,7 @@ use crate::query::Providers; use crate::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use crate::ty::{self, Ty, TyCtxt, TypeFlags, TypeVisitableExt}; +use tracing::debug; pub(super) fn provide(providers: &mut Providers) { *providers = Providers { erase_regions_ty, ..*providers }; diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 99d703be873e..32dc9fa5fc62 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -1,91 +1,26 @@ use crate::ty::print::{with_forced_trimmed_paths, FmtPrinter, PrettyPrinter}; -use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt}; +use crate::ty::{self, Ty, TyCtxt}; + use rustc_errors::pluralize; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind}; -use rustc_hir::def_id::DefId; -use rustc_macros::{TypeFoldable, TypeVisitable}; -use rustc_span::symbol::Symbol; -use rustc_target::spec::abi; +use rustc_macros::extension; +pub use rustc_type_ir::error::ExpectedFound; + use std::borrow::Cow; use std::hash::{DefaultHasher, Hash, Hasher}; use std::path::PathBuf; -#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable)] -pub struct ExpectedFound { - pub expected: T, - pub found: T, -} +pub type TypeError<'tcx> = rustc_type_ir::error::TypeError>; -impl ExpectedFound { - pub fn new(a_is_expected: bool, a: T, b: T) -> Self { - if a_is_expected { - ExpectedFound { expected: a, found: b } - } else { - ExpectedFound { expected: b, found: a } - } - } -} - -// Data structures used in type unification -#[derive(Copy, Clone, Debug, TypeVisitable, PartialEq, Eq)] -#[rustc_pass_by_value] -pub enum TypeError<'tcx> { - Mismatch, - ConstnessMismatch(ExpectedFound), - PolarityMismatch(ExpectedFound), - SafetyMismatch(ExpectedFound), - AbiMismatch(ExpectedFound), - Mutability, - ArgumentMutability(usize), - TupleSize(ExpectedFound), - FixedArraySize(ExpectedFound), - ArgCount, - FieldMisMatch(Symbol, Symbol), - - RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>), - RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>), - RegionsPlaceholderMismatch, - - Sorts(ExpectedFound>), - ArgumentSorts(ExpectedFound>, usize), - IntMismatch(ExpectedFound), - FloatMismatch(ExpectedFound), - Traits(ExpectedFound), - VariadicMismatch(ExpectedFound), - - /// Instantiating a type variable with the given type would have - /// created a cycle (because it appears somewhere within that - /// type). - CyclicTy(Ty<'tcx>), - CyclicConst(ty::Const<'tcx>), - ProjectionMismatched(ExpectedFound), - ExistentialMismatch(ExpectedFound<&'tcx ty::List>>), - ConstMismatch(ExpectedFound>), - - IntrinsicCast, - /// Safe `#[target_feature]` functions are not assignable to safe function pointers. - TargetFeatureCast(DefId), -} - -impl TypeError<'_> { - pub fn involves_regions(self) -> bool { - match self { - TypeError::RegionsDoesNotOutlive(_, _) - | TypeError::RegionsInsufficientlyPolymorphic(_, _) - | TypeError::RegionsPlaceholderMismatch => true, - _ => false, - } - } -} - -/// Explains the source of a type err in a short, human readable way. This is meant to be placed -/// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()` -/// afterwards to present additional details, particularly when it comes to lifetime-related -/// errors. +/// Explains the source of a type err in a short, human readable way. +/// This is meant to be placed in parentheses after some larger message. +/// You should also invoke `note_and_explain_type_err()` afterwards +/// to present additional details, particularly when it comes to lifetime- +/// related errors. +#[extension(pub trait TypeErrorToStringExt<'tcx>)] impl<'tcx> TypeError<'tcx> { - pub fn to_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> { - use self::TypeError::*; + fn to_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> { fn report_maybe_different(expected: &str, found: &str) -> String { // A naive approach to making sure that we're not reporting silly errors such as: // (expected closure, found closure). @@ -97,24 +32,26 @@ impl<'tcx> TypeError<'tcx> { } match self { - CyclicTy(_) => "cyclic type of infinite size".into(), - CyclicConst(_) => "encountered a self-referencing constant".into(), - Mismatch => "types differ".into(), - ConstnessMismatch(values) => { + TypeError::CyclicTy(_) => "cyclic type of infinite size".into(), + TypeError::CyclicConst(_) => "encountered a self-referencing constant".into(), + TypeError::Mismatch => "types differ".into(), + TypeError::ConstnessMismatch(values) => { format!("expected {} bound, found {} bound", values.expected, values.found).into() } - PolarityMismatch(values) => { + TypeError::PolarityMismatch(values) => { format!("expected {} polarity, found {} polarity", values.expected, values.found) .into() } - SafetyMismatch(values) => { + TypeError::SafetyMismatch(values) => { format!("expected {} fn, found {} fn", values.expected, values.found).into() } - AbiMismatch(values) => { + TypeError::AbiMismatch(values) => { format!("expected {} fn, found {} fn", values.expected, values.found).into() } - ArgumentMutability(_) | Mutability => "types differ in mutability".into(), - TupleSize(values) => format!( + TypeError::ArgumentMutability(_) | TypeError::Mutability => { + "types differ in mutability".into() + } + TypeError::TupleSize(values) => format!( "expected a tuple with {} element{}, found one with {} element{}", values.expected, pluralize!(values.expected), @@ -122,7 +59,7 @@ impl<'tcx> TypeError<'tcx> { pluralize!(values.found) ) .into(), - FixedArraySize(values) => format!( + TypeError::FixedArraySize(values) => format!( "expected an array with a fixed size of {} element{}, found one with {} element{}", values.expected, pluralize!(values.expected), @@ -130,20 +67,21 @@ impl<'tcx> TypeError<'tcx> { pluralize!(values.found) ) .into(), - ArgCount => "incorrect number of function parameters".into(), - FieldMisMatch(adt, field) => format!("field type mismatch: {adt}.{field}").into(), - RegionsDoesNotOutlive(..) => "lifetime mismatch".into(), + TypeError::ArgCount => "incorrect number of function parameters".into(), + TypeError::RegionsDoesNotOutlive(..) => "lifetime mismatch".into(), // Actually naming the region here is a bit confusing because context is lacking - RegionsInsufficientlyPolymorphic(..) => { + TypeError::RegionsInsufficientlyPolymorphic(..) => { "one type is more general than the other".into() } - RegionsPlaceholderMismatch => "one type is more general than the other".into(), - ArgumentSorts(values, _) | Sorts(values) => { + TypeError::RegionsPlaceholderMismatch => { + "one type is more general than the other".into() + } + TypeError::ArgumentSorts(values, _) | TypeError::Sorts(values) => { let expected = values.expected.sort_string(tcx); let found = values.found.sort_string(tcx); report_maybe_different(&expected, &found).into() } - Traits(values) => { + TypeError::Traits(values) => { let (mut expected, mut found) = with_forced_trimmed_paths!(( tcx.def_path_str(values.expected), tcx.def_path_str(values.found), @@ -155,77 +93,34 @@ impl<'tcx> TypeError<'tcx> { report_maybe_different(&format!("trait `{expected}`"), &format!("trait `{found}`")) .into() } - IntMismatch(ref values) => { - let expected = match values.expected { - ty::IntVarValue::IntType(ty) => ty.name_str(), - ty::IntVarValue::UintType(ty) => ty.name_str(), - }; - let found = match values.found { - ty::IntVarValue::IntType(ty) => ty.name_str(), - ty::IntVarValue::UintType(ty) => ty.name_str(), - }; - format!("expected `{expected}`, found `{found}`").into() - } - FloatMismatch(ref values) => format!( - "expected `{}`, found `{}`", - values.expected.name_str(), - values.found.name_str() - ) - .into(), - VariadicMismatch(ref values) => format!( + TypeError::VariadicMismatch(ref values) => format!( "expected {} fn, found {} function", if values.expected { "variadic" } else { "non-variadic" }, if values.found { "variadic" } else { "non-variadic" } ) .into(), - ProjectionMismatched(ref values) => format!( + TypeError::ProjectionMismatched(ref values) => format!( "expected `{}`, found `{}`", tcx.def_path_str(values.expected), tcx.def_path_str(values.found) ) .into(), - ExistentialMismatch(ref values) => report_maybe_different( + TypeError::ExistentialMismatch(ref values) => report_maybe_different( &format!("trait `{}`", values.expected), &format!("trait `{}`", values.found), ) .into(), - ConstMismatch(ref values) => { + TypeError::ConstMismatch(ref values) => { format!("expected `{}`, found `{}`", values.expected, values.found).into() } - IntrinsicCast => "cannot coerce intrinsics to function pointers".into(), - TargetFeatureCast(_) => { + TypeError::IntrinsicCast => "cannot coerce intrinsics to function pointers".into(), + TypeError::TargetFeatureCast(_) => { "cannot coerce functions with `#[target_feature]` to safe function pointers".into() } } } } -impl<'tcx> TypeError<'tcx> { - pub fn must_include_note(self) -> bool { - use self::TypeError::*; - match self { - CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | ConstnessMismatch(_) - | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_) - | ArgumentSorts(..) | Sorts(_) | IntMismatch(_) | FloatMismatch(_) - | VariadicMismatch(_) | TargetFeatureCast(_) => false, - - Mutability - | ArgumentMutability(_) - | TupleSize(_) - | ArgCount - | FieldMisMatch(..) - | RegionsDoesNotOutlive(..) - | RegionsInsufficientlyPolymorphic(..) - | RegionsPlaceholderMismatch - | Traits(_) - | ProjectionMismatched(_) - | ExistentialMismatch(_) - | ConstMismatch(_) - | IntrinsicCast => true, - } - } -} - impl<'tcx> Ty<'tcx> { pub fn sort_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> { match *self.kind() { diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index 7508f0080ccf..923667e609be 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -337,7 +337,7 @@ impl DeepRejectCtxt { | ty::ConstKind::Error(_) => { return true; } - ty::ConstKind::Value(impl_val) => impl_val, + ty::ConstKind::Value(_, impl_val) => impl_val, ty::ConstKind::Infer(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => { bug!("unexpected impl arg: {:?}", impl_ct) } @@ -357,7 +357,7 @@ impl DeepRejectCtxt { ty::ConstKind::Expr(_) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => { true } - ty::ConstKind::Value(obl_val) => obl_val == impl_val, + ty::ConstKind::Value(_, obl_val) => obl_val == impl_val, ty::ConstKind::Infer(_) => true, diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 4de7d532c967..21c115c2c966 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -28,10 +28,9 @@ impl FlagComputation { result } - pub fn for_const(c: &ty::ConstKind<'_>, t: Ty<'_>) -> FlagComputation { + pub fn for_const_kind(kind: &ty::ConstKind<'_>) -> FlagComputation { let mut result = FlagComputation::new(); - result.add_const_kind(c); - result.add_ty(t); + result.add_const_kind(kind); result } @@ -373,27 +372,8 @@ impl FlagComputation { self.add_flags(TypeFlags::HAS_CT_PLACEHOLDER); self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); } - ty::ConstKind::Value(_) => {} - ty::ConstKind::Expr(e) => { - use ty::Expr; - match e { - Expr::Binop(_, l, r) => { - self.add_const(l); - self.add_const(r); - } - Expr::UnOp(_, v) => self.add_const(v), - Expr::FunctionCall(f, args) => { - self.add_const(f); - for arg in args { - self.add_const(arg); - } - } - Expr::Cast(_, c, t) => { - self.add_ty(t); - self.add_const(c); - } - } - } + ty::ConstKind::Value(ty, _) => self.add_ty(ty), + ty::ConstKind::Expr(e) => self.add_args(e.args()), ty::ConstKind::Error(_) => self.add_flags(TypeFlags::HAS_ERROR), } } diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index fd3bee16e268..9b5b1430c27f 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -1,8 +1,11 @@ use crate::ty::{self, Binder, BoundTy, Ty, TyCtxt, TypeVisitableExt}; use rustc_data_structures::fx::FxIndexMap; use rustc_hir::def_id::DefId; +use tracing::{debug, instrument}; -pub use rustc_type_ir::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable}; +pub use rustc_type_ir::fold::{ + shift_region, shift_vars, FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable, +}; /////////////////////////////////////////////////////////////////////////// // Some sample folders @@ -131,13 +134,13 @@ impl<'a, 'tcx> TypeFolder> for RegionFolder<'a, 'tcx> { pub trait BoundVarReplacerDelegate<'tcx> { fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx>; fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx>; - fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx>; + fn replace_const(&mut self, bv: ty::BoundVar) -> ty::Const<'tcx>; } pub struct FnMutDelegate<'a, 'tcx> { pub regions: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a), pub types: &'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a), - pub consts: &'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx> + 'a), + pub consts: &'a mut (dyn FnMut(ty::BoundVar) -> ty::Const<'tcx> + 'a), } impl<'a, 'tcx> BoundVarReplacerDelegate<'tcx> for FnMutDelegate<'a, 'tcx> { @@ -147,8 +150,8 @@ impl<'a, 'tcx> BoundVarReplacerDelegate<'tcx> for FnMutDelegate<'a, 'tcx> { fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> { (self.types)(bt) } - fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx> { - (self.consts)(bv, ty) + fn replace_const(&mut self, bv: ty::BoundVar) -> ty::Const<'tcx> { + (self.consts)(bv) } } @@ -221,7 +224,7 @@ where fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { match ct.kind() { ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => { - let ct = self.delegate.replace_const(bound_const, ct.ty()); + let ct = self.delegate.replace_const(bound_const); debug_assert!(!ct.has_vars_bound_above(ty::INNERMOST)); ty::fold::shift_vars(self.tcx, ct, self.current_index.as_u32()) } @@ -279,7 +282,7 @@ impl<'tcx> TyCtxt<'tcx> { let delegate = FnMutDelegate { regions: &mut replace_regions, types: &mut |b| bug!("unexpected bound ty in binder: {b:?}"), - consts: &mut |b, ty| bug!("unexpected bound ct in binder: {b:?} {ty}"), + consts: &mut |b| bug!("unexpected bound ct in binder: {b:?}"), }; let mut replacer = BoundVarReplacer::new(self, delegate); value.fold_with(&mut replacer) @@ -350,9 +353,7 @@ impl<'tcx> TyCtxt<'tcx> { ty::BoundTy { var: shift_bv(t.var), kind: t.kind }, ) }, - consts: &mut |c, ty: Ty<'tcx>| { - ty::Const::new_bound(self, ty::INNERMOST, shift_bv(c), ty) - }, + consts: &mut |c| ty::Const::new_bound(self, ty::INNERMOST, shift_bv(c)), }, ) } @@ -395,12 +396,12 @@ impl<'tcx> TyCtxt<'tcx> { .expect_ty(); Ty::new_bound(self.tcx, ty::INNERMOST, BoundTy { var, kind }) } - fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx> { + fn replace_const(&mut self, bv: ty::BoundVar) -> ty::Const<'tcx> { let entry = self.map.entry(bv); let index = entry.index(); let var = ty::BoundVar::from_usize(index); let () = entry.or_insert_with(|| ty::BoundVariableKind::Const).expect_const(); - ty::Const::new_bound(self.tcx, ty::INNERMOST, var, ty) + ty::Const::new_bound(self.tcx, ty::INNERMOST, var) } } @@ -411,103 +412,3 @@ impl<'tcx> TyCtxt<'tcx> { Binder::bind_with_vars(inner, bound_vars) } } - -/////////////////////////////////////////////////////////////////////////// -// Shifter -// -// Shifts the De Bruijn indices on all escaping bound vars by a -// fixed amount. Useful in instantiation or when otherwise introducing -// a binding level that is not intended to capture the existing bound -// vars. See comment on `shift_vars_through_binders` method in -// `rustc_middle/src/ty/generic_args.rs` for more details. - -struct Shifter<'tcx> { - tcx: TyCtxt<'tcx>, - current_index: ty::DebruijnIndex, - amount: u32, -} - -impl<'tcx> Shifter<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, amount: u32) -> Self { - Shifter { tcx, current_index: ty::INNERMOST, amount } - } -} - -impl<'tcx> TypeFolder> for Shifter<'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_binder>>( - &mut self, - t: ty::Binder<'tcx, T>, - ) -> ty::Binder<'tcx, T> { - self.current_index.shift_in(1); - let t = t.super_fold_with(self); - self.current_index.shift_out(1); - t - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { - ty::ReBound(debruijn, br) if debruijn >= self.current_index => { - let debruijn = debruijn.shifted_in(self.amount); - ty::Region::new_bound(self.tcx, debruijn, br) - } - _ => r, - } - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match *ty.kind() { - ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { - let debruijn = debruijn.shifted_in(self.amount); - Ty::new_bound(self.tcx, debruijn, bound_ty) - } - - _ if ty.has_vars_bound_at_or_above(self.current_index) => ty.super_fold_with(self), - _ => ty, - } - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - if let ty::ConstKind::Bound(debruijn, bound_ct) = ct.kind() - && debruijn >= self.current_index - { - let debruijn = debruijn.shifted_in(self.amount); - ty::Const::new_bound(self.tcx, debruijn, bound_ct, ct.ty()) - } else { - ct.super_fold_with(self) - } - } - - fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { - if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } - } -} - -pub fn shift_region<'tcx>( - tcx: TyCtxt<'tcx>, - region: ty::Region<'tcx>, - amount: u32, -) -> ty::Region<'tcx> { - match *region { - ty::ReBound(debruijn, br) if amount > 0 => { - ty::Region::new_bound(tcx, debruijn.shifted_in(amount), br) - } - _ => region, - } -} - -pub fn shift_vars<'tcx, T>(tcx: TyCtxt<'tcx>, value: T, amount: u32) -> T -where - T: TypeFoldable>, -{ - debug!("shift_vars(value={:?}, amount={})", value, amount); - - if amount == 0 || !value.has_escaping_bound_vars() { - return value; - } - - value.fold_with(&mut Shifter::new(tcx, amount)) -} diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index 38b2987399a9..83d45ca78d9d 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -1,10 +1,11 @@ // Generic arguments. use crate::ty::codec::{TyDecoder, TyEncoder}; -use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable}; -use crate::ty::sty::{ClosureArgs, CoroutineArgs, CoroutineClosureArgs, InlineConstArgs}; -use crate::ty::visit::{TypeVisitable, TypeVisitableExt, TypeVisitor}; -use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt}; +use crate::ty::fold::{FallibleTypeFolder, TypeFoldable}; +use crate::ty::visit::{TypeVisitable, TypeVisitor}; +use crate::ty::{ + self, ClosureArgs, CoroutineArgs, CoroutineClosureArgs, InlineConstArgs, Lift, List, Ty, TyCtxt, +}; use rustc_ast_ir::visit::VisitorResult; use rustc_ast_ir::walk_visitable_list; @@ -12,9 +13,7 @@ use rustc_data_structures::intern::Interned; use rustc_errors::{DiagArgValue, IntoDiagArg}; use rustc_hir::def_id::DefId; use rustc_macros::extension; -use rustc_macros::{ - Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, -}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_serialize::{Decodable, Encodable}; use rustc_type_ir::WithCachedTypeInfo; use smallvec::SmallVec; @@ -23,10 +22,10 @@ use core::intrinsics; use std::marker::PhantomData; use std::mem; use std::num::NonZero; -use std::ops::Deref; use std::ptr::NonNull; pub type GenericArgKind<'tcx> = rustc_type_ir::GenericArgKind>; +pub type TermKind<'tcx> = rustc_type_ir::TermKind>; /// An entity in the Rust type system, which can be one of /// several kinds (types, lifetimes, and consts). @@ -42,14 +41,99 @@ pub struct GenericArg<'tcx> { marker: PhantomData<(Ty<'tcx>, ty::Region<'tcx>, ty::Const<'tcx>)>, } +impl<'tcx> rustc_type_ir::inherent::GenericArg> for GenericArg<'tcx> {} + impl<'tcx> rustc_type_ir::inherent::GenericArgs> for ty::GenericArgsRef<'tcx> { + fn rebase_onto( + self, + tcx: TyCtxt<'tcx>, + source_ancestor: DefId, + target_args: GenericArgsRef<'tcx>, + ) -> GenericArgsRef<'tcx> { + self.rebase_onto(tcx, source_ancestor, target_args) + } + fn type_at(self, i: usize) -> Ty<'tcx> { self.type_at(i) } + fn region_at(self, i: usize) -> ty::Region<'tcx> { + self.region_at(i) + } + + fn const_at(self, i: usize) -> ty::Const<'tcx> { + self.const_at(i) + } + fn identity_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::GenericArgsRef<'tcx> { GenericArgs::identity_for_item(tcx, def_id) } + + fn extend_with_error( + tcx: TyCtxt<'tcx>, + def_id: DefId, + original_args: &[ty::GenericArg<'tcx>], + ) -> ty::GenericArgsRef<'tcx> { + ty::GenericArgs::extend_with_error(tcx, def_id, original_args) + } + + fn split_closure_args(self) -> ty::ClosureArgsParts> { + match self[..] { + [ref parent_args @ .., closure_kind_ty, closure_sig_as_fn_ptr_ty, tupled_upvars_ty] => { + ty::ClosureArgsParts { + parent_args, + closure_kind_ty: closure_kind_ty.expect_ty(), + closure_sig_as_fn_ptr_ty: closure_sig_as_fn_ptr_ty.expect_ty(), + tupled_upvars_ty: tupled_upvars_ty.expect_ty(), + } + } + _ => bug!("closure args missing synthetics"), + } + } + + fn split_coroutine_closure_args(self) -> ty::CoroutineClosureArgsParts> { + match self[..] { + [ + ref parent_args @ .., + closure_kind_ty, + signature_parts_ty, + tupled_upvars_ty, + coroutine_captures_by_ref_ty, + coroutine_witness_ty, + ] => ty::CoroutineClosureArgsParts { + parent_args, + closure_kind_ty: closure_kind_ty.expect_ty(), + signature_parts_ty: signature_parts_ty.expect_ty(), + tupled_upvars_ty: tupled_upvars_ty.expect_ty(), + coroutine_captures_by_ref_ty: coroutine_captures_by_ref_ty.expect_ty(), + coroutine_witness_ty: coroutine_witness_ty.expect_ty(), + }, + _ => bug!("closure args missing synthetics"), + } + } + + fn split_coroutine_args(self) -> ty::CoroutineArgsParts> { + match self[..] { + [ + ref parent_args @ .., + kind_ty, + resume_ty, + yield_ty, + return_ty, + witness, + tupled_upvars_ty, + ] => ty::CoroutineArgsParts { + parent_args, + kind_ty: kind_ty.expect_ty(), + resume_ty: resume_ty.expect_ty(), + yield_ty: yield_ty.expect_ty(), + return_ty: return_ty.expect_ty(), + witness: witness.expect_ty(), + tupled_upvars_ty: tupled_upvars_ty.expect_ty(), + }, + _ => bug!("coroutine args missing synthetics"), + } + } } impl<'tcx> rustc_type_ir::inherent::IntoKind for GenericArg<'tcx> { @@ -163,7 +247,7 @@ impl<'tcx> GenericArg<'tcx> { ptr.cast::>>().as_ref(), ))), CONST_TAG => GenericArgKind::Const(ty::Const(Interned::new_unchecked( - ptr.cast::>>().as_ref(), + ptr.cast::>>().as_ref(), ))), _ => intrinsics::unreachable(), } @@ -214,6 +298,7 @@ impl<'tcx> GenericArg<'tcx> { pub fn is_non_region_infer(self) -> bool { match self.unpack() { GenericArgKind::Lifetime(_) => false, + // FIXME: This shouldn't return numerical/float. GenericArgKind::Type(ty) => ty.is_ty_or_numeric_infer(), GenericArgKind::Const(ct) => ct.is_ct_infer(), } @@ -289,7 +374,7 @@ impl<'tcx> GenericArgs<'tcx> { /// Closure args have a particular structure controlled by the /// compiler that encodes information like the signature and closure kind; /// see `ty::ClosureArgs` struct for more comments. - pub fn as_closure(&'tcx self) -> ClosureArgs<'tcx> { + pub fn as_closure(&'tcx self) -> ClosureArgs> { ClosureArgs { args: self } } @@ -297,7 +382,7 @@ impl<'tcx> GenericArgs<'tcx> { /// Coroutine-closure args have a particular structure controlled by the /// compiler that encodes information like the signature and closure kind; /// see `ty::CoroutineClosureArgs` struct for more comments. - pub fn as_coroutine_closure(&'tcx self) -> CoroutineClosureArgs<'tcx> { + pub fn as_coroutine_closure(&'tcx self) -> CoroutineClosureArgs> { CoroutineClosureArgs { args: self } } @@ -305,7 +390,7 @@ impl<'tcx> GenericArgs<'tcx> { /// Coroutine args have a particular structure controlled by the /// compiler that encodes information like the signature and coroutine kind; /// see `ty::CoroutineArgs` struct for more comments. - pub fn as_coroutine(&'tcx self) -> CoroutineArgs<'tcx> { + pub fn as_coroutine(&'tcx self) -> CoroutineArgs> { CoroutineArgs { args: self } } @@ -389,11 +474,11 @@ impl<'tcx> GenericArgs<'tcx> { def_id: DefId, original_args: &[GenericArg<'tcx>], ) -> GenericArgsRef<'tcx> { - ty::GenericArgs::for_item(tcx, def_id, |def, args| { + ty::GenericArgs::for_item(tcx, def_id, |def, _| { if let Some(arg) = original_args.get(def.index as usize) { *arg } else { - def.to_error(tcx, args) + def.to_error(tcx) } }) } @@ -566,490 +651,6 @@ impl<'tcx, T: TypeVisitable>> TypeVisitable> for &'tcx } } -/// Similar to [`super::Binder`] except that it tracks early bound generics, i.e. `struct Foo(T)` -/// needs `T` instantiated immediately. This type primarily exists to avoid forgetting to call -/// `instantiate`. -/// -/// If you don't have anything to `instantiate`, you may be looking for -/// [`instantiate_identity`](EarlyBinder::instantiate_identity) or [`skip_binder`](EarlyBinder::skip_binder). -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[derive(Encodable, Decodable, HashStable)] -pub struct EarlyBinder { - value: T, -} - -/// For early binders, you should first call `instantiate` before using any visitors. -impl<'tcx, T> !TypeFoldable> for ty::EarlyBinder {} -impl<'tcx, T> !TypeVisitable> for ty::EarlyBinder {} - -impl EarlyBinder { - pub fn bind(value: T) -> EarlyBinder { - EarlyBinder { value } - } - - pub fn as_ref(&self) -> EarlyBinder<&T> { - EarlyBinder { value: &self.value } - } - - pub fn map_bound_ref(&self, f: F) -> EarlyBinder - where - F: FnOnce(&T) -> U, - { - self.as_ref().map_bound(f) - } - - pub fn map_bound(self, f: F) -> EarlyBinder - where - F: FnOnce(T) -> U, - { - let value = f(self.value); - EarlyBinder { value } - } - - pub fn try_map_bound(self, f: F) -> Result, E> - where - F: FnOnce(T) -> Result, - { - let value = f(self.value)?; - Ok(EarlyBinder { value }) - } - - pub fn rebind(&self, value: U) -> EarlyBinder { - EarlyBinder { value } - } - - /// Skips the binder and returns the "bound" value. - /// This can be used to extract data that does not depend on generic parameters - /// (e.g., getting the `DefId` of the inner value or getting the number of - /// arguments of an `FnSig`). Otherwise, consider using - /// [`instantiate_identity`](EarlyBinder::instantiate_identity). - /// - /// To skip the binder on `x: &EarlyBinder` to obtain `&T`, leverage - /// [`EarlyBinder::as_ref`](EarlyBinder::as_ref): `x.as_ref().skip_binder()`. - /// - /// See also [`Binder::skip_binder`](super::Binder::skip_binder), which is - /// the analogous operation on [`super::Binder`]. - pub fn skip_binder(self) -> T { - self.value - } -} - -impl EarlyBinder> { - pub fn transpose(self) -> Option> { - self.value.map(|value| EarlyBinder { value }) - } -} - -impl<'tcx, 's, I: IntoIterator> EarlyBinder -where - I::Item: TypeFoldable>, -{ - pub fn iter_instantiated( - self, - tcx: TyCtxt<'tcx>, - args: &'s [GenericArg<'tcx>], - ) -> IterInstantiated<'s, 'tcx, I> { - IterInstantiated { it: self.value.into_iter(), tcx, args } - } - - /// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity), - /// but on an iterator of `TypeFoldable` values. - pub fn instantiate_identity_iter(self) -> I::IntoIter { - self.value.into_iter() - } -} - -pub struct IterInstantiated<'s, 'tcx, I: IntoIterator> { - it: I::IntoIter, - tcx: TyCtxt<'tcx>, - args: &'s [GenericArg<'tcx>], -} - -impl<'tcx, I: IntoIterator> Iterator for IterInstantiated<'_, 'tcx, I> -where - I::Item: TypeFoldable>, -{ - type Item = I::Item; - - fn next(&mut self) -> Option { - Some(EarlyBinder { value: self.it.next()? }.instantiate(self.tcx, self.args)) - } - - fn size_hint(&self) -> (usize, Option) { - self.it.size_hint() - } -} - -impl<'tcx, I: IntoIterator> DoubleEndedIterator for IterInstantiated<'_, 'tcx, I> -where - I::IntoIter: DoubleEndedIterator, - I::Item: TypeFoldable>, -{ - fn next_back(&mut self) -> Option { - Some(EarlyBinder { value: self.it.next_back()? }.instantiate(self.tcx, self.args)) - } -} - -impl<'tcx, I: IntoIterator> ExactSizeIterator for IterInstantiated<'_, 'tcx, I> -where - I::IntoIter: ExactSizeIterator, - I::Item: TypeFoldable>, -{ -} - -impl<'tcx, 's, I: IntoIterator> EarlyBinder -where - I::Item: Deref, - ::Target: Copy + TypeFoldable>, -{ - pub fn iter_instantiated_copied( - self, - tcx: TyCtxt<'tcx>, - args: &'s [GenericArg<'tcx>], - ) -> IterInstantiatedCopied<'s, 'tcx, I> { - IterInstantiatedCopied { it: self.value.into_iter(), tcx, args } - } - - /// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity), - /// but on an iterator of values that deref to a `TypeFoldable`. - pub fn instantiate_identity_iter_copied( - self, - ) -> impl Iterator::Target> { - self.value.into_iter().map(|v| *v) - } -} - -pub struct IterInstantiatedCopied<'a, 'tcx, I: IntoIterator> { - it: I::IntoIter, - tcx: TyCtxt<'tcx>, - args: &'a [GenericArg<'tcx>], -} - -impl<'tcx, I: IntoIterator> Iterator for IterInstantiatedCopied<'_, 'tcx, I> -where - I::Item: Deref, - ::Target: Copy + TypeFoldable>, -{ - type Item = ::Target; - - fn next(&mut self) -> Option { - self.it.next().map(|value| EarlyBinder { value: *value }.instantiate(self.tcx, self.args)) - } - - fn size_hint(&self) -> (usize, Option) { - self.it.size_hint() - } -} - -impl<'tcx, I: IntoIterator> DoubleEndedIterator for IterInstantiatedCopied<'_, 'tcx, I> -where - I::IntoIter: DoubleEndedIterator, - I::Item: Deref, - ::Target: Copy + TypeFoldable>, -{ - fn next_back(&mut self) -> Option { - self.it - .next_back() - .map(|value| EarlyBinder { value: *value }.instantiate(self.tcx, self.args)) - } -} - -impl<'tcx, I: IntoIterator> ExactSizeIterator for IterInstantiatedCopied<'_, 'tcx, I> -where - I::IntoIter: ExactSizeIterator, - I::Item: Deref, - ::Target: Copy + TypeFoldable>, -{ -} - -pub struct EarlyBinderIter { - t: T, -} - -impl EarlyBinder { - pub fn transpose_iter(self) -> EarlyBinderIter { - EarlyBinderIter { t: self.value.into_iter() } - } -} - -impl Iterator for EarlyBinderIter { - type Item = EarlyBinder; - - fn next(&mut self) -> Option { - self.t.next().map(|value| EarlyBinder { value }) - } - - fn size_hint(&self) -> (usize, Option) { - self.t.size_hint() - } -} - -impl<'tcx, T: TypeFoldable>> ty::EarlyBinder { - pub fn instantiate(self, tcx: TyCtxt<'tcx>, args: &[GenericArg<'tcx>]) -> T { - let mut folder = ArgFolder { tcx, args, binders_passed: 0 }; - self.value.fold_with(&mut folder) - } - - /// Makes the identity replacement `T0 => T0, ..., TN => TN`. - /// Conceptually, this converts universally bound variables into placeholders - /// when inside of a given item. - /// - /// For example, consider `for fn foo(){ .. }`: - /// - Outside of `foo`, `T` is bound (represented by the presence of `EarlyBinder`). - /// - Inside of the body of `foo`, we treat `T` as a placeholder by calling - /// `instantiate_identity` to discharge the `EarlyBinder`. - pub fn instantiate_identity(self) -> T { - self.value - } - - /// Returns the inner value, but only if it contains no bound vars. - pub fn no_bound_vars(self) -> Option { - if !self.value.has_param() { Some(self.value) } else { None } - } -} - -/////////////////////////////////////////////////////////////////////////// -// The actual instantiation engine itself is a type folder. - -struct ArgFolder<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - args: &'a [GenericArg<'tcx>], - - /// Number of region binders we have passed through while doing the instantiation - binders_passed: u32, -} - -impl<'a, 'tcx> TypeFolder> for ArgFolder<'a, 'tcx> { - #[inline] - fn interner(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_binder>>( - &mut self, - t: ty::Binder<'tcx, T>, - ) -> ty::Binder<'tcx, T> { - self.binders_passed += 1; - let t = t.super_fold_with(self); - self.binders_passed -= 1; - t - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - #[cold] - #[inline(never)] - fn region_param_out_of_range(data: ty::EarlyParamRegion, args: &[GenericArg<'_>]) -> ! { - bug!( - "Region parameter out of range when instantiating in region {} (index={}, args = {:?})", - data.name, - data.index, - args, - ) - } - - #[cold] - #[inline(never)] - fn region_param_invalid(data: ty::EarlyParamRegion, other: GenericArgKind<'_>) -> ! { - bug!( - "Unexpected parameter {:?} when instantiating in region {} (index={})", - other, - data.name, - data.index - ) - } - - // Note: This routine only handles regions that are bound on - // type declarations and other outer declarations, not those - // bound in *fn types*. Region instantiation of the bound - // regions that appear in a function signature is done using - // the specialized routine `ty::replace_late_regions()`. - match *r { - ty::ReEarlyParam(data) => { - let rk = self.args.get(data.index as usize).map(|k| k.unpack()); - match rk { - Some(GenericArgKind::Lifetime(lt)) => self.shift_region_through_binders(lt), - Some(other) => region_param_invalid(data, other), - None => region_param_out_of_range(data, self.args), - } - } - ty::ReBound(..) - | ty::ReLateParam(_) - | ty::ReStatic - | ty::RePlaceholder(_) - | ty::ReErased - | ty::ReError(_) => r, - ty::ReVar(_) => bug!("unexpected region: {r:?}"), - } - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !t.has_param() { - return t; - } - - match *t.kind() { - ty::Param(p) => self.ty_for_param(p, t), - _ => t.super_fold_with(self), - } - } - - fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { - if let ty::ConstKind::Param(p) = c.kind() { - self.const_for_param(p, c) - } else { - c.super_fold_with(self) - } - } -} - -impl<'a, 'tcx> ArgFolder<'a, 'tcx> { - fn ty_for_param(&self, p: ty::ParamTy, source_ty: Ty<'tcx>) -> Ty<'tcx> { - // Look up the type in the args. It really should be in there. - let opt_ty = self.args.get(p.index as usize).map(|k| k.unpack()); - let ty = match opt_ty { - Some(GenericArgKind::Type(ty)) => ty, - Some(kind) => self.type_param_expected(p, source_ty, kind), - None => self.type_param_out_of_range(p, source_ty), - }; - - self.shift_vars_through_binders(ty) - } - - #[cold] - #[inline(never)] - fn type_param_expected(&self, p: ty::ParamTy, ty: Ty<'tcx>, kind: GenericArgKind<'tcx>) -> ! { - bug!( - "expected type for `{:?}` ({:?}/{}) but found {:?} when instantiating, args={:?}", - p, - ty, - p.index, - kind, - self.args, - ) - } - - #[cold] - #[inline(never)] - fn type_param_out_of_range(&self, p: ty::ParamTy, ty: Ty<'tcx>) -> ! { - bug!( - "type parameter `{:?}` ({:?}/{}) out of range when instantiating, args={:?}", - p, - ty, - p.index, - self.args, - ) - } - - fn const_for_param(&self, p: ParamConst, source_ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - // Look up the const in the args. It really should be in there. - let opt_ct = self.args.get(p.index as usize).map(|k| k.unpack()); - let ct = match opt_ct { - Some(GenericArgKind::Const(ct)) => ct, - Some(kind) => self.const_param_expected(p, source_ct, kind), - None => self.const_param_out_of_range(p, source_ct), - }; - - self.shift_vars_through_binders(ct) - } - - #[cold] - #[inline(never)] - fn const_param_expected( - &self, - p: ty::ParamConst, - ct: ty::Const<'tcx>, - kind: GenericArgKind<'tcx>, - ) -> ! { - bug!( - "expected const for `{:?}` ({:?}/{}) but found {:?} when instantiating args={:?}", - p, - ct, - p.index, - kind, - self.args, - ) - } - - #[cold] - #[inline(never)] - fn const_param_out_of_range(&self, p: ty::ParamConst, ct: ty::Const<'tcx>) -> ! { - bug!( - "const parameter `{:?}` ({:?}/{}) out of range when instantiating args={:?}", - p, - ct, - p.index, - self.args, - ) - } - - /// It is sometimes necessary to adjust the De Bruijn indices during instantiation. This occurs - /// when we are instantating a type with escaping bound vars into a context where we have - /// passed through binders. That's quite a mouthful. Let's see an example: - /// - /// ``` - /// type Func = fn(A); - /// type MetaFunc = for<'a> fn(Func<&'a i32>); - /// ``` - /// - /// The type `MetaFunc`, when fully expanded, will be - /// ```ignore (illustrative) - /// for<'a> fn(fn(&'a i32)) - /// // ^~ ^~ ^~~ - /// // | | | - /// // | | DebruijnIndex of 2 - /// // Binders - /// ``` - /// Here the `'a` lifetime is bound in the outer function, but appears as an argument of the - /// inner one. Therefore, that appearance will have a DebruijnIndex of 2, because we must skip - /// over the inner binder (remember that we count De Bruijn indices from 1). However, in the - /// definition of `MetaFunc`, the binder is not visible, so the type `&'a i32` will have a - /// De Bruijn index of 1. It's only during the instantiation that we can see we must increase the - /// depth by 1 to account for the binder that we passed through. - /// - /// As a second example, consider this twist: - /// - /// ``` - /// type FuncTuple = (A,fn(A)); - /// type MetaFuncTuple = for<'a> fn(FuncTuple<&'a i32>); - /// ``` - /// - /// Here the final type will be: - /// ```ignore (illustrative) - /// for<'a> fn((&'a i32, fn(&'a i32))) - /// // ^~~ ^~~ - /// // | | - /// // DebruijnIndex of 1 | - /// // DebruijnIndex of 2 - /// ``` - /// As indicated in the diagram, here the same type `&'a i32` is instantiated once, but in the - /// first case we do not increase the De Bruijn index and in the second case we do. The reason - /// is that only in the second case have we passed through a fn binder. - fn shift_vars_through_binders>>(&self, val: T) -> T { - debug!( - "shift_vars(val={:?}, binders_passed={:?}, has_escaping_bound_vars={:?})", - val, - self.binders_passed, - val.has_escaping_bound_vars() - ); - - if self.binders_passed == 0 || !val.has_escaping_bound_vars() { - return val; - } - - let result = ty::fold::shift_vars(TypeFolder::interner(self), val, self.binders_passed); - debug!("shift_vars: shifted result = {:?}", result); - - result - } - - fn shift_region_through_binders(&self, region: ty::Region<'tcx>) -> ty::Region<'tcx> { - if self.binders_passed == 0 || !region.has_escaping_bound_vars() { - return region; - } - ty::fold::shift_region(self.tcx, region, self.binders_passed) - } -} - /// Stores the user-given args to reach some fully qualified path /// (e.g., `::Item` or `::Item`). #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 04655c5d20ba..6467689a8aa1 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -6,6 +6,7 @@ use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::symbol::{kw, Symbol}; use rustc_span::Span; +use tracing::instrument; use super::{Clause, InstantiatedPredicates, ParamConst, ParamTy, Ty, TyCtxt}; @@ -65,7 +66,7 @@ pub struct GenericParamDef { impl GenericParamDef { pub fn to_early_bound_region_data(&self) -> ty::EarlyParamRegion { if let GenericParamDefKind::Lifetime = self.kind { - ty::EarlyParamRegion { def_id: self.def_id, index: self.index, name: self.name } + ty::EarlyParamRegion { index: self.index, name: self.name } } else { bug!("cannot convert a non-lifetime parameter def to an early bound region") } @@ -87,7 +88,7 @@ impl GenericParamDef { pub fn default_value<'tcx>( &self, tcx: TyCtxt<'tcx>, - ) -> Option>> { + ) -> Option>> { match self.kind { GenericParamDefKind::Type { has_default, .. } if has_default => { Some(tcx.type_of(self.def_id).map_bound(|t| t.into())) @@ -99,19 +100,11 @@ impl GenericParamDef { } } - pub fn to_error<'tcx>( - &self, - tcx: TyCtxt<'tcx>, - preceding_args: &[ty::GenericArg<'tcx>], - ) -> ty::GenericArg<'tcx> { + pub fn to_error<'tcx>(&self, tcx: TyCtxt<'tcx>) -> ty::GenericArg<'tcx> { match &self.kind { ty::GenericParamDefKind::Lifetime => ty::Region::new_error_misc(tcx).into(), ty::GenericParamDefKind::Type { .. } => Ty::new_misc_error(tcx).into(), - ty::GenericParamDefKind::Const { .. } => ty::Const::new_misc_error( - tcx, - tcx.type_of(self.def_id).instantiate(tcx, preceding_args), - ) - .into(), + ty::GenericParamDefKind::Const { .. } => ty::Const::new_misc_error(tcx).into(), } } } @@ -244,20 +237,6 @@ impl<'tcx> Generics { } } - /// Returns the `GenericParamDef` with the given index if available. - pub fn opt_param_at( - &'tcx self, - param_index: usize, - tcx: TyCtxt<'tcx>, - ) -> Option<&'tcx GenericParamDef> { - if let Some(index) = param_index.checked_sub(self.parent_count) { - self.own_params.get(index) - } else { - tcx.generics_of(self.parent.expect("parent_count > 0 but no parent?")) - .opt_param_at(param_index, tcx) - } - } - pub fn params_to(&'tcx self, param_index: usize, tcx: TyCtxt<'tcx>) -> &'tcx [GenericParamDef] { if let Some(index) = param_index.checked_sub(self.parent_count) { &self.own_params[..index] @@ -289,20 +268,6 @@ impl<'tcx> Generics { } } - /// Returns the `GenericParamDef` associated with this `ParamTy` if it belongs to this - /// `Generics`. - pub fn opt_type_param( - &'tcx self, - param: ParamTy, - tcx: TyCtxt<'tcx>, - ) -> Option<&'tcx GenericParamDef> { - let param = self.opt_param_at(param.index as usize, tcx)?; - match param.kind { - GenericParamDefKind::Type { .. } => Some(param), - _ => None, - } - } - /// Returns the `GenericParamDef` associated with this `ParamConst`. pub fn const_param(&'tcx self, param: ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef { let param = self.param_at(param.index as usize, tcx); @@ -391,6 +356,14 @@ impl<'tcx> Generics { } false } + + pub fn is_empty(&'tcx self) -> bool { + self.count() == 0 + } + + pub fn is_own_empty(&'tcx self) -> bool { + self.own_params.is_empty() + } } /// Bounds on generics. @@ -419,6 +392,10 @@ impl<'tcx> GenericPredicates<'tcx> { EarlyBinder::bind(self.predicates).iter_instantiated_copied(tcx, args) } + pub fn instantiate_own_identity(&self) -> impl Iterator, Span)> { + EarlyBinder::bind(self.predicates).instantiate_identity_iter_copied() + } + #[instrument(level = "debug", skip(self, tcx))] fn instantiate_into( &self, diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs index ef7a7a99ff7e..efcf428c2136 100644 --- a/compiler/rustc_middle/src/ty/impls_ty.rs +++ b/compiler/rustc_middle/src/ty/impls_ty.rs @@ -12,6 +12,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHas use rustc_query_system::ich::StableHashingContext; use std::cell::RefCell; use std::ptr; +use tracing::trace; impl<'a, 'tcx, H, T> HashStable> for &'tcx ty::list::RawList where diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs index e3eea83ba49a..54b8507babfa 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs @@ -1,5 +1,6 @@ use rustc_macros::HashStable; use smallvec::SmallVec; +use tracing::instrument; use crate::ty::context::TyCtxt; use crate::ty::{self, DefId, OpaqueTypeKey, ParamEnv, Ty}; diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index da5d57db5bef..afdf2cbc7266 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -48,6 +48,7 @@ use crate::ty::context::TyCtxt; use crate::ty::{self, DefId, Ty, TypeVisitableExt, VariantDef, Visibility}; use rustc_type_ir::TyKind::*; +use tracing::instrument; pub mod inhabited_predicate; diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index a08fde976bc1..efaf9c7231bb 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -14,14 +14,15 @@ use rustc_macros::{ use rustc_middle::ty::normalize_erasing_regions::NormalizationError; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::Symbol; +use tracing::{debug, instrument}; use std::assert_matches::assert_matches; use std::fmt; -/// A monomorphized `InstanceDef`. +/// An `InstanceKind` along with the args that are needed to substitute the instance. /// /// Monomorphization happens on-the-fly and no monomorphized MIR is ever created. Instead, this type -/// simply couples a potentially generic `InstanceDef` with some args, and codegen and const eval +/// simply couples a potentially generic `InstanceKind` with some args, and codegen and const eval /// will do all required instantiations as they run. /// /// Note: the `Lift` impl is currently not used by rustc, but is used by @@ -29,7 +30,7 @@ use std::fmt; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable, Lift, TypeFoldable, TypeVisitable)] pub struct Instance<'tcx> { - pub def: InstanceDef<'tcx>, + pub def: InstanceKind<'tcx>, pub args: GenericArgsRef<'tcx>, } @@ -57,7 +58,7 @@ pub enum ReifyReason { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable, Lift)] -pub enum InstanceDef<'tcx> { +pub enum InstanceKind<'tcx> { /// A user-defined callable item. /// /// This includes: @@ -68,7 +69,7 @@ pub enum InstanceDef<'tcx> { /// An intrinsic `fn` item (with `"rust-intrinsic"` or `"platform-intrinsic"` ABI). /// - /// Alongside `Virtual`, this is the only `InstanceDef` that does not have its own callable MIR. + /// Alongside `Virtual`, this is the only `InstanceKind` that does not have its own callable MIR. /// Instead, codegen and const eval "magically" evaluate calls to intrinsics purely in the /// caller. Intrinsic(DefId), @@ -84,7 +85,7 @@ pub enum InstanceDef<'tcx> { /// /// One example is `::fn`, where the shim contains /// a virtual call, which codegen supports only via a direct call to the - /// `::fn` instance (an `InstanceDef::Virtual`). + /// `::fn` instance (an `InstanceKind::Virtual`). /// /// Another example is functions annotated with `#[track_caller]`, which /// must have their implicit caller location argument populated for a call. @@ -106,7 +107,7 @@ pub enum InstanceDef<'tcx> { /// Dynamic dispatch to `::fn`. /// - /// This `InstanceDef` does not have callable MIR. Calls to `Virtual` instances must be + /// This `InstanceKind` does not have callable MIR. Calls to `Virtual` instances must be /// codegen'd as virtual calls through the vtable. /// /// If this is reified to a `fn` pointer, a `ReifyShim` is used (see `ReifyShim` above for more @@ -215,59 +216,61 @@ impl<'tcx> Instance<'tcx> { } match self.def { - InstanceDef::Item(def) => tcx + InstanceKind::Item(def) => tcx .upstream_monomorphizations_for(def) .and_then(|monos| monos.get(&self.args).cloned()), - InstanceDef::DropGlue(_, Some(_)) | InstanceDef::AsyncDropGlueCtorShim(_, _) => { - tcx.upstream_drop_glue_for(self.args) + InstanceKind::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.args), + InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) => { + tcx.upstream_async_drop_glue_for(self.args) } _ => None, } } } -impl<'tcx> InstanceDef<'tcx> { +impl<'tcx> InstanceKind<'tcx> { #[inline] pub fn def_id(self) -> DefId { 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) - | InstanceDef::ThreadLocalShim(def_id) - | InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ } - | ty::InstanceDef::ConstructCoroutineInClosureShim { + InstanceKind::Item(def_id) + | InstanceKind::VTableShim(def_id) + | InstanceKind::ReifyShim(def_id, _) + | InstanceKind::FnPtrShim(def_id, _) + | InstanceKind::Virtual(def_id, _) + | InstanceKind::Intrinsic(def_id) + | InstanceKind::ThreadLocalShim(def_id) + | InstanceKind::ClosureOnceShim { call_once: def_id, track_caller: _ } + | ty::InstanceKind::ConstructCoroutineInClosureShim { coroutine_closure_def_id: def_id, receiver_by_ref: _, } - | ty::InstanceDef::CoroutineKindShim { coroutine_def_id: def_id } - | InstanceDef::DropGlue(def_id, _) - | InstanceDef::CloneShim(def_id, _) - | InstanceDef::FnPtrAddrShim(def_id, _) - | InstanceDef::AsyncDropGlueCtorShim(def_id, _) => def_id, + | ty::InstanceKind::CoroutineKindShim { coroutine_def_id: def_id } + | InstanceKind::DropGlue(def_id, _) + | InstanceKind::CloneShim(def_id, _) + | InstanceKind::FnPtrAddrShim(def_id, _) + | InstanceKind::AsyncDropGlueCtorShim(def_id, _) => def_id, } } /// Returns the `DefId` of instances which might not require codegen locally. pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option { match self { - ty::InstanceDef::Item(def) => Some(def), - ty::InstanceDef::DropGlue(def_id, Some(_)) - | InstanceDef::AsyncDropGlueCtorShim(def_id, _) - | InstanceDef::ThreadLocalShim(def_id) => Some(def_id), - InstanceDef::VTableShim(..) - | InstanceDef::ReifyShim(..) - | InstanceDef::FnPtrShim(..) - | InstanceDef::Virtual(..) - | InstanceDef::Intrinsic(..) - | InstanceDef::ClosureOnceShim { .. } - | ty::InstanceDef::ConstructCoroutineInClosureShim { .. } - | ty::InstanceDef::CoroutineKindShim { .. } - | InstanceDef::DropGlue(..) - | InstanceDef::CloneShim(..) - | InstanceDef::FnPtrAddrShim(..) => None, + ty::InstanceKind::Item(def) => Some(def), + ty::InstanceKind::DropGlue(def_id, Some(_)) + | InstanceKind::AsyncDropGlueCtorShim(def_id, Some(_)) + | InstanceKind::ThreadLocalShim(def_id) => Some(def_id), + InstanceKind::VTableShim(..) + | InstanceKind::ReifyShim(..) + | InstanceKind::FnPtrShim(..) + | InstanceKind::Virtual(..) + | InstanceKind::Intrinsic(..) + | InstanceKind::ClosureOnceShim { .. } + | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } + | ty::InstanceKind::CoroutineKindShim { .. } + | InstanceKind::DropGlue(..) + | InstanceKind::AsyncDropGlueCtorShim(..) + | InstanceKind::CloneShim(..) + | InstanceKind::FnPtrAddrShim(..) => None, } } @@ -288,10 +291,10 @@ impl<'tcx> InstanceDef<'tcx> { pub fn requires_inline(&self, tcx: TyCtxt<'tcx>) -> bool { use rustc_hir::definitions::DefPathData; let def_id = match *self { - ty::InstanceDef::Item(def) => def, - ty::InstanceDef::DropGlue(_, Some(_)) => return false, - ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(_)) => return false, - ty::InstanceDef::ThreadLocalShim(_) => return false, + ty::InstanceKind::Item(def) => def, + ty::InstanceKind::DropGlue(_, Some(_)) => return false, + ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) => return false, + ty::InstanceKind::ThreadLocalShim(_) => return false, _ => return true, }; matches!( @@ -311,7 +314,9 @@ impl<'tcx> InstanceDef<'tcx> { if self.requires_inline(tcx) { return true; } - if let ty::InstanceDef::DropGlue(.., Some(ty)) = *self { + if let ty::InstanceKind::DropGlue(.., Some(ty)) + | ty::InstanceKind::AsyncDropGlueCtorShim(.., Some(ty)) = *self + { // Drop glue generally wants to be instantiated at every codegen // unit, but without an #[inline] hint. We should make this // available to normal end-users. @@ -326,12 +331,17 @@ impl<'tcx> InstanceDef<'tcx> { // drops of `Option::None` before LTO. We also respect the intent of // `#[inline]` on `Drop::drop` implementations. return ty.ty_adt_def().map_or(true, |adt_def| { - adt_def - .destructor(tcx) - .map_or_else(|| adt_def.is_enum(), |dtor| tcx.cross_crate_inlinable(dtor.did)) + match *self { + ty::InstanceKind::DropGlue(..) => adt_def.destructor(tcx).map(|dtor| dtor.did), + ty::InstanceKind::AsyncDropGlueCtorShim(..) => { + adt_def.async_destructor(tcx).map(|dtor| dtor.ctor) + } + _ => unreachable!(), + } + .map_or_else(|| adt_def.is_enum(), |did| tcx.cross_crate_inlinable(did)) }); } - if let ty::InstanceDef::ThreadLocalShim(..) = *self { + if let ty::InstanceKind::ThreadLocalShim(..) = *self { return false; } tcx.cross_crate_inlinable(self.def_id()) @@ -339,10 +349,10 @@ impl<'tcx> InstanceDef<'tcx> { pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool { match *self { - InstanceDef::Item(def_id) | InstanceDef::Virtual(def_id, _) => { + InstanceKind::Item(def_id) | InstanceKind::Virtual(def_id, _) => { tcx.body_codegen_attrs(def_id).flags.contains(CodegenFnAttrFlags::TRACK_CALLER) } - InstanceDef::ClosureOnceShim { call_once: _, track_caller } => track_caller, + InstanceKind::ClosureOnceShim { call_once: _, track_caller } => track_caller, _ => false, } } @@ -355,22 +365,22 @@ impl<'tcx> InstanceDef<'tcx> { /// body should perform necessary instantiations. pub fn has_polymorphic_mir_body(&self) -> bool { match *self { - InstanceDef::CloneShim(..) - | InstanceDef::ThreadLocalShim(..) - | InstanceDef::FnPtrAddrShim(..) - | InstanceDef::FnPtrShim(..) - | InstanceDef::DropGlue(_, Some(_)) - | InstanceDef::AsyncDropGlueCtorShim(_, Some(_)) => false, - InstanceDef::ClosureOnceShim { .. } - | InstanceDef::ConstructCoroutineInClosureShim { .. } - | InstanceDef::CoroutineKindShim { .. } - | InstanceDef::DropGlue(..) - | InstanceDef::AsyncDropGlueCtorShim(..) - | InstanceDef::Item(_) - | InstanceDef::Intrinsic(..) - | InstanceDef::ReifyShim(..) - | InstanceDef::Virtual(..) - | InstanceDef::VTableShim(..) => true, + InstanceKind::CloneShim(..) + | InstanceKind::ThreadLocalShim(..) + | InstanceKind::FnPtrAddrShim(..) + | InstanceKind::FnPtrShim(..) + | InstanceKind::DropGlue(_, Some(_)) + | InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) => false, + InstanceKind::ClosureOnceShim { .. } + | InstanceKind::ConstructCoroutineInClosureShim { .. } + | InstanceKind::CoroutineKindShim { .. } + | InstanceKind::DropGlue(..) + | InstanceKind::AsyncDropGlueCtorShim(..) + | InstanceKind::Item(_) + | InstanceKind::Intrinsic(..) + | InstanceKind::ReifyShim(..) + | InstanceKind::Virtual(..) + | InstanceKind::VTableShim(..) => true, } } } @@ -394,24 +404,24 @@ fn fmt_instance( })?; match instance.def { - InstanceDef::Item(_) => Ok(()), - InstanceDef::VTableShim(_) => write!(f, " - shim(vtable)"), - InstanceDef::ReifyShim(_, None) => write!(f, " - shim(reify)"), - InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr)) => write!(f, " - shim(reify-fnptr)"), - InstanceDef::ReifyShim(_, Some(ReifyReason::Vtable)) => write!(f, " - shim(reify-vtable)"), - InstanceDef::ThreadLocalShim(_) => write!(f, " - shim(tls)"), - InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), - InstanceDef::Virtual(_, num) => write!(f, " - virtual#{num}"), - InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({ty})"), - InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"), - InstanceDef::ConstructCoroutineInClosureShim { .. } => write!(f, " - shim"), - InstanceDef::CoroutineKindShim { .. } => write!(f, " - shim"), - InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"), - InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"), - InstanceDef::CloneShim(_, ty) => write!(f, " - shim({ty})"), - InstanceDef::FnPtrAddrShim(_, ty) => write!(f, " - shim({ty})"), - InstanceDef::AsyncDropGlueCtorShim(_, None) => write!(f, " - shim(None)"), - InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) => write!(f, " - shim(Some({ty}))"), + InstanceKind::Item(_) => Ok(()), + InstanceKind::VTableShim(_) => write!(f, " - shim(vtable)"), + InstanceKind::ReifyShim(_, None) => write!(f, " - shim(reify)"), + InstanceKind::ReifyShim(_, Some(ReifyReason::FnPtr)) => write!(f, " - shim(reify-fnptr)"), + InstanceKind::ReifyShim(_, Some(ReifyReason::Vtable)) => write!(f, " - shim(reify-vtable)"), + InstanceKind::ThreadLocalShim(_) => write!(f, " - shim(tls)"), + InstanceKind::Intrinsic(_) => write!(f, " - intrinsic"), + InstanceKind::Virtual(_, num) => write!(f, " - virtual#{num}"), + InstanceKind::FnPtrShim(_, ty) => write!(f, " - shim({ty})"), + InstanceKind::ClosureOnceShim { .. } => write!(f, " - shim"), + InstanceKind::ConstructCoroutineInClosureShim { .. } => write!(f, " - shim"), + InstanceKind::CoroutineKindShim { .. } => write!(f, " - shim"), + InstanceKind::DropGlue(_, None) => write!(f, " - shim(None)"), + InstanceKind::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"), + InstanceKind::CloneShim(_, ty) => write!(f, " - shim({ty})"), + InstanceKind::FnPtrAddrShim(_, ty) => write!(f, " - shim({ty})"), + InstanceKind::AsyncDropGlueCtorShim(_, None) => write!(f, " - shim(None)"), + InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)) => write!(f, " - shim(Some({ty}))"), } } @@ -433,9 +443,9 @@ impl<'tcx> Instance<'tcx> { pub fn new(def_id: DefId, args: GenericArgsRef<'tcx>) -> Instance<'tcx> { assert!( !args.has_escaping_bound_vars(), - "args of instance {def_id:?} not normalized for codegen: {args:?}" + "args of instance {def_id:?} has escaping bound vars: {args:?}" ); - Instance { def: InstanceDef::Item(def_id), args } + Instance { def: InstanceKind::Item(def_id), args } } pub fn mono(tcx: TyCtxt<'tcx>, def_id: DefId) -> Instance<'tcx> { @@ -525,13 +535,13 @@ impl<'tcx> Instance<'tcx> { let reason = tcx.sess.is_sanitizer_kcfi_enabled().then_some(ReifyReason::FnPtr); Instance::resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| { match resolved.def { - InstanceDef::Item(def) if resolved.def.requires_caller_location(tcx) => { + InstanceKind::Item(def) if resolved.def.requires_caller_location(tcx) => { debug!(" => fn pointer created for function with #[track_caller]"); - resolved.def = InstanceDef::ReifyShim(def, reason); + resolved.def = InstanceKind::ReifyShim(def, reason); } - InstanceDef::Virtual(def_id, _) => { + InstanceKind::Virtual(def_id, _) => { debug!(" => fn pointer created for virtual call"); - resolved.def = InstanceDef::ReifyShim(def_id, reason); + resolved.def = InstanceKind::ReifyShim(def_id, reason); } // Reify `Trait::method` implementations if KCFI is enabled // FIXME(maurer) only reify it if it is a vtable-safe function @@ -543,7 +553,7 @@ impl<'tcx> Instance<'tcx> { { // If this function could also go in a vtable, we need to `ReifyShim` it with // KCFI because it can only attach one type per function. - resolved.def = InstanceDef::ReifyShim(resolved.def_id(), reason) + resolved.def = InstanceKind::ReifyShim(resolved.def_id(), reason) } // Reify `::call`-like method implementations if KCFI is enabled _ if tcx.sess.is_sanitizer_kcfi_enabled() @@ -552,7 +562,7 @@ impl<'tcx> Instance<'tcx> { // Reroute through a reify via the *unresolved* instance. The resolved one can't // be directly reified because it's closure-like. The reify can handle the // unresolved instance. - resolved = Instance { def: InstanceDef::ReifyShim(def_id, reason), args } + resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args } } _ => {} } @@ -574,12 +584,12 @@ impl<'tcx> Instance<'tcx> { && tcx.generics_of(def_id).has_self; if is_vtable_shim { debug!(" => associated item with unsizeable self: Self"); - Some(Instance { def: InstanceDef::VTableShim(def_id), args }) + Some(Instance { def: InstanceKind::VTableShim(def_id), args }) } else { let reason = tcx.sess.is_sanitizer_kcfi_enabled().then_some(ReifyReason::Vtable); Instance::resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| { match resolved.def { - InstanceDef::Item(def) => { + InstanceKind::Item(def) => { // We need to generate a shim when we cannot guarantee that // the caller of a trait object method will be aware of // `#[track_caller]` - this ensures that the caller @@ -613,18 +623,18 @@ impl<'tcx> Instance<'tcx> { // Create a shim for the `FnOnce/FnMut/Fn` method we are calling // - unlike functions, invoking a closure always goes through a // trait. - resolved = Instance { def: InstanceDef::ReifyShim(def_id, reason), args }; + resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args }; } else { debug!( " => vtable fn pointer created for function with #[track_caller]: {:?}", def ); - resolved.def = InstanceDef::ReifyShim(def, reason); + resolved.def = InstanceKind::ReifyShim(def, reason); } } } - InstanceDef::Virtual(def_id, _) => { + InstanceKind::Virtual(def_id, _) => { debug!(" => vtable fn pointer created for virtual call"); - resolved.def = InstanceDef::ReifyShim(def_id, reason) + resolved.def = InstanceKind::ReifyShim(def_id, reason) } _ => {} } @@ -675,7 +685,7 @@ impl<'tcx> Instance<'tcx> { .def_id; let track_caller = tcx.codegen_fn_attrs(closure_did).flags.contains(CodegenFnAttrFlags::TRACK_CALLER); - let def = ty::InstanceDef::ClosureOnceShim { call_once, track_caller }; + let def = ty::InstanceKind::ClosureOnceShim { call_once, track_caller }; let self_ty = Ty::new_closure(tcx, closure_did, args); @@ -698,26 +708,25 @@ impl<'tcx> Instance<'tcx> { }; let coroutine_kind = tcx.coroutine_kind(coroutine_def_id).unwrap(); - let lang_items = tcx.lang_items(); - let coroutine_callable_item = if Some(trait_id) == lang_items.future_trait() { + let coroutine_callable_item = if tcx.is_lang_item(trait_id, LangItem::Future) { assert_matches!( coroutine_kind, hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) ); hir::LangItem::FuturePoll - } else if Some(trait_id) == lang_items.iterator_trait() { + } else if tcx.is_lang_item(trait_id, LangItem::Iterator) { assert_matches!( coroutine_kind, hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) ); hir::LangItem::IteratorNext - } else if Some(trait_id) == lang_items.async_iterator_trait() { + } else if tcx.is_lang_item(trait_id, LangItem::AsyncIterator) { assert_matches!( coroutine_kind, hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) ); hir::LangItem::AsyncIteratorPollNext - } else if Some(trait_id) == lang_items.coroutine_trait() { + } else if tcx.is_lang_item(trait_id, LangItem::Coroutine) { assert_matches!(coroutine_kind, hir::CoroutineKind::Coroutine(_)); hir::LangItem::CoroutineResume } else { @@ -733,10 +742,10 @@ impl<'tcx> Instance<'tcx> { // If the closure's kind ty disagrees with the identity closure's kind ty, // then this must be a coroutine generated by one of the `ConstructCoroutineInClosureShim`s. if args.as_coroutine().kind_ty() == id_args.as_coroutine().kind_ty() { - Some(Instance { def: ty::InstanceDef::Item(coroutine_def_id), args }) + Some(Instance { def: ty::InstanceKind::Item(coroutine_def_id), args }) } else { Some(Instance { - def: ty::InstanceDef::CoroutineKindShim { coroutine_def_id }, + def: ty::InstanceKind::CoroutineKindShim { coroutine_def_id }, args, }) } @@ -749,7 +758,7 @@ impl<'tcx> Instance<'tcx> { } } - /// Depending on the kind of `InstanceDef`, the MIR body associated with an + /// Depending on the kind of `InstanceKind`, the MIR body associated with an /// instance is expressed in terms of the generic parameters of `self.def_id()`, and in other /// cases the MIR body is expressed in terms of the types found in the generic parameter array. /// In the former case, we want to instantiate those generic types and replace them with the @@ -763,7 +772,7 @@ impl<'tcx> Instance<'tcx> { self.def.has_polymorphic_mir_body().then_some(self.args) } - pub fn instantiate_mir(&self, tcx: TyCtxt<'tcx>, v: EarlyBinder<&T>) -> T + pub fn instantiate_mir(&self, tcx: TyCtxt<'tcx>, v: EarlyBinder<'tcx, &T>) -> T where T: TypeFoldable> + Copy, { @@ -781,7 +790,7 @@ impl<'tcx> Instance<'tcx> { &self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - v: EarlyBinder, + v: EarlyBinder<'tcx, T>, ) -> T where T: TypeFoldable>, @@ -799,7 +808,7 @@ impl<'tcx> Instance<'tcx> { &self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - v: EarlyBinder, + v: EarlyBinder<'tcx, T>, ) -> Result> where T: TypeFoldable>, @@ -832,7 +841,7 @@ impl<'tcx> Instance<'tcx> { fn polymorphize<'tcx>( tcx: TyCtxt<'tcx>, - instance: ty::InstanceDef<'tcx>, + instance: ty::InstanceKind<'tcx>, args: GenericArgsRef<'tcx>, ) -> GenericArgsRef<'tcx> { debug!("polymorphize({:?}, {:?})", instance, args); @@ -873,7 +882,7 @@ fn polymorphize<'tcx>( match *ty.kind() { ty::Closure(def_id, args) => { let polymorphized_args = - polymorphize(self.tcx, ty::InstanceDef::Item(def_id), args); + polymorphize(self.tcx, ty::InstanceKind::Item(def_id), args); if args == polymorphized_args { ty } else { @@ -882,7 +891,7 @@ fn polymorphize<'tcx>( } ty::Coroutine(def_id, args) => { let polymorphized_args = - polymorphize(self.tcx, ty::InstanceDef::Item(def_id), args); + polymorphize(self.tcx, ty::InstanceKind::Item(def_id), args); if args == polymorphized_args { ty } else { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index cf701f837d89..3d397b6b37e1 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -2,13 +2,14 @@ use crate::error::UnsupportedFnAbi; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::query::TyCtxtAt; use crate::ty::normalize_erasing_regions::NormalizationError; -use crate::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use crate::ty::{self, CoroutineArgsExt, Ty, TyCtxt, TypeVisitableExt}; use rustc_error_messages::DiagMessage; use rustc_errors::{ - Diag, DiagArgValue, DiagCtxt, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, + Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, }; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::LangItem; use rustc_index::IndexVec; use rustc_macros::{extension, HashStable, TyDecodable, TyEncodable}; use rustc_session::config::OptLevel; @@ -19,6 +20,7 @@ use rustc_target::abi::*; use rustc_target::spec::{ abi::Abi as SpecAbi, HasTargetSpec, HasWasmCAbiOpt, PanicStrategy, Target, WasmCAbi, }; +use tracing::debug; use std::borrow::Cow; use std::cmp; @@ -384,9 +386,7 @@ impl<'tcx> SizeSkeleton<'tcx> { ), } } - ty::Array(inner, len) - if len.ty() == tcx.types.usize && tcx.features().transmute_generic_consts => - { + ty::Array(inner, len) if tcx.features().transmute_generic_consts => { let len_eval = len.try_eval_target_usize(tcx, param_env); if len_eval == Some(0) { return Ok(SizeSkeleton::Known(Size::from_bytes(0))); @@ -826,25 +826,14 @@ where }); } - let mk_dyn_vtable = || { + let mk_dyn_vtable = |principal: Option>| { + let min_count = ty::vtable_min_entries(tcx, principal); Ty::new_imm_ref( tcx, tcx.lifetimes.re_static, - Ty::new_array(tcx, tcx.types.usize, 3), + // FIXME: properly type (e.g. usize and fn pointers) the fields. + Ty::new_array(tcx, tcx.types.usize, min_count.try_into().unwrap()), ) - /* FIXME: use actual fn pointers - Warning: naively computing the number of entries in the - vtable by counting the methods on the trait + methods on - all parent traits does not work, because some methods can - be not object safe and thus excluded from the vtable. - Increase this counter if you tried to implement this but - failed to do it without duplicating a lot of code from - other places in the compiler: 2 - Ty::new_tup(tcx,&[ - Ty::new_array(tcx,tcx.types.usize, 3), - Ty::new_array(tcx,Option), - ]) - */ }; let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() @@ -862,17 +851,17 @@ where // and we rely on this layout information to trigger a panic in // `std::mem::uninitialized::<&dyn Trait>()`, for example. if let ty::Adt(def, args) = metadata.kind() - && Some(def.did()) == tcx.lang_items().dyn_metadata() - && args.type_at(0).is_trait() + && tcx.is_lang_item(def.did(), LangItem::DynMetadata) + && let ty::Dynamic(data, _, ty::Dyn) = args.type_at(0).kind() { - mk_dyn_vtable() + mk_dyn_vtable(data.principal()) } else { metadata } } else { match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() { ty::Slice(_) | ty::Str => tcx.types.usize, - ty::Dynamic(_, _, ty::Dyn) => mk_dyn_vtable(), + ty::Dynamic(data, _, ty::Dyn) => mk_dyn_vtable(data.principal()), _ => bug!("TyAndLayout::field({:?}): not applicable", this), } }; @@ -1181,7 +1170,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option, abi: SpecAbi) -> // This is not part of `codegen_fn_attrs` as it can differ between crates // and therefore cannot be computed in core. if tcx.sess.opts.unstable_opts.panic_in_drop == PanicStrategy::Abort { - if Some(did) == tcx.lang_items().drop_in_place_fn() { + if tcx.is_lang_item(did, LangItem::DropInPlace) { return false; } } @@ -1267,7 +1256,7 @@ pub enum FnAbiError<'tcx> { } impl<'a, 'b, G: EmissionGuarantee> Diagnostic<'a, G> for FnAbiError<'b> { - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { match self { Self::Layout(e) => e.into_diagnostic().into_diag(dcx, level), Self::AdjustForForeignAbi(call::AdjustForForeignAbiError::Unsupported { @@ -1313,7 +1302,7 @@ pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> { /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. /// /// NB: this doesn't handle virtual calls - those should use `fn_abi_of_instance` - /// instead, where the instance is an `InstanceDef::Virtual`. + /// instead, where the instance is an `InstanceKind::Virtual`. #[inline] fn fn_abi_of_fn_ptr( &self, @@ -1333,7 +1322,7 @@ pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> { /// direct calls to an `fn`. /// /// NB: that includes virtual calls, which are represented by "direct calls" - /// to an `InstanceDef::Virtual` instance (of `::fn`). + /// to an `InstanceKind::Virtual` instance (of `::fn`). #[inline] #[tracing::instrument(level = "debug", skip(self))] fn fn_abi_of_instance( @@ -1363,3 +1352,37 @@ pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> { } impl<'tcx, C: FnAbiOfHelpers<'tcx>> FnAbiOf<'tcx> for C {} + +impl<'tcx> TyCtxt<'tcx> { + pub fn offset_of_subfield( + self, + param_env: ty::ParamEnv<'tcx>, + mut layout: TyAndLayout<'tcx>, + indices: I, + ) -> Size + where + I: Iterator, + { + let cx = LayoutCx { tcx: self, param_env }; + let mut offset = Size::ZERO; + + for (variant, field) in indices { + layout = layout.for_variant(&cx, variant); + let index = field.index(); + offset += layout.fields.offset(index); + layout = layout.field(&cx, index); + if !layout.is_sized() { + // If it is not sized, then the tail must still have at least a known static alignment. + let tail = self.struct_tail_erasing_lifetimes(layout.ty, param_env); + if !matches!(tail.kind(), ty::Slice(..)) { + bug!( + "offset of not-statically-aligned field (type {:?}) cannot be computed statically", + layout.ty + ); + } + } + } + + offset + } +} diff --git a/compiler/rustc_middle/src/ty/list.rs b/compiler/rustc_middle/src/ty/list.rs index 0db541899d20..71a93cc520d5 100644 --- a/compiler/rustc_middle/src/ty/list.rs +++ b/compiler/rustc_middle/src/ty/list.rs @@ -1,5 +1,5 @@ use super::flags::FlagComputation; -use super::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, TyCtxt, TypeFlags, WithInfcx}; +use super::{DebruijnIndex, TypeFlags}; use crate::arena::Arena; use rustc_data_structures::aligned::{align_of, Aligned}; use rustc_serialize::{Encodable, Encoder}; @@ -162,14 +162,6 @@ impl fmt::Debug for RawList { (**self).fmt(f) } } -impl<'tcx, H, T: DebugWithInfcx>> DebugWithInfcx> for RawList { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - fmt::Debug::fmt(&this.map(|this| this.as_slice()), f) - } -} impl> Encodable for RawList { #[inline] diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index ce63fc20ddb2..9c2bfc12a18a 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -16,7 +16,6 @@ pub use self::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeV pub use self::AssocItemContainer::*; pub use self::BorrowKind::*; pub use self::IntVarValue::*; -pub use self::Variance::*; use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason}; use crate::metadata::ModChild; use crate::middle::privacy::EffectiveVisibilities; @@ -28,7 +27,7 @@ use crate::ty::fast_reject::SimplifiedType; use crate::ty::util::Discr; pub use adt::*; pub use assoc::*; -pub use generic_args::{GenericArgKind, *}; +pub use generic_args::{GenericArgKind, TermKind, *}; pub use generics::*; pub use intrinsic::IntrinsicDef; use rustc_ast as ast; @@ -48,7 +47,8 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res} use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; use rustc_index::IndexVec; use rustc_macros::{ - Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, + extension, Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, + TypeVisitable, }; use rustc_query_system::ich::StableHashingContext; use rustc_serialize::{Decodable, Encodable}; @@ -59,7 +59,8 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{ExpnId, ExpnKind, Span}; use rustc_target::abi::{Align, FieldIdx, Integer, IntegerType, VariantIdx}; pub use rustc_target::abi::{ReprFlags, ReprOptions}; -pub use rustc_type_ir::{DebugWithInfcx, InferCtxtLike, WithInfcx}; +pub use rustc_type_ir::relate::VarianceDiagInfo; +use tracing::{debug, instrument}; pub use vtable::*; use std::assert_matches::assert_matches; @@ -85,14 +86,15 @@ pub use self::closure::{ CAPTURE_STRUCT_LOCAL, }; pub use self::consts::{ - Const, ConstData, ConstInt, ConstKind, Expr, ScalarInt, UnevaluatedConst, ValTree, + Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst, ValTree, }; pub use self::context::{ tls, CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, }; -pub use self::instance::{Instance, InstanceDef, ReifyReason, ShortInstance, UnusedGenericParams}; +pub use self::instance::{Instance, InstanceKind, ReifyReason, ShortInstance, UnusedGenericParams}; pub use self::list::{List, ListWithCachedTypeInfo}; +pub use self::opaque_types::OpaqueTypeKey; pub use self::parameterized::ParameterizedOverTcx; pub use self::pattern::{Pattern, PatternKind}; pub use self::predicate::{ @@ -111,10 +113,8 @@ pub use self::region::{ pub use self::rvalue_scopes::RvalueScopes; pub use self::sty::{ AliasTy, Article, Binder, BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig, - ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, CoroutineClosureArgs, - CoroutineClosureArgsParts, CoroutineClosureSignature, FnSig, GenSig, InlineConstArgs, - InlineConstArgsParts, ParamConst, ParamTy, PolyFnSig, TyKind, TypeAndMut, UpvarArgs, - VarianceDiagInfo, + CoroutineArgsExt, EarlyBinder, FnSig, InlineConstArgs, InlineConstArgsParts, ParamConst, + ParamTy, PolyFnSig, TyKind, TypeAndMut, UpvarArgs, }; pub use self::trait_def::TraitDef; pub use self::typeck_results::{ @@ -122,7 +122,6 @@ pub use self::typeck_results::{ TypeckResults, UserType, UserTypeAnnotationIndex, }; -pub mod _match; pub mod abstract_const; pub mod adjustment; pub mod cast; @@ -264,7 +263,7 @@ pub struct ImplHeader<'tcx> { #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub struct ImplTraitHeader<'tcx> { - pub trait_ref: ty::EarlyBinder>, + pub trait_ref: ty::EarlyBinder<'tcx, ty::TraitRef<'tcx>>, pub polarity: ImplPolarity, pub safety: hir::Safety, } @@ -313,38 +312,6 @@ impl Visibility { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)] -pub enum BoundConstness { - /// `Type: Trait` - NotConst, - /// `Type: const Trait` - Const, - /// `Type: ~const Trait` - /// - /// Requires resolving to const only when we are in a const context. - ConstIfConst, -} - -impl BoundConstness { - pub fn as_str(self) -> &'static str { - match self { - Self::NotConst => "", - Self::Const => "const", - Self::ConstIfConst => "~const", - } - } -} - -impl fmt::Display for BoundConstness { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::NotConst => f.write_str("normal"), - Self::Const => f.write_str("const"), - Self::ConstIfConst => f.write_str("~const"), - } - } -} - #[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, TyEncodable, TyDecodable, HashStable)] #[derive(TypeFoldable, TypeVisitable)] pub struct ClosureSizeProfileData<'tcx> { @@ -521,6 +488,16 @@ pub struct Term<'tcx> { marker: PhantomData<(Ty<'tcx>, Const<'tcx>)>, } +impl<'tcx> rustc_type_ir::inherent::Term> for Term<'tcx> {} + +impl<'tcx> rustc_type_ir::inherent::IntoKind for Term<'tcx> { + type Kind = TermKind<'tcx>; + + fn kind(self) -> Self::Kind { + self.unpack() + } +} + #[cfg(parallel_compiler)] unsafe impl<'tcx> rustc_data_structures::sync::DynSend for Term<'tcx> where &'tcx (Ty<'tcx>, Const<'tcx>): rustc_data_structures::sync::DynSend @@ -536,14 +513,10 @@ unsafe impl<'tcx> Sync for Term<'tcx> where &'tcx (Ty<'tcx>, Const<'tcx>): Sync impl Debug for Term<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let data = if let Some(ty) = self.ty() { - format!("Term::Ty({ty:?})") - } else if let Some(ct) = self.ct() { - format!("Term::Ct({ct:?})") - } else { - unreachable!() - }; - f.write_str(&data) + match self.unpack() { + TermKind::Ty(ty) => write!(f, "Term::Ty({ty:?})"), + TermKind::Const(ct) => write!(f, "Term::Const({ct:?})"), + } } } @@ -570,13 +543,19 @@ impl<'tcx> TypeFoldable> for Term<'tcx> { self, folder: &mut F, ) -> Result { - Ok(self.unpack().try_fold_with(folder)?.pack()) + match self.unpack() { + ty::TermKind::Ty(ty) => ty.try_fold_with(folder).map(Into::into), + ty::TermKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), + } } } impl<'tcx> TypeVisitable> for Term<'tcx> { fn visit_with>>(&self, visitor: &mut V) -> V::Result { - self.unpack().visit_with(visitor) + match self.unpack() { + ty::TermKind::Ty(ty) => ty.visit_with(visitor), + ty::TermKind::Const(ct) => ct.visit_with(visitor), + } } } @@ -607,21 +586,29 @@ impl<'tcx> Term<'tcx> { ptr.cast::>>().as_ref(), ))), CONST_TAG => TermKind::Const(ty::Const(Interned::new_unchecked( - ptr.cast::>>().as_ref(), + ptr.cast::>>().as_ref(), ))), _ => core::intrinsics::unreachable(), } } } - pub fn ty(&self) -> Option> { + pub fn as_type(&self) -> Option> { if let TermKind::Ty(ty) = self.unpack() { Some(ty) } else { None } } - pub fn ct(&self) -> Option> { + pub fn expect_type(&self) -> Ty<'tcx> { + self.as_type().expect("expected a type, but found a const") + } + + pub fn as_const(&self) -> Option> { if let TermKind::Const(c) = self.unpack() { Some(c) } else { None } } + pub fn expect_const(&self) -> Const<'tcx> { + self.as_const().expect("expected a const, but found a type") + } + pub fn into_arg(self) -> GenericArg<'tcx> { match self.unpack() { TermKind::Ty(ty) => ty.into(), @@ -654,13 +641,7 @@ const TAG_MASK: usize = 0b11; const TYPE_TAG: usize = 0b00; const CONST_TAG: usize = 0b01; -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable, TypeVisitable)] -pub enum TermKind<'tcx> { - Ty(Ty<'tcx>), - Const(Const<'tcx>), -} - +#[extension(pub trait TermKindPackExt<'tcx>)] impl<'tcx> TermKind<'tcx> { #[inline] fn pack(self) -> Term<'tcx> { @@ -778,45 +759,6 @@ impl<'a, 'tcx> IntoIterator for &'a InstantiatedPredicates<'tcx> { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)] -#[derive(TypeFoldable, TypeVisitable)] -pub struct OpaqueTypeKey<'tcx> { - pub def_id: LocalDefId, - pub args: GenericArgsRef<'tcx>, -} - -impl<'tcx> OpaqueTypeKey<'tcx> { - pub fn iter_captured_args( - self, - tcx: TyCtxt<'tcx>, - ) -> impl Iterator)> { - std::iter::zip(self.args, tcx.variances_of(self.def_id)).enumerate().filter_map( - |(i, (arg, v))| match (arg.unpack(), v) { - (_, ty::Invariant) => Some((i, arg)), - (ty::GenericArgKind::Lifetime(_), ty::Bivariant) => None, - _ => bug!("unexpected opaque type arg variance"), - }, - ) - } - - pub fn fold_captured_lifetime_args( - self, - tcx: TyCtxt<'tcx>, - mut f: impl FnMut(Region<'tcx>) -> Region<'tcx>, - ) -> Self { - let Self { def_id, args } = self; - let args = std::iter::zip(args, tcx.variances_of(def_id)).map(|(arg, v)| { - match (arg.unpack(), v) { - (ty::GenericArgKind::Lifetime(_), ty::Bivariant) => arg, - (ty::GenericArgKind::Lifetime(lt), _) => f(lt).into(), - _ => arg, - } - }); - let args = tcx.mk_args_from_iter(args); - Self { def_id, args } - } -} - #[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)] pub struct OpaqueHiddenType<'tcx> { /// The span of this particular definition of the opaque type. So @@ -922,6 +864,30 @@ pub struct Placeholder { pub universe: UniverseIndex, pub bound: T, } +impl Placeholder { + pub fn find_const_ty_from_env<'tcx>(self, env: ParamEnv<'tcx>) -> Ty<'tcx> { + let mut candidates = env.caller_bounds().iter().filter_map(|clause| { + // `ConstArgHasType` are never desugared to be higher ranked. + match clause.kind().skip_binder() { + ty::ClauseKind::ConstArgHasType(placeholder_ct, ty) => { + assert!(!(placeholder_ct, ty).has_escaping_bound_vars()); + + match placeholder_ct.kind() { + ty::ConstKind::Placeholder(placeholder_ct) if placeholder_ct == self => { + Some(ty) + } + _ => None, + } + } + _ => None, + } + }); + + let ty = candidates.next().unwrap(); + assert!(candidates.next().is_none()); + ty + } +} pub type PlaceholderRegion = Placeholder; @@ -1024,6 +990,16 @@ pub struct ParamEnv<'tcx> { packed: CopyTaggedPtr, ParamTag, true>, } +impl<'tcx> rustc_type_ir::inherent::ParamEnv> for ParamEnv<'tcx> { + fn reveal(self) -> Reveal { + self.reveal() + } + + fn caller_bounds(self) -> impl IntoIterator> { + self.caller_bounds() + } +} + #[derive(Copy, Clone)] struct ParamTag { reveal: traits::Reveal, @@ -1166,6 +1142,15 @@ pub struct Destructor { pub constness: hir::Constness, } +// FIXME: consider combining this definition with regular `Destructor` +#[derive(Copy, Clone, Debug, HashStable, Encodable, Decodable)] +pub struct AsyncDestructor { + /// The `DefId` of the async destructor future constructor + pub ctor: DefId, + /// The `DefId` of the async destructor future type + pub future: DefId, +} + #[derive(Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] pub struct VariantFlags(u8); bitflags::bitflags! { @@ -1718,11 +1703,11 @@ impl<'tcx> TyCtxt<'tcx> { } } - /// Returns the possibly-auto-generated MIR of a [`ty::InstanceDef`]. + /// Returns the possibly-auto-generated MIR of a [`ty::InstanceKind`]. #[instrument(skip(self), level = "debug")] - pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> { + pub fn instance_mir(self, instance: ty::InstanceKind<'tcx>) -> &'tcx Body<'tcx> { match instance { - ty::InstanceDef::Item(def) => { + ty::InstanceKind::Item(def) => { debug!("calling def_kind on def: {:?}", def); let def_kind = self.def_kind(def); debug!("returned from def_kind: {:?}", def_kind); @@ -1738,19 +1723,19 @@ impl<'tcx> TyCtxt<'tcx> { _ => self.optimized_mir(def), } } - ty::InstanceDef::VTableShim(..) - | ty::InstanceDef::ReifyShim(..) - | ty::InstanceDef::Intrinsic(..) - | ty::InstanceDef::FnPtrShim(..) - | ty::InstanceDef::Virtual(..) - | ty::InstanceDef::ClosureOnceShim { .. } - | ty::InstanceDef::ConstructCoroutineInClosureShim { .. } - | ty::InstanceDef::CoroutineKindShim { .. } - | ty::InstanceDef::DropGlue(..) - | ty::InstanceDef::CloneShim(..) - | ty::InstanceDef::ThreadLocalShim(..) - | ty::InstanceDef::FnPtrAddrShim(..) - | ty::InstanceDef::AsyncDropGlueCtorShim(..) => self.mir_shims(instance), + ty::InstanceKind::VTableShim(..) + | ty::InstanceKind::ReifyShim(..) + | ty::InstanceKind::Intrinsic(..) + | ty::InstanceKind::FnPtrShim(..) + | ty::InstanceKind::Virtual(..) + | ty::InstanceKind::ClosureOnceShim { .. } + | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } + | ty::InstanceKind::CoroutineKindShim { .. } + | ty::InstanceKind::DropGlue(..) + | ty::InstanceKind::CloneShim(..) + | ty::InstanceKind::ThreadLocalShim(..) + | ty::InstanceKind::FnPtrAddrShim(..) + | ty::InstanceKind::AsyncDropGlueCtorShim(..) => self.mir_shims(instance), } } @@ -1809,6 +1794,11 @@ impl<'tcx> TyCtxt<'tcx> { self.get_attrs(did, attr).next().is_some() } + /// Determines whether an item is annotated with a multi-segement attribute + pub fn has_attrs_with_path(self, did: impl Into, attrs: &[Symbol]) -> bool { + self.get_attrs_by_path(did.into(), attrs).next().is_some() + } + /// Returns `true` if this is an `auto trait`. pub fn trait_is_auto(self, trait_def_id: DefId) -> bool { self.trait_def(trait_def_id).has_auto_impl diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index 115cf3eeb226..fb16cf5bd363 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -11,6 +11,7 @@ use crate::traits::query::NoSolution; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder}; use crate::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; +use tracing::{debug, instrument}; #[derive(Debug, Copy, Clone, HashStable, TyEncodable, TyDecodable)] pub enum NormalizationError<'tcx> { @@ -124,7 +125,7 @@ impl<'tcx> TyCtxt<'tcx> { self, param_args: GenericArgsRef<'tcx>, param_env: ty::ParamEnv<'tcx>, - value: EarlyBinder, + value: EarlyBinder<'tcx, T>, ) -> T where T: TypeFoldable>, @@ -142,7 +143,7 @@ impl<'tcx> TyCtxt<'tcx> { self, param_args: GenericArgsRef<'tcx>, param_env: ty::ParamEnv<'tcx>, - value: EarlyBinder, + value: EarlyBinder<'tcx, T>, ) -> Result> where T: TypeFoldable>, diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs index fa5265c58e43..08b2f9e89203 100644 --- a/compiler/rustc_middle/src/ty/opaque_types.rs +++ b/compiler/rustc_middle/src/ty/opaque_types.rs @@ -5,6 +5,9 @@ use crate::ty::{GenericArg, GenericArgKind}; use rustc_data_structures::fx::FxHashMap; use rustc_span::def_id::DefId; use rustc_span::Span; +use tracing::{debug, instrument, trace}; + +pub type OpaqueTypeKey<'tcx> = rustc_type_ir::OpaqueTypeKey>; /// Converts generic params of a TypeFoldable from one /// item's generics to another. Usually from a function's generics @@ -215,7 +218,7 @@ impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { }) .emit_unless(self.ignore_errors); - ty::Const::new_error(self.tcx, guar, ct.ty()) + ty::Const::new_error(self.tcx, guar) } } } diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 571c6e918dcb..f394b3c990ce 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -33,8 +33,8 @@ impl ParameterizedOverTcx for ty::Binder<'static, T> { type Value<'tcx> = ty::Binder<'tcx, T::Value<'tcx>>; } -impl ParameterizedOverTcx for ty::EarlyBinder { - type Value<'tcx> = ty::EarlyBinder>; +impl ParameterizedOverTcx for ty::EarlyBinder<'static, T> { + type Value<'tcx> = ty::EarlyBinder<'tcx, T::Value<'tcx>>; } #[macro_export] diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 644fca7c5fee..e9b37503bb3e 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -1,15 +1,14 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::intern::Interned; use rustc_hir::def_id::DefId; -use rustc_macros::{ - extension, HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, -}; +use rustc_macros::{extension, HashStable}; use rustc_type_ir as ir; use std::cmp::Ordering; +use tracing::instrument; use crate::ty::{ - self, Binder, DebruijnIndex, EarlyBinder, PredicatePolarity, Term, Ty, TyCtxt, TypeFlags, - Upcast, UpcastFrom, WithCachedTypeInfo, + self, DebruijnIndex, EarlyBinder, PredicatePolarity, Ty, TyCtxt, TypeFlags, Upcast, UpcastFrom, + WithCachedTypeInfo, }; pub type TraitRef<'tcx> = ir::TraitRef>; @@ -24,6 +23,15 @@ pub type PredicateKind<'tcx> = ir::PredicateKind>; pub type NormalizesTo<'tcx> = ir::NormalizesTo>; pub type CoercePredicate<'tcx> = ir::CoercePredicate>; pub type SubtypePredicate<'tcx> = ir::SubtypePredicate>; +pub type OutlivesPredicate<'tcx, T> = ir::OutlivesPredicate, T>; +pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, ty::Region<'tcx>>; +pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, Ty<'tcx>>; +pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>; +pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>; +pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>; +pub type PolySubtypePredicate<'tcx> = ty::Binder<'tcx, SubtypePredicate<'tcx>>; +pub type PolyCoercePredicate<'tcx> = ty::Binder<'tcx, CoercePredicate<'tcx>>; +pub type PolyProjectionPredicate<'tcx> = ty::Binder<'tcx, ProjectionPredicate<'tcx>>; /// A statement that can be proven by a trait solver. This includes things that may /// show up in where clauses, such as trait predicates and projection predicates, @@ -37,7 +45,23 @@ pub struct Predicate<'tcx>( pub(super) Interned<'tcx, WithCachedTypeInfo>>>, ); -impl<'tcx> rustc_type_ir::inherent::Predicate> for Predicate<'tcx> {} +impl<'tcx> rustc_type_ir::inherent::Predicate> for Predicate<'tcx> { + fn is_coinductive(self, interner: TyCtxt<'tcx>) -> bool { + self.is_coinductive(interner) + } + + fn allow_normalization(self) -> bool { + self.allow_normalization() + } +} + +impl<'tcx> rustc_type_ir::inherent::IntoKind for Predicate<'tcx> { + type Kind = ty::Binder<'tcx, ty::PredicateKind<'tcx>>; + + fn kind(self) -> Self::Kind { + self.kind() + } +} impl<'tcx> rustc_type_ir::visit::Flags for Predicate<'tcx> { fn flags(&self) -> TypeFlags { @@ -108,18 +132,16 @@ impl<'tcx> Predicate<'tcx> { /// unsoundly accept some programs. See #91068. #[inline] pub fn allow_normalization(self) -> bool { + // Keep this in sync with the one in `rustc_type_ir::inherent`! match self.kind().skip_binder() { - PredicateKind::Clause(ClauseKind::WellFormed(_)) => false, - // `NormalizesTo` is only used in the new solver, so this shouldn't - // matter. Normalizing `term` would be 'wrong' however, as it changes whether - // `normalizes-to(::Assoc, ::Assoc)` holds. - PredicateKind::NormalizesTo(..) => false, + PredicateKind::Clause(ClauseKind::WellFormed(_)) + | PredicateKind::AliasRelate(..) + | PredicateKind::NormalizesTo(..) => false, PredicateKind::Clause(ClauseKind::Trait(_)) | PredicateKind::Clause(ClauseKind::RegionOutlives(_)) | PredicateKind::Clause(ClauseKind::TypeOutlives(_)) | PredicateKind::Clause(ClauseKind::Projection(_)) | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) - | PredicateKind::AliasRelate(..) | PredicateKind::ObjectSafe(_) | PredicateKind::Subtype(_) | PredicateKind::Coerce(_) @@ -151,6 +173,16 @@ pub struct Clause<'tcx>( pub(super) Interned<'tcx, WithCachedTypeInfo>>>, ); +impl<'tcx> rustc_type_ir::inherent::Clause> for Clause<'tcx> {} + +impl<'tcx> rustc_type_ir::inherent::IntoKind for Clause<'tcx> { + type Kind = ty::Binder<'tcx, ClauseKind<'tcx>>; + + fn kind(self) -> Self::Kind { + self.kind() + } +} + impl<'tcx> Clause<'tcx> { pub fn as_predicate(self) -> Predicate<'tcx> { Predicate(self.0) @@ -227,31 +259,25 @@ impl<'tcx> ExistentialPredicate<'tcx> { pub type PolyExistentialPredicate<'tcx> = ty::Binder<'tcx, ExistentialPredicate<'tcx>>; -impl<'tcx> PolyExistentialPredicate<'tcx> { - /// Given an existential predicate like `?Self: PartialEq` (e.g., derived from `dyn PartialEq`), - /// and a concrete type `self_ty`, returns a full predicate where the existentially quantified variable `?Self` - /// has been replaced with `self_ty` (e.g., `self_ty: PartialEq`, in our example). - pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::Clause<'tcx> { - match self.skip_binder() { - ExistentialPredicate::Trait(tr) => { - self.rebind(tr).with_self_ty(tcx, self_ty).upcast(tcx) - } - ExistentialPredicate::Projection(p) => { - self.rebind(p.with_self_ty(tcx, self_ty)).upcast(tcx) - } - ExistentialPredicate::AutoTrait(did) => { - let generics = tcx.generics_of(did); - let trait_ref = if generics.own_params.len() == 1 { - ty::TraitRef::new(tcx, did, [self_ty]) - } else { - // If this is an ill-formed auto trait, then synthesize - // new error args for the missing generics. - let err_args = ty::GenericArgs::extend_with_error(tcx, did, &[self_ty.into()]); - ty::TraitRef::new(tcx, did, err_args) - }; - self.rebind(trait_ref).upcast(tcx) - } - } +impl<'tcx> rustc_type_ir::inherent::BoundExistentialPredicates> + for &'tcx ty::List> +{ + fn principal_def_id(self) -> Option { + self.principal_def_id() + } + + fn principal(self) -> Option> { + self.principal() + } + + fn auto_traits(self) -> impl IntoIterator { + self.auto_traits() + } + + fn projection_bounds( + self, + ) -> impl IntoIterator>> { + self.projection_bounds() } } @@ -318,49 +344,9 @@ impl<'tcx> ty::List> { } pub type PolyTraitRef<'tcx> = ty::Binder<'tcx, TraitRef<'tcx>>; - -impl<'tcx> PolyTraitRef<'tcx> { - pub fn self_ty(&self) -> ty::Binder<'tcx, Ty<'tcx>> { - self.map_bound_ref(|tr| tr.self_ty()) - } - - pub fn def_id(&self) -> DefId { - self.skip_binder().def_id - } -} - pub type PolyExistentialTraitRef<'tcx> = ty::Binder<'tcx, ExistentialTraitRef<'tcx>>; - -impl<'tcx> PolyExistentialTraitRef<'tcx> { - pub fn def_id(&self) -> DefId { - self.skip_binder().def_id - } - - /// Object types don't have a self type specified. Therefore, when - /// we convert the principal trait-ref into a normal trait-ref, - /// you must give *some* self type. A common choice is `mk_err()` - /// or some placeholder type. - pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::PolyTraitRef<'tcx> { - self.map_bound(|trait_ref| trait_ref.with_self_ty(tcx, self_ty)) - } -} - pub type PolyExistentialProjection<'tcx> = ty::Binder<'tcx, ExistentialProjection<'tcx>>; -impl<'tcx> PolyExistentialProjection<'tcx> { - pub fn with_self_ty( - &self, - tcx: TyCtxt<'tcx>, - self_ty: Ty<'tcx>, - ) -> ty::PolyProjectionPredicate<'tcx> { - self.map_bound(|p| p.with_self_ty(tcx, self_ty)) - } - - pub fn item_def_id(&self) -> DefId { - self.skip_binder().def_id - } -} - impl<'tcx> Clause<'tcx> { /// Performs a instantiation suitable for going from a /// poly-trait-ref to supertraits that must hold if that @@ -370,7 +356,7 @@ impl<'tcx> Clause<'tcx> { pub fn instantiate_supertrait( self, tcx: TyCtxt<'tcx>, - trait_ref: &ty::PolyTraitRef<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, ) -> Clause<'tcx> { // The interaction between HRTB and supertraits is not entirely // obvious. Let me walk you (and myself) through an example. @@ -467,73 +453,6 @@ impl<'tcx> Clause<'tcx> { } } -pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>; - -impl<'tcx> PolyTraitPredicate<'tcx> { - pub fn def_id(self) -> DefId { - // Ok to skip binder since trait `DefId` does not care about regions. - self.skip_binder().def_id() - } - - pub fn self_ty(self) -> ty::Binder<'tcx, Ty<'tcx>> { - self.map_bound(|trait_ref| trait_ref.self_ty()) - } - - #[inline] - pub fn polarity(self) -> PredicatePolarity { - self.skip_binder().polarity - } -} - -/// `A: B` -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] -pub struct OutlivesPredicate(pub A, pub B); -pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; -pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; -pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>; -pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>; - -pub type PolySubtypePredicate<'tcx> = ty::Binder<'tcx, SubtypePredicate<'tcx>>; - -pub type PolyCoercePredicate<'tcx> = ty::Binder<'tcx, CoercePredicate<'tcx>>; - -pub type PolyProjectionPredicate<'tcx> = Binder<'tcx, ProjectionPredicate<'tcx>>; - -impl<'tcx> PolyProjectionPredicate<'tcx> { - /// Returns the `DefId` of the trait of the associated item being projected. - #[inline] - pub fn trait_def_id(&self, tcx: TyCtxt<'tcx>) -> DefId { - self.skip_binder().projection_term.trait_def_id(tcx) - } - - /// Get the [PolyTraitRef] required for this projection to be well formed. - /// Note that for generic associated types the predicates of the associated - /// type also need to be checked. - #[inline] - pub fn required_poly_trait_ref(&self, tcx: TyCtxt<'tcx>) -> PolyTraitRef<'tcx> { - // Note: unlike with `TraitRef::to_poly_trait_ref()`, - // `self.0.trait_ref` is permitted to have escaping regions. - // This is because here `self` has a `Binder` and so does our - // return value, so we are preserving the number of binding - // levels. - self.map_bound(|predicate| predicate.projection_term.trait_ref(tcx)) - } - - pub fn term(&self) -> Binder<'tcx, Term<'tcx>> { - self.map_bound(|predicate| predicate.term) - } - - /// The `DefId` of the `TraitItem` for the associated type. - /// - /// Note that this is not the `DefId` of the `TraitRef` containing this - /// associated type, which is in `tcx.associated_item(projection_def_id()).container`. - pub fn projection_def_id(&self) -> DefId { - // Ok to skip binder since trait `DefId` does not care about regions. - self.skip_binder().projection_term.def_id - } -} - pub trait ToPolyTraitRef<'tcx> { fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>; } @@ -550,8 +469,8 @@ impl<'tcx> UpcastFrom, PredicateKind<'tcx>> for Predicate<'tcx> { } } -impl<'tcx> UpcastFrom, Binder<'tcx, PredicateKind<'tcx>>> for Predicate<'tcx> { - fn upcast_from(from: Binder<'tcx, PredicateKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, ty::Binder<'tcx, PredicateKind<'tcx>>> for Predicate<'tcx> { + fn upcast_from(from: ty::Binder<'tcx, PredicateKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { tcx.mk_predicate(from) } } @@ -562,8 +481,8 @@ impl<'tcx> UpcastFrom, ClauseKind<'tcx>> for Predicate<'tcx> { } } -impl<'tcx> UpcastFrom, Binder<'tcx, ClauseKind<'tcx>>> for Predicate<'tcx> { - fn upcast_from(from: Binder<'tcx, ClauseKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, ty::Binder<'tcx, ClauseKind<'tcx>>> for Predicate<'tcx> { + fn upcast_from(from: ty::Binder<'tcx, ClauseKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { tcx.mk_predicate(from.map_bound(PredicateKind::Clause)) } } @@ -576,12 +495,12 @@ impl<'tcx> UpcastFrom, Clause<'tcx>> for Predicate<'tcx> { impl<'tcx> UpcastFrom, ClauseKind<'tcx>> for Clause<'tcx> { fn upcast_from(from: ClauseKind<'tcx>, tcx: TyCtxt<'tcx>) -> Self { - tcx.mk_predicate(Binder::dummy(PredicateKind::Clause(from))).expect_clause() + tcx.mk_predicate(ty::Binder::dummy(PredicateKind::Clause(from))).expect_clause() } } -impl<'tcx> UpcastFrom, Binder<'tcx, ClauseKind<'tcx>>> for Clause<'tcx> { - fn upcast_from(from: Binder<'tcx, ClauseKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, ty::Binder<'tcx, ClauseKind<'tcx>>> for Clause<'tcx> { + fn upcast_from(from: ty::Binder<'tcx, ClauseKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { tcx.mk_predicate(from.map_bound(|clause| PredicateKind::Clause(clause))).expect_clause() } } @@ -592,12 +511,6 @@ impl<'tcx> UpcastFrom, TraitRef<'tcx>> for Predicate<'tcx> { } } -impl<'tcx> UpcastFrom, TraitRef<'tcx>> for TraitPredicate<'tcx> { - fn upcast_from(from: TraitRef<'tcx>, _tcx: TyCtxt<'tcx>) -> Self { - TraitPredicate { trait_ref: from, polarity: PredicatePolarity::Positive } - } -} - impl<'tcx> UpcastFrom, TraitRef<'tcx>> for Clause<'tcx> { fn upcast_from(from: TraitRef<'tcx>, tcx: TyCtxt<'tcx>) -> Self { let p: Predicate<'tcx> = from.upcast(tcx); @@ -605,22 +518,22 @@ impl<'tcx> UpcastFrom, TraitRef<'tcx>> for Clause<'tcx> { } } -impl<'tcx> UpcastFrom, Binder<'tcx, TraitRef<'tcx>>> for Predicate<'tcx> { - fn upcast_from(from: Binder<'tcx, TraitRef<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, ty::Binder<'tcx, TraitRef<'tcx>>> for Predicate<'tcx> { + fn upcast_from(from: ty::Binder<'tcx, TraitRef<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { let pred: PolyTraitPredicate<'tcx> = from.upcast(tcx); pred.upcast(tcx) } } -impl<'tcx> UpcastFrom, Binder<'tcx, TraitRef<'tcx>>> for Clause<'tcx> { - fn upcast_from(from: Binder<'tcx, TraitRef<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, ty::Binder<'tcx, TraitRef<'tcx>>> for Clause<'tcx> { + fn upcast_from(from: ty::Binder<'tcx, TraitRef<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { let pred: PolyTraitPredicate<'tcx> = from.upcast(tcx); pred.upcast(tcx) } } -impl<'tcx> UpcastFrom, Binder<'tcx, TraitRef<'tcx>>> for PolyTraitPredicate<'tcx> { - fn upcast_from(from: Binder<'tcx, TraitRef<'tcx>>, _tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, ty::Binder<'tcx, TraitRef<'tcx>>> for PolyTraitPredicate<'tcx> { + fn upcast_from(from: ty::Binder<'tcx, TraitRef<'tcx>>, _tcx: TyCtxt<'tcx>) -> Self { from.map_bound(|trait_ref| TraitPredicate { trait_ref, polarity: PredicatePolarity::Positive, @@ -654,16 +567,20 @@ impl<'tcx> UpcastFrom, PolyTraitPredicate<'tcx>> for Clause<'tcx> { } } +impl<'tcx> UpcastFrom, RegionOutlivesPredicate<'tcx>> for Predicate<'tcx> { + fn upcast_from(from: RegionOutlivesPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self { + ty::Binder::dummy(PredicateKind::Clause(ClauseKind::RegionOutlives(from))).upcast(tcx) + } +} + impl<'tcx> UpcastFrom, PolyRegionOutlivesPredicate<'tcx>> for Predicate<'tcx> { fn upcast_from(from: PolyRegionOutlivesPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self { from.map_bound(|p| PredicateKind::Clause(ClauseKind::RegionOutlives(p))).upcast(tcx) } } -impl<'tcx> UpcastFrom, OutlivesPredicate, ty::Region<'tcx>>> - for Predicate<'tcx> -{ - fn upcast_from(from: OutlivesPredicate, ty::Region<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, TypeOutlivesPredicate<'tcx>> for Predicate<'tcx> { + fn upcast_from(from: TypeOutlivesPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self { ty::Binder::dummy(PredicateKind::Clause(ClauseKind::TypeOutlives(from))).upcast(tcx) } } diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index e7589737d64b..3c27df9529aa 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -7,6 +7,7 @@ use rustc_data_structures::sso::SsoHashSet; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; +use tracing::{debug, instrument, trace}; // `pretty` is a separate module only for organization. mod pretty; @@ -160,7 +161,7 @@ pub trait Printer<'tcx>: Sized { // If we have any generic arguments to print, we do that // on top of the same path, but without its own generics. _ => { - if !generics.own_params.is_empty() && args.len() >= generics.count() { + if !generics.is_own_empty() && args.len() >= generics.count() { let args = generics.own_args_no_defaults(self.tcx(), args); return self.path_generic_args( |cx| cx.print_def_path(def_id, parent_args), diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 0dbb17e9db44..72cb3e134027 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -7,7 +7,7 @@ use crate::ty::{ ConstInt, Expr, ParamConst, ScalarInt, Term, TermKind, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; -use rustc_apfloat::ieee::{Double, Single}; +use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_apfloat::Float; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::unord::UnordMap; @@ -999,7 +999,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let trait_ref = bound_predicate.rebind(pred.trait_ref); // Don't print `+ Sized`, but rather `+ ?Sized` if absent. - if Some(trait_ref.def_id()) == tcx.lang_items().sized_trait() { + if tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) { match pred.polarity { ty::PredicatePolarity::Positive => { has_sized_bound = true; @@ -1077,7 +1077,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } p!(")"); - if let Some(ty) = return_ty.skip_binder().ty() { + if let Some(ty) = return_ty.skip_binder().as_type() { if !ty.is_unit() { p!(" -> ", print(return_ty)); } @@ -1144,7 +1144,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { for (assoc_item_def_id, term) in assoc_items { // Skip printing `<{coroutine@} as Coroutine<_>>::Return` from async blocks, // unless we can find out what coroutine return type it comes from. - let term = if let Some(ty) = term.skip_binder().ty() + let term = if let Some(ty) = term.skip_binder().as_type() && let ty::Alias(ty::Projection, proj) = ty.kind() && let Some(assoc) = tcx.opt_associated_item(proj.def_id) && assoc.trait_container(tcx) == tcx.lang_items().coroutine_trait() @@ -1254,14 +1254,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } entry.has_fn_once = true; return; - } else if Some(trait_def_id) == self.tcx().lang_items().fn_mut_trait() { + } else if self.tcx().is_lang_item(trait_def_id, LangItem::FnMut) { let super_trait_ref = supertraits_for_pretty_printing(self.tcx(), trait_ref) .find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait) .unwrap(); fn_traits.entry(super_trait_ref).or_default().fn_mut_trait_ref = Some(trait_ref); return; - } else if Some(trait_def_id) == self.tcx().lang_items().fn_trait() { + } else if self.tcx().is_lang_item(trait_def_id, LangItem::Fn) { let super_trait_ref = supertraits_for_pretty_printing(self.tcx(), trait_ref) .find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait) .unwrap(); @@ -1322,7 +1322,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p!(pretty_fn_sig( tys, false, - proj.skip_binder().term.ty().expect("Return type was a const") + proj.skip_binder().term.as_type().expect("Return type was a const") )); resugared = true; } @@ -1459,23 +1459,6 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { return Ok(()); } - macro_rules! print_underscore { - () => {{ - if print_ty { - self.typed_value( - |this| { - write!(this, "_")?; - Ok(()) - }, - |this| this.print_type(ct.ty()), - ": ", - )?; - } else { - write!(self, "_")?; - } - }}; - } - match ct.kind() { ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => { match self.tcx().def_kind(def) { @@ -1508,11 +1491,11 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::InferConst::Var(ct_vid) if let Some(name) = self.const_infer_name(ct_vid) => { p!(write("{}", name)) } - _ => print_underscore!(), + _ => write!(self, "_")?, }, ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)), - ty::ConstKind::Value(value) => { - return self.pretty_print_const_valtree(value, ct.ty(), print_ty); + ty::ConstKind::Value(ty, value) => { + return self.pretty_print_const_valtree(value, ty, print_ty); } ty::ConstKind::Bound(debruijn, bound_var) => { @@ -1533,8 +1516,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { print_ty: bool, ) -> Result<(), PrintError> { define_scoped_cx!(self); - match expr { - Expr::Binop(op, c1, c2) => { + match expr.kind { + ty::ExprKind::Binop(op) => { + let (_, _, c1, c2) = expr.binop_args(); + let precedence = |binop: rustc_middle::mir::BinOp| { use rustc_ast::util::parser::AssocOp; AssocOp::from_ast_binop(binop.to_hir_binop().into()).precedence() @@ -1543,22 +1528,26 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let formatted_op = op.to_hir_binop().as_str(); let (lhs_parenthesized, rhs_parenthesized) = match (c1.kind(), c2.kind()) { ( - ty::ConstKind::Expr(Expr::Binop(lhs_op, _, _)), - ty::ConstKind::Expr(Expr::Binop(rhs_op, _, _)), + ty::ConstKind::Expr(ty::Expr { kind: ty::ExprKind::Binop(lhs_op), .. }), + ty::ConstKind::Expr(ty::Expr { kind: ty::ExprKind::Binop(rhs_op), .. }), ) => (precedence(lhs_op) < op_precedence, precedence(rhs_op) < op_precedence), - (ty::ConstKind::Expr(Expr::Binop(lhs_op, ..)), ty::ConstKind::Expr(_)) => { - (precedence(lhs_op) < op_precedence, true) - } - (ty::ConstKind::Expr(_), ty::ConstKind::Expr(Expr::Binop(rhs_op, ..))) => { - (true, precedence(rhs_op) < op_precedence) - } + ( + ty::ConstKind::Expr(ty::Expr { kind: ty::ExprKind::Binop(lhs_op), .. }), + ty::ConstKind::Expr(_), + ) => (precedence(lhs_op) < op_precedence, true), + ( + ty::ConstKind::Expr(_), + ty::ConstKind::Expr(ty::Expr { kind: ty::ExprKind::Binop(rhs_op), .. }), + ) => (true, precedence(rhs_op) < op_precedence), (ty::ConstKind::Expr(_), ty::ConstKind::Expr(_)) => (true, true), - (ty::ConstKind::Expr(Expr::Binop(lhs_op, ..)), _) => { - (precedence(lhs_op) < op_precedence, false) - } - (_, ty::ConstKind::Expr(Expr::Binop(rhs_op, ..))) => { - (false, precedence(rhs_op) < op_precedence) - } + ( + ty::ConstKind::Expr(ty::Expr { kind: ty::ExprKind::Binop(lhs_op), .. }), + _, + ) => (precedence(lhs_op) < op_precedence, false), + ( + _, + ty::ConstKind::Expr(ty::Expr { kind: ty::ExprKind::Binop(rhs_op), .. }), + ) => (false, precedence(rhs_op) < op_precedence), (ty::ConstKind::Expr(_), _) => (true, false), (_, ty::ConstKind::Expr(_)) => (false, true), _ => (false, false), @@ -1574,14 +1563,20 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { rhs_parenthesized, )?; } - Expr::UnOp(op, ct) => { + ty::ExprKind::UnOp(op) => { + let (_, ct) = expr.unop_args(); + use rustc_middle::mir::UnOp; let formatted_op = match op { UnOp::Not => "!", UnOp::Neg => "-", + UnOp::PtrMetadata => "PtrMetadata", }; let parenthesized = match ct.kind() { - ty::ConstKind::Expr(Expr::UnOp(c_op, ..)) => c_op != op, + _ if op == UnOp::PtrMetadata => true, + ty::ConstKind::Expr(ty::Expr { kind: ty::ExprKind::UnOp(c_op), .. }) => { + c_op != op + } ty::ConstKind::Expr(_) => true, _ => false, }; @@ -1591,61 +1586,37 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { parenthesized, )? } - Expr::FunctionCall(fn_def, fn_args) => { - use ty::TyKind; - match fn_def.ty().kind() { - TyKind::FnDef(def_id, gen_args) => { - p!(print_value_path(*def_id, gen_args), "("); - if print_ty { - let tcx = self.tcx(); - let sig = tcx.fn_sig(def_id).instantiate(tcx, gen_args).skip_binder(); + ty::ExprKind::FunctionCall => { + let (_, fn_def, fn_args) = expr.call_args(); - let mut args_with_ty = fn_args.iter().map(|ct| (ct, ct.ty())); - let output_ty = sig.output(); - - if let Some((ct, ty)) = args_with_ty.next() { - self.typed_value( - |this| this.pretty_print_const(ct, print_ty), - |this| this.pretty_print_type(ty), - ": ", - )?; - for (ct, ty) in args_with_ty { - p!(", "); - self.typed_value( - |this| this.pretty_print_const(ct, print_ty), - |this| this.pretty_print_type(ty), - ": ", - )?; - } - } - p!(write(") -> {output_ty}")); - } else { - p!(comma_sep(fn_args.iter()), ")"); - } - } - _ => bug!("unexpected type of fn def"), - } + write!(self, "(")?; + self.pretty_print_const(fn_def, print_ty)?; + p!(")(", comma_sep(fn_args), ")"); } - Expr::Cast(kind, ct, ty) => { + ty::ExprKind::Cast(kind) => { + let (_, value, to_ty) = expr.cast_args(); + use ty::abstract_const::CastKind; if kind == CastKind::As || (kind == CastKind::Use && self.should_print_verbose()) { - let parenthesized = match ct.kind() { - ty::ConstKind::Expr(Expr::Cast(_, _, _)) => false, + let parenthesized = match value.kind() { + ty::ConstKind::Expr(ty::Expr { + kind: ty::ExprKind::Cast { .. }, .. + }) => false, ty::ConstKind::Expr(_) => true, _ => false, }; self.maybe_parenthesized( |this| { this.typed_value( - |this| this.pretty_print_const(ct, print_ty), - |this| this.pretty_print_type(ty), + |this| this.pretty_print_const(value, print_ty), + |this| this.pretty_print_type(to_ty), " as ", ) }, parenthesized, )?; } else { - self.pretty_print_const(ct, print_ty)? + self.pretty_print_const(value, print_ty)? } } } @@ -1678,10 +1649,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::Ref(_, inner, _) => { if let ty::Array(elem, len) = inner.kind() { if let ty::Uint(ty::UintTy::U8) = elem.kind() { - if let ty::ConstKind::Value(ty::ValTree::Leaf(int)) = len.kind() { + if let ty::ConstKind::Value(_, ty::ValTree::Leaf(int)) = len.kind() { match self.tcx().try_get_global_alloc(prov.alloc_id()) { Some(GlobalAlloc::Memory(alloc)) => { - let len = int.assert_bits(self.tcx().data_layout.pointer_size); + let len = int.to_bits(self.tcx().data_layout.pointer_size); let range = AllocRange { start: offset, size: Size::from_bytes(len) }; if let Ok(byte_str) = @@ -1739,6 +1710,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::Bool if int == ScalarInt::FALSE => p!("false"), ty::Bool if int == ScalarInt::TRUE => p!("true"), // Float + ty::Float(ty::FloatTy::F16) => { + let val = Half::try_from(int).unwrap(); + p!(write("{}{}f16", val, if val.is_finite() { "" } else { "_" })) + } ty::Float(ty::FloatTy::F32) => { let val = Single::try_from(int).unwrap(); p!(write("{}{}f32", val, if val.is_finite() { "" } else { "_" })) @@ -1747,6 +1722,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let val = Double::try_from(int).unwrap(); p!(write("{}{}f64", val, if val.is_finite() { "" } else { "_" })) } + ty::Float(ty::FloatTy::F128) => { + let val = Quad::try_from(int).unwrap(); + p!(write("{}{}f128", val, if val.is_finite() { "" } else { "_" })) + } // Int ty::Uint(_) | ty::Int(_) => { let int = @@ -1759,7 +1738,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } // Pointer types ty::Ref(..) | ty::RawPtr(_, _) | ty::FnPtr(_) => { - let data = int.assert_bits(self.tcx().data_layout.pointer_size); + let data = int.to_bits(self.tcx().data_layout.pointer_size); self.typed_value( |this| { write!(this, "0x{data:x}")?; @@ -1936,7 +1915,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { Ok(()) } - fn pretty_closure_as_impl(&mut self, closure: ty::ClosureArgs<'tcx>) -> Result<(), PrintError> { + fn pretty_closure_as_impl( + &mut self, + closure: ty::ClosureArgs>, + ) -> Result<(), PrintError> { let sig = closure.sig(); let kind = closure.kind_ty().to_opt_closure_kind().unwrap_or(ty::ClosureKind::Fn); @@ -2860,10 +2842,9 @@ where } } -impl<'tcx, T, U, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::OutlivesPredicate +impl<'tcx, T, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::OutlivesPredicate<'tcx, T> where T: Print<'tcx, P>, - U: Print<'tcx, P>, { fn print(&self, cx: &mut P) -> Result<(), PrintError> { define_scoped_cx!(cx); @@ -2934,12 +2915,13 @@ impl<'tcx> ty::TraitRef<'tcx> { } } +#[extension(pub trait PrintPolyTraitRefExt<'tcx>)] impl<'tcx> ty::Binder<'tcx, ty::TraitRef<'tcx>> { - pub fn print_only_trait_path(self) -> ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>> { + fn print_only_trait_path(self) -> ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>> { self.map_bound(|tr| tr.print_only_trait_path()) } - pub fn print_trait_sugared(self) -> ty::Binder<'tcx, TraitRefPrintSugared<'tcx>> { + fn print_trait_sugared(self) -> ty::Binder<'tcx, TraitRefPrintSugared<'tcx>> { self.map_bound(|tr| tr.print_trait_sugared()) } } @@ -2960,8 +2942,9 @@ impl<'tcx> ty::TraitPredicate<'tcx> { } } +#[extension(pub trait PrintPolyTraitPredicateExt<'tcx>)] impl<'tcx> ty::PolyTraitPredicate<'tcx> { - pub fn print_modifiers_and_trait_path( + fn print_modifiers_and_trait_path( self, ) -> ty::Binder<'tcx, TraitPredPrintModifiersAndPath<'tcx>> { self.map_bound(TraitPredPrintModifiersAndPath) @@ -2970,7 +2953,7 @@ impl<'tcx> ty::PolyTraitPredicate<'tcx> { #[derive(Debug, Copy, Clone, Lift)] pub struct PrintClosureAsImpl<'tcx> { - pub closure: ty::ClosureArgs<'tcx>, + pub closure: ty::ClosureArgs>, } macro_rules! forward_display_to_print { @@ -3014,21 +2997,7 @@ forward_display_to_print! { ty::Region<'tcx>, Ty<'tcx>, &'tcx ty::List>, - ty::Const<'tcx>, - - // HACK(eddyb) these are exhaustive instead of generic, - // because `for<'tcx>` isn't possible yet. - ty::PolyExistentialProjection<'tcx>, - ty::PolyExistentialTraitRef<'tcx>, - ty::Binder<'tcx, ty::TraitRef<'tcx>>, - ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - ty::Binder<'tcx, TraitRefPrintSugared<'tcx>>, - ty::Binder<'tcx, ty::FnSig<'tcx>>, - ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, - ty::Binder<'tcx, TraitPredPrintModifiersAndPath<'tcx>>, - ty::Binder<'tcx, ty::ProjectionPredicate<'tcx>>, - ty::OutlivesPredicate, ty::Region<'tcx>>, - ty::OutlivesPredicate, ty::Region<'tcx>> + ty::Const<'tcx> } define_print! { @@ -3210,7 +3179,7 @@ define_print_and_forward_display! { if let ty::PredicatePolarity::Negative = self.0.polarity { p!("!") } - p!(print(self.0.trait_ref.print_only_trait_path())); + p!(print(self.0.trait_ref.print_trait_sugared())); } PrintClosureAsImpl<'tcx> { @@ -3327,7 +3296,7 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N /// /// The implementation uses similar import discovery logic to that of 'use' suggestions. /// -/// See also [`DelayDm`](rustc_error_messages::DelayDm) and [`with_no_trimmed_paths!`]. +/// See also [`with_no_trimmed_paths!`]. // this is pub to be able to intra-doc-link it pub fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> DefIdMap { // Trimming paths is expensive and not optimized, since we expect it to only be used for error diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index 7540f0ab83ff..4e85af901707 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -1,14 +1,14 @@ -use polonius_engine::Atom; use rustc_data_structures::intern::Interned; use rustc_errors::MultiSpan; use rustc_hir::def_id::DefId; -use rustc_index::Idx; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::symbol::sym; use rustc_span::symbol::{kw, Symbol}; use rustc_span::{ErrorGuaranteed, DUMMY_SP}; use rustc_type_ir::RegionKind as IrRegionKind; +pub use rustc_type_ir::RegionVid; use std::ops::Deref; +use tracing::debug; use crate::ty::{self, BoundVar, TyCtxt, TypeFlags}; @@ -138,6 +138,14 @@ impl<'tcx> Region<'tcx> { } impl<'tcx> rustc_type_ir::inherent::Region> for Region<'tcx> { + fn new_bound( + interner: TyCtxt<'tcx>, + debruijn: ty::DebruijnIndex, + var: ty::BoundRegion, + ) -> Self { + Region::new_bound(interner, debruijn, var) + } + fn new_anon_bound(tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self { Region::new_bound(tcx, debruijn, ty::BoundRegion { var, kind: ty::BoundRegionKind::BrAnon }) } @@ -265,33 +273,6 @@ impl<'tcx> Region<'tcx> { flags } - /// Given an early-bound or free region, returns the `DefId` where it was bound. - /// For example, consider the regions in this snippet of code: - /// - /// ```ignore (illustrative) - /// impl<'a> Foo { - /// // ^^ -- early bound, declared on an impl - /// - /// fn bar<'b, 'c>(x: &self, y: &'b u32, z: &'c u64) where 'static: 'c - /// // ^^ ^^ ^ anonymous, late-bound - /// // | early-bound, appears in where-clauses - /// // late-bound, appears only in fn args - /// {..} - /// } - /// ``` - /// - /// Here, `free_region_binding_scope('a)` would return the `DefId` - /// of the impl, and for all the other highlighted regions, it - /// would return the `DefId` of the function. In other cases (not shown), this - /// function might return the `DefId` of a closure. - pub fn free_region_binding_scope(self, tcx: TyCtxt<'_>) -> DefId { - match *self { - ty::ReEarlyParam(br) => tcx.parent(br.def_id), - ty::ReLateParam(fr) => fr.scope, - _ => bug!("free_region_binding_scope invoked on inappropriate region: {:?}", self), - } - } - /// True for free regions other than `'static`. pub fn is_param(self) -> bool { matches!(*self, ty::ReEarlyParam(_) | ty::ReLateParam(_)) @@ -321,6 +302,21 @@ impl<'tcx> Region<'tcx> { _ => bug!("expected region {:?} to be of kind ReVar", self), } } + + /// Given some item `binding_item`, check if this region is a generic parameter introduced by it + /// or one of the parent generics. Returns the `DefId` of the parameter definition if so. + pub fn opt_param_def_id(self, tcx: TyCtxt<'tcx>, binding_item: DefId) -> Option { + match self.kind() { + ty::ReEarlyParam(ebr) => { + Some(tcx.generics_of(binding_item).region_param(ebr, tcx).def_id) + } + ty::ReLateParam(ty::LateParamRegion { + bound_region: ty::BoundRegionKind::BrNamed(def_id, _), + .. + }) => Some(def_id), + _ => None, + } + } } impl<'tcx> Deref for Region<'tcx> { @@ -335,31 +331,19 @@ impl<'tcx> Deref for Region<'tcx> { #[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable)] pub struct EarlyParamRegion { - pub def_id: DefId, pub index: u32, pub name: Symbol, } -impl std::fmt::Debug for EarlyParamRegion { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // FIXME(BoxyUwU): self.def_id goes first because of `erased-regions-in-hidden-ty.rs` being impossible to write - // error annotations for otherwise. :). Ideally this would be `self.name, self.index, self.def_id`. - write!(f, "{:?}_{}/#{}", self.def_id, self.name, self.index) +impl rustc_type_ir::inherent::ParamLike for EarlyParamRegion { + fn index(self) -> u32 { + self.index } } -rustc_index::newtype_index! { - /// A **region** (lifetime) **v**ariable **ID**. - #[derive(HashStable)] - #[encodable] - #[orderable] - #[debug_format = "'?{}"] - pub struct RegionVid {} -} - -impl Atom for RegionVid { - fn index(self) -> usize { - Idx::index(self) +impl std::fmt::Debug for EarlyParamRegion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}/#{}", self.name, self.index) } } @@ -367,6 +351,12 @@ impl Atom for RegionVid { #[derive(HashStable)] /// The parameter representation of late-bound function parameters, "some region /// at least as big as the scope `fr.scope`". +/// +/// Similar to a placeholder region as we create `LateParam` regions when entering a binder +/// except they are always in the root universe and instead of using a boundvar to distinguish +/// between others we use the `DefId` of the parameter. For this reason the `bound_region` field +/// should basically always be `BoundRegionKind::BrNamed` as otherwise there is no way of telling +/// different parameters apart. pub struct LateParamRegion { pub scope: DefId, pub bound_region: BoundRegionKind, @@ -400,6 +390,10 @@ impl<'tcx> rustc_type_ir::inherent::BoundVarLike> for BoundRegion { fn var(self) -> BoundVar { self.var } + + fn assert_eq(self, var: ty::BoundVariableKind) { + assert_eq!(self.kind, var.expect_region()) + } } impl core::fmt::Debug for BoundRegion { diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 947de3f3aaa5..b169d672a846 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -1,382 +1,54 @@ -//! Generalized type relating mechanism. -//! -//! A type relation `R` relates a pair of values `(A, B)`. `A and B` are usually -//! types or regions but can be other things. Examples of type relations are -//! subtyping, type equality, etc. - -use crate::ty::error::{ExpectedFound, TypeError}; -use crate::ty::{ - self, ExistentialPredicate, ExistentialPredicateStableCmpExt as _, Expr, GenericArg, - GenericArgKind, GenericArgsRef, ImplSubject, Term, TermKind, Ty, TyCtxt, TypeFoldable, -}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_macros::TypeVisitable; -use rustc_target::spec::abi; use std::iter; -use super::Pattern; +use rustc_hir as hir; +use rustc_target::spec::abi; +pub use rustc_type_ir::relate::*; -pub type RelateResult<'tcx, T> = Result>; +use crate::ty::error::{ExpectedFound, TypeError}; +use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; +use crate::ty::{self as ty, Ty, TyCtxt}; -pub trait TypeRelation<'tcx>: Sized { - fn tcx(&self) -> TyCtxt<'tcx>; +pub type RelateResult<'tcx, T> = rustc_type_ir::relate::RelateResult, T>; - /// Returns a static string we can use for printouts. - fn tag(&self) -> &'static str; - - /// Generic relation routine suitable for most anything. - fn relate>(&mut self, a: T, b: T) -> RelateResult<'tcx, T> { - Relate::relate(self, a, b) - } - - /// Relate the two args for the given item. The default - /// is to look up the variance for the item and proceed - /// accordingly. - fn relate_item_args( - &mut self, - item_def_id: DefId, - a_arg: GenericArgsRef<'tcx>, - b_arg: GenericArgsRef<'tcx>, - ) -> RelateResult<'tcx, GenericArgsRef<'tcx>> { - debug!( - "relate_item_args(item_def_id={:?}, a_arg={:?}, b_arg={:?})", - item_def_id, a_arg, b_arg - ); - - let tcx = self.tcx(); - let opt_variances = tcx.variances_of(item_def_id); - relate_args_with_variances(self, item_def_id, opt_variances, a_arg, b_arg, true) - } - - /// Switch variance for the purpose of relating `a` and `b`. - fn relate_with_variance>( - &mut self, - variance: ty::Variance, - info: ty::VarianceDiagInfo<'tcx>, - a: T, - b: T, - ) -> RelateResult<'tcx, T>; - - // Overridable relations. You shouldn't typically call these - // directly, instead call `relate()`, which in turn calls - // these. This is both more uniform but also allows us to add - // additional hooks for other types in the future if needed - // without making older code, which called `relate`, obsolete. - - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>>; - - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>>; - - fn consts( - &mut self, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>>; - - fn binders( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate<'tcx>; +/// Whether aliases should be related structurally or not. Used +/// to adjust the behavior of generalization and combine. +/// +/// This should always be `No` unless in a few special-cases when +/// instantiating canonical responses and in the new solver. Each +/// such case should have a comment explaining why it is used. +#[derive(Debug, Copy, Clone)] +pub enum StructurallyRelateAliases { + Yes, + No, } -pub trait Relate<'tcx>: TypeFoldable> + PartialEq + Copy { - fn relate>( - relation: &mut R, - a: Self, - b: Self, - ) -> RelateResult<'tcx, Self>; -} - -/////////////////////////////////////////////////////////////////////////// -// Relate impls - -#[inline] -pub fn relate_args_invariantly<'tcx, R: TypeRelation<'tcx>>( - relation: &mut R, - a_arg: GenericArgsRef<'tcx>, - b_arg: GenericArgsRef<'tcx>, -) -> RelateResult<'tcx, GenericArgsRef<'tcx>> { - relation.tcx().mk_args_from_iter(iter::zip(a_arg, b_arg).map(|(a, b)| { - relation.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b) - })) -} - -pub fn relate_args_with_variances<'tcx, R: TypeRelation<'tcx>>( - relation: &mut R, - ty_def_id: DefId, - variances: &[ty::Variance], - a_arg: GenericArgsRef<'tcx>, - b_arg: GenericArgsRef<'tcx>, - fetch_ty_for_diag: bool, -) -> RelateResult<'tcx, GenericArgsRef<'tcx>> { - let tcx = relation.tcx(); - - let mut cached_ty = None; - let params = iter::zip(a_arg, b_arg).enumerate().map(|(i, (a, b))| { - let variance = variances[i]; - let variance_info = if variance == ty::Invariant && fetch_ty_for_diag { - let ty = - *cached_ty.get_or_insert_with(|| tcx.type_of(ty_def_id).instantiate(tcx, a_arg)); - ty::VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() } - } else { - ty::VarianceDiagInfo::default() - }; - relation.relate_with_variance(variance, variance_info, a, b) - }); - - tcx.mk_args_from_iter(params) -} - -impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> { - fn relate>( - relation: &mut R, - a: ty::FnSig<'tcx>, - b: ty::FnSig<'tcx>, - ) -> RelateResult<'tcx, ty::FnSig<'tcx>> { - let tcx = relation.tcx(); - - if a.c_variadic != b.c_variadic { - return Err(TypeError::VariadicMismatch(expected_found(a.c_variadic, b.c_variadic))); - } - let safety = relation.relate(a.safety, b.safety)?; - let abi = relation.relate(a.abi, b.abi)?; - - if a.inputs().len() != b.inputs().len() { - return Err(TypeError::ArgCount); - } - - let inputs_and_output = iter::zip(a.inputs(), b.inputs()) - .map(|(&a, &b)| ((a, b), false)) - .chain(iter::once(((a.output(), b.output()), true))) - .map(|((a, b), is_output)| { - if is_output { - relation.relate(a, b) - } else { - relation.relate_with_variance( - ty::Contravariant, - ty::VarianceDiagInfo::default(), - a, - b, - ) - } - }) - .enumerate() - .map(|(i, r)| match r { - Err(TypeError::Sorts(exp_found) | TypeError::ArgumentSorts(exp_found, _)) => { - Err(TypeError::ArgumentSorts(exp_found, i)) - } - Err(TypeError::Mutability | TypeError::ArgumentMutability(_)) => { - Err(TypeError::ArgumentMutability(i)) - } - r => r, - }); - Ok(ty::FnSig { - inputs_and_output: tcx.mk_type_list_from_iter(inputs_and_output)?, - c_variadic: a.c_variadic, - safety, - abi, - }) - } -} - -impl<'tcx> Relate<'tcx> for ty::BoundConstness { - fn relate>( - _relation: &mut R, - a: ty::BoundConstness, - b: ty::BoundConstness, - ) -> RelateResult<'tcx, ty::BoundConstness> { - if a != b { Err(TypeError::ConstnessMismatch(expected_found(a, b))) } else { Ok(a) } - } -} - -impl<'tcx> Relate<'tcx> for hir::Safety { - fn relate>( - _relation: &mut R, - a: hir::Safety, - b: hir::Safety, - ) -> RelateResult<'tcx, hir::Safety> { - if a != b { Err(TypeError::SafetyMismatch(expected_found(a, b))) } else { Ok(a) } - } -} - -impl<'tcx> Relate<'tcx> for abi::Abi { - fn relate>( - _relation: &mut R, - a: abi::Abi, - b: abi::Abi, - ) -> RelateResult<'tcx, abi::Abi> { - if a == b { Ok(a) } else { Err(TypeError::AbiMismatch(expected_found(a, b))) } - } -} - -impl<'tcx> Relate<'tcx> for ty::AliasTy<'tcx> { - fn relate>( - relation: &mut R, - a: ty::AliasTy<'tcx>, - b: ty::AliasTy<'tcx>, - ) -> RelateResult<'tcx, ty::AliasTy<'tcx>> { - if a.def_id != b.def_id { - Err(TypeError::ProjectionMismatched(expected_found(a.def_id, b.def_id))) - } else { - let args = match a.kind(relation.tcx()) { - ty::Opaque => relate_args_with_variances( - relation, - a.def_id, - relation.tcx().variances_of(a.def_id), - a.args, - b.args, - false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle - )?, - ty::Projection | ty::Weak | ty::Inherent => { - relate_args_invariantly(relation, a.args, b.args)? - } - }; - Ok(ty::AliasTy::new(relation.tcx(), a.def_id, args)) - } - } -} - -impl<'tcx> Relate<'tcx> for ty::AliasTerm<'tcx> { - fn relate>( - relation: &mut R, - a: ty::AliasTerm<'tcx>, - b: ty::AliasTerm<'tcx>, - ) -> RelateResult<'tcx, ty::AliasTerm<'tcx>> { - if a.def_id != b.def_id { - Err(TypeError::ProjectionMismatched(expected_found(a.def_id, b.def_id))) - } else { - let args = match a.kind(relation.tcx()) { - ty::AliasTermKind::OpaqueTy => relate_args_with_variances( - relation, - a.def_id, - relation.tcx().variances_of(a.def_id), - a.args, - b.args, - false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle - )?, - ty::AliasTermKind::ProjectionTy - | ty::AliasTermKind::WeakTy - | ty::AliasTermKind::InherentTy - | ty::AliasTermKind::UnevaluatedConst - | ty::AliasTermKind::ProjectionConst => { - relate_args_invariantly(relation, a.args, b.args)? - } - }; - Ok(ty::AliasTerm::new(relation.tcx(), a.def_id, args)) - } - } -} - -impl<'tcx> Relate<'tcx> for ty::ExistentialProjection<'tcx> { - fn relate>( - relation: &mut R, - a: ty::ExistentialProjection<'tcx>, - b: ty::ExistentialProjection<'tcx>, - ) -> RelateResult<'tcx, ty::ExistentialProjection<'tcx>> { - if a.def_id != b.def_id { - Err(TypeError::ProjectionMismatched(expected_found(a.def_id, b.def_id))) - } else { - let term = relation.relate_with_variance( - ty::Invariant, - ty::VarianceDiagInfo::default(), - a.term, - b.term, - )?; - let args = relation.relate_with_variance( - ty::Invariant, - ty::VarianceDiagInfo::default(), - a.args, - b.args, - )?; - Ok(ty::ExistentialProjection { def_id: a.def_id, args, term }) - } - } -} - -impl<'tcx> Relate<'tcx> for ty::TraitRef<'tcx> { - fn relate>( - relation: &mut R, - a: ty::TraitRef<'tcx>, - b: ty::TraitRef<'tcx>, - ) -> RelateResult<'tcx, ty::TraitRef<'tcx>> { - // Different traits cannot be related. - if a.def_id != b.def_id { - Err(TypeError::Traits(expected_found(a.def_id, b.def_id))) - } else { - let args = relate_args_invariantly(relation, a.args, b.args)?; - Ok(ty::TraitRef::new(relation.tcx(), a.def_id, args)) - } - } -} - -impl<'tcx> Relate<'tcx> for ty::ExistentialTraitRef<'tcx> { - fn relate>( - relation: &mut R, - a: ty::ExistentialTraitRef<'tcx>, - b: ty::ExistentialTraitRef<'tcx>, - ) -> RelateResult<'tcx, ty::ExistentialTraitRef<'tcx>> { - // Different traits cannot be related. - if a.def_id != b.def_id { - Err(TypeError::Traits(expected_found(a.def_id, b.def_id))) - } else { - let args = relate_args_invariantly(relation, a.args, b.args)?; - Ok(ty::ExistentialTraitRef { def_id: a.def_id, args }) - } - } -} - -#[derive(PartialEq, Copy, Debug, Clone, TypeFoldable, TypeVisitable)] -struct CoroutineWitness<'tcx>(&'tcx ty::List>); - -impl<'tcx> Relate<'tcx> for CoroutineWitness<'tcx> { - fn relate>( - relation: &mut R, - a: CoroutineWitness<'tcx>, - b: CoroutineWitness<'tcx>, - ) -> RelateResult<'tcx, CoroutineWitness<'tcx>> { - assert_eq!(a.0.len(), b.0.len()); - let tcx = relation.tcx(); - let types = - tcx.mk_type_list_from_iter(iter::zip(a.0, b.0).map(|(a, b)| relation.relate(a, b)))?; - Ok(CoroutineWitness(types)) - } -} - -impl<'tcx> Relate<'tcx> for ImplSubject<'tcx> { +impl<'tcx> Relate> for ty::ImplSubject<'tcx> { #[inline] - fn relate>( + fn relate>>( relation: &mut R, - a: ImplSubject<'tcx>, - b: ImplSubject<'tcx>, - ) -> RelateResult<'tcx, ImplSubject<'tcx>> { + a: ty::ImplSubject<'tcx>, + b: ty::ImplSubject<'tcx>, + ) -> RelateResult<'tcx, ty::ImplSubject<'tcx>> { match (a, b) { - (ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => { + (ty::ImplSubject::Trait(trait_ref_a), ty::ImplSubject::Trait(trait_ref_b)) => { let trait_ref = ty::TraitRef::relate(relation, trait_ref_a, trait_ref_b)?; - Ok(ImplSubject::Trait(trait_ref)) + Ok(ty::ImplSubject::Trait(trait_ref)) } - (ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => { + (ty::ImplSubject::Inherent(ty_a), ty::ImplSubject::Inherent(ty_b)) => { let ty = Ty::relate(relation, ty_a, ty_b)?; - Ok(ImplSubject::Inherent(ty)) + Ok(ty::ImplSubject::Inherent(ty)) } - (ImplSubject::Trait(_), ImplSubject::Inherent(_)) - | (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => { + (ty::ImplSubject::Trait(_), ty::ImplSubject::Inherent(_)) + | (ty::ImplSubject::Inherent(_), ty::ImplSubject::Trait(_)) => { bug!("can not relate TraitRef and Ty"); } } } } -impl<'tcx> Relate<'tcx> for Ty<'tcx> { +impl<'tcx> Relate> for Ty<'tcx> { #[inline] - fn relate>( + fn relate>>( relation: &mut R, a: Ty<'tcx>, b: Ty<'tcx>, @@ -385,9 +57,9 @@ impl<'tcx> Relate<'tcx> for Ty<'tcx> { } } -impl<'tcx> Relate<'tcx> for Pattern<'tcx> { +impl<'tcx> Relate> for ty::Pattern<'tcx> { #[inline] - fn relate>( + fn relate>>( relation: &mut R, a: Self, b: Self, @@ -415,303 +87,8 @@ impl<'tcx> Relate<'tcx> for Pattern<'tcx> { } } -/// Relates `a` and `b` structurally, calling the relation for all nested values. -/// Any semantic equality, e.g. of projections, and inference variables have to be -/// handled by the caller. -#[instrument(level = "trace", skip(relation), ret)] -pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>( - relation: &mut R, - a: Ty<'tcx>, - b: Ty<'tcx>, -) -> RelateResult<'tcx, Ty<'tcx>> { - let tcx = relation.tcx(); - match (a.kind(), b.kind()) { - (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { - // The caller should handle these cases! - bug!("var types encountered in structurally_relate_tys") - } - - (ty::Bound(..), _) | (_, ty::Bound(..)) => { - bug!("bound types encountered in structurally_relate_tys") - } - - (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(tcx, guar)), - - (&ty::Never, _) - | (&ty::Char, _) - | (&ty::Bool, _) - | (&ty::Int(_), _) - | (&ty::Uint(_), _) - | (&ty::Float(_), _) - | (&ty::Str, _) - if a == b => - { - Ok(a) - } - - (ty::Param(a_p), ty::Param(b_p)) if a_p.index == b_p.index => { - debug_assert_eq!(a_p.name, b_p.name, "param types with same index differ in name"); - Ok(a) - } - - (ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a), - - (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) if a_def == b_def => { - let args = relation.relate_item_args(a_def.did(), a_args, b_args)?; - Ok(Ty::new_adt(tcx, a_def, args)) - } - - (&ty::Foreign(a_id), &ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(tcx, a_id)), - - (&ty::Dynamic(a_obj, a_region, a_repr), &ty::Dynamic(b_obj, b_region, b_repr)) - if a_repr == b_repr => - { - Ok(Ty::new_dynamic( - tcx, - relation.relate(a_obj, b_obj)?, - relation.relate(a_region, b_region)?, - a_repr, - )) - } - - (&ty::Coroutine(a_id, a_args), &ty::Coroutine(b_id, b_args)) if a_id == b_id => { - // All Coroutine types with the same id represent - // the (anonymous) type of the same coroutine expression. So - // all of their regions should be equated. - let args = relate_args_invariantly(relation, a_args, b_args)?; - Ok(Ty::new_coroutine(tcx, a_id, args)) - } - - (&ty::CoroutineWitness(a_id, a_args), &ty::CoroutineWitness(b_id, b_args)) - if a_id == b_id => - { - // All CoroutineWitness types with the same id represent - // the (anonymous) type of the same coroutine expression. So - // all of their regions should be equated. - let args = relate_args_invariantly(relation, a_args, b_args)?; - Ok(Ty::new_coroutine_witness(tcx, a_id, args)) - } - - (&ty::Closure(a_id, a_args), &ty::Closure(b_id, b_args)) if a_id == b_id => { - // All Closure types with the same id represent - // the (anonymous) type of the same closure expression. So - // all of their regions should be equated. - let args = relate_args_invariantly(relation, a_args, b_args)?; - Ok(Ty::new_closure(tcx, a_id, args)) - } - - (&ty::CoroutineClosure(a_id, a_args), &ty::CoroutineClosure(b_id, b_args)) - if a_id == b_id => - { - let args = relate_args_invariantly(relation, a_args, b_args)?; - Ok(Ty::new_coroutine_closure(tcx, a_id, args)) - } - - (&ty::RawPtr(a_ty, a_mutbl), &ty::RawPtr(b_ty, b_mutbl)) => { - if a_mutbl != b_mutbl { - return Err(TypeError::Mutability); - } - - let (variance, info) = match a_mutbl { - hir::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None), - hir::Mutability::Mut => { - (ty::Invariant, ty::VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) - } - }; - - let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?; - - Ok(Ty::new_ptr(tcx, ty, a_mutbl)) - } - - (&ty::Ref(a_r, a_ty, a_mutbl), &ty::Ref(b_r, b_ty, b_mutbl)) => { - if a_mutbl != b_mutbl { - return Err(TypeError::Mutability); - } - - let (variance, info) = match a_mutbl { - hir::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None), - hir::Mutability::Mut => { - (ty::Invariant, ty::VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) - } - }; - - let r = relation.relate(a_r, b_r)?; - let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?; - - Ok(Ty::new_ref(tcx, r, ty, a_mutbl)) - } - - (&ty::Array(a_t, sz_a), &ty::Array(b_t, sz_b)) => { - let t = relation.relate(a_t, b_t)?; - match relation.relate(sz_a, sz_b) { - Ok(sz) => Ok(Ty::new_array_with_const_len(tcx, t, sz)), - Err(err) => { - // Check whether the lengths are both concrete/known values, - // but are unequal, for better diagnostics. - let sz_a = sz_a.try_to_target_usize(tcx); - let sz_b = sz_b.try_to_target_usize(tcx); - - match (sz_a, sz_b) { - (Some(sz_a_val), Some(sz_b_val)) if sz_a_val != sz_b_val => { - Err(TypeError::FixedArraySize(expected_found(sz_a_val, sz_b_val))) - } - _ => Err(err), - } - } - } - } - - (&ty::Slice(a_t), &ty::Slice(b_t)) => { - let t = relation.relate(a_t, b_t)?; - Ok(Ty::new_slice(tcx, t)) - } - - (&ty::Tuple(as_), &ty::Tuple(bs)) => { - if as_.len() == bs.len() { - Ok(Ty::new_tup_from_iter( - tcx, - iter::zip(as_, bs).map(|(a, b)| relation.relate(a, b)), - )?) - } else if !(as_.is_empty() || bs.is_empty()) { - Err(TypeError::TupleSize(expected_found(as_.len(), bs.len()))) - } else { - Err(TypeError::Sorts(expected_found(a, b))) - } - } - - (&ty::FnDef(a_def_id, a_args), &ty::FnDef(b_def_id, b_args)) if a_def_id == b_def_id => { - let args = relation.relate_item_args(a_def_id, a_args, b_args)?; - Ok(Ty::new_fn_def(tcx, a_def_id, args)) - } - - (&ty::FnPtr(a_fty), &ty::FnPtr(b_fty)) => { - let fty = relation.relate(a_fty, b_fty)?; - Ok(Ty::new_fn_ptr(tcx, fty)) - } - - // Alias tend to mostly already be handled downstream due to normalization. - (&ty::Alias(a_kind, a_data), &ty::Alias(b_kind, b_data)) => { - let alias_ty = relation.relate(a_data, b_data)?; - assert_eq!(a_kind, b_kind); - Ok(Ty::new_alias(tcx, a_kind, alias_ty)) - } - - (&ty::Pat(a_ty, a_pat), &ty::Pat(b_ty, b_pat)) => { - let ty = relation.relate(a_ty, b_ty)?; - let pat = relation.relate(a_pat, b_pat)?; - Ok(Ty::new_pat(tcx, ty, pat)) - } - - _ => Err(TypeError::Sorts(expected_found(a, b))), - } -} - -/// Relates `a` and `b` structurally, calling the relation for all nested values. -/// Any semantic equality, e.g. of unevaluated consts, and inference variables have -/// to be handled by the caller. -/// -/// FIXME: This is not totally structual, which probably should be fixed. -/// See the HACKs below. -pub fn structurally_relate_consts<'tcx, R: TypeRelation<'tcx>>( - relation: &mut R, - mut a: ty::Const<'tcx>, - mut b: ty::Const<'tcx>, -) -> RelateResult<'tcx, ty::Const<'tcx>> { - debug!("{}.structurally_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b); - let tcx = relation.tcx(); - - if tcx.features().generic_const_exprs { - a = tcx.expand_abstract_consts(a); - b = tcx.expand_abstract_consts(b); - } - - debug!("{}.structurally_relate_consts(normed_a = {:?}, normed_b = {:?})", relation.tag(), a, 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 is_match = match (a.kind(), b.kind()) { - (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => { - // The caller should handle these cases! - bug!("var types encountered in structurally_relate_consts: {:?} {:?}", a, b) - } - - (ty::ConstKind::Error(_), _) => return Ok(a), - (_, ty::ConstKind::Error(_)) => return Ok(b), - - (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index == b_p.index => { - debug_assert_eq!(a_p.name, b_p.name, "param types with same index differ in name"); - true - } - (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2, - (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, - - // While this is slightly incorrect, it shouldn't matter for `min_const_generics` - // and is the better alternative to waiting until `generic_const_exprs` can - // be stabilized. - (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if au.def == bu.def => { - assert_eq!(a.ty(), b.ty()); - let args = relation.relate_with_variance( - ty::Variance::Invariant, - ty::VarianceDiagInfo::default(), - au.args, - bu.args, - )?; - return Ok(ty::Const::new_unevaluated( - tcx, - ty::UnevaluatedConst { def: au.def, args }, - a.ty(), - )); - } - // Before calling relate on exprs, it is necessary to ensure that the nested consts - // have identical types. - (ty::ConstKind::Expr(ae), ty::ConstKind::Expr(be)) => { - let r = relation; - - // FIXME(generic_const_exprs): is it possible to relate two consts which are not identical - // exprs? Should we care about that? - // FIXME(generic_const_exprs): relating the `ty()`s is a little weird since it is supposed to - // ICE If they mismatch. Unfortunately `ConstKind::Expr` is a little special and can be thought - // of as being generic over the argument types, however this is implicit so these types don't get - // related when we relate the args of the item this const arg is for. - let expr = match (ae, be) { - (Expr::Binop(a_op, al, ar), Expr::Binop(b_op, bl, br)) if a_op == b_op => { - r.relate(al.ty(), bl.ty())?; - r.relate(ar.ty(), br.ty())?; - Expr::Binop(a_op, r.consts(al, bl)?, r.consts(ar, br)?) - } - (Expr::UnOp(a_op, av), Expr::UnOp(b_op, bv)) if a_op == b_op => { - r.relate(av.ty(), bv.ty())?; - Expr::UnOp(a_op, r.consts(av, bv)?) - } - (Expr::Cast(ak, av, at), Expr::Cast(bk, bv, bt)) if ak == bk => { - r.relate(av.ty(), bv.ty())?; - Expr::Cast(ak, r.consts(av, bv)?, r.tys(at, bt)?) - } - (Expr::FunctionCall(af, aa), Expr::FunctionCall(bf, ba)) - if aa.len() == ba.len() => - { - r.relate(af.ty(), bf.ty())?; - let func = r.consts(af, bf)?; - let mut related_args = Vec::with_capacity(aa.len()); - for (a_arg, b_arg) in aa.iter().zip(ba.iter()) { - related_args.push(r.consts(a_arg, b_arg)?); - } - let related_args = tcx.mk_const_list(&related_args); - Expr::FunctionCall(func, related_args) - } - _ => return Err(TypeError::ConstMismatch(expected_found(a, b))), - }; - return Ok(ty::Const::new_expr(tcx, expr, a.ty())); - } - _ => false, - }; - if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(expected_found(a, b))) } -} - -impl<'tcx> Relate<'tcx> for &'tcx ty::List> { - fn relate>( +impl<'tcx> Relate> for &'tcx ty::List> { + fn relate>>( relation: &mut R, a: Self, b: Self, @@ -729,66 +106,65 @@ impl<'tcx> Relate<'tcx> for &'tcx ty::List> { b_v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); b_v.dedup(); if a_v.len() != b_v.len() { - return Err(TypeError::ExistentialMismatch(expected_found(a, b))); + return Err(TypeError::ExistentialMismatch(ExpectedFound::new(true, a, b))); } let v = iter::zip(a_v, b_v).map(|(ep_a, ep_b)| { match (ep_a.skip_binder(), ep_b.skip_binder()) { - (ExistentialPredicate::Trait(a), ExistentialPredicate::Trait(b)) => Ok(ep_a - .rebind(ExistentialPredicate::Trait( - relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), - ))), - (ExistentialPredicate::Projection(a), ExistentialPredicate::Projection(b)) => { - Ok(ep_a.rebind(ExistentialPredicate::Projection( + (ty::ExistentialPredicate::Trait(a), ty::ExistentialPredicate::Trait(b)) => { + Ok(ep_a.rebind(ty::ExistentialPredicate::Trait( relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), ))) } - (ExistentialPredicate::AutoTrait(a), ExistentialPredicate::AutoTrait(b)) - if a == b => - { - Ok(ep_a.rebind(ExistentialPredicate::AutoTrait(a))) - } - _ => Err(TypeError::ExistentialMismatch(expected_found(a, b))), + ( + ty::ExistentialPredicate::Projection(a), + ty::ExistentialPredicate::Projection(b), + ) => Ok(ep_a.rebind(ty::ExistentialPredicate::Projection( + relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), + ))), + ( + ty::ExistentialPredicate::AutoTrait(a), + ty::ExistentialPredicate::AutoTrait(b), + ) if a == b => Ok(ep_a.rebind(ty::ExistentialPredicate::AutoTrait(a))), + _ => Err(TypeError::ExistentialMismatch(ExpectedFound::new(true, a, b))), } }); tcx.mk_poly_existential_predicates_from_iter(v) } } -impl<'tcx> Relate<'tcx> for ty::ClosureArgs<'tcx> { - fn relate>( - relation: &mut R, - a: ty::ClosureArgs<'tcx>, - b: ty::ClosureArgs<'tcx>, - ) -> RelateResult<'tcx, ty::ClosureArgs<'tcx>> { - let args = relate_args_invariantly(relation, a.args, b.args)?; - Ok(ty::ClosureArgs { args }) +impl<'tcx> Relate> for hir::Safety { + fn relate>>( + _relation: &mut R, + a: hir::Safety, + b: hir::Safety, + ) -> RelateResult<'tcx, hir::Safety> { + if a != b { Err(TypeError::SafetyMismatch(ExpectedFound::new(true, a, b))) } else { Ok(a) } } } -impl<'tcx> Relate<'tcx> for ty::CoroutineArgs<'tcx> { - fn relate>( - relation: &mut R, - a: ty::CoroutineArgs<'tcx>, - b: ty::CoroutineArgs<'tcx>, - ) -> RelateResult<'tcx, ty::CoroutineArgs<'tcx>> { - let args = relate_args_invariantly(relation, a.args, b.args)?; - Ok(ty::CoroutineArgs { args }) +impl<'tcx> Relate> for abi::Abi { + fn relate>>( + _relation: &mut R, + a: abi::Abi, + b: abi::Abi, + ) -> RelateResult<'tcx, abi::Abi> { + if a == b { Ok(a) } else { Err(TypeError::AbiMismatch(ExpectedFound::new(true, a, b))) } } } -impl<'tcx> Relate<'tcx> for GenericArgsRef<'tcx> { - fn relate>( +impl<'tcx> Relate> for ty::GenericArgsRef<'tcx> { + fn relate>>( relation: &mut R, - a: GenericArgsRef<'tcx>, - b: GenericArgsRef<'tcx>, - ) -> RelateResult<'tcx, GenericArgsRef<'tcx>> { + a: ty::GenericArgsRef<'tcx>, + b: ty::GenericArgsRef<'tcx>, + ) -> RelateResult<'tcx, ty::GenericArgsRef<'tcx>> { relate_args_invariantly(relation, a, b) } } -impl<'tcx> Relate<'tcx> for ty::Region<'tcx> { - fn relate>( +impl<'tcx> Relate> for ty::Region<'tcx> { + fn relate>>( relation: &mut R, a: ty::Region<'tcx>, b: ty::Region<'tcx>, @@ -797,8 +173,8 @@ impl<'tcx> Relate<'tcx> for ty::Region<'tcx> { } } -impl<'tcx> Relate<'tcx> for ty::Const<'tcx> { - fn relate>( +impl<'tcx> Relate> for ty::Const<'tcx> { + fn relate>>( relation: &mut R, a: ty::Const<'tcx>, b: ty::Const<'tcx>, @@ -807,85 +183,70 @@ impl<'tcx> Relate<'tcx> for ty::Const<'tcx> { } } -impl<'tcx, T: Relate<'tcx>> Relate<'tcx> for ty::Binder<'tcx, T> { - fn relate>( +impl<'tcx> Relate> for ty::Expr<'tcx> { + fn relate>>( relation: &mut R, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> { - relation.binders(a, b) + ae: ty::Expr<'tcx>, + be: ty::Expr<'tcx>, + ) -> RelateResult<'tcx, ty::Expr<'tcx>> { + // FIXME(generic_const_exprs): is it possible to relate two consts which are not identical + // exprs? Should we care about that? + // FIXME(generic_const_exprs): relating the `ty()`s is a little weird since it is supposed to + // ICE If they mismatch. Unfortunately `ConstKind::Expr` is a little special and can be thought + // of as being generic over the argument types, however this is implicit so these types don't get + // related when we relate the args of the item this const arg is for. + match (ae.kind, be.kind) { + (ty::ExprKind::Binop(a_binop), ty::ExprKind::Binop(b_binop)) if a_binop == b_binop => {} + (ty::ExprKind::UnOp(a_unop), ty::ExprKind::UnOp(b_unop)) if a_unop == b_unop => {} + (ty::ExprKind::FunctionCall, ty::ExprKind::FunctionCall) => {} + (ty::ExprKind::Cast(a_kind), ty::ExprKind::Cast(b_kind)) if a_kind == b_kind => {} + _ => return Err(TypeError::Mismatch), + } + + let args = relation.relate(ae.args(), be.args())?; + Ok(ty::Expr::new(ae.kind, args)) } } -impl<'tcx> Relate<'tcx> for GenericArg<'tcx> { - fn relate>( +impl<'tcx> Relate> for ty::GenericArg<'tcx> { + fn relate>>( relation: &mut R, - a: GenericArg<'tcx>, - b: GenericArg<'tcx>, - ) -> RelateResult<'tcx, GenericArg<'tcx>> { + a: ty::GenericArg<'tcx>, + b: ty::GenericArg<'tcx>, + ) -> RelateResult<'tcx, ty::GenericArg<'tcx>> { match (a.unpack(), b.unpack()) { - (GenericArgKind::Lifetime(a_lt), GenericArgKind::Lifetime(b_lt)) => { + (ty::GenericArgKind::Lifetime(a_lt), ty::GenericArgKind::Lifetime(b_lt)) => { Ok(relation.relate(a_lt, b_lt)?.into()) } - (GenericArgKind::Type(a_ty), GenericArgKind::Type(b_ty)) => { + (ty::GenericArgKind::Type(a_ty), ty::GenericArgKind::Type(b_ty)) => { Ok(relation.relate(a_ty, b_ty)?.into()) } - (GenericArgKind::Const(a_ct), GenericArgKind::Const(b_ct)) => { + (ty::GenericArgKind::Const(a_ct), ty::GenericArgKind::Const(b_ct)) => { Ok(relation.relate(a_ct, b_ct)?.into()) } - (GenericArgKind::Lifetime(unpacked), x) => { + (ty::GenericArgKind::Lifetime(unpacked), x) => { bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) } - (GenericArgKind::Type(unpacked), x) => { + (ty::GenericArgKind::Type(unpacked), x) => { bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) } - (GenericArgKind::Const(unpacked), x) => { + (ty::GenericArgKind::Const(unpacked), x) => { bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) } } } } -impl<'tcx> Relate<'tcx> for ty::PredicatePolarity { - fn relate>( - _relation: &mut R, - a: ty::PredicatePolarity, - b: ty::PredicatePolarity, - ) -> RelateResult<'tcx, ty::PredicatePolarity> { - if a != b { Err(TypeError::PolarityMismatch(expected_found(a, b))) } else { Ok(a) } - } -} - -impl<'tcx> Relate<'tcx> for ty::TraitPredicate<'tcx> { - fn relate>( - relation: &mut R, - a: ty::TraitPredicate<'tcx>, - b: ty::TraitPredicate<'tcx>, - ) -> RelateResult<'tcx, ty::TraitPredicate<'tcx>> { - Ok(ty::TraitPredicate { - trait_ref: relation.relate(a.trait_ref, b.trait_ref)?, - polarity: relation.relate(a.polarity, b.polarity)?, - }) - } -} - -impl<'tcx> Relate<'tcx> for Term<'tcx> { - fn relate>( +impl<'tcx> Relate> for ty::Term<'tcx> { + fn relate>>( relation: &mut R, a: Self, b: Self, ) -> RelateResult<'tcx, Self> { Ok(match (a.unpack(), b.unpack()) { - (TermKind::Ty(a), TermKind::Ty(b)) => relation.relate(a, b)?.into(), - (TermKind::Const(a), TermKind::Const(b)) => relation.relate(a, b)?.into(), + (ty::TermKind::Ty(a), ty::TermKind::Ty(b)) => relation.relate(a, b)?.into(), + (ty::TermKind::Const(a), ty::TermKind::Const(b)) => relation.relate(a, b)?.into(), _ => return Err(TypeError::Mismatch), }) } } - -/////////////////////////////////////////////////////////////////////////// -// Error handling - -pub fn expected_found(a: T, b: T) -> ExpectedFound { - ExpectedFound::new(true, a, b) -} diff --git a/compiler/rustc_middle/src/ty/rvalue_scopes.rs b/compiler/rustc_middle/src/ty/rvalue_scopes.rs index cb2f7284eaa0..8ec7946e7180 100644 --- a/compiler/rustc_middle/src/ty/rvalue_scopes.rs +++ b/compiler/rustc_middle/src/ty/rvalue_scopes.rs @@ -2,6 +2,7 @@ use crate::middle::region::{Scope, ScopeData, ScopeTree}; use rustc_hir as hir; use rustc_hir::ItemLocalMap; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; +use tracing::debug; /// `RvalueScopes` is a mapping from sub-expressions to _extended_ lifetime as determined by /// rules laid out in `rustc_hir_analysis::check::rvalue_scopes`. diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 7d24824d568f..71e2e3e9f994 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -13,7 +13,7 @@ use rustc_ast_ir::visit::VisitorResult; use rustc_hir::def::Namespace; use rustc_span::source_map::Spanned; use rustc_target::abi::TyAndLayout; -use rustc_type_ir::{ConstKind, DebugWithInfcx, InferCtxtLike, WithInfcx}; +use rustc_type_ir::ConstKind; use std::fmt::{self, Debug}; @@ -83,14 +83,6 @@ impl fmt::Debug for ty::LateParamRegion { } } -impl<'tcx> ty::DebugWithInfcx> for Ty<'tcx> { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - this.data.fmt(f) - } -} impl<'tcx> fmt::Debug for Ty<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { with_no_trimmed_paths!(fmt::Debug::fmt(self.kind(), f)) @@ -121,50 +113,33 @@ impl<'tcx> fmt::Debug for ty::Clause<'tcx> { } } -impl<'tcx> DebugWithInfcx> for Pattern<'tcx> { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - match &**this.data { - ty::PatternKind::Range { start, end, include_end } => f - .debug_struct("Pattern::Range") - .field("start", start) - .field("end", end) - .field("include_end", include_end) - .finish(), - } - } -} - impl<'tcx> fmt::Debug for ty::consts::Expr<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} -impl<'tcx> DebugWithInfcx> for ty::consts::Expr<'tcx> { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - match this.data { - ty::Expr::Binop(op, lhs, rhs) => { - write!(f, "({op:?}: {:?}, {:?})", &this.wrap(lhs), &this.wrap(rhs)) + match self.kind { + ty::ExprKind::Binop(op) => { + let (lhs_ty, rhs_ty, lhs, rhs) = self.binop_args(); + write!(f, "({op:?}: ({:?}: {:?}), ({:?}: {:?}))", lhs, lhs_ty, rhs, rhs_ty,) } - ty::Expr::UnOp(op, rhs) => write!(f, "({op:?}: {:?})", &this.wrap(rhs)), - ty::Expr::FunctionCall(func, args) => { - write!(f, "{:?}(", &this.wrap(func))?; - for arg in args.as_slice().iter().rev().skip(1).rev() { - write!(f, "{:?}, ", &this.wrap(arg))?; + ty::ExprKind::UnOp(op) => { + let (rhs_ty, rhs) = self.unop_args(); + write!(f, "({op:?}: ({:?}: {:?}))", rhs, rhs_ty) + } + ty::ExprKind::FunctionCall => { + let (func_ty, func, args) = self.call_args(); + let args = args.collect::>(); + write!(f, "({:?}: {:?})(", func, func_ty)?; + for arg in args.iter().rev().skip(1).rev() { + write!(f, "{:?}, ", arg)?; } if let Some(arg) = args.last() { - write!(f, "{:?}", &this.wrap(arg))?; + write!(f, "{:?}", arg)?; } write!(f, ")") } - ty::Expr::Cast(cast_kind, lhs, rhs) => { - write!(f, "({cast_kind:?}: {:?}, {:?})", &this.wrap(lhs), &this.wrap(rhs)) + ty::ExprKind::Cast(kind) => { + let (value_ty, value, to_ty) = self.cast_args(); + write!(f, "({kind:?}: ({:?}: {:?}), {:?})", value, value_ty, to_ty) } } } @@ -172,35 +147,22 @@ impl<'tcx> DebugWithInfcx> for ty::consts::Expr<'tcx> { impl<'tcx> fmt::Debug for ty::Const<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} -impl<'tcx> DebugWithInfcx> for ty::Const<'tcx> { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { // If this is a value, we spend some effort to make it look nice. - if let ConstKind::Value(_) = this.data.kind() { + if let ConstKind::Value(_, _) = self.kind() { return ty::tls::with(move |tcx| { // Somehow trying to lift the valtree results in lifetime errors, so we lift the // entire constant. - let lifted = tcx.lift(*this.data).unwrap(); - let ConstKind::Value(valtree) = lifted.kind() else { + let lifted = tcx.lift(*self).unwrap(); + let ConstKind::Value(ty, valtree) = lifted.kind() else { bug!("we checked that this is a valtree") }; let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); - cx.pretty_print_const_valtree(valtree, lifted.ty(), /*print_ty*/ true)?; + cx.pretty_print_const_valtree(valtree, ty, /*print_ty*/ true)?; f.write_str(&cx.into_buffer()) }); } // Fall back to something verbose. - write!( - f, - "{kind:?}: {ty:?}", - ty = &this.map(|data| data.ty()), - kind = &this.map(|data| data.kind()) - ) + write!(f, "{:?}", self.kind()) } } @@ -232,56 +194,12 @@ impl<'tcx> fmt::Debug for GenericArg<'tcx> { } } } -impl<'tcx> DebugWithInfcx> for GenericArg<'tcx> { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - match this.data.unpack() { - GenericArgKind::Lifetime(lt) => write!(f, "{:?}", &this.wrap(lt)), - GenericArgKind::Const(ct) => write!(f, "{:?}", &this.wrap(ct)), - GenericArgKind::Type(ty) => write!(f, "{:?}", &this.wrap(ty)), - } - } -} impl<'tcx> fmt::Debug for Region<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.kind()) } } -impl<'tcx> DebugWithInfcx> for Region<'tcx> { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - write!(f, "{:?}", &this.map(|data| data.kind())) - } -} - -impl<'tcx> DebugWithInfcx> for ty::RegionVid { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - match this.infcx.universe_of_lt(*this.data) { - Some(universe) => write!(f, "'?{}_{}", this.data.index(), universe.index()), - None => write!(f, "{:?}", this.data), - } - } -} - -impl<'tcx, T: DebugWithInfcx>> DebugWithInfcx> for ty::Binder<'tcx, T> { - fn fmt>>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - f.debug_tuple("Binder") - .field(&this.map(|data| data.as_ref().skip_binder())) - .field(&this.data.bound_vars()) - .finish() - } -} /////////////////////////////////////////////////////////////////////////// // Atomic structs @@ -305,7 +223,6 @@ TrivialTypeTraversalImpls! { ::rustc_target::abi::FieldIdx, ::rustc_target::abi::VariantIdx, crate::middle::region::Scope, - crate::ty::FloatTy, ::rustc_ast::InlineAsmOptions, ::rustc_ast::InlineAsmTemplatePiece, ::rustc_ast::NodeId, @@ -325,7 +242,7 @@ TrivialTypeTraversalImpls! { crate::traits::Reveal, crate::ty::adjustment::AutoBorrowMutability, crate::ty::AdtKind, - crate::ty::BoundConstness, + crate::ty::BoundRegion, // Including `BoundRegionKind` is a *bit* dubious, but direct // references to bound region appear in `ty::Error`, and aren't // really meant to be folded. In general, we can only fold a fully @@ -333,16 +250,11 @@ TrivialTypeTraversalImpls! { crate::ty::BoundRegionKind, crate::ty::AssocItem, crate::ty::AssocKind, - crate::ty::AliasTyKind, crate::ty::Placeholder, crate::ty::Placeholder, crate::ty::Placeholder, crate::ty::LateParamRegion, - crate::ty::InferTy, - crate::ty::IntVarValue, crate::ty::adjustment::PointerCoercion, - crate::ty::RegionVid, - crate::ty::Variance, ::rustc_span::Span, ::rustc_span::symbol::Ident, ::rustc_errors::ErrorGuaranteed, @@ -383,13 +295,10 @@ impl<'tcx, T: Lift>> Lift> for Option { impl<'a, 'tcx> Lift> for Term<'a> { type Lifted = ty::Term<'tcx>; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { - Some( - match self.unpack() { - TermKind::Ty(ty) => TermKind::Ty(tcx.lift(ty)?), - TermKind::Const(c) => TermKind::Const(tcx.lift(c)?), - } - .pack(), - ) + match self.unpack() { + TermKind::Ty(ty) => tcx.lift(ty).map(Into::into), + TermKind::Const(c) => tcx.lift(c).map(Into::into), + } } } @@ -402,38 +311,6 @@ impl<'tcx> TypeVisitable> for ty::AdtDef<'tcx> { } } -impl<'tcx, T: TypeFoldable>> TypeFoldable> for ty::Binder<'tcx, T> { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - folder.try_fold_binder(self) - } -} - -impl<'tcx, T: TypeVisitable>> TypeVisitable> for ty::Binder<'tcx, T> { - fn visit_with>>(&self, visitor: &mut V) -> V::Result { - visitor.visit_binder(self) - } -} - -impl<'tcx, T: TypeFoldable>> TypeSuperFoldable> for ty::Binder<'tcx, T> { - fn try_super_fold_with>>( - self, - folder: &mut F, - ) -> Result { - self.try_map_bound(|ty| ty.try_fold_with(folder)) - } -} - -impl<'tcx, T: TypeVisitable>> TypeSuperVisitable> - for ty::Binder<'tcx, T> -{ - fn super_visit_with>>(&self, visitor: &mut V) -> V::Result { - self.as_ref().skip_binder().visit_with(visitor) - } -} - impl<'tcx> TypeFoldable> for &'tcx ty::List> { fn try_fold_with>>( self, @@ -686,7 +563,6 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { self, folder: &mut F, ) -> Result { - let ty = self.ty().try_fold_with(folder)?; let kind = match self.kind() { ConstKind::Param(p) => ConstKind::Param(p.try_fold_with(folder)?), ConstKind::Infer(i) => ConstKind::Infer(i.try_fold_with(folder)?), @@ -695,21 +571,18 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { } ConstKind::Placeholder(p) => ConstKind::Placeholder(p.try_fold_with(folder)?), ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?), - ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?), + ConstKind::Value(t, v) => { + ConstKind::Value(t.try_fold_with(folder)?, v.try_fold_with(folder)?) + } ConstKind::Error(e) => ConstKind::Error(e.try_fold_with(folder)?), ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?), }; - if ty != self.ty() || kind != self.kind() { - Ok(folder.interner().mk_ct_from_kind(kind, ty)) - } else { - Ok(self) - } + if kind != self.kind() { Ok(folder.interner().mk_ct_from_kind(kind)) } else { Ok(self) } } } impl<'tcx> TypeSuperVisitable> for ty::Const<'tcx> { fn super_visit_with>>(&self, visitor: &mut V) -> V::Result { - try_visit!(self.ty().visit_with(visitor)); match self.kind() { ConstKind::Param(p) => p.visit_with(visitor), ConstKind::Infer(i) => i.visit_with(visitor), @@ -719,7 +592,10 @@ impl<'tcx> TypeSuperVisitable> for ty::Const<'tcx> { } ConstKind::Placeholder(p) => p.visit_with(visitor), ConstKind::Unevaluated(uv) => uv.visit_with(visitor), - ConstKind::Value(v) => v.visit_with(visitor), + ConstKind::Value(t, v) => { + try_visit!(t.visit_with(visitor)); + v.visit_with(visitor) + } ConstKind::Error(e) => e.visit_with(visitor), ConstKind::Expr(e) => e.visit_with(visitor), } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 7c41c5f512e6..9c8a3484aa5b 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -3,35 +3,34 @@ #![allow(rustc::usage_of_ty_tykind)] use crate::infer::canonical::Canonical; -use crate::ty::visit::ValidateBoundVars; use crate::ty::InferTy::*; use crate::ty::{ self, AdtDef, BoundRegionKind, Discr, Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, + TypeVisitable, TypeVisitor, }; use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use crate::ty::{List, ParamEnv}; use hir::def::{CtorKind, DefKind}; use rustc_data_structures::captures::Captures; -use rustc_errors::{DiagArgValue, ErrorGuaranteed, IntoDiagArg, MultiSpan}; +use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; -use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable}; +use rustc_macros::{extension, HashStable, TyDecodable, TyEncodable, TypeFoldable}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; -use rustc_target::spec::abi::{self, Abi}; +use rustc_target::spec::abi; +use rustc_type_ir::visit::TypeVisitableExt; use std::assert_matches::debug_assert_matches; use std::borrow::Cow; use std::iter; -use std::ops::{ControlFlow, Deref, Range}; -use ty::util::IntTypeExt; +use std::ops::{ControlFlow, Range}; +use ty::util::{AsyncDropGlueMorphology, IntTypeExt}; use rustc_type_ir::TyKind::*; use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind}; -use super::fold::FnMutDelegate; use super::GenericParamDefKind; // Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here @@ -40,6 +39,8 @@ pub type TyKind<'tcx> = ir::TyKind>; pub type TypeAndMut<'tcx> = ir::TypeAndMut>; pub type AliasTy<'tcx> = ir::AliasTy>; pub type FnSig<'tcx> = ir::FnSig>; +pub type Binder<'tcx, T> = ir::Binder, T>; +pub type EarlyBinder<'tcx, T> = ir::EarlyBinder, T>; pub trait Article { fn article(&self) -> &'static str; @@ -59,631 +60,14 @@ impl<'tcx> Article for TyKind<'tcx> { } } -/// A closure can be modeled as a struct that looks like: -/// ```ignore (illustrative) -/// struct Closure<'l0...'li, T0...Tj, CK, CS, U>(...U); -/// ``` -/// where: -/// -/// - 'l0...'li and T0...Tj are the generic parameters -/// in scope on the function that defined the closure, -/// - CK represents the *closure kind* (Fn vs FnMut vs FnOnce). This -/// is rather hackily encoded via a scalar type. See -/// `Ty::to_opt_closure_kind` for details. -/// - CS represents the *closure signature*, representing as a `fn()` -/// type. For example, `fn(u32, u32) -> u32` would mean that the closure -/// implements `CK<(u32, u32), Output = u32>`, where `CK` is the trait -/// specified above. -/// - U is a type parameter representing the types of its upvars, tupled up -/// (borrowed, if appropriate; that is, if a U field represents a by-ref upvar, -/// and the up-var has the type `Foo`, then that field of U will be `&Foo`). -/// -/// So, for example, given this function: -/// ```ignore (illustrative) -/// fn foo<'a, T>(data: &'a mut T) { -/// do(|| data.count += 1) -/// } -/// ``` -/// the type of the closure would be something like: -/// ```ignore (illustrative) -/// struct Closure<'a, T, U>(...U); -/// ``` -/// Note that the type of the upvar is not specified in the struct. -/// You may wonder how the impl would then be able to use the upvar, -/// if it doesn't know it's type? The answer is that the impl is -/// (conceptually) not fully generic over Closure but rather tied to -/// instances with the expected upvar types: -/// ```ignore (illustrative) -/// impl<'b, 'a, T> FnMut() for Closure<'a, T, (&'b mut &'a mut T,)> { -/// ... -/// } -/// ``` -/// You can see that the *impl* fully specified the type of the upvar -/// and thus knows full well that `data` has type `&'b mut &'a mut T`. -/// (Here, I am assuming that `data` is mut-borrowed.) -/// -/// Now, the last question you may ask is: Why include the upvar types -/// in an extra type parameter? The reason for this design is that the -/// upvar types can reference lifetimes that are internal to the -/// creating function. In my example above, for example, the lifetime -/// `'b` represents the scope of the closure itself; this is some -/// subset of `foo`, probably just the scope of the call to the to -/// `do()`. If we just had the lifetime/type parameters from the -/// enclosing function, we couldn't name this lifetime `'b`. Note that -/// there can also be lifetimes in the types of the upvars themselves, -/// if one of them happens to be a reference to something that the -/// creating fn owns. -/// -/// OK, you say, so why not create a more minimal set of parameters -/// that just includes the extra lifetime parameters? The answer is -/// primarily that it would be hard --- we don't know at the time when -/// we create the closure type what the full types of the upvars are, -/// nor do we know which are borrowed and which are not. In this -/// design, we can just supply a fresh type parameter and figure that -/// out later. -/// -/// All right, you say, but why include the type parameters from the -/// original function then? The answer is that codegen may need them -/// when monomorphizing, and they may not appear in the upvars. A -/// closure could capture no variables but still make use of some -/// in-scope type parameter with a bound (e.g., if our example above -/// had an extra `U: Default`, and the closure called `U::default()`). -/// -/// There is another reason. This design (implicitly) prohibits -/// closures from capturing themselves (except via a trait -/// object). This simplifies closure inference considerably, since it -/// means that when we infer the kind of a closure or its upvars, we -/// don't have to handle cycles where the decisions we make for -/// closure C wind up influencing the decisions we ought to make for -/// closure C (which would then require fixed point iteration to -/// handle). Plus it fixes an ICE. :P -/// -/// ## Coroutines -/// -/// Coroutines are handled similarly in `CoroutineArgs`. The set of -/// type parameters is similar, but `CK` and `CS` are replaced by the -/// following type parameters: -/// -/// * `GS`: The coroutine's "resume type", which is the type of the -/// argument passed to `resume`, and the type of `yield` expressions -/// inside the coroutine. -/// * `GY`: The "yield type", which is the type of values passed to -/// `yield` inside the coroutine. -/// * `GR`: The "return type", which is the type of value returned upon -/// completion of the coroutine. -/// * `GW`: The "coroutine witness". -#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable, Lift)] -pub struct ClosureArgs<'tcx> { - /// Lifetime and type parameters from the enclosing function, - /// concatenated with a tuple containing the types of the upvars. - /// - /// These are separated out because codegen wants to pass them around - /// when monomorphizing. - pub args: GenericArgsRef<'tcx>, -} - -/// Struct returned by `split()`. -pub struct ClosureArgsParts<'tcx> { - /// This is the args of the typeck root. - pub parent_args: &'tcx [GenericArg<'tcx>], - /// Represents the maximum calling capability of the closure. - pub closure_kind_ty: Ty<'tcx>, - /// Captures the closure's signature. This closure signature is "tupled", and - /// thus has a peculiar signature of `extern "rust-call" fn((Args, ...)) -> Ty`. - pub closure_sig_as_fn_ptr_ty: Ty<'tcx>, - /// The upvars captured by the closure. Remains an inference variable - /// until the upvar analysis, which happens late in HIR typeck. - pub tupled_upvars_ty: Ty<'tcx>, -} - -impl<'tcx> ClosureArgs<'tcx> { - /// Construct `ClosureArgs` from `ClosureArgsParts`, containing `Args` - /// for the closure parent, alongside additional closure-specific components. - pub fn new(tcx: TyCtxt<'tcx>, parts: ClosureArgsParts<'tcx>) -> ClosureArgs<'tcx> { - ClosureArgs { - args: tcx.mk_args_from_iter(parts.parent_args.iter().copied().chain([ - parts.closure_kind_ty.into(), - parts.closure_sig_as_fn_ptr_ty.into(), - parts.tupled_upvars_ty.into(), - ])), - } - } - - /// Divides the closure args into their respective components. - /// The ordering assumed here must match that used by `ClosureArgs::new` above. - fn split(self) -> ClosureArgsParts<'tcx> { - match self.args[..] { - [ref parent_args @ .., closure_kind_ty, closure_sig_as_fn_ptr_ty, tupled_upvars_ty] => { - ClosureArgsParts { - parent_args, - closure_kind_ty: closure_kind_ty.expect_ty(), - closure_sig_as_fn_ptr_ty: closure_sig_as_fn_ptr_ty.expect_ty(), - tupled_upvars_ty: tupled_upvars_ty.expect_ty(), - } - } - _ => bug!("closure args missing synthetics"), - } - } - - /// Returns the generic parameters of the closure's parent. - pub fn parent_args(self) -> &'tcx [GenericArg<'tcx>] { - self.split().parent_args - } - - /// Returns an iterator over the list of types of captured paths by the closure. - /// In case there was a type error in figuring out the types of the captured path, an - /// empty iterator is returned. - #[inline] - pub fn upvar_tys(self) -> &'tcx List> { - match *self.tupled_upvars_ty().kind() { - TyKind::Error(_) => ty::List::empty(), - TyKind::Tuple(tys) => tys, - TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"), - ty => bug!("Unexpected representation of upvar types tuple {:?}", ty), - } - } - - /// Returns the tuple type representing the upvars for this closure. - #[inline] - pub fn tupled_upvars_ty(self) -> Ty<'tcx> { - self.split().tupled_upvars_ty - } - - /// 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(args)`. - pub fn kind_ty(self) -> Ty<'tcx> { - self.split().closure_kind_ty - } - - /// Returns the `fn` pointer type representing the closure signature for this - /// closure. - // FIXME(eddyb) this should be unnecessary, as the shallowly resolved - // type is known at the time of the creation of `ClosureArgs`, - // see `rustc_hir_analysis::check::closure`. - pub fn sig_as_fn_ptr_ty(self) -> Ty<'tcx> { - self.split().closure_sig_as_fn_ptr_ty - } - - /// Returns the closure kind for this closure; only usable outside - /// of an inference context, because in that context we know that - /// there are no type variables. - /// - /// If you have an inference context, use `infcx.closure_kind()`. - pub fn kind(self) -> ty::ClosureKind { - self.kind_ty().to_opt_closure_kind().unwrap() - } - - /// Extracts the signature from the closure. - pub fn sig(self) -> ty::PolyFnSig<'tcx> { - match *self.sig_as_fn_ptr_ty().kind() { - ty::FnPtr(sig) => sig, - ty => bug!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {ty:?}"), - } - } - - pub fn print_as_impl_trait(self) -> ty::print::PrintClosureAsImpl<'tcx> { - ty::print::PrintClosureAsImpl { closure: self } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable, Lift)] -pub struct CoroutineClosureArgs<'tcx> { - pub args: GenericArgsRef<'tcx>, -} - -/// See docs for explanation of how each argument is used. -/// -/// See [`CoroutineClosureSignature`] for how these arguments are put together -/// to make a callable [`FnSig`] suitable for typeck and borrowck. -pub struct CoroutineClosureArgsParts<'tcx> { - /// This is the args of the typeck root. - pub parent_args: &'tcx [GenericArg<'tcx>], - /// Represents the maximum calling capability of the closure. - pub closure_kind_ty: Ty<'tcx>, - /// Represents all of the relevant parts of the coroutine returned by this - /// coroutine-closure. This signature parts type will have the general - /// shape of `fn(tupled_inputs, resume_ty) -> (return_ty, yield_ty)`, where - /// `resume_ty`, `return_ty`, and `yield_ty` are the respective types for the - /// coroutine returned by the coroutine-closure. - /// - /// Use `coroutine_closure_sig` to break up this type rather than using it - /// yourself. - pub signature_parts_ty: Ty<'tcx>, - /// The upvars captured by the closure. Remains an inference variable - /// until the upvar analysis, which happens late in HIR typeck. - pub tupled_upvars_ty: Ty<'tcx>, - /// a function pointer that has the shape `for<'env> fn() -> (&'env T, ...)`. - /// This allows us to represent the binder of the self-captures of the closure. - /// - /// For example, if the coroutine returned by the closure borrows `String` - /// from the closure's upvars, this will be `for<'env> fn() -> (&'env String,)`, - /// while the `tupled_upvars_ty`, representing the by-move version of the same - /// captures, will be `(String,)`. - pub coroutine_captures_by_ref_ty: Ty<'tcx>, - /// Witness type returned by the generator produced by this coroutine-closure. - pub coroutine_witness_ty: Ty<'tcx>, -} - -impl<'tcx> CoroutineClosureArgs<'tcx> { - pub fn new( - tcx: TyCtxt<'tcx>, - parts: CoroutineClosureArgsParts<'tcx>, - ) -> CoroutineClosureArgs<'tcx> { - CoroutineClosureArgs { - args: tcx.mk_args_from_iter(parts.parent_args.iter().copied().chain([ - parts.closure_kind_ty.into(), - parts.signature_parts_ty.into(), - parts.tupled_upvars_ty.into(), - parts.coroutine_captures_by_ref_ty.into(), - parts.coroutine_witness_ty.into(), - ])), - } - } - - fn split(self) -> CoroutineClosureArgsParts<'tcx> { - match self.args[..] { - [ - ref parent_args @ .., - closure_kind_ty, - signature_parts_ty, - tupled_upvars_ty, - coroutine_captures_by_ref_ty, - coroutine_witness_ty, - ] => CoroutineClosureArgsParts { - parent_args, - closure_kind_ty: closure_kind_ty.expect_ty(), - signature_parts_ty: signature_parts_ty.expect_ty(), - tupled_upvars_ty: tupled_upvars_ty.expect_ty(), - coroutine_captures_by_ref_ty: coroutine_captures_by_ref_ty.expect_ty(), - coroutine_witness_ty: coroutine_witness_ty.expect_ty(), - }, - _ => bug!("closure args missing synthetics"), - } - } - - pub fn parent_args(self) -> &'tcx [GenericArg<'tcx>] { - self.split().parent_args - } - - #[inline] - pub fn upvar_tys(self) -> &'tcx List> { - match self.tupled_upvars_ty().kind() { - TyKind::Error(_) => ty::List::empty(), - TyKind::Tuple(..) => self.tupled_upvars_ty().tuple_fields(), - TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"), - ty => bug!("Unexpected representation of upvar types tuple {:?}", ty), - } - } - - #[inline] - pub fn tupled_upvars_ty(self) -> Ty<'tcx> { - self.split().tupled_upvars_ty - } - - pub fn kind_ty(self) -> Ty<'tcx> { - self.split().closure_kind_ty - } - - pub fn kind(self) -> ty::ClosureKind { - self.kind_ty().to_opt_closure_kind().unwrap() - } - - pub fn signature_parts_ty(self) -> Ty<'tcx> { - self.split().signature_parts_ty - } - - pub fn coroutine_closure_sig(self) -> ty::Binder<'tcx, CoroutineClosureSignature<'tcx>> { - let interior = self.coroutine_witness_ty(); - let ty::FnPtr(sig) = self.signature_parts_ty().kind() else { bug!() }; - sig.map_bound(|sig| { - let [resume_ty, tupled_inputs_ty] = *sig.inputs() else { - bug!(); - }; - let [yield_ty, return_ty] = **sig.output().tuple_fields() else { bug!() }; - CoroutineClosureSignature { - interior, - tupled_inputs_ty, - resume_ty, - yield_ty, - return_ty, - c_variadic: sig.c_variadic, - safety: sig.safety, - abi: sig.abi, - } - }) - } - - pub fn coroutine_captures_by_ref_ty(self) -> Ty<'tcx> { - self.split().coroutine_captures_by_ref_ty - } - - pub fn coroutine_witness_ty(self) -> Ty<'tcx> { - self.split().coroutine_witness_ty - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)] -pub struct CoroutineClosureSignature<'tcx> { - pub interior: Ty<'tcx>, - pub tupled_inputs_ty: Ty<'tcx>, - pub resume_ty: Ty<'tcx>, - pub yield_ty: Ty<'tcx>, - pub return_ty: Ty<'tcx>, - - // Like the `fn_sig_as_fn_ptr_ty` of a regular closure, these types - // never actually differ. But we save them rather than recreating them - // from scratch just for good measure. - /// Always false - pub c_variadic: bool, - /// Always [`hir::Safety::Safe`] - pub safety: hir::Safety, - /// Always [`abi::Abi::RustCall`] - pub abi: abi::Abi, -} - -impl<'tcx> CoroutineClosureSignature<'tcx> { - /// Construct a coroutine from the closure signature. Since a coroutine signature - /// is agnostic to the type of generator that is returned (by-ref/by-move), - /// the caller must specify what "flavor" of generator that they'd like to - /// create. Additionally, they must manually compute the upvars of the closure. - /// - /// This helper is not really meant to be used directly except for early on - /// during typeck, when we want to put inference vars into the kind and upvars tys. - /// When the kind and upvars are known, use the other helper functions. - pub fn to_coroutine( - self, - tcx: TyCtxt<'tcx>, - parent_args: &'tcx [GenericArg<'tcx>], - coroutine_kind_ty: Ty<'tcx>, - coroutine_def_id: DefId, - tupled_upvars_ty: Ty<'tcx>, - ) -> Ty<'tcx> { - let coroutine_args = ty::CoroutineArgs::new( - tcx, - ty::CoroutineArgsParts { - parent_args, - kind_ty: coroutine_kind_ty, - resume_ty: self.resume_ty, - yield_ty: self.yield_ty, - return_ty: self.return_ty, - witness: self.interior, - tupled_upvars_ty, - }, - ); - - Ty::new_coroutine(tcx, coroutine_def_id, coroutine_args.args) - } - - /// Given known upvars and a [`ClosureKind`](ty::ClosureKind), compute the coroutine - /// returned by that corresponding async fn trait. - /// - /// This function expects the upvars to have been computed already, and doesn't check - /// that the `ClosureKind` is actually supported by the coroutine-closure. - pub fn to_coroutine_given_kind_and_upvars( - self, - tcx: TyCtxt<'tcx>, - parent_args: &'tcx [GenericArg<'tcx>], - coroutine_def_id: DefId, - goal_kind: ty::ClosureKind, - env_region: ty::Region<'tcx>, - closure_tupled_upvars_ty: Ty<'tcx>, - coroutine_captures_by_ref_ty: Ty<'tcx>, - ) -> Ty<'tcx> { - let tupled_upvars_ty = Self::tupled_upvars_by_closure_kind( - tcx, - goal_kind, - self.tupled_inputs_ty, - closure_tupled_upvars_ty, - coroutine_captures_by_ref_ty, - env_region, - ); - - self.to_coroutine( - tcx, - parent_args, - Ty::from_coroutine_closure_kind(tcx, goal_kind), - coroutine_def_id, - tupled_upvars_ty, - ) - } - - /// Compute the tupled upvars that a coroutine-closure's output coroutine - /// would return for the given `ClosureKind`. - /// - /// When `ClosureKind` is `FnMut`/`Fn`, then this will use the "captures by ref" - /// to return a set of upvars which are borrowed with the given `env_region`. - /// - /// This ensures that the `AsyncFn::call` will return a coroutine whose upvars' - /// lifetimes are related to the lifetime of the borrow on the closure made for - /// the call. This allows borrowck to enforce the self-borrows correctly. - pub fn tupled_upvars_by_closure_kind( - tcx: TyCtxt<'tcx>, - kind: ty::ClosureKind, - tupled_inputs_ty: Ty<'tcx>, - closure_tupled_upvars_ty: Ty<'tcx>, - coroutine_captures_by_ref_ty: Ty<'tcx>, - env_region: ty::Region<'tcx>, - ) -> Ty<'tcx> { - match kind { - ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { - let ty::FnPtr(sig) = *coroutine_captures_by_ref_ty.kind() else { - bug!(); - }; - let coroutine_captures_by_ref_ty = tcx.replace_escaping_bound_vars_uncached( - sig.output().skip_binder(), - FnMutDelegate { - consts: &mut |c, t| ty::Const::new_bound(tcx, ty::INNERMOST, c, t), - types: &mut |t| Ty::new_bound(tcx, ty::INNERMOST, t), - regions: &mut |_| env_region, - }, - ); - Ty::new_tup_from_iter( - tcx, - tupled_inputs_ty - .tuple_fields() - .iter() - .chain(coroutine_captures_by_ref_ty.tuple_fields()), - ) - } - ty::ClosureKind::FnOnce => Ty::new_tup_from_iter( - tcx, - tupled_inputs_ty - .tuple_fields() - .iter() - .chain(closure_tupled_upvars_ty.tuple_fields()), - ), - } - } -} -/// Similar to `ClosureArgs`; see the above documentation for more. -#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)] -pub struct CoroutineArgs<'tcx> { - pub args: GenericArgsRef<'tcx>, -} - -pub struct CoroutineArgsParts<'tcx> { - /// This is the args of the typeck root. - pub parent_args: &'tcx [GenericArg<'tcx>], - - /// The coroutines returned by a coroutine-closure's `AsyncFnOnce`/`AsyncFnMut` - /// implementations must be distinguished since the former takes the closure's - /// upvars by move, and the latter takes the closure's upvars by ref. - /// - /// This field distinguishes these fields so that codegen can select the right - /// body for the coroutine. This has the same type representation as the closure - /// kind: `i8`/`i16`/`i32`. - /// - /// For regular coroutines, this field will always just be `()`. - pub kind_ty: Ty<'tcx>, - - pub resume_ty: Ty<'tcx>, - pub yield_ty: Ty<'tcx>, - pub return_ty: Ty<'tcx>, - - /// The interior type of the coroutine. - /// Represents all types that are stored in locals - /// in the coroutine's body. - pub witness: Ty<'tcx>, - - /// The upvars captured by the closure. Remains an inference variable - /// until the upvar analysis, which happens late in HIR typeck. - pub tupled_upvars_ty: Ty<'tcx>, -} - -impl<'tcx> CoroutineArgs<'tcx> { - /// Construct `CoroutineArgs` from `CoroutineArgsParts`, containing `Args` - /// for the coroutine parent, alongside additional coroutine-specific components. - pub fn new(tcx: TyCtxt<'tcx>, parts: CoroutineArgsParts<'tcx>) -> CoroutineArgs<'tcx> { - CoroutineArgs { - args: tcx.mk_args_from_iter(parts.parent_args.iter().copied().chain([ - parts.kind_ty.into(), - parts.resume_ty.into(), - parts.yield_ty.into(), - parts.return_ty.into(), - parts.witness.into(), - parts.tupled_upvars_ty.into(), - ])), - } - } - - /// Divides the coroutine args into their respective components. - /// The ordering assumed here must match that used by `CoroutineArgs::new` above. - fn split(self) -> CoroutineArgsParts<'tcx> { - match self.args[..] { - [ - ref parent_args @ .., - kind_ty, - resume_ty, - yield_ty, - return_ty, - witness, - tupled_upvars_ty, - ] => CoroutineArgsParts { - parent_args, - kind_ty: kind_ty.expect_ty(), - resume_ty: resume_ty.expect_ty(), - yield_ty: yield_ty.expect_ty(), - return_ty: return_ty.expect_ty(), - witness: witness.expect_ty(), - tupled_upvars_ty: tupled_upvars_ty.expect_ty(), - }, - _ => bug!("coroutine args missing synthetics"), - } - } - - /// Returns the generic parameters of the coroutine's parent. - pub fn parent_args(self) -> &'tcx [GenericArg<'tcx>] { - self.split().parent_args - } - - // Returns the kind of the coroutine. See docs on the `kind_ty` field. - pub fn kind_ty(self) -> Ty<'tcx> { - self.split().kind_ty - } - - /// This describes the types that can be contained in a coroutine. - /// It will be a type variable initially and unified in the last stages of typeck of a body. - /// It contains a tuple of all the types that could end up on a coroutine frame. - /// The state transformation MIR pass may only produce layouts which mention types - /// in this tuple. Upvars are not counted here. - pub fn witness(self) -> Ty<'tcx> { - self.split().witness - } - - /// Returns an iterator over the list of types of captured paths by the coroutine. - /// In case there was a type error in figuring out the types of the captured path, an - /// empty iterator is returned. - #[inline] - pub fn upvar_tys(self) -> &'tcx List> { - match *self.tupled_upvars_ty().kind() { - TyKind::Error(_) => ty::List::empty(), - TyKind::Tuple(tys) => tys, - TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"), - ty => bug!("Unexpected representation of upvar types tuple {:?}", ty), - } - } - - /// Returns the tuple type representing the upvars for this coroutine. - #[inline] - pub fn tupled_upvars_ty(self) -> Ty<'tcx> { - self.split().tupled_upvars_ty - } - - /// Returns the type representing the resume type of the coroutine. - pub fn resume_ty(self) -> Ty<'tcx> { - self.split().resume_ty - } - - /// Returns the type representing the yield type of the coroutine. - pub fn yield_ty(self) -> Ty<'tcx> { - self.split().yield_ty - } - - /// Returns the type representing the return type of the coroutine. - pub fn return_ty(self) -> Ty<'tcx> { - self.split().return_ty - } - - /// Returns the "coroutine signature", which consists of its resume, yield - /// and return types. - pub fn sig(self) -> GenSig<'tcx> { - let parts = self.split(); - ty::GenSig { - resume_ty: parts.resume_ty, - yield_ty: parts.yield_ty, - return_ty: parts.return_ty, - } - } -} - -impl<'tcx> CoroutineArgs<'tcx> { +#[extension(pub trait CoroutineArgsExt<'tcx>)] +impl<'tcx> ty::CoroutineArgs> { /// Coroutine has not been resumed yet. - pub const UNRESUMED: usize = 0; + const UNRESUMED: usize = 0; /// Coroutine has returned or is completed. - pub const RETURNED: usize = 1; + const RETURNED: usize = 1; /// Coroutine has been poisoned. - pub const POISONED: usize = 2; + const POISONED: usize = 2; const UNRESUMED_NAME: &'static str = "Unresumed"; const RETURNED_NAME: &'static str = "Returned"; @@ -691,7 +75,7 @@ impl<'tcx> CoroutineArgs<'tcx> { /// The valid variant indices of this coroutine. #[inline] - pub fn variant_range(&self, def_id: DefId, tcx: TyCtxt<'tcx>) -> Range { + fn variant_range(&self, def_id: DefId, tcx: TyCtxt<'tcx>) -> Range { // FIXME requires optimized MIR FIRST_VARIANT ..tcx.coroutine_layout(def_id, tcx.types.unit).unwrap().variant_fields.next_index() @@ -700,7 +84,7 @@ impl<'tcx> CoroutineArgs<'tcx> { /// The discriminant for the given variant. Panics if the `variant_index` is /// out of range. #[inline] - pub fn discriminant_for_variant( + fn discriminant_for_variant( &self, def_id: DefId, tcx: TyCtxt<'tcx>, @@ -715,7 +99,7 @@ impl<'tcx> CoroutineArgs<'tcx> { /// The set of all discriminants for the coroutine, enumerated with their /// variant indices. #[inline] - pub fn discriminants( + fn discriminants( self, def_id: DefId, tcx: TyCtxt<'tcx>, @@ -727,7 +111,7 @@ impl<'tcx> CoroutineArgs<'tcx> { /// Calls `f` with a reference to the name of the enumerator for the given /// variant `v`. - pub fn variant_name(v: VariantIdx) -> Cow<'static, str> { + fn variant_name(v: VariantIdx) -> Cow<'static, str> { match v.as_usize() { Self::UNRESUMED => Cow::from(Self::UNRESUMED_NAME), Self::RETURNED => Cow::from(Self::RETURNED_NAME), @@ -738,7 +122,7 @@ impl<'tcx> CoroutineArgs<'tcx> { /// The type of the state discriminant used in the coroutine type. #[inline] - pub fn discr_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + fn discr_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { tcx.types.u32 } @@ -749,7 +133,7 @@ impl<'tcx> CoroutineArgs<'tcx> { /// The locals are grouped by their variant number. Note that some locals may /// be repeated in multiple variants. #[inline] - pub fn state_tys( + fn state_tys( self, def_id: DefId, tcx: TyCtxt<'tcx>, @@ -765,7 +149,7 @@ impl<'tcx> CoroutineArgs<'tcx> { /// This is the types of the fields of a coroutine which are not stored in a /// variant. #[inline] - pub fn prefix_tys(self) -> &'tcx List> { + fn prefix_tys(self) -> &'tcx List> { self.upvar_tys() } } @@ -819,7 +203,7 @@ impl<'tcx> UpvarArgs<'tcx> { /// /// When the inline const is instantiated, `R` is instantiated as the actual inferred /// type of the constant. The reason that `R` is represented as an extra type parameter -/// is the same reason that [`ClosureArgs`] have `CS` and `U` as type parameters: +/// is the same reason that [`ty::ClosureArgs`] have `CS` and `U` as type parameters: /// inline const can reference lifetimes that are internal to the creating function. #[derive(Copy, Clone, Debug)] pub struct InlineConstArgs<'tcx> { @@ -898,253 +282,7 @@ impl BoundVariableKind { } } -/// Binder is a binder for higher-ranked lifetimes or types. It is part of the -/// compiler's representation for things like `for<'a> Fn(&'a isize)` -/// (which would be represented by the type `PolyTraitRef == -/// Binder<'tcx, TraitRef>`). Note that when we instantiate, -/// erase, or otherwise "discharge" these bound vars, we change the -/// type from `Binder<'tcx, T>` to just `T` (see -/// e.g., `liberate_late_bound_regions`). -/// -/// `Decodable` and `Encodable` are implemented for `Binder` using the `impl_binder_encode_decode!` macro. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[derive(HashStable, Lift)] -pub struct Binder<'tcx, T> { - value: T, - bound_vars: &'tcx List, -} - -impl<'tcx, T> Binder<'tcx, T> -where - T: TypeVisitable>, -{ - /// Wraps `value` in a binder, asserting that `value` does not - /// contain any bound vars that would be bound by the - /// binder. This is commonly used to 'inject' a value T into a - /// different binding level. - #[track_caller] - pub fn dummy(value: T) -> Binder<'tcx, T> { - assert!( - !value.has_escaping_bound_vars(), - "`{value:?}` has escaping bound vars, so it cannot be wrapped in a dummy binder." - ); - Binder { value, bound_vars: ty::List::empty() } - } - - pub fn bind_with_vars(value: T, bound_vars: &'tcx List) -> Binder<'tcx, T> { - if cfg!(debug_assertions) { - let mut validator = ValidateBoundVars::new(bound_vars); - value.visit_with(&mut validator); - } - Binder { value, bound_vars } - } -} - -impl<'tcx, T> rustc_type_ir::inherent::BoundVars> for ty::Binder<'tcx, T> { - fn bound_vars(&self) -> &'tcx List { - self.bound_vars - } - - fn has_no_bound_vars(&self) -> bool { - self.bound_vars.is_empty() - } -} - -impl<'tcx, T> Binder<'tcx, T> { - /// Skips the binder and returns the "bound" value. This is a - /// risky thing to do because it's easy to get confused about - /// De Bruijn indices and the like. It is usually better to - /// discharge the binder using `no_bound_vars` or - /// `instantiate_bound_regions` or something like - /// that. `skip_binder` is only valid when you are either - /// extracting data that has nothing to do with bound vars, you - /// are doing some sort of test that does not involve bound - /// regions, or you are being very careful about your depth - /// accounting. - /// - /// Some examples where `skip_binder` is reasonable: - /// - /// - extracting the `DefId` from a PolyTraitRef; - /// - comparing the self type of a PolyTraitRef to see if it is equal to - /// a type parameter `X`, since the type `X` does not reference any regions - pub fn skip_binder(self) -> T { - self.value - } - - pub fn bound_vars(&self) -> &'tcx List { - self.bound_vars - } - - pub fn as_ref(&self) -> Binder<'tcx, &T> { - Binder { value: &self.value, bound_vars: self.bound_vars } - } - - pub fn as_deref(&self) -> Binder<'tcx, &T::Target> - where - T: Deref, - { - Binder { value: &self.value, bound_vars: self.bound_vars } - } - - pub fn map_bound_ref>>(&self, f: F) -> Binder<'tcx, U> - where - F: FnOnce(&T) -> U, - { - self.as_ref().map_bound(f) - } - - pub fn map_bound>>(self, f: F) -> Binder<'tcx, U> - where - F: FnOnce(T) -> U, - { - let Binder { value, bound_vars } = self; - let value = f(value); - if cfg!(debug_assertions) { - let mut validator = ValidateBoundVars::new(bound_vars); - value.visit_with(&mut validator); - } - Binder { value, bound_vars } - } - - pub fn try_map_bound>, E>( - self, - f: F, - ) -> Result, E> - where - F: FnOnce(T) -> Result, - { - let Binder { value, bound_vars } = self; - let value = f(value)?; - if cfg!(debug_assertions) { - let mut validator = ValidateBoundVars::new(bound_vars); - value.visit_with(&mut validator); - } - Ok(Binder { value, bound_vars }) - } - - /// Wraps a `value` in a binder, using the same bound variables as the - /// current `Binder`. This should not be used if the new value *changes* - /// the bound variables. Note: the (old or new) value itself does not - /// necessarily need to *name* all the bound variables. - /// - /// This currently doesn't do anything different than `bind`, because we - /// don't actually track bound vars. However, semantically, it is different - /// because bound vars aren't allowed to change here, whereas they are - /// in `bind`. This may be (debug) asserted in the future. - pub fn rebind(&self, value: U) -> Binder<'tcx, U> - where - U: TypeVisitable>, - { - Binder::bind_with_vars(value, self.bound_vars) - } - - /// Unwraps and returns the value within, but only if it contains - /// no bound vars at all. (In other words, if this binder -- - /// and indeed any enclosing binder -- doesn't bind anything at - /// all.) Otherwise, returns `None`. - /// - /// (One could imagine having a method that just unwraps a single - /// binder, but permits late-bound vars bound by enclosing - /// binders, but that would require adjusting the debruijn - /// indices, and given the shallow binding structure we often use, - /// would not be that useful.) - pub fn no_bound_vars(self) -> Option - where - T: TypeVisitable>, - { - // `self.value` is equivalent to `self.skip_binder()` - if self.value.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) } - } - - /// Splits the contents into two things that share the same binder - /// level as the original, returning two distinct binders. - /// - /// `f` should consider bound regions at depth 1 to be free, and - /// anything it produces with bound regions at depth 1 will be - /// bound in the resulting return values. - pub fn split(self, f: F) -> (Binder<'tcx, U>, Binder<'tcx, V>) - where - F: FnOnce(T) -> (U, V), - { - let Binder { value, bound_vars } = self; - let (u, v) = f(value); - (Binder { value: u, bound_vars }, Binder { value: v, bound_vars }) - } -} - -impl<'tcx, T> Binder<'tcx, Option> { - pub fn transpose(self) -> Option> { - let Binder { value, bound_vars } = self; - value.map(|value| Binder { value, bound_vars }) - } -} - -impl<'tcx, T: IntoIterator> Binder<'tcx, T> { - pub fn iter(self) -> impl Iterator> { - let Binder { value, bound_vars } = self; - value.into_iter().map(|value| Binder { value, bound_vars }) - } -} - -impl<'tcx, T> IntoDiagArg for Binder<'tcx, T> -where - T: IntoDiagArg, -{ - fn into_diag_arg(self) -> DiagArgValue { - self.value.into_diag_arg() - } -} - -#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)] -pub struct GenSig<'tcx> { - pub resume_ty: Ty<'tcx>, - pub yield_ty: Ty<'tcx>, - pub return_ty: Ty<'tcx>, -} - pub type PolyFnSig<'tcx> = Binder<'tcx, FnSig<'tcx>>; - -impl<'tcx> PolyFnSig<'tcx> { - #[inline] - pub fn inputs(&self) -> Binder<'tcx, &'tcx [Ty<'tcx>]> { - self.map_bound_ref(|fn_sig| fn_sig.inputs()) - } - - #[inline] - #[track_caller] - pub fn input(&self, index: usize) -> ty::Binder<'tcx, Ty<'tcx>> { - self.map_bound_ref(|fn_sig| fn_sig.inputs()[index]) - } - - pub fn inputs_and_output(&self) -> ty::Binder<'tcx, &'tcx List>> { - self.map_bound_ref(|fn_sig| fn_sig.inputs_and_output) - } - - #[inline] - pub fn output(&self) -> ty::Binder<'tcx, Ty<'tcx>> { - self.map_bound_ref(|fn_sig| fn_sig.output()) - } - - pub fn c_variadic(&self) -> bool { - self.skip_binder().c_variadic - } - - pub fn safety(&self) -> hir::Safety { - self.skip_binder().safety - } - - pub fn abi(&self) -> abi::Abi { - self.skip_binder().abi - } - - pub fn is_fn_trait_compatible(&self) -> bool { - matches!( - self.skip_binder(), - ty::FnSig { safety: rustc_hir::Safety::Safe, abi: Abi::Rust, c_variadic: false, .. } - ) - } -} - pub type CanonicalPolyFnSig<'tcx> = Canonical<'tcx, Binder<'tcx, FnSig<'tcx>>>; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] @@ -1154,6 +292,12 @@ pub struct ParamTy { pub name: Symbol, } +impl rustc_type_ir::inherent::ParamLike for ParamTy { + fn index(self) -> u32 { + self.index + } +} + impl<'tcx> ParamTy { pub fn new(index: u32, name: Symbol) -> ParamTy { ParamTy { index, name } @@ -1182,6 +326,12 @@ pub struct ParamConst { pub name: Symbol, } +impl rustc_type_ir::inherent::ParamLike for ParamConst { + fn index(self) -> u32 { + self.index + } +} + impl ParamConst { pub fn new(index: u32, name: Symbol) -> ParamConst { ParamConst { index, name } @@ -1190,6 +340,27 @@ impl ParamConst { pub fn for_def(def: &ty::GenericParamDef) -> ParamConst { ParamConst::new(def.index, def.name) } + + pub fn find_ty_from_env<'tcx>(self, env: ParamEnv<'tcx>) -> Ty<'tcx> { + let mut candidates = env.caller_bounds().iter().filter_map(|clause| { + // `ConstArgHasType` are never desugared to be higher ranked. + match clause.kind().skip_binder() { + ty::ClauseKind::ConstArgHasType(param_ct, ty) => { + assert!(!(param_ct, ty).has_escaping_bound_vars()); + + match param_ct.kind() { + ty::ConstKind::Param(param_ct) if param_ct.index == self.index => Some(ty), + _ => None, + } + } + _ => None, + } + }); + + let ty = candidates.next().unwrap(); + assert!(candidates.next().is_none()); + ty + } } #[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] @@ -1203,6 +374,10 @@ impl<'tcx> rustc_type_ir::inherent::BoundVarLike> for BoundTy { fn var(self) -> BoundVar { self.var } + + fn assert_eq(self, var: ty::BoundVariableKind) { + assert_eq!(self.kind, var.expect_ty()) + } } #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] @@ -1611,6 +786,22 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { tcx.types.bool } + fn new_u8(tcx: TyCtxt<'tcx>) -> Self { + tcx.types.u8 + } + + fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferTy) -> Self { + Ty::new_infer(tcx, infer) + } + + fn new_var(tcx: TyCtxt<'tcx>, vid: ty::TyVid) -> Self { + Ty::new_var(tcx, vid) + } + + fn new_bound(interner: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundTy) -> Self { + Ty::new_bound(interner, debruijn, var) + } + fn new_anon_bound(tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self { Ty::new_bound(tcx, debruijn, ty::BoundTy { var, kind: ty::BoundTyKind::Anon }) } @@ -1622,6 +813,139 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { ) -> Self { Ty::new_alias(interner, kind, alias_ty) } + + fn new_error(interner: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self { + Ty::new_error(interner, guar) + } + + fn new_adt( + interner: TyCtxt<'tcx>, + adt_def: ty::AdtDef<'tcx>, + args: ty::GenericArgsRef<'tcx>, + ) -> Self { + Ty::new_adt(interner, adt_def, args) + } + + fn new_foreign(interner: TyCtxt<'tcx>, def_id: DefId) -> Self { + Ty::new_foreign(interner, def_id) + } + + fn new_dynamic( + interner: TyCtxt<'tcx>, + preds: &'tcx List>, + region: ty::Region<'tcx>, + kind: ty::DynKind, + ) -> Self { + Ty::new_dynamic(interner, preds, region, kind) + } + + fn new_coroutine( + interner: TyCtxt<'tcx>, + def_id: DefId, + args: ty::GenericArgsRef<'tcx>, + ) -> Self { + Ty::new_coroutine(interner, def_id, args) + } + + fn new_coroutine_closure( + interner: TyCtxt<'tcx>, + def_id: DefId, + args: ty::GenericArgsRef<'tcx>, + ) -> Self { + Ty::new_coroutine_closure(interner, def_id, args) + } + + fn new_closure(interner: TyCtxt<'tcx>, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> Self { + Ty::new_closure(interner, def_id, args) + } + + fn new_coroutine_witness( + interner: TyCtxt<'tcx>, + def_id: DefId, + args: ty::GenericArgsRef<'tcx>, + ) -> Self { + Ty::new_coroutine_witness(interner, def_id, args) + } + + fn new_ptr(interner: TyCtxt<'tcx>, ty: Self, mutbl: hir::Mutability) -> Self { + Ty::new_ptr(interner, ty, mutbl) + } + + fn new_ref( + interner: TyCtxt<'tcx>, + region: ty::Region<'tcx>, + ty: Self, + mutbl: hir::Mutability, + ) -> Self { + Ty::new_ref(interner, region, ty, mutbl) + } + + fn new_array_with_const_len(interner: TyCtxt<'tcx>, ty: Self, len: ty::Const<'tcx>) -> Self { + Ty::new_array_with_const_len(interner, ty, len) + } + + fn new_slice(interner: TyCtxt<'tcx>, ty: Self) -> Self { + Ty::new_slice(interner, ty) + } + + fn new_tup(interner: TyCtxt<'tcx>, tys: &[Ty<'tcx>]) -> Self { + Ty::new_tup(interner, tys) + } + + fn new_tup_from_iter(interner: TyCtxt<'tcx>, iter: It) -> T::Output + where + It: Iterator, + T: CollectAndApply, + { + Ty::new_tup_from_iter(interner, iter) + } + + fn tuple_fields(self) -> &'tcx ty::List> { + self.tuple_fields() + } + + fn to_opt_closure_kind(self) -> Option { + self.to_opt_closure_kind() + } + + fn from_closure_kind(interner: TyCtxt<'tcx>, kind: ty::ClosureKind) -> Self { + Ty::from_closure_kind(interner, kind) + } + + fn from_coroutine_closure_kind( + interner: TyCtxt<'tcx>, + kind: rustc_type_ir::ClosureKind, + ) -> Self { + Ty::from_coroutine_closure_kind(interner, kind) + } + + fn new_fn_def(interner: TyCtxt<'tcx>, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> Self { + Ty::new_fn_def(interner, def_id, args) + } + + fn new_fn_ptr(interner: TyCtxt<'tcx>, sig: ty::Binder<'tcx, ty::FnSig<'tcx>>) -> Self { + Ty::new_fn_ptr(interner, sig) + } + + fn new_pat(interner: TyCtxt<'tcx>, ty: Self, pat: ty::Pattern<'tcx>) -> Self { + Ty::new_pat(interner, ty, pat) + } + + fn new_unit(interner: TyCtxt<'tcx>) -> Self { + interner.types.unit + } + + fn new_usize(interner: TyCtxt<'tcx>) -> Self { + interner.types.usize + } + + fn discriminant_ty(self, interner: TyCtxt<'tcx>) -> Ty<'tcx> { + self.discriminant_ty(interner) + } + + fn async_destructor_ty(self, interner: TyCtxt<'tcx>) -> Ty<'tcx> { + self.async_destructor_ty(interner) + } } /// Type utilities @@ -1993,7 +1317,7 @@ impl<'tcx> Ty<'tcx> { FnPtr(f) => *f, Error(_) => { // ignore errors (#54954) - ty::Binder::dummy(ty::FnSig { + Binder::dummy(ty::FnSig { inputs_and_output: ty::List::empty(), c_variadic: false, safety: hir::Safety::Safe, @@ -2122,11 +1446,22 @@ impl<'tcx> Ty<'tcx> { } /// Returns the type of the async destructor of this type. - pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Ty<'tcx> { - if self.is_async_destructor_noop(tcx, param_env) || matches!(self.kind(), ty::Error(_)) { - return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop) - .instantiate_identity(); + pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + match self.async_drop_glue_morphology(tcx) { + AsyncDropGlueMorphology::Noop => { + return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop) + .instantiate_identity(); + } + AsyncDropGlueMorphology::DeferredDropInPlace => { + let drop_in_place = + Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDeferredDropInPlace) + .instantiate(tcx, &[self.into()]); + return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) + .instantiate(tcx, &[drop_in_place.into()]); + } + AsyncDropGlueMorphology::Custom => (), } + match *self.kind() { ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => { let assoc_items = tcx @@ -2145,24 +1480,18 @@ impl<'tcx> Ty<'tcx> { .adt_async_destructor_ty( tcx, adt_def.variants().iter().map(|v| v.fields.iter().map(|f| f.ty(tcx, args))), - param_env, ), - ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys), param_env), - ty::Closure(_, args) => self.adt_async_destructor_ty( - tcx, - iter::once(args.as_closure().upvar_tys()), - param_env, - ), - ty::CoroutineClosure(_, args) => self.adt_async_destructor_ty( - tcx, - iter::once(args.as_coroutine_closure().upvar_tys()), - param_env, - ), + ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys)), + ty::Closure(_, args) => { + self.adt_async_destructor_ty(tcx, iter::once(args.as_closure().upvar_tys())) + } + ty::CoroutineClosure(_, args) => self + .adt_async_destructor_ty(tcx, iter::once(args.as_coroutine_closure().upvar_tys())), ty::Adt(adt_def, _) => { assert!(adt_def.is_union()); - let surface_drop = self.surface_async_dropper_ty(tcx, param_env).unwrap(); + let surface_drop = self.surface_async_dropper_ty(tcx).unwrap(); Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) .instantiate(tcx, &[surface_drop.into()]) @@ -2179,17 +1508,12 @@ impl<'tcx> Ty<'tcx> { } } - fn adt_async_destructor_ty( - self, - tcx: TyCtxt<'tcx>, - variants: I, - param_env: ParamEnv<'tcx>, - ) -> Ty<'tcx> + fn adt_async_destructor_ty(self, tcx: TyCtxt<'tcx>, variants: I) -> Ty<'tcx> where I: Iterator + ExactSizeIterator, I::Item: IntoIterator>, { - debug_assert!(!self.is_async_destructor_noop(tcx, param_env)); + debug_assert_eq!(self.async_drop_glue_morphology(tcx), AsyncDropGlueMorphology::Custom); let defer = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDefer); let chain = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain); @@ -2212,7 +1536,7 @@ impl<'tcx> Ty<'tcx> { }) .unwrap(); - let dtor = if let Some(dropper_ty) = self.surface_async_dropper_ty(tcx, param_env) { + let dtor = if let Some(dropper_ty) = self.surface_async_dropper_ty(tcx) { Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain) .instantiate(tcx, &[dropper_ty.into(), variants_dtor.into()]) } else { @@ -2223,27 +1547,19 @@ impl<'tcx> Ty<'tcx> { .instantiate(tcx, &[dtor.into()]) } - fn surface_async_dropper_ty( - self, - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - ) -> Option> { - if self.has_surface_async_drop(tcx, param_env) { - Some(LangItem::SurfaceAsyncDropInPlace) - } else if self.has_surface_drop(tcx, param_env) { - Some(LangItem::AsyncDropSurfaceDropInPlace) - } else { - None - } - .map(|dropper| { - Ty::async_destructor_combinator(tcx, dropper).instantiate(tcx, &[self.into()]) - }) + fn surface_async_dropper_ty(self, tcx: TyCtxt<'tcx>) -> Option> { + let adt_def = self.ty_adt_def()?; + let dropper = adt_def + .async_destructor(tcx) + .map(|_| LangItem::SurfaceAsyncDropInPlace) + .or_else(|| adt_def.destructor(tcx).map(|_| LangItem::AsyncDropSurfaceDropInPlace))?; + Some(Ty::async_destructor_combinator(tcx, dropper).instantiate(tcx, &[self.into()])) } fn async_destructor_combinator( tcx: TyCtxt<'tcx>, lang_item: LangItem, - ) -> ty::EarlyBinder> { + ) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { tcx.fn_sig(tcx.require_lang_item(lang_item, None)) .map_bound(|fn_sig| fn_sig.output().no_bound_vars().unwrap()) } @@ -2348,8 +1664,8 @@ impl<'tcx> Ty<'tcx> { /// } /// ``` /// - /// After upvar analysis, you should instead use [`ClosureArgs::kind()`] - /// or [`CoroutineClosureArgs::kind()`] to assert that the `ClosureKind` + /// After upvar analysis, you should instead use [`ty::ClosureArgs::kind()`] + /// or [`ty::CoroutineClosureArgs::kind()`] to assert that the `ClosureKind` /// has been constrained instead of manually calling this method. /// /// ```rust,ignore (snippet of compiler code) @@ -2598,43 +1914,6 @@ impl<'tcx> rustc_type_ir::inherent::Tys> for &'tcx ty::List { - /// No additional information - this is the default. - /// We will not add any additional information to error messages. - #[default] - None, - /// We switched our variance because a generic argument occurs inside - /// the invariant generic argument of another type. - Invariant { - /// The generic type containing the generic parameter - /// that changes the variance (e.g. `*mut T`, `MyStruct`) - ty: Ty<'tcx>, - /// The index of the generic parameter being used - /// (e.g. `0` for `*mut T`, `1` for `MyStruct<'CovariantParam, 'InvariantParam>`) - param_index: u32, - }, -} - -impl<'tcx> VarianceDiagInfo<'tcx> { - /// Mirrors `Variance::xform` - used to 'combine' the existing - /// and new `VarianceDiagInfo`s when our variance changes. - pub fn xform(self, other: VarianceDiagInfo<'tcx>) -> VarianceDiagInfo<'tcx> { - // For now, just use the first `VarianceDiagInfo::Invariant` that we see - match self { - VarianceDiagInfo::None => other, - VarianceDiagInfo::Invariant { .. } => self, - } - } -} - // Some types are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index c5b3de17bcb3..4dba97c3b5b8 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -5,6 +5,7 @@ use hir::def_id::LOCAL_CRATE; use rustc_hir as hir; use rustc_hir::def_id::DefId; use std::iter; +use tracing::debug; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; @@ -39,11 +40,16 @@ pub struct TraitDef { /// also have already switched to the new trait solver. pub is_coinductive: bool, - /// If `true`, then this trait has the `#[rustc_skip_array_during_method_dispatch]` + /// If `true`, then this trait has the `#[rustc_skip_during_method_dispatch(array)]` /// attribute, indicating that editions before 2021 should not consider this trait /// during method dispatch if the receiver is an array. pub skip_array_during_method_dispatch: bool, + /// If `true`, then this trait has the `#[rustc_skip_during_method_dispatch(boxed_slice)]` + /// attribute, indicating that editions before 2024 should not consider this trait + /// during method dispatch if the receiver is a boxed slice. + pub skip_boxed_slice_during_method_dispatch: bool, + /// Used to determine whether the standard library is allowed to specialize /// on this trait. pub specialization_kind: TraitSpecializationKind, diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 8c14f1e080a1..b079ed521d34 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -4,7 +4,7 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::query::{IntoQueryParam, Providers}; use crate::ty::layout::{FloatExt, IntegerExt}; use crate::ty::{ - self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + self, Asyncness, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, Upcast, }; use crate::ty::{GenericArgKind, GenericArgsRef}; @@ -24,6 +24,7 @@ use rustc_target::abi::{Float, Integer, IntegerType, Size}; use rustc_target::spec::abi::Abi; use smallvec::{smallvec, SmallVec}; use std::{fmt, iter}; +use tracing::{debug, instrument, trace}; #[derive(Copy, Clone, Debug)] pub struct Discr<'tcx> { @@ -381,6 +382,64 @@ impl<'tcx> TyCtxt<'tcx> { Some(ty::Destructor { did, constness }) } + /// Calculate the async destructor of a given type. + pub fn calculate_async_dtor( + self, + adt_did: DefId, + validate: impl Fn(Self, DefId) -> Result<(), ErrorGuaranteed>, + ) -> Option { + let async_drop_trait = self.lang_items().async_drop_trait()?; + self.ensure().coherent_trait(async_drop_trait).ok()?; + + let ty = self.type_of(adt_did).instantiate_identity(); + let mut dtor_candidate = None; + self.for_each_relevant_impl(async_drop_trait, ty, |impl_did| { + if validate(self, impl_did).is_err() { + // Already `ErrorGuaranteed`, no need to delay a span bug here. + return; + } + + let [future, ctor] = self.associated_item_def_ids(impl_did) else { + self.dcx().span_delayed_bug( + self.def_span(impl_did), + "AsyncDrop impl without async_drop function or Dropper type", + ); + return; + }; + + if let Some((_, _, old_impl_did)) = dtor_candidate { + self.dcx() + .struct_span_err(self.def_span(impl_did), "multiple async drop impls found") + .with_span_note(self.def_span(old_impl_did), "other impl here") + .delay_as_bug(); + } + + dtor_candidate = Some((*future, *ctor, impl_did)); + }); + + let (future, ctor, _) = dtor_candidate?; + Some(ty::AsyncDestructor { future, ctor }) + } + + /// Returns async drop glue morphology for a definition. To get async drop + /// glue morphology for a type see [`Ty::async_drop_glue_morphology`]. + // + // FIXME: consider making this a query + pub fn async_drop_glue_morphology(self, did: DefId) -> AsyncDropGlueMorphology { + let ty: Ty<'tcx> = self.type_of(did).instantiate_identity(); + + // Async drop glue morphology is an internal detail, so reveal_all probably + // should be fine + let param_env = ty::ParamEnv::reveal_all(); + if ty.needs_async_drop(self, param_env) { + AsyncDropGlueMorphology::Custom + } else if ty.needs_drop(self, param_env) { + AsyncDropGlueMorphology::DeferredDropInPlace + } else { + AsyncDropGlueMorphology::Noop + } + } + /// Returns the set of types that are required to be alive in /// order to run the destructor of `def` (see RFCs 769 and /// 1238). @@ -506,42 +565,6 @@ impl<'tcx> TyCtxt<'tcx> { Ok(()) } - /// Checks whether each generic argument is simply a unique generic placeholder. - /// - /// This is used in the new solver, which canonicalizes params to placeholders - /// for better caching. - pub fn uses_unique_placeholders_ignoring_regions( - self, - args: GenericArgsRef<'tcx>, - ) -> Result<(), NotUniqueParam<'tcx>> { - let mut seen = GrowableBitSet::default(); - for arg in args { - match arg.unpack() { - // Ignore regions, since we can't resolve those in a canonicalized - // query in the trait solver. - GenericArgKind::Lifetime(_) => {} - GenericArgKind::Type(t) => match t.kind() { - ty::Placeholder(p) => { - if !seen.insert(p.bound.var) { - return Err(NotUniqueParam::DuplicateParam(t.into())); - } - } - _ => return Err(NotUniqueParam::NotParam(t.into())), - }, - GenericArgKind::Const(c) => match c.kind() { - ty::ConstKind::Placeholder(p) => { - if !seen.insert(p.bound) { - return Err(NotUniqueParam::DuplicateParam(c.into())); - } - } - _ => return Err(NotUniqueParam::NotParam(c.into())), - }, - } - } - - Ok(()) - } - /// Returns `true` if `def_id` refers to a closure, coroutine, or coroutine-closure /// (i.e. an async closure). These are all represented by `hir::Closure`, and all /// have the same `DefKind`. @@ -693,7 +716,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn coroutine_hidden_types( self, def_id: DefId, - ) -> impl Iterator>> { + ) -> impl Iterator>> { let coroutine_layout = self.mir_coroutine_witnesses(def_id); coroutine_layout .as_ref() @@ -708,7 +731,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn bound_coroutine_hidden_types( self, def_id: DefId, - ) -> impl Iterator>>> { + ) -> impl Iterator>>> { let coroutine_layout = self.mir_coroutine_witnesses(def_id); coroutine_layout .as_ref() @@ -1137,6 +1160,18 @@ impl<'tcx> TypeFolder> for WeakAliasTypeExpander<'tcx> { } } +/// Indicates the form of `AsyncDestruct::Destructor`. Used to simplify async +/// drop glue for types not using async drop. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum AsyncDropGlueMorphology { + /// Async destructor simply does nothing + Noop, + /// Async destructor simply runs `drop_in_place` + DeferredDropInPlace, + /// Async destructor has custom logic + Custom, +} + impl<'tcx> Ty<'tcx> { /// Returns the `Size` for primitive types (bool, uint, int, char, float). pub fn primitive_size(self, tcx: TyCtxt<'tcx>) -> Size { @@ -1302,80 +1337,16 @@ impl<'tcx> Ty<'tcx> { } } - /// Checks whether values of this type `T` implements the `AsyncDrop` - /// trait. - pub fn has_surface_async_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { - self.could_have_surface_async_drop() && tcx.has_surface_async_drop_raw(param_env.and(self)) - } - - /// Fast path helper for testing if a type has `AsyncDrop` - /// implementation. - /// - /// Returning `false` means the type is known to not have `AsyncDrop` - /// implementation. Returning `true` means nothing -- could be - /// `AsyncDrop`, might not be. - fn could_have_surface_async_drop(self) -> bool { - !self.is_async_destructor_trivially_noop() - && !matches!( - self.kind(), - ty::Tuple(_) - | ty::Slice(_) - | ty::Array(_, _) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - ) - } - - /// Checks whether values of this type `T` implements the `Drop` - /// trait. - pub fn has_surface_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { - self.could_have_surface_drop() && tcx.has_surface_drop_raw(param_env.and(self)) - } - - /// Fast path helper for testing if a type has `Drop` implementation. - /// - /// Returning `false` means the type is known to not have `Drop` - /// implementation. Returning `true` means nothing -- could be - /// `Drop`, might not be. - fn could_have_surface_drop(self) -> bool { - !self.is_async_destructor_trivially_noop() - && !matches!( - self.kind(), - ty::Tuple(_) - | ty::Slice(_) - | ty::Array(_, _) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - ) - } - - /// Checks whether values of this type `T` implement has noop async destructor. + /// Get morphology of the async drop glue, needed for types which do not + /// use async drop. To get async drop glue morphology for a definition see + /// [`TyCtxt::async_drop_glue_morphology`]. Used for `AsyncDestruct::Destructor` + /// type construction. // - // FIXME: implement optimization to make ADTs, which do not need drop, - // to skip fields or to have noop async destructor. - pub fn is_async_destructor_noop( - self, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { - self.is_async_destructor_trivially_noop() - || if let ty::Adt(adt_def, _) = self.kind() { - (adt_def.is_union() || adt_def.is_payloadfree()) - && !self.has_surface_async_drop(tcx, param_env) - && !self.has_surface_drop(tcx, param_env) - } else { - false - } - } - - /// Fast path helper for testing if a type has noop async destructor. - /// - /// Returning `true` means the type is known to have noop async destructor - /// implementation. Returning `true` means nothing -- could be - /// `Drop`, might not be. - fn is_async_destructor_trivially_noop(self) -> bool { + // FIXME: implement optimization to not instantiate a certain morphology of + // async drop glue too soon to allow per type optimizations, see array case + // for more info. Perhaps then remove this method and use `needs_(async_)drop` + // instead. + pub fn async_drop_glue_morphology(self, tcx: TyCtxt<'tcx>) -> AsyncDropGlueMorphology { match self.kind() { ty::Int(_) | ty::Uint(_) @@ -1387,10 +1358,43 @@ impl<'tcx> Ty<'tcx> { | ty::Ref(..) | ty::RawPtr(..) | ty::FnDef(..) - | ty::FnPtr(_) => true, - ty::Tuple(tys) => tys.is_empty(), - ty::Adt(adt_def, _) => adt_def.is_manually_drop(), - _ => false, + | ty::FnPtr(_) + | ty::Infer(ty::FreshIntTy(_)) + | ty::Infer(ty::FreshFloatTy(_)) => AsyncDropGlueMorphology::Noop, + + ty::Tuple(tys) if tys.is_empty() => AsyncDropGlueMorphology::Noop, + ty::Adt(adt_def, _) if adt_def.is_manually_drop() => AsyncDropGlueMorphology::Noop, + + // Foreign types can never have destructors. + ty::Foreign(_) => AsyncDropGlueMorphology::Noop, + + // FIXME: implement dynamic types async drops + ty::Error(_) | ty::Dynamic(..) => AsyncDropGlueMorphology::DeferredDropInPlace, + + ty::Tuple(_) | ty::Array(_, _) | ty::Slice(_) => { + // Assume worst-case scenario, because we can instantiate async + // destructors in different orders: + // + // 1. Instantiate [T; N] with T = String and N = 0 + // 2. Instantiate <[String; 0] as AsyncDestruct>::Destructor + // + // And viceversa, thus we cannot rely on String not using async + // drop or array having zero (0) elements + AsyncDropGlueMorphology::Custom + } + ty::Pat(ty, _) => ty.async_drop_glue_morphology(tcx), + + ty::Adt(adt_def, _) => tcx.async_drop_glue_morphology(adt_def.did()), + + ty::Closure(did, _) + | ty::CoroutineClosure(did, _) + | ty::Coroutine(did, _) + | ty::CoroutineWitness(did, _) => tcx.async_drop_glue_morphology(*did), + + ty::Alias(..) | ty::Param(_) | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(_) => { + // No specifics, but would usually mean forwarding async drop glue + AsyncDropGlueMorphology::Custom + } } } @@ -1429,6 +1433,46 @@ impl<'tcx> Ty<'tcx> { } } + /// If `ty.needs_async_drop(...)` returns `true`, then `ty` is definitely + /// non-copy and *might* have a async destructor attached; if it returns + /// `false`, then `ty` definitely has no async destructor (i.e., no async + /// drop glue). + /// + /// (Note that this implies that if `ty` has an async destructor attached, + /// then `needs_async_drop` will definitely return `true` for `ty`.) + /// + /// When constructing `AsyncDestruct::Destructor` type, use + /// [`Ty::async_drop_glue_morphology`] instead. + // + // FIXME(zetanumbers): Note that this method is used to check eligible types + // in unions. + #[inline] + pub fn needs_async_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { + // Avoid querying in simple cases. + match needs_drop_components(tcx, self) { + Err(AlwaysRequiresDrop) => true, + Ok(components) => { + let query_ty = match *components { + [] => return false, + // If we've got a single component, call the query with that + // to increase the chance that we hit the query cache. + [component_ty] => component_ty, + _ => self, + }; + + // This doesn't depend on regions, so try to minimize distinct + // query keys used. + // If normalization fails, we just use `query_ty`. + debug_assert!(!param_env.has_infer()); + let query_ty = tcx + .try_normalize_erasing_regions(param_env, query_ty) + .unwrap_or_else(|_| tcx.erase_regions(query_ty)); + + tcx.needs_async_drop_raw(param_env.and(query_ty)) + } + } + } + /// Checks if `ty` has a significant drop. /// /// Note that this method can return false even if `ty` has a destructor @@ -1600,9 +1644,24 @@ impl<'tcx> ExplicitSelf<'tcx> { /// Returns a list of types such that the given type needs drop if and only if /// *any* of the returned types need drop. Returns `Err(AlwaysRequiresDrop)` if /// this type always needs drop. +// +// FIXME(zetanumbers): consider replacing this with only +// `needs_drop_components_with_async` +#[inline] pub fn needs_drop_components<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, +) -> Result; 2]>, AlwaysRequiresDrop> { + needs_drop_components_with_async(tcx, ty, Asyncness::No) +} + +/// Returns a list of types such that the given type needs drop if and only if +/// *any* of the returned types need drop. Returns `Err(AlwaysRequiresDrop)` if +/// this type always needs drop. +pub fn needs_drop_components_with_async<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + asyncness: Asyncness, ) -> Result; 2]>, AlwaysRequiresDrop> { match *ty.kind() { ty::Infer(ty::FreshIntTy(_)) @@ -1622,11 +1681,18 @@ pub fn needs_drop_components<'tcx>( // Foreign types can never have destructors. ty::Foreign(..) => Ok(SmallVec::new()), - ty::Dynamic(..) | ty::Error(_) => Err(AlwaysRequiresDrop), + // FIXME(zetanumbers): Temporary workaround for async drop of dynamic types + ty::Dynamic(..) | ty::Error(_) => { + if asyncness.is_async() { + Ok(SmallVec::new()) + } else { + Err(AlwaysRequiresDrop) + } + } - ty::Pat(ty, _) | ty::Slice(ty) => needs_drop_components(tcx, ty), + ty::Pat(ty, _) | ty::Slice(ty) => needs_drop_components_with_async(tcx, ty, asyncness), ty::Array(elem_ty, size) => { - match needs_drop_components(tcx, elem_ty) { + match needs_drop_components_with_async(tcx, elem_ty, asyncness) { Ok(v) if v.is_empty() => Ok(v), res => match size.try_to_target_usize(tcx) { // Arrays of size zero don't need drop, even if their element @@ -1642,7 +1708,7 @@ pub fn needs_drop_components<'tcx>( } // If any field needs drop, then the whole tuple does. ty::Tuple(fields) => fields.iter().try_fold(SmallVec::new(), move |mut acc, elem| { - acc.extend(needs_drop_components(tcx, elem)?); + acc.extend(needs_drop_components_with_async(tcx, elem, asyncness)?); Ok(acc) }), diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 218567bbd59e..b1bbfd420e1b 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -1,7 +1,6 @@ use crate::ty::{self, Binder, Ty, TyCtxt, TypeFlags}; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sso::SsoHashSet; use rustc_type_ir::fold::TypeFoldable; use std::ops::ControlFlow; @@ -145,103 +144,6 @@ impl<'tcx> TyCtxt<'tcx> { } } -pub struct ValidateBoundVars<'tcx> { - bound_vars: &'tcx ty::List, - binder_index: ty::DebruijnIndex, - // We may encounter the same variable at different levels of binding, so - // this can't just be `Ty` - visited: SsoHashSet<(ty::DebruijnIndex, Ty<'tcx>)>, -} - -impl<'tcx> ValidateBoundVars<'tcx> { - pub fn new(bound_vars: &'tcx ty::List) -> Self { - ValidateBoundVars { - bound_vars, - binder_index: ty::INNERMOST, - visited: SsoHashSet::default(), - } - } -} - -impl<'tcx> TypeVisitor> for ValidateBoundVars<'tcx> { - type Result = ControlFlow<()>; - - fn visit_binder>>( - &mut self, - t: &Binder<'tcx, T>, - ) -> Self::Result { - self.binder_index.shift_in(1); - let result = t.super_visit_with(self); - self.binder_index.shift_out(1); - result - } - - fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { - if t.outer_exclusive_binder() < self.binder_index - || !self.visited.insert((self.binder_index, t)) - { - return ControlFlow::Break(()); - } - match *t.kind() { - ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { - if self.bound_vars.len() <= bound_ty.var.as_usize() { - bug!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars); - } - let list_var = self.bound_vars[bound_ty.var.as_usize()]; - match list_var { - ty::BoundVariableKind::Ty(kind) => { - if kind != bound_ty.kind { - bug!( - "Mismatched type kinds: {:?} doesn't var in list {:?}", - bound_ty.kind, - list_var - ); - } - } - _ => { - bug!("Mismatched bound variable kinds! Expected type, found {:?}", list_var) - } - } - } - - _ => (), - }; - - t.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result { - match *r { - ty::ReBound(index, br) if index == self.binder_index => { - if self.bound_vars.len() <= br.var.as_usize() { - bug!("Not enough bound vars: {:?} not found in {:?}", br, self.bound_vars); - } - let list_var = self.bound_vars[br.var.as_usize()]; - match list_var { - ty::BoundVariableKind::Region(kind) => { - if kind != br.kind { - bug!( - "Mismatched region kinds: {:?} doesn't match var ({:?}) in list ({:?})", - br.kind, - list_var, - self.bound_vars - ); - } - } - _ => bug!( - "Mismatched bound variable kinds! Expected region, found {:?}", - list_var - ), - } - } - - _ => (), - }; - - ControlFlow::Continue(()) - } -} - /// Collects all the late-bound regions at the innermost binding level /// into a hash set. struct LateBoundRegionsCollector { diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index 2ad194313100..dc3c84f9e439 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -3,6 +3,8 @@ use std::fmt; use crate::mir::interpret::{alloc_range, AllocId, Allocation, Pointer, Scalar}; use crate::ty::{self, Instance, PolyTraitRef, Ty, TyCtxt}; use rustc_ast::Mutability; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def_id::DefId; use rustc_macros::HashStable; #[derive(Clone, Copy, PartialEq, HashStable)] @@ -40,12 +42,69 @@ impl<'tcx> fmt::Debug for VtblEntry<'tcx> { impl<'tcx> TyCtxt<'tcx> { pub const COMMON_VTABLE_ENTRIES: &'tcx [VtblEntry<'tcx>] = &[VtblEntry::MetadataDropInPlace, VtblEntry::MetadataSize, VtblEntry::MetadataAlign]; + + pub fn supertrait_def_ids(self, trait_def_id: DefId) -> SupertraitDefIds<'tcx> { + SupertraitDefIds { + tcx: self, + stack: vec![trait_def_id], + visited: Some(trait_def_id).into_iter().collect(), + } + } } pub const COMMON_VTABLE_ENTRIES_DROPINPLACE: usize = 0; pub const COMMON_VTABLE_ENTRIES_SIZE: usize = 1; pub const COMMON_VTABLE_ENTRIES_ALIGN: usize = 2; +pub struct SupertraitDefIds<'tcx> { + tcx: TyCtxt<'tcx>, + stack: Vec, + visited: FxHashSet, +} + +impl Iterator for SupertraitDefIds<'_> { + type Item = DefId; + + fn next(&mut self) -> Option { + let def_id = self.stack.pop()?; + let predicates = self.tcx.super_predicates_of(def_id); + let visited = &mut self.visited; + self.stack.extend( + predicates + .predicates + .iter() + .filter_map(|(pred, _)| pred.as_trait_clause()) + .map(|trait_ref| trait_ref.def_id()) + .filter(|&super_def_id| visited.insert(super_def_id)), + ); + Some(def_id) + } +} + +// Note that we don't have access to a self type here, this has to be purely based on the trait (and +// supertrait) definitions. That means we can't call into the same vtable_entries code since that +// returns a specific instantiation (e.g., with Vacant slots when bounds aren't satisfied). The goal +// here is to do a best-effort approximation without duplicating a lot of code. +// +// This function is used in layout computation for e.g. &dyn Trait, so it's critical that this +// function is an accurate approximation. We verify this when actually computing the vtable below. +pub(crate) fn vtable_min_entries<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: Option>, +) -> usize { + let mut count = TyCtxt::COMMON_VTABLE_ENTRIES.len(); + let Some(trait_ref) = trait_ref else { + return count; + }; + + // This includes self in supertraits. + for def_id in tcx.supertrait_def_ids(trait_ref.def_id()) { + count += tcx.own_existential_vtable_entries(def_id).len(); + } + + count +} + /// Retrieves an allocation that represents the contents of a vtable. /// Since this is a query, allocations are cached and not duplicated. pub(super) fn vtable_allocation_provider<'tcx>( @@ -63,6 +122,9 @@ pub(super) fn vtable_allocation_provider<'tcx>( TyCtxt::COMMON_VTABLE_ENTRIES }; + // This confirms that the layout computation for &dyn Trait has an accurate sizing. + assert!(vtable_entries.len() >= vtable_min_entries(tcx, poly_trait_ref)); + let layout = tcx .layout_of(ty::ParamEnv::reveal_all().and(ty)) .expect("failed to build vtable representation"); @@ -84,10 +146,14 @@ pub(super) fn vtable_allocation_provider<'tcx>( let idx: u64 = u64::try_from(idx).unwrap(); let scalar = match entry { VtblEntry::MetadataDropInPlace => { - let instance = ty::Instance::resolve_drop_in_place(tcx, ty); - let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance); - let fn_ptr = Pointer::from(fn_alloc_id); - Scalar::from_pointer(fn_ptr, &tcx) + if ty.needs_drop(tcx, ty::ParamEnv::reveal_all()) { + let instance = ty::Instance::resolve_drop_in_place(tcx, ty); + let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance); + let fn_ptr = Pointer::from(fn_alloc_id); + Scalar::from_pointer(fn_ptr, &tcx) + } else { + Scalar::from_maybe_pointer(Pointer::null(), &tcx) + } } VtblEntry::MetadataSize => Scalar::from_uint(size, ptr_size), VtblEntry::MetadataAlign => Scalar::from_uint(align, ptr_size), diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index 089e61749c3e..e0f204a687f6 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -5,6 +5,7 @@ use crate::ty::{self, Ty}; use crate::ty::{GenericArg, GenericArgKind}; use rustc_data_structures::sso::SsoHashSet; use smallvec::{smallvec, SmallVec}; +use tracing::debug; // The TypeWalker's stack is hot enough that it's worth going to some effort to // avoid heap allocations. @@ -211,38 +212,19 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) } }, GenericArgKind::Lifetime(_) => {} - GenericArgKind::Const(parent_ct) => { - stack.push(parent_ct.ty().into()); - match parent_ct.kind() { - ty::ConstKind::Infer(_) - | ty::ConstKind::Param(_) - | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Bound(..) - | ty::ConstKind::Value(_) - | ty::ConstKind::Error(_) => {} + GenericArgKind::Const(parent_ct) => match parent_ct.kind() { + ty::ConstKind::Infer(_) + | ty::ConstKind::Param(_) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Error(_) => {} - ty::ConstKind::Expr(expr) => match expr { - ty::Expr::UnOp(_, v) => push_inner(stack, v.into()), - ty::Expr::Binop(_, l, r) => { - push_inner(stack, r.into()); - push_inner(stack, l.into()) - } - ty::Expr::FunctionCall(func, args) => { - for a in args.iter().rev() { - push_inner(stack, a.into()); - } - push_inner(stack, func.into()); - } - ty::Expr::Cast(_, c, t) => { - push_inner(stack, t.into()); - push_inner(stack, c.into()); - } - }, + ty::ConstKind::Value(ty, _) => stack.push(ty.into()), - ty::ConstKind::Unevaluated(ct) => { - stack.extend(ct.args.iter().rev()); - } + ty::ConstKind::Expr(expr) => stack.extend(expr.args().iter().rev()), + ty::ConstKind::Unevaluated(ct) => { + stack.extend(ct.args.iter().rev()); } - } + }, } } diff --git a/compiler/rustc_middle/src/util/call_kind.rs b/compiler/rustc_middle/src/util/call_kind.rs index 4e2a2c6ae0ac..dc1d73684f4f 100644 --- a/compiler/rustc_middle/src/util/call_kind.rs +++ b/compiler/rustc_middle/src/util/call_kind.rs @@ -8,6 +8,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::{lang_items, LangItem}; use rustc_span::symbol::Ident; use rustc_span::{sym, DesugaringKind, Span}; +use tracing::debug; #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum CallDesugaringKind { @@ -116,19 +117,19 @@ pub fn call_kind<'tcx>( kind.unwrap_or_else(|| { // This isn't a 'special' use of `self` debug!(?method_did, ?fn_call_span); - let desugaring = if Some(method_did) == tcx.lang_items().into_iter_fn() + let desugaring = if tcx.is_lang_item(method_did, LangItem::IntoIterIntoIter) && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop) { Some((CallDesugaringKind::ForLoopIntoIter, method_args.type_at(0))) } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) { - if Some(method_did) == tcx.lang_items().branch_fn() { + if tcx.is_lang_item(method_did, LangItem::TryTraitBranch) { Some((CallDesugaringKind::QuestionBranch, method_args.type_at(0))) - } else if Some(method_did) == tcx.lang_items().from_residual_fn() { + } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromResidual) { Some((CallDesugaringKind::QuestionFromResidual, method_args.type_at(0))) } else { None } - } else if Some(method_did) == tcx.lang_items().from_output_fn() + } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromOutput) && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock) { Some((CallDesugaringKind::TryBlockFromOutput, method_args.type_at(0))) diff --git a/compiler/rustc_middle/src/util/find_self_call.rs b/compiler/rustc_middle/src/util/find_self_call.rs index 0ca4fce5da9b..027e2703e98d 100644 --- a/compiler/rustc_middle/src/util/find_self_call.rs +++ b/compiler/rustc_middle/src/util/find_self_call.rs @@ -3,6 +3,7 @@ use crate::ty::GenericArgsRef; use crate::ty::{self, TyCtxt}; use rustc_span::def_id::DefId; use rustc_span::source_map::Spanned; +use tracing::debug; /// Checks if the specified `local` is used as the `self` parameter of a method call /// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 79f36cfe5695..8c323188826b 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -23,7 +23,7 @@ impl<'tcx> Value> for Ty<'_> { } } -impl<'tcx> Value> for Result>, CyclePlaceholder> { +impl<'tcx> Value> for Result>, CyclePlaceholder> { fn from_cycle_error(_tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self { Err(CyclePlaceholder(guar)) } @@ -111,7 +111,7 @@ impl<'tcx> Value> for Representability { } } -impl<'tcx> Value> for ty::EarlyBinder> { +impl<'tcx> Value> for ty::EarlyBinder<'_, Ty<'_>> { fn from_cycle_error( tcx: TyCtxt<'tcx>, cycle_error: &CycleError, @@ -121,7 +121,7 @@ impl<'tcx> Value> for ty::EarlyBinder> { } } -impl<'tcx> Value> for ty::EarlyBinder>> { +impl<'tcx> Value> for ty::EarlyBinder<'_, ty::Binder<'_, ty::FnSig<'_>>> { fn from_cycle_error( tcx: TyCtxt<'tcx>, cycle_error: &CycleError, @@ -142,7 +142,7 @@ impl<'tcx> Value> for &[ty::Variance] { && let Some(def_id) = frame.query.def_id { let n = tcx.generics_of(def_id).own_params.len(); - vec![ty::Variance::Bivariant; n].leak() + vec![ty::Bivariant; n].leak() } else { span_bug!( cycle_error.usage.as_ref().unwrap().0, diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index 77f27236437d..5d828d0093f3 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -either = "1" itertools = "0.12" rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } @@ -24,6 +23,5 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 0bb44dbb8706..1ee8777c2741 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -28,6 +28,12 @@ mir_build_borrow_of_moved_value = borrow of moved value .value_borrowed_label = value borrowed here after move .suggestion = borrow this binding in the pattern to avoid moving the value +mir_build_call_to_deprecated_safe_fn_requires_unsafe = + call to deprecated safe function `{$function}` is unsafe and requires unsafe block + .note = consult the function's documentation for information on how to avoid undefined behavior + .label = call to unsafe function + .suggestion = you can wrap the call in an `unsafe` block if you can guarantee the code is only ever called from single-threaded code + mir_build_call_to_fn_with_requires_unsafe = call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block .help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count -> @@ -97,7 +103,7 @@ mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior .label = dereference of raw pointer -mir_build_exceeds_mcdc_condition_num_limit = Conditions number of the decision ({$conditions_num}) exceeds limit ({$max_conditions_num}). MCDC analysis will not count this expression. +mir_build_exceeds_mcdc_condition_limit = Number of conditions in decision ({$num_conditions}) exceeds limit ({$max_conditions}). MC/DC analysis will not count this expression. mir_build_extern_static_requires_unsafe = use of extern static is unsafe and requires unsafe block @@ -109,9 +115,6 @@ mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior .label = use of extern static -mir_build_indirect_structural_match = - to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq)]` - mir_build_inform_irrefutable = `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant mir_build_initializing_type_with_requires_unsafe = @@ -257,9 +260,6 @@ mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type mir_build_non_partial_eq_match = to use a constant of type `{$non_peq_ty}` in a pattern, the type must implement `PartialEq` -mir_build_nontrivial_structural_match = - to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq)]` - mir_build_pattern_not_covered = refutable pattern in {$origin} .pattern_ty = the matched value is of type `{$pattern_ty}` @@ -335,12 +335,12 @@ mir_build_unsafe_fn_safe_body = an unsafe function restricts its caller, but its mir_build_unsafe_not_inherited = items do not inherit unsafety from separate enclosing items mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe = - borrow of layout constrained field with interior mutability is unsafe and requires unsafe block (error E0133) + borrow of layout constrained field with interior mutability is unsafe and requires unsafe block .note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values .label = borrow of layout constrained field with interior mutability mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe = - call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block (error E0133) + call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block .help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count -> [1] feature *[count] features @@ -355,48 +355,47 @@ mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe = .label = call to function with `#[target_feature]` mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe = - call to unsafe function `{$function}` is unsafe and requires unsafe block (error E0133) + call to unsafe function `{$function}` is unsafe and requires unsafe block .note = consult the function's documentation for information on how to avoid undefined behavior .label = call to unsafe function mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe_nameless = - call to unsafe function is unsafe and requires unsafe block (error E0133) + call to unsafe function is unsafe and requires unsafe block .note = consult the function's documentation for information on how to avoid undefined behavior .label = call to unsafe function mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe = - dereference of raw pointer is unsafe and requires unsafe block (error E0133) + dereference of raw pointer is unsafe and requires unsafe block .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior .label = dereference of raw pointer mir_build_unsafe_op_in_unsafe_fn_extern_static_requires_unsafe = - use of extern static is unsafe and requires unsafe block (error E0133) + use of extern static is unsafe and requires unsafe block .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior .label = use of extern static mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe = - initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe - block (error E0133) + initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe block .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior .label = initializing type with `rustc_layout_scalar_valid_range` attr mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe = - use of inline assembly is unsafe and requires unsafe block (error E0133) + use of inline assembly is unsafe and requires unsafe block .note = inline assembly is entirely unchecked and can cause undefined behavior .label = use of inline assembly mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe = - use of mutable static is unsafe and requires unsafe block (error E0133) + use of mutable static is unsafe and requires unsafe block .note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior .label = use of mutable static mir_build_unsafe_op_in_unsafe_fn_mutation_of_layout_constrained_field_requires_unsafe = - mutation of layout constrained field is unsafe and requires unsafe block (error E0133) + mutation of layout constrained field is unsafe and requires unsafe block .note = mutating layout constrained fields cannot statically be checked for valid values .label = mutation of layout constrained field mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe = - access to union field is unsafe and requires unsafe block (error E0133) + access to union field is unsafe and requires unsafe block .note = the field may not be properly initialized: using uninitialized data will cause undefined behavior .label = access to union field diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index 6ae98e15946d..c1d645aa42cb 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -5,6 +5,7 @@ use rustc_middle::span_bug; use rustc_middle::thir::*; use rustc_middle::{mir::*, ty}; use rustc_span::Span; +use tracing::debug; impl<'a, 'tcx> Builder<'a, 'tcx> { pub(crate) fn ast_block( diff --git a/compiler/rustc_mir_build/src/build/cfg.rs b/compiler/rustc_mir_build/src/build/cfg.rs index 18e45291e9a8..6034c8fadc57 100644 --- a/compiler/rustc_mir_build/src/build/cfg.rs +++ b/compiler/rustc_mir_build/src/build/cfg.rs @@ -3,6 +3,7 @@ use crate::build::CFG; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use tracing::debug; impl<'tcx> CFG<'tcx> { pub(crate) fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<'tcx> { diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs index ee9eeb62990d..876faca5172a 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs @@ -118,17 +118,35 @@ impl BranchInfoBuilder { } } - fn add_two_way_branch<'tcx>( + fn register_two_way_branch<'tcx>( &mut self, + tcx: TyCtxt<'tcx>, cfg: &mut CFG<'tcx>, source_info: SourceInfo, true_block: BasicBlock, false_block: BasicBlock, ) { - let true_marker = self.markers.inject_block_marker(cfg, source_info, true_block); - let false_marker = self.markers.inject_block_marker(cfg, source_info, false_block); + // Separate path for handling branches when MC/DC is enabled. + if let Some(mcdc_info) = self.mcdc_info.as_mut() { + let inject_block_marker = + |source_info, block| self.markers.inject_block_marker(cfg, source_info, block); + mcdc_info.visit_evaluated_condition( + tcx, + source_info, + true_block, + false_block, + inject_block_marker, + ); + } else { + let true_marker = self.markers.inject_block_marker(cfg, source_info, true_block); + let false_marker = self.markers.inject_block_marker(cfg, source_info, false_block); - self.branch_spans.push(BranchSpan { span: source_info.span, true_marker, false_marker }); + self.branch_spans.push(BranchSpan { + span: source_info.span, + true_marker, + false_marker, + }); + } } pub(crate) fn into_done(self) -> Option> { @@ -157,6 +175,70 @@ impl BranchInfoBuilder { } impl<'tcx> Builder<'_, 'tcx> { + /// If condition coverage is enabled, inject extra blocks and marker statements + /// that will let us track the value of the condition in `place`. + pub(crate) fn visit_coverage_standalone_condition( + &mut self, + mut expr_id: ExprId, // Expression giving the span of the condition + place: mir::Place<'tcx>, // Already holds the boolean condition value + block: &mut BasicBlock, + ) { + // Bail out if condition coverage is not enabled for this function. + let Some(branch_info) = self.coverage_branch_info.as_mut() else { return }; + if !self.tcx.sess.instrument_coverage_condition() { + return; + }; + + // Remove any wrappers, so that we can inspect the real underlying expression. + while let ExprKind::Use { source: inner } | ExprKind::Scope { value: inner, .. } = + self.thir[expr_id].kind + { + expr_id = inner; + } + // If the expression is a lazy logical op, it will naturally get branch + // coverage as part of its normal lowering, so we can disregard it here. + if let ExprKind::LogicalOp { .. } = self.thir[expr_id].kind { + return; + } + + let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope }; + + // Using the boolean value that has already been stored in `place`, set up + // control flow in the shape of a diamond, so that we can place separate + // marker statements in the true and false blocks. The coverage MIR pass + // will use those markers to inject coverage counters as appropriate. + // + // block + // / \ + // true_block false_block + // (marker) (marker) + // \ / + // join_block + + let true_block = self.cfg.start_new_block(); + let false_block = self.cfg.start_new_block(); + self.cfg.terminate( + *block, + source_info, + mir::TerminatorKind::if_(mir::Operand::Copy(place), true_block, false_block), + ); + + // Separate path for handling branches when MC/DC is enabled. + branch_info.register_two_way_branch( + self.tcx, + &mut self.cfg, + source_info, + true_block, + false_block, + ); + + let join_block = self.cfg.start_new_block(); + self.cfg.goto(true_block, source_info, join_block); + self.cfg.goto(false_block, source_info, join_block); + // Any subsequent codegen in the caller should use the new join block. + *block = join_block; + } + /// If branch coverage is enabled, inject marker statements into `then_block` /// and `else_block`, and record their IDs in the table of branch spans. pub(crate) fn visit_coverage_branch_condition( @@ -179,22 +261,13 @@ impl<'tcx> Builder<'_, 'tcx> { let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope }; - // Separate path for handling branches when MC/DC is enabled. - if let Some(mcdc_info) = branch_info.mcdc_info.as_mut() { - let inject_block_marker = |source_info, block| { - branch_info.markers.inject_block_marker(&mut self.cfg, source_info, block) - }; - mcdc_info.visit_evaluated_condition( - self.tcx, - source_info, - then_block, - else_block, - inject_block_marker, - ); - return; - } - - branch_info.add_two_way_branch(&mut self.cfg, source_info, then_block, else_block); + branch_info.register_two_way_branch( + self.tcx, + &mut self.cfg, + source_info, + then_block, + else_block, + ); } /// If branch coverage is enabled, inject marker statements into `true_block` @@ -213,6 +286,12 @@ impl<'tcx> Builder<'_, 'tcx> { // FIXME(#124144) This may need special handling when MC/DC is enabled. let source_info = SourceInfo { span: pattern.span, scope: self.source_scope }; - branch_info.add_two_way_branch(&mut self.cfg, source_info, true_block, false_block); + branch_info.register_two_way_branch( + self.tcx, + &mut self.cfg, + source_info, + true_block, + false_block, + ); } } diff --git a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs index 9cfb25e663d1..f97e9ef60a29 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs +++ b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs @@ -10,12 +10,12 @@ use rustc_middle::ty::TyCtxt; use rustc_span::Span; use crate::build::Builder; -use crate::errors::MCDCExceedsConditionNumLimit; +use crate::errors::MCDCExceedsConditionLimit; /// The MCDC bitmap scales exponentially (2^n) based on the number of conditions seen, /// So llvm sets a maximum value prevents the bitmap footprint from growing too large without the user's knowledge. /// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged. -const MAX_CONDITIONS_NUM_IN_DECISION: usize = 6; +const MAX_CONDITIONS_IN_DECISION: usize = 6; #[derive(Default)] struct MCDCDecisionCtx { @@ -100,7 +100,7 @@ impl MCDCState { } None => decision_ctx.processing_decision.insert(MCDCDecisionSpan { span, - conditions_num: 0, + num_conditions: 0, end_markers: vec![], decision_depth, }), @@ -108,14 +108,14 @@ impl MCDCState { let parent_condition = decision_ctx.decision_stack.pop_back().unwrap_or_default(); let lhs_id = if parent_condition.condition_id == ConditionId::NONE { - decision.conditions_num += 1; - ConditionId::from(decision.conditions_num) + decision.num_conditions += 1; + ConditionId::from(decision.num_conditions) } else { parent_condition.condition_id }; - decision.conditions_num += 1; - let rhs_condition_id = ConditionId::from(decision.conditions_num); + decision.num_conditions += 1; + let rhs_condition_id = ConditionId::from(decision.num_conditions); let (lhs, rhs) = match op { LogicalOp::And => { @@ -179,18 +179,18 @@ impl MCDCState { } } -pub struct MCDCInfoBuilder { +pub(crate) struct MCDCInfoBuilder { branch_spans: Vec, decision_spans: Vec, state: MCDCState, } impl MCDCInfoBuilder { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self { branch_spans: vec![], decision_spans: vec![], state: MCDCState::new() } } - pub fn visit_evaluated_condition( + pub(crate) fn visit_evaluated_condition( &mut self, tcx: TyCtxt<'_>, source_info: SourceInfo, @@ -208,27 +208,28 @@ impl MCDCInfoBuilder { // is empty, i.e. when all the conditions of the decision were instrumented, // and the decision is "complete". if let Some(decision) = decision_result { - match decision.conditions_num { + match decision.num_conditions { 0 => { unreachable!("Decision with no condition is not expected"); } - 1..=MAX_CONDITIONS_NUM_IN_DECISION => { + 1..=MAX_CONDITIONS_IN_DECISION => { self.decision_spans.push(decision); } _ => { // Do not generate mcdc mappings and statements for decisions with too many conditions. - let rebase_idx = self.branch_spans.len() - decision.conditions_num + 1; + // Therefore, first erase the condition info of the (N-1) previous branch spans. + let rebase_idx = self.branch_spans.len() - (decision.num_conditions - 1); for branch in &mut self.branch_spans[rebase_idx..] { branch.condition_info = None; } - // ConditionInfo of this branch shall also be reset. + // Then, erase this last branch span's info too, for a total of N. condition_info = None; - tcx.dcx().emit_warn(MCDCExceedsConditionNumLimit { + tcx.dcx().emit_warn(MCDCExceedsConditionLimit { span: decision.span, - conditions_num: decision.conditions_num, - max_conditions_num: MAX_CONDITIONS_NUM_IN_DECISION, + num_conditions: decision.num_conditions, + max_conditions: MAX_CONDITIONS_IN_DECISION, }); } } @@ -242,7 +243,7 @@ impl MCDCInfoBuilder { }); } - pub fn into_done(self) -> (Vec, Vec) { + pub(crate) fn into_done(self) -> (Vec, Vec) { (self.decision_spans, self.branch_spans) } } diff --git a/compiler/rustc_mir_build/src/build/custom/parse.rs b/compiler/rustc_mir_build/src/build/custom/parse.rs index 0384b9bc154e..9607022c6df0 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse.rs @@ -38,7 +38,7 @@ macro_rules! parse_by_kind { ) => {{ let expr_id = $self.preparse($expr_id); let expr = &$self.thir[expr_id]; - debug!("Trying to parse {:?} as {}", expr.kind, $expected); + tracing::debug!("Trying to parse {:?} as {}", expr.kind, $expected); let $expr_name = expr; match &expr.kind { $( @@ -91,7 +91,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { } } - pub fn parse_args(&mut self, params: &IndexSlice>) -> PResult<()> { + pub(crate) fn parse_args(&mut self, params: &IndexSlice>) -> PResult<()> { for param in params.iter() { let (var, span) = { let pat = param.pat.as_ref().unwrap(); @@ -149,7 +149,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { /// /// This allows us to easily parse the basic blocks declarations, local declarations, and /// basic block definitions in order. - pub fn parse_body(&mut self, expr_id: ExprId) -> PResult<()> { + pub(crate) fn parse_body(&mut self, expr_id: ExprId) -> PResult<()> { let body = parse_by_kind!(self, expr_id, _, "whole body", ExprKind::Block { block } => self.thir[*block].expr.unwrap(), ); diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index c669d3fd6230..b1a305efa4c6 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -12,7 +12,7 @@ use crate::build::expr::as_constant::as_constant_inner; use super::{parse_by_kind, PResult, ParseCtxt}; impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { - pub fn parse_statement(&self, expr_id: ExprId) -> PResult> { + pub(crate) fn parse_statement(&self, expr_id: ExprId) -> PResult> { parse_by_kind!(self, expr_id, _, "statement", @call(mir_storage_live, args) => { Ok(StatementKind::StorageLive(self.parse_local(args[0])?)) @@ -46,7 +46,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { ) } - pub fn parse_terminator(&self, expr_id: ExprId) -> PResult> { + pub(crate) fn parse_terminator(&self, expr_id: ExprId) -> PResult> { parse_by_kind!(self, expr_id, expr, "terminator", @call(mir_return, _args) => { Ok(TerminatorKind::Return) @@ -195,9 +195,15 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { }, @call(mir_checked, args) => { parse_by_kind!(self, args[0], _, "binary op", - ExprKind::Binary { op, lhs, rhs } => Ok(Rvalue::CheckedBinaryOp( - *op, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?)) - )), + ExprKind::Binary { op, lhs, rhs } => { + if let Some(op_with_overflow) = op.wrapping_to_overflowing() { + Ok(Rvalue::BinaryOp( + op_with_overflow, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?)) + )) + } else { + Err(self.expr_error(expr_id, "No WithOverflow form of this operator")) + } + }, ) }, @call(mir_offset, args) => { @@ -206,6 +212,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { Ok(Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, offset)))) }, @call(mir_len, args) => Ok(Rvalue::Len(self.parse_place(args[0])?)), + @call(mir_ptr_metadata, args) => Ok(Rvalue::UnaryOp(UnOp::PtrMetadata, self.parse_operand(args[0])?)), @call(mir_copy_for_deref, args) => Ok(Rvalue::CopyForDeref(self.parse_place(args[0])?)), ExprKind::Borrow { borrow_kind, arg } => Ok( Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?) @@ -254,7 +261,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { ) } - pub fn parse_operand(&self, expr_id: ExprId) -> PResult> { + pub(crate) fn parse_operand(&self, expr_id: ExprId) -> PResult> { parse_by_kind!(self, expr_id, expr, "operand", @call(mir_move, args) => self.parse_place(args[0]).map(Operand::Move), @call(mir_static, args) => self.parse_static(args[0]), diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs index 817f5f787b1b..3b69058d3cb4 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -2,15 +2,17 @@ use crate::build::{parse_float_into_constval, Builder}; use rustc_ast as ast; +use rustc_hir::LangItem; use rustc_middle::mir; use rustc_middle::mir::interpret::{Allocation, LitToConstError, LitToConstInput, Scalar}; use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::{ - self, CanonicalUserType, CanonicalUserTypeAnnotation, TyCtxt, UserTypeAnnotationIndex, + self, CanonicalUserType, CanonicalUserTypeAnnotation, Ty, TyCtxt, UserTypeAnnotationIndex, }; use rustc_middle::{bug, span_bug}; use rustc_target::abi::Size; +use tracing::{instrument, trace}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr`, yielding a compile-time constant. Assumes that @@ -38,7 +40,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } -pub fn as_constant_inner<'tcx>( +pub(crate) fn as_constant_inner<'tcx>( expr: &Expr<'tcx>, push_cuta: impl FnMut(&Box>) -> Option, tcx: TyCtxt<'tcx>, @@ -50,7 +52,7 @@ pub fn as_constant_inner<'tcx>( { Ok(c) => c, Err(LitToConstError::Reported(guar)) => { - Const::Ty(ty::Const::new_error(tcx, guar, ty)) + Const::Ty(Ty::new_error(tcx, guar), ty::Const::new_error(tcx, guar)) } Err(LitToConstError::TypeError) => { bug!("encountered type error in `lit_to_mir_constant`") @@ -82,8 +84,8 @@ pub fn as_constant_inner<'tcx>( ConstOperand { user_ty, span, const_ } } ExprKind::ConstParam { param, def_id: _ } => { - let const_param = ty::Const::new_param(tcx, param, expr.ty); - let const_ = Const::Ty(const_param); + let const_param = ty::Const::new_param(tcx, param); + let const_ = Const::Ty(expr.ty, const_param); ConstOperand { user_ty: None, span, const_ } } @@ -141,7 +143,7 @@ fn lit_to_mir_constant<'tcx>( let id = tcx.allocate_bytes(data); ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx)) } - (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().c_str()) => + (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) => { let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]); let allocation = tcx.mk_const_alloc(allocation); diff --git a/compiler/rustc_mir_build/src/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/build/expr/as_operand.rs index 076ee7f85ff6..09ce134a2bf6 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_operand.rs @@ -5,6 +5,7 @@ use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary}; use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_middle::thir::*; +use tracing::{debug, instrument}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Returns an operand suitable for use until the end of the current diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 9963629fc52f..4b62afa61bbd 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -4,7 +4,6 @@ use crate::build::expr::category::Category; use crate::build::ForGuard::{OutsideGuard, RefWithinGuard}; use crate::build::{BlockAnd, BlockAndExtension, Builder, Capture, CaptureMap}; use rustc_hir::def_id::LocalDefId; -use rustc_middle::bug; use rustc_middle::hir::place::Projection as HirProjection; use rustc_middle::hir::place::ProjectionKind as HirProjectionKind; use rustc_middle::middle::region; @@ -13,8 +12,10 @@ use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::AdtDef; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, Variance}; +use rustc_middle::{bug, span_bug}; use rustc_span::Span; use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; +use tracing::{debug, instrument, trace}; use std::assert_matches::assert_matches; use std::iter; @@ -251,7 +252,18 @@ fn strip_prefix<'a, 'tcx>( impl<'tcx> PlaceBuilder<'tcx> { pub(in crate::build) fn to_place(&self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> { - self.try_to_place(cx).unwrap() + self.try_to_place(cx).unwrap_or_else(|| match self.base { + PlaceBase::Local(local) => span_bug!( + cx.local_decls[local].source_info.span, + "could not resolve local: {local:#?} + {:?}", + self.projection + ), + PlaceBase::Upvar { var_hir_id, closure_def_id: _ } => span_bug!( + cx.tcx.hir().span(var_hir_id.0), + "could not resolve upvar: {var_hir_id:?} + {:?}", + self.projection + ), + }) } /// Creates a `Place` or returns `None` if an upvar cannot be resolved diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index 0b2248d049af..9f9b566bb8fa 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -18,6 +18,7 @@ use rustc_middle::ty::cast::{mir_cast_kind, CastTy}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, Ty, UpvarArgs}; use rustc_span::{Span, DUMMY_SP}; +use tracing::debug; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Returns an rvalue suitable for use until the end of the current @@ -568,11 +569,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let result_tup = Ty::new_tup(self.tcx, &[ty, bool_ty]); let result_value = self.temp(result_tup, span); + let op_with_overflow = op.wrapping_to_overflowing().unwrap(); + self.cfg.push_assign( block, source_info, result_value, - Rvalue::CheckedBinaryOp(op, Box::new((lhs.to_copy(), rhs.to_copy()))), + Rvalue::BinaryOp(op_with_overflow, Box::new((lhs.to_copy(), rhs.to_copy()))), ); let val_fld = FieldIdx::ZERO; let of_fld = FieldIdx::new(1); diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/build/expr/as_temp.rs index 27e66e7f54d0..607c7c3259c1 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs @@ -6,6 +6,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_middle::thir::*; +use tracing::{debug, instrument}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr` into a fresh temporary. This is used when building diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index f8c910730456..6b4dd8b64a42 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -12,6 +12,7 @@ use rustc_middle::thir::*; use rustc_middle::ty::CanonicalUserTypeAnnotation; use rustc_span::source_map::Spanned; use std::iter; +use tracing::{debug, instrument}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr`, storing the result into `destination`, which @@ -149,6 +150,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::LogicalOp { op, lhs, rhs } => { let condition_scope = this.local_scope(); let source_info = this.source_info(expr.span); + + this.visit_coverage_branch_operation(op, expr.span); + // We first evaluate the left-hand side of the predicate ... let (then_block, else_block) = this.in_if_then_scope(condition_scope, expr.span, |this| { @@ -182,9 +186,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { const_: Const::from_bool(this.tcx, constant), }, ); - let rhs = unpack!(this.expr_into_dest(destination, continuation, rhs)); + let mut rhs_block = unpack!(this.expr_into_dest(destination, continuation, rhs)); + // Instrument the lowered RHS's value for condition coverage. + // (Does nothing if condition coverage is not enabled.) + this.visit_coverage_standalone_condition(rhs, destination, &mut rhs_block); + let target = this.cfg.start_new_block(); - this.cfg.goto(rhs, source_info, target); + this.cfg.goto(rhs_block, source_info, target); this.cfg.goto(short_circuit, source_info, target); target.unit() } diff --git a/compiler/rustc_mir_build/src/build/expr/mod.rs b/compiler/rustc_mir_build/src/build/expr/mod.rs index dfe85b858cd0..3de43a3370f3 100644 --- a/compiler/rustc_mir_build/src/build/expr/mod.rs +++ b/compiler/rustc_mir_build/src/build/expr/mod.rs @@ -62,9 +62,9 @@ pub(crate) mod as_constant; mod as_operand; -pub mod as_place; +pub(crate) mod as_place; mod as_rvalue; mod as_temp; -pub mod category; +pub(crate) mod category; mod into; mod stmt; diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs index 7f5e45e20cc1..2bdeb579a02d 100644 --- a/compiler/rustc_mir_build/src/build/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs @@ -3,6 +3,7 @@ use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_middle::thir::*; +use tracing::debug; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Builds a block of MIR statements to evaluate the THIR `expr`. diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 3fc719394bf9..68244136d1ad 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -20,6 +20,8 @@ use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty}; use rustc_span::symbol::Symbol; use rustc_span::{BytePos, Pos, Span}; use rustc_target::abi::VariantIdx; +use tracing::{debug, instrument}; + // helper functions, broken out by category: mod simplify; mod test; @@ -697,7 +699,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // exactly `T` (i.e., with invariance). The variance field, in // contrast, is intended to be used to relate `T` to the type of // ``. - ty::Variance::Invariant, + ty::Invariant, ), }, ); @@ -925,7 +927,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for subpattern in prefix.iter() { self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f); } - for subpattern in slice { + if let Some(subpattern) = slice { self.visit_primary_bindings( subpattern, pattern_user_ty.clone().subslice(from, to), @@ -1072,12 +1074,9 @@ struct Candidate<'pat, 'tcx> { // because that would break binding consistency. subcandidates: Vec>, - /// ...and the guard must be evaluated if there is one. + /// ...and if there is a guard it must be evaluated; if it's `false` then branch to `otherwise_block`. has_guard: bool, - /// If the guard is `false` then branch to `otherwise_block`. - otherwise_block: Option, - /// If the candidate matches, bindings and ascriptions must be established. extra_data: PatternExtraData<'tcx>, @@ -1088,6 +1087,9 @@ struct Candidate<'pat, 'tcx> { /// The block before the `bindings` have been established. pre_binding_block: Option, + /// The block to branch to if the guard or a nested candidate fails to match. + otherwise_block: Option, + /// The earliest block that has only candidates >= this one as descendents. Used for false /// edges, see the doc for [`Builder::match_expr`]. false_edge_start_block: Option, @@ -1362,56 +1364,105 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { otherwise_block: BasicBlock, candidates: &mut [&mut Candidate<'pat, 'tcx>], ) { - let mut split_or_candidate = false; - for candidate in &mut *candidates { - if let [MatchPair { test_case: TestCase::Or { .. }, .. }] = &*candidate.match_pairs { - // Split a candidate in which the only match-pair is an or-pattern into multiple - // candidates. This is so that - // - // match x { - // 0 | 1 => { ... }, - // 2 | 3 => { ... }, - // } - // - // only generates a single switch. - let match_pair = candidate.match_pairs.pop().unwrap(); - self.create_or_subcandidates(candidate, match_pair); - split_or_candidate = true; + // We process or-patterns here. If any candidate starts with an or-pattern, we have to + // expand the or-pattern before we can proceed further. + // + // We can't expand them freely however. The rule is: if the candidate has an or-pattern as + // its only remaining match pair, we can expand it freely. If it has other match pairs, we + // can expand it but we can't process more candidates after it. + // + // If we didn't stop, the `otherwise` cases could get mixed up. E.g. in the following, + // or-pattern simplification (in `merge_trivial_subcandidates`) makes it so the `1` and `2` + // cases branch to a same block (which then tests `false`). If we took `(2, _)` in the same + // set of candidates, when we reach the block that tests `false` we don't know whether we + // came from `1` or `2`, hence we can't know where to branch on failure. + // ```ignore(illustrative) + // match (1, true) { + // (1 | 2, false) => {}, + // (2, _) => {}, + // _ => {} + // } + // ``` + // + // We therefore split the `candidates` slice in two, expand or-patterns in the first half, + // and process both halves separately. + let mut expand_until = 0; + for (i, candidate) in candidates.iter().enumerate() { + if matches!( + &*candidate.match_pairs, + [MatchPair { test_case: TestCase::Or { .. }, .. }, ..] + ) { + expand_until = i + 1; + if candidate.match_pairs.len() > 1 { + break; + } } } + let (candidates_to_expand, remaining_candidates) = candidates.split_at_mut(expand_until); ensure_sufficient_stack(|| { - if split_or_candidate { - // At least one of the candidates has been split into subcandidates. - // We need to change the candidate list to include those. - let mut new_candidates = Vec::new(); - for candidate in candidates.iter_mut() { - candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate)); + if candidates_to_expand.is_empty() { + // No candidates start with an or-pattern, we can continue. + self.match_expanded_candidates( + span, + scrutinee_span, + start_block, + otherwise_block, + remaining_candidates, + ); + } else { + // Expand one level of or-patterns for each candidate in `candidates_to_expand`. + let mut expanded_candidates = Vec::new(); + for candidate in candidates_to_expand.iter_mut() { + if let [MatchPair { test_case: TestCase::Or { .. }, .. }, ..] = + &*candidate.match_pairs + { + let or_match_pair = candidate.match_pairs.remove(0); + // Expand the or-pattern into subcandidates. + self.create_or_subcandidates(candidate, or_match_pair); + // Collect the newly created subcandidates. + for subcandidate in candidate.subcandidates.iter_mut() { + expanded_candidates.push(subcandidate); + } + } else { + expanded_candidates.push(candidate); + } } + + // Process the expanded candidates. + let remainder_start = self.cfg.start_new_block(); + // There might be new or-patterns obtained from expanding the old ones, so we call + // `match_candidates` again. self.match_candidates( span, scrutinee_span, start_block, - otherwise_block, - &mut *new_candidates, + remainder_start, + expanded_candidates.as_mut_slice(), ); - for candidate in candidates { - self.merge_trivial_subcandidates(candidate); + // Simplify subcandidates and process any leftover match pairs. + for candidate in candidates_to_expand { + if !candidate.subcandidates.is_empty() { + self.finalize_or_candidate(span, scrutinee_span, candidate); + } } - } else { - self.match_simplified_candidates( + + // Process the remaining candidates. + self.match_candidates( span, scrutinee_span, - start_block, + remainder_start, otherwise_block, - candidates, + remaining_candidates, ); } }); } - fn match_simplified_candidates( + /// Construct the decision tree for `candidates`. Caller must ensure that no candidate in + /// `candidates` starts with an or-pattern. + fn match_expanded_candidates( &mut self, span: Span, scrutinee_span: Span, @@ -1436,7 +1487,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // The first candidate has satisfied all its match pairs; we link it up and continue // with the remaining candidates. start_block = self.select_matched_candidate(first, start_block); - self.match_simplified_candidates( + self.match_expanded_candidates( span, scrutinee_span, start_block, @@ -1446,7 +1497,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } candidates => { // The first candidate has some unsatisfied match pairs; we proceed to do more tests. - self.test_candidates_with_or( + self.test_candidates( span, scrutinee_span, candidates, @@ -1493,16 +1544,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate.pre_binding_block = Some(start_block); let otherwise_block = self.cfg.start_new_block(); - if candidate.has_guard { - // Create the otherwise block for this candidate, which is the - // pre-binding block for the next candidate. - candidate.otherwise_block = Some(otherwise_block); - } + // Create the otherwise block for this candidate, which is the + // pre-binding block for the next candidate. + candidate.otherwise_block = Some(otherwise_block); otherwise_block } - /// Tests a candidate where there are only or-patterns left to test, or - /// forwards to [Builder::test_candidates]. + /// Simplify subcandidates and process any leftover match pairs. The candidate should have been + /// expanded with `create_or_subcandidates`. /// /// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like /// so: @@ -1554,84 +1603,56 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// | /// ... /// ``` - fn test_candidates_with_or( + fn finalize_or_candidate( &mut self, span: Span, scrutinee_span: Span, - candidates: &mut [&mut Candidate<'_, 'tcx>], - start_block: BasicBlock, - otherwise_block: BasicBlock, + candidate: &mut Candidate<'_, 'tcx>, ) { - let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap(); - assert!(first_candidate.subcandidates.is_empty()); - if !matches!(first_candidate.match_pairs[0].test_case, TestCase::Or { .. }) { - self.test_candidates(span, scrutinee_span, candidates, start_block, otherwise_block); + if candidate.subcandidates.is_empty() { return; } - let first_match_pair = first_candidate.match_pairs.remove(0); - let remaining_match_pairs = mem::take(&mut first_candidate.match_pairs); - let remainder_start = self.cfg.start_new_block(); - // Test the alternatives of this or-pattern. - self.test_or_pattern(first_candidate, start_block, remainder_start, first_match_pair); + self.merge_trivial_subcandidates(candidate); - if !remaining_match_pairs.is_empty() { + if !candidate.match_pairs.is_empty() { // If more match pairs remain, test them after each subcandidate. // We could add them to the or-candidates before the call to `test_or_pattern` but this // would make it impossible to detect simplifiable or-patterns. That would guarantee // exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`. - first_candidate.visit_leaves(|leaf_candidate| { + let mut last_otherwise = None; + candidate.visit_leaves(|leaf_candidate| { + last_otherwise = leaf_candidate.otherwise_block; + }); + let remaining_match_pairs = mem::take(&mut candidate.match_pairs); + candidate.visit_leaves(|leaf_candidate| { assert!(leaf_candidate.match_pairs.is_empty()); leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned()); let or_start = leaf_candidate.pre_binding_block.unwrap(); - // In a case like `(a | b, c | d)`, if `a` succeeds and `c | d` fails, we know `(b, - // c | d)` will fail too. If there is no guard, we skip testing of `b` by branching - // directly to `remainder_start`. If there is a guard, we have to try `(b, c | d)`. - let or_otherwise = leaf_candidate.otherwise_block.unwrap_or(remainder_start); - self.test_candidates_with_or( + // In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q, + // R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching + // directly to `last_otherwise`. If there is a guard, + // `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we + // can't skip `Q`. + let or_otherwise = if leaf_candidate.has_guard { + leaf_candidate.otherwise_block.unwrap() + } else { + last_otherwise.unwrap() + }; + self.match_candidates( span, scrutinee_span, - &mut [leaf_candidate], or_start, or_otherwise, + &mut [leaf_candidate], ); }); } - - // Test the remaining candidates. - self.match_candidates( - span, - scrutinee_span, - remainder_start, - otherwise_block, - remaining_candidates, - ); - } - - #[instrument(skip(self, start_block, otherwise_block, candidate, match_pair), level = "debug")] - fn test_or_pattern<'pat>( - &mut self, - candidate: &mut Candidate<'pat, 'tcx>, - start_block: BasicBlock, - otherwise_block: BasicBlock, - match_pair: MatchPair<'pat, 'tcx>, - ) { - let or_span = match_pair.pattern.span; - self.create_or_subcandidates(candidate, match_pair); - let mut or_candidate_refs: Vec<_> = candidate.subcandidates.iter_mut().collect(); - self.match_candidates( - or_span, - or_span, - start_block, - otherwise_block, - &mut or_candidate_refs, - ); - self.merge_trivial_subcandidates(candidate); } /// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new /// subcandidate. Any candidate that has been expanded that way should be passed to - /// `merge_trivial_subcandidates` after its subcandidates have been processed. + /// `finalize_or_candidate` after its subcandidates have been processed. fn create_or_subcandidates<'pat>( &mut self, candidate: &mut Candidate<'pat, 'tcx>, @@ -1649,8 +1670,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// Try to merge all of the subcandidates of the given candidate into one. This avoids - /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The or-pattern should have - /// been expanded with `create_or_subcandidates`. + /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The candidate should have been + /// expanded with `create_or_subcandidates`. fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) { if candidate.subcandidates.is_empty() || candidate.has_guard { // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. @@ -1662,6 +1683,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty() }); if can_merge { + let mut last_otherwise = None; let any_matches = self.cfg.start_new_block(); let or_span = candidate.or_span.take().unwrap(); let source_info = self.source_info(or_span); @@ -1672,8 +1694,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for subcandidate in mem::take(&mut candidate.subcandidates) { let or_block = subcandidate.pre_binding_block.unwrap(); self.cfg.goto(or_block, source_info, any_matches); + last_otherwise = subcandidate.otherwise_block; } candidate.pre_binding_block = Some(any_matches); + assert!(last_otherwise.is_some()); + candidate.otherwise_block = last_otherwise; } else { // Never subcandidates may have a set of bindings inconsistent with their siblings, // which would break later code. So we filter them out. Note that we can't filter out diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index 90f12e55ff4c..543301c71a0e 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -14,6 +14,7 @@ use crate::build::matches::{MatchPair, PatternExtraData, TestCase}; use crate::build::Builder; +use tracing::{debug, instrument}; use std::mem; diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index f3faeb4158ee..11d3e2a8180f 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -18,6 +18,7 @@ use rustc_span::def_id::DefId; use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; +use tracing::{debug, instrument}; use std::cmp::Ordering; @@ -140,7 +141,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let success_block = target_block(TestBranch::Success); let fail_block = target_block(TestBranch::Failure); if let ty::Adt(def, _) = ty.kind() - && Some(def.did()) == tcx.lang_items().string() + && tcx.is_lang_item(def.did(), LangItem::String) { if !tcx.features().string_deref_patterns { bug!( diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index 14c74c761ebb..50f4ca2d819d 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -7,6 +7,7 @@ use rustc_middle::thir::{self, *}; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; +use tracing::debug; impl<'a, 'tcx> Builder<'a, 'tcx> { pub(crate) fn field_match_pairs<'pat>( @@ -455,7 +456,7 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } #[must_use] -pub fn ref_pat_borrow_kind(ref_mutability: Mutability) -> BorrowKind { +pub(crate) fn ref_pat_borrow_kind(ref_mutability: Mutability) -> BorrowKind { match ref_mutability { Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default }, Mutability::Not => BorrowKind::Shared, diff --git a/compiler/rustc_mir_build/src/build/misc.rs b/compiler/rustc_mir_build/src/build/misc.rs index c263de79c3b8..04e6d24e5a17 100644 --- a/compiler/rustc_mir_build/src/build/misc.rs +++ b/compiler/rustc_mir_build/src/build/misc.rs @@ -7,6 +7,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; +use tracing::debug; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Adds a new temporary value of type `ty` storing the result of diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 92cd7f75d628..601e5d4d3dc7 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -15,11 +15,10 @@ use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::middle::region; -use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::*; use rustc_middle::query::TyCtxtAt; use rustc_middle::thir::{self, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -452,7 +451,7 @@ fn construct_fn<'tcx>( assert_eq!(expr.as_usize(), thir.exprs.len() - 1); // Figure out what primary body this item has. - let body_id = tcx.hir().body_owned_by(fn_def); + let body = tcx.hir().body_owned_by(fn_def); let span_with_body = tcx.hir().span_with_body(fn_id); let return_ty_span = tcx .hir() @@ -512,9 +511,9 @@ fn construct_fn<'tcx>( ); let call_site_scope = - region::Scope { id: body_id.hir_id.local_id, data: region::ScopeData::CallSite }; + region::Scope { id: body.id().hir_id.local_id, data: region::ScopeData::CallSite }; let arg_scope = - region::Scope { id: body_id.hir_id.local_id, data: region::ScopeData::Arguments }; + region::Scope { id: body.id().hir_id.local_id, data: region::ScopeData::Arguments }; let source_info = builder.source_info(span); let call_site_s = (call_site_scope, source_info); unpack!(builder.in_scope(call_site_s, LintLevel::Inherited, |builder| { @@ -1014,14 +1013,14 @@ fn parse_float_into_constval<'tcx>( float_ty: ty::FloatTy, neg: bool, ) -> Option> { - parse_float_into_scalar(num, float_ty, neg).map(ConstValue::Scalar) + parse_float_into_scalar(num, float_ty, neg).map(|s| ConstValue::Scalar(s.into())) } pub(crate) fn parse_float_into_scalar( num: Symbol, float_ty: ty::FloatTy, neg: bool, -) -> Option { +) -> Option { let num = num.as_str(); match float_ty { // FIXME(f16_f128): When available, compare to the library parser as with `f32` and `f64` @@ -1030,7 +1029,7 @@ pub(crate) fn parse_float_into_scalar( if neg { f = -f; } - Some(Scalar::from_f16(f)) + Some(ScalarInt::from(f)) } ty::FloatTy::F32 => { let Ok(rust_f) = num.parse::() else { return None }; @@ -1053,7 +1052,7 @@ pub(crate) fn parse_float_into_scalar( f = -f; } - Some(Scalar::from_f32(f)) + Some(ScalarInt::from(f)) } ty::FloatTy::F64 => { let Ok(rust_f) = num.parse::() else { return None }; @@ -1076,7 +1075,7 @@ pub(crate) fn parse_float_into_scalar( f = -f; } - Some(Scalar::from_f64(f)) + Some(ScalarInt::from(f)) } // FIXME(f16_f128): When available, compare to the library parser as with `f32` and `f64` ty::FloatTy::F128 => { @@ -1084,7 +1083,7 @@ pub(crate) fn parse_float_into_scalar( if neg { f = -f; } - Some(Scalar::from_f128(f)) + Some(ScalarInt::from(f)) } } } diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index be32363fec52..5b6de39bb2e7 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -94,9 +94,10 @@ use rustc_middle::{bug, span_bug}; use rustc_session::lint::Level; use rustc_span::source_map::Spanned; use rustc_span::{Span, DUMMY_SP}; +use tracing::{debug, instrument}; #[derive(Debug)] -pub struct Scopes<'tcx> { +pub(crate) struct Scopes<'tcx> { scopes: Vec, /// The current set of breakable scopes. See module comment for more details. diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index b5f7ffbd2afb..a65586ccdb7e 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -2,6 +2,7 @@ use crate::build::ExprCategory; use crate::errors::*; use rustc_errors::DiagArgValue; +use rustc_hir::def::DefKind; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; use rustc_middle::mir::BorrowKind; use rustc_middle::span_bug; @@ -9,7 +10,7 @@ use rustc_middle::thir::visit::Visitor; use rustc_middle::thir::*; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; -use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; +use rustc_session::lint::builtin::{DEPRECATED_SAFE, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; use rustc_session::lint::Level; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::Symbol; @@ -88,6 +89,36 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { } } + fn emit_deprecated_safe_fn_call(&self, span: Span, kind: &UnsafeOpKind) -> bool { + match kind { + // Allow calls to deprecated-safe unsafe functions if the caller is + // from an edition before 2024. + &UnsafeOpKind::CallToUnsafeFunction(Some(id)) + if !span.at_least_rust_2024() + && self.tcx.has_attr(id, sym::rustc_deprecated_safe_2024) => + { + let sm = self.tcx.sess.source_map(); + self.tcx.emit_node_span_lint( + DEPRECATED_SAFE, + self.hir_context, + span, + CallToDeprecatedSafeFnRequiresUnsafe { + span, + function: with_no_trimmed_paths!(self.tcx.def_path_str(id)), + sub: CallToDeprecatedSafeFnRequiresUnsafeSub { + indent: sm.indentation_before(span).unwrap_or_default(), + start_of_line: sm.span_extend_to_line(span).shrink_to_lo(), + left: span.shrink_to_lo(), + right: span.shrink_to_hi(), + }, + }, + ); + true + } + _ => false, + } + } + fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) { let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed(); match self.safety_context { @@ -101,22 +132,28 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { } SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {} SafetyContext::UnsafeFn => { - // unsafe_op_in_unsafe_fn is disallowed - kind.emit_unsafe_op_in_unsafe_fn_lint( - self.tcx, - self.hir_context, - span, - self.suggest_unsafe_block, - ); - self.suggest_unsafe_block = false; + let deprecated_safe_fn = self.emit_deprecated_safe_fn_call(span, &kind); + if !deprecated_safe_fn { + // unsafe_op_in_unsafe_fn is disallowed + kind.emit_unsafe_op_in_unsafe_fn_lint( + self.tcx, + self.hir_context, + span, + self.suggest_unsafe_block, + ); + self.suggest_unsafe_block = false; + } } SafetyContext::Safe => { - kind.emit_requires_unsafe_err( - self.tcx, - span, - self.hir_context, - unsafe_op_in_unsafe_fn_allowed, - ); + let deprecated_safe_fn = self.emit_deprecated_safe_fn_call(span, &kind); + if !deprecated_safe_fn { + kind.emit_requires_unsafe_err( + self.tcx, + span, + self.hir_context, + unsafe_op_in_unsafe_fn_allowed, + ); + } } } } @@ -436,7 +473,10 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { if self.tcx.is_mutable_static(def_id) { self.requires_unsafe(expr.span, UseOfMutableStatic); } else if self.tcx.is_foreign_item(def_id) { - self.requires_unsafe(expr.span, UseOfExternStatic); + match self.tcx.def_kind(def_id) { + DefKind::Static { safety: hir::Safety::Safe, .. } => {} + _ => self.requires_unsafe(expr.span, UseOfExternStatic), + } } } else if self.thir[arg].ty.is_unsafe_ptr() { self.requires_unsafe(expr.span, DerefOfRawPointer); @@ -577,7 +617,7 @@ enum UnsafeOpKind { use UnsafeOpKind::*; impl UnsafeOpKind { - pub fn emit_unsafe_op_in_unsafe_fn_lint( + fn emit_unsafe_op_in_unsafe_fn_lint( &self, tcx: TyCtxt<'_>, hir_id: HirId, @@ -717,7 +757,7 @@ impl UnsafeOpKind { } } - pub fn emit_requires_unsafe_err( + fn emit_requires_unsafe_err( &self, tcx: TyCtxt<'_>, span: Span, diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index e2a28467b845..7c73d8a6d47d 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1,9 +1,9 @@ use crate::fluent_generated as fluent; -use rustc_errors::DiagArgValue; use rustc_errors::{ - codes::*, Applicability, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level, MultiSpan, + codes::*, Applicability, Diag, Diagnostic, EmissionGuarantee, Level, MultiSpan, SubdiagMessageOp, Subdiagnostic, }; +use rustc_errors::{DiagArgValue, DiagCtxtHandle}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; use rustc_pattern_analysis::{errors::Uncovered, rustc::RustcPatCtxt}; @@ -13,163 +13,193 @@ use rustc_span::Span; #[derive(LintDiagnostic)] #[diag(mir_build_unconditional_recursion)] #[help] -pub struct UnconditionalRecursion { +pub(crate) struct UnconditionalRecursion { #[label] - pub span: Span, + pub(crate) span: Span, #[label(mir_build_unconditional_recursion_call_site_label)] - pub call_sites: Vec, + pub(crate) call_sites: Vec, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe)] +#[diag(mir_build_call_to_deprecated_safe_fn_requires_unsafe)] +pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafe { + #[label] + pub(crate) span: Span, + pub(crate) function: String, + #[subdiagnostic] + pub(crate) sub: CallToDeprecatedSafeFnRequiresUnsafeSub, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(mir_build_suggestion, applicability = "machine-applicable")] +pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafeSub { + pub(crate) indent: String, + #[suggestion_part( + code = "{indent}// TODO: Audit that the environment access only happens in single-threaded code.\n" // ignore-tidy-todo + )] + pub(crate) start_of_line: Span, + #[suggestion_part(code = "unsafe {{ ")] + pub(crate) left: Span, + #[suggestion_part(code = " }}")] + pub(crate) right: Span, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe, code = E0133)] #[note] -pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe { +pub(crate) struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe { #[label] - pub span: Span, - pub function: String, + pub(crate) span: Span, + pub(crate) function: String, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe_nameless)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe_nameless, code = E0133)] #[note] -pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { +pub(crate) struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe, code = E0133)] #[note] -pub struct UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { +pub(crate) struct UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe, code = E0133)] #[note] -pub struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { +pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe, code = E0133)] #[note] -pub struct UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { +pub(crate) struct UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_extern_static_requires_unsafe)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_extern_static_requires_unsafe, code = E0133)] #[note] -pub struct UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { +pub(crate) struct UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe, code = E0133)] #[note] -pub struct UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { +pub(crate) struct UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe, code = E0133)] #[note] -pub struct UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { +pub(crate) struct UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_mutation_of_layout_constrained_field_requires_unsafe)] +#[diag( + mir_build_unsafe_op_in_unsafe_fn_mutation_of_layout_constrained_field_requires_unsafe, + code = E0133 +)] #[note] -pub struct UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { +pub(crate) struct UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe)] -pub struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { +#[diag( + mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe, + code = E0133, +)] +pub(crate) struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(LintDiagnostic)] -#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe, code = E0133)] #[help] -pub struct UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe { +pub(crate) struct UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe { #[label] - pub span: Span, - pub function: String, - pub missing_target_features: DiagArgValue, - pub missing_target_features_count: usize, + pub(crate) span: Span, + pub(crate) function: String, + pub(crate) missing_target_features: DiagArgValue, + pub(crate) missing_target_features_count: usize, #[note] - pub note: Option<()>, - pub build_target_features: DiagArgValue, - pub build_target_features_count: usize, + pub(crate) note: Option<()>, + pub(crate) build_target_features: DiagArgValue, + pub(crate) build_target_features_count: usize, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_call_to_unsafe_fn_requires_unsafe, code = E0133)] #[note] -pub struct CallToUnsafeFunctionRequiresUnsafe { +pub(crate) struct CallToUnsafeFunctionRequiresUnsafe { #[primary_span] #[label] - pub span: Span, - pub function: String, + pub(crate) span: Span, + pub(crate) function: String, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_call_to_unsafe_fn_requires_unsafe_nameless, code = E0133)] #[note] -pub struct CallToUnsafeFunctionRequiresUnsafeNameless { +pub(crate) struct CallToUnsafeFunctionRequiresUnsafeNameless { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] #[note] -pub struct CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed { +pub(crate) struct CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] #[label] - pub span: Span, - pub function: String, + pub(crate) span: Span, + pub(crate) function: String, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] @@ -178,45 +208,45 @@ pub struct CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed { code = E0133 )] #[note] -pub struct CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed { +pub(crate) struct CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_inline_assembly_requires_unsafe, code = E0133)] #[note] -pub struct UseOfInlineAssemblyRequiresUnsafe { +pub(crate) struct UseOfInlineAssemblyRequiresUnsafe { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] #[note] -pub struct UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed { +pub(crate) struct UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_initializing_type_with_requires_unsafe, code = E0133)] #[note] -pub struct InitializingTypeWithRequiresUnsafe { +pub(crate) struct InitializingTypeWithRequiresUnsafe { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] @@ -225,111 +255,111 @@ pub struct InitializingTypeWithRequiresUnsafe { code = E0133 )] #[note] -pub struct InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { +pub(crate) struct InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_mutable_static_requires_unsafe, code = E0133)] #[note] -pub struct UseOfMutableStaticRequiresUnsafe { +pub(crate) struct UseOfMutableStaticRequiresUnsafe { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_mutable_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] #[note] -pub struct UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { +pub(crate) struct UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_extern_static_requires_unsafe, code = E0133)] #[note] -pub struct UseOfExternStaticRequiresUnsafe { +pub(crate) struct UseOfExternStaticRequiresUnsafe { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] #[note] -pub struct UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { +pub(crate) struct UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_deref_raw_pointer_requires_unsafe, code = E0133)] #[note] -pub struct DerefOfRawPointerRequiresUnsafe { +pub(crate) struct DerefOfRawPointerRequiresUnsafe { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] #[note] -pub struct DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed { +pub(crate) struct DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_union_field_requires_unsafe, code = E0133)] #[note] -pub struct AccessToUnionFieldRequiresUnsafe { +pub(crate) struct AccessToUnionFieldRequiresUnsafe { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] #[note] -pub struct AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { +pub(crate) struct AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_mutation_of_layout_constrained_field_requires_unsafe, code = E0133)] #[note] -pub struct MutationOfLayoutConstrainedFieldRequiresUnsafe { +pub(crate) struct MutationOfLayoutConstrainedFieldRequiresUnsafe { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] @@ -338,23 +368,23 @@ pub struct MutationOfLayoutConstrainedFieldRequiresUnsafe { code = E0133 )] #[note] -pub struct MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { +pub(crate) struct MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_borrow_of_layout_constrained_field_requires_unsafe, code = E0133)] #[note] -pub struct BorrowOfLayoutConstrainedFieldRequiresUnsafe { +pub(crate) struct BorrowOfLayoutConstrainedFieldRequiresUnsafe { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] @@ -363,60 +393,60 @@ pub struct BorrowOfLayoutConstrainedFieldRequiresUnsafe { code = E0133 )] #[note] -pub struct BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { +pub(crate) struct BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_call_to_fn_with_requires_unsafe, code = E0133)] #[help] -pub struct CallToFunctionWithRequiresUnsafe { +pub(crate) struct CallToFunctionWithRequiresUnsafe { #[primary_span] #[label] - pub span: Span, - pub function: String, - pub missing_target_features: DiagArgValue, - pub missing_target_features_count: usize, + pub(crate) span: Span, + pub(crate) function: String, + pub(crate) missing_target_features: DiagArgValue, + pub(crate) missing_target_features_count: usize, #[note] - pub note: Option<()>, - pub build_target_features: DiagArgValue, - pub build_target_features_count: usize, + pub(crate) note: Option<()>, + pub(crate) build_target_features: DiagArgValue, + pub(crate) build_target_features_count: usize, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Diagnostic)] #[diag(mir_build_call_to_fn_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] #[help] -pub struct CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { +pub(crate) struct CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { #[primary_span] #[label] - pub span: Span, - pub function: String, - pub missing_target_features: DiagArgValue, - pub missing_target_features_count: usize, + pub(crate) span: Span, + pub(crate) function: String, + pub(crate) missing_target_features: DiagArgValue, + pub(crate) missing_target_features_count: usize, #[note] - pub note: Option<()>, - pub build_target_features: DiagArgValue, - pub build_target_features_count: usize, + pub(crate) note: Option<()>, + pub(crate) build_target_features: DiagArgValue, + pub(crate) build_target_features_count: usize, #[subdiagnostic] - pub unsafe_not_inherited_note: Option, + pub(crate) unsafe_not_inherited_note: Option, } #[derive(Subdiagnostic)] #[label(mir_build_unsafe_not_inherited)] -pub struct UnsafeNotInheritedNote { +pub(crate) struct UnsafeNotInheritedNote { #[primary_span] - pub span: Span, + pub(crate) span: Span, } -pub struct UnsafeNotInheritedLintNote { - pub signature_span: Span, - pub body_span: Span, +pub(crate) struct UnsafeNotInheritedLintNote { + pub(crate) signature_span: Span, + pub(crate) body_span: Span, } impl Subdiagnostic for UnsafeNotInheritedLintNote { @@ -438,15 +468,15 @@ impl Subdiagnostic for UnsafeNotInheritedLintNote { #[derive(LintDiagnostic)] #[diag(mir_build_unused_unsafe)] -pub struct UnusedUnsafe { +pub(crate) struct UnusedUnsafe { #[label] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub enclosing: Option, + pub(crate) enclosing: Option, } #[derive(Subdiagnostic)] -pub enum UnusedUnsafeEnclosing { +pub(crate) enum UnusedUnsafeEnclosing { #[label(mir_build_unused_unsafe_enclosing_block_label)] Block { #[primary_span] @@ -455,14 +485,14 @@ pub enum UnusedUnsafeEnclosing { } pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> { - pub cx: &'m RustcPatCtxt<'p, 'tcx>, - pub scrut_span: Span, - pub braces_span: Option, - pub ty: Ty<'tcx>, + pub(crate) cx: &'m RustcPatCtxt<'p, 'tcx>, + pub(crate) scrut_span: Span, + pub(crate) braces_span: Option, + pub(crate) ty: Ty<'tcx>, } impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> { - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'_, G> { let mut diag = Diag::new(dcx, level, fluent::mir_build_non_exhaustive_patterns_type_not_empty); diag.span(self.scrut_span); @@ -527,197 +557,197 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo #[derive(Subdiagnostic)] #[note(mir_build_non_exhaustive_match_all_arms_guarded)] -pub struct NonExhaustiveMatchAllArmsGuarded; +pub(crate) struct NonExhaustiveMatchAllArmsGuarded; #[derive(Diagnostic)] #[diag(mir_build_static_in_pattern, code = E0158)] -pub struct StaticInPattern { +pub(crate) struct StaticInPattern { #[primary_span] - pub span: Span, + pub(crate) span: Span, } #[derive(Diagnostic)] #[diag(mir_build_assoc_const_in_pattern, code = E0158)] -pub struct AssocConstInPattern { +pub(crate) struct AssocConstInPattern { #[primary_span] - pub span: Span, + pub(crate) span: Span, } #[derive(Diagnostic)] #[diag(mir_build_const_param_in_pattern, code = E0158)] -pub struct ConstParamInPattern { +pub(crate) struct ConstParamInPattern { #[primary_span] - pub span: Span, + pub(crate) span: Span, } #[derive(Diagnostic)] #[diag(mir_build_non_const_path, code = E0080)] -pub struct NonConstPath { +pub(crate) struct NonConstPath { #[primary_span] - pub span: Span, + pub(crate) span: Span, } #[derive(LintDiagnostic)] #[diag(mir_build_unreachable_pattern)] -pub struct UnreachablePattern { +pub(crate) struct UnreachablePattern { #[label] - pub span: Option, + pub(crate) span: Option, #[label(mir_build_catchall_label)] - pub catchall: Option, + pub(crate) catchall: Option, } #[derive(Diagnostic)] #[diag(mir_build_const_pattern_depends_on_generic_parameter)] -pub struct ConstPatternDependsOnGenericParameter { +pub(crate) struct ConstPatternDependsOnGenericParameter { #[primary_span] - pub span: Span, + pub(crate) span: Span, } #[derive(Diagnostic)] #[diag(mir_build_could_not_eval_const_pattern)] -pub struct CouldNotEvalConstPattern { +pub(crate) struct CouldNotEvalConstPattern { #[primary_span] - pub span: Span, + pub(crate) span: Span, } #[derive(Diagnostic)] #[diag(mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper, code = E0030)] -pub struct LowerRangeBoundMustBeLessThanOrEqualToUpper { +pub(crate) struct LowerRangeBoundMustBeLessThanOrEqualToUpper { #[primary_span] #[label] - pub span: Span, + pub(crate) span: Span, #[note(mir_build_teach_note)] - pub teach: Option<()>, + pub(crate) teach: Option<()>, } #[derive(Diagnostic)] #[diag(mir_build_literal_in_range_out_of_bounds)] -pub struct LiteralOutOfRange<'tcx> { +pub(crate) struct LiteralOutOfRange<'tcx> { #[primary_span] #[label] - pub span: Span, - pub ty: Ty<'tcx>, - pub min: i128, - pub max: u128, + pub(crate) span: Span, + pub(crate) ty: Ty<'tcx>, + pub(crate) min: i128, + pub(crate) max: u128, } #[derive(Diagnostic)] #[diag(mir_build_lower_range_bound_must_be_less_than_upper, code = E0579)] -pub struct LowerRangeBoundMustBeLessThanUpper { +pub(crate) struct LowerRangeBoundMustBeLessThanUpper { #[primary_span] - pub span: Span, + pub(crate) span: Span, } #[derive(LintDiagnostic)] #[diag(mir_build_leading_irrefutable_let_patterns)] #[note] #[help] -pub struct LeadingIrrefutableLetPatterns { - pub count: usize, +pub(crate) struct LeadingIrrefutableLetPatterns { + pub(crate) count: usize, } #[derive(LintDiagnostic)] #[diag(mir_build_trailing_irrefutable_let_patterns)] #[note] #[help] -pub struct TrailingIrrefutableLetPatterns { - pub count: usize, +pub(crate) struct TrailingIrrefutableLetPatterns { + pub(crate) count: usize, } #[derive(LintDiagnostic)] #[diag(mir_build_bindings_with_variant_name, code = E0170)] -pub struct BindingsWithVariantName { +pub(crate) struct BindingsWithVariantName { #[suggestion(code = "{ty_path}::{name}", applicability = "machine-applicable")] - pub suggestion: Option, - pub ty_path: String, - pub name: Symbol, + pub(crate) suggestion: Option, + pub(crate) ty_path: String, + pub(crate) name: Symbol, } #[derive(LintDiagnostic)] #[diag(mir_build_irrefutable_let_patterns_if_let)] #[note] #[help] -pub struct IrrefutableLetPatternsIfLet { - pub count: usize, +pub(crate) struct IrrefutableLetPatternsIfLet { + pub(crate) count: usize, } #[derive(LintDiagnostic)] #[diag(mir_build_irrefutable_let_patterns_if_let_guard)] #[note] #[help] -pub struct IrrefutableLetPatternsIfLetGuard { - pub count: usize, +pub(crate) struct IrrefutableLetPatternsIfLetGuard { + pub(crate) count: usize, } #[derive(LintDiagnostic)] #[diag(mir_build_irrefutable_let_patterns_let_else)] #[note] #[help] -pub struct IrrefutableLetPatternsLetElse { - pub count: usize, +pub(crate) struct IrrefutableLetPatternsLetElse { + pub(crate) count: usize, } #[derive(LintDiagnostic)] #[diag(mir_build_irrefutable_let_patterns_while_let)] #[note] #[help] -pub struct IrrefutableLetPatternsWhileLet { - pub count: usize, +pub(crate) struct IrrefutableLetPatternsWhileLet { + pub(crate) count: usize, } #[derive(Diagnostic)] #[diag(mir_build_borrow_of_moved_value)] -pub struct BorrowOfMovedValue<'tcx> { +pub(crate) struct BorrowOfMovedValue<'tcx> { #[primary_span] #[label] #[label(mir_build_occurs_because_label)] - pub binding_span: Span, + pub(crate) binding_span: Span, #[label(mir_build_value_borrowed_label)] - pub conflicts_ref: Vec, - pub name: Symbol, - pub ty: Ty<'tcx>, + pub(crate) conflicts_ref: Vec, + pub(crate) name: Symbol, + pub(crate) ty: Ty<'tcx>, #[suggestion(code = "ref ", applicability = "machine-applicable")] - pub suggest_borrowing: Option, + pub(crate) suggest_borrowing: Option, } #[derive(Diagnostic)] #[diag(mir_build_multiple_mut_borrows)] -pub struct MultipleMutBorrows { +pub(crate) struct MultipleMutBorrows { #[primary_span] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub occurrences: Vec, + pub(crate) occurrences: Vec, } #[derive(Diagnostic)] #[diag(mir_build_already_borrowed)] -pub struct AlreadyBorrowed { +pub(crate) struct AlreadyBorrowed { #[primary_span] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub occurrences: Vec, + pub(crate) occurrences: Vec, } #[derive(Diagnostic)] #[diag(mir_build_already_mut_borrowed)] -pub struct AlreadyMutBorrowed { +pub(crate) struct AlreadyMutBorrowed { #[primary_span] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub occurrences: Vec, + pub(crate) occurrences: Vec, } #[derive(Diagnostic)] #[diag(mir_build_moved_while_borrowed)] -pub struct MovedWhileBorrowed { +pub(crate) struct MovedWhileBorrowed { #[primary_span] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub occurrences: Vec, + pub(crate) occurrences: Vec, } #[derive(Subdiagnostic)] -pub enum Conflict { +pub(crate) enum Conflict { #[label(mir_build_mutable_borrow)] Mut { #[primary_span] @@ -740,131 +770,118 @@ pub enum Conflict { #[derive(Diagnostic)] #[diag(mir_build_union_pattern)] -pub struct UnionPattern { +pub(crate) struct UnionPattern { #[primary_span] - pub span: Span, + pub(crate) span: Span, } #[derive(Diagnostic)] #[diag(mir_build_type_not_structural)] #[note(mir_build_type_not_structural_tip)] #[note(mir_build_type_not_structural_more_info)] -pub struct TypeNotStructural<'tcx> { +pub(crate) struct TypeNotStructural<'tcx> { #[primary_span] - pub span: Span, - pub non_sm_ty: Ty<'tcx>, + pub(crate) span: Span, + pub(crate) non_sm_ty: Ty<'tcx>, } #[derive(Diagnostic)] #[diag(mir_build_non_partial_eq_match)] -pub struct TypeNotPartialEq<'tcx> { +pub(crate) struct TypeNotPartialEq<'tcx> { #[primary_span] - pub span: Span, - pub non_peq_ty: Ty<'tcx>, + pub(crate) span: Span, + pub(crate) non_peq_ty: Ty<'tcx>, } #[derive(Diagnostic)] #[diag(mir_build_invalid_pattern)] -pub struct InvalidPattern<'tcx> { +pub(crate) struct InvalidPattern<'tcx> { #[primary_span] - pub span: Span, - pub non_sm_ty: Ty<'tcx>, + pub(crate) span: Span, + pub(crate) non_sm_ty: Ty<'tcx>, } #[derive(Diagnostic)] #[diag(mir_build_unsized_pattern)] -pub struct UnsizedPattern<'tcx> { +pub(crate) struct UnsizedPattern<'tcx> { #[primary_span] - pub span: Span, - pub non_sm_ty: Ty<'tcx>, + pub(crate) span: Span, + pub(crate) non_sm_ty: Ty<'tcx>, } #[derive(Diagnostic)] #[diag(mir_build_nan_pattern)] #[note] #[help] -pub struct NaNPattern { +pub(crate) struct NaNPattern { #[primary_span] - pub span: Span, + pub(crate) span: Span, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag(mir_build_pointer_pattern)] -pub struct PointerPattern; +pub(crate) struct PointerPattern { + #[primary_span] + pub(crate) span: Span, +} #[derive(Diagnostic)] #[diag(mir_build_non_empty_never_pattern)] #[note] -pub struct NonEmptyNeverPattern<'tcx> { +pub(crate) struct NonEmptyNeverPattern<'tcx> { #[primary_span] #[label] - pub span: Span, - pub ty: Ty<'tcx>, -} - -#[derive(LintDiagnostic)] -#[diag(mir_build_indirect_structural_match)] -#[note(mir_build_type_not_structural_tip)] -#[note(mir_build_type_not_structural_more_info)] -pub struct IndirectStructuralMatch<'tcx> { - pub non_sm_ty: Ty<'tcx>, -} - -#[derive(LintDiagnostic)] -#[diag(mir_build_nontrivial_structural_match)] -#[note(mir_build_type_not_structural_tip)] -#[note(mir_build_type_not_structural_more_info)] -pub struct NontrivialStructuralMatch<'tcx> { - pub non_sm_ty: Ty<'tcx>, + pub(crate) span: Span, + pub(crate) ty: Ty<'tcx>, } #[derive(Diagnostic)] -#[diag(mir_build_exceeds_mcdc_condition_num_limit)] -pub(crate) struct MCDCExceedsConditionNumLimit { +#[diag(mir_build_exceeds_mcdc_condition_limit)] +pub(crate) struct MCDCExceedsConditionLimit { #[primary_span] - pub span: Span, - pub conditions_num: usize, - pub max_conditions_num: usize, + pub(crate) span: Span, + pub(crate) num_conditions: usize, + pub(crate) max_conditions: usize, } #[derive(Diagnostic)] #[diag(mir_build_pattern_not_covered, code = E0005)] pub(crate) struct PatternNotCovered<'s, 'tcx> { #[primary_span] - pub span: Span, - pub origin: &'s str, + pub(crate) span: Span, + pub(crate) origin: &'s str, #[subdiagnostic] - pub uncovered: Uncovered<'tcx>, + pub(crate) uncovered: Uncovered<'tcx>, #[subdiagnostic] - pub inform: Option, + pub(crate) inform: Option, #[subdiagnostic] - pub interpreted_as_const: Option, + pub(crate) interpreted_as_const: Option, #[subdiagnostic] - pub adt_defined_here: Option>, + pub(crate) adt_defined_here: Option>, #[note(mir_build_privately_uninhabited)] - pub witness_1_is_privately_uninhabited: Option<()>, + pub(crate) witness_1_is_privately_uninhabited: Option<()>, #[note(mir_build_pattern_ty)] - pub _p: (), - pub pattern_ty: Ty<'tcx>, + pub(crate) _p: (), + pub(crate) pattern_ty: Ty<'tcx>, #[subdiagnostic] - pub let_suggestion: Option, + pub(crate) let_suggestion: Option, #[subdiagnostic] - pub misc_suggestion: Option, + pub(crate) misc_suggestion: Option, } #[derive(Subdiagnostic)] #[note(mir_build_inform_irrefutable)] #[note(mir_build_more_information)] -pub struct Inform; +pub(crate) struct Inform; -pub struct AdtDefinedHere<'tcx> { - pub adt_def_span: Span, - pub ty: Ty<'tcx>, - pub variants: Vec, +pub(crate) struct AdtDefinedHere<'tcx> { + pub(crate) adt_def_span: Span, + pub(crate) ty: Ty<'tcx>, + pub(crate) variants: Vec, } -pub struct Variant { - pub span: Span, +pub(crate) struct Variant { + pub(crate) span: Span, } impl<'tcx> Subdiagnostic for AdtDefinedHere<'tcx> { @@ -891,14 +908,14 @@ impl<'tcx> Subdiagnostic for AdtDefinedHere<'tcx> { applicability = "maybe-incorrect" )] #[label(mir_build_confused)] -pub struct InterpretedAsConst { +pub(crate) struct InterpretedAsConst { #[primary_span] - pub span: Span, - pub variable: String, + pub(crate) span: Span, + pub(crate) variable: String, } #[derive(Subdiagnostic)] -pub enum SuggestLet { +pub(crate) enum SuggestLet { #[multipart_suggestion(mir_build_suggest_if_let, applicability = "has-placeholders")] If { #[suggestion_part(code = "if ")] @@ -920,7 +937,7 @@ pub enum SuggestLet { } #[derive(Subdiagnostic)] -pub enum MiscPatternSuggestion { +pub(crate) enum MiscPatternSuggestion { #[suggestion( mir_build_suggest_attempted_int_lit, code = "_", @@ -934,15 +951,15 @@ pub enum MiscPatternSuggestion { #[derive(Diagnostic)] #[diag(mir_build_rustc_box_attribute_error)] -pub struct RustcBoxAttributeError { +pub(crate) struct RustcBoxAttributeError { #[primary_span] - pub span: Span, + pub(crate) span: Span, #[subdiagnostic] - pub reason: RustcBoxAttrReason, + pub(crate) reason: RustcBoxAttrReason, } #[derive(Subdiagnostic)] -pub enum RustcBoxAttrReason { +pub(crate) enum RustcBoxAttrReason { #[note(mir_build_attributes)] Attributes, #[note(mir_build_not_box)] @@ -953,13 +970,13 @@ pub enum RustcBoxAttrReason { #[derive(LintDiagnostic)] #[diag(mir_build_rust_2024_incompatible_pat)] -pub struct Rust2024IncompatiblePat { +pub(crate) struct Rust2024IncompatiblePat { #[subdiagnostic] - pub sugg: Rust2024IncompatiblePatSugg, + pub(crate) sugg: Rust2024IncompatiblePatSugg, } -pub struct Rust2024IncompatiblePatSugg { - pub suggestion: Vec<(Span, String)>, +pub(crate) struct Rust2024IncompatiblePatSugg { + pub(crate) suggestion: Vec<(Span, String)>, } impl Subdiagnostic for Rust2024IncompatiblePatSugg { diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 2e1cb0e1b5e0..66004179b10b 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -1,7 +1,6 @@ //! Construction of MIR from HIR. -//! -//! This crate also contains the match exhaustiveness and usefulness checking. +// tidy-alphabetical-start #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] #![feature(assert_matches)] @@ -9,9 +8,7 @@ #![feature(if_let_guard)] #![feature(let_chains)] #![feature(try_blocks)] - -#[macro_use] -extern crate tracing; +// tidy-alphabetical-end mod build; mod check_unsafety; diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index 03c7c1fd6ec6..7b94867114d5 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -1,7 +1,9 @@ use rustc_ast as ast; +use rustc_hir::LangItem; use rustc_middle::bug; use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; use rustc_middle::ty::{self, ParamEnv, ScalarInt, TyCtxt}; +use tracing::trace; use crate::build::parse_float_into_scalar; @@ -45,7 +47,7 @@ pub(crate) fn lit_to_const<'tcx>( (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { ty::ValTree::from_scalar_int((*n).into()) } - (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().c_str()) => + (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) => { let bytes = data as &[u8]; ty::ValTree::from_raw_bytes(tcx, bytes) @@ -57,11 +59,9 @@ pub(crate) fn lit_to_const<'tcx>( } (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()), (ast::LitKind::Float(n, _), ty::Float(fty)) => { - let bits = parse_float_into_scalar(*n, *fty, neg) - .ok_or_else(|| { - tcx.dcx().bug(format!("couldn't parse float literal: {:?}", lit_input.lit)) - })? - .assert_int(); + let bits = parse_float_into_scalar(*n, *fty, neg).ok_or_else(|| { + tcx.dcx().bug(format!("couldn't parse float literal: {:?}", lit_input.lit)) + })?; ty::ValTree::from_scalar_int(bits) } (ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int((*c).into()), diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index d4a347975dba..95cd703dbb3c 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -1,12 +1,12 @@ use crate::thir::cx::Cx; use rustc_hir as hir; +use rustc_index::Idx; use rustc_middle::middle::region; use rustc_middle::thir::*; use rustc_middle::ty; - -use rustc_index::Idx; use rustc_middle::ty::CanonicalUserTypeAnnotation; +use tracing::debug; impl<'tcx> Cx<'tcx> { pub(crate) fn mirror_block(&mut self, block: &'tcx hir::Block<'tcx>) -> BlockId { @@ -92,7 +92,7 @@ impl<'tcx> Cx<'tcx> { kind: PatKind::AscribeUserType { ascription: Ascription { annotation, - variance: ty::Variance::Covariant, + variance: ty::Covariant, }, subpattern: pattern, }, diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index b77666669605..28f9300b97a8 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -25,6 +25,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::source_map::Spanned; use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; +use tracing::{debug, info, instrument, trace}; impl<'tcx> Cx<'tcx> { pub(crate) fn mirror_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> ExprId { diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index d1d21f88aef6..244ac409fd38 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -17,13 +17,14 @@ use rustc_middle::bug; use rustc_middle::middle::region; use rustc_middle::thir::*; use rustc_middle::ty::{self, RvalueScopes, TyCtxt}; +use tracing::instrument; pub(crate) fn thir_body( tcx: TyCtxt<'_>, owner_def: LocalDefId, ) -> Result<(&Steal>, ExprId), ErrorGuaranteed> { let hir = tcx.hir(); - let body = hir.body(hir.body_owned_by(owner_def)); + let body = hir.body_owned_by(owner_def); let mut cx = Cx::new(tcx, owner_def); if let Some(reported) = cx.typeck_results.tainted_by_errors { return Err(reported); @@ -33,7 +34,7 @@ pub(crate) fn thir_body( let owner_id = tcx.local_def_id_to_hir_id(owner_def); if let Some(fn_decl) = hir.fn_decl_by_hir_id(owner_id) { let closure_env_param = cx.closure_env_param(owner_def, owner_id); - let explicit_params = cx.explicit_params(owner_id, fn_decl, body); + let explicit_params = cx.explicit_params(owner_id, fn_decl, &body); cx.thir.params = closure_env_param.into_iter().chain(explicit_params).collect(); // The resume argument may be missing, in that case we need to provide it here. diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 592f0dcf4ef5..70065b5a2c32 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -24,6 +24,7 @@ use rustc_session::lint::builtin::{ }; use rustc_span::hygiene::DesugaringKind; use rustc_span::{sym, Span}; +use tracing::instrument; pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> { let typeck_results = tcx.typeck(def_id); @@ -1136,7 +1137,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( let all_arms_have_guards = arms.iter().all(|arm_id| thir[*arm_id].guard.is_some()); if !is_empty_match && all_arms_have_guards { - err.subdiagnostic(cx.tcx.dcx(), NonExhaustiveMatchAllArmsGuarded); + err.subdiagnostic(NonExhaustiveMatchAllArmsGuarded); } if let Some((span, sugg)) = suggestion { err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders); diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index c6f81c3cb997..192d706bce2c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -7,18 +7,18 @@ use rustc_middle::mir; use rustc_middle::span_bug; use rustc_middle::thir::{FieldPat, Pat, PatKind}; use rustc_middle::ty::{self, Ty, TyCtxt, ValTree}; -use rustc_session::lint; use rustc_span::{ErrorGuaranteed, Span}; use rustc_target::abi::{FieldIdx, VariantIdx}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCause}; +use tracing::{debug, instrument, trace}; use std::cell::Cell; use super::PatCtxt; use crate::errors::{ - IndirectStructuralMatch, InvalidPattern, NaNPattern, PointerPattern, TypeNotPartialEq, - TypeNotStructural, UnionPattern, UnsizedPattern, + InvalidPattern, NaNPattern, PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern, + UnsizedPattern, }; impl<'a, 'tcx> PatCtxt<'a, 'tcx> { @@ -50,15 +50,6 @@ struct ConstToPat<'tcx> { // value. saw_const_match_error: Cell>, - // This tracks if we emitted some diagnostic for a given const value, so that - // we will not subsequently issue an irrelevant lint for the same const - // value. - saw_const_match_lint: Cell, - - // For backcompat we need to keep allowing non-structurally-eq types behind references. - // See also all the `cant-hide-behind` tests. - behind_reference: Cell, - // inference context used for checking `T: Structural` bounds. infcx: InferCtxt<'tcx>, @@ -85,8 +76,6 @@ impl<'tcx> ConstToPat<'tcx> { infcx, param_env: pat_ctxt.param_env, saw_const_match_error: Cell::new(None), - saw_const_match_lint: Cell::new(false), - behind_reference: Cell::new(false), treat_byte_string_as_slice: pat_ctxt .typeck_results .treat_byte_string_as_slice @@ -112,9 +101,9 @@ impl<'tcx> ConstToPat<'tcx> { // level of indirection can be eliminated let have_valtree = - matches!(cv, mir::Const::Ty(c) if matches!(c.kind(), ty::ConstKind::Value(_))); + matches!(cv, mir::Const::Ty(_, c) if matches!(c.kind(), ty::ConstKind::Value(_, _))); let inlined_const_as_pat = match cv { - mir::Const::Ty(c) => match c.kind() { + mir::Const::Ty(_, c) => match c.kind() { ty::ConstKind::Param(_) | ty::ConstKind::Infer(_) | ty::ConstKind::Bound(_, _) @@ -124,8 +113,8 @@ impl<'tcx> ConstToPat<'tcx> { | ty::ConstKind::Expr(_) => { span_bug!(self.span, "unexpected const in `to_pat`: {:?}", c.kind()) } - ty::ConstKind::Value(valtree) => { - self.recur(valtree, cv.ty()).unwrap_or_else(|_: FallbackToOpaqueConst| { + ty::ConstKind::Value(ty, valtree) => { + self.recur(valtree, ty).unwrap_or_else(|_: FallbackToOpaqueConst| { Box::new(Pat { span: self.span, ty: cv.ty(), @@ -198,15 +187,12 @@ impl<'tcx> ConstToPat<'tcx> { // complained about structural match violations there, so we don't // have to check anything any more. } - } else if !have_valtree && !self.saw_const_match_lint.get() { + } else if !have_valtree { // The only way valtree construction can fail without the structural match // checker finding a violation is if there is a pointer somewhere. - self.tcx().emit_node_span_lint( - lint::builtin::POINTER_STRUCTURAL_MATCH, - self.id, - self.span, - PointerPattern, - ); + let e = self.tcx().dcx().emit_err(PointerPattern { span: self.span }); + let kind = PatKind::Error(e); + return Box::new(Pat { span: self.span, ty: cv.ty(), kind }); } // Always check for `PartialEq` if we had no other errors yet. @@ -275,36 +261,11 @@ impl<'tcx> ConstToPat<'tcx> { cv: ValTree<'tcx>, ty: Ty<'tcx>, ) -> Result>, FallbackToOpaqueConst> { - let id = self.id; let span = self.span; let tcx = self.tcx(); let param_env = self.param_env; let kind = match ty.kind() { - // If the type is not structurally comparable, just emit the constant directly, - // causing the pattern match code to treat it opaquely. - // FIXME: This code doesn't emit errors itself, the caller emits the errors. - // So instead of specific errors, you just get blanket errors about the whole - // const type. See - // https://github.com/rust-lang/rust/pull/70743#discussion_r404701963 for - // details. - // Backwards compatibility hack because we can't cause hard errors on these - // types, so we compare them via `PartialEq::eq` at runtime. - ty::Adt(..) if !self.type_marked_structural(ty) && self.behind_reference.get() => { - if self.saw_const_match_error.get().is_none() && !self.saw_const_match_lint.get() { - self.saw_const_match_lint.set(true); - tcx.emit_node_span_lint( - lint::builtin::INDIRECT_STRUCTURAL_MATCH, - id, - span, - IndirectStructuralMatch { non_sm_ty: ty }, - ); - } - // Since we are behind a reference, we can just bubble the error up so we get a - // constant at reference type, making it easy to let the fallback call - // `PartialEq::eq` on it. - return Err(FallbackToOpaqueConst); - } ty::FnDef(..) => { let e = tcx.dcx().emit_err(InvalidPattern { span, non_sm_ty: ty }); self.saw_const_match_error.set(Some(e)); @@ -321,8 +282,7 @@ impl<'tcx> ConstToPat<'tcx> { } ty::Adt(adt_def, args) if adt_def.is_enum() => { let (&variant_index, fields) = cv.unwrap_branch().split_first().unwrap(); - let variant_index = - VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap()); + let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().to_u32()); PatKind::Variant { adt_def: *adt_def, args, @@ -375,41 +335,9 @@ impl<'tcx> ConstToPat<'tcx> { ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() { // `&str` is represented as a valtree, let's keep using this // optimization for now. - ty::Str => { - PatKind::Constant { value: mir::Const::Ty(ty::Const::new_value(tcx, cv, ty)) } - } - // Backwards compatibility hack: support references to non-structural types, - // but hard error if we aren't behind a double reference. We could just use - // the fallback code path below, but that would allow *more* of this fishy - // code to compile, as then it only goes through the future incompat lint - // instead of a hard error. - ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => { - if self.behind_reference.get() { - if self.saw_const_match_error.get().is_none() - && !self.saw_const_match_lint.get() - { - self.saw_const_match_lint.set(true); - tcx.emit_node_span_lint( - lint::builtin::INDIRECT_STRUCTURAL_MATCH, - self.id, - span, - IndirectStructuralMatch { non_sm_ty: *pointee_ty }, - ); - } - return Err(FallbackToOpaqueConst); - } else { - if let Some(e) = self.saw_const_match_error.get() { - // We already errored. Signal that in the pattern, so that follow up errors can be silenced. - PatKind::Error(e) - } else { - let err = TypeNotStructural { span, non_sm_ty: *pointee_ty }; - let e = tcx.dcx().emit_err(err); - self.saw_const_match_error.set(Some(e)); - // We errored. Signal that in the pattern, so that follow up errors can be silenced. - PatKind::Error(e) - } - } - } + ty::Str => PatKind::Constant { + value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)), + }, // All other references are converted into deref patterns and then recursively // convert the dereferenced constant to a pattern that is the sub-pattern of the // deref pattern. @@ -420,7 +348,6 @@ impl<'tcx> ConstToPat<'tcx> { // We errored. Signal that in the pattern, so that follow up errors can be silenced. PatKind::Error(e) } else { - let old = self.behind_reference.replace(true); // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when // matching against references, you can only use byte string literals. // The typechecker has a special case for byte string literals, by treating them @@ -435,7 +362,6 @@ impl<'tcx> ConstToPat<'tcx> { }; // References have the same valtree representation as their pointee. let subpattern = self.recur(cv, pointee_ty)?; - self.behind_reference.set(old); PatKind::Deref { subpattern } } } @@ -444,8 +370,8 @@ impl<'tcx> ConstToPat<'tcx> { let v = cv.unwrap_leaf(); let is_nan = match flt { ty::FloatTy::F16 => unimplemented!("f16_f128"), - ty::FloatTy::F32 => v.try_to_f32().unwrap().is_nan(), - ty::FloatTy::F64 => v.try_to_f64().unwrap().is_nan(), + ty::FloatTy::F32 => v.to_f32().is_nan(), + ty::FloatTy::F64 => v.to_f64().is_nan(), ty::FloatTy::F128 => unimplemented!("f16_f128"), }; if is_nan { @@ -455,13 +381,15 @@ impl<'tcx> ConstToPat<'tcx> { self.saw_const_match_error.set(Some(e)); return Err(FallbackToOpaqueConst); } else { - PatKind::Constant { value: mir::Const::Ty(ty::Const::new_value(tcx, cv, ty)) } + PatKind::Constant { + value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)), + } } } ty::Pat(..) | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => { // The raw pointers we see here have been "vetted" by valtree construction to be // just integers, so we simply allow them. - PatKind::Constant { value: mir::Const::Ty(ty::Const::new_value(tcx, cv, ty)) } + PatKind::Constant { value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)) } } ty::FnPtr(..) => { unreachable!( diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index dc845b3d4acc..93db1f618533 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -25,6 +25,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; use rustc_span::{ErrorGuaranteed, Span}; use rustc_target::abi::{FieldIdx, Integer}; +use tracing::{debug, instrument}; use std::cmp::Ordering; @@ -524,7 +525,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }; kind = PatKind::AscribeUserType { subpattern: Box::new(Pat { span, ty, kind }), - ascription: Ascription { annotation, variance: ty::Variance::Covariant }, + ascription: Ascription { annotation, variance: ty::Covariant }, }; } @@ -579,7 +580,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .tcx .const_eval_global_id_for_typeck(param_env_reveal_all, cid, span) .map(|val| match val { - Some(valtree) => mir::Const::Ty(ty::Const::new_value(self.tcx, valtree, ty)), + Some(valtree) => mir::Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)), None => mir::Const::Val( self.tcx .const_eval_global_id(param_env_reveal_all, cid, span) @@ -611,7 +612,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { annotation, // Note that use `Contravariant` here. See the // `variance` field documentation for details. - variance: ty::Variance::Contravariant, + variance: ty::Contravariant, }, }, ty: const_.ty(), @@ -660,7 +661,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }; if let Some(lit_input) = lit_input { match tcx.at(expr.span).lit_to_const(lit_input) { - Ok(c) => return self.const_to_pat(Const::Ty(c), id, span).kind, + Ok(c) => return self.const_to_pat(Const::Ty(ty, c), id, span).kind, // If an error occurred, ignore that it's a literal // and leave reporting the error up to const eval of // the unevaluated constant below. @@ -682,8 +683,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // but something more principled, like a trait query checking whether this can be turned into a valtree. if let Ok(Some(valtree)) = self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, span) { - let subpattern = - self.const_to_pat(Const::Ty(ty::Const::new_value(self.tcx, valtree, ty)), id, span); + let subpattern = self.const_to_pat( + Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)), + id, + span, + ); PatKind::InlineConstant { subpattern, def: def_id } } else { // If that fails, convert it to an opaque constant pattern. @@ -721,10 +725,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { _ => span_bug!(expr.span, "not a literal: {:?}", expr), }; - let lit_input = - LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg }; + let ct_ty = self.typeck_results.expr_ty(expr); + let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg }; match self.tcx.at(expr.span).lit_to_const(lit_input) { - Ok(constant) => self.const_to_pat(Const::Ty(constant), expr.hir_id, lit.span).kind, + Ok(constant) => { + self.const_to_pat(Const::Ty(ct_ty, constant), expr.hir_id, lit.span).kind + } Err(LitToConstError::Reported(e)) => PatKind::Error(e), Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"), } diff --git a/compiler/rustc_mir_build/src/thir/util.rs b/compiler/rustc_mir_build/src/thir/util.rs index 340eb3c2eea0..53a2a0852eb4 100644 --- a/compiler/rustc_mir_build/src/thir/util.rs +++ b/compiler/rustc_mir_build/src/thir/util.rs @@ -1,6 +1,7 @@ use rustc_hir as hir; use rustc_middle::bug; use rustc_middle::ty::{self, CanonicalUserType, TyCtxt, UserType}; +use tracing::debug; pub(crate) trait UserAnnotatedTyHelpers<'tcx> { fn tcx(&self) -> TyCtxt<'tcx>; diff --git a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs index de1ca8d823b3..82c59d7d9595 100644 --- a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs +++ b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs @@ -1,6 +1,7 @@ use crate::elaborate_drops::DropFlagState; use rustc_middle::mir::{self, Body, Location, Terminator, TerminatorKind}; use rustc_target::abi::VariantIdx; +use tracing::debug; use super::move_paths::{InitKind, LookupResult, MoveData, MovePathIndex}; use super::MoveDataParamEnv; diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index 185e87baed8c..654020164db8 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -11,6 +11,7 @@ use rustc_span::source_map::Spanned; use rustc_span::DUMMY_SP; use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; use std::{fmt, iter}; +use tracing::{debug, instrument}; /// The value of an inserted drop flag. #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -453,8 +454,7 @@ where }); } - let skip_contents = - adt.is_union() || Some(adt.did()) == self.tcx().lang_items().manually_drop(); + let skip_contents = adt.is_union() || adt.is_manually_drop(); let contents_drop = if skip_contents { (self.succ, self.unwind) } else { diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs index ea9cf6565e7c..564a99e5df81 100644 --- a/compiler/rustc_mir_dataflow/src/framework/engine.rs +++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs @@ -19,6 +19,7 @@ use rustc_middle::mir::{create_dump_file, dump_enabled}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{sym, Symbol}; +use tracing::{debug, error}; use super::fmt::DebugWithContext; use super::graphviz; diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index bdc70de58e84..706bb796349f 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -109,7 +109,6 @@ where | Rvalue::Repeat(..) | Rvalue::Len(..) | Rvalue::BinaryOp(..) - | Rvalue::CheckedBinaryOp(..) | Rvalue::NullaryOp(..) | Rvalue::UnaryOp(..) | Rvalue::Discriminant(..) diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 180168c73168..f0b79dab0c97 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -3,6 +3,7 @@ use rustc_index::Idx; use rustc_middle::bug; use rustc_middle::mir::{self, Body, CallReturnPlaces, Location, TerminatorEdges}; use rustc_middle::ty::{self, TyCtxt}; +use tracing::{debug, instrument}; use crate::drop_flag_effects_for_function_entry; use crate::drop_flag_effects_for_location; diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 5e96a73f6c53..b0808ba2067e 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -1,11 +1,10 @@ +// tidy-alphabetical-start #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(exact_size_is_empty)] #![feature(let_chains)] #![feature(try_blocks)] - -#[macro_use] -extern crate tracing; +// tidy-alphabetical-end use rustc_middle::ty; diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 6ae7df79d309..1fb77bef3d41 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -4,6 +4,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use smallvec::{smallvec, SmallVec}; +use tracing::debug; use std::mem; @@ -420,8 +421,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | Rvalue::Cast(_, ref operand, _) | Rvalue::ShallowInitBox(ref operand, _) | Rvalue::UnaryOp(_, ref operand) => self.gather_operand(operand), - Rvalue::BinaryOp(ref _binop, box (ref lhs, ref rhs)) - | Rvalue::CheckedBinaryOp(ref _binop, box (ref lhs, ref rhs)) => { + Rvalue::BinaryOp(ref _binop, box (ref lhs, ref rhs)) => { self.gather_operand(lhs); self.gather_operand(rhs); } diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index d7e3c91b875f..1de9055273b7 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -18,6 +18,7 @@ use rustc_middle::mir::{self, Body, Local, Location}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; +use tracing::{debug, info}; pub struct SanityCheck; diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 807bef074117..5e2d88f94ca2 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -45,6 +45,7 @@ use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_target::abi::{FieldIdx, VariantIdx}; +use tracing::debug; use crate::lattice::{HasBottom, HasTop}; use crate::{ @@ -184,7 +185,6 @@ pub trait ValueAnalysis<'tcx> { | Rvalue::Len(..) | Rvalue::Cast(..) | Rvalue::BinaryOp(..) - | Rvalue::CheckedBinaryOp(..) | Rvalue::NullaryOp(..) | Rvalue::UnaryOp(..) | Rvalue::Discriminant(..) diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index bd0a54ef3638..f864a13a31bb 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -16,6 +16,7 @@ rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } +rustc_infer = { path = "../rustc_infer" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_mir_build = { path = "../rustc_mir_build" } diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs index f880476cec2f..16977a63c598 100644 --- a/compiler/rustc_mir_transform/src/add_retag.rs +++ b/compiler/rustc_mir_transform/src/add_retag.rs @@ -4,6 +4,7 @@ //! of MIR building, and only after this pass we think of the program has having the //! normal MIR semantics. +use rustc_hir::LangItem; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -27,7 +28,7 @@ fn may_contain_reference<'tcx>(ty: Ty<'tcx>, depth: u32, tcx: TyCtxt<'tcx>) -> b // References and Boxes (`noalias` sources) ty::Ref(..) => true, ty::Adt(..) if ty.is_box() => true, - ty::Adt(adt, _) if Some(adt.did()) == tcx.lang_items().ptr_unique() => true, + ty::Adt(adt, _) if tcx.is_lang_item(adt.did(), LangItem::PtrUnique) => true, // Compound types: recurse ty::Array(ty, _) | ty::Slice(ty) => { // This does not branch so we keep the depth the same. diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index 48a6a83e1460..264d8a139960 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -18,7 +18,8 @@ use crate::MirPass; use rustc_middle::mir::coverage::CoverageKind; -use rustc_middle::mir::{Body, BorrowKind, Rvalue, StatementKind, TerminatorKind}; +use rustc_middle::mir::{Body, BorrowKind, CastKind, Rvalue, StatementKind, TerminatorKind}; +use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::TyCtxt; pub struct CleanupPostBorrowck; @@ -36,6 +37,22 @@ impl<'tcx> MirPass<'tcx> for CleanupPostBorrowck { CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. }, ) | StatementKind::FakeRead(..) => statement.make_nop(), + StatementKind::Assign(box ( + _, + Rvalue::Cast( + ref mut cast_kind @ CastKind::PointerCoercion( + PointerCoercion::ArrayToPointer + | PointerCoercion::MutToConstPointer, + ), + .., + ), + )) => { + // BorrowCk needed to track whether these cases were coercions or casts, + // to know whether to check lifetimes in their pointees, + // but from now on that distinction doesn't matter, + // so just make them ordinary pointer casts instead. + *cast_kind = CastKind::PtrToPtr; + } _ => (), } } diff --git a/compiler/rustc_mir_transform/src/const_debuginfo.rs b/compiler/rustc_mir_transform/src/const_debuginfo.rs deleted file mode 100644 index e4e4270c4995..000000000000 --- a/compiler/rustc_mir_transform/src/const_debuginfo.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! Finds locals which are assigned once to a const and unused except for debuginfo and converts -//! their debuginfo to use the const directly, allowing the local to be removed. - -use rustc_middle::{ - mir::{ - visit::{PlaceContext, Visitor}, - Body, ConstOperand, Local, Location, Operand, Rvalue, StatementKind, VarDebugInfoContents, - }, - ty::TyCtxt, -}; - -use crate::MirPass; -use rustc_index::{bit_set::BitSet, IndexVec}; - -pub struct ConstDebugInfo; - -impl<'tcx> MirPass<'tcx> for ConstDebugInfo { - fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.mir_opt_level() > 0 - } - - fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - trace!("running ConstDebugInfo on {:?}", body.source); - - for (local, constant) in find_optimization_opportunities(body) { - for debuginfo in &mut body.var_debug_info { - if let VarDebugInfoContents::Place(p) = debuginfo.value { - if p.local == local && p.projection.is_empty() { - trace!( - "changing debug info for {:?} from place {:?} to constant {:?}", - debuginfo.name, - p, - constant - ); - debuginfo.value = VarDebugInfoContents::Const(constant); - } - } - } - } - } -} - -struct LocalUseVisitor { - local_mutating_uses: IndexVec, - local_assignment_locations: IndexVec>, -} - -fn find_optimization_opportunities<'tcx>(body: &Body<'tcx>) -> Vec<(Local, ConstOperand<'tcx>)> { - let mut visitor = LocalUseVisitor { - local_mutating_uses: IndexVec::from_elem(0, &body.local_decls), - local_assignment_locations: IndexVec::from_elem(None, &body.local_decls), - }; - - visitor.visit_body(body); - - let mut locals_to_debuginfo = BitSet::new_empty(body.local_decls.len()); - for debuginfo in &body.var_debug_info { - if let VarDebugInfoContents::Place(p) = debuginfo.value - && let Some(l) = p.as_local() - { - locals_to_debuginfo.insert(l); - } - } - - let mut eligible_locals = Vec::new(); - for (local, mutating_uses) in visitor.local_mutating_uses.drain_enumerated(..) { - if mutating_uses != 1 || !locals_to_debuginfo.contains(local) { - continue; - } - - if let Some(location) = visitor.local_assignment_locations[local] { - let bb = &body[location.block]; - - // The value is assigned as the result of a call, not a constant - if bb.statements.len() == location.statement_index { - continue; - } - - if let StatementKind::Assign(box (p, Rvalue::Use(Operand::Constant(box c)))) = - &bb.statements[location.statement_index].kind - { - if let Some(local) = p.as_local() { - eligible_locals.push((local, *c)); - } - } - } - } - - eligible_locals -} - -impl Visitor<'_> for LocalUseVisitor { - fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) { - if context.is_mutating_use() { - self.local_mutating_uses[local] = self.local_mutating_uses[local].saturating_add(1); - - if context.is_place_assignment() { - self.local_assignment_locations[local] = Some(location); - } - } - } -} diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index a3e6e5a5a915..bf79b4e133a9 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -68,8 +68,8 @@ use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::CoroutineArgs; -use rustc_middle::ty::InstanceDef; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::InstanceKind; +use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_mir_dataflow::impls::{ MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive, @@ -1276,7 +1276,7 @@ fn create_coroutine_drop_shim<'tcx>( // Update the body's def to become the drop glue. let coroutine_instance = body.source.instance; let drop_in_place = tcx.require_lang_item(LangItem::DropInPlace, None); - let drop_instance = InstanceDef::DropGlue(drop_in_place, Some(coroutine_ty)); + let drop_instance = InstanceKind::DropGlue(drop_in_place, Some(coroutine_ty)); // Temporary change MirSource to coroutine's instance so that dump_mir produces more sensible // filename. @@ -1608,7 +1608,7 @@ fn check_field_tys_sized<'tcx>( let infcx = tcx.infer_ctxt().ignoring_regions().build(); let param_env = tcx.param_env(def_id); - let ocx = ObligationCtxt::new(&infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); for field_ty in &coroutine_layout.field_tys { ocx.register_bound( ObligationCause::new( diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index 10c0567eb4b7..69d21a63f557 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -75,7 +75,7 @@ use rustc_middle::bug; use rustc_middle::hir::place::{Projection, ProjectionKind}; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::{self, dump_mir, MirPass}; -use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, TypeVisitableExt}; use rustc_target::abi::{FieldIdx, VariantIdx}; pub struct ByMoveBody; @@ -102,7 +102,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { // We don't need to generate a by-move coroutine if the coroutine body was // produced by the `CoroutineKindShim`, since it's already by-move. - if matches!(body.source.instance, ty::InstanceDef::CoroutineKindShim { .. }) { + if matches!(body.source.instance, ty::InstanceKind::CoroutineKindShim { .. }) { return; } @@ -193,7 +193,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty }.visit_body(&mut by_move_body); dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(())); // FIXME: use query feeding to generate the body right here and then only store the `DefId` of the new body. - by_move_body.source = mir::MirSource::from_instance(InstanceDef::CoroutineKindShim { + by_move_body.source = mir::MirSource::from_instance(InstanceKind::CoroutineKindShim { coroutine_def_id: coroutine_def_id.to_def_id(), }); body.coroutine.as_mut().unwrap().by_move_body = Some(by_move_body); diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index b98554ec00fa..a8b0f4a8d6df 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -11,7 +11,7 @@ use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, TraverseCoverage /// The coverage counter or counter expression associated with a particular /// BCB node or BCB edge. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] pub(super) enum BcbCounter { Counter { id: CounterId }, Expression { id: ExpressionId }, @@ -35,6 +35,13 @@ impl Debug for BcbCounter { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +struct BcbExpression { + lhs: BcbCounter, + op: Op, + rhs: BcbCounter, +} + #[derive(Debug)] pub(super) enum CounterIncrementSite { Node { bcb: BasicCoverageBlock }, @@ -56,9 +63,13 @@ pub(super) struct CoverageCounters { /// We currently don't iterate over this map, but if we do in the future, /// switch it back to `FxIndexMap` to avoid query stability hazards. bcb_edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), BcbCounter>, + /// Table of expression data, associating each expression ID with its /// corresponding operator (+ or -) and its LHS/RHS operands. - expressions: IndexVec, + expressions: IndexVec, + /// Remember expressions that have already been created (or simplified), + /// so that we don't create unnecessary duplicates. + expressions_memo: FxHashMap, } impl CoverageCounters { @@ -76,6 +87,7 @@ impl CoverageCounters { bcb_counters: IndexVec::from_elem_n(None, num_bcbs), bcb_edge_counters: FxHashMap::default(), expressions: IndexVec::new(), + expressions_memo: FxHashMap::default(), }; MakeBcbCounters::new(&mut this, basic_coverage_blocks) @@ -90,8 +102,57 @@ impl CoverageCounters { } fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter { - let expression = Expression { lhs: lhs.as_term(), op, rhs: rhs.as_term() }; - let id = self.expressions.push(expression); + let new_expr = BcbExpression { lhs, op, rhs }; + *self + .expressions_memo + .entry(new_expr) + .or_insert_with(|| Self::make_expression_inner(&mut self.expressions, new_expr)) + } + + /// This is an associated function so that we can call it while borrowing + /// `&mut self.expressions_memo`. + fn make_expression_inner( + expressions: &mut IndexVec, + new_expr: BcbExpression, + ) -> BcbCounter { + // Simplify expressions using basic algebra. + // + // Some of these cases might not actually occur in practice, depending + // on the details of how the instrumentor builds expressions. + let BcbExpression { lhs, op, rhs } = new_expr; + + if let BcbCounter::Expression { id } = lhs { + let lhs_expr = &expressions[id]; + + // Simplify `(a - b) + b` to `a`. + if lhs_expr.op == Op::Subtract && op == Op::Add && lhs_expr.rhs == rhs { + return lhs_expr.lhs; + } + // Simplify `(a + b) - b` to `a`. + if lhs_expr.op == Op::Add && op == Op::Subtract && lhs_expr.rhs == rhs { + return lhs_expr.lhs; + } + // Simplify `(a + b) - a` to `b`. + if lhs_expr.op == Op::Add && op == Op::Subtract && lhs_expr.lhs == rhs { + return lhs_expr.rhs; + } + } + + if let BcbCounter::Expression { id } = rhs { + let rhs_expr = &expressions[id]; + + // Simplify `a + (b - a)` to `b`. + if op == Op::Add && rhs_expr.op == Op::Subtract && lhs == rhs_expr.rhs { + return rhs_expr.lhs; + } + // Simplify `a - (a - b)` to `b`. + if op == Op::Subtract && rhs_expr.op == Op::Subtract && lhs == rhs_expr.lhs { + return rhs_expr.rhs; + } + } + + // Simplification failed, so actually create the new expression. + let id = expressions.push(new_expr); BcbCounter::Expression { id } } @@ -107,11 +168,6 @@ impl CoverageCounters { self.counter_increment_sites.len() } - #[cfg(test)] - pub(super) fn num_expressions(&self) -> usize { - self.expressions.len() - } - fn set_bcb_counter(&mut self, bcb: BasicCoverageBlock, counter_kind: BcbCounter) -> BcbCounter { if let Some(replaced) = self.bcb_counters[bcb].replace(counter_kind) { bug!( @@ -166,7 +222,21 @@ impl CoverageCounters { } pub(super) fn into_expressions(self) -> IndexVec { - self.expressions + let old_len = self.expressions.len(); + let expressions = self + .expressions + .into_iter() + .map(|BcbExpression { lhs, op, rhs }| Expression { + lhs: lhs.as_term(), + op, + rhs: rhs.as_term(), + }) + .collect::>(); + + // Expression IDs are indexes into this vector, so make sure we didn't + // accidentally invalidate them by changing its length. + assert_eq!(old_len, expressions.len()); + expressions } } diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index fd74a2a97e2c..360dccb240dc 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -14,16 +14,16 @@ use std::ops::{Index, IndexMut}; /// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s /// nodes are `BasicCoverageBlock`s, which encompass one or more MIR `BasicBlock`s. #[derive(Debug)] -pub(super) struct CoverageGraph { +pub(crate) struct CoverageGraph { bcbs: IndexVec, bb_to_bcb: IndexVec>, - pub successors: IndexVec>, - pub predecessors: IndexVec>, + pub(crate) successors: IndexVec>, + pub(crate) predecessors: IndexVec>, dominators: Option>, } impl CoverageGraph { - pub fn from_mir(mir_body: &mir::Body<'_>) -> Self { + pub(crate) fn from_mir(mir_body: &mir::Body<'_>) -> Self { let (bcbs, bb_to_bcb) = Self::compute_basic_coverage_blocks(mir_body); // Pre-transform MIR `BasicBlock` successors and predecessors into the BasicCoverageBlock @@ -135,24 +135,28 @@ impl CoverageGraph { } #[inline(always)] - pub fn iter_enumerated( + pub(crate) fn iter_enumerated( &self, ) -> impl Iterator { self.bcbs.iter_enumerated() } #[inline(always)] - pub fn bcb_from_bb(&self, bb: BasicBlock) -> Option { + pub(crate) fn bcb_from_bb(&self, bb: BasicBlock) -> Option { if bb.index() < self.bb_to_bcb.len() { self.bb_to_bcb[bb] } else { None } } #[inline(always)] - pub fn dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool { + pub(crate) fn dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool { self.dominators.as_ref().unwrap().dominates(dom, node) } #[inline(always)] - pub fn cmp_in_dominator_order(&self, a: BasicCoverageBlock, b: BasicCoverageBlock) -> Ordering { + pub(crate) fn cmp_in_dominator_order( + &self, + a: BasicCoverageBlock, + b: BasicCoverageBlock, + ) -> Ordering { self.dominators.as_ref().unwrap().cmp_in_dominator_order(a, b) } @@ -166,7 +170,7 @@ impl CoverageGraph { /// /// FIXME: That assumption might not be true for [`TerminatorKind::Yield`]? #[inline(always)] - pub(super) fn bcb_has_multiple_in_edges(&self, bcb: BasicCoverageBlock) -> bool { + pub(crate) fn bcb_has_multiple_in_edges(&self, bcb: BasicCoverageBlock) -> bool { // Even though bcb0 conceptually has an extra virtual in-edge due to // being the entry point, we've already asserted that it has no _other_ // in-edges, so there's no possibility of it having _multiple_ in-edges. @@ -212,7 +216,7 @@ impl graph::StartNode for CoverageGraph { impl graph::Successors for CoverageGraph { #[inline] fn successors(&self, node: Self::Node) -> impl Iterator { - self.successors[node].iter().cloned() + self.successors[node].iter().copied() } } @@ -227,7 +231,7 @@ rustc_index::newtype_index! { /// A node in the control-flow graph of CoverageGraph. #[orderable] #[debug_format = "bcb{}"] - pub(super) struct BasicCoverageBlock { + pub(crate) struct BasicCoverageBlock { const START_BCB = 0; } } @@ -259,23 +263,23 @@ rustc_index::newtype_index! { /// queries (`dominates()`, `predecessors`, `successors`, etc.) have branch (control flow) /// significance. #[derive(Debug, Clone)] -pub(super) struct BasicCoverageBlockData { - pub basic_blocks: Vec, +pub(crate) struct BasicCoverageBlockData { + pub(crate) basic_blocks: Vec, } impl BasicCoverageBlockData { - pub fn from(basic_blocks: Vec) -> Self { + fn from(basic_blocks: Vec) -> Self { assert!(basic_blocks.len() > 0); Self { basic_blocks } } #[inline(always)] - pub fn leader_bb(&self) -> BasicBlock { + pub(crate) fn leader_bb(&self) -> BasicBlock { self.basic_blocks[0] } #[inline(always)] - pub fn last_bb(&self) -> BasicBlock { + pub(crate) fn last_bb(&self) -> BasicBlock { *self.basic_blocks.last().unwrap() } } @@ -364,7 +368,7 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera /// CoverageGraph outside all loops. This supports traversing the BCB CFG in a way that /// ensures a loop is completely traversed before processing Blocks after the end of the loop. #[derive(Debug)] -pub(super) struct TraversalContext { +struct TraversalContext { /// BCB with one or more incoming loop backedges, indicating which loop /// this context is for. /// @@ -375,7 +379,7 @@ pub(super) struct TraversalContext { worklist: VecDeque, } -pub(super) struct TraverseCoverageGraphWithLoops<'a> { +pub(crate) struct TraverseCoverageGraphWithLoops<'a> { basic_coverage_blocks: &'a CoverageGraph, backedges: IndexVec>, @@ -384,7 +388,7 @@ pub(super) struct TraverseCoverageGraphWithLoops<'a> { } impl<'a> TraverseCoverageGraphWithLoops<'a> { - pub(super) fn new(basic_coverage_blocks: &'a CoverageGraph) -> Self { + pub(crate) fn new(basic_coverage_blocks: &'a CoverageGraph) -> Self { let backedges = find_loop_backedges(basic_coverage_blocks); let worklist = VecDeque::from([basic_coverage_blocks.start_node()]); @@ -400,7 +404,7 @@ impl<'a> TraverseCoverageGraphWithLoops<'a> { /// For each loop on the loop context stack (top-down), yields a list of BCBs /// within that loop that have an outgoing edge back to the loop header. - pub(super) fn reloop_bcbs_per_loop(&self) -> impl Iterator { + pub(crate) fn reloop_bcbs_per_loop(&self) -> impl Iterator { self.context_stack .iter() .rev() @@ -408,39 +412,38 @@ impl<'a> TraverseCoverageGraphWithLoops<'a> { .map(|header_bcb| self.backedges[header_bcb].as_slice()) } - pub(super) fn next(&mut self) -> Option { + pub(crate) fn next(&mut self) -> Option { debug!( "TraverseCoverageGraphWithLoops::next - context_stack: {:?}", self.context_stack.iter().rev().collect::>() ); while let Some(context) = self.context_stack.last_mut() { - if let Some(bcb) = context.worklist.pop_front() { - if !self.visited.insert(bcb) { - debug!("Already visited: {bcb:?}"); - continue; - } - debug!("Visiting {bcb:?}"); - - if self.backedges[bcb].len() > 0 { - debug!("{bcb:?} is a loop header! Start a new TraversalContext..."); - self.context_stack.push(TraversalContext { - loop_header: Some(bcb), - worklist: VecDeque::new(), - }); - } - self.add_successors_to_worklists(bcb); - return Some(bcb); - } else { - // Strip contexts with empty worklists from the top of the stack + let Some(bcb) = context.worklist.pop_front() else { + // This stack level is exhausted; pop it and try the next one. self.context_stack.pop(); + continue; + }; + + if !self.visited.insert(bcb) { + debug!("Already visited: {bcb:?}"); + continue; } + debug!("Visiting {bcb:?}"); + + if self.backedges[bcb].len() > 0 { + debug!("{bcb:?} is a loop header! Start a new TraversalContext..."); + self.context_stack + .push(TraversalContext { loop_header: Some(bcb), worklist: VecDeque::new() }); + } + self.add_successors_to_worklists(bcb); + return Some(bcb); } None } - pub fn add_successors_to_worklists(&mut self, bcb: BasicCoverageBlock) { + fn add_successors_to_worklists(&mut self, bcb: BasicCoverageBlock) { let successors = &self.basic_coverage_blocks.successors[bcb]; debug!("{:?} has {} successors:", bcb, successors.len()); @@ -494,11 +497,11 @@ impl<'a> TraverseCoverageGraphWithLoops<'a> { } } - pub fn is_complete(&self) -> bool { + pub(crate) fn is_complete(&self) -> bool { self.visited.count() == self.visited.domain_size() } - pub fn unvisited(&self) -> Vec { + pub(crate) fn unvisited(&self) -> Vec { let mut unvisited_set: BitSet = BitSet::new_filled(self.visited.domain_size()); unvisited_set.subtract(&self.visited); @@ -506,7 +509,7 @@ impl<'a> TraverseCoverageGraphWithLoops<'a> { } } -pub(super) fn find_loop_backedges( +fn find_loop_backedges( basic_coverage_blocks: &CoverageGraph, ) -> IndexVec> { let num_bcbs = basic_coverage_blocks.num_nodes(); diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index 61aabea1d8b3..759bb7c1f9d9 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -5,6 +5,7 @@ use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, ConditionInfo, CoverageKind}; use rustc_middle::mir::{self, BasicBlock, StatementKind}; +use rustc_middle::ty::TyCtxt; use rustc_span::Span; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB}; @@ -48,7 +49,7 @@ pub(super) struct MCDCDecision { pub(super) span: Span, pub(super) end_bcbs: BTreeSet, pub(super) bitmap_idx: u32, - pub(super) conditions_num: u16, + pub(super) num_conditions: u16, pub(super) decision_depth: u16, } @@ -63,30 +64,34 @@ pub(super) struct ExtractedMappings { /// Extracts coverage-relevant spans from MIR, and associates them with /// their corresponding BCBs. -pub(super) fn extract_all_mapping_info_from_mir( - mir_body: &mir::Body<'_>, +pub(super) fn extract_all_mapping_info_from_mir<'tcx>( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, hir_info: &ExtractedHirInfo, basic_coverage_blocks: &CoverageGraph, ) -> ExtractedMappings { - if hir_info.is_async_fn { - // An async function desugars into a function that returns a future, - // with the user code wrapped in a closure. Any spans in the desugared - // outer function will be unhelpful, so just keep the signature span - // and ignore all of the spans in the MIR body. - let mut mappings = ExtractedMappings::default(); - if let Some(span) = hir_info.fn_sig_span_extended { - mappings.code_mappings.push(CodeMapping { span, bcb: START_BCB }); - } - return mappings; - } - let mut code_mappings = vec![]; let mut branch_pairs = vec![]; let mut mcdc_bitmap_bytes = 0; let mut mcdc_branches = vec![]; let mut mcdc_decisions = vec![]; - extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut code_mappings); + if hir_info.is_async_fn || tcx.sess.coverage_no_mir_spans() { + // An async function desugars into a function that returns a future, + // with the user code wrapped in a closure. Any spans in the desugared + // outer function will be unhelpful, so just keep the signature span + // and ignore all of the spans in the MIR body. + // + // When debugging flag `-Zcoverage-options=no-mir-spans` is set, we need + // to give the same treatment to _all_ functions, because `llvm-cov` + // seems to ignore functions that don't have any ordinary code spans. + if let Some(span) = hir_info.fn_sig_span_extended { + code_mappings.push(CodeMapping { span, bcb: START_BCB }); + } + } else { + // Extract coverage spans from MIR statements/terminators as normal. + extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut code_mappings); + } branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, basic_coverage_blocks)); @@ -268,13 +273,13 @@ pub(super) fn extract_mcdc_mappings( // the bitmap, rounded up to a whole number of bytes. // The decision's "bitmap index" points to its first byte in the bitmap. let bitmap_idx = *mcdc_bitmap_bytes; - *mcdc_bitmap_bytes += (1_u32 << decision.conditions_num).div_ceil(8); + *mcdc_bitmap_bytes += (1_u32 << decision.num_conditions).div_ceil(8); Some(MCDCDecision { span, end_bcbs, bitmap_idx, - conditions_num: decision.conditions_num as u16, + num_conditions: decision.num_conditions as u16, decision_depth: decision.decision_depth, }) }, diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 28e0c633d5aa..4a64d21f3d17 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -71,8 +71,12 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: //////////////////////////////////////////////////// // Extract coverage spans and other mapping info from MIR. - let extracted_mappings = - mappings::extract_all_mapping_info_from_mir(mir_body, &hir_info, &basic_coverage_blocks); + let extracted_mappings = mappings::extract_all_mapping_info_from_mir( + tcx, + mir_body, + &hir_info, + &basic_coverage_blocks, + ); //////////////////////////////////////////////////// // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure @@ -195,9 +199,9 @@ fn create_mappings<'tcx>( )); mappings.extend(mcdc_decisions.iter().filter_map( - |&mappings::MCDCDecision { span, bitmap_idx, conditions_num, .. }| { + |&mappings::MCDCDecision { span, bitmap_idx, num_conditions, .. }| { let code_region = region_for_span(span)?; - let kind = MappingKind::MCDCDecision(DecisionInfo { bitmap_idx, conditions_num }); + let kind = MappingKind::MCDCDecision(DecisionInfo { bitmap_idx, num_conditions }); Some(Mapping { kind, code_region }) }, )); @@ -269,7 +273,7 @@ fn inject_mcdc_statements<'tcx>( span: _, ref end_bcbs, bitmap_idx, - conditions_num: _, + num_conditions: _, decision_depth, } in &extracted_mappings.mcdc_decisions { diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 65715253647a..25744009be8b 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -49,7 +49,7 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { /// Query implementation for `coverage_ids_info`. fn coverage_ids_info<'tcx>( tcx: TyCtxt<'tcx>, - instance_def: ty::InstanceDef<'tcx>, + instance_def: ty::InstanceKind<'tcx>, ) -> CoverageIdsInfo { let mir_body = tcx.instance_mir(instance_def); diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index f2f76ac70c20..84a70d1f02d7 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -1,10 +1,15 @@ -use rustc_middle::bug; +use std::collections::VecDeque; + +use rustc_data_structures::captures::Captures; +use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir; -use rustc_span::{BytePos, Span}; +use rustc_span::Span; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; use crate::coverage::mappings; -use crate::coverage::spans::from_mir::SpanFromMir; +use crate::coverage::spans::from_mir::{ + extract_covspans_and_holes_from_mir, ExtractedCovspans, Hole, SpanFromMir, +}; use crate::coverage::ExtractedHirInfo; mod from_mir; @@ -20,302 +25,244 @@ pub(super) fn extract_refined_covspans( basic_coverage_blocks: &CoverageGraph, code_mappings: &mut impl Extend, ) { - let sorted_spans = - from_mir::mir_to_initial_sorted_coverage_spans(mir_body, hir_info, basic_coverage_blocks); - let coverage_spans = SpansRefiner::refine_sorted_spans(sorted_spans); - code_mappings.extend(coverage_spans.into_iter().map(|RefinedCovspan { bcb, span, .. }| { - // Each span produced by the generator represents an ordinary code region. - mappings::CodeMapping { span, bcb } - })); -} + let ExtractedCovspans { mut covspans, mut holes } = + extract_covspans_and_holes_from_mir(mir_body, hir_info, basic_coverage_blocks); -#[derive(Debug)] -struct CurrCovspan { - span: Span, - bcb: BasicCoverageBlock, - is_hole: bool, -} + // First, perform the passes that need macro information. + covspans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb)); + remove_unwanted_macro_spans(&mut covspans); + split_visible_macro_spans(&mut covspans); -impl CurrCovspan { - fn new(span: Span, bcb: BasicCoverageBlock, is_hole: bool) -> Self { - Self { span, bcb, is_hole } - } + // We no longer need the extra information in `SpanFromMir`, so convert to `Covspan`. + let mut covspans = covspans.into_iter().map(SpanFromMir::into_covspan).collect::>(); - fn into_prev(self) -> PrevCovspan { - let Self { span, bcb, is_hole } = self; - PrevCovspan { span, bcb, merged_spans: vec![span], is_hole } - } + let compare_covspans = |a: &Covspan, b: &Covspan| { + compare_spans(a.span, b.span) + // After deduplication, we want to keep only the most-dominated BCB. + .then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb).reverse()) + }; + covspans.sort_by(compare_covspans); - fn into_refined(self) -> RefinedCovspan { - // This is only called in cases where `curr` is a hole span that has - // been carved out of `prev`. - debug_assert!(self.is_hole); - self.into_prev().into_refined() - } -} + // Among covspans with the same span, keep only one, + // preferring the one with the most-dominated BCB. + // (Ideally we should try to preserve _all_ non-dominating BCBs, but that + // requires a lot more complexity in the span refiner, for little benefit.) + covspans.dedup_by(|b, a| a.span.source_equal(b.span)); -#[derive(Debug)] -struct PrevCovspan { - span: Span, - bcb: BasicCoverageBlock, - /// List of all the original spans from MIR that have been merged into this - /// span. Mainly used to precisely skip over gaps when truncating a span. - merged_spans: Vec, - is_hole: bool, -} + // Sort the holes, and merge overlapping/adjacent holes. + holes.sort_by(|a, b| compare_spans(a.span, b.span)); + holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b)); -impl PrevCovspan { - fn is_mergeable(&self, other: &CurrCovspan) -> bool { - self.bcb == other.bcb && !self.is_hole && !other.is_hole - } + // Split the covspans into separate buckets that don't overlap any holes. + let buckets = divide_spans_into_buckets(covspans, &holes); - fn merge_from(&mut self, other: &CurrCovspan) { - debug_assert!(self.is_mergeable(other)); - self.span = self.span.to(other.span); - self.merged_spans.push(other.span); - } + for mut covspans in buckets { + // Make sure each individual bucket is internally sorted. + covspans.sort_by(compare_covspans); + let _span = debug_span!("processing bucket", ?covspans).entered(); - fn cutoff_statements_at(mut self, cutoff_pos: BytePos) -> Option { - self.merged_spans.retain(|span| span.hi() <= cutoff_pos); - if let Some(max_hi) = self.merged_spans.iter().map(|span| span.hi()).max() { - self.span = self.span.with_hi(max_hi); - } - - if self.merged_spans.is_empty() { None } else { Some(self.into_refined()) } - } - - fn refined_copy(&self) -> RefinedCovspan { - let &Self { span, bcb, merged_spans: _, is_hole } = self; - RefinedCovspan { span, bcb, is_hole } - } - - fn into_refined(self) -> RefinedCovspan { - // Even though we consume self, we can just reuse the copying impl. - self.refined_copy() - } -} - -#[derive(Debug)] -struct RefinedCovspan { - span: Span, - bcb: BasicCoverageBlock, - is_hole: bool, -} - -impl RefinedCovspan { - fn is_mergeable(&self, other: &Self) -> bool { - self.bcb == other.bcb && !self.is_hole && !other.is_hole - } - - fn merge_from(&mut self, other: &Self) { - debug_assert!(self.is_mergeable(other)); - self.span = self.span.to(other.span); - } -} - -/// Converts the initial set of coverage spans (one per MIR `Statement` or `Terminator`) into a -/// minimal set of coverage spans, using the BCB CFG to determine where it is safe and useful to: -/// -/// * Remove duplicate source code coverage regions -/// * Merge spans that represent continuous (both in source code and control flow), non-branching -/// execution -/// * Carve out (leave uncovered) any "hole" spans that need to be left blank -/// (e.g. closures that will be counted by their own MIR body) -struct SpansRefiner { - /// The initial set of coverage spans, sorted by `Span` (`lo` and `hi`) and by relative - /// dominance between the `BasicCoverageBlock`s of equal `Span`s. - sorted_spans_iter: std::vec::IntoIter, - - /// The current coverage span to compare to its `prev`, to possibly merge, discard, - /// or cause `prev` to be modified or discarded. - /// If `curr` is not discarded or merged, it becomes `prev` for the next iteration. - some_curr: Option, - - /// The coverage span from a prior iteration; typically assigned from that iteration's `curr`. - /// If that `curr` was discarded, `prev` retains its value from the previous iteration. - some_prev: Option, - - /// The final coverage spans to add to the coverage map. A `Counter` or `Expression` - /// will also be injected into the MIR for each BCB that has associated spans. - refined_spans: Vec, -} - -impl SpansRefiner { - /// Takes the initial list of (sorted) spans extracted from MIR, and "refines" - /// them by merging compatible adjacent spans, removing redundant spans, - /// and carving holes in spans when they overlap in unwanted ways. - fn refine_sorted_spans(sorted_spans: Vec) -> Vec { - let sorted_spans_len = sorted_spans.len(); - let this = Self { - sorted_spans_iter: sorted_spans.into_iter(), - some_curr: None, - some_prev: None, - refined_spans: Vec::with_capacity(sorted_spans_len), - }; - - this.to_refined_spans() - } - - /// Iterate through the sorted coverage spans, and return the refined list of merged and - /// de-duplicated spans. - fn to_refined_spans(mut self) -> Vec { - while self.next_coverage_span() { - // For the first span we don't have `prev` set, so most of the - // span-processing steps don't make sense yet. - if self.some_prev.is_none() { - debug!(" initial span"); - continue; - } - - // The remaining cases assume that `prev` and `curr` are set. - let prev = self.prev(); - let curr = self.curr(); - - if prev.is_mergeable(curr) { - debug!(?prev, "curr will be merged into prev"); - let curr = self.take_curr(); - self.prev_mut().merge_from(&curr); - } else if prev.span.hi() <= curr.span.lo() { - debug!( - " different bcbs and disjoint spans, so keep curr for next iter, and add prev={prev:?}", - ); - let prev = self.take_prev().into_refined(); - self.refined_spans.push(prev); - } else if prev.is_hole { - // drop any equal or overlapping span (`curr`) and keep `prev` to test again in the - // next iter - debug!(?prev, "prev (a hole) overlaps curr, so discarding curr"); - self.take_curr(); // Discards curr. - } else if curr.is_hole { - self.carve_out_span_for_hole(); - } else { - self.cutoff_prev_at_overlapping_curr(); - } - } - - // There is usually a final span remaining in `prev` after the loop ends, - // so add it to the output as well. - if let Some(prev) = self.some_prev.take() { - debug!(" AT END, adding last prev={prev:?}"); - self.refined_spans.push(prev.into_refined()); - } + let mut covspans = remove_unwanted_overlapping_spans(covspans); + debug!(?covspans, "after removing overlaps"); // Do one last merge pass, to simplify the output. - self.refined_spans.dedup_by(|b, a| { - if a.is_mergeable(b) { - debug!(?a, ?b, "merging list-adjacent refined spans"); - a.merge_from(b); - true - } else { - false - } - }); + covspans.dedup_by(|b, a| a.merge_if_eligible(b)); + debug!(?covspans, "after merge"); - // Discard hole spans, since their purpose was to carve out chunks from - // other spans, but we don't want the holes themselves in the final mappings. - self.refined_spans.retain(|covspan| !covspan.is_hole); - self.refined_spans - } - - #[track_caller] - fn curr(&self) -> &CurrCovspan { - self.some_curr.as_ref().unwrap_or_else(|| bug!("some_curr is None (curr)")) - } - - /// If called, then the next call to `next_coverage_span()` will *not* update `prev` with the - /// `curr` coverage span. - #[track_caller] - fn take_curr(&mut self) -> CurrCovspan { - self.some_curr.take().unwrap_or_else(|| bug!("some_curr is None (take_curr)")) - } - - #[track_caller] - fn prev(&self) -> &PrevCovspan { - self.some_prev.as_ref().unwrap_or_else(|| bug!("some_prev is None (prev)")) - } - - #[track_caller] - fn prev_mut(&mut self) -> &mut PrevCovspan { - self.some_prev.as_mut().unwrap_or_else(|| bug!("some_prev is None (prev_mut)")) - } - - #[track_caller] - fn take_prev(&mut self) -> PrevCovspan { - self.some_prev.take().unwrap_or_else(|| bug!("some_prev is None (take_prev)")) - } - - /// Advance `prev` to `curr` (if any), and `curr` to the next coverage span in sorted order. - fn next_coverage_span(&mut self) -> bool { - if let Some(curr) = self.some_curr.take() { - self.some_prev = Some(curr.into_prev()); - } - while let Some(curr) = self.sorted_spans_iter.next() { - debug!("FOR curr={:?}", curr); - if let Some(prev) = &self.some_prev - && prev.span.lo() > curr.span.lo() - { - // Skip curr because prev has already advanced beyond the end of curr. - // This can only happen if a prior iteration updated `prev` to skip past - // a region of code, such as skipping past a hole. - debug!(?prev, "prev.span starts after curr.span, so curr will be dropped"); - } else { - self.some_curr = Some(CurrCovspan::new(curr.span, curr.bcb, curr.is_hole)); - return true; - } - } - false - } - - /// If `prev`s span extends left of the hole (`curr`), carve out the hole's span from - /// `prev`'s span. Add the portion of the span to the left of the hole; and if the span - /// extends to the right of the hole, update `prev` to that portion of the span. - fn carve_out_span_for_hole(&mut self) { - let prev = self.prev(); - let curr = self.curr(); - - let left_cutoff = curr.span.lo(); - let right_cutoff = curr.span.hi(); - let has_pre_hole_span = prev.span.lo() < right_cutoff; - let has_post_hole_span = prev.span.hi() > right_cutoff; - - if has_pre_hole_span { - let mut pre_hole = prev.refined_copy(); - pre_hole.span = pre_hole.span.with_hi(left_cutoff); - debug!(?pre_hole, "prev overlaps a hole; adding pre-hole span"); - self.refined_spans.push(pre_hole); - } - - if has_post_hole_span { - // Mutate `prev.span` to start after the hole (and discard curr). - self.prev_mut().span = self.prev().span.with_lo(right_cutoff); - debug!(prev=?self.prev(), "mutated prev to start after the hole"); - - // Prevent this curr from becoming prev. - let hole_covspan = self.take_curr().into_refined(); - self.refined_spans.push(hole_covspan); // since self.prev() was already updated - } - } - - /// `curr` overlaps `prev`. If `prev`s span extends left of `curr`s span, keep _only_ - /// statements that end before `curr.lo()` (if any), and add the portion of the - /// combined span for those statements. Any other statements have overlapping spans - /// that can be ignored because `curr` and/or other upcoming statements/spans inside - /// the overlap area will produce their own counters. This disambiguation process - /// avoids injecting multiple counters for overlapping spans, and the potential for - /// double-counting. - fn cutoff_prev_at_overlapping_curr(&mut self) { - debug!( - " different bcbs, overlapping spans, so ignore/drop pending and only add prev \ - if it has statements that end before curr; prev={:?}", - self.prev() - ); - - let curr_span = self.curr().span; - if let Some(prev) = self.take_prev().cutoff_statements_at(curr_span.lo()) { - debug!("after cutoff, adding {prev:?}"); - self.refined_spans.push(prev); - } else { - debug!("prev was eliminated by cutoff"); - } + code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| { + // Each span produced by the refiner represents an ordinary code region. + mappings::CodeMapping { span, bcb } + })); } } + +/// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate +/// multiple condition/consequent blocks that have the span of the whole macro +/// invocation, which is unhelpful. Keeping only the first such span seems to +/// give better mappings, so remove the others. +/// +/// (The input spans should be sorted in BCB dominator order, so that the +/// retained "first" span is likely to dominate the others.) +fn remove_unwanted_macro_spans(covspans: &mut Vec) { + let mut seen_macro_spans = FxHashSet::default(); + covspans.retain(|covspan| { + // Ignore (retain) non-macro-expansion spans. + if covspan.visible_macro.is_none() { + return true; + } + + // Retain only the first macro-expanded covspan with this span. + seen_macro_spans.insert(covspan.span) + }); +} + +/// When a span corresponds to a macro invocation that is visible from the +/// function body, split it into two parts. The first part covers just the +/// macro name plus `!`, and the second part covers the rest of the macro +/// invocation. This seems to give better results for code that uses macros. +fn split_visible_macro_spans(covspans: &mut Vec) { + let mut extra_spans = vec![]; + + covspans.retain(|covspan| { + let Some(visible_macro) = covspan.visible_macro else { return true }; + + let split_len = visible_macro.as_str().len() as u32 + 1; + let (before, after) = covspan.span.split_at(split_len); + if !covspan.span.contains(before) || !covspan.span.contains(after) { + // Something is unexpectedly wrong with the split point. + // The debug assertion in `split_at` will have already caught this, + // but in release builds it's safer to do nothing and maybe get a + // bug report for unexpected coverage, rather than risk an ICE. + return true; + } + + extra_spans.push(SpanFromMir::new(before, covspan.visible_macro, covspan.bcb)); + extra_spans.push(SpanFromMir::new(after, covspan.visible_macro, covspan.bcb)); + false // Discard the original covspan that we just split. + }); + + // The newly-split spans are added at the end, so any previous sorting + // is not preserved. + covspans.extend(extra_spans); +} + +/// Uses the holes to divide the given covspans into buckets, such that: +/// - No span in any hole overlaps a bucket (truncating the spans if necessary). +/// - The spans in each bucket are strictly after all spans in previous buckets, +/// and strictly before all spans in subsequent buckets. +/// +/// The resulting buckets are sorted relative to each other, but might not be +/// internally sorted. +#[instrument(level = "debug")] +fn divide_spans_into_buckets(input_covspans: Vec, holes: &[Hole]) -> Vec> { + debug_assert!(input_covspans.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le())); + debug_assert!(holes.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le())); + + // Now we're ready to start carving holes out of the initial coverage spans, + // and grouping them in buckets separated by the holes. + + let mut input_covspans = VecDeque::from(input_covspans); + let mut fragments = vec![]; + + // For each hole: + // - Identify the spans that are entirely or partly before the hole. + // - Put those spans in a corresponding bucket, truncated to the start of the hole. + // - If one of those spans also extends after the hole, put the rest of it + // in a "fragments" vector that is processed by the next hole. + let mut buckets = (0..holes.len()).map(|_| vec![]).collect::>(); + for (hole, bucket) in holes.iter().zip(&mut buckets) { + let fragments_from_prev = std::mem::take(&mut fragments); + + // Only inspect spans that precede or overlap this hole, + // leaving the rest to be inspected by later holes. + // (This relies on the spans and holes both being sorted.) + let relevant_input_covspans = + drain_front_while(&mut input_covspans, |c| c.span.lo() < hole.span.hi()); + + for covspan in fragments_from_prev.into_iter().chain(relevant_input_covspans) { + let (before, after) = covspan.split_around_hole_span(hole.span); + bucket.extend(before); + fragments.extend(after); + } + } + + // After finding the spans before each hole, any remaining fragments/spans + // form their own final bucket, after the final hole. + // (If there were no holes, this will just be all of the initial spans.) + fragments.extend(input_covspans); + buckets.push(fragments); + + buckets +} + +/// Similar to `.drain(..)`, but stops just before it would remove an item not +/// satisfying the predicate. +fn drain_front_while<'a, T>( + queue: &'a mut VecDeque, + mut pred_fn: impl FnMut(&T) -> bool, +) -> impl Iterator + Captures<'a> { + std::iter::from_fn(move || if pred_fn(queue.front()?) { queue.pop_front() } else { None }) +} + +/// Takes one of the buckets of (sorted) spans extracted from MIR, and "refines" +/// those spans by removing spans that overlap in unwanted ways. +#[instrument(level = "debug")] +fn remove_unwanted_overlapping_spans(sorted_spans: Vec) -> Vec { + debug_assert!(sorted_spans.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le())); + + // Holds spans that have been read from the input vector, but haven't yet + // been committed to the output vector. + let mut pending = vec![]; + let mut refined = vec![]; + + for curr in sorted_spans { + pending.retain(|prev: &Covspan| { + if prev.span.hi() <= curr.span.lo() { + // There's no overlap between the previous/current covspans, + // so move the previous one into the refined list. + refined.push(prev.clone()); + false + } else { + // Otherwise, retain the previous covspan only if it has the + // same BCB. This tends to discard long outer spans that enclose + // smaller inner spans with different control flow. + prev.bcb == curr.bcb + } + }); + pending.push(curr); + } + + // Drain the rest of the pending list into the refined list. + refined.extend(pending); + refined +} + +#[derive(Clone, Debug)] +struct Covspan { + span: Span, + bcb: BasicCoverageBlock, +} + +impl Covspan { + /// Splits this covspan into 0-2 parts: + /// - The part that is strictly before the hole span, if any. + /// - The part that is strictly after the hole span, if any. + fn split_around_hole_span(&self, hole_span: Span) -> (Option, Option) { + let before = try { + let span = self.span.trim_end(hole_span)?; + Self { span, ..*self } + }; + let after = try { + let span = self.span.trim_start(hole_span)?; + Self { span, ..*self } + }; + + (before, after) + } + + /// If `self` and `other` can be merged (i.e. they have the same BCB), + /// mutates `self.span` to also include `other.span` and returns true. + /// + /// Note that compatible covspans can be merged even if their underlying + /// spans are not overlapping/adjacent; any space between them will also be + /// part of the merged covspan. + fn merge_if_eligible(&mut self, other: &Self) -> bool { + if self.bcb != other.bcb { + return false; + } + + self.span = self.span.to(other.span); + true + } +} + +/// Compares two spans in (lo ascending, hi descending) order. +fn compare_spans(a: Span, b: Span) -> std::cmp::Ordering { + // First sort by span start. + Ord::cmp(&a.lo(), &b.lo()) + // If span starts are the same, sort by span end in reverse order. + // This ensures that if spans A and B are adjacent in the list, + // and they overlap but are not equal, then either: + // - Span A extends further left, or + // - Both have the same start and span A extends further right + .then_with(|| Ord::cmp(&a.hi(), &b.hi()).reverse()) +} diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index d1727a94a35e..09deb7534bfd 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -1,5 +1,3 @@ -use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::FxHashSet; use rustc_middle::bug; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::{ @@ -11,118 +9,48 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol}; use crate::coverage::graph::{ BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB, }; +use crate::coverage::spans::Covspan; use crate::coverage::ExtractedHirInfo; +pub(crate) struct ExtractedCovspans { + pub(crate) covspans: Vec, + pub(crate) holes: Vec, +} + /// Traverses the MIR body to produce an initial collection of coverage-relevant /// spans, each associated with a node in the coverage graph (BCB) and possibly /// other metadata. -/// -/// The returned spans are sorted in a specific order that is expected by the -/// subsequent span-refinement step. -pub(super) fn mir_to_initial_sorted_coverage_spans( +pub(crate) fn extract_covspans_and_holes_from_mir( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, basic_coverage_blocks: &CoverageGraph, -) -> Vec { +) -> ExtractedCovspans { let &ExtractedHirInfo { body_span, .. } = hir_info; - let mut initial_spans = vec![]; + let mut covspans = vec![]; + let mut holes = vec![]; for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() { - initial_spans.extend(bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data)); + bcb_to_initial_coverage_spans( + mir_body, + body_span, + bcb, + bcb_data, + &mut covspans, + &mut holes, + ); } // Only add the signature span if we found at least one span in the body. - if !initial_spans.is_empty() { + if !covspans.is_empty() || !holes.is_empty() { // If there is no usable signature span, add a fake one (before refinement) // to avoid an ugly gap between the body start and the first real span. // FIXME: Find a more principled way to solve this problem. let fn_sig_span = hir_info.fn_sig_span_extended.unwrap_or_else(|| body_span.shrink_to_lo()); - initial_spans.push(SpanFromMir::for_fn_sig(fn_sig_span)); + covspans.push(SpanFromMir::for_fn_sig(fn_sig_span)); } - initial_spans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb)); - remove_unwanted_macro_spans(&mut initial_spans); - split_visible_macro_spans(&mut initial_spans); - - initial_spans.sort_by(|a, b| { - // First sort by span start. - Ord::cmp(&a.span.lo(), &b.span.lo()) - // If span starts are the same, sort by span end in reverse order. - // This ensures that if spans A and B are adjacent in the list, - // and they overlap but are not equal, then either: - // - Span A extends further left, or - // - Both have the same start and span A extends further right - .then_with(|| Ord::cmp(&a.span.hi(), &b.span.hi()).reverse()) - // If two spans have the same lo & hi, put hole spans first, - // as they take precedence over non-hole spans. - .then_with(|| Ord::cmp(&a.is_hole, &b.is_hole).reverse()) - // After deduplication, we want to keep only the most-dominated BCB. - .then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb).reverse()) - }); - - // Among covspans with the same span, keep only one. Hole spans take - // precedence, otherwise keep the one with the most-dominated BCB. - // (Ideally we should try to preserve _all_ non-dominating BCBs, but that - // requires a lot more complexity in the span refiner, for little benefit.) - initial_spans.dedup_by(|b, a| a.span.source_equal(b.span)); - - initial_spans -} - -/// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate -/// multiple condition/consequent blocks that have the span of the whole macro -/// invocation, which is unhelpful. Keeping only the first such span seems to -/// give better mappings, so remove the others. -/// -/// (The input spans should be sorted in BCB dominator order, so that the -/// retained "first" span is likely to dominate the others.) -fn remove_unwanted_macro_spans(initial_spans: &mut Vec) { - let mut seen_macro_spans = FxHashSet::default(); - initial_spans.retain(|covspan| { - // Ignore (retain) hole spans and non-macro-expansion spans. - if covspan.is_hole || covspan.visible_macro.is_none() { - return true; - } - - // Retain only the first macro-expanded covspan with this span. - seen_macro_spans.insert(covspan.span) - }); -} - -/// When a span corresponds to a macro invocation that is visible from the -/// function body, split it into two parts. The first part covers just the -/// macro name plus `!`, and the second part covers the rest of the macro -/// invocation. This seems to give better results for code that uses macros. -fn split_visible_macro_spans(initial_spans: &mut Vec) { - let mut extra_spans = vec![]; - - initial_spans.retain(|covspan| { - if covspan.is_hole { - return true; - } - - let Some(visible_macro) = covspan.visible_macro else { return true }; - - let split_len = visible_macro.as_str().len() as u32 + 1; - let (before, after) = covspan.span.split_at(split_len); - if !covspan.span.contains(before) || !covspan.span.contains(after) { - // Something is unexpectedly wrong with the split point. - // The debug assertion in `split_at` will have already caught this, - // but in release builds it's safer to do nothing and maybe get a - // bug report for unexpected coverage, rather than risk an ICE. - return true; - } - - assert!(!covspan.is_hole); - extra_spans.push(SpanFromMir::new(before, covspan.visible_macro, covspan.bcb, false)); - extra_spans.push(SpanFromMir::new(after, covspan.visible_macro, covspan.bcb, false)); - false // Discard the original covspan that we just split. - }); - - // The newly-split spans are added at the end, so any previous sorting - // is not preserved. - initial_spans.extend(extra_spans); + ExtractedCovspans { covspans, holes } } // Generate a set of coverage spans from the filtered set of `Statement`s and `Terminator`s of @@ -135,8 +63,10 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>( body_span: Span, bcb: BasicCoverageBlock, bcb_data: &'a BasicCoverageBlockData, -) -> impl Iterator + Captures<'a> + Captures<'tcx> { - bcb_data.basic_blocks.iter().flat_map(move |&bb| { + initial_covspans: &mut Vec, + holes: &mut Vec, +) { + for &bb in &bcb_data.basic_blocks { let data = &mir_body[bb]; let unexpand = move |expn_span| { @@ -146,24 +76,32 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>( .filter(|(span, _)| !span.source_equal(body_span)) }; - let statement_spans = data.statements.iter().filter_map(move |statement| { + let mut extract_statement_span = |statement| { let expn_span = filtered_statement_span(statement)?; let (span, visible_macro) = unexpand(expn_span)?; // A statement that looks like the assignment of a closure expression // is treated as a "hole" span, to be carved out of other spans. - Some(SpanFromMir::new(span, visible_macro, bcb, is_closure_like(statement))) - }); + if is_closure_like(statement) { + holes.push(Hole { span }); + } else { + initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb)); + } + Some(()) + }; + for statement in data.statements.iter() { + extract_statement_span(statement); + } - let terminator_span = Some(data.terminator()).into_iter().filter_map(move |terminator| { + let mut extract_terminator_span = |terminator| { let expn_span = filtered_terminator_span(terminator)?; let (span, visible_macro) = unexpand(expn_span)?; - Some(SpanFromMir::new(span, visible_macro, bcb, false)) - }); - - statement_spans.chain(terminator_span) - }) + initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb)); + Some(()) + }; + extract_terminator_span(data.terminator()); + } } fn is_closure_like(statement: &Statement<'_>) -> bool { @@ -331,7 +269,23 @@ fn unexpand_into_body_span_with_prev( } #[derive(Debug)] -pub(super) struct SpanFromMir { +pub(crate) struct Hole { + pub(crate) span: Span, +} + +impl Hole { + pub(crate) fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool { + if !self.span.overlaps_or_adjacent(other.span) { + return false; + } + + self.span = self.span.to(other.span); + true + } +} + +#[derive(Debug)] +pub(crate) struct SpanFromMir { /// A span that has been extracted from MIR and then "un-expanded" back to /// within the current function's `body_span`. After various intermediate /// processing steps, this span is emitted as part of the final coverage @@ -339,26 +293,22 @@ pub(super) struct SpanFromMir { /// /// With the exception of `fn_sig_span`, this should always be contained /// within `body_span`. - pub(super) span: Span, - visible_macro: Option, - pub(super) bcb: BasicCoverageBlock, - /// If true, this covspan represents a "hole" that should be carved out - /// from other spans, e.g. because it represents a closure expression that - /// will be instrumented separately as its own function. - pub(super) is_hole: bool, + pub(crate) span: Span, + pub(crate) visible_macro: Option, + pub(crate) bcb: BasicCoverageBlock, } impl SpanFromMir { fn for_fn_sig(fn_sig_span: Span) -> Self { - Self::new(fn_sig_span, None, START_BCB, false) + Self::new(fn_sig_span, None, START_BCB) } - fn new( - span: Span, - visible_macro: Option, - bcb: BasicCoverageBlock, - is_hole: bool, - ) -> Self { - Self { span, visible_macro, bcb, is_hole } + pub(crate) fn new(span: Span, visible_macro: Option, bcb: BasicCoverageBlock) -> Self { + Self { span, visible_macro, bcb } + } + + pub(crate) fn into_covspan(self) -> Covspan { + let Self { span, visible_macro: _, bcb } = self; + Covspan { span, bcb } } } diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index ca64688e6b87..048547dc9f54 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -24,7 +24,6 @@ //! globals is comparatively simpler. The easiest way is to wrap the test in a closure argument //! to: `rustc_span::create_default_session_globals_then(|| { test_here(); })`. -use super::counters; use super::graph::{self, BasicCoverageBlock}; use itertools::Itertools; @@ -551,108 +550,3 @@ fn test_covgraph_switchint_loop_then_inner_loop_else_break() { assert_successors(&basic_coverage_blocks, bcb(5), &[bcb(1)]); assert_successors(&basic_coverage_blocks, bcb(6), &[bcb(4)]); } - -#[test] -fn test_find_loop_backedges_none() { - let mir_body = goto_switchint(); - let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); - if false { - eprintln!( - "basic_coverage_blocks = {:?}", - basic_coverage_blocks.iter_enumerated().collect::>() - ); - eprintln!("successors = {:?}", basic_coverage_blocks.successors); - } - let backedges = graph::find_loop_backedges(&basic_coverage_blocks); - assert_eq!( - backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), - 0, - "backedges: {:?}", - backedges - ); -} - -#[test] -fn test_find_loop_backedges_one() { - let mir_body = switchint_then_loop_else_return(); - let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); - let backedges = graph::find_loop_backedges(&basic_coverage_blocks); - assert_eq!( - backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), - 1, - "backedges: {:?}", - backedges - ); - - assert_eq!(backedges[bcb(1)], &[bcb(3)]); -} - -#[test] -fn test_find_loop_backedges_two() { - let mir_body = switchint_loop_then_inner_loop_else_break(); - let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); - let backedges = graph::find_loop_backedges(&basic_coverage_blocks); - assert_eq!( - backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), - 2, - "backedges: {:?}", - backedges - ); - - assert_eq!(backedges[bcb(1)], &[bcb(5)]); - assert_eq!(backedges[bcb(4)], &[bcb(6)]); -} - -#[test] -fn test_traverse_coverage_with_loops() { - let mir_body = switchint_loop_then_inner_loop_else_break(); - let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); - let mut traversed_in_order = Vec::new(); - let mut traversal = graph::TraverseCoverageGraphWithLoops::new(&basic_coverage_blocks); - while let Some(bcb) = traversal.next() { - traversed_in_order.push(bcb); - } - - // bcb0 is visited first. Then bcb1 starts the first loop, and all remaining nodes, *except* - // bcb6 are inside the first loop. - assert_eq!( - *traversed_in_order.last().expect("should have elements"), - bcb(6), - "bcb6 should not be visited until all nodes inside the first loop have been visited" - ); -} - -#[test] -fn test_make_bcb_counters() { - rustc_span::create_default_session_globals_then(|| { - let mir_body = goto_switchint(); - let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); - // Historically this test would use `spans` internals to set up fake - // coverage spans for BCBs 1 and 2. Now we skip that step and just tell - // BCB counter construction that those BCBs have spans. - let bcb_has_coverage_spans = |bcb: BasicCoverageBlock| (1..=2).contains(&bcb.as_usize()); - let coverage_counters = counters::CoverageCounters::make_bcb_counters( - &basic_coverage_blocks, - bcb_has_coverage_spans, - ); - assert_eq!(coverage_counters.num_expressions(), 0); - - assert_eq!( - 0, // bcb1 has a `Counter` with id = 0 - match coverage_counters.bcb_counter(bcb(1)).expect("should have a counter") { - counters::BcbCounter::Counter { id, .. } => id, - _ => panic!("expected a Counter"), - } - .as_u32() - ); - - assert_eq!( - 1, // bcb2 has a `Counter` with id = 1 - match coverage_counters.bcb_counter(bcb(2)).expect("should have a counter") { - counters::BcbCounter::Counter { id, .. } => id, - _ => panic!("expected a Counter"), - } - .as_u32() - ); - }); -} diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index a42d64f86be1..0fd85eb345d0 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -10,7 +10,7 @@ use rustc_middle::bug; use rustc_middle::mir::interpret::{InterpResult, Scalar}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; -use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::layout::{HasParamEnv, LayoutOf}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::value_analysis::{ Map, PlaceIndex, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace, @@ -69,7 +69,7 @@ struct ConstAnalysis<'a, 'tcx> { map: Map, tcx: TyCtxt<'tcx>, local_decls: &'a LocalDecls<'tcx>, - ecx: InterpCx<'tcx, 'tcx, DummyMachine>, + ecx: InterpCx<'tcx, DummyMachine>, param_env: ty::ParamEnv<'tcx>, } @@ -142,11 +142,10 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { _ => return, }; if let Some(variant_target_idx) = variant_target { - for (field_index, operand) in operands.iter().enumerate() { - if let Some(field) = self.map().apply( - variant_target_idx, - TrackElem::Field(FieldIdx::from_usize(field_index)), - ) { + for (field_index, operand) in operands.iter_enumerated() { + if let Some(field) = + self.map().apply(variant_target_idx, TrackElem::Field(field_index)) + { self.assign_operand(state, field, operand); } } @@ -165,7 +164,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { } } } - Rvalue::CheckedBinaryOp(op, box (left, right)) => { + Rvalue::BinaryOp(op, box (left, right)) if op.is_overflowing() => { // Flood everything now, so we can use `insert_value_idx` directly later. state.flood(target.as_ref(), self.map()); @@ -184,7 +183,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { if let Some(overflow_target) = overflow_target { let overflow = match overflow { FlatSet::Top => FlatSet::Top, - FlatSet::Elem(overflow) => FlatSet::Elem(Scalar::from_bool(overflow)), + FlatSet::Elem(overflow) => FlatSet::Elem(overflow), FlatSet::Bottom => FlatSet::Bottom, }; // We have flooded `target` earlier. @@ -204,7 +203,8 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { && let operand_ty = operand.ty(self.local_decls, self.tcx) && let Some(operand_ty) = operand_ty.builtin_deref(true) && let ty::Array(_, len) = operand_ty.kind() - && let Some(len) = Const::Ty(*len).try_eval_scalar_int(self.tcx, self.param_env) + && let Some(len) = Const::Ty(self.tcx.types.usize, *len) + .try_eval_scalar_int(self.tcx, self.param_env) { state.insert_value_idx(target_len, FlatSet::Elem(len.into()), self.map()); } @@ -222,7 +222,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { Rvalue::Len(place) => { let place_ty = place.ty(self.local_decls, self.tcx); if let ty::Array(_, len) = place_ty.ty.kind() { - Const::Ty(*len) + Const::Ty(self.tcx.types.usize, *len) .try_eval_scalar(self.tcx, self.param_env) .map_or(FlatSet::Top, FlatSet::Elem) } else if let [ProjectionElem::Deref] = place.projection[..] { @@ -264,15 +264,16 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { FlatSet::Top => FlatSet::Top, } } - Rvalue::BinaryOp(op, box (left, right)) => { + Rvalue::BinaryOp(op, box (left, right)) if !op.is_overflowing() => { // Overflows must be ignored here. + // The overflowing operators are handled in `handle_assign`. let (val, _overflow) = self.binary_op(state, *op, left, right); val } Rvalue::UnaryOp(op, operand) => match self.eval_operand(operand, state) { FlatSet::Elem(value) => self .ecx - .wrapping_unary_op(*op, &value) + .unary_op(*op, &value) .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)), FlatSet::Bottom => FlatSet::Bottom, FlatSet::Top => FlatSet::Top, @@ -284,9 +285,11 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { let val = match null_op { NullOp::SizeOf if layout.is_sized() => layout.size.bytes(), NullOp::AlignOf if layout.is_sized() => layout.align.abi.bytes(), - NullOp::OffsetOf(fields) => { - layout.offset_of_subfield(&self.ecx, fields.iter()).bytes() - } + NullOp::OffsetOf(fields) => self + .ecx + .tcx + .offset_of_subfield(self.ecx.param_env(), layout, fields.iter()) + .bytes(), _ => return ValueOrPlace::Value(FlatSet::Top), }; FlatSet::Elem(Scalar::from_target_usize(val, &self.tcx)) @@ -323,7 +326,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> { // This allows the set of visited edges to grow monotonically with the lattice. FlatSet::Bottom => TerminatorEdges::None, FlatSet::Elem(scalar) => { - let choice = scalar.assert_bits(scalar.size()); + let choice = scalar.assert_scalar_int().to_bits_unchecked(); TerminatorEdges::Single(targets.target_for_value(choice)) } FlatSet::Top => TerminatorEdges::SwitchInt { discr, targets }, @@ -437,7 +440,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { op: BinOp, left: &Operand<'tcx>, right: &Operand<'tcx>, - ) -> (FlatSet, FlatSet) { + ) -> (FlatSet, FlatSet) { let left = self.eval_operand(left, state); let right = self.eval_operand(right, state); @@ -445,9 +448,17 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { (FlatSet::Bottom, _) | (_, FlatSet::Bottom) => (FlatSet::Bottom, FlatSet::Bottom), // Both sides are known, do the actual computation. (FlatSet::Elem(left), FlatSet::Elem(right)) => { - match self.ecx.overflowing_binary_op(op, &left, &right) { - Ok((val, overflow)) => { - (FlatSet::Elem(val.to_scalar()), FlatSet::Elem(overflow)) + match self.ecx.binary_op(op, &left, &right) { + // Ideally this would return an Immediate, since it's sometimes + // a pair and sometimes not. But as a hack we always return a pair + // and just make the 2nd component `Bottom` when it does not exist. + Ok(val) => { + if matches!(val.layout.abi, Abi::ScalarPair(..)) { + let (val, overflow) = val.to_scalar_pair(); + (FlatSet::Elem(val), FlatSet::Elem(overflow)) + } else { + (FlatSet::Elem(val.to_scalar()), FlatSet::Bottom) + } } _ => (FlatSet::Top, FlatSet::Top), } @@ -473,7 +484,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { (FlatSet::Elem(arg_scalar), FlatSet::Bottom) } BinOp::Mul if layout.ty.is_integral() && arg_value == 0 => { - (FlatSet::Elem(arg_scalar), FlatSet::Elem(false)) + (FlatSet::Elem(arg_scalar), FlatSet::Elem(Scalar::from_bool(false))) } _ => (FlatSet::Top, FlatSet::Top), } @@ -556,7 +567,7 @@ impl<'tcx, 'locals> Collector<'tcx, 'locals> { fn try_make_constant( &self, - ecx: &mut InterpCx<'tcx, 'tcx, DummyMachine>, + ecx: &mut InterpCx<'tcx, DummyMachine>, place: Place<'tcx>, state: &State>, map: &Map, @@ -598,7 +609,7 @@ fn propagatable_scalar( map: &Map, ) -> Option { if let FlatSet::Elem(value) = state.get_idx(place, map) - && value.try_to_int().is_ok() + && value.try_to_scalar_int().is_ok() { // Do not attempt to propagate pointers, as we may fail to preserve their identity. Some(value) @@ -609,7 +620,7 @@ fn propagatable_scalar( #[instrument(level = "trace", skip(ecx, state, map))] fn try_write_constant<'tcx>( - ecx: &mut InterpCx<'_, 'tcx, DummyMachine>, + ecx: &mut InterpCx<'tcx, DummyMachine>, dest: &PlaceTy<'tcx>, place: PlaceIndex, ty: Ty<'tcx>, @@ -659,7 +670,7 @@ fn try_write_constant<'tcx>( let FlatSet::Elem(Scalar::Int(discr)) = state.get_idx(discr, map) else { throw_machine_stop_str!("discriminant with provenance") }; - let discr_bits = discr.assert_bits(discr.size()); + let discr_bits = discr.to_bits(discr.size()); let Some((variant, _)) = def.discriminants(*ecx.tcx).find(|(_, var)| discr_bits == var.val) else { throw_machine_stop_str!("illegal discriminant for enum") }; @@ -827,7 +838,7 @@ impl<'tcx> MutVisitor<'tcx> for Patch<'tcx> { struct OperandCollector<'tcx, 'map, 'locals, 'a> { state: &'a State>, visitor: &'a mut Collector<'tcx, 'locals>, - ecx: &'map mut InterpCx<'tcx, 'tcx, DummyMachine>, + ecx: &'map mut InterpCx<'tcx, DummyMachine>, map: &'map Map, } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 1bc383fccc72..b1016c0867c6 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -564,7 +564,7 @@ impl WriteInfo { | Rvalue::ShallowInitBox(op, _) => { self.add_operand(op); } - Rvalue::BinaryOp(_, ops) | Rvalue::CheckedBinaryOp(_, ops) => { + Rvalue::BinaryOp(_, ops) => { for op in [&ops.0, &ops.1] { self.add_operand(op); } diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs index 13841be494cf..3b71cf02c1a0 100644 --- a/compiler/rustc_mir_transform/src/dump_mir.rs +++ b/compiler/rustc_mir_transform/src/dump_mir.rs @@ -28,6 +28,9 @@ pub fn emit_mir(tcx: TyCtxt<'_>) -> io::Result<()> { OutFileName::Real(path) => { let mut f = io::BufWriter::new(File::create(&path)?); write_mir_pretty(tcx, None, &mut f)?; + if tcx.sess.opts.json_artifact_notifications { + tcx.dcx().emit_artifact_notification(&path, "mir"); + } } } Ok(()) diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 03d952abad11..665b2260294a 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -34,14 +34,14 @@ use std::fmt; /// /// ```text // fn drop_term(t: &mut T) { -// mir!( +// mir! { // { // Drop(*t, exit) // } // exit = { // Return() // } -// ) +// } // } /// ``` pub struct ElaborateDrops; diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 0634e321ea30..dc7648d27b56 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -1,4 +1,4 @@ -use rustc_errors::{codes::*, Diag, DiagMessage, LintDiagnostic}; +use rustc_errors::{codes::*, Diag, LintDiagnostic}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::mir::AssertKind; use rustc_middle::ty::TyCtxt; @@ -50,18 +50,15 @@ pub(crate) enum AssertLintKind { impl<'a, P: std::fmt::Debug> LintDiagnostic<'a, ()> for AssertLint

::Metadata; } +/// Lowers in MIR to `Rvalue::UnaryOp` with `UnOp::PtrMetadata`. +/// +/// This is used to implement functions like `ptr::metadata`. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub const fn ptr_metadata + ?Sized, M>(_ptr: *const P) -> M { + // To implement a fallback we'd have to assume the layout of the pointer, + // but the whole point of this intrinsic is that we shouldn't do that. + unreachable!() +} + // Some functions are defined here because they accidentally got made // available in this module on stable. See . // (`transmute` also falls into this category, but it cannot be wrapped due to the @@ -3030,8 +3043,7 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { unsafe { ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ - and the specified memory ranges do not overlap", + "ptr::copy requires that both pointer arguments are aligned and non-null", ( src: *const () = src as *const (), dst: *mut () = dst as *mut (), diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index 02665b2676cc..ec8488009b96 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -20,7 +20,7 @@ //! //! #[custom_mir(dialect = "built")] //! pub fn simple(x: i32) -> i32 { -//! mir!( +//! mir! { //! let temp2: i32; //! //! { @@ -33,7 +33,7 @@ //! RET = temp2; //! Return() //! } -//! ) +//! } //! } //! ``` //! @@ -71,7 +71,7 @@ //! //! #[custom_mir(dialect = "built")] //! pub fn choose_load(a: &i32, b: &i32, c: bool) -> i32 { -//! mir!( +//! mir! { //! { //! match c { //! true => t, @@ -93,20 +93,22 @@ //! RET = *temp; //! Return() //! } -//! ) +//! } //! } //! //! #[custom_mir(dialect = "built")] //! fn unwrap_unchecked(opt: Option) -> T { -//! mir!({ -//! RET = Move(Field(Variant(opt, 1), 0)); -//! Return() -//! }) +//! mir! { +//! { +//! RET = Move(Field(Variant(opt, 1), 0)); +//! Return() +//! } +//! } //! } //! //! #[custom_mir(dialect = "runtime", phase = "optimized")] //! fn push_and_pop(v: &mut Vec, value: T) { -//! mir!( +//! mir! { //! let _unused; //! let popped; //! @@ -125,19 +127,19 @@ //! ret = { //! Return() //! } -//! ) +//! } //! } //! //! #[custom_mir(dialect = "runtime", phase = "optimized")] //! fn annotated_return_type() -> (i32, bool) { -//! mir!( +//! mir! { //! type RET = (i32, bool); //! { //! RET.0 = 1; //! RET.1 = true; //! Return() //! } -//! ) +//! } //! } //! ``` //! @@ -152,7 +154,7 @@ //! //! #[custom_mir(dialect = "built")] //! fn borrow_error(should_init: bool) -> i32 { -//! mir!( +//! mir! { //! let temp: i32; //! //! { @@ -171,7 +173,7 @@ //! RET = temp; //! Return() //! } -//! ) +//! } //! } //! ``` //! @@ -179,7 +181,7 @@ //! error[E0381]: used binding is possibly-uninitialized //! --> test.rs:24:13 //! | -//! 8 | / mir!( +//! 8 | / mir! { //! 9 | | let temp: i32; //! 10 | | //! 11 | | { @@ -191,7 +193,7 @@ //! | | ^^^^^^^^^^ value used here but it is possibly-uninitialized //! 25 | | Return() //! 26 | | } -//! 27 | | ) +//! 27 | | } //! | |_____- binding declared here but left uninitialized //! //! error: aborting due to 1 previous error @@ -360,6 +362,10 @@ define!("mir_assume", fn Assume(operand: bool)); define!("mir_deinit", fn Deinit(place: T)); define!("mir_checked", fn Checked(binop: T) -> (T, bool)); define!("mir_len", fn Len(place: T) -> usize); +define!( + "mir_ptr_metadata", + fn PtrMetadata(place: *const P) ->

::Metadata +); define!("mir_copy_for_deref", fn CopyForDeref(place: T) -> T); define!("mir_retag", fn Retag(place: T)); define!("mir_move", fn Move(place: T) -> T); @@ -403,18 +409,22 @@ define!( /// /// #[custom_mir(dialect = "built")] /// fn unwrap_deref(opt: Option<&i32>) -> i32 { - /// mir!({ - /// RET = *Field::<&i32>(Variant(opt, 1), 0); - /// Return() - /// }) + /// mir! { + /// { + /// RET = *Field::<&i32>(Variant(opt, 1), 0); + /// Return() + /// } + /// } /// } /// /// #[custom_mir(dialect = "built")] /// fn set(opt: &mut Option) { - /// mir!({ - /// place!(Field(Variant(*opt, 1), 0)) = 5; - /// Return() - /// }) + /// mir! { + /// { + /// place!(Field(Variant(*opt, 1), 0)) = 5; + /// Return() + /// } + /// } /// } /// ``` fn Field(place: (), field: u32) -> F @@ -451,7 +461,7 @@ define!( /// your MIR into something that is easier to parse in the compiler. #[rustc_macro_transparency = "transparent"] pub macro mir { - ( + { $(type RET = $ret_ty:ty ;)? $(let $local_decl:ident $(: $local_decl_ty:ty)? ;)* $(debug $dbg_name:ident => $dbg_data:expr ;)* @@ -465,7 +475,7 @@ pub macro mir { $($block:tt)* } )* - ) => {{ + } => {{ // First, we declare all basic blocks. __internal_declare_basic_blocks!($( $block_name $(($block_cleanup))? diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index ceea67901294..4be5e62ea5bc 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -152,7 +152,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_fabs(x: T) -> T; - /// Elementwise minimum of a vector. + /// Elementwise minimum of two vectors. /// /// `T` must be a vector of floating-point primitive types. /// @@ -160,7 +160,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_fmin(x: T, y: T) -> T; - /// Elementwise maximum of a vector. + /// Elementwise maximum of two vectors. /// /// `T` must be a vector of floating-point primitive types. /// @@ -387,7 +387,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_reduce_mul_ordered(x: T, y: U) -> U; - /// Add elements within a vector in arbitrary order. May also be re-associated with + /// Multiply elements within a vector in arbitrary order. May also be re-associated with /// unordered additions on the inputs/outputs. /// /// `T` must be a vector of integer or floating-point primitive types. @@ -405,7 +405,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_reduce_all(x: T) -> bool; - /// Check if all mask values are true. + /// Check if any mask value is true. /// /// `T` must be a vector of integer primitive types. /// @@ -569,6 +569,12 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_ctlz(x: T) -> T; + /// Count the number of ones in each element. + /// + /// `T` must be a vector of integers. + #[rustc_nounwind] + pub fn simd_ctpop(x: T) -> T; + /// Count the trailing zeros of each element. /// /// `T` must be a vector of integers. diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index 81371708b51e..d497da33dd92 100644 --- a/library/core/src/io/borrowed_buf.rs +++ b/library/core/src/io/borrowed_buf.rs @@ -92,14 +92,20 @@ impl<'data> BorrowedBuf<'data> { #[inline] pub fn filled(&self) -> &[u8] { // SAFETY: We only slice the filled part of the buffer, which is always valid - unsafe { MaybeUninit::slice_assume_init_ref(&self.buf[0..self.filled]) } + unsafe { + let buf = self.buf.get_unchecked(..self.filled); + MaybeUninit::slice_assume_init_ref(buf) + } } /// Returns a mutable reference to the filled portion of the buffer. #[inline] pub fn filled_mut(&mut self) -> &mut [u8] { // SAFETY: We only slice the filled part of the buffer, which is always valid - unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buf[0..self.filled]) } + unsafe { + let buf = self.buf.get_unchecked_mut(..self.filled); + MaybeUninit::slice_assume_init_mut(buf) + } } /// Returns a cursor over the unfilled part of the buffer. @@ -205,7 +211,10 @@ impl<'a> BorrowedCursor<'a> { #[inline] pub fn init_ref(&self) -> &[u8] { // SAFETY: We only slice the initialized part of the buffer, which is always valid - unsafe { MaybeUninit::slice_assume_init_ref(&self.buf.buf[self.buf.filled..self.buf.init]) } + unsafe { + let buf = self.buf.buf.get_unchecked(self.buf.filled..self.buf.init); + MaybeUninit::slice_assume_init_ref(buf) + } } /// Returns a mutable reference to the initialized portion of the cursor. @@ -213,7 +222,8 @@ impl<'a> BorrowedCursor<'a> { pub fn init_mut(&mut self) -> &mut [u8] { // SAFETY: We only slice the initialized part of the buffer, which is always valid unsafe { - MaybeUninit::slice_assume_init_mut(&mut self.buf.buf[self.buf.filled..self.buf.init]) + let buf = self.buf.buf.get_unchecked_mut(self.buf.filled..self.buf.init); + MaybeUninit::slice_assume_init_mut(buf) } } @@ -222,7 +232,8 @@ impl<'a> BorrowedCursor<'a> { /// It is safe to uninitialize any of these bytes. #[inline] pub fn uninit_mut(&mut self) -> &mut [MaybeUninit] { - &mut self.buf.buf[self.buf.init..] + // SAFETY: always in bounds + unsafe { self.buf.buf.get_unchecked_mut(self.buf.init..) } } /// Returns a mutable reference to the whole cursor. @@ -232,7 +243,8 @@ impl<'a> BorrowedCursor<'a> { /// The caller must not uninitialize any bytes in the initialized portion of the cursor. #[inline] pub unsafe fn as_mut(&mut self) -> &mut [MaybeUninit] { - &mut self.buf.buf[self.buf.filled..] + // SAFETY: always in bounds + unsafe { self.buf.buf.get_unchecked_mut(self.buf.filled..) } } /// Advance the cursor by asserting that `n` bytes have been filled. diff --git a/library/core/src/iter/adapters/chain.rs b/library/core/src/iter/adapters/chain.rs index bcaac2f42cf0..dad3d79acb18 100644 --- a/library/core/src/iter/adapters/chain.rs +++ b/library/core/src/iter/adapters/chain.rs @@ -4,8 +4,8 @@ use crate::ops::Try; /// An iterator that links two iterators together, in a chain. /// -/// This `struct` is created by [`Iterator::chain`]. See its documentation -/// for more. +/// This `struct` is created by [`chain`] or [`Iterator::chain`]. See their +/// documentation for more. /// /// # Examples /// @@ -38,6 +38,39 @@ impl Chain { } } +/// Converts the arguments to iterators and links them together, in a chain. +/// +/// See the documentation of [`Iterator::chain`] for more. +/// +/// # Examples +/// +/// ``` +/// #![feature(iter_chain)] +/// +/// use std::iter::chain; +/// +/// let a = [1, 2, 3]; +/// let b = [4, 5, 6]; +/// +/// let mut iter = chain(a, b); +/// +/// assert_eq!(iter.next(), Some(1)); +/// assert_eq!(iter.next(), Some(2)); +/// assert_eq!(iter.next(), Some(3)); +/// assert_eq!(iter.next(), Some(4)); +/// assert_eq!(iter.next(), Some(5)); +/// assert_eq!(iter.next(), Some(6)); +/// assert_eq!(iter.next(), None); +/// ``` +#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] +pub fn chain(a: A, b: B) -> Chain +where + A: IntoIterator, + B: IntoIterator, +{ + Chain::new(a.into_iter(), b.into_iter()) +} + #[stable(feature = "rust1", since = "1.0.0")] impl Iterator for Chain where diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index cc514bd914f1..05a5f2689056 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -41,6 +41,9 @@ pub use self::array_chunks::ArrayChunks; #[unstable(feature = "std_internals", issue = "none")] pub use self::by_ref_sized::ByRefSized; +#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] +pub use self::chain::chain; + #[stable(feature = "iter_cloned", since = "1.1.0")] pub use self::cloned::Cloned; diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index 44fef3e145b7..921c75c85f16 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -428,6 +428,8 @@ pub use self::traits::{ DoubleEndedIterator, ExactSizeIterator, Extend, FromIterator, IntoIterator, Product, Sum, }; +#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] +pub use self::adapters::chain; #[stable(feature = "iter_zip", since = "1.59.0")] pub use self::adapters::zip; #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] diff --git a/library/core/src/iter/sources/repeat_n.rs b/library/core/src/iter/sources/repeat_n.rs index 8224e4b12a0e..8390dab8e543 100644 --- a/library/core/src/iter/sources/repeat_n.rs +++ b/library/core/src/iter/sources/repeat_n.rs @@ -1,4 +1,4 @@ -use crate::iter::{FusedIterator, TrustedLen}; +use crate::iter::{FusedIterator, TrustedLen, UncheckedIterator}; use crate::mem::ManuallyDrop; use crate::num::NonZero; @@ -193,3 +193,5 @@ impl FusedIterator for RepeatN {} #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for RepeatN {} +#[unstable(feature = "trusted_len_next_unchecked", issue = "37572")] +impl UncheckedIterator for RepeatN {} diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 563781230c02..61a457890134 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -268,7 +268,6 @@ where /// } /// ``` #[rustc_diagnostic_item = "IntoIterator"] -#[rustc_skip_array_during_method_dispatch] #[rustc_on_unimplemented( on( _Self = "core::ops::range::RangeTo", @@ -312,6 +311,7 @@ where label = "`{Self}` is not an iterator", message = "`{Self}` is not an iterator" )] +#[rustc_skip_during_method_dispatch(array, boxed_slice)] #[stable(feature = "rust1", since = "1.0.0")] pub trait IntoIterator { /// The type of the elements being iterated over. diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index cee99e28b5a9..733d414d4446 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -2080,8 +2080,7 @@ pub trait Iterator { fn try_collect(&mut self) -> ChangeOutputType where Self: Sized, - ::Item: Try, - <::Item as Try>::Residual: Residual, + Self::Item: Try>, B: FromIterator<::Output>, { try_process(ByRefSized(self), |i| i.collect()) @@ -2689,12 +2688,13 @@ pub trait Iterator { #[inline] #[unstable(feature = "iterator_try_reduce", reason = "new API", issue = "87053")] #[rustc_do_not_const_check] - fn try_reduce(&mut self, f: F) -> ChangeOutputType> + fn try_reduce( + &mut self, + f: impl FnMut(Self::Item, Self::Item) -> R, + ) -> ChangeOutputType> where Self: Sized, - F: FnMut(Self::Item, Self::Item) -> R, - R: Try, - R::Residual: Residual>, + R: Try>>, { let first = match self.next() { Some(i) => i, @@ -2956,12 +2956,13 @@ pub trait Iterator { #[inline] #[unstable(feature = "try_find", reason = "new API", issue = "63178")] #[rustc_do_not_const_check] - fn try_find(&mut self, f: F) -> ChangeOutputType> + fn try_find( + &mut self, + f: impl FnMut(&Self::Item) -> R, + ) -> ChangeOutputType> where Self: Sized, - F: FnMut(&Self::Item) -> R, - R: Try, - R::Residual: Residual>, + R: Try>>, { #[inline] fn check( diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index dcf68b36c7a2..ef28bc99c4fc 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -174,7 +174,6 @@ #![feature(duration_consts_float)] #![feature(internal_impls_macro)] #![feature(ip)] -#![feature(ip_bits)] #![feature(is_ascii_octdigit)] #![feature(isqrt)] #![feature(link_cfg)] @@ -191,6 +190,7 @@ #![feature(str_split_remainder)] #![feature(strict_provenance)] #![feature(ub_checks)] +#![feature(unchecked_neg)] #![feature(unchecked_shifts)] #![feature(utf16_extra)] #![feature(utf16_extra_const)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 2ddedfa37fe2..0d4ca4d5f01e 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1569,7 +1569,12 @@ pub(crate) mod builtin { #[rustc_builtin_macro] #[macro_export] #[rustc_diagnostic_item = "assert_macro"] - #[allow_internal_unstable(panic_internals, edition_panic, generic_assert_internals)] + #[allow_internal_unstable( + core_intrinsics, + panic_internals, + edition_panic, + generic_assert_internals + )] macro_rules! assert { ($cond:expr $(,)?) => {{ /* compiler built-in */ }}; ($cond:expr, $($arg:tt)+) => {{ /* compiler built-in */ }}; diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 026e21586d40..4175d4a33294 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -120,12 +120,8 @@ use crate::slice; /// use std::mem::{self, MaybeUninit}; /// /// let data = { -/// // Create an uninitialized array of `MaybeUninit`. The `assume_init` is -/// // safe because the type we are claiming to have initialized here is a -/// // bunch of `MaybeUninit`s, which do not require initialization. -/// let mut data: [MaybeUninit>; 1000] = unsafe { -/// MaybeUninit::uninit().assume_init() -/// }; +/// // Create an uninitialized array of `MaybeUninit`. +/// let mut data: [MaybeUninit>; 1000] = [const { MaybeUninit::uninit() }; 1000]; /// /// // Dropping a `MaybeUninit` does nothing, so if there is a panic during this loop, /// // we have a memory leak, but there is no memory safety issue. @@ -147,10 +143,8 @@ use crate::slice; /// ``` /// use std::mem::MaybeUninit; /// -/// // Create an uninitialized array of `MaybeUninit`. The `assume_init` is -/// // safe because the type we are claiming to have initialized here is a -/// // bunch of `MaybeUninit`s, which do not require initialization. -/// let mut data: [MaybeUninit; 1000] = unsafe { MaybeUninit::uninit().assume_init() }; +/// // Create an uninitialized array of `MaybeUninit`. +/// let mut data: [MaybeUninit; 1000] = [const { MaybeUninit::uninit() }; 1000]; /// // Count the number of elements we have assigned. /// let mut data_len: usize = 0; /// @@ -348,8 +342,7 @@ impl MaybeUninit { #[must_use] #[inline(always)] pub const fn uninit_array() -> [Self; N] { - // SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid. - unsafe { MaybeUninit::<[MaybeUninit; N]>::uninit().assume_init() } + [const { MaybeUninit::uninit() }; N] } /// Creates a new `MaybeUninit` in an uninitialized state, with the memory being diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index 959c3289affb..b578756e46a6 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -460,12 +460,11 @@ impl Ipv4Addr { /// # Examples /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv4Addr; /// /// assert_eq!(Ipv4Addr::BITS, 32); /// ``` - #[unstable(feature = "ip_bits", issue = "113744")] + #[stable(feature = "ip_bits", since = "1.80.0")] pub const BITS: u32 = 32; /// Converts an IPv4 address into a `u32` representation using native byte order. @@ -479,7 +478,6 @@ impl Ipv4Addr { /// # Examples /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv4Addr; /// /// let addr = Ipv4Addr::new(0x12, 0x34, 0x56, 0x78); @@ -487,7 +485,6 @@ impl Ipv4Addr { /// ``` /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv4Addr; /// /// let addr = Ipv4Addr::new(0x12, 0x34, 0x56, 0x78); @@ -495,8 +492,8 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(0x12, 0x34, 0x56, 0x00), Ipv4Addr::from_bits(addr_bits)); /// /// ``` - #[rustc_const_unstable(feature = "ip_bits", issue = "113744")] - #[unstable(feature = "ip_bits", issue = "113744")] + #[rustc_const_stable(feature = "ip_bits", since = "1.80.0")] + #[stable(feature = "ip_bits", since = "1.80.0")] #[must_use] #[inline] pub const fn to_bits(self) -> u32 { @@ -510,14 +507,13 @@ impl Ipv4Addr { /// # Examples /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv4Addr; /// /// let addr = Ipv4Addr::from(0x12345678); /// assert_eq!(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78), addr); /// ``` - #[rustc_const_unstable(feature = "ip_bits", issue = "113744")] - #[unstable(feature = "ip_bits", issue = "113744")] + #[rustc_const_stable(feature = "ip_bits", since = "1.80.0")] + #[stable(feature = "ip_bits", since = "1.80.0")] #[must_use] #[inline] pub const fn from_bits(bits: u32) -> Ipv4Addr { @@ -1238,12 +1234,11 @@ impl Ipv6Addr { /// # Examples /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv6Addr; /// /// assert_eq!(Ipv6Addr::BITS, 128); /// ``` - #[unstable(feature = "ip_bits", issue = "113744")] + #[stable(feature = "ip_bits", since = "1.80.0")] pub const BITS: u32 = 128; /// Converts an IPv6 address into a `u128` representation using native byte order. @@ -1257,7 +1252,6 @@ impl Ipv6Addr { /// # Examples /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv6Addr; /// /// let addr = Ipv6Addr::new( @@ -1268,7 +1262,6 @@ impl Ipv6Addr { /// ``` /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv6Addr; /// /// let addr = Ipv6Addr::new( @@ -1284,8 +1277,8 @@ impl Ipv6Addr { /// Ipv6Addr::from_bits(addr_bits)); /// /// ``` - #[rustc_const_unstable(feature = "ip_bits", issue = "113744")] - #[unstable(feature = "ip_bits", issue = "113744")] + #[rustc_const_stable(feature = "ip_bits", since = "1.80.0")] + #[stable(feature = "ip_bits", since = "1.80.0")] #[must_use] #[inline] pub const fn to_bits(self) -> u128 { @@ -1299,7 +1292,6 @@ impl Ipv6Addr { /// # Examples /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv6Addr; /// /// let addr = Ipv6Addr::from(0x102030405060708090A0B0C0D0E0F00D_u128); @@ -1310,8 +1302,8 @@ impl Ipv6Addr { /// ), /// addr); /// ``` - #[rustc_const_unstable(feature = "ip_bits", issue = "113744")] - #[unstable(feature = "ip_bits", issue = "113744")] + #[rustc_const_stable(feature = "ip_bits", since = "1.80.0")] + #[stable(feature = "ip_bits", since = "1.80.0")] #[must_use] #[inline] pub const fn from_bits(bits: u128) -> Ipv6Addr { diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 9362dc876549..129f62fb43d1 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -68,6 +68,13 @@ pub mod consts { pub const FRAC_1_SQRT_PI: f128 = 0.564189583547756286948079451560772585844050629328998856844086_f128; + /// 1/sqrt(2π) + #[doc(alias = "FRAC_1_SQRT_TAU")] + #[unstable(feature = "f128", issue = "116909")] + // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + pub const FRAC_1_SQRT_2PI: f128 = + 0.398942280401432677939946059934381868475858631164934657665926_f128; + /// 2/π #[unstable(feature = "f128", issue = "116909")] pub const FRAC_2_PI: f128 = 0.636619772367581343075535053490057448137838582961825794990669_f128; diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index c4d4584544ba..7a488cd6bf6f 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -67,6 +67,12 @@ pub mod consts { // Also, #[unstable(feature = "more_float_constants", issue = "103883")] pub const FRAC_1_SQRT_PI: f16 = 0.564189583547756286948079451560772586_f16; + /// 1/sqrt(2π) + #[doc(alias = "FRAC_1_SQRT_TAU")] + #[unstable(feature = "f16", issue = "116909")] + // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + pub const FRAC_1_SQRT_2PI: f16 = 0.398942280401432677939946059934381868_f16; + /// 2/π #[unstable(feature = "f16", issue = "116909")] pub const FRAC_2_PI: f16 = 0.636619772367581343075535053490057448_f16; diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 2e715fb0bdde..9d34d3da9e95 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -327,6 +327,11 @@ pub mod consts { #[unstable(feature = "more_float_constants", issue = "103883")] pub const FRAC_1_SQRT_PI: f32 = 0.564189583547756286948079451560772586_f32; + /// 1/sqrt(2π) + #[doc(alias = "FRAC_1_SQRT_TAU")] + #[unstable(feature = "more_float_constants", issue = "103883")] + pub const FRAC_1_SQRT_2PI: f32 = 0.398942280401432677939946059934381868_f32; + /// 2/π #[stable(feature = "rust1", since = "1.0.0")] pub const FRAC_2_PI: f32 = 0.636619772367581343075535053490057448_f32; @@ -901,8 +906,8 @@ impl f32 { #[stable(feature = "f32_deg_rad_conversions", since = "1.7.0")] #[inline] pub fn to_radians(self) -> f32 { - let value: f32 = consts::PI; - self * (value / 180.0f32) + const RADS_PER_DEG: f32 = consts::PI / 180.0; + self * RADS_PER_DEG } /// Returns the maximum of the two numbers, ignoring NaN. @@ -1030,25 +1035,42 @@ impl f32 { /// ``` #[unstable(feature = "num_midpoint", issue = "110840")] pub fn midpoint(self, other: f32) -> f32 { - const LO: f32 = f32::MIN_POSITIVE * 2.; - const HI: f32 = f32::MAX / 2.; + cfg_if! { + if #[cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + all(any(target_arch="riscv32", target_arch= "riscv64"), target_feature="d"), + all(target_arch = "arm", target_feature="vfp2"), + target_arch = "wasm32", + target_arch = "wasm64", + ))] { + // whitelist the faster implementation to targets that have known good 64-bit float + // implementations. Falling back to the branchy code on targets that don't have + // 64-bit hardware floats or buggy implementations. + // see: https://github.com/rust-lang/rust/pull/121062#issuecomment-2123408114 + ((f64::from(self) + f64::from(other)) / 2.0) as f32 + } else { + const LO: f32 = f32::MIN_POSITIVE * 2.; + const HI: f32 = f32::MAX / 2.; - let (a, b) = (self, other); - let abs_a = a.abs_private(); - let abs_b = b.abs_private(); + let (a, b) = (self, other); + let abs_a = a.abs_private(); + let abs_b = b.abs_private(); - if abs_a <= HI && abs_b <= HI { - // Overflow is impossible - (a + b) / 2. - } else if abs_a < LO { - // Not safe to halve a - a + (b / 2.) - } else if abs_b < LO { - // Not safe to halve b - (a / 2.) + b - } else { - // Not safe to halve a and b - (a / 2.) + (b / 2.) + if abs_a <= HI && abs_b <= HI { + // Overflow is impossible + (a + b) / 2. + } else if abs_a < LO { + // Not safe to halve a + a + (b / 2.) + } else if abs_b < LO { + // Not safe to halve b + (a / 2.) + b + } else { + // Not safe to halve a and b + (a / 2.) + (b / 2.) + } + } } } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index db8e1f318adb..95f021b2541a 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -327,6 +327,11 @@ pub mod consts { #[unstable(feature = "more_float_constants", issue = "103883")] pub const FRAC_1_SQRT_PI: f64 = 0.564189583547756286948079451560772586_f64; + /// 1/sqrt(2π) + #[doc(alias = "FRAC_1_SQRT_TAU")] + #[unstable(feature = "more_float_constants", issue = "103883")] + pub const FRAC_1_SQRT_2PI: f64 = 0.398942280401432677939946059934381868_f64; + /// 2/π #[stable(feature = "rust1", since = "1.0.0")] pub const FRAC_2_PI: f64 = 0.636619772367581343075535053490057448_f64; @@ -912,8 +917,8 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn to_radians(self) -> f64 { - let value: f64 = consts::PI; - self * (value / 180.0) + const RADS_PER_DEG: f64 = consts::PI / 180.0; + self * RADS_PER_DEG } /// Returns the maximum of the two numbers, ignoring NaN. diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 77b1039039b1..448894849639 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -183,6 +183,30 @@ macro_rules! int_impl { (self as $UnsignedT).trailing_ones() } + /// Returns the bit pattern of `self` reinterpreted as an unsigned integer of the same size. + /// + /// This produces the same result as an `as` cast, but ensures that the bit-width remains + /// the same. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(integer_sign_cast)] + /// + #[doc = concat!("let n = -1", stringify!($SelfT), ";")] + /// + #[doc = concat!("assert_eq!(n.cast_unsigned(), ", stringify!($UnsignedT), "::MAX);")] + /// ``` + #[unstable(feature = "integer_sign_cast", issue = "125882")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn cast_unsigned(self) -> $UnsignedT { + self as $UnsignedT + } + /// Shifts the bits to the left by a specified amount, `n`, /// wrapping the truncated bits to the end of the resulting integer. /// @@ -488,9 +512,19 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_add`. - unsafe { intrinsics::unchecked_add(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_add cannot overflow"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => !lhs.overflowing_add(rhs).1, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_add(self, rhs) + } } /// Checked addition with an unsigned integer. Computes `self + rhs`, @@ -630,9 +664,19 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_sub`. - unsafe { intrinsics::unchecked_sub(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_sub cannot overflow"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => !lhs.overflowing_sub(rhs).1, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_sub(self, rhs) + } } /// Checked subtraction with an unsigned integer. Computes `self - rhs`, @@ -772,9 +816,19 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_mul`. - unsafe { intrinsics::unchecked_mul(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_mul cannot overflow"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => !lhs.overflowing_mul(rhs).1, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_mul(self, rhs) + } } /// Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0` @@ -1111,9 +1165,18 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_neg(self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_neg`. - unsafe { intrinsics::unchecked_sub(0, self) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_neg cannot overflow"), + ( + lhs: $SelfT = self, + ) => !lhs.overflowing_neg().1, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_sub(0, self) + } } /// Strict negation. Computes `-self`, panicking if `self == MIN`. @@ -1234,9 +1297,18 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shl`. - unsafe { intrinsics::unchecked_shl(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_shl cannot overflow"), + ( + rhs: u32 = rhs, + ) => rhs < <$ActualT>::BITS, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_shl(self, rhs) + } } /// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is @@ -1323,9 +1395,18 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shr`. - unsafe { intrinsics::unchecked_shr(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_shr cannot overflow"), + ( + rhs: u32 = rhs, + ) => rhs < <$ActualT>::BITS, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_shr(self, rhs) + } } /// Checked absolute value. Computes `self.abs()`, returning `None` if @@ -2703,8 +2784,10 @@ macro_rules! int_impl { /// /// In other words, the result is `self / rhs` rounded to the integer `q` /// such that `self >= q * rhs`. - /// If `self > 0`, this is equal to round towards zero (the default in Rust); - /// if `self < 0`, this is equal to round towards +/- infinity. + /// If `self > 0`, this is equal to rounding towards zero (the default in Rust); + /// if `self < 0`, this is equal to rounding away from zero (towards +/- infinity). + /// If `rhs > 0`, this is equal to rounding towards -infinity; + /// if `rhs < 0`, this is equal to rounding towards +infinity. /// /// # Panics /// @@ -2742,8 +2825,8 @@ macro_rules! int_impl { /// Calculates the least nonnegative remainder of `self (mod rhs)`. /// /// This is done as if by the Euclidean division algorithm -- given - /// `r = self.rem_euclid(rhs)`, `self = rhs * self.div_euclid(rhs) + r`, and - /// `0 <= r < abs(rhs)`. + /// `r = self.rem_euclid(rhs)`, the result satisfies + /// `self = rhs * self.div_euclid(rhs) + r` and `0 <= r < abs(rhs)`. /// /// # Panics /// diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 09a341e4d80a..ab1ede38979d 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -7,6 +7,7 @@ use crate::hint; use crate::intrinsics; use crate::mem; use crate::str::FromStr; +use crate::ub_checks::assert_unsafe_precondition; // Used because the `?` operator is not allowed in a const context. macro_rules! try_opt { diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index fcdd983343d6..5956a08593ad 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -517,9 +517,13 @@ macro_rules! nonzero_integer { /// ``` /// # use std::num::NonZero; /// # - #[doc = concat!("let n = NonZero::<", stringify!($Int), ">::new(", $leading_zeros_test, ").unwrap();")] + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let n = NonZero::<", stringify!($Int), ">::new(", $leading_zeros_test, ")?;")] /// /// assert_eq!(n.leading_zeros(), 0); + /// # Some(()) + /// # } /// ``` #[stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] #[rustc_const_stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] @@ -545,9 +549,13 @@ macro_rules! nonzero_integer { /// ``` /// # use std::num::NonZero; /// # - #[doc = concat!("let n = NonZero::<", stringify!($Int), ">::new(0b0101000).unwrap();")] + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let n = NonZero::<", stringify!($Int), ">::new(0b0101000)?;")] /// /// assert_eq!(n.trailing_zeros(), 3); + /// # Some(()) + /// # } /// ``` #[stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] #[rustc_const_stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] @@ -1051,7 +1059,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods { unsafe { Self::new_unchecked(self.get().unchecked_add(other)) } } - /// Returns the smallest power of two greater than or equal to n. + /// Returns the smallest power of two greater than or equal to `self`. /// Checks for overflow and returns [`None`] /// if the next power of two is greater than the type’s maximum value. /// As a consequence, the result cannot wrap to zero. @@ -1101,9 +1109,13 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// ``` /// # use std::num::NonZero; /// # - #[doc = concat!("assert_eq!(NonZero::new(7", stringify!($Int), ").unwrap().ilog2(), 2);")] - #[doc = concat!("assert_eq!(NonZero::new(8", stringify!($Int), ").unwrap().ilog2(), 3);")] - #[doc = concat!("assert_eq!(NonZero::new(9", stringify!($Int), ").unwrap().ilog2(), 3);")] + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("assert_eq!(NonZero::new(7", stringify!($Int), ")?.ilog2(), 2);")] + #[doc = concat!("assert_eq!(NonZero::new(8", stringify!($Int), ")?.ilog2(), 3);")] + #[doc = concat!("assert_eq!(NonZero::new(9", stringify!($Int), ")?.ilog2(), 3);")] + /// # Some(()) + /// # } /// ``` #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] @@ -1126,9 +1138,13 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// ``` /// # use std::num::NonZero; /// # - #[doc = concat!("assert_eq!(NonZero::new(99", stringify!($Int), ").unwrap().ilog10(), 1);")] - #[doc = concat!("assert_eq!(NonZero::new(100", stringify!($Int), ").unwrap().ilog10(), 2);")] - #[doc = concat!("assert_eq!(NonZero::new(101", stringify!($Int), ").unwrap().ilog10(), 2);")] + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("assert_eq!(NonZero::new(99", stringify!($Int), ")?.ilog10(), 1);")] + #[doc = concat!("assert_eq!(NonZero::new(100", stringify!($Int), ")?.ilog10(), 2);")] + #[doc = concat!("assert_eq!(NonZero::new(101", stringify!($Int), ")?.ilog10(), 2);")] + /// # Some(()) + /// # } /// ``` #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] @@ -1187,10 +1203,16 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// Basic usage: /// /// ``` - #[doc = concat!("let eight = std::num::NonZero::new(8", stringify!($Int), ").unwrap();")] + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let eight = NonZero::new(8", stringify!($Int), ")?;")] /// assert!(eight.is_power_of_two()); - #[doc = concat!("let ten = std::num::NonZero::new(10", stringify!($Int), ").unwrap();")] + #[doc = concat!("let ten = NonZero::new(10", stringify!($Int), ")?;")] /// assert!(!ten.is_power_of_two()); + /// # Some(()) + /// # } /// ``` #[must_use] #[stable(feature = "nonzero_is_power_of_two", since = "1.59.0")] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 446d0658c126..cdbd695008e8 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -184,6 +184,30 @@ macro_rules! uint_impl { (!self).trailing_zeros() } + /// Returns the bit pattern of `self` reinterpreted as a signed integer of the same size. + /// + /// This produces the same result as an `as` cast, but ensures that the bit-width remains + /// the same. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(integer_sign_cast)] + /// + #[doc = concat!("let n = ", stringify!($SelfT), "::MAX;")] + /// + #[doc = concat!("assert_eq!(n.cast_signed(), -1", stringify!($SignedT), ");")] + /// ``` + #[unstable(feature = "integer_sign_cast", issue = "125882")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn cast_signed(self) -> $SignedT { + self as $SignedT + } + /// Shifts the bits to the left by a specified amount, `n`, /// wrapping the truncated bits to the end of the resulting integer. /// @@ -495,9 +519,19 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_add`. - unsafe { intrinsics::unchecked_add(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_add cannot overflow"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => !lhs.overflowing_add(rhs).1, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_add(self, rhs) + } } /// Checked addition with a signed integer. Computes `self + rhs`, @@ -677,9 +711,19 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_sub`. - unsafe { intrinsics::unchecked_sub(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_sub cannot overflow"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => !lhs.overflowing_sub(rhs).1, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_sub(self, rhs) + } } /// Checked integer multiplication. Computes `self * rhs`, returning @@ -763,9 +807,19 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_mul`. - unsafe { intrinsics::unchecked_mul(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_mul cannot overflow"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => !lhs.overflowing_mul(rhs).1, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_mul(self, rhs) + } } /// Checked integer division. Computes `self / rhs`, returning `None` @@ -1118,9 +1172,12 @@ macro_rules! uint_impl { pub const fn checked_ilog(self, base: Self) -> Option { if self <= 0 || base <= 1 { None + } else if self < base { + Some(0) } else { - let mut n = 0; - let mut r = 1; + // Since base >= self, n >= 1 + let mut n = 1; + let mut r = base; // Optimization for 128 bit wide integers. if Self::BITS == 128 { @@ -1334,9 +1391,18 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shl`. - unsafe { intrinsics::unchecked_shl(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_shl cannot overflow"), + ( + rhs: u32 = rhs, + ) => rhs < <$ActualT>::BITS, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_shl(self, rhs) + } } /// Checked shift right. Computes `self >> rhs`, returning `None` @@ -1423,9 +1489,18 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shr`. - unsafe { intrinsics::unchecked_shr(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_shr cannot overflow"), + ( + rhs: u32 = rhs, + ) => rhs < <$ActualT>::BITS, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_shr(self, rhs) + } } /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if @@ -2755,7 +2830,7 @@ macro_rules! uint_impl { /// /// When return value overflows (i.e., `self > (1 << (N-1))` for type /// `uN`), it panics in debug mode and the return value is wrapped to 0 in - /// release mode (the only situation in which method can return 0). + /// release mode (the only situation in which this method can return 0). /// /// # Examples /// @@ -2776,7 +2851,7 @@ macro_rules! uint_impl { self.one_less_than_next_power_of_two() + 1 } - /// Returns the smallest power of two greater than or equal to `n`. If + /// Returns the smallest power of two greater than or equal to `self`. If /// the next power of two is greater than the type's maximum value, /// `None` is returned, otherwise the power of two is wrapped in `Some`. /// diff --git a/library/core/src/ops/async_function.rs b/library/core/src/ops/async_function.rs index 18bcee5a1c7e..48d1042d9df4 100644 --- a/library/core/src/ops/async_function.rs +++ b/library/core/src/ops/async_function.rs @@ -26,6 +26,7 @@ pub trait AsyncFn: AsyncFnMut { pub trait AsyncFnMut: AsyncFnOnce { /// Future returned by [`AsyncFnMut::async_call_mut`] and [`AsyncFn::async_call`]. #[unstable(feature = "async_fn_traits", issue = "none")] + #[lang = "call_ref_future"] type CallRefFuture<'a>: Future where Self: 'a; @@ -46,10 +47,12 @@ pub trait AsyncFnMut: AsyncFnOnce { pub trait AsyncFnOnce { /// Future returned by [`AsyncFnOnce::async_call_once`]. #[unstable(feature = "async_fn_traits", issue = "none")] + #[lang = "call_once_future"] type CallOnceFuture: Future; /// Output type of the called closure's future. #[unstable(feature = "async_fn_traits", issue = "none")] + #[lang = "async_fn_once_output"] type Output; /// Call the [`AsyncFnOnce`], returning a future which may move out of the called closure. @@ -143,6 +146,7 @@ mod internal_implementation_detail { // `for<'env> fn() -> (&'env T, ...)`. This allows us to represent the binder // of the closure's self-capture, and these upvar types will be instantiated with // the `'closure_env` region provided to the associated type. + #[lang = "async_fn_kind_upvars"] type Upvars<'closure_env, Inputs, Upvars, BorrowedUpvarsAsFnPtr>; } } diff --git a/library/core/src/ops/coroutine.rs b/library/core/src/ops/coroutine.rs index 6a6c5db1ab11..753f14c6b85e 100644 --- a/library/core/src/ops/coroutine.rs +++ b/library/core/src/ops/coroutine.rs @@ -76,6 +76,7 @@ pub trait Coroutine { /// values which are allowed to be returned each time a coroutine yields. /// For example an iterator-as-a-coroutine would likely have this type as /// `T`, the type being iterated over. + #[cfg_attr(not(bootstrap), lang = "coroutine_yield")] type Yield; /// The type of value this coroutine returns. @@ -84,6 +85,7 @@ pub trait Coroutine { /// `return` statement or implicitly as the last expression of a coroutine /// literal. For example futures would use this as `Result` as it /// represents a completed future. + #[cfg_attr(not(bootstrap), lang = "coroutine_return")] type Return; /// Resumes the execution of this coroutine. diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs index 65bda9177c7b..64214eae377d 100644 --- a/library/core/src/ops/index_range.rs +++ b/library/core/src/ops/index_range.rs @@ -1,4 +1,3 @@ -use crate::intrinsics::{unchecked_add, unchecked_sub}; use crate::iter::{FusedIterator, TrustedLen}; use crate::num::NonZero; use crate::ub_checks; @@ -46,7 +45,7 @@ impl IndexRange { #[inline] pub const fn len(&self) -> usize { // SAFETY: By invariant, this cannot wrap - unsafe { unchecked_sub(self.end, self.start) } + unsafe { self.end.unchecked_sub(self.start) } } /// # Safety @@ -57,7 +56,7 @@ impl IndexRange { let value = self.start; // SAFETY: The range isn't empty, so this cannot overflow - self.start = unsafe { unchecked_add(value, 1) }; + self.start = unsafe { value.unchecked_add(1) }; value } @@ -68,7 +67,7 @@ impl IndexRange { debug_assert!(self.start < self.end); // SAFETY: The range isn't empty, so this cannot overflow - let value = unsafe { unchecked_sub(self.end, 1) }; + let value = unsafe { self.end.unchecked_sub(1) }; self.end = value; value } @@ -83,7 +82,7 @@ impl IndexRange { let mid = if n <= self.len() { // SAFETY: We just checked that this will be between start and end, // and thus the addition cannot overflow. - unsafe { unchecked_add(self.start, n) } + unsafe { self.start.unchecked_add(n) } } else { self.end }; @@ -102,7 +101,7 @@ impl IndexRange { let mid = if n <= self.len() { // SAFETY: We just checked that this will be between start and end, // and thus the addition cannot overflow. - unsafe { unchecked_sub(self.end, n) } + unsafe { self.end.unchecked_sub(n) } } else { self.start }; diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index 483f55b20709..cd444c86ed06 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -363,7 +363,9 @@ pub trait Residual { } #[unstable(feature = "pub_crate_should_not_need_unstable_attr", issue = "none")] -pub(crate) type ChangeOutputType = <::Residual as Residual>::TryType; +#[allow(type_alias_bounds)] +pub(crate) type ChangeOutputType>, V> = + >::TryType; /// An adapter for implementing non-try methods via the `Try` implementation. /// diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 1e3ed0f7c49f..4d6ba2fa3b38 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -137,10 +137,13 @@ //! //! [^extern_fn]: this remains true for any argument/return types and any other ABI: `extern "abi" fn` (_e.g._, `extern "system" fn`) //! +//! Under some conditions the above types `T` are also null pointer optimized when wrapped in a [`Result`][result_repr]. +//! //! [`Box`]: ../../std/boxed/struct.Box.html //! [`num::NonZero*`]: crate::num //! [`ptr::NonNull`]: crate::ptr::NonNull //! [function call ABI]: ../primitive.fn.html#abi-compatibility +//! [result_repr]: crate::result#representation //! //! This is called the "null pointer optimization" or NPO. //! @@ -651,6 +654,32 @@ impl Option { !self.is_some() } + /// Returns `true` if the option is a [`None`] or the value inside of it matches a predicate. + /// + /// # Examples + /// + /// ``` + /// #![feature(is_none_or)] + /// + /// let x: Option = Some(2); + /// assert_eq!(x.is_none_or(|x| x > 1), true); + /// + /// let x: Option = Some(0); + /// assert_eq!(x.is_none_or(|x| x > 1), false); + /// + /// let x: Option = None; + /// assert_eq!(x.is_none_or(|x| x > 1), true); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "is_none_or", issue = "126383")] + pub fn is_none_or(self, f: impl FnOnce(T) -> bool) -> bool { + match self { + None => true, + Some(x) => f(x), + } + } + ///////////////////////////////////////////////////////////////////////// // Adapter for working with references ///////////////////////////////////////////////////////////////////////// @@ -1705,8 +1734,6 @@ impl Option { /// # Examples /// /// ``` - /// #![feature(option_take_if)] - /// /// let mut x = Some(42); /// /// let prev = x.take_if(|v| if *v == 42 { @@ -1723,7 +1750,7 @@ impl Option { /// assert_eq!(prev, Some(43)); /// ``` #[inline] - #[unstable(feature = "option_take_if", issue = "98934")] + #[stable(feature = "option_take_if", since = "1.80.0")] pub fn take_if

(&mut self, predicate: P) -> Option where P: FnOnce(&mut T) -> bool, diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 8771f40f9b42..56ede02673c0 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -12,6 +12,8 @@ use crate::any::Any; pub use self::location::Location; #[stable(feature = "panic_hooks", since = "1.10.0")] pub use self::panic_info::PanicInfo; +#[unstable(feature = "panic_info_message", issue = "66745")] +pub use self::panic_info::PanicMessage; #[stable(feature = "catch_unwind", since = "1.9.0")] pub use self::unwind_safe::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe}; @@ -144,7 +146,7 @@ pub macro unreachable_2021 { /// use. #[unstable(feature = "std_internals", issue = "none")] #[doc(hidden)] -pub unsafe trait PanicPayload { +pub unsafe trait PanicPayload: crate::fmt::Display { /// Take full ownership of the contents. /// The return type is actually `Box`, but we cannot use `Box` in core. /// @@ -157,4 +159,9 @@ pub unsafe trait PanicPayload { /// Just borrow the contents. fn get(&mut self) -> &(dyn Any + Send); + + /// Try to borrow the contents as `&str`, if possible without doing any allocations. + fn as_str(&mut self) -> Option<&str> { + None + } } diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index eb27da1724ec..8c04994ac0fc 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -2,9 +2,10 @@ use crate::fmt; /// A struct containing information about the location of a panic. /// -/// This structure is created by [`PanicInfo::location()`]. +/// This structure is created by [`PanicHookInfo::location()`] and [`PanicInfo::location()`]. /// /// [`PanicInfo::location()`]: crate::panic::PanicInfo::location +/// [`PanicHookInfo::location()`]: ../../std/panic/struct.PanicHookInfo.html#method.location /// /// # Examples /// diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index 403262212580..91953fd656b6 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -1,99 +1,65 @@ -use crate::any::Any; -use crate::fmt; +use crate::fmt::{self, Display}; use crate::panic::Location; /// A struct providing information about a panic. /// -/// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`] -/// function. +/// A `PanicInfo` structure is passed to the panic handler defined by `#[panic_handler]`. /// -/// [`set_hook`]: ../../std/panic/fn.set_hook.html +/// For the type used by the panic hook mechanism in `std`, see [`std::panic::PanicHookInfo`]. /// -/// # Examples -/// -/// ```should_panic -/// use std::panic; -/// -/// panic::set_hook(Box::new(|panic_info| { -/// println!("panic occurred: {panic_info}"); -/// })); -/// -/// panic!("critical system failure"); -/// ``` +/// [`std::panic::PanicHookInfo`]: ../../std/panic/struct.PanicHookInfo.html #[lang = "panic_info"] #[stable(feature = "panic_hooks", since = "1.10.0")] #[derive(Debug)] pub struct PanicInfo<'a> { - payload: &'a (dyn Any + Send), - message: Option<&'a fmt::Arguments<'a>>, + message: fmt::Arguments<'a>, location: &'a Location<'a>, can_unwind: bool, force_no_backtrace: bool, } +/// A message that was given to the `panic!()` macro. +/// +/// The [`Display`] implementation of this type will format the message with the arguments +/// that were given to the `panic!()` macro. +/// +/// See [`PanicInfo::message`]. +#[unstable(feature = "panic_info_message", issue = "66745")] +pub struct PanicMessage<'a> { + message: fmt::Arguments<'a>, +} + impl<'a> PanicInfo<'a> { - #[unstable( - feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` and related macros", - issue = "none" - )] - #[doc(hidden)] #[inline] - pub fn internal_constructor( - message: Option<&'a fmt::Arguments<'a>>, + pub(crate) fn new( + message: fmt::Arguments<'a>, location: &'a Location<'a>, can_unwind: bool, force_no_backtrace: bool, ) -> Self { - struct NoPayload; - PanicInfo { location, message, payload: &NoPayload, can_unwind, force_no_backtrace } + PanicInfo { location, message, can_unwind, force_no_backtrace } } - #[unstable( - feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` and related macros", - issue = "none" - )] - #[doc(hidden)] - #[inline] - pub fn set_payload(&mut self, info: &'a (dyn Any + Send)) { - self.payload = info; - } - - /// Returns the payload associated with the panic. + /// The message that was given to the `panic!` macro. /// - /// This will commonly, but not always, be a `&'static str` or [`String`]. + /// # Example /// - /// [`String`]: ../../std/string/struct.String.html + /// The type returned by this method implements `Display`, so it can + /// be passed directly to [`write!()`] and similar macros. /// - /// # Examples + /// [`write!()`]: core::write /// - /// ```should_panic - /// use std::panic; - /// - /// panic::set_hook(Box::new(|panic_info| { - /// if let Some(s) = panic_info.payload().downcast_ref::<&str>() { - /// println!("panic occurred: {s:?}"); - /// } else { - /// println!("panic occurred"); - /// } - /// })); - /// - /// panic!("Normal panic"); + /// ```ignore (no_std) + /// #[panic_handler] + /// fn panic_handler(panic_info: &PanicInfo<'_>) -> ! { + /// write!(DEBUG_OUTPUT, "panicked: {}", panic_info.message()); + /// loop {} + /// } /// ``` #[must_use] - #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn payload(&self) -> &(dyn Any + Send) { - self.payload - } - - /// If the `panic!` macro from the `core` crate (not from `std`) - /// was used with a formatting string and some additional arguments, - /// returns that message ready to be used for example with [`fmt::write`] - #[must_use] #[unstable(feature = "panic_info_message", issue = "66745")] - pub fn message(&self) -> Option<&fmt::Arguments<'_>> { - self.message + pub fn message(&self) -> PanicMessage<'_> { + PanicMessage { message: self.message } } /// Returns information about the location from which the panic originated, @@ -128,6 +94,24 @@ impl<'a> PanicInfo<'a> { Some(&self.location) } + /// Returns the payload associated with the panic. + /// + /// On this type, `core::panic::PanicInfo`, this method never returns anything useful. + /// It only exists because of compatibility with [`std::panic::PanicHookInfo`], + /// which used to be the same type. + /// + /// See [`std::panic::PanicHookInfo::payload`]. + /// + /// [`std::panic::PanicHookInfo`]: ../../std/panic/struct.PanicHookInfo.html + /// [`std::panic::PanicHookInfo::payload`]: ../../std/panic/struct.PanicHookInfo.html#method.payload + #[deprecated(since = "1.81.0", note = "this never returns anything useful")] + #[stable(feature = "panic_hooks", since = "1.10.0")] + #[allow(deprecated, deprecated_in_future)] + pub fn payload(&self) -> &(dyn crate::any::Any + Send) { + struct NoPayload; + &NoPayload + } + /// Returns whether the panic handler is allowed to unwind the stack from /// the point where the panic occurred. /// @@ -157,22 +141,50 @@ impl<'a> PanicInfo<'a> { } #[stable(feature = "panic_hook_display", since = "1.26.0")] -impl fmt::Display for PanicInfo<'_> { +impl Display for PanicInfo<'_> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("panicked at ")?; self.location.fmt(formatter)?; - formatter.write_str(":")?; - if let Some(message) = self.message { - formatter.write_str("\n")?; - formatter.write_fmt(*message)?; - } else if let Some(payload) = self.payload.downcast_ref::<&'static str>() { - formatter.write_str("\n")?; - formatter.write_str(payload)?; - } - // NOTE: we cannot use downcast_ref::() here - // since String is not available in core! - // The payload is a String when `std::panic!` is called with multiple arguments, - // but in that case the message is also available. + formatter.write_str(":\n")?; + formatter.write_fmt(self.message)?; Ok(()) } } + +impl<'a> PanicMessage<'a> { + /// Get the formatted message, if it has no arguments to be formatted at runtime. + /// + /// This can be used to avoid allocations in some cases. + /// + /// # Guarantees + /// + /// For `panic!("just a literal")`, this function is guaranteed to + /// return `Some("just a literal")`. + /// + /// For most cases with placeholders, this function will return `None`. + /// + /// See [`fmt::Arguments::as_str`] for details. + #[unstable(feature = "panic_info_message", issue = "66745")] + #[rustc_const_unstable(feature = "const_arguments_as_str", issue = "103900")] + #[must_use] + #[inline] + pub const fn as_str(&self) -> Option<&'static str> { + self.message.as_str() + } +} + +#[unstable(feature = "panic_info_message", issue = "66745")] +impl Display for PanicMessage<'_> { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_fmt(self.message) + } +} + +#[unstable(feature = "panic_info_message", issue = "66745")] +impl fmt::Debug for PanicMessage<'_> { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_fmt(self.message) + } +} diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index ca06e059b75a..97fb1d6b7323 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -1,7 +1,14 @@ //! Panic support for core //! -//! The core library cannot define panicking, but it does *declare* panicking. This -//! means that the functions inside of core are allowed to panic, but to be +//! In core, panicking is always done with a message, resulting in a `core::panic::PanicInfo` +//! containing a `fmt::Arguments`. In std, however, panicking can be done with panic_any, which +//! throws a `Box` containing any type of value. Because of this, +//! `std::panic::PanicHookInfo` is a different type, which contains a `&dyn Any` instead of a +//! `fmt::Arguments`. std's panic handler will convert the `fmt::Arguments` to a `&dyn Any` +//! containing either a `&'static str` or `String` containing the formatted message. +//! +//! The core library cannot define any panic handler, but it can invoke it. +//! This means that the functions inside of core are allowed to panic, but to be //! useful an upstream crate must define panicking for core to use. The current //! interface for panicking is: //! @@ -10,11 +17,6 @@ //! # { loop {} } //! ``` //! -//! This definition allows for panicking with any general message, but it does not -//! allow for failing with a `Box` value. (`PanicInfo` just contains a `&(dyn Any + Send)`, -//! for which we fill in a dummy value in `PanicInfo::internal_constructor`.) -//! The reason for this is that core is not allowed to allocate. -//! //! This module contains a few other panicking functions, but these are just the //! necessary lang items for the compiler. All panics are funneled through this //! one function. The actual symbol is declared through the `#[panic_handler]` attribute. @@ -61,8 +63,8 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { fn panic_impl(pi: &PanicInfo<'_>) -> !; } - let pi = PanicInfo::internal_constructor( - Some(&fmt), + let pi = PanicInfo::new( + fmt, Location::caller(), /* can_unwind */ true, /* force_no_backtrace */ false, @@ -99,8 +101,8 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo } // PanicInfo with the `can_unwind` flag set to false forces an abort. - let pi = PanicInfo::internal_constructor( - Some(&fmt), + let pi = PanicInfo::new( + fmt, Location::caller(), /* can_unwind */ false, force_no_backtrace, diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index d8fc3b7177f3..0d2aa3070a19 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -184,7 +184,7 @@ //! requires at least a level of pointer indirection each time a new object is added to the mix //! (and, practically, a heap allocation). //! -//! Although there were other reason as well, this issue of expensive composition is the key thing +//! Although there were other reasons as well, this issue of expensive composition is the key thing //! that drove Rust towards adopting a different model. It is particularly a problem //! when one considers, for example, the implications of composing together the [`Future`]s which //! will eventually make up an asynchronous task (including address-sensitive `async fn` state diff --git a/library/core/src/prelude/common.rs b/library/core/src/prelude/common.rs index afc6817aa1d2..a6a1a055e298 100644 --- a/library/core/src/prelude/common.rs +++ b/library/core/src/prelude/common.rs @@ -14,6 +14,9 @@ pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] pub use crate::mem::drop; +#[stable(feature = "size_of_prelude", since = "1.80.0")] +#[doc(no_inline)] +pub use crate::mem::{align_of, align_of_val, size_of, size_of_val}; // Re-exported types and traits #[stable(feature = "core_prelude", since = "1.4.0")] diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 8283fdc459be..5989bcbcc520 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1476,14 +1476,17 @@ mod prim_usize {} /// /// For instance, this means that unsafe code in a safe function may assume these invariants are /// ensured of arguments passed by the caller, and it may assume that these invariants are ensured -/// of return values from any safe functions it calls. In most cases, the inverse is also true: -/// unsafe code must not violate these invariants when passing arguments to safe functions or -/// returning values from safe functions; such violations may result in undefined behavior. Where -/// exceptions to this latter requirement exist, they will be called out explicitly in documentation. +/// of return values from any safe functions it calls. +/// +/// For the other direction, things are more complicated: when unsafe code passes arguments +/// to safe functions or returns values from safe functions, they generally must *at least* +/// not violate these invariants. The full requirements are stronger, as the reference generally +/// must point to data that is safe to use at type `T`. /// /// It is not decided yet whether unsafe code may violate these invariants temporarily on internal /// data. As a consequence, unsafe code which violates these invariants temporarily on internal data -/// may become unsound in future versions of Rust depending on how this question is decided. +/// may be unsound or become unsound in future versions of Rust depending on how this question is +/// decided. /// /// [allocated object]: ptr#allocated-object #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 73bb256518d8..b1f94caed351 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -330,7 +330,7 @@ impl *const T { /// /// unsafe { /// if let Some(val_back) = ptr.as_ref() { - /// println!("We got back the value: {val_back}!"); + /// assert_eq!(val_back, &10); /// } /// } /// ``` @@ -346,7 +346,7 @@ impl *const T { /// /// unsafe { /// let val_back = &*ptr; - /// println!("We got back the value: {val_back}!"); + /// assert_eq!(val_back, &10); /// } /// ``` #[stable(feature = "ptr_as_ref", since = "1.9.0")] @@ -393,7 +393,7 @@ impl *const T { /// let ptr: *const u8 = &10u8 as *const u8; /// /// unsafe { - /// println!("We got back the value: {}!", ptr.as_ref_unchecked()); + /// assert_eq!(ptr.as_ref_unchecked(), &10); /// } /// ``` // FIXME: mention it in the docs for `as_ref` and `as_uninit_ref` once stabilized. @@ -439,7 +439,7 @@ impl *const T { /// /// unsafe { /// if let Some(val_back) = ptr.as_uninit_ref() { - /// println!("We got back the value: {}!", val_back.assume_init()); + /// assert_eq!(val_back.assume_init(), 10); /// } /// } /// ``` @@ -465,8 +465,9 @@ impl *const T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -500,8 +501,8 @@ impl *const T { /// let ptr: *const u8 = s.as_ptr(); /// /// unsafe { - /// println!("{}", *ptr.offset(1) as char); - /// println!("{}", *ptr.offset(2) as char); + /// assert_eq!(*ptr.offset(1) as char, '2'); + /// assert_eq!(*ptr.offset(2) as char, '3'); /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -572,19 +573,21 @@ impl *const T { /// # Examples /// /// ``` + /// # use std::fmt::Write; /// // Iterate using a raw pointer in increments of two elements /// let data = [1u8, 2, 3, 4, 5]; /// let mut ptr: *const u8 = data.as_ptr(); /// let step = 2; /// let end_rounded_up = ptr.wrapping_offset(6); /// - /// // This loop prints "1, 3, 5, " + /// let mut out = String::new(); /// while ptr != end_rounded_up { /// unsafe { - /// print!("{}, ", *ptr); + /// write!(&mut out, "{}, ", *ptr).unwrap(); /// } /// ptr = ptr.wrapping_offset(step); /// } + /// assert_eq!(out.as_str(), "1, 3, 5, "); /// ``` #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] #[must_use = "returns a new pointer rather than modifying its argument"] @@ -676,11 +679,11 @@ impl *const T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both `self` and `origin` must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * `self` and `origin` must either /// - /// * Both pointers must be *derived from* a pointer to the same object. - /// (See below for an example.) + /// * both be *derived from* a pointer to the same [allocated object], and the memory range between + /// the two pointers must be either empty or in bounds of that object. (See below for an example.) + /// * or both be derived from an integer literal/constant, and point to the same address. /// /// * The distance between the pointers, in bytes, must be an exact multiple /// of the size of `T`. @@ -951,8 +954,9 @@ impl *const T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -986,8 +990,8 @@ impl *const T { /// let ptr: *const u8 = s.as_ptr(); /// /// unsafe { - /// println!("{}", *ptr.add(1) as char); - /// println!("{}", *ptr.add(2) as char); + /// assert_eq!(*ptr.add(1), b'2'); + /// assert_eq!(*ptr.add(2), b'3'); /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] @@ -1035,8 +1039,9 @@ impl *const T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset cannot exceed `isize::MAX` **bytes**. /// @@ -1070,13 +1075,14 @@ impl *const T { /// /// unsafe { /// let end: *const u8 = s.as_ptr().add(3); - /// println!("{}", *end.sub(1) as char); - /// println!("{}", *end.sub(2) as char); + /// assert_eq!(*end.sub(1), b'3'); + /// assert_eq!(*end.sub(2), b'2'); /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] + #[rustc_allow_const_fn_unstable(unchecked_neg)] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn sub(self, count: usize) -> Self @@ -1090,7 +1096,7 @@ impl *const T { // SAFETY: the caller must uphold the safety contract for `offset`. // Because the pointee is *not* a ZST, that means that `count` is // at most `isize::MAX`, and thus the negation cannot overflow. - unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) } + unsafe { self.offset((count as isize).unchecked_neg()) } } } @@ -1151,19 +1157,21 @@ impl *const T { /// # Examples /// /// ``` + /// # use std::fmt::Write; /// // Iterate using a raw pointer in increments of two elements /// let data = [1u8, 2, 3, 4, 5]; /// let mut ptr: *const u8 = data.as_ptr(); /// let step = 2; /// let end_rounded_up = ptr.wrapping_add(6); /// - /// // This loop prints "1, 3, 5, " + /// let mut out = String::new(); /// while ptr != end_rounded_up { /// unsafe { - /// print!("{}, ", *ptr); + /// write!(&mut out, "{}, ", *ptr).unwrap(); /// } /// ptr = ptr.wrapping_add(step); /// } + /// assert_eq!(out, "1, 3, 5, "); /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] @@ -1230,19 +1238,21 @@ impl *const T { /// # Examples /// /// ``` + /// # use std::fmt::Write; /// // Iterate using a raw pointer in increments of two elements (backwards) /// let data = [1u8, 2, 3, 4, 5]; /// let mut ptr: *const u8 = data.as_ptr(); /// let start_rounded_down = ptr.wrapping_sub(2); /// ptr = ptr.wrapping_add(4); /// let step = 2; - /// // This loop prints "5, 3, 1, " + /// let mut out = String::new(); /// while ptr != start_rounded_down { /// unsafe { - /// print!("{}, ", *ptr); + /// write!(&mut out, "{}, ", *ptr).unwrap(); /// } /// ptr = ptr.wrapping_sub(step); /// } + /// assert_eq!(out, "5, 3, 1, "); /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index eb815b6d822c..eb86bf662065 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -3,6 +3,7 @@ use crate::fmt; use crate::hash::{Hash, Hasher}; use crate::intrinsics::aggregate_raw_ptr; +use crate::intrinsics::ptr_metadata; use crate::marker::Freeze; /// Provides the pointer metadata type of any pointed-to type. @@ -94,10 +95,7 @@ pub trait Thin = Pointee; #[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[inline] pub const fn metadata(ptr: *const T) -> ::Metadata { - // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T - // and PtrComponents have the same memory layouts. Only std can make this - // guarantee. - unsafe { PtrRepr { const_ptr: ptr }.components.metadata } + ptr_metadata(ptr) } /// Forms a (possibly-wide) raw pointer from a data pointer and metadata. @@ -111,7 +109,7 @@ pub const fn metadata(ptr: *const T) -> ::Metadata { #[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[inline] pub const fn from_raw_parts( - data_pointer: *const (), + data_pointer: *const impl Thin, metadata: ::Metadata, ) -> *const T { aggregate_raw_ptr(data_pointer, metadata) @@ -125,35 +123,12 @@ pub const fn from_raw_parts( #[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[inline] pub const fn from_raw_parts_mut( - data_pointer: *mut (), + data_pointer: *mut impl Thin, metadata: ::Metadata, ) -> *mut T { aggregate_raw_ptr(data_pointer, metadata) } -#[repr(C)] -union PtrRepr { - const_ptr: *const T, - mut_ptr: *mut T, - components: PtrComponents, -} - -#[repr(C)] -struct PtrComponents { - data_pointer: *const (), - metadata: ::Metadata, -} - -// Manual impl needed to avoid `T: Copy` bound. -impl Copy for PtrComponents {} - -// Manual impl needed to avoid `T: Clone` bound. -impl Clone for PtrComponents { - fn clone(&self) -> Self { - *self - } -} - /// The metadata for a `Dyn = dyn SomeTrait` trait object type. /// /// It is a pointer to a vtable (virtual call table) @@ -178,8 +153,8 @@ impl Clone for PtrComponents { /// compare equal (since identical vtables can be deduplicated within a codegen unit). #[lang = "dyn_metadata"] pub struct DynMetadata { - vtable_ptr: &'static VTable, - phantom: crate::marker::PhantomData, + _vtable_ptr: &'static VTable, + _phantom: crate::marker::PhantomData, } extern "C" { @@ -191,6 +166,17 @@ extern "C" { } impl DynMetadata { + /// One of the things that rustc_middle does with this being a lang item is + /// give it `FieldsShape::Primitive`, which means that as far as codegen can + /// tell, it *is* a reference, and thus doesn't have any fields. + /// That means we can't use field access, and have to transmute it instead. + #[inline] + fn vtable_ptr(self) -> *const VTable { + // SAFETY: this layout assumption is hard-coded into the compiler. + // If it's somehow not a size match, the transmute will error. + unsafe { crate::mem::transmute::(self) } + } + /// Returns the size of the type associated with this vtable. #[inline] pub fn size_of(self) -> usize { @@ -198,18 +184,14 @@ impl DynMetadata { // Consider a reference like `&(i32, dyn Send)`: the vtable will only store the size of the // `Send` part! // SAFETY: DynMetadata always contains a valid vtable pointer - return unsafe { - crate::intrinsics::vtable_size(self.vtable_ptr as *const VTable as *const ()) - }; + return unsafe { crate::intrinsics::vtable_size(self.vtable_ptr() as *const ()) }; } /// Returns the alignment of the type associated with this vtable. #[inline] pub fn align_of(self) -> usize { // SAFETY: DynMetadata always contains a valid vtable pointer - return unsafe { - crate::intrinsics::vtable_align(self.vtable_ptr as *const VTable as *const ()) - }; + return unsafe { crate::intrinsics::vtable_align(self.vtable_ptr() as *const ()) }; } /// Returns the size and alignment together as a `Layout` @@ -226,7 +208,7 @@ unsafe impl Sync for DynMetadata {} impl fmt::Debug for DynMetadata { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("DynMetadata").field(&(self.vtable_ptr as *const VTable)).finish() + f.debug_tuple("DynMetadata").field(&self.vtable_ptr()).finish() } } @@ -248,7 +230,7 @@ impl Eq for DynMetadata {} impl PartialEq for DynMetadata { #[inline] fn eq(&self, other: &Self) -> bool { - crate::ptr::eq::(self.vtable_ptr, other.vtable_ptr) + crate::ptr::eq::(self.vtable_ptr(), other.vtable_ptr()) } } @@ -256,7 +238,7 @@ impl Ord for DynMetadata { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] fn cmp(&self, other: &Self) -> crate::cmp::Ordering { - (self.vtable_ptr as *const VTable).cmp(&(other.vtable_ptr as *const VTable)) + <*const VTable>::cmp(&self.vtable_ptr(), &other.vtable_ptr()) } } @@ -270,6 +252,6 @@ impl PartialOrd for DynMetadata { impl Hash for DynMetadata { #[inline] fn hash(&self, hasher: &mut H) { - crate::ptr::hash::(self.vtable_ptr, hasher) + crate::ptr::hash::(self.vtable_ptr(), hasher) } } diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index f87b6bbfcfb2..a8a47b69632f 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -15,18 +15,13 @@ //! The precise rules for validity are not determined yet. The guarantees that are //! provided at this point are very minimal: //! -//! * A [null] pointer is *never* valid, not even for accesses of [size zero][zst]. +//! * For operations of [size zero][zst], *every* pointer is valid, including the [null] pointer. +//! The following points are only concerned with non-zero-sized accesses. +//! * A [null] pointer is *never* valid. //! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer //! be *dereferenceable*: the memory range of the given size starting at the pointer must all be //! within the bounds of a single allocated object. Note that in Rust, //! every (stack-allocated) variable is considered a separate allocated object. -//! * Even for operations of [size zero][zst], the pointer must not be pointing to deallocated -//! memory, i.e., deallocation makes pointers invalid even for zero-sized operations. However, -//! casting any non-zero integer *literal* to a pointer is valid for zero-sized accesses, even if -//! some memory happens to exist at that address and gets deallocated. This corresponds to writing -//! your own allocator: allocating zero-sized objects is not very hard. The canonical way to -//! obtain a pointer that is valid for zero-sized accesses is [`NonNull::dangling`]. -//FIXME: mention `ptr::dangling` above, once it is stable. //! * All accesses performed by functions in this module are *non-atomic* in the sense //! of [atomic operations] used to synchronize between threads. This means it is //! undefined behavior to perform two concurrent accesses to the same location from different @@ -242,7 +237,7 @@ //! pointer. For code which *does* cast a usize to a pointer, the scope of the change depends //! on exactly what you're doing. //! -//! In general you just need to make sure that if you want to convert a usize address to a +//! In general, you just need to make sure that if you want to convert a usize address to a //! pointer and then use that pointer to read/write memory, you need to keep around a pointer //! that has sufficient provenance to perform that read/write itself. In this way all of your //! casts from an address to a pointer are essentially just applying offsets/indexing. @@ -314,7 +309,7 @@ //! i.e. the usual "ZSTs are fake, do what you want" rules apply *but* this only applies //! for actual forgery (integers cast to pointers). If you borrow some struct's field //! that *happens* to be zero-sized, the resulting pointer will have provenance tied to -//! that allocation and it will still get invalidated if the allocation gets deallocated. +//! that allocation, and it will still get invalidated if the allocation gets deallocated. //! In the future we may introduce an API to make such a forged allocation explicit. //! //! * [`wrapping_offset`][] a pointer outside its provenance. This includes pointers @@ -420,7 +415,7 @@ use crate::intrinsics; use crate::marker::FnPtr; use crate::ub_checks; -use crate::mem::{self, align_of, size_of, MaybeUninit}; +use crate::mem::{self, MaybeUninit}; mod alignment; #[unstable(feature = "ptr_alignment_type", issue = "102070")] @@ -455,8 +450,13 @@ mod mut_ptr; /// Executes the destructor (if any) of the pointed-to value. /// -/// This is semantically equivalent to calling [`ptr::read`] and discarding +/// This is almost the same as calling [`ptr::read`] and discarding /// the result, but has the following advantages: +// FIXME: say something more useful than "almost the same"? +// There are open questions here: `read` requires the value to be fully valid, e.g. if `T` is a +// `bool` it must be 0 or 1, if it is a reference then it must be dereferenceable. `drop_in_place` +// only requires that `*to_drop` be "valid for dropping" and we have not defined what that means. In +// Miri it currently (May 2024) requires nothing at all for types without drop glue. /// /// * It is *required* to use `drop_in_place` to drop unsized types like /// trait objects, because they can't be read out onto the stack and @@ -570,7 +570,7 @@ pub unsafe fn drop_in_place(to_drop: *mut T) { #[rustc_allow_const_fn_unstable(ptr_metadata)] #[rustc_diagnostic_item = "ptr_null"] pub const fn null() -> *const T { - from_raw_parts(without_provenance(0), ()) + from_raw_parts(without_provenance::<()>(0), ()) } /// Creates a null mutable raw pointer. @@ -596,7 +596,7 @@ pub const fn null() -> *const T { #[rustc_allow_const_fn_unstable(ptr_metadata)] #[rustc_diagnostic_item = "ptr_null_mut"] pub const fn null_mut() -> *mut T { - from_raw_parts_mut(without_provenance_mut(0), ()) + from_raw_parts_mut(without_provenance_mut::<()>(0), ()) } /// Creates a pointer with the given address and no provenance. @@ -698,7 +698,7 @@ pub const fn dangling_mut() -> *mut T { /// /// If there is no 'exposed' provenance that justifies the way this pointer will be used, /// the program has undefined behavior. In particular, the aliasing rules still apply: pointers -/// and references that have been invalidated due to aliasing accesses cannot be used any more, +/// and references that have been invalidated due to aliasing accesses cannot be used anymore, /// even if they have been exposed! /// /// Note that there is no algorithm that decides which provenance will be used. You can think of this @@ -840,7 +840,7 @@ pub const fn from_mut(r: &mut T) -> *mut T { #[rustc_allow_const_fn_unstable(ptr_metadata)] #[rustc_diagnostic_item = "ptr_slice_from_raw_parts"] pub const fn slice_from_raw_parts(data: *const T, len: usize) -> *const [T] { - intrinsics::aggregate_raw_ptr(data, len) + from_raw_parts(data, len) } /// Forms a raw mutable slice from a pointer and a length. @@ -886,7 +886,7 @@ pub const fn slice_from_raw_parts(data: *const T, len: usize) -> *const [T] { #[rustc_const_unstable(feature = "const_slice_from_raw_parts_mut", issue = "67456")] #[rustc_diagnostic_item = "ptr_slice_from_raw_parts_mut"] pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { - intrinsics::aggregate_raw_ptr(data, len) + from_raw_parts_mut(data, len) } /// Swaps the values at two mutable locations of the same type, without @@ -1097,7 +1097,7 @@ const unsafe fn swap_nonoverlapping_simple_untyped(x: *mut T, y: *mut T, coun // If we end up here, it's because we're using a simple type -- like // a small power-of-two-sized thing -- or a special type with particularly // large alignment, particularly SIMD types. - // Thus we're fine just reading-and-writing it, as either it's small + // Thus, we're fine just reading-and-writing it, as either it's small // and that works well anyway or it's special and the type's author // presumably wanted things to be done in the larger chunk. @@ -1290,7 +1290,7 @@ pub const unsafe fn read(src: *const T) -> T { // provides enough information to know that this is a typed operation. // However, as of March 2023 the compiler was not capable of taking advantage - // of that information. Thus the implementation here switched to an intrinsic, + // of that information. Thus, the implementation here switched to an intrinsic, // which lowers to `_0 = *src` in MIR, to address a few issues: // // - Using `MaybeUninit::assume_init` after a `copy_nonoverlapping` was not @@ -1570,7 +1570,7 @@ pub const unsafe fn write(dst: *mut T, src: T) { /// As a result, using `&packed.unaligned as *const FieldType` causes immediate /// *undefined behavior* in your program. /// -/// Instead you must use the [`ptr::addr_of_mut!`](addr_of_mut) +/// Instead, you must use the [`ptr::addr_of_mut!`](addr_of_mut) /// macro to create the pointer. You may use that returned pointer together with /// this function. /// diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 7856a1d85817..7c685156cbb0 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -480,8 +480,9 @@ impl *mut T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -904,11 +905,11 @@ impl *mut T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both `self` and `origin` must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * `self` and `origin` must either /// - /// * Both pointers must be *derived from* a pointer to the same object. - /// (See below for an example.) + /// * both be *derived from* a pointer to the same [allocated object], and the memory range between + /// the two pointers must be either empty or in bounds of that object. (See below for an example.) + /// * or both be derived from an integer literal/constant, and point to the same address. /// /// * The distance between the pointers, in bytes, must be an exact multiple /// of the size of `T`. @@ -1095,8 +1096,9 @@ impl *mut T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -1179,8 +1181,9 @@ impl *mut T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset cannot exceed `isize::MAX` **bytes**. /// @@ -1221,6 +1224,7 @@ impl *mut T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] + #[rustc_allow_const_fn_unstable(unchecked_neg)] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn sub(self, count: usize) -> Self @@ -1234,7 +1238,7 @@ impl *mut T { // SAFETY: the caller must uphold the safety contract for `offset`. // Because the pointee is *not* a ZST, that means that `count` is // at most `isize::MAX`, and thus the negation cannot overflow. - unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) } + unsafe { self.offset((count as isize).unchecked_neg()) } } } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 617890cf083b..0504a0fc32f4 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -525,8 +525,8 @@ impl NonNull { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[must_use = "returns a new pointer rather than modifying its argument"] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn offset(self, count: isize) -> Self where T: Sized, @@ -551,8 +551,8 @@ impl NonNull { #[must_use] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn byte_offset(self, count: isize) -> Self { // SAFETY: the caller must uphold the safety contract for `offset` and `byte_offset` has // the same safety contract. @@ -611,8 +611,8 @@ impl NonNull { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[must_use = "returns a new pointer rather than modifying its argument"] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn add(self, count: usize) -> Self where T: Sized, @@ -638,8 +638,8 @@ impl NonNull { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_allow_const_fn_unstable(set_ptr_value)] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn byte_add(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `add` and `byte_add` has the same // safety contract. @@ -699,8 +699,9 @@ impl NonNull { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[must_use = "returns a new pointer rather than modifying its argument"] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_allow_const_fn_unstable(unchecked_neg)] pub const unsafe fn sub(self, count: usize) -> Self where T: Sized, @@ -712,7 +713,7 @@ impl NonNull { // SAFETY: the caller must uphold the safety contract for `offset`. // Because the pointee is *not* a ZST, that means that `count` is // at most `isize::MAX`, and thus the negation cannot overflow. - unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) } + unsafe { self.offset((count as isize).unchecked_neg()) } } } @@ -731,8 +732,8 @@ impl NonNull { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_allow_const_fn_unstable(set_ptr_value)] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn byte_sub(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `sub` and `byte_sub` has the same // safety contract. @@ -846,8 +847,8 @@ impl NonNull { /// ``` #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn offset_from(self, origin: NonNull) -> isize where T: Sized, @@ -867,8 +868,8 @@ impl NonNull { /// ignoring the metadata. #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn byte_offset_from(self, origin: NonNull) -> isize { // SAFETY: the caller must uphold the safety contract for `byte_offset_from`. unsafe { self.pointer.byte_offset_from(origin.pointer) } @@ -957,8 +958,8 @@ impl NonNull { /// [`ptr::read`]: crate::ptr::read() #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn read(self) -> T where T: Sized, @@ -979,7 +980,7 @@ impl NonNull { /// [`ptr::read_volatile`]: crate::ptr::read_volatile() #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] pub unsafe fn read_volatile(self) -> T where T: Sized, @@ -998,8 +999,8 @@ impl NonNull { /// [`ptr::read_unaligned`]: crate::ptr::read_unaligned() #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn read_unaligned(self) -> T where T: Sized, @@ -1018,7 +1019,7 @@ impl NonNull { /// [`ptr::copy`]: crate::ptr::copy() #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] pub const unsafe fn copy_to(self, dest: NonNull, count: usize) where @@ -1038,7 +1039,7 @@ impl NonNull { /// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping() #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] pub const unsafe fn copy_to_nonoverlapping(self, dest: NonNull, count: usize) where @@ -1058,7 +1059,7 @@ impl NonNull { /// [`ptr::copy`]: crate::ptr::copy() #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] pub const unsafe fn copy_from(self, src: NonNull, count: usize) where @@ -1078,7 +1079,7 @@ impl NonNull { /// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping() #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] pub const unsafe fn copy_from_nonoverlapping(self, src: NonNull, count: usize) where @@ -1094,7 +1095,7 @@ impl NonNull { /// /// [`ptr::drop_in_place`]: crate::ptr::drop_in_place() #[inline(always)] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] pub unsafe fn drop_in_place(self) { // SAFETY: the caller must uphold the safety contract for `drop_in_place`. unsafe { ptr::drop_in_place(self.as_ptr()) } @@ -1108,7 +1109,7 @@ impl NonNull { /// [`ptr::write`]: crate::ptr::write() #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] pub const unsafe fn write(self, val: T) where @@ -1127,7 +1128,7 @@ impl NonNull { #[inline(always)] #[doc(alias = "memset")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] pub const unsafe fn write_bytes(self, val: u8, count: usize) where @@ -1149,7 +1150,7 @@ impl NonNull { /// [`ptr::write_volatile`]: crate::ptr::write_volatile() #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] pub unsafe fn write_volatile(self, val: T) where T: Sized, @@ -1168,7 +1169,7 @@ impl NonNull { /// [`ptr::write_unaligned`]: crate::ptr::write_unaligned() #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] pub const unsafe fn write_unaligned(self, val: T) where @@ -1185,7 +1186,7 @@ impl NonNull { /// /// [`ptr::replace`]: crate::ptr::replace() #[inline(always)] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] pub unsafe fn replace(self, src: T) -> T where T: Sized, @@ -1202,7 +1203,7 @@ impl NonNull { /// /// [`ptr::swap`]: crate::ptr::swap() #[inline(always)] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_swap", issue = "83163")] pub const unsafe fn swap(self, with: NonNull) where @@ -1254,7 +1255,7 @@ impl NonNull { /// ``` #[inline] #[must_use] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_align_offset", issue = "90962")] pub const fn align_offset(self, align: usize) -> usize where diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 4c6dc4bba437..f8cdcc000c50 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -228,6 +228,27 @@ //! [`Err(E)`]: Err //! [io::Error]: ../../std/io/struct.Error.html "io::Error" //! +//! # Representation +//! +//! In some cases, [`Result`] will gain the same size, alignment, and ABI +//! guarantees as [`Option`] has. One of either the `T` or `E` type must be a +//! type that qualifies for the `Option` [representation guarantees][opt-rep], +//! and the *other* type must meet all of the following conditions: +//! * Is a zero-sized type with alignment 1 (a "1-ZST"). +//! * Has no fields. +//! * Does not have the `#[non_exhaustive]` attribute. +//! +//! For example, `NonZeroI32` qualifies for the `Option` representation +//! guarantees, and `()` is a zero-sized type with alignment 1, no fields, and +//! it isn't `non_exhaustive`. This means that both `Result` and +//! `Result<(), NonZeroI32>` have the same size, alignment, and ABI guarantees +//! as `Option`. The only difference is the implied semantics: +//! * `Option` is "a non-zero i32 might be present" +//! * `Result` is "a non-zero i32 success result, if any" +//! * `Result<(), NonZeroI32>` is "a non-zero i32 error result, if any" +//! +//! [opt-rep]: ../option/index.html#representation "Option Representation" +//! //! # Method overview //! //! In addition to working with pattern matching, [`Result`] provides a diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 8ad045275ade..bf444d2f68af 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -123,8 +123,8 @@ impl [u8] { /// assert_eq!(b" ".trim_ascii_start(), b""); /// assert_eq!(b"".trim_ascii_start(), b""); /// ``` - #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] #[inline] pub const fn trim_ascii_start(&self) -> &[u8] { let mut bytes = self; @@ -152,8 +152,8 @@ impl [u8] { /// assert_eq!(b" ".trim_ascii_end(), b""); /// assert_eq!(b"".trim_ascii_end(), b""); /// ``` - #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] #[inline] pub const fn trim_ascii_end(&self) -> &[u8] { let mut bytes = self; @@ -182,8 +182,8 @@ impl [u8] { /// assert_eq!(b" ".trim_ascii(), b""); /// assert_eq!(b"".trim_ascii(), b""); /// ``` - #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] #[inline] pub const fn trim_ascii(&self) -> &[u8] { self.trim_ascii_start().trim_ascii_end() diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 8d7b6165510a..143dbd8a496d 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -1,9 +1,7 @@ //! Indexing implementations for `[T]`. use crate::intrinsics::const_eval_select; -use crate::intrinsics::unchecked_sub; use crate::ops; -use crate::ptr; use crate::ub_checks::assert_unsafe_precondition; #[stable(feature = "rust1", since = "1.0.0")] @@ -107,6 +105,47 @@ const fn slice_end_index_overflow_fail() -> ! { panic!("attempted to index slice up to maximum usize"); } +// The UbChecks are great for catching bugs in the unsafe methods, but including +// them in safe indexing is unnecessary and hurts inlining and debug runtime perf. +// Both the safe and unsafe public methods share these helpers, +// which use intrinsics directly to get *no* extra checks. + +#[inline(always)] +const unsafe fn get_noubcheck(ptr: *const [T], index: usize) -> *const T { + let ptr = ptr as *const T; + // SAFETY: The caller already checked these preconditions + unsafe { crate::intrinsics::offset(ptr, index) } +} + +#[inline(always)] +const unsafe fn get_mut_noubcheck(ptr: *mut [T], index: usize) -> *mut T { + let ptr = ptr as *mut T; + // SAFETY: The caller already checked these preconditions + unsafe { crate::intrinsics::offset(ptr, index) } +} + +#[inline(always)] +const unsafe fn get_offset_len_noubcheck( + ptr: *const [T], + offset: usize, + len: usize, +) -> *const [T] { + // SAFETY: The caller already checked these preconditions + let ptr = unsafe { get_noubcheck(ptr, offset) }; + crate::intrinsics::aggregate_raw_ptr(ptr, len) +} + +#[inline(always)] +const unsafe fn get_offset_len_mut_noubcheck( + ptr: *mut [T], + offset: usize, + len: usize, +) -> *mut [T] { + // SAFETY: The caller already checked these preconditions + let ptr = unsafe { get_mut_noubcheck(ptr, offset) }; + crate::intrinsics::aggregate_raw_ptr(ptr, len) +} + mod private_slice_index { use super::ops; #[stable(feature = "slice_get_slice", since = "1.28.0")] @@ -204,13 +243,17 @@ unsafe impl SliceIndex<[T]> for usize { #[inline] fn get(self, slice: &[T]) -> Option<&T> { // SAFETY: `self` is checked to be in bounds. - if self < slice.len() { unsafe { Some(&*self.get_unchecked(slice)) } } else { None } + if self < slice.len() { unsafe { Some(&*get_noubcheck(slice, self)) } } else { None } } #[inline] fn get_mut(self, slice: &mut [T]) -> Option<&mut T> { - // SAFETY: `self` is checked to be in bounds. - if self < slice.len() { unsafe { Some(&mut *self.get_unchecked_mut(slice)) } } else { None } + if self < slice.len() { + // SAFETY: `self` is checked to be in bounds. + unsafe { Some(&mut *get_mut_noubcheck(slice, self)) } + } else { + None + } } #[inline] @@ -228,7 +271,7 @@ unsafe impl SliceIndex<[T]> for usize { // Use intrinsics::assume instead of hint::assert_unchecked so that we don't check the // precondition of this function twice. crate::intrinsics::assume(self < slice.len()); - slice.as_ptr().add(self) + get_noubcheck(slice, self) } } @@ -240,7 +283,7 @@ unsafe impl SliceIndex<[T]> for usize { (this: usize = self, len: usize = slice.len()) => this < len ); // SAFETY: see comments for `get_unchecked` above. - unsafe { slice.as_mut_ptr().add(self) } + unsafe { get_mut_noubcheck(slice, self) } } #[inline] @@ -266,7 +309,7 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { fn get(self, slice: &[T]) -> Option<&[T]> { if self.end() <= slice.len() { // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { Some(&*self.get_unchecked(slice)) } + unsafe { Some(&*get_offset_len_noubcheck(slice, self.start(), self.len())) } } else { None } @@ -276,7 +319,7 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { if self.end() <= slice.len() { // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { Some(&mut *self.get_unchecked_mut(slice)) } + unsafe { Some(&mut *get_offset_len_mut_noubcheck(slice, self.start(), self.len())) } } else { None } @@ -293,7 +336,7 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { // cannot be longer than `isize::MAX`. They also guarantee that // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, // so the call to `add` is safe. - unsafe { ptr::slice_from_raw_parts(slice.as_ptr().add(self.start()), self.len()) } + unsafe { get_offset_len_noubcheck(slice, self.start(), self.len()) } } #[inline] @@ -305,14 +348,14 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { ); // SAFETY: see comments for `get_unchecked` above. - unsafe { ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start()), self.len()) } + unsafe { get_offset_len_mut_noubcheck(slice, self.start(), self.len()) } } #[inline] fn index(self, slice: &[T]) -> &[T] { if self.end() <= slice.len() { // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { &*self.get_unchecked(slice) } + unsafe { &*get_offset_len_noubcheck(slice, self.start(), self.len()) } } else { slice_end_index_len_fail(self.end(), slice.len()) } @@ -322,7 +365,7 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { fn index_mut(self, slice: &mut [T]) -> &mut [T] { if self.end() <= slice.len() { // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { &mut *self.get_unchecked_mut(slice) } + unsafe { &mut *get_offset_len_mut_noubcheck(slice, self.start(), self.len()) } } else { slice_end_index_len_fail(self.end(), slice.len()) } @@ -339,21 +382,26 @@ unsafe impl SliceIndex<[T]> for ops::Range { #[inline] fn get(self, slice: &[T]) -> Option<&[T]> { - if self.start > self.end || self.end > slice.len() { - None - } else { + // Using checked_sub is a safe way to get `SubUnchecked` in MIR + if let Some(new_len) = usize::checked_sub(self.end, self.start) + && self.end <= slice.len() + { // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { Some(&*self.get_unchecked(slice)) } + unsafe { Some(&*get_offset_len_noubcheck(slice, self.start, new_len)) } + } else { + None } } #[inline] fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { - if self.start > self.end || self.end > slice.len() { - None - } else { + if let Some(new_len) = usize::checked_sub(self.end, self.start) + && self.end <= slice.len() + { // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { Some(&mut *self.get_unchecked_mut(slice)) } + unsafe { Some(&mut *get_offset_len_mut_noubcheck(slice, self.start, new_len)) } + } else { + None } } @@ -374,8 +422,10 @@ unsafe impl SliceIndex<[T]> for ops::Range { // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, // so the call to `add` is safe and the length calculation cannot overflow. unsafe { - let new_len = unchecked_sub(self.end, self.start); - ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), new_len) + // Using the intrinsic avoids a superfluous UB check, + // since the one on this method already checked `end >= start`. + let new_len = crate::intrinsics::unchecked_sub(self.end, self.start); + get_offset_len_noubcheck(slice, self.start, new_len) } } @@ -392,31 +442,34 @@ unsafe impl SliceIndex<[T]> for ops::Range { ); // SAFETY: see comments for `get_unchecked` above. unsafe { - let new_len = unchecked_sub(self.end, self.start); - ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), new_len) + let new_len = crate::intrinsics::unchecked_sub(self.end, self.start); + get_offset_len_mut_noubcheck(slice, self.start, new_len) } } #[inline(always)] fn index(self, slice: &[T]) -> &[T] { - if self.start > self.end { - slice_index_order_fail(self.start, self.end); - } else if self.end > slice.len() { + // Using checked_sub is a safe way to get `SubUnchecked` in MIR + let Some(new_len) = usize::checked_sub(self.end, self.start) else { + slice_index_order_fail(self.start, self.end) + }; + if self.end > slice.len() { slice_end_index_len_fail(self.end, slice.len()); } // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { &*self.get_unchecked(slice) } + unsafe { &*get_offset_len_noubcheck(slice, self.start, new_len) } } #[inline] fn index_mut(self, slice: &mut [T]) -> &mut [T] { - if self.start > self.end { - slice_index_order_fail(self.start, self.end); - } else if self.end > slice.len() { + let Some(new_len) = usize::checked_sub(self.end, self.start) else { + slice_index_order_fail(self.start, self.end) + }; + if self.end > slice.len() { slice_end_index_len_fail(self.end, slice.len()); } // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { &mut *self.get_unchecked_mut(slice) } + unsafe { &mut *get_offset_len_mut_noubcheck(slice, self.start, new_len) } } } diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index d7d4f90c1a53..ca1920f98120 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -16,6 +16,9 @@ use crate::ptr::{self, without_provenance, without_provenance_mut, NonNull}; use super::{from_raw_parts, from_raw_parts_mut}; +#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] +impl !Iterator for [T] {} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> IntoIterator for &'a [T] { type Item = &'a T; diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index f82f965e67cf..17b4f9ece9d3 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2082,8 +2082,8 @@ impl [T] { /// /// assert_eq!(None, v.split_at_checked(7)); /// ``` - #[stable(feature = "split_at_checked", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "split_at_checked", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "split_at_checked", since = "1.80.0")] + #[rustc_const_stable(feature = "split_at_checked", since = "1.80.0")] #[inline] #[must_use] pub const fn split_at_checked(&self, mid: usize) -> Option<(&[T], &[T])> { @@ -2121,7 +2121,7 @@ impl [T] { /// /// assert_eq!(None, v.split_at_mut_checked(7)); /// ``` - #[stable(feature = "split_at_checked", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "split_at_checked", since = "1.80.0")] #[rustc_const_unstable(feature = "const_slice_split_at_mut", issue = "101804")] #[inline] #[must_use] @@ -4531,8 +4531,6 @@ impl [[T; N]] { /// # Examples /// /// ``` - /// #![feature(slice_flatten)] - /// /// assert_eq!([[1, 2, 3], [4, 5, 6]].as_flattened(), &[1, 2, 3, 4, 5, 6]); /// /// assert_eq!( @@ -4546,7 +4544,8 @@ impl [[T; N]] { /// let empty_slice_of_arrays: &[[u32; 10]] = &[]; /// assert!(empty_slice_of_arrays.as_flattened().is_empty()); /// ``` - #[unstable(feature = "slice_flatten", issue = "95629")] + #[stable(feature = "slice_flatten", since = "1.80.0")] + #[rustc_const_unstable(feature = "const_slice_flatten", issue = "95629")] pub const fn as_flattened(&self) -> &[T] { let len = if T::IS_ZST { self.len().checked_mul(N).expect("slice len overflow") @@ -4572,8 +4571,6 @@ impl [[T; N]] { /// # Examples /// /// ``` - /// #![feature(slice_flatten)] - /// /// fn add_5_to_all(slice: &mut [i32]) { /// for i in slice { /// *i += 5; @@ -4584,7 +4581,7 @@ impl [[T; N]] { /// add_5_to_all(array.as_flattened_mut()); /// assert_eq!(array, [[6, 7, 8], [9, 10, 11], [12, 13, 14]]); /// ``` - #[unstable(feature = "slice_flatten", issue = "95629")] + #[stable(feature = "slice_flatten", since = "1.80.0")] pub fn as_flattened_mut(&mut self) -> &mut [T] { let len = if T::IS_ZST { self.len().checked_mul(N).expect("slice len overflow") diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 29a12f106c5e..280aead270e7 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -1,7 +1,6 @@ //! Free functions to create `&[T]` and `&mut [T]`. use crate::array; -use crate::mem::{align_of, size_of}; use crate::ops::Range; use crate::ptr; use crate::ub_checks; @@ -83,6 +82,39 @@ use crate::ub_checks; /// } /// ``` /// +/// ### FFI: Handling null pointers +/// +/// In languages such as C++, pointers to empty collections are not guaranteed to be non-null. +/// When accepting such pointers, they have to be checked for null-ness to avoid undefined +/// behavior. +/// +/// ``` +/// use std::slice; +/// +/// /// Sum the elements of an FFI slice. +/// /// +/// /// # Safety +/// /// +/// /// If ptr is not NULL, it must be correctly aligned and +/// /// point to `len` initialized items of type `f32`. +/// unsafe extern "C" fn sum_slice(ptr: *const f32, len: usize) -> f32 { +/// let data = if ptr.is_null() { +/// // `len` is assumed to be 0. +/// &[] +/// } else { +/// // SAFETY: see function docstring. +/// unsafe { slice::from_raw_parts(ptr, len) } +/// }; +/// data.into_iter().sum() +/// } +/// +/// // This could be the result of C++'s std::vector::data(): +/// let ptr = std::ptr::null(); +/// // And this could be std::vector::size(): +/// let len = 0; +/// assert_eq!(unsafe { sum_slice(ptr, len) }, 0.0); +/// ``` +/// /// [valid]: ptr#safety /// [`NonNull::dangling()`]: ptr::NonNull::dangling #[inline] diff --git a/library/core/src/slice/rotate.rs b/library/core/src/slice/rotate.rs index fa8c238f8e7a..1d7b86339799 100644 --- a/library/core/src/slice/rotate.rs +++ b/library/core/src/slice/rotate.rs @@ -71,7 +71,9 @@ pub unsafe fn ptr_rotate(mut left: usize, mut mid: *mut T, mut right: usize) if (right == 0) || (left == 0) { return; } - if (left + right < 24) || (mem::size_of::() > mem::size_of::<[usize; 4]>()) { + if !cfg!(feature = "optimize_for_size") + && ((left + right < 24) || (mem::size_of::() > mem::size_of::<[usize; 4]>())) + { // Algorithm 1 // Microbenchmarks indicate that the average performance for random shifts is better all // the way until about `left + right == 32`, but the worst case performance breaks even @@ -158,7 +160,9 @@ pub unsafe fn ptr_rotate(mut left: usize, mut mid: *mut T, mut right: usize) } return; // `T` is not a zero-sized type, so it's okay to divide by its size. - } else if cmp::min(left, right) <= mem::size_of::() / mem::size_of::() { + } else if !cfg!(feature = "optimize_for_size") + && cmp::min(left, right) <= mem::size_of::() / mem::size_of::() + { // Algorithm 2 // The `[T; 0]` here is to ensure this is appropriately aligned for T let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit(); diff --git a/library/core/src/str/converts.rs b/library/core/src/str/converts.rs index b6ffb0a608d0..397759bd5cae 100644 --- a/library/core/src/str/converts.rs +++ b/library/core/src/str/converts.rs @@ -222,7 +222,7 @@ pub const unsafe fn from_utf8_unchecked_mut(v: &mut [u8]) -> &mut str { #[rustc_const_unstable(feature = "str_from_raw_parts", issue = "119206")] pub const unsafe fn from_raw_parts<'a>(ptr: *const u8, len: usize) -> &'a str { // SAFETY: the caller must uphold the safety contract for `from_raw_parts`. - unsafe { &*ptr::from_raw_parts(ptr.cast(), len) } + unsafe { &*ptr::from_raw_parts(ptr, len) } } /// Creates an `&mut str` from a pointer and a length. @@ -241,5 +241,5 @@ pub const unsafe fn from_raw_parts<'a>(ptr: *const u8, len: usize) -> &'a str { #[rustc_const_unstable(feature = "const_str_from_raw_parts_mut", issue = "119206")] pub const unsafe fn from_raw_parts_mut<'a>(ptr: *mut u8, len: usize) -> &'a mut str { // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`. - unsafe { &mut *ptr::from_raw_parts_mut(ptr.cast(), len) } + unsafe { &mut *ptr::from_raw_parts_mut(ptr, len) } } diff --git a/library/core/src/str/count.rs b/library/core/src/str/count.rs index 28567a7e753a..d8667864fe55 100644 --- a/library/core/src/str/count.rs +++ b/library/core/src/str/count.rs @@ -24,7 +24,7 @@ const UNROLL_INNER: usize = 4; #[inline] pub(super) fn count_chars(s: &str) -> usize { - if s.len() < USIZE_SIZE * UNROLL_INNER { + if cfg!(feature = "optimize_for_size") || s.len() < USIZE_SIZE * UNROLL_INNER { // Avoid entering the optimized implementation for strings where the // difference is not likely to matter, or where it might even be slower. // That said, a ton of thought was not spent on the particular threshold diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 669cdc92e358..683109380439 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -732,7 +732,7 @@ impl str { /// ``` #[inline] #[must_use] - #[stable(feature = "split_at_checked", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "split_at_checked", since = "1.80.0")] pub fn split_at_checked(&self, mid: usize) -> Option<(&str, &str)> { // is_char_boundary checks that the index is in [0, .len()] if self.is_char_boundary(mid) { @@ -772,7 +772,7 @@ impl str { /// ``` #[inline] #[must_use] - #[stable(feature = "split_at_checked", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "split_at_checked", since = "1.80.0")] pub fn split_at_mut_checked(&mut self, mid: usize) -> Option<(&mut str, &mut str)> { // is_char_boundary checks that the index is in [0, .len()] if self.is_char_boundary(mid) { @@ -2537,8 +2537,8 @@ impl str { /// ``` #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] - #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] #[inline] pub const fn trim_ascii_start(&self) -> &str { // SAFETY: Removing ASCII characters from a `&str` does not invalidate @@ -2562,8 +2562,8 @@ impl str { /// ``` #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] - #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] #[inline] pub const fn trim_ascii_end(&self) -> &str { // SAFETY: Removing ASCII characters from a `&str` does not invalidate @@ -2588,8 +2588,8 @@ impl str { /// ``` #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] - #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] #[inline] pub const fn trim_ascii(&self) -> &str { // SAFETY: Removing ASCII characters from a `&str` does not invalidate diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs index cc66da25795d..8988229be2e5 100644 --- a/library/core/src/str/pattern.rs +++ b/library/core/src/str/pattern.rs @@ -342,7 +342,7 @@ pub unsafe trait ReverseSearcher<'a>: Searcher<'a> { /// /// `(&str)::Searcher` is not a `DoubleEndedSearcher` because /// the pattern `"aa"` in the haystack `"aaa"` matches as either -/// `"[aa]a"` or `"a[aa]"`, depending from which side it is searched. +/// `"[aa]a"` or `"a[aa]"`, depending on which side it is searched. pub trait DoubleEndedSearcher<'a>: ReverseSearcher<'a> {} ///////////////////////////////////////////////////////////////////////////// diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 232ec589093d..c709ea2a15db 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -183,7 +183,7 @@ //! //! let spinlock_clone = Arc::clone(&spinlock); //! -//! let thread = thread::spawn(move|| { +//! let thread = thread::spawn(move || { //! spinlock_clone.store(0, Ordering::Release); //! }); //! @@ -1296,7 +1296,6 @@ impl AtomicPtr { #[cfg(target_has_atomic_equal_alignment = "ptr")] #[unstable(feature = "atomic_from_mut", issue = "76314")] pub fn from_mut(v: &mut *mut T) -> &mut Self { - use crate::mem::align_of; let [] = [(); align_of::>() - align_of::<*mut ()>()]; // SAFETY: // - the mutable reference guarantees unique ownership. @@ -2286,7 +2285,6 @@ macro_rules! atomic_int { #[$cfg_align] #[unstable(feature = "atomic_from_mut", issue = "76314")] pub fn from_mut(v: &mut $int_type) -> &mut Self { - use crate::mem::align_of; let [] = [(); align_of::() - align_of::<$int_type>()]; // SAFETY: // - the mutable reference guarantees unique ownership. @@ -2354,7 +2352,6 @@ macro_rules! atomic_int { #[$cfg_align] #[unstable(feature = "atomic_from_mut", issue = "76314")] pub fn from_mut_slice(v: &mut [$int_type]) -> &mut [Self] { - use crate::mem::align_of; let [] = [(); align_of::() - align_of::<$int_type>()]; // SAFETY: // - the mutable reference guarantees unique ownership. diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 7691721b91e2..3d21b09fa8a0 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -5,6 +5,7 @@ use crate::mem::transmute; use crate::any::Any; use crate::fmt; use crate::marker::PhantomData; +use crate::panic::AssertUnwindSafe; use crate::ptr; /// A `RawWaker` allows the implementor of a task executor to create a [`Waker`] @@ -236,7 +237,7 @@ enum ExtData<'a> { pub struct Context<'a> { waker: &'a Waker, local_waker: &'a LocalWaker, - ext: ExtData<'a>, + ext: AssertUnwindSafe>, // Ensure we future-proof against variance changes by forcing // the lifetime to be invariant (argument-position lifetimes // are contravariant while return-position lifetimes are @@ -279,7 +280,9 @@ impl<'a> Context<'a> { #[unstable(feature = "context_ext", issue = "123392")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn ext(&mut self) -> &mut dyn Any { - match &mut self.ext { + // FIXME: this field makes Context extra-weird about unwind safety + // can we justify AssertUnwindSafe if we stabilize this? do we care? + match &mut *self.ext { ExtData::Some(data) => *data, ExtData::None(unit) => unit, } @@ -353,7 +356,7 @@ impl<'a> ContextBuilder<'a> { #[rustc_const_unstable(feature = "const_waker", issue = "102012")] #[unstable(feature = "context_ext", issue = "123392")] pub const fn from(cx: &'a mut Context<'_>) -> Self { - let ext = match &mut cx.ext { + let ext = match &mut *cx.ext { ExtData::Some(ext) => ExtData::Some(*ext), ExtData::None(()) => ExtData::None(()), }; @@ -396,7 +399,7 @@ impl<'a> ContextBuilder<'a> { #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn build(self) -> Context<'a> { let ContextBuilder { waker, local_waker, ext, _marker, _marker2 } = self; - Context { waker, local_waker, ext, _marker, _marker2 } + Context { waker, local_waker, ext: AssertUnwindSafe(ext), _marker, _marker2 } } } diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 88fe29c99974..bc0fa3f0968d 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -348,7 +348,7 @@ impl Duration { #[inline] pub const fn from_weeks(weeks: u64) -> Duration { if weeks > u64::MAX / (SECS_PER_MINUTE * MINS_PER_HOUR * HOURS_PER_DAY * DAYS_PER_WEEK) { - panic!("overflow in Duration::from_days"); + panic!("overflow in Duration::from_weeks"); } Duration::from_secs(weeks * MINS_PER_HOUR * SECS_PER_MINUTE * HOURS_PER_DAY * DAYS_PER_WEEK) @@ -1084,40 +1084,42 @@ impl Duration { /// /// # Examples /// ``` - /// #![feature(div_duration)] /// use std::time::Duration; /// /// let dur1 = Duration::new(2, 700_000_000); /// let dur2 = Duration::new(5, 400_000_000); /// assert_eq!(dur1.div_duration_f64(dur2), 0.5); /// ``` - #[unstable(feature = "div_duration", issue = "63139")] + #[stable(feature = "div_duration", since = "1.80.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")] pub const fn div_duration_f64(self, rhs: Duration) -> f64 { - self.as_secs_f64() / rhs.as_secs_f64() + let self_nanos = (self.secs as f64) * (NANOS_PER_SEC as f64) + (self.nanos.0 as f64); + let rhs_nanos = (rhs.secs as f64) * (NANOS_PER_SEC as f64) + (rhs.nanos.0 as f64); + self_nanos / rhs_nanos } /// Divide `Duration` by `Duration` and return `f32`. /// /// # Examples /// ``` - /// #![feature(div_duration)] /// use std::time::Duration; /// /// let dur1 = Duration::new(2, 700_000_000); /// let dur2 = Duration::new(5, 400_000_000); /// assert_eq!(dur1.div_duration_f32(dur2), 0.5); /// ``` - #[unstable(feature = "div_duration", issue = "63139")] + #[stable(feature = "div_duration", since = "1.80.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")] pub const fn div_duration_f32(self, rhs: Duration) -> f32 { - self.as_secs_f32() / rhs.as_secs_f32() + let self_nanos = (self.secs as f32) * (NANOS_PER_SEC as f32) + (self.nanos.0 as f32); + let rhs_nanos = (rhs.secs as f32) * (NANOS_PER_SEC as f32) + (rhs.nanos.0 as f32); + self_nanos / rhs_nanos } } diff --git a/library/core/tests/iter/adapters/chain.rs b/library/core/tests/iter/adapters/chain.rs index b2429588de12..c93510df524c 100644 --- a/library/core/tests/iter/adapters/chain.rs +++ b/library/core/tests/iter/adapters/chain.rs @@ -2,6 +2,14 @@ use super::*; use core::iter::*; use core::num::NonZero; +#[test] +fn test_chain() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [30, 40, 50, 60]; + let expected = [0, 1, 2, 3, 4, 5, 30, 40, 50, 60]; + assert_eq!(Vec::from_iter(chain(xs, ys)), expected); +} + #[test] fn test_iterator_chain() { let xs = [0, 1, 2, 3, 4, 5]; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 797108a8425d..f632883b563d 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -29,7 +29,6 @@ #![feature(core_private_bignum)] #![feature(core_private_diy_float)] #![feature(dec2flt)] -#![feature(div_duration)] #![feature(duration_abs_diff)] #![feature(duration_consts_float)] #![feature(duration_constants)] @@ -76,6 +75,7 @@ #![feature(ip)] #![feature(iter_advance_by)] #![feature(iter_array_chunks)] +#![feature(iter_chain)] #![feature(iter_collect_into)] #![feature(iter_partition_in_place)] #![feature(iter_intersperse)] @@ -96,7 +96,6 @@ #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(ptr_metadata)] -#![feature(lazy_cell)] #![feature(unsized_tuple_coercion)] #![feature(const_option)] #![feature(const_option_ext)] @@ -112,9 +111,7 @@ #![feature(const_array_from_ref)] #![feature(const_slice_from_ref)] #![feature(waker_getters)] -#![feature(slice_flatten)] #![feature(error_generic_member_access)] -#![feature(error_in_core)] #![feature(trait_upcasting)] #![feature(is_ascii_octdigit)] #![feature(get_many_mut)] diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index e388800f400d..cc7339163076 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -83,12 +83,12 @@ fn align_of_val_raw_packed() { f: [u32], } let storage = [0u8; 4]; - let b: *const B = ptr::from_raw_parts(storage.as_ptr().cast(), 1); + let b: *const B = ptr::from_raw_parts(storage.as_ptr(), 1); assert_eq!(unsafe { align_of_val_raw(b) }, 1); const ALIGN_OF_VAL_RAW: usize = { let storage = [0u8; 4]; - let b: *const B = ptr::from_raw_parts(storage.as_ptr().cast(), 1); + let b: *const B = ptr::from_raw_parts(storage.as_ptr(), 1); unsafe { align_of_val_raw(b) } }; assert_eq!(ALIGN_OF_VAL_RAW, 1); diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs index 0fed854318d5..9d2912c4b22d 100644 --- a/library/core/tests/num/mod.rs +++ b/library/core/tests/num/mod.rs @@ -729,7 +729,7 @@ assume_usize_width! { } macro_rules! test_float { - ($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr) => { + ($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr, $max_exp:expr) => { mod $modname { #[test] fn min() { @@ -880,6 +880,27 @@ macro_rules! test_float { assert!(($nan as $fty).midpoint(1.0).is_nan()); assert!((1.0 as $fty).midpoint($nan).is_nan()); assert!(($nan as $fty).midpoint($nan).is_nan()); + + // test if large differences in magnitude are still correctly computed. + // NOTE: that because of how small x and y are, x + y can never overflow + // so (x + y) / 2.0 is always correct + // in particular, `2.pow(i)` will never be at the max exponent, so it could + // be safely doubled, while j is significantly smaller. + for i in $max_exp.saturating_sub(64)..$max_exp { + for j in 0..64u8 { + let large = <$fty>::from(2.0f32).powi(i); + // a much smaller number, such that there is no chance of overflow to test + // potential double rounding in midpoint's implementation. + let small = <$fty>::from(2.0f32).powi($max_exp - 1) + * <$fty>::EPSILON + * <$fty>::from(j); + + let naive = (large + small) / 2.0; + let midpoint = large.midpoint(small); + + assert_eq!(naive, midpoint); + } + } } #[test] fn rem_euclid() { @@ -912,7 +933,8 @@ test_float!( f32::NAN, f32::MIN, f32::MAX, - f32::MIN_POSITIVE + f32::MIN_POSITIVE, + f32::MAX_EXP ); test_float!( f64, @@ -922,5 +944,6 @@ test_float!( f64::NAN, f64::MIN, f64::MAX, - f64::MIN_POSITIVE + f64::MIN_POSITIVE, + f64::MAX_EXP ); diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 7b55c2bf8a81..e3830165eda6 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -965,7 +965,7 @@ fn thin_box() { fn value_ptr(&self) -> *const T { let (_, offset) = self.layout(); let data_ptr = unsafe { self.ptr.cast::().as_ptr().add(offset) }; - ptr::from_raw_parts(data_ptr.cast(), self.meta()) + ptr::from_raw_parts(data_ptr, self.meta()) } fn value_mut_ptr(&mut self) -> *mut T { @@ -973,7 +973,7 @@ fn thin_box() { // FIXME: can this line be shared with the same in `value_ptr()` // without upsetting Stacked Borrows? let data_ptr = unsafe { self.ptr.cast::().as_ptr().add(offset) }; - from_raw_parts_mut(data_ptr.cast(), self.meta()) + from_raw_parts_mut(data_ptr, self.meta()) } } @@ -1171,3 +1171,15 @@ fn test_ptr_from_raw_parts_in_const() { assert_eq!(EMPTY_SLICE_PTR.addr(), 123); assert_eq!(EMPTY_SLICE_PTR.len(), 456); } + +#[test] +fn test_ptr_metadata_in_const() { + use std::fmt::Debug; + + const ARRAY_META: () = std::ptr::metadata::<[u16; 3]>(&[1, 2, 3]); + const SLICE_META: usize = std::ptr::metadata::<[u16]>(&[1, 2, 3]); + const DYN_META: DynMetadata = std::ptr::metadata::(&[0_u8; 42]); + assert_eq!(ARRAY_META, ()); + assert_eq!(SLICE_META, 3); + assert_eq!(DYN_META.size_of(), 42); +} diff --git a/library/core/tests/result.rs b/library/core/tests/result.rs index a47ef7aa1ebc..00a6fd75b4f4 100644 --- a/library/core/tests/result.rs +++ b/library/core/tests/result.rs @@ -170,6 +170,7 @@ pub fn test_iter() { } #[test] +#[allow(for_loops_over_fallibles)] pub fn test_iter_mut() { let mut ok: Result = Ok(100); for loc in ok.iter_mut() { diff --git a/library/portable-simd/crates/core_simd/src/ops.rs b/library/portable-simd/crates/core_simd/src/ops.rs index d8e10eeaa1a2..dd7303a97b19 100644 --- a/library/portable-simd/crates/core_simd/src/ops.rs +++ b/library/portable-simd/crates/core_simd/src/ops.rs @@ -122,7 +122,7 @@ macro_rules! for_base_types { #[inline] #[must_use = "operator returns a new vector without mutating the inputs"] // TODO: only useful for int Div::div, but we hope that this - // will essentially always always get inlined anyway. + // will essentially always get inlined anyway. #[track_caller] fn $call(self, rhs: Self) -> Self::Output { $macro_impl!(self, rhs, $inner, $scalar) diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs index 0f1719206c9c..cbffbc564cfe 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -96,7 +96,7 @@ where fn cast(self) -> Self::CastPtr { // SimdElement currently requires zero-sized metadata, so this should never fail. // If this ever changes, `simd_cast_ptr` should produce a post-mono error. - use core::{mem::size_of, ptr::Pointee}; + use core::ptr::Pointee; assert_eq!(size_of::<::Metadata>(), 0); assert_eq!(size_of::<::Metadata>(), 0); diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs index 7ba996d149c0..6bc6ca3ac42d 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -93,7 +93,7 @@ where fn cast(self) -> Self::CastPtr { // SimdElement currently requires zero-sized metadata, so this should never fail. // If this ever changes, `simd_cast_ptr` should produce a post-mono error. - use core::{mem::size_of, ptr::Pointee}; + use core::ptr::Pointee; assert_eq!(size_of::<::Metadata>(), 0); assert_eq!(size_of::<::Metadata>(), 0); diff --git a/library/proc_macro/src/bridge/fxhash.rs b/library/proc_macro/src/bridge/fxhash.rs index f4e905441972..9fb79eabd055 100644 --- a/library/proc_macro/src/bridge/fxhash.rs +++ b/library/proc_macro/src/bridge/fxhash.rs @@ -7,7 +7,6 @@ use std::collections::HashMap; use std::hash::BuildHasherDefault; use std::hash::Hasher; -use std::mem::size_of; use std::ops::BitXor; /// Type alias for a hashmap using the `fx` hash algorithm. @@ -69,7 +68,7 @@ impl Hasher for FxHasher { hash.add_to_hash(u16::from_ne_bytes(bytes[..2].try_into().unwrap()) as usize); bytes = &bytes[2..]; } - if (size_of::() > 1) && bytes.len() >= 1 { + if (size_of::() > 1) && !bytes.is_empty() { hash.add_to_hash(bytes[0] as usize); } self.hash = hash.hash; diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 6d75a5a627c8..202a8e04543b 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -264,9 +264,9 @@ impl From> for PanicMessage { } } -impl Into> for PanicMessage { - fn into(self) -> Box { - match self { +impl From for Box { + fn from(val: PanicMessage) -> Self { + match val { PanicMessage::StaticStr(s) => Box::new(s), PanicMessage::String(s) => Box::new(s), PanicMessage::Unknown => { diff --git a/library/proc_macro/src/escape.rs b/library/proc_macro/src/escape.rs new file mode 100644 index 000000000000..87a4d1d50fd4 --- /dev/null +++ b/library/proc_macro/src/escape.rs @@ -0,0 +1,57 @@ +#[derive(Copy, Clone)] +pub(crate) struct EscapeOptions { + /// Produce \'. + pub escape_single_quote: bool, + /// Produce \". + pub escape_double_quote: bool, + /// Produce \x escapes for non-ASCII, and use \x rather than \u for ASCII + /// control characters. + pub escape_nonascii: bool, +} + +pub(crate) fn escape_bytes(bytes: &[u8], opt: EscapeOptions) -> String { + let mut repr = String::new(); + + if opt.escape_nonascii { + for &byte in bytes { + escape_single_byte(byte, opt, &mut repr); + } + } else { + let mut chunks = bytes.utf8_chunks(); + while let Some(chunk) = chunks.next() { + for ch in chunk.valid().chars() { + escape_single_char(ch, opt, &mut repr); + } + for &byte in chunk.invalid() { + escape_single_byte(byte, opt, &mut repr); + } + } + } + + repr +} + +fn escape_single_byte(byte: u8, opt: EscapeOptions, repr: &mut String) { + if byte == b'\0' { + repr.push_str("\\0"); + } else if (byte == b'\'' && !opt.escape_single_quote) + || (byte == b'"' && !opt.escape_double_quote) + { + repr.push(byte as char); + } else { + // Escapes \t, \r, \n, \\, \', \", and uses \x## for non-ASCII and + // for ASCII control characters. + repr.extend(byte.escape_ascii().map(char::from)); + } +} + +fn escape_single_char(ch: char, opt: EscapeOptions, repr: &mut String) { + if (ch == '\'' && !opt.escape_single_quote) || (ch == '"' && !opt.escape_double_quote) { + repr.push(ch); + } else { + // Escapes \0, \t, \r, \n, \\, \', \", and uses \u{...} for + // non-printable characters and for Grapheme_Extend characters, which + // includes things like U+0300 "Combining Grave Accent". + repr.extend(ch.escape_debug()); + } +} diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 23ae2e7dc0de..581d7e3efe37 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -43,10 +43,12 @@ pub mod bridge; mod diagnostic; +mod escape; #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] pub use diagnostic::{Diagnostic, Level, MultiSpan}; +use crate::escape::{escape_bytes, EscapeOptions}; use std::ffi::CStr; use std::ops::{Range, RangeBounds}; use std::path::PathBuf; @@ -815,6 +817,18 @@ pub enum Delimiter { /// "macro variable" `$var`. It is important to preserve operator priorities in cases like /// `$var * 3` where `$var` is `1 + 2`. /// Invisible delimiters might not survive roundtrip of a token stream through a string. + /// + ///

#[stable(feature = "proc_macro_lib2", since = "1.29.0")] None, } @@ -1344,40 +1358,61 @@ impl Literal { /// String literal. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn string(string: &str) -> Literal { - let quoted = format!("{:?}", string); - assert!(quoted.starts_with('"') && quoted.ends_with('"')); - let symbol = "ed[1..quoted.len() - 1]; - Literal::new(bridge::LitKind::Str, symbol, None) + let escape = EscapeOptions { + escape_single_quote: false, + escape_double_quote: true, + escape_nonascii: false, + }; + let repr = escape_bytes(string.as_bytes(), escape); + Literal::new(bridge::LitKind::Str, &repr, None) } /// Character literal. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn character(ch: char) -> Literal { - let quoted = format!("{:?}", ch); - assert!(quoted.starts_with('\'') && quoted.ends_with('\'')); - let symbol = "ed[1..quoted.len() - 1]; - Literal::new(bridge::LitKind::Char, symbol, None) + let escape = EscapeOptions { + escape_single_quote: true, + escape_double_quote: false, + escape_nonascii: false, + }; + let repr = escape_bytes(ch.encode_utf8(&mut [0u8; 4]).as_bytes(), escape); + Literal::new(bridge::LitKind::Char, &repr, None) } /// Byte character literal. #[stable(feature = "proc_macro_byte_character", since = "1.79.0")] pub fn byte_character(byte: u8) -> Literal { - let string = [byte].escape_ascii().to_string(); - Literal::new(bridge::LitKind::Byte, &string, None) + let escape = EscapeOptions { + escape_single_quote: true, + escape_double_quote: false, + escape_nonascii: true, + }; + let repr = escape_bytes(&[byte], escape); + Literal::new(bridge::LitKind::Byte, &repr, None) } /// Byte string literal. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn byte_string(bytes: &[u8]) -> Literal { - let string = bytes.escape_ascii().to_string(); - Literal::new(bridge::LitKind::ByteStr, &string, None) + let escape = EscapeOptions { + escape_single_quote: false, + escape_double_quote: true, + escape_nonascii: true, + }; + let repr = escape_bytes(bytes, escape); + Literal::new(bridge::LitKind::ByteStr, &repr, None) } /// C string literal. #[stable(feature = "proc_macro_c_str_literals", since = "1.79.0")] pub fn c_string(string: &CStr) -> Literal { - let string = string.to_bytes().escape_ascii().to_string(); - Literal::new(bridge::LitKind::CStr, &string, None) + let escape = EscapeOptions { + escape_single_quote: false, + escape_double_quote: true, + escape_nonascii: false, + }; + let repr = escape_bytes(string.to_bytes(), escape); + Literal::new(bridge::LitKind::CStr, &repr, None) } /// Returns the span encompassing this literal. diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 1720fe84fa7c..32479cd2836f 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -24,24 +24,20 @@ hashbrown = { version = "0.14", default-features = false, features = ['rustc-dep std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = ['rustc-dep-of-std'] } # Dependencies of the `backtrace` crate -rustc-demangle = { version = "0.1.21", features = ['rustc-dep-of-std'] } +rustc-demangle = { version = "0.1.24", features = ['rustc-dep-of-std'] } [target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies] miniz_oxide = { version = "0.7.0", optional = true, default-features = false } -addr2line = { version = "0.21.0", optional = true, default-features = false } +addr2line = { version = "0.22.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "=0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true } - -# Pin libc (pending https://github.com/rust-lang/rust/pull/124560) -[target.'cfg(all(windows, target_env = "msvc"))'.dependencies] -libc = { version = "=0.2.153", default-features = false } +libc = { version = "0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true } [target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies] -object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] } +object = { version = "0.36.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] } [target.'cfg(target_os = "aix")'.dependencies] -object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'xcoff', 'unaligned', 'archive'] } +object = { version = "0.36.0", default-features = false, optional = true, features = ['read_core', 'xcoff', 'unaligned', 'archive'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } @@ -54,7 +50,7 @@ dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] } fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'], public = true } [target.'cfg(target_os = "hermit")'.dependencies] -hermit-abi = { version = "0.3.9", features = ['rustc-dep-of-std'], public = true } +hermit-abi = { version = "0.4.0", features = ['rustc-dep-of-std'], public = true } [target.'cfg(target_os = "wasi")'.dependencies] wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } @@ -82,6 +78,8 @@ system-llvm-libunwind = ["unwind/system-llvm-libunwind"] # Make panics and failed asserts immediately abort without formatting any message panic_immediate_abort = ["core/panic_immediate_abort", "alloc/panic_immediate_abort"] +# Choose algorithms that are optimized for binary size instead of runtime performance +optimize_for_size = ["core/optimize_for_size", "alloc/optimize_for_size"] # Enable std_detect default features for stdarch/crates/std_detect: # https://github.com/rust-lang/stdarch/blob/master/crates/std_detect/Cargo.toml @@ -99,3 +97,16 @@ heap_size = 0x8000000 name = "stdbenches" path = "benches/lib.rs" test = true + +[lints.rust.unexpected_cfgs] +level = "warn" +# x.py uses beta cargo, so `check-cfg` entries do not yet take effect +# for rust-lang/rust. But for users of `-Zbuild-std` it does. +check-cfg = [ + 'cfg(bootstrap)', + 'cfg(target_arch, values("xtensa"))', + # std use #[path] imports to portable-simd `std_float` crate + # and to the `backtrace` crate which messes-up with Cargo list + # of declared features, we therefor expect any feature cfg + 'cfg(feature, values(any()))', +] diff --git a/library/std/build.rs b/library/std/build.rs index 7a47b52e8e4e..7d975df545ec 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -7,9 +7,13 @@ fn main() { let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").expect("CARGO_CFG_TARGET_VENDOR was not set"); let target_env = env::var("CARGO_CFG_TARGET_ENV").expect("CARGO_CFG_TARGET_ENV was not set"); + + println!("cargo:rustc-check-cfg=cfg(netbsd10)"); if target_os == "netbsd" && env::var("RUSTC_STD_NETBSD10").is_ok() { println!("cargo:rustc-cfg=netbsd10"); } + + println!("cargo:rustc-check-cfg=cfg(restricted_std)"); if target_os == "linux" || target_os == "android" || target_os == "netbsd" @@ -59,8 +63,11 @@ fn main() { // - arch=avr // - JSON targets // - Any new targets that have not been explicitly added above. - println!("cargo:rustc-cfg=feature=\"restricted-std\""); + println!("cargo:rustc-cfg=restricted_std"); } - println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap()); + + println!("cargo:rustc-check-cfg=cfg(backtrace_in_libstd)"); println!("cargo:rustc-cfg=backtrace_in_libstd"); + + println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap()); } diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index b98fbbf762fa..dc4924cdf581 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -73,7 +73,9 @@ pub use alloc_crate::alloc::*; /// work, such as to serve alignment requests greater than the alignment /// provided directly by the backing system allocator. /// -/// This type implements the `GlobalAlloc` trait and Rust programs by default +/// This type implements the [`GlobalAlloc`] trait. Currently the default +/// global allocator is unspecified. Libraries, however, like `cdylib`s and +/// `staticlib`s are guaranteed to use the [`System`] by default and as such /// work as if they had this definition: /// /// ```rust diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs index 475b3e7eb931..4d376753cb6d 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -95,7 +95,7 @@ use crate::fmt; use crate::panic::UnwindSafe; use crate::sync::atomic::{AtomicU8, Ordering::Relaxed}; use crate::sync::LazyLock; -use crate::sys_common::backtrace::{lock, output_filename, set_image_base}; +use crate::sys::backtrace::{lock, output_filename, set_image_base}; /// A captured OS thread stack backtrace. /// @@ -428,39 +428,43 @@ impl fmt::Display for Backtrace { } } -type LazyResolve = impl (FnOnce() -> Capture) + Send + Sync + UnwindSafe; +mod helper { + use super::*; + pub(super) type LazyResolve = impl (FnOnce() -> Capture) + Send + Sync + UnwindSafe; -fn lazy_resolve(mut capture: Capture) -> LazyResolve { - move || { - // Use the global backtrace lock to synchronize this as it's a - // requirement of the `backtrace` crate, and then actually resolve - // everything. - let _lock = lock(); - for frame in capture.frames.iter_mut() { - let symbols = &mut frame.symbols; - let frame = match &frame.frame { - RawFrame::Actual(frame) => frame, - #[cfg(test)] - RawFrame::Fake => unimplemented!(), - }; - unsafe { - backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { - symbols.push(BacktraceSymbol { - name: symbol.name().map(|m| m.as_bytes().to_vec()), - filename: symbol.filename_raw().map(|b| match b { - BytesOrWideString::Bytes(b) => BytesOrWide::Bytes(b.to_owned()), - BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()), - }), - lineno: symbol.lineno(), - colno: symbol.colno(), + pub(super) fn lazy_resolve(mut capture: Capture) -> LazyResolve { + move || { + // Use the global backtrace lock to synchronize this as it's a + // requirement of the `backtrace` crate, and then actually resolve + // everything. + let _lock = lock(); + for frame in capture.frames.iter_mut() { + let symbols = &mut frame.symbols; + let frame = match &frame.frame { + RawFrame::Actual(frame) => frame, + #[cfg(test)] + RawFrame::Fake => unimplemented!(), + }; + unsafe { + backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { + symbols.push(BacktraceSymbol { + name: symbol.name().map(|m| m.as_bytes().to_vec()), + filename: symbol.filename_raw().map(|b| match b { + BytesOrWideString::Bytes(b) => BytesOrWide::Bytes(b.to_owned()), + BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()), + }), + lineno: symbol.lineno(), + colno: symbol.colno(), + }); }); - }); + } } - } - capture + capture + } } } +use helper::*; impl RawFrame { fn ip(&self) -> *mut c_void { diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 5039f0b6bb28..fcd1c307b5af 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -1218,7 +1218,7 @@ where /// will cause the map to produce seemingly random results. Higher-level and /// more foolproof APIs like `entry` should be preferred when possible. /// - /// In particular, the hash used to initialized the raw entry must still be + /// In particular, the hash used to initialize the raw entry must still be /// consistent with the hash of the key that is ultimately stored in the entry. /// This is because implementations of HashMap may need to recompute hashes /// when resizing, at which point only the keys are available. diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 6f8ac17f12c7..2f35e721610e 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -318,22 +318,27 @@ impl Error for VarError { /// /// # Safety /// -/// Even though this function is currently not marked as `unsafe`, it needs to -/// be because invoking it can cause undefined behaviour. The function will be -/// marked `unsafe` in a future version of Rust. This is tracked in -/// [rust#27970](https://github.com/rust-lang/rust/issues/27970). -/// /// This function is safe to call in a single-threaded program. /// -/// In multi-threaded programs, you must ensure that are no other threads -/// concurrently writing or *reading*(!) from the environment through functions -/// other than the ones in this module. You are responsible for figuring out -/// how to achieve this, but we strongly suggest not using `set_var` or -/// `remove_var` in multi-threaded programs at all. +/// This function is also always safe to call on Windows, in single-threaded +/// and multi-threaded programs. /// -/// Most C libraries, including libc itself do not advertise which functions -/// read from the environment. Even functions from the Rust standard library do -/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`]. +/// In multi-threaded programs on other operating systems, the only safe option is +/// to not use `set_var` or `remove_var` at all. +/// +/// The exact requirement is: you +/// must ensure that there are no other threads concurrently writing or +/// *reading*(!) the environment through functions or global variables other +/// than the ones in this module. The problem is that these operating systems +/// do not provide a thread-safe way to read the environment, and most C +/// libraries, including libc itself, do not advertise which functions read +/// from the environment. Even functions from the Rust standard library may +/// read the environment without going through this module, e.g. for DNS +/// lookups from [`std::net::ToSocketAddrs`]. No stable guarantee is made about +/// which functions may read from the environment in future versions of a +/// library. All this makes it not practically possible for you to guarantee +/// that no other thread will read the environment, so the only safe option is +/// to not use `set_var` or `remove_var` in multi-threaded programs at all. /// /// Discussion of this unsafety on Unix may be found in: /// @@ -353,15 +358,18 @@ impl Error for VarError { /// use std::env; /// /// let key = "KEY"; -/// env::set_var(key, "VALUE"); +/// unsafe { +/// env::set_var(key, "VALUE"); +/// } /// assert_eq!(env::var(key), Ok("VALUE".to_string())); /// ``` +#[rustc_deprecated_safe_2024] #[stable(feature = "env", since = "1.0.0")] -pub fn set_var, V: AsRef>(key: K, value: V) { +pub unsafe fn set_var, V: AsRef>(key: K, value: V) { _set_var(key.as_ref(), value.as_ref()) } -fn _set_var(key: &OsStr, value: &OsStr) { +unsafe fn _set_var(key: &OsStr, value: &OsStr) { os_imp::setenv(key, value).unwrap_or_else(|e| { panic!("failed to set environment variable `{key:?}` to `{value:?}`: {e}") }) @@ -371,22 +379,27 @@ fn _set_var(key: &OsStr, value: &OsStr) { /// /// # Safety /// -/// Even though this function is currently not marked as `unsafe`, it needs to -/// be because invoking it can cause undefined behaviour. The function will be -/// marked `unsafe` in a future version of Rust. This is tracked in -/// [rust#27970](https://github.com/rust-lang/rust/issues/27970). -/// /// This function is safe to call in a single-threaded program. /// -/// In multi-threaded programs, you must ensure that are no other threads -/// concurrently writing or *reading*(!) from the environment through functions -/// other than the ones in this module. You are responsible for figuring out -/// how to achieve this, but we strongly suggest not using `set_var` or -/// `remove_var` in multi-threaded programs at all. +/// This function is also always safe to call on Windows, in single-threaded +/// and multi-threaded programs. /// -/// Most C libraries, including libc itself do not advertise which functions -/// read from the environment. Even functions from the Rust standard library do -/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`]. +/// In multi-threaded programs on other operating systems, the only safe option is +/// to not use `set_var` or `remove_var` at all. +/// +/// The exact requirement is: you +/// must ensure that there are no other threads concurrently writing or +/// *reading*(!) the environment through functions or global variables other +/// than the ones in this module. The problem is that these operating systems +/// do not provide a thread-safe way to read the environment, and most C +/// libraries, including libc itself, do not advertise which functions read +/// from the environment. Even functions from the Rust standard library may +/// read the environment without going through this module, e.g. for DNS +/// lookups from [`std::net::ToSocketAddrs`]. No stable guarantee is made about +/// which functions may read from the environment in future versions of a +/// library. All this makes it not practically possible for you to guarantee +/// that no other thread will read the environment, so the only safe option is +/// to not use `set_var` or `remove_var` in multi-threaded programs at all. /// /// Discussion of this unsafety on Unix may be found in: /// @@ -403,22 +416,27 @@ fn _set_var(key: &OsStr, value: &OsStr) { /// /// # Examples /// -/// ``` +/// ```no_run /// use std::env; /// /// let key = "KEY"; -/// env::set_var(key, "VALUE"); +/// unsafe { +/// env::set_var(key, "VALUE"); +/// } /// assert_eq!(env::var(key), Ok("VALUE".to_string())); /// -/// env::remove_var(key); +/// unsafe { +/// env::remove_var(key); +/// } /// assert!(env::var(key).is_err()); /// ``` +#[rustc_deprecated_safe_2024] #[stable(feature = "env", since = "1.0.0")] -pub fn remove_var>(key: K) { +pub unsafe fn remove_var>(key: K) { _remove_var(key.as_ref()) } -fn _remove_var(key: &OsStr) { +unsafe fn _remove_var(key: &OsStr) { os_imp::unsetenv(key) .unwrap_or_else(|e| panic!("failed to remove environment variable `{key:?}`: {e}")) } diff --git a/library/std/src/error.rs b/library/std/src/error.rs index b240e4e2c45b..87aad8f764bd 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -429,7 +429,7 @@ impl Report { /// 1: rust_out::main::_doctest_main_src_error_rs_1158_0 /// 2: rust_out::main /// 3: core::ops::function::FnOnce::call_once - /// 4: std::sys_common::backtrace::__rust_begin_short_backtrace + /// 4: std::sys::backtrace::__rust_begin_short_backtrace /// 5: std::rt::lang_start::{{closure}} /// 6: std::panicking::try /// 7: std::rt::lang_start_internal diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 9dd3d7d3fa16..f6b9de26c1c4 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -533,6 +533,25 @@ impl OsString { unsafe { Box::from_raw(rw) } } + /// Consumes and leaks the `OsString`, returning a mutable reference to the contents, + /// `&'a mut OsStr`. + /// + /// The caller has free choice over the returned lifetime, including 'static. + /// Indeed, this function is ideally used for data that lives for the remainder of + /// the program’s life, as dropping the returned reference will cause a memory leak. + /// + /// It does not reallocate or shrink the `OsString`, so the leaked allocation may include + /// unused capacity that is not part of the returned slice. If you want to discard excess + /// capacity, call [`into_boxed_os_str`], and then [`Box::leak`] instead. + /// However, keep in mind that trimming the capacity may result in a reallocation and copy. + /// + /// [`into_boxed_os_str`]: Self::into_boxed_os_str + #[unstable(feature = "os_string_pathbuf_leak", issue = "125965")] + #[inline] + pub fn leak<'a>(self) -> &'a mut OsStr { + OsStr::from_inner_mut(self.inner.leak()) + } + /// Part of a hack to make PathBuf::push/pop more efficient. #[inline] pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec { diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs index b020e05eaab2..5b39b9e34d8c 100644 --- a/library/std/src/ffi/os_str/tests.rs +++ b/library/std/src/ffi/os_str/tests.rs @@ -23,6 +23,15 @@ fn test_os_string_clear() { assert_eq!(0, os_string.inner.as_inner().len()); } +#[test] +fn test_os_string_leak() { + let os_string = OsString::from("have a cake"); + let (len, cap) = (os_string.len(), os_string.capacity()); + let leaked = os_string.leak(); + assert_eq!(leaked.as_encoded_bytes(), b"have a cake"); + unsafe { drop(String::from_raw_parts(leaked as *mut OsStr as _, len, cap)) } +} + #[test] fn test_os_string_capacity() { let os_string = OsString::with_capacity(0); diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 77e94365b08e..cf9a3446522c 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -767,11 +767,33 @@ fn buffer_capacity_required(mut file: &File) -> Option { #[stable(feature = "rust1", since = "1.0.0")] impl Read for &File { + /// Read some bytes from the file. + /// + /// See [`Read::read`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `read` function on Unix and + /// the `NtReadFile` function on Windows. Note that this [may change in + /// the future][changes]. + /// + /// [changes]: io#platform-specific-behavior #[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result { self.inner.read(buf) } + /// Like `read`, except that it reads into a slice of buffers. + /// + /// See [`Read::read_vectored`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `readv` function on Unix and + /// falls back to the `read` implementation on Windows. Note that this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior #[inline] fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { self.inner.read_vectored(bufs) @@ -782,6 +804,16 @@ impl Read for &File { self.inner.read_buf(cursor) } + /// Determines if `File` has an efficient `read_vectored` implementation. + /// + /// See [`Read::is_read_vectored`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently returns `true` on Unix an `false` on Windows. + /// Note that this [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior #[inline] fn is_read_vectored(&self) -> bool { self.inner.is_read_vectored() @@ -803,19 +835,63 @@ impl Read for &File { } #[stable(feature = "rust1", since = "1.0.0")] impl Write for &File { + /// Write some bytes from the file. + /// + /// See [`Write::write`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `write` function on Unix and + /// the `NtWriteFile` function on Windows. Note that this [may change in + /// the future][changes]. + /// + /// [changes]: io#platform-specific-behavior fn write(&mut self, buf: &[u8]) -> io::Result { self.inner.write(buf) } + /// Like `write`, except that it writes into a slice of buffers. + /// + /// See [`Write::write_vectored`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `writev` function on Unix + /// and falls back to the `write` implementation on Windows. Note that this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { self.inner.write_vectored(bufs) } + /// Determines if `File` has an efficient `write_vectored` implementation. + /// + /// See [`Write::is_write_vectored`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently returns `true` on Unix an `false` on Windows. + /// Note that this [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior #[inline] fn is_write_vectored(&self) -> bool { self.inner.is_write_vectored() } + /// Flushes the file, ensuring that all intermediately buffered contents + /// reach their destination. + /// + /// See [`Write::flush`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// Since a `File` structure doesn't contain any buffers, this function is + /// currently a no-op on Unix and Windows. Note that this [may change in + /// the future][changes]. + /// + /// [changes]: io#platform-specific-behavior #[inline] fn flush(&mut self) -> io::Result<()> { self.inner.flush() @@ -2226,7 +2302,7 @@ pub fn read_link>(path: P) -> io::Result { /// /// This function currently corresponds to the `realpath` function on Unix /// and the `CreateFile` and `GetFinalPathNameByHandle` functions on Windows. -/// Note that, this [may change in the future][changes]. +/// Note that this [may change in the future][changes]. /// /// On Windows, this converts the path to use [extended length path][path] /// syntax, which allows your program to use longer path names, but means you @@ -2310,6 +2386,9 @@ pub fn create_dir>(path: P) -> io::Result<()> { /// If this function returns an error, some of the parent components might have /// been created already. /// +/// If the empty path is passed to this function, it always succeeds without +/// creating any directories. +/// /// # Platform-specific behavior /// /// This function currently corresponds to multiple calls to the `mkdir` diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index dfa05671ab0f..62a268facb63 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1431,7 +1431,7 @@ fn metadata_access_times() { assert_eq!(check!(a.modified()), check!(a.modified())); assert_eq!(check!(b.accessed()), check!(b.modified())); - if cfg!(target_os = "macos") || cfg!(target_os = "windows") { + if cfg!(target_vendor = "apple") || cfg!(target_os = "windows") { check!(a.created()); check!(b.created()); } diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 37492e9efab7..a1a8b2a3505c 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -328,7 +328,7 @@ where fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { let prev_written = cursor.written(); - Read::read_buf(&mut self.fill_buf()?, cursor.reborrow())?; + Read::read_buf(&mut self.remaining_slice(), cursor.reborrow())?; self.pos += (cursor.written() - prev_written) as u64; @@ -352,17 +352,24 @@ where } fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - let n = buf.len(); - Read::read_exact(&mut self.remaining_slice(), buf)?; - self.pos += n as u64; - Ok(()) + let result = Read::read_exact(&mut self.remaining_slice(), buf); + + match result { + Ok(_) => self.pos += buf.len() as u64, + // The only possible error condition is EOF, so place the cursor at "EOF" + Err(_) => self.pos = self.inner.as_ref().len() as u64, + } + + result } - fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - let n = cursor.capacity(); - Read::read_buf_exact(&mut self.remaining_slice(), cursor)?; - self.pos += n as u64; - Ok(()) + fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + let prev_written = cursor.written(); + + let result = Read::read_buf_exact(&mut self.remaining_slice(), cursor.reborrow()); + self.pos += (cursor.written() - prev_written) as u64; + + result } fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index 6f8d5e377756..c9d3934ad70c 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -104,7 +104,6 @@ use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; use core::marker::PhantomData; -use core::mem::{align_of, size_of}; use core::ptr::{self, NonNull}; // The 2 least-significant bits are used as tag. diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 46f04c7cd395..a8a2e9413e11 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -287,6 +287,9 @@ impl Read for &[u8] { #[inline] fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { if buf.len() > self.len() { + // `read_exact` makes no promise about the content of `buf` if it + // fails so don't bother about that. + *self = &self[self.len()..]; return Err(io::Error::READ_EXACT_EOF); } let (a, b) = self.split_at(buf.len()); @@ -307,6 +310,9 @@ impl Read for &[u8] { #[inline] fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { if cursor.capacity() > self.len() { + // Append everything we can to the cursor. + cursor.append(*self); + *self = &self[self.len()..]; return Err(io::Error::READ_EXACT_EOF); } let (a, b) = self.split_at(cursor.capacity()); diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index f55ec1588f91..97b72f9664bb 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2058,7 +2058,7 @@ pub trait Seek { /// ``` /// /// [`BufReader`]: crate::io::BufReader - #[stable(feature = "seek_seek_relative", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "seek_seek_relative", since = "1.80.0")] fn seek_relative(&mut self, offset: i64) -> Result<()> { self.seek(SeekFrom::Current(offset))?; Ok(()) diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 07fa9259e0b7..9aee2bb5e1c5 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -1161,7 +1161,40 @@ pub trait IsTerminal: crate::sealed::Sealed { /// starting with `msys-` or `cygwin-` and ending in `-pty` will be considered terminals. /// Note that this [may change in the future][changes]. /// + /// # Examples + /// + /// An example of a type for which `IsTerminal` is implemented is [`Stdin`]: + /// + /// ```no_run + /// use std::io::{self, IsTerminal, Write}; + /// + /// fn main() -> io::Result<()> { + /// let stdin = io::stdin(); + /// + /// // Indicate that the user is prompted for input, if this is a terminal. + /// if stdin.is_terminal() { + /// print!("> "); + /// io::stdout().flush()?; + /// } + /// + /// let mut name = String::new(); + /// let _ = stdin.read_line(&mut name)?; + /// + /// println!("Hello {}", name.trim_end()); + /// + /// Ok(()) + /// } + /// ``` + /// + /// The example can be run in two ways: + /// + /// - If you run this example by piping some text to it, e.g. `echo "foo" | path/to/executable` + /// it will print: `Hello foo`. + /// - If you instead run the example interactively by running `path/to/executable` directly, it will + /// prompt for input. + /// /// [changes]: io#platform-specific-behavior + /// [`Stdin`]: crate::io::Stdin #[stable(feature = "is_terminal", since = "1.70.0")] fn is_terminal(&self) -> bool; } diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index 090a091b09a1..a2c1c430863a 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -653,6 +653,38 @@ fn test_take_wrong_length() { let _ = reader.read(&mut buffer[..]); } +#[test] +fn slice_read_exact_eof() { + let slice = &b"123456"[..]; + + let mut r = slice; + assert!(r.read_exact(&mut [0; 10]).is_err()); + assert!(r.is_empty()); + + let mut r = slice; + let buf = &mut [0; 10]; + let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + assert!(r.read_buf_exact(buf.unfilled()).is_err()); + assert!(r.is_empty()); + assert_eq!(buf.filled(), b"123456"); +} + +#[test] +fn cursor_read_exact_eof() { + let slice = Cursor::new(b"123456"); + + let mut r = slice.clone(); + assert!(r.read_exact(&mut [0; 10]).is_err()); + assert!(r.is_empty()); + + let mut r = slice; + let buf = &mut [0; 10]; + let mut buf = BorrowedBuf::from(buf.as_mut_slice()); + assert!(r.read_buf_exact(buf.unfilled()).is_err()); + assert!(r.is_empty()); + assert_eq!(buf.filled(), b"123456"); +} + #[bench] fn bench_take_read(b: &mut test::Bencher) { b.iter(|| { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 27b46b462044..1c226f9f08f1 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -213,9 +213,9 @@ //! [array]: prim@array //! [slice]: prim@slice -#![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))] +#![cfg_attr(not(restricted_std), stable(feature = "rust1", since = "1.0.0"))] #![cfg_attr( - feature = "restricted-std", + restricted_std, unstable( feature = "restricted_std", issue = "none", @@ -324,7 +324,6 @@ #![feature(core_io_borrowed_buf)] #![feature(duration_constants)] #![feature(error_generic_member_access)] -#![feature(error_in_core)] #![feature(error_iter)] #![feature(exact_size_is_empty)] #![feature(exclusive_wrapper)] @@ -395,7 +394,6 @@ #![feature(edition_panic)] #![feature(format_args_nl)] #![feature(get_many_mut)] -#![feature(lazy_cell)] #![feature(log_syntax)] #![feature(test)] #![feature(trace_macros)] @@ -435,6 +433,7 @@ extern crate alloc as alloc_crate; // so include it here even if it's unused. #[doc(masked)] #[allow(unused_extern_crates)] +#[cfg(not(all(windows, target_env = "msvc")))] extern crate libc; // We always need an unwinder currently for backtraces diff --git a/library/std/src/os/hermit/io/mod.rs b/library/std/src/os/hermit/io/mod.rs index 524dfae0d63a..df93f63a003c 100644 --- a/library/std/src/os/hermit/io/mod.rs +++ b/library/std/src/os/hermit/io/mod.rs @@ -1,13 +1,4 @@ -#![stable(feature = "os_fd", since = "1.66.0")] +#![stable(feature = "rust1", since = "1.0.0")] -mod net; -#[path = "../../fd/owned.rs"] -mod owned; -#[path = "../../fd/raw.rs"] -mod raw; - -// Export the types and traits for the public API. -#[stable(feature = "os_fd", since = "1.66.0")] -pub use owned::*; -#[stable(feature = "os_fd", since = "1.66.0")] -pub use raw::*; +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::os::fd::*; diff --git a/library/std/src/os/hermit/mod.rs b/library/std/src/os/hermit/mod.rs index 89b1b831912d..02a4b2c3ab5e 100644 --- a/library/std/src/os/hermit/mod.rs +++ b/library/std/src/os/hermit/mod.rs @@ -2,7 +2,7 @@ #[allow(unused_extern_crates)] #[stable(feature = "rust1", since = "1.0.0")] -pub extern crate hermit_abi as abi; +pub extern crate hermit_abi; pub mod ffi; pub mod io; diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs index c29dd62bc06f..d53674d3c5f2 100644 --- a/library/std/src/os/linux/raw.rs +++ b/library/std/src/os/linux/raw.rs @@ -244,7 +244,11 @@ mod arch { pub use libc::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; } -#[cfg(target_arch = "aarch64")] +#[cfg(any( + target_arch = "aarch64", + // Arm64EC is Windows-only, but docs are always build as Linux, so re-use AArch64 for Arm64EC. + all(doc, target_arch = "arm64ec") +))] mod arch { use crate::os::raw::{c_int, c_long}; diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index ca3584e82f91..d2a7b316b813 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -160,7 +160,7 @@ pub(crate) mod watchos; #[cfg(target_os = "xous")] pub mod xous; -#[cfg(any(unix, target_os = "wasi", doc))] +#[cfg(any(unix, target_os = "hermit", target_os = "wasi", doc))] pub mod fd; #[cfg(any(target_os = "linux", target_os = "android", doc))] diff --git a/library/std/src/os/raw/tests.rs b/library/std/src/os/raw/tests.rs index e7bb7d7e73e8..f41a22e1bcce 100644 --- a/library/std/src/os/raw/tests.rs +++ b/library/std/src/os/raw/tests.rs @@ -1,3 +1,5 @@ +#![cfg(not(all(windows, target_env = "msvc")))] + use crate::any::TypeId; macro_rules! ok { diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs index 0597fdcbd728..fe8e2be93724 100644 --- a/library/std/src/os/unix/net/ancillary.rs +++ b/library/std/src/os/unix/net/ancillary.rs @@ -3,7 +3,7 @@ use super::{sockaddr_un, SocketAddr}; use crate::io::{self, IoSlice, IoSliceMut}; use crate::marker::PhantomData; -use crate::mem::{size_of, zeroed}; +use crate::mem::zeroed; use crate::os::unix::io::RawFd; use crate::path::Path; use crate::ptr::{eq, read_unaligned}; diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 15ab22501223..9cca27fa5dd5 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -199,14 +199,14 @@ pub trait CommandExt: Sealed { /// Append literal text to the command line without any quoting or escaping. /// - /// This is useful for passing arguments to applications which doesn't follow + /// This is useful for passing arguments to applications that don't follow /// the standard C run-time escaping rules, such as `cmd.exe /c`. /// - /// # Bat files + /// # Batch files /// - /// Note the `cmd /c` command line has slightly different escaping rules then bat files + /// Note the `cmd /c` command line has slightly different escaping rules than batch files /// themselves. If possible, it may be better to write complex arguments to a temporary - /// .bat file, with appropriate escaping, and simply run that using: + /// `.bat` file, with appropriate escaping, and simply run that using: /// /// ```no_run /// # use std::process::Command; @@ -217,7 +217,7 @@ pub trait CommandExt: Sealed { /// /// # Example /// - /// Run a bat script using both trusted and untrusted arguments. + /// Run a batch script using both trusted and untrusted arguments. /// /// ```no_run /// #[cfg(windows)] @@ -241,9 +241,10 @@ pub trait CommandExt: Sealed { /// if !user_name.chars().all(|c| c.is_alphanumeric()) { /// return Err(Error::new(ErrorKind::InvalidInput, "invalid user name")); /// } - /// // now we've checked the user name, let's add that too. - /// cmd_args.push(' '); - /// cmd_args.push_str(&format!("--user {user_name}")); + /// + /// // now we have validated the user name, let's add that too. + /// cmd_args.push_str(" --user "); + /// cmd_args.push_str(user_name); /// /// // call cmd.exe and return the output /// Command::new("cmd.exe") @@ -287,25 +288,37 @@ pub trait CommandExt: Sealed { #[unstable(feature = "windows_process_extensions_async_pipes", issue = "98289")] fn async_pipes(&mut self, always_async: bool) -> &mut process::Command; - /// Sets a raw attribute on the command, providing extended configuration options for Windows processes. + /// Set a raw attribute on the command, providing extended configuration options for Windows + /// processes. /// - /// This method allows you to specify custom attributes for a child process on Windows systems using raw attribute values. - /// Raw attributes provide extended configurability for process creation, but their usage can be complex and potentially unsafe. + /// This method allows you to specify custom attributes for a child process on Windows systems + /// using raw attribute values. Raw attributes provide extended configurability for process + /// creation, but their usage can be complex and potentially unsafe. /// - /// The `attribute` parameter specifies the raw attribute to be set, while the `value` parameter holds the value associated with that attribute. - /// Please refer to the [`windows-rs`](https://microsoft.github.io/windows-docs-rs/doc/windows/) documentation or the [`Win32 API documentation`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute) for detailed information about available attributes and their meanings. + /// The `attribute` parameter specifies the raw attribute to be set, while the `value` + /// parameter holds the value associated with that attribute. Please refer to the + /// [`windows-rs` documentation] or the [Win32 API documentation] for detailed information + /// about available attributes and their meanings. + /// + /// [`windows-rs` documentation]: https://microsoft.github.io/windows-docs-rs/doc/windows/ + /// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute /// /// # Note /// /// The maximum number of raw attributes is the value of [`u32::MAX`]. - /// If this limit is exceeded, the call to [`process::Command::spawn`] will return an `Error` indicating that the maximum number of attributes has been exceeded. + /// If this limit is exceeded, the call to [`process::Command::spawn`] will return an `Error` + /// indicating that the maximum number of attributes has been exceeded. + /// /// # Safety /// - /// The usage of raw attributes is potentially unsafe and should be done with caution. Incorrect attribute values or improper configuration can lead to unexpected behavior or errors. + /// The usage of raw attributes is potentially unsafe and should be done with caution. + /// Incorrect attribute values or improper configuration can lead to unexpected behavior or + /// errors. /// /// # Example /// - /// The following example demonstrates how to create a child process with a specific parent process ID using a raw attribute. + /// The following example demonstrates how to create a child process with a specific parent + /// process ID using a raw attribute. /// /// ```rust /// #![feature(windows_process_extensions_raw_attribute)] @@ -339,7 +352,9 @@ pub trait CommandExt: Sealed { /// /// # Safety Note /// - /// Remember that improper use of raw attributes can lead to undefined behavior or security vulnerabilities. Always consult the documentation and ensure proper attribute values are used. + /// Remember that improper use of raw attributes can lead to undefined behavior or security + /// vulnerabilities. Always consult the documentation and ensure proper attribute values are + /// used. #[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] unsafe fn raw_attribute( &mut self, diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index e63b46ab7054..c5d1a893ee80 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -4,11 +4,212 @@ use crate::any::Any; use crate::collections; +use crate::fmt; use crate::panicking; use crate::sync::atomic::{AtomicU8, Ordering}; use crate::sync::{Condvar, Mutex, RwLock}; use crate::thread::Result; +#[stable(feature = "panic_hooks", since = "1.10.0")] +#[deprecated( + since = "1.82.0", + note = "use `PanicHookInfo` instead", + suggestion = "std::panic::PanicHookInfo" +)] +/// A struct providing information about a panic. +/// +/// `PanicInfo` has been renamed to [`PanicHookInfo`] to avoid confusion with +/// [`core::panic::PanicInfo`]. +pub type PanicInfo<'a> = PanicHookInfo<'a>; + +/// A struct providing information about a panic. +/// +/// `PanicHookInfo` structure is passed to a panic hook set by the [`set_hook`] function. +/// +/// # Examples +/// +/// ```should_panic +/// use std::panic; +/// +/// panic::set_hook(Box::new(|panic_info| { +/// println!("panic occurred: {panic_info}"); +/// })); +/// +/// panic!("critical system failure"); +/// ``` +/// +/// [`set_hook`]: ../../std/panic/fn.set_hook.html +#[stable(feature = "panic_hook_info", since = "CURRENT_RUSTC_VERSION")] +#[derive(Debug)] +pub struct PanicHookInfo<'a> { + payload: &'a (dyn Any + Send), + location: &'a Location<'a>, + can_unwind: bool, + force_no_backtrace: bool, +} + +impl<'a> PanicHookInfo<'a> { + #[inline] + pub(crate) fn new( + location: &'a Location<'a>, + payload: &'a (dyn Any + Send), + can_unwind: bool, + force_no_backtrace: bool, + ) -> Self { + PanicHookInfo { payload, location, can_unwind, force_no_backtrace } + } + + /// Returns the payload associated with the panic. + /// + /// This will commonly, but not always, be a `&'static str` or [`String`]. + /// + /// A invocation of the `panic!()` macro in Rust 2021 or later will always result in a + /// panic payload of type `&'static str` or `String`. + /// + /// Only an invocation of [`panic_any`] + /// (or, in Rust 2018 and earlier, `panic!(x)` where `x` is something other than a string) + /// can result in a panic payload other than a `&'static str` or `String`. + /// + /// [`String`]: ../../std/string/struct.String.html + /// + /// # Examples + /// + /// ```should_panic + /// use std::panic; + /// + /// panic::set_hook(Box::new(|panic_info| { + /// if let Some(s) = panic_info.payload().downcast_ref::<&str>() { + /// println!("panic occurred: {s:?}"); + /// } else if let Some(s) = panic_info.payload().downcast_ref::() { + /// println!("panic occurred: {s:?}"); + /// } else { + /// println!("panic occurred"); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` + #[must_use] + #[inline] + #[stable(feature = "panic_hooks", since = "1.10.0")] + pub fn payload(&self) -> &(dyn Any + Send) { + self.payload + } + + /// Returns the payload associated with the panic, if it is a string. + /// + /// This returns the payload if it is of type `&'static str` or `String`. + /// + /// A invocation of the `panic!()` macro in Rust 2021 or later will always result in a + /// panic payload where `payload_as_str` returns `Some`. + /// + /// Only an invocation of [`panic_any`] + /// (or, in Rust 2018 and earlier, `panic!(x)` where `x` is something other than a string) + /// can result in a panic payload where `payload_as_str` returns `None`. + /// + /// # Example + /// + /// ```should_panic + /// #![feature(panic_payload_as_str)] + /// + /// std::panic::set_hook(Box::new(|panic_info| { + /// if let Some(s) = panic_info.payload_as_str() { + /// println!("panic occurred: {s:?}"); + /// } else { + /// println!("panic occurred"); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "panic_payload_as_str", issue = "125175")] + pub fn payload_as_str(&self) -> Option<&str> { + if let Some(s) = self.payload.downcast_ref::<&str>() { + Some(s) + } else if let Some(s) = self.payload.downcast_ref::() { + Some(s) + } else { + None + } + } + + /// Returns information about the location from which the panic originated, + /// if available. + /// + /// This method will currently always return [`Some`], but this may change + /// in future versions. + /// + /// # Examples + /// + /// ```should_panic + /// use std::panic; + /// + /// panic::set_hook(Box::new(|panic_info| { + /// if let Some(location) = panic_info.location() { + /// println!("panic occurred in file '{}' at line {}", + /// location.file(), + /// location.line(), + /// ); + /// } else { + /// println!("panic occurred but can't get location information..."); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` + #[must_use] + #[inline] + #[stable(feature = "panic_hooks", since = "1.10.0")] + pub fn location(&self) -> Option<&Location<'_>> { + // NOTE: If this is changed to sometimes return None, + // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. + Some(&self.location) + } + + /// Returns whether the panic handler is allowed to unwind the stack from + /// the point where the panic occurred. + /// + /// This is true for most kinds of panics with the exception of panics + /// caused by trying to unwind out of a `Drop` implementation or a function + /// whose ABI does not support unwinding. + /// + /// It is safe for a panic handler to unwind even when this function returns + /// false, however this will simply cause the panic handler to be called + /// again. + #[must_use] + #[inline] + #[unstable(feature = "panic_can_unwind", issue = "92988")] + pub fn can_unwind(&self) -> bool { + self.can_unwind + } + + #[unstable( + feature = "panic_internals", + reason = "internal details of the implementation of the `panic!` and related macros", + issue = "none" + )] + #[doc(hidden)] + #[inline] + pub fn force_no_backtrace(&self) -> bool { + self.force_no_backtrace + } +} + +#[stable(feature = "panic_hook_display", since = "1.26.0")] +impl fmt::Display for PanicHookInfo<'_> { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("panicked at ")?; + self.location.fmt(formatter)?; + if let Some(payload) = self.payload_as_str() { + formatter.write_str(":\n")?; + formatter.write_str(payload)?; + } + Ok(()) + } +} + #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] #[allow_internal_unstable(libstd_sys_internals, const_format_args, panic_internals, rt)] @@ -43,7 +244,7 @@ pub use crate::panicking::{set_hook, take_hook}; pub use crate::panicking::update_hook; #[stable(feature = "panic_hooks", since = "1.10.0")] -pub use core::panic::{Location, PanicInfo}; +pub use core::panic::Location; #[stable(feature = "catch_unwind", since = "1.9.0")] pub use core::panic::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe}; @@ -53,7 +254,7 @@ pub use core::panic::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe}; /// The message can be of any (`Any + Send`) type, not just strings. /// /// The message is wrapped in a `Box<'static + Any + Send>`, which can be -/// accessed later using [`PanicInfo::payload`]. +/// accessed later using [`PanicHookInfo::payload`]. /// /// See the [`panic!`] macro for more information about panicking. #[stable(feature = "panic_any", since = "1.51.0")] diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 5699937cdb49..ebd054156951 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -9,8 +9,8 @@ #![deny(unsafe_op_in_unsafe_fn)] -use crate::panic::BacktraceStyle; -use core::panic::{Location, PanicInfo, PanicPayload}; +use crate::panic::{BacktraceStyle, PanicHookInfo}; +use core::panic::{Location, PanicPayload}; use crate::any::Any; use crate::fmt; @@ -19,8 +19,8 @@ use crate::mem::{self, ManuallyDrop}; use crate::process; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::{PoisonError, RwLock}; +use crate::sys::backtrace; use crate::sys::stdio::panic_output; -use crate::sys_common::backtrace; use crate::thread; #[cfg(not(test))] @@ -70,12 +70,12 @@ extern "C" fn __rust_foreign_exception() -> ! { enum Hook { Default, - Custom(Box) + 'static + Sync + Send>), + Custom(Box) + 'static + Sync + Send>), } impl Hook { #[inline] - fn into_box(self) -> Box) + 'static + Sync + Send> { + fn into_box(self) -> Box) + 'static + Sync + Send> { match self { Hook::Default => Box::new(default_hook), Hook::Custom(hook) => hook, @@ -105,7 +105,7 @@ static HOOK: RwLock = RwLock::new(Hook::Default); /// /// [`take_hook`]: ./fn.take_hook.html /// -/// The hook is provided with a `PanicInfo` struct which contains information +/// The hook is provided with a `PanicHookInfo` struct which contains information /// about the origin of the panic, including the payload passed to `panic!` and /// the source code location from which the panic originated. /// @@ -129,7 +129,7 @@ static HOOK: RwLock = RwLock::new(Hook::Default); /// panic!("Normal panic"); /// ``` #[stable(feature = "panic_hooks", since = "1.10.0")] -pub fn set_hook(hook: Box) + 'static + Sync + Send>) { +pub fn set_hook(hook: Box) + 'static + Sync + Send>) { if thread::panicking() { panic!("cannot modify the panic hook from a panicking thread"); } @@ -173,7 +173,7 @@ pub fn set_hook(hook: Box) + 'static + Sync + Send>) { /// ``` #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] -pub fn take_hook() -> Box) + 'static + Sync + Send> { +pub fn take_hook() -> Box) + 'static + Sync + Send> { if thread::panicking() { panic!("cannot modify the panic hook from a panicking thread"); } @@ -219,7 +219,7 @@ pub fn take_hook() -> Box) + 'static + Sync + Send> { #[unstable(feature = "panic_update_hook", issue = "92649")] pub fn update_hook(hook_fn: F) where - F: Fn(&(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static), &PanicInfo<'_>) + F: Fn(&(dyn Fn(&PanicHookInfo<'_>) + Send + Sync + 'static), &PanicHookInfo<'_>) + Sync + Send + 'static, @@ -234,7 +234,7 @@ where } /// The default panic handler. -fn default_hook(info: &PanicInfo<'_>) { +fn default_hook(info: &PanicHookInfo<'_>) { // If this is a double panic, make sure that we print a backtrace // for this panic. Otherwise only print it if logging is enabled. let backtrace = if info.force_no_backtrace() { @@ -248,13 +248,7 @@ fn default_hook(info: &PanicInfo<'_>) { // The current implementation always returns `Some`. let location = info.location().unwrap(); - let msg = match info.payload().downcast_ref::<&'static str>() { - Some(s) => *s, - None => match info.payload().downcast_ref::() { - Some(s) => &s[..], - None => "Box", - }, - }; + let msg = payload_as_str(info.payload()); let thread = thread::try_current(); let name = thread.as_ref().and_then(|t| t.name()).unwrap_or(""); @@ -597,31 +591,26 @@ pub fn panicking() -> bool { /// Entry point of panics from the core crate (`panic_impl` lang item). #[cfg(not(any(test, doctest)))] #[panic_handler] -pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { +pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { struct FormatStringPayload<'a> { - inner: &'a fmt::Arguments<'a>, + inner: &'a core::panic::PanicMessage<'a>, string: Option, } - impl<'a> FormatStringPayload<'a> { - fn new(inner: &'a fmt::Arguments<'a>) -> Self { - Self { inner, string: None } - } - + impl FormatStringPayload<'_> { fn fill(&mut self) -> &mut String { - use crate::fmt::Write; - let inner = self.inner; // Lazily, the first time this gets called, run the actual string formatting. self.string.get_or_insert_with(|| { let mut s = String::new(); - let _err = s.write_fmt(*inner); + let mut fmt = fmt::Formatter::new(&mut s); + let _err = fmt::Display::fmt(&inner, &mut fmt); s }) } } - unsafe impl<'a> PanicPayload for FormatStringPayload<'a> { + unsafe impl PanicPayload for FormatStringPayload<'_> { fn take_box(&mut self) -> *mut (dyn Any + Send) { // We do two allocations here, unfortunately. But (a) they're required with the current // scheme, and (b) we don't handle panic + OOM properly anyway (see comment in @@ -635,6 +624,16 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { } } + impl fmt::Display for FormatStringPayload<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(s) = &self.string { + f.write_str(s) + } else { + fmt::Display::fmt(&self.inner, f) + } + } + } + struct StaticStrPayload(&'static str); unsafe impl PanicPayload for StaticStrPayload { @@ -645,25 +644,31 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { fn get(&mut self) -> &(dyn Any + Send) { &self.0 } + + fn as_str(&mut self) -> Option<&str> { + Some(self.0) + } + } + + impl fmt::Display for StaticStrPayload { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.0) + } } let loc = info.location().unwrap(); // The current implementation always returns Some - let msg = info.message().unwrap(); // The current implementation always returns Some - crate::sys_common::backtrace::__rust_end_short_backtrace(move || { - // FIXME: can we just pass `info` along rather than taking it apart here, only to have - // `rust_panic_with_hook` construct a new `PanicInfo`? - if let Some(msg) = msg.as_str() { + let msg = info.message(); + crate::sys::backtrace::__rust_end_short_backtrace(move || { + if let Some(s) = msg.as_str() { rust_panic_with_hook( - &mut StaticStrPayload(msg), - info.message(), + &mut StaticStrPayload(s), loc, info.can_unwind(), info.force_no_backtrace(), ); } else { rust_panic_with_hook( - &mut FormatStringPayload::new(msg), - info.message(), + &mut FormatStringPayload { inner: &msg, string: None }, loc, info.can_unwind(), info.force_no_backtrace(), @@ -689,27 +694,10 @@ pub const fn begin_panic(msg: M) -> ! { intrinsics::abort() } - let loc = Location::caller(); - return crate::sys_common::backtrace::__rust_end_short_backtrace(move || { - rust_panic_with_hook( - &mut Payload::new(msg), - None, - loc, - /* can_unwind */ true, - /* force_no_backtrace */ false, - ) - }); - struct Payload { inner: Option, } - impl Payload { - fn new(inner: A) -> Payload { - Payload { inner: Some(inner) } - } - } - unsafe impl PanicPayload for Payload { fn take_box(&mut self) -> *mut (dyn Any + Send) { // Note that this should be the only allocation performed in this code path. Currently @@ -731,6 +719,35 @@ pub const fn begin_panic(msg: M) -> ! { } } } + + impl fmt::Display for Payload { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.inner { + Some(a) => f.write_str(payload_as_str(a)), + None => process::abort(), + } + } + } + + let loc = Location::caller(); + crate::sys::backtrace::__rust_end_short_backtrace(move || { + rust_panic_with_hook( + &mut Payload { inner: Some(msg) }, + loc, + /* can_unwind */ true, + /* force_no_backtrace */ false, + ) + }) +} + +fn payload_as_str(payload: &dyn Any) -> &str { + if let Some(&s) = payload.downcast_ref::<&'static str>() { + s + } else if let Some(s) = payload.downcast_ref::() { + s.as_str() + } else { + "Box" + } } /// Central point for dispatching panics. @@ -740,7 +757,6 @@ pub const fn begin_panic(msg: M) -> ! { /// abort or unwind. fn rust_panic_with_hook( payload: &mut dyn PanicPayload, - message: Option<&fmt::Arguments<'_>>, location: &Location<'_>, can_unwind: bool, force_no_backtrace: bool, @@ -754,35 +770,21 @@ fn rust_panic_with_hook( // Don't try to format the message in this case, perhaps that is causing the // recursive panics. However if the message is just a string, no user-defined // code is involved in printing it, so that is risk-free. - let msg_str = message.and_then(|m| m.as_str()).map(|m| [m]); - let message = msg_str.as_ref().map(|m| fmt::Arguments::new_const(m)); - let panicinfo = PanicInfo::internal_constructor( - message.as_ref(), - location, - can_unwind, - force_no_backtrace, + let message: &str = payload.as_str().unwrap_or_default(); + rtprintpanic!( + "panicked at {location}:\n{message}\nthread panicked while processing panic. aborting.\n" ); - rtprintpanic!("{panicinfo}\nthread panicked while processing panic. aborting.\n"); } panic_count::MustAbort::AlwaysAbort => { // Unfortunately, this does not print a backtrace, because creating // a `Backtrace` will allocate, which we must avoid here. - let panicinfo = PanicInfo::internal_constructor( - message, - location, - can_unwind, - force_no_backtrace, - ); - rtprintpanic!("{panicinfo}\npanicked after panic::always_abort(), aborting.\n"); + rtprintpanic!("aborting due to panic at {location}:\n{payload}\n"); } } crate::sys::abort_internal(); } - let mut info = - PanicInfo::internal_constructor(message, location, can_unwind, force_no_backtrace); - let hook = HOOK.read().unwrap_or_else(PoisonError::into_inner); - match *hook { + match *HOOK.read().unwrap_or_else(PoisonError::into_inner) { // Some platforms (like wasm) know that printing to stderr won't ever actually // print anything, and if that's the case we can skip the default // hook. Since string formatting happens lazily when calling `payload` @@ -791,15 +793,17 @@ fn rust_panic_with_hook( // formatting.) Hook::Default if panic_output().is_none() => {} Hook::Default => { - info.set_payload(payload.get()); - default_hook(&info); + default_hook(&PanicHookInfo::new( + location, + payload.get(), + can_unwind, + force_no_backtrace, + )); } Hook::Custom(ref hook) => { - info.set_payload(payload.get()); - hook(&info); + hook(&PanicHookInfo::new(location, payload.get(), can_unwind, force_no_backtrace)); } - }; - drop(hook); + } // Indicate that we have finished executing the panic hook. After this point // it is fine if there is a panic while executing destructors, as long as it @@ -835,6 +839,12 @@ pub fn rust_panic_without_hook(payload: Box) -> ! { } } + impl fmt::Display for RewrapBox { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(payload_as_str(&self.0)) + } + } + rust_panic(&mut RewrapBox(payload)) } diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 79d800ff0729..7b8caaa26847 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1226,6 +1226,25 @@ impl PathBuf { self } + /// Consumes and leaks the `PathBuf`, returning a mutable reference to the contents, + /// `&'a mut Path`. + /// + /// The caller has free choice over the returned lifetime, including 'static. + /// Indeed, this function is ideally used for data that lives for the remainder of + /// the program’s life, as dropping the returned reference will cause a memory leak. + /// + /// It does not reallocate or shrink the `PathBuf`, so the leaked allocation may include + /// unused capacity that is not part of the returned slice. If you want to discard excess + /// capacity, call [`into_boxed_path`], and then [`Box::leak`] instead. + /// However, keep in mind that trimming the capacity may result in a reallocation and copy. + /// + /// [`into_boxed_path`]: Self::into_boxed_path + #[unstable(feature = "os_string_pathbuf_leak", issue = "125965")] + #[inline] + pub fn leak<'a>(self) -> &'a mut Path { + Path::from_inner_mut(self.inner.leak()) + } + /// Extends `self` with `path`. /// /// If `path` is absolute, it replaces the current path. @@ -1425,6 +1444,11 @@ impl PathBuf { /// If `extension` is the empty string, [`self.extension`] will be [`None`] /// afterwards, not `Some("")`. /// + /// # Panics + /// + /// Panics if the passed extension contains a path separator (see + /// [`is_separator`]). + /// /// # Caveats /// /// The new `extension` may contain dots and will be used in its entirety, @@ -1470,6 +1494,14 @@ impl PathBuf { } fn _set_extension(&mut self, extension: &OsStr) -> bool { + for &b in extension.as_encoded_bytes() { + if b < 128 { + if is_separator(b as char) { + panic!("extension cannot contain path separators: {:?}", extension); + } + } + } + let file_stem = match self.file_stem() { None => return false, Some(f) => f.as_encoded_bytes(), @@ -3313,65 +3345,79 @@ impl Error for StripPrefixError { /// Makes the path absolute without accessing the filesystem. /// /// If the path is relative, the current directory is used as the base directory. -/// All intermediate components will be resolved according to platforms-specific -/// rules but unlike [`canonicalize`][crate::fs::canonicalize] this does not +/// All intermediate components will be resolved according to platform-specific +/// rules, but unlike [`canonicalize`][crate::fs::canonicalize], this does not /// resolve symlinks and may succeed even if the path does not exist. /// /// If the `path` is empty or getting the -/// [current directory][crate::env::current_dir] fails then an error will be +/// [current directory][crate::env::current_dir] fails, then an error will be /// returned. /// +/// # Platform-specific behavior +/// +/// On POSIX platforms, the path is resolved using [POSIX semantics][posix-semantics], +/// except that it stops short of resolving symlinks. This means it will keep `..` +/// components and trailing slashes. +/// +/// On Windows, for verbatim paths, this will simply return the path as given. For other +/// paths, this is currently equivalent to calling +/// [`GetFullPathNameW`][windows-path]. +/// +/// Note that these [may change in the future][changes]. +/// +/// # Errors +/// +/// This function may return an error in the following situations: +/// +/// * If `path` is syntactically invalid; in particular, if it is empty. +/// * If getting the [current directory][crate::env::current_dir] fails. +/// /// # Examples /// -/// ## Posix paths +/// ## POSIX paths /// /// ``` /// # #[cfg(unix)] /// fn main() -> std::io::Result<()> { -/// use std::path::{self, Path}; +/// use std::path::{self, Path}; /// -/// // Relative to absolute -/// let absolute = path::absolute("foo/./bar")?; -/// assert!(absolute.ends_with("foo/bar")); +/// // Relative to absolute +/// let absolute = path::absolute("foo/./bar")?; +/// assert!(absolute.ends_with("foo/bar")); /// -/// // Absolute to absolute -/// let absolute = path::absolute("/foo//test/.././bar.rs")?; -/// assert_eq!(absolute, Path::new("/foo/test/../bar.rs")); -/// Ok(()) +/// // Absolute to absolute +/// let absolute = path::absolute("/foo//test/.././bar.rs")?; +/// assert_eq!(absolute, Path::new("/foo/test/../bar.rs")); +/// Ok(()) /// } /// # #[cfg(not(unix))] /// # fn main() {} /// ``` /// -/// The path is resolved using [POSIX semantics][posix-semantics] except that -/// it stops short of resolving symlinks. This means it will keep `..` -/// components and trailing slashes. -/// /// ## Windows paths /// /// ``` /// # #[cfg(windows)] /// fn main() -> std::io::Result<()> { -/// use std::path::{self, Path}; +/// use std::path::{self, Path}; /// -/// // Relative to absolute -/// let absolute = path::absolute("foo/./bar")?; -/// assert!(absolute.ends_with(r"foo\bar")); +/// // Relative to absolute +/// let absolute = path::absolute("foo/./bar")?; +/// assert!(absolute.ends_with(r"foo\bar")); /// -/// // Absolute to absolute -/// let absolute = path::absolute(r"C:\foo//test\..\./bar.rs")?; +/// // Absolute to absolute +/// let absolute = path::absolute(r"C:\foo//test\..\./bar.rs")?; /// -/// assert_eq!(absolute, Path::new(r"C:\foo\bar.rs")); -/// Ok(()) +/// assert_eq!(absolute, Path::new(r"C:\foo\bar.rs")); +/// Ok(()) /// } /// # #[cfg(not(windows))] /// # fn main() {} /// ``` /// -/// For verbatim paths this will simply return the path as given. For other -/// paths this is currently equivalent to calling [`GetFullPathNameW`][windows-path] -/// This may change in the future. +/// Note that this [may change in the future][changes]. /// +/// [changes]: io#platform-specific-behavior /// [posix-semantics]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 /// [windows-path]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew #[stable(feature = "absolute_path", since = "1.79.0")] diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index fde6ed4f0c05..92702b395dfe 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -126,6 +126,16 @@ fn into() { assert_eq!(static_cow_path, owned_cow_path); } +#[test] +fn test_pathbuf_leak() { + let string = "/have/a/cake".to_owned(); + let (len, cap) = (string.len(), string.capacity()); + let buf = PathBuf::from(string); + let leaked = buf.leak(); + assert_eq!(leaked.as_os_str().as_encoded_bytes(), b"/have/a/cake"); + unsafe { drop(String::from_raw_parts(leaked.as_mut_os_str() as *mut OsStr as _, len, cap)) } +} + #[test] #[cfg(unix)] pub fn test_decompositions_unix() { @@ -1803,6 +1813,29 @@ fn test_windows_absolute() { assert_eq!(absolute(r"COM1").unwrap().as_os_str(), Path::new(r"\\.\COM1").as_os_str()); } +#[test] +#[should_panic = "path separator"] +fn test_extension_path_sep() { + let mut path = PathBuf::from("path/to/file"); + path.set_extension("d/../../../../../etc/passwd"); +} + +#[test] +#[should_panic = "path separator"] +#[cfg(windows)] +fn test_extension_path_sep_alternate() { + let mut path = PathBuf::from("path/to/file"); + path.set_extension("d\\test"); +} + +#[test] +#[cfg(not(windows))] +fn test_extension_path_sep_alternate() { + let mut path = PathBuf::from("path/to/file"); + path.set_extension("d\\test"); + assert_eq!(path, Path::new("path/to/file.d\\test")); +} + #[bench] #[cfg_attr(miri, ignore)] // Miri isn't fast... fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) { diff --git a/library/std/src/prelude/common.rs b/library/std/src/prelude/common.rs index 01936734d754..055ab7eb6d98 100644 --- a/library/std/src/prelude/common.rs +++ b/library/std/src/prelude/common.rs @@ -14,6 +14,9 @@ pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use crate::mem::drop; +#[stable(feature = "size_of_prelude", since = "1.80.0")] +#[doc(no_inline)] +pub use crate::mem::{align_of, align_of_val, size_of, size_of_val}; // Re-exported types and traits #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 0bdbab716adb..2d4639342bf8 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -36,6 +36,10 @@ //! operations for both destructors and overloading `()`. //! * [std::mem]::[drop], a convenience function for explicitly //! dropping a value. +//! * [std::mem]::{[size_of], [size_of_val]}, to get the size of +//! a type or value. +//! * [std::mem]::{[align_of], [align_of_val]}, to get the +//! alignment of a type or value. //! * [std::boxed]::[Box], a way to allocate values on the heap. //! * [std::borrow]::[ToOwned], the conversion trait that defines //! [`to_owned`], the generic method for creating an owned type from a diff --git a/library/std/src/process.rs b/library/std/src/process.rs index d1848224251a..f351dab78dc1 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -90,8 +90,8 @@ //! //! # Windows argument splitting //! -//! On Unix systems arguments are passed to a new process as an array of strings -//! but on Windows arguments are passed as a single commandline string and it's +//! On Unix systems arguments are passed to a new process as an array of strings, +//! but on Windows arguments are passed as a single commandline string and it is //! up to the child process to parse it into an array. Therefore the parent and //! child processes must agree on how the commandline string is encoded. //! @@ -107,26 +107,26 @@ //! * Use [`raw_arg`] to build a custom commandline. This bypasses the escaping //! rules used by [`arg`] so should be used with due caution. //! -//! `cmd.exe` and `.bat` use non-standard argument parsing and are especially +//! `cmd.exe` and `.bat` files use non-standard argument parsing and are especially //! vulnerable to malicious input as they may be used to run arbitrary shell //! commands. Untrusted arguments should be restricted as much as possible. //! For examples on handling this see [`raw_arg`]. //! -//! ### Bat file special handling +//! ### Batch file special handling //! //! On Windows, `Command` uses the Windows API function [`CreateProcessW`] to -//! spawn new processes. An undocumented feature of this function is that, +//! spawn new processes. An undocumented feature of this function is that //! when given a `.bat` file as the application to run, it will automatically -//! convert that into running `cmd.exe /c` with the bat file as the next argument. +//! convert that into running `cmd.exe /c` with the batch file as the next argument. //! //! For historical reasons Rust currently preserves this behaviour when using //! [`Command::new`], and escapes the arguments according to `cmd.exe` rules. //! Due to the complexity of `cmd.exe` argument handling, it might not be -//! possible to safely escape some special chars, and using them will result +//! possible to safely escape some special characters, and using them will result //! in an error being returned at process spawn. The set of unescapeable -//! special chars might change between releases. +//! special characters might change between releases. //! -//! Also note that running `.bat` scripts in this way may be removed in the +//! Also note that running batch scripts in this way may be removed in the //! future and so should not be relied upon. //! //! [`spawn`]: Command::spawn @@ -629,6 +629,25 @@ impl Command { /// .spawn() /// .expect("sh command failed to start"); /// ``` + /// + /// # Caveats + /// + /// [`Command::new`] is only intended to accept the path of the program. If you pass a program + /// path along with arguments like `Command::new("ls -l").spawn()`, it will try to search for + /// `ls -l` literally. The arguments need to be passed separately, such as via [`arg`] or + /// [`args`]. + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .arg("-l") // arg passed separately + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + /// + /// [`arg`]: Self::arg + /// [`args`]: Self::args #[stable(feature = "process", since = "1.0.0")] pub fn new>(program: S) -> Command { Command { inner: imp::Command::new(program.as_ref()) } @@ -659,16 +678,19 @@ impl Command { /// /// Note that the argument is not passed through a shell, but given /// literally to the program. This means that shell syntax like quotes, - /// escaped characters, word splitting, glob patterns, variable substitution, etc. - /// have no effect. + /// escaped characters, word splitting, glob patterns, variable substitution, + /// etc. have no effect. /// ///
/// - /// On Windows use caution with untrusted inputs. Most applications use the - /// standard convention for decoding arguments passed to them. These are safe to use with `arg`. - /// However some applications, such as `cmd.exe` and `.bat` files, use a non-standard way of decoding arguments - /// and are therefore vulnerable to malicious input. - /// In the case of `cmd.exe` this is especially important because a malicious argument can potentially run arbitrary shell commands. + /// On Windows, use caution with untrusted inputs. Most applications use the + /// standard convention for decoding arguments passed to them. These are safe to + /// use with `arg`. However, some applications such as `cmd.exe` and `.bat` files + /// use a non-standard way of decoding arguments. They are therefore vulnerable + /// to malicious input. + /// + /// In the case of `cmd.exe` this is especially important because a malicious + /// argument can potentially run arbitrary shell commands. /// /// See [Windows argument splitting][windows-args] for more details /// or [`raw_arg`] for manually implementing non-standard argument encoding. @@ -710,11 +732,14 @@ impl Command { /// ///
/// - /// On Windows use caution with untrusted inputs. Most applications use the - /// standard convention for decoding arguments passed to them. These are safe to use with `args`. - /// However some applications, such as `cmd.exe` and `.bat` files, use a non-standard way of decoding arguments - /// and are therefore vulnerable to malicious input. - /// In the case of `cmd.exe` this is especially important because a malicious argument can potentially run arbitrary shell commands. + /// On Windows, use caution with untrusted inputs. Most applications use the + /// standard convention for decoding arguments passed to them. These are safe to + /// use with `arg`. However, some applications such as `cmd.exe` and `.bat` files + /// use a non-standard way of decoding arguments. They are therefore vulnerable + /// to malicious input. + /// + /// In the case of `cmd.exe` this is especially important because a malicious + /// argument can potentially run arbitrary shell commands. /// /// See [Windows argument splitting][windows-args] for more details /// or [`raw_arg`] for manually implementing non-standard argument encoding. diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 46f691d7b750..b03fa1c01f26 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -156,7 +156,7 @@ fn lang_start( sigpipe: u8, ) -> isize { let Ok(v) = lang_start_internal( - &move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report().to_i32(), + &move || crate::sys::backtrace::__rust_begin_short_backtrace(main).report().to_i32(), argc, argv, sigpipe, diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index b4bac081e7ab..82cc13a74b7f 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -20,7 +20,7 @@ use crate::sync::{Condvar, Mutex}; /// let c = Arc::clone(&barrier); /// // The same messages will be printed together. /// // You will NOT see any interleaving. -/// handles.push(thread::spawn(move|| { +/// handles.push(thread::spawn(move || { /// println!("before wait"); /// c.wait(); /// println!("after wait"); @@ -115,7 +115,7 @@ impl Barrier { /// let c = Arc::clone(&barrier); /// // The same messages will be printed together. /// // You will NOT see any interleaving. - /// handles.push(thread::spawn(move|| { + /// handles.push(thread::spawn(move || { /// println!("before wait"); /// c.wait(); /// println!("after wait"); diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs index b20574e4f149..f9f83fb4f63c 100644 --- a/library/std/src/sync/condvar.rs +++ b/library/std/src/sync/condvar.rs @@ -88,7 +88,7 @@ impl WaitTimeoutResult { /// let pair2 = Arc::clone(&pair); /// /// // Inside of our lock, spawn a new thread, and then wait for it to start. -/// thread::spawn(move|| { +/// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; @@ -166,7 +166,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; @@ -221,7 +221,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(true), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut pending = lock.lock().unwrap(); /// *pending = false; @@ -280,7 +280,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; @@ -352,7 +352,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; @@ -420,7 +420,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(true), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut pending = lock.lock().unwrap(); /// *pending = false; @@ -484,7 +484,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; @@ -524,7 +524,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index 27b59cfc8c24..b70c041fa428 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -29,40 +29,28 @@ union Data { /// # Examples /// /// Initialize static variables with `LazyLock`. -/// /// ``` -/// #![feature(lazy_cell)] -/// -/// use std::collections::HashMap; -/// /// use std::sync::LazyLock; /// -/// static HASHMAP: LazyLock> = LazyLock::new(|| { -/// println!("initializing"); -/// let mut m = HashMap::new(); -/// m.insert(13, "Spica".to_string()); -/// m.insert(74, "Hoyten".to_string()); -/// m +/// // n.b. static items do not call [`Drop`] on program termination, so this won't be deallocated. +/// // this is fine, as the OS can deallocate the terminated program faster than we can free memory +/// // but tools like valgrind might report "memory leaks" as it isn't obvious this is intentional. +/// static DEEP_THOUGHT: LazyLock = LazyLock::new(|| { +/// # mod another_crate { +/// # pub fn great_question() -> String { "42".to_string() } +/// # } +/// // M3 Ultra takes about 16 million years in --release config +/// another_crate::great_question() /// }); /// -/// fn main() { -/// println!("ready"); -/// std::thread::spawn(|| { -/// println!("{:?}", HASHMAP.get(&13)); -/// }).join().unwrap(); -/// println!("{:?}", HASHMAP.get(&74)); -/// -/// // Prints: -/// // ready -/// // initializing -/// // Some("Spica") -/// // Some("Hoyten") -/// } +/// // The `String` is built, stored in the `LazyLock`, and returned as `&String`. +/// let _ = &*DEEP_THOUGHT; +/// // The `String` is retrieved from the `LazyLock` and returned as `&String`. +/// let _ = &*DEEP_THOUGHT; /// ``` +/// /// Initialize fields with `LazyLock`. /// ``` -/// #![feature(lazy_cell)] -/// /// use std::sync::LazyLock; /// /// #[derive(Debug)] @@ -76,8 +64,7 @@ union Data { /// println!("{}", *data.number); /// } /// ``` - -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] pub struct LazyLock T> { once: Once, data: UnsafeCell>, @@ -85,8 +72,21 @@ pub struct LazyLock T> { impl T> LazyLock { /// Creates a new lazy value with the given initializing function. + /// + /// # Examples + /// + /// ``` + /// use std::sync::LazyLock; + /// + /// let hello = "Hello, World!".to_string(); + /// + /// let lazy = LazyLock::new(|| hello.to_uppercase()); + /// + /// assert_eq!(&*lazy, "HELLO, WORLD!"); + /// ``` #[inline] - #[unstable(feature = "lazy_cell", issue = "109736")] + #[stable(feature = "lazy_cell", since = "1.80.0")] + #[rustc_const_stable(feature = "lazy_cell", since = "1.80.0")] pub const fn new(f: F) -> LazyLock { LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) } } @@ -107,7 +107,6 @@ impl T> LazyLock { /// # Examples /// /// ``` - /// #![feature(lazy_cell)] /// #![feature(lazy_cell_consume)] /// /// use std::sync::LazyLock; @@ -119,7 +118,7 @@ impl T> LazyLock { /// assert_eq!(&*lazy, "HELLO, WORLD!"); /// assert_eq!(LazyLock::into_inner(lazy).ok(), Some("HELLO, WORLD!".to_string())); /// ``` - #[unstable(feature = "lazy_cell_consume", issue = "109736")] + #[unstable(feature = "lazy_cell_consume", issue = "125623")] pub fn into_inner(mut this: Self) -> Result { let state = this.once.state(); match state { @@ -145,8 +144,6 @@ impl T> LazyLock { /// # Examples /// /// ``` - /// #![feature(lazy_cell)] - /// /// use std::sync::LazyLock; /// /// let lazy = LazyLock::new(|| 92); @@ -155,7 +152,7 @@ impl T> LazyLock { /// assert_eq!(&*lazy, &92); /// ``` #[inline] - #[unstable(feature = "lazy_cell", issue = "109736")] + #[stable(feature = "lazy_cell", since = "1.80.0")] pub fn force(this: &LazyLock) -> &T { this.once.call_once(|| { // SAFETY: `call_once` only runs this closure once, ever. @@ -191,7 +188,7 @@ impl LazyLock { } } -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl Drop for LazyLock { fn drop(&mut self) { match self.once.state() { @@ -204,7 +201,7 @@ impl Drop for LazyLock { } } -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl T> Deref for LazyLock { type Target = T; @@ -219,7 +216,7 @@ impl T> Deref for LazyLock { } } -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl Default for LazyLock { /// Creates a new lazy value using `Default` as the initializing function. #[inline] @@ -228,7 +225,7 @@ impl Default for LazyLock { } } -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl fmt::Debug for LazyLock { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_tuple("LazyLock"); @@ -242,13 +239,13 @@ impl fmt::Debug for LazyLock { // We never create a `&F` from a `&LazyLock` so it is fine // to not impl `Sync` for `F`. -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] unsafe impl Sync for LazyLock {} // auto-derived `Send` impl is OK. -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl RefUnwindSafe for LazyLock {} -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl UnwindSafe for LazyLock {} #[cfg(test)] diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index e8c35bd48a70..9a38c42f43a0 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -133,10 +133,14 @@ //! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at //! most one thread at a time is able to access some data. //! -//! - [`Once`]: Used for a thread-safe, one-time global initialization routine +//! - [`Once`]: Used for a thread-safe, one-time global initialization routine. +//! Mostly useful for implementing other types like `OnceLock`. //! //! - [`OnceLock`]: Used for thread-safe, one-time initialization of a -//! global variable. +//! variable, with potentially different initializers based on the caller. +//! +//! - [`LazyLock`]: Used for thread-safe, one-time initialization of a +//! variable, using one nullary initializer function provided at creation. //! //! - [`RwLock`]: Provides a mutual exclusion mechanism which allows //! multiple readers at the same time, while allowing only one @@ -179,7 +183,7 @@ pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] pub use self::lazy_lock::LazyLock; #[stable(feature = "once_cell", since = "1.70.0")] pub use self::once_lock::OnceLock; diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs index d353c7bd5de9..feee6948db4f 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc/mod.rs @@ -51,7 +51,7 @@ //! //! // Create a simple streaming channel //! let (tx, rx) = channel(); -//! thread::spawn(move|| { +//! thread::spawn(move || { //! tx.send(10).unwrap(); //! }); //! assert_eq!(rx.recv().unwrap(), 10); @@ -69,7 +69,7 @@ //! let (tx, rx) = channel(); //! for i in 0..10 { //! let tx = tx.clone(); -//! thread::spawn(move|| { +//! thread::spawn(move || { //! tx.send(i).unwrap(); //! }); //! } @@ -99,7 +99,7 @@ //! use std::sync::mpsc::sync_channel; //! //! let (tx, rx) = sync_channel::(0); -//! thread::spawn(move|| { +//! thread::spawn(move || { //! // This will wait for the parent thread to start receiving //! tx.send(53).unwrap(); //! }); @@ -510,7 +510,7 @@ pub enum TrySendError { /// let (sender, receiver) = channel(); /// /// // Spawn off an expensive computation -/// thread::spawn(move|| { +/// thread::spawn(move || { /// # fn expensive_computation() {} /// sender.send(expensive_computation()).unwrap(); /// }); @@ -561,7 +561,7 @@ pub fn channel() -> (Sender, Receiver) { /// // this returns immediately /// sender.send(1).unwrap(); /// -/// thread::spawn(move|| { +/// thread::spawn(move || { /// // this will block until the previous message has been received /// sender.send(2).unwrap(); /// }); diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 608229fd674d..9d969af8c6d8 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -10,9 +10,15 @@ use crate::fmt; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sys::sync as sys; -/// A synchronization primitive which can be used to run a one-time global -/// initialization. Useful for one-time initialization for FFI or related -/// functionality. This type can only be constructed with [`Once::new()`]. +/// A low-level synchronization primitive for one-time global execution. +/// +/// Previously this was the only "execute once" synchronization in `std`. +/// Other libraries implemented novel synchronizing types with `Once`, like +/// [`OnceLock`] or [`LazyLock`], before those were added to `std`. +/// `OnceLock` in particular supersedes `Once` in functionality and should +/// be preferred for the common case where the `Once` is associated with data. +/// +/// This type can only be constructed with [`Once::new()`]. /// /// # Examples /// @@ -25,6 +31,9 @@ use crate::sys::sync as sys; /// // run initialization here /// }); /// ``` +/// +/// [`OnceLock`]: crate::sync::OnceLock +/// [`LazyLock`]: crate::sync::LazyLock #[stable(feature = "rust1", since = "1.0.0")] pub struct Once { inner: sys::Once, diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index fc830baccedd..f52b9e52c54d 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -5,50 +5,20 @@ use crate::mem::MaybeUninit; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sync::Once; -/// A synchronization primitive which can be written to only once. +/// A synchronization primitive which can nominally be written to only once. /// /// This type is a thread-safe [`OnceCell`], and can be used in statics. +/// In many simple cases, you can use [`LazyLock`] instead to get the benefits of this type +/// with less effort: `LazyLock` "looks like" `&T` because it initializes with `F` on deref! +/// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock +/// doesn't allow additional inputs to its function after you call [`LazyLock::new(|| ...)`]. /// /// [`OnceCell`]: crate::cell::OnceCell +/// [`LazyLock`]: crate::sync::LazyLock +/// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new /// /// # Examples /// -/// Using `OnceLock` to store a function’s previously computed value (a.k.a. -/// ‘lazy static’ or ‘memoizing’): -/// -/// ``` -/// use std::sync::OnceLock; -/// -/// struct DeepThought { -/// answer: String, -/// } -/// -/// impl DeepThought { -/// # fn great_question() -> String { -/// # "42".to_string() -/// # } -/// # -/// fn new() -> Self { -/// Self { -/// // M3 Ultra takes about 16 million years in --release config -/// answer: Self::great_question(), -/// } -/// } -/// } -/// -/// fn computation() -> &'static DeepThought { -/// // n.b. static items do not call [`Drop`] on program termination, so if -/// // [`DeepThought`] impls Drop, that will not be used for this instance. -/// static COMPUTATION: OnceLock = OnceLock::new(); -/// COMPUTATION.get_or_init(|| DeepThought::new()) -/// } -/// -/// // The `DeepThought` is built, stored in the `OnceLock`, and returned. -/// let _ = computation().answer; -/// // The `DeepThought` is retrieved from the `OnceLock` and returned. -/// let _ = computation().answer; -/// ``` -/// /// Writing to a `OnceLock` from a separate thread: /// /// ``` @@ -73,6 +43,55 @@ use crate::sync::Once; /// Some(&12345), /// ); /// ``` +/// +/// You can use `OnceLock` to implement a type that requires "append-only" logic: +/// +/// ``` +/// use std::sync::{OnceLock, atomic::{AtomicU32, Ordering}}; +/// use std::thread; +/// +/// struct OnceList { +/// data: OnceLock, +/// next: OnceLock>>, +/// } +/// impl OnceList { +/// const fn new() -> OnceList { +/// OnceList { data: OnceLock::new(), next: OnceLock::new() } +/// } +/// fn push(&self, value: T) { +/// // FIXME: this impl is concise, but is also slow for long lists or many threads. +/// // as an exercise, consider how you might improve on it while preserving the behavior +/// if let Err(value) = self.data.set(value) { +/// let next = self.next.get_or_init(|| Box::new(OnceList::new())); +/// next.push(value) +/// }; +/// } +/// fn contains(&self, example: &T) -> bool +/// where +/// T: PartialEq, +/// { +/// self.data.get().map(|item| item == example).filter(|v| *v).unwrap_or_else(|| { +/// self.next.get().map(|next| next.contains(example)).unwrap_or(false) +/// }) +/// } +/// } +/// +/// // Let's exercise this new Sync append-only list by doing a little counting +/// static LIST: OnceList = OnceList::new(); +/// static COUNTER: AtomicU32 = AtomicU32::new(0); +/// +/// let vec = (0..thread::available_parallelism().unwrap().get()).map(|_| thread::spawn(|| { +/// while let i @ 0..=1000 = COUNTER.fetch_add(1, Ordering::Relaxed) { +/// LIST.push(i); +/// } +/// })).collect::>>(); +/// vec.into_iter().for_each(|handle| handle.join().unwrap()); +/// +/// for i in 0..=1000 { +/// assert!(LIST.contains(&i)); +/// } +/// +/// ``` #[stable(feature = "once_cell", since = "1.70.0")] pub struct OnceLock { once: Once, diff --git a/library/std/src/sync/reentrant_lock.rs b/library/std/src/sync/reentrant_lock.rs index 80b9e0cf1521..f7fe8eb1c7fd 100644 --- a/library/std/src/sync/reentrant_lock.rs +++ b/library/std/src/sync/reentrant_lock.rs @@ -116,6 +116,9 @@ pub struct ReentrantLockGuard<'a, T: ?Sized + 'a> { #[unstable(feature = "reentrant_lock", issue = "121440")] impl !Send for ReentrantLockGuard<'_, T> {} +#[unstable(feature = "reentrant_lock", issue = "121440")] +unsafe impl Sync for ReentrantLockGuard<'_, T> {} + #[unstable(feature = "reentrant_lock", issue = "121440")] impl ReentrantLock { /// Creates a new re-entrant lock in an unlocked state ready for use. diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys/backtrace.rs similarity index 98% rename from library/std/src/sys_common/backtrace.rs rename to library/std/src/sys/backtrace.rs index 67711dbd5bc7..0b2338fd7de9 100644 --- a/library/std/src/sys_common/backtrace.rs +++ b/library/std/src/sys/backtrace.rs @@ -1,7 +1,7 @@ +//! Common code for printing backtraces. + use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt}; use crate::borrow::Cow; -/// Common code for printing the backtrace in the same way across the different -/// supported platforms. use crate::env; use crate::fmt; use crate::io; diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index bbd1d840e92d..8aa35c40fe05 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -5,12 +5,11 @@ mod pal; mod personality; +pub mod backtrace; pub mod cmath; pub mod os_str; pub mod path; pub mod sync; -#[allow(dead_code)] -#[allow(unused_imports)] pub mod thread_local; // FIXME(117276): remove this, move feature implementations into individual diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs index 18b969bca85a..f7c6b0877aa6 100644 --- a/library/std/src/sys/os_str/bytes.rs +++ b/library/std/src/sys/os_str/bytes.rs @@ -176,6 +176,11 @@ impl Buf { self.inner.extend_from_slice(&s.inner) } + #[inline] + pub fn leak<'a>(self) -> &'a mut Slice { + unsafe { mem::transmute(self.inner.leak()) } + } + #[inline] pub fn into_box(self) -> Box { unsafe { mem::transmute(self.inner.into_boxed_slice()) } diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index b3ceb55802dc..dfff4dd4fb02 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -138,6 +138,11 @@ impl Buf { self.inner.shrink_to(min_capacity) } + #[inline] + pub fn leak<'a>(self) -> &'a mut Slice { + unsafe { mem::transmute(self.inner.leak()) } + } + #[inline] pub fn into_box(self) -> Box { unsafe { mem::transmute(self.inner.into_box()) } diff --git a/library/std/src/sys/pal/hermit/alloc.rs b/library/std/src/sys/pal/hermit/alloc.rs index de550987a435..2cd0db909403 100644 --- a/library/std/src/sys/pal/hermit/alloc.rs +++ b/library/std/src/sys/pal/hermit/alloc.rs @@ -1,4 +1,4 @@ -use super::abi; +use super::hermit_abi; use crate::alloc::{GlobalAlloc, Layout, System}; use crate::ptr; @@ -6,11 +6,11 @@ use crate::ptr; unsafe impl GlobalAlloc for System { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - abi::malloc(layout.size(), layout.align()) + hermit_abi::malloc(layout.size(), layout.align()) } unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - let addr = abi::malloc(layout.size(), layout.align()); + let addr = hermit_abi::malloc(layout.size(), layout.align()); if !addr.is_null() { ptr::write_bytes(addr, 0x00, layout.size()); @@ -21,11 +21,11 @@ unsafe impl GlobalAlloc for System { #[inline] unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - abi::free(ptr, layout.size(), layout.align()) + hermit_abi::free(ptr, layout.size(), layout.align()) } #[inline] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - abi::realloc(ptr, layout.size(), layout.align(), new_size) + hermit_abi::realloc(ptr, layout.size(), layout.align(), new_size) } } diff --git a/library/std/src/sys/pal/hermit/fd.rs b/library/std/src/sys/pal/hermit/fd.rs index 962577bb1ed8..3c52b85de23a 100644 --- a/library/std/src/sys/pal/hermit/fd.rs +++ b/library/std/src/sys/pal/hermit/fd.rs @@ -1,7 +1,8 @@ #![unstable(reason = "not public", issue = "none", feature = "fd")] -use super::abi; -use crate::io::{self, Read}; +use super::hermit_abi; +use crate::cmp; +use crate::io::{self, IoSlice, IoSliceMut, Read}; use crate::os::hermit::io::{FromRawFd, OwnedFd, RawFd}; use crate::sys::cvt; use crate::sys::unsupported; @@ -9,6 +10,10 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::os::hermit::io::*; +const fn max_iov() -> usize { + hermit_abi::IOV_MAX +} + #[derive(Debug)] pub struct FileDesc { fd: OwnedFd, @@ -16,20 +21,54 @@ pub struct FileDesc { impl FileDesc { pub fn read(&self, buf: &mut [u8]) -> io::Result { - let result = cvt(unsafe { abi::read(self.fd.as_raw_fd(), buf.as_mut_ptr(), buf.len()) })?; + let result = + cvt(unsafe { hermit_abi::read(self.fd.as_raw_fd(), buf.as_mut_ptr(), buf.len()) })?; Ok(result as usize) } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let ret = cvt(unsafe { + hermit_abi::readv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut hermit_abi::iovec as *const hermit_abi::iovec, + cmp::min(bufs.len(), max_iov()), + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { let mut me = self; (&mut me).read_to_end(buf) } pub fn write(&self, buf: &[u8]) -> io::Result { - let result = cvt(unsafe { abi::write(self.fd.as_raw_fd(), buf.as_ptr(), buf.len()) })?; + let result = + cvt(unsafe { hermit_abi::write(self.fd.as_raw_fd(), buf.as_ptr(), buf.len()) })?; Ok(result as usize) } + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + let ret = cvt(unsafe { + hermit_abi::writev( + self.as_raw_fd(), + bufs.as_ptr() as *const hermit_abi::iovec, + cmp::min(bufs.len(), max_iov()), + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + pub fn duplicate(&self) -> io::Result { self.duplicate_path(&[]) } @@ -49,8 +88,8 @@ impl FileDesc { unsupported() } - pub fn fstat(&self, stat: *mut abi::stat) -> io::Result<()> { - cvt(unsafe { abi::fstat(self.fd.as_raw_fd(), stat) })?; + pub fn fstat(&self, stat: *mut hermit_abi::stat) -> io::Result<()> { + cvt(unsafe { hermit_abi::fstat(self.fd.as_raw_fd(), stat) })?; Ok(()) } } diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index 6519cc22f1f6..a98a1006ef47 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -1,8 +1,8 @@ -use super::abi::{ - self, dirent64, stat as stat_struct, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, - O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, -}; use super::fd::FileDesc; +use super::hermit_abi::{ + self, dirent64, stat as stat_struct, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, + O_DIRECTORY, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, +}; use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt; use crate::io::{self, Error, ErrorKind}; @@ -47,7 +47,7 @@ impl InnerReadDir { pub struct ReadDir { inner: Arc, - pos: i64, + pos: usize, } impl ReadDir { @@ -62,7 +62,7 @@ pub struct DirEntry { /// 64-bit inode number ino: u64, /// File type - type_: u32, + type_: u8, /// name of the entry name: OsString, } @@ -90,7 +90,7 @@ pub struct FilePermissions { #[derive(Copy, Clone, Eq, Debug)] pub struct FileType { - mode: u32, + mode: u8, } impl PartialEq for FileType { @@ -112,31 +112,23 @@ pub struct DirBuilder { impl FileAttr { pub fn modified(&self) -> io::Result { - Ok(SystemTime::new( - self.stat_val.st_mtime.try_into().unwrap(), - self.stat_val.st_mtime_nsec.try_into().unwrap(), - )) + Ok(SystemTime::new(self.stat_val.st_mtim.tv_sec, self.stat_val.st_mtim.tv_nsec)) } pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new( - self.stat_val.st_atime.try_into().unwrap(), - self.stat_val.st_atime_nsec.try_into().unwrap(), - )) + Ok(SystemTime::new(self.stat_val.st_atim.tv_sec, self.stat_val.st_atim.tv_nsec)) } pub fn created(&self) -> io::Result { - Ok(SystemTime::new( - self.stat_val.st_ctime.try_into().unwrap(), - self.stat_val.st_ctime_nsec.try_into().unwrap(), - )) + Ok(SystemTime::new(self.stat_val.st_ctim.tv_sec, self.stat_val.st_ctim.tv_nsec)) } pub fn size(&self) -> u64 { self.stat_val.st_size as u64 } + pub fn perm(&self) -> FilePermissions { - FilePermissions { mode: (self.stat_val.st_mode) } + FilePermissions { mode: self.stat_val.st_mode } } pub fn file_type(&self) -> FileType { @@ -197,38 +189,31 @@ impl Iterator for ReadDir { fn next(&mut self) -> Option> { let mut counter: usize = 0; - let mut offset: i64 = 0; + let mut offset: usize = 0; // loop over all directory entries and search the entry for the current position loop { // leave function, if the loop reaches the of the buffer (with all entries) - if offset >= self.inner.dir.len().try_into().unwrap() { + if offset >= self.inner.dir.len() { return None; } - let dir = unsafe { - &*(self.inner.dir.as_ptr().offset(offset.try_into().unwrap()) as *const dirent64) - }; + let dir = unsafe { &*(self.inner.dir.as_ptr().add(offset) as *const dirent64) }; - if counter == self.pos.try_into().unwrap() { + if counter == self.pos { self.pos += 1; // After dirent64, the file name is stored. d_reclen represents the length of the dirent64 // plus the length of the file name. Consequently, file name has a size of d_reclen minus // the size of dirent64. The file name is always a C string and terminated by `\0`. // Consequently, we are able to ignore the last byte. - let name_bytes = unsafe { - core::slice::from_raw_parts( - &dir.d_name as *const _ as *const u8, - dir.d_reclen as usize - core::mem::size_of::() - 1, - ) - .to_vec() - }; + let name_bytes = + unsafe { CStr::from_ptr(&dir.d_name as *const _ as *const i8).to_bytes() }; let entry = DirEntry { root: self.inner.root.clone(), ino: dir.d_ino, - type_: dir.d_type as u32, - name: OsString::from_vec(name_bytes), + type_: dir.d_type, + name: OsString::from_vec(name_bytes.to_vec()), }; return Some(Ok(entry)); @@ -237,7 +222,7 @@ impl Iterator for ReadDir { counter += 1; // move to the next dirent64, which is directly stored after the previous one - offset = offset + dir.d_off; + offset = offset + usize::from(dir.d_reclen); } } } @@ -258,7 +243,7 @@ impl DirEntry { } pub fn file_type(&self) -> io::Result { - Ok(FileType { mode: self.type_ as u32 }) + Ok(FileType { mode: self.type_ }) } #[allow(dead_code)] @@ -365,7 +350,7 @@ impl File { mode = 0; } - let fd = unsafe { cvt(abi::open(path.as_ptr(), flags, mode))? }; + let fd = unsafe { cvt(hermit_abi::open(path.as_ptr(), flags, mode))? }; Ok(File(unsafe { FileDesc::from_raw_fd(fd as i32) })) } @@ -392,12 +377,12 @@ impl File { } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - crate::io::default_read_vectored(|buf| self.read(buf), bufs) + self.0.read_vectored(bufs) } #[inline] pub fn is_read_vectored(&self) -> bool { - false + self.0.is_read_vectored() } pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { @@ -409,12 +394,12 @@ impl File { } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - crate::io::default_write_vectored(|buf| self.write(buf), bufs) + self.0.write_vectored(bufs) } #[inline] pub fn is_write_vectored(&self) -> bool { - false + self.0.is_write_vectored() } #[inline] @@ -446,13 +431,13 @@ impl DirBuilder { pub fn mkdir(&self, path: &Path) -> io::Result<()> { run_path_with_cstr(path, &|path| { - cvt(unsafe { abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ()) + cvt(unsafe { hermit_abi::mkdir(path.as_ptr(), self.mode.into()) }).map(|_| ()) }) } #[allow(dead_code)] pub fn set_mode(&mut self, mode: u32) { - self.mode = mode as u32; + self.mode = mode; } } @@ -508,7 +493,9 @@ impl FromRawFd for File { } pub fn readdir(path: &Path) -> io::Result { - let fd_raw = run_path_with_cstr(path, &|path| cvt(unsafe { abi::opendir(path.as_ptr()) }))?; + let fd_raw = run_path_with_cstr(path, &|path| { + cvt(unsafe { hermit_abi::open(path.as_ptr(), O_RDONLY | O_DIRECTORY, 0) }) + })?; let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) }; let root = path.to_path_buf(); @@ -519,8 +506,9 @@ pub fn readdir(path: &Path) -> io::Result { // reserve memory to receive all directory entries vec.resize(sz, 0); - let readlen = - unsafe { abi::getdents64(fd.as_raw_fd(), vec.as_mut_ptr() as *mut dirent64, sz) }; + let readlen = unsafe { + hermit_abi::getdents64(fd.as_raw_fd(), vec.as_mut_ptr() as *mut dirent64, sz) + }; if readlen > 0 { // shrink down to the minimal size vec.resize(readlen.try_into().unwrap(), 0); @@ -529,7 +517,7 @@ pub fn readdir(path: &Path) -> io::Result { // if the buffer is too small, getdents64 returns EINVAL // otherwise, getdents64 returns an error number - if readlen != (-abi::errno::EINVAL).into() { + if readlen != (-hermit_abi::errno::EINVAL).into() { return Err(Error::from_raw_os_error(readlen.try_into().unwrap())); } @@ -547,7 +535,7 @@ pub fn readdir(path: &Path) -> io::Result { } pub fn unlink(path: &Path) -> io::Result<()> { - run_path_with_cstr(path, &|path| cvt(unsafe { abi::unlink(path.as_ptr()) }).map(|_| ())) + run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::unlink(path.as_ptr()) }).map(|_| ())) } pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { @@ -559,7 +547,7 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { } pub fn rmdir(path: &Path) -> io::Result<()> { - run_path_with_cstr(path, &|path| cvt(unsafe { abi::rmdir(path.as_ptr()) }).map(|_| ())) + run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::rmdir(path.as_ptr()) }).map(|_| ())) } pub fn remove_dir_all(_path: &Path) -> io::Result<()> { @@ -581,7 +569,7 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { pub fn stat(path: &Path) -> io::Result { run_path_with_cstr(path, &|path| { let mut stat_val: stat_struct = unsafe { mem::zeroed() }; - cvt(unsafe { abi::stat(path.as_ptr(), &mut stat_val) })?; + cvt(unsafe { hermit_abi::stat(path.as_ptr(), &mut stat_val) })?; Ok(FileAttr::from_stat(stat_val)) }) } @@ -589,7 +577,7 @@ pub fn stat(path: &Path) -> io::Result { pub fn lstat(path: &Path) -> io::Result { run_path_with_cstr(path, &|path| { let mut stat_val: stat_struct = unsafe { mem::zeroed() }; - cvt(unsafe { abi::lstat(path.as_ptr(), &mut stat_val) })?; + cvt(unsafe { hermit_abi::lstat(path.as_ptr(), &mut stat_val) })?; Ok(FileAttr::from_stat(stat_val)) }) } diff --git a/library/std/src/sys/pal/hermit/futex.rs b/library/std/src/sys/pal/hermit/futex.rs index 427d8ff6f2e4..b2d74d1311bc 100644 --- a/library/std/src/sys/pal/hermit/futex.rs +++ b/library/std/src/sys/pal/hermit/futex.rs @@ -1,4 +1,4 @@ -use super::abi; +use super::hermit_abi; use crate::ptr::null; use crate::sync::atomic::AtomicU32; use crate::time::Duration; @@ -8,32 +8,32 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - // // Overflows are rounded up to an infinite timeout (None). let timespec = timeout.and_then(|dur| { - Some(abi::timespec { + Some(hermit_abi::timespec { tv_sec: dur.as_secs().try_into().ok()?, - tv_nsec: dur.subsec_nanos().into(), + tv_nsec: dur.subsec_nanos().try_into().ok()?, }) }); let r = unsafe { - abi::futex_wait( + hermit_abi::futex_wait( futex.as_ptr(), expected, - timespec.as_ref().map_or(null(), |t| t as *const abi::timespec), - abi::FUTEX_RELATIVE_TIMEOUT, + timespec.as_ref().map_or(null(), |t| t as *const hermit_abi::timespec), + hermit_abi::FUTEX_RELATIVE_TIMEOUT, ) }; - r != -abi::errno::ETIMEDOUT + r != -hermit_abi::errno::ETIMEDOUT } #[inline] pub fn futex_wake(futex: &AtomicU32) -> bool { - unsafe { abi::futex_wake(futex.as_ptr(), 1) > 0 } + unsafe { hermit_abi::futex_wake(futex.as_ptr(), 1) > 0 } } #[inline] pub fn futex_wake_all(futex: &AtomicU32) { unsafe { - abi::futex_wake(futex.as_ptr(), i32::MAX); + hermit_abi::futex_wake(futex.as_ptr(), i32::MAX); } } diff --git a/library/std/src/sys/pal/hermit/io.rs b/library/std/src/sys/pal/hermit/io.rs new file mode 100644 index 000000000000..9de7b53e53c0 --- /dev/null +++ b/library/std/src/sys/pal/hermit/io.rs @@ -0,0 +1,82 @@ +use crate::marker::PhantomData; +use crate::os::hermit::io::{AsFd, AsRawFd}; +use crate::slice; + +use hermit_abi::{c_void, iovec}; + +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct IoSlice<'a> { + vec: iovec, + _p: PhantomData<&'a [u8]>, +} + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice { + vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.iov_len < n { + panic!("advancing IoSlice beyond its length"); + } + + unsafe { + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } +} + +#[repr(transparent)] +pub struct IoSliceMut<'a> { + vec: iovec, + _p: PhantomData<&'a mut [u8]>, +} + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut { + vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.iov_len < n { + panic!("advancing IoSliceMut beyond its length"); + } + + unsafe { + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } +} + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + hermit_abi::isatty(fd.as_raw_fd()) +} diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index 910935541bd3..eca7351d54c8 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -23,7 +23,6 @@ pub mod env; pub mod fd; pub mod fs; pub mod futex; -#[path = "../unsupported/io.rs"] pub mod io; pub mod net; pub mod os; @@ -39,7 +38,7 @@ pub mod thread_local_key; pub mod time; use crate::io::ErrorKind; -use crate::os::hermit::abi; +use crate::os::hermit::hermit_abi; pub fn unsupported() -> crate::io::Result { Err(unsupported_err()) @@ -54,7 +53,7 @@ pub fn unsupported_err() -> crate::io::Error { pub fn abort_internal() -> ! { unsafe { - abi::abort(); + hermit_abi::abort(); } } @@ -62,7 +61,7 @@ pub fn hashmap_random_keys() -> (u64, u64) { let mut buf = [0; 16]; let mut slice = &mut buf[..]; while !slice.is_empty() { - let res = cvt(unsafe { abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) }) + let res = cvt(unsafe { hermit_abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) }) .expect("failed to generate random hashmap keys"); slice = &mut slice[res as usize..]; } @@ -109,31 +108,31 @@ pub unsafe extern "C" fn runtime_entry( let result = main(argc as isize, argv); run_dtors(); - abi::exit(result); + hermit_abi::exit(result); } #[inline] pub(crate) fn is_interrupted(errno: i32) -> bool { - errno == abi::errno::EINTR + errno == hermit_abi::errno::EINTR } pub fn decode_error_kind(errno: i32) -> ErrorKind { match errno { - abi::errno::EACCES => ErrorKind::PermissionDenied, - abi::errno::EADDRINUSE => ErrorKind::AddrInUse, - abi::errno::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, - abi::errno::EAGAIN => ErrorKind::WouldBlock, - abi::errno::ECONNABORTED => ErrorKind::ConnectionAborted, - abi::errno::ECONNREFUSED => ErrorKind::ConnectionRefused, - abi::errno::ECONNRESET => ErrorKind::ConnectionReset, - abi::errno::EEXIST => ErrorKind::AlreadyExists, - abi::errno::EINTR => ErrorKind::Interrupted, - abi::errno::EINVAL => ErrorKind::InvalidInput, - abi::errno::ENOENT => ErrorKind::NotFound, - abi::errno::ENOTCONN => ErrorKind::NotConnected, - abi::errno::EPERM => ErrorKind::PermissionDenied, - abi::errno::EPIPE => ErrorKind::BrokenPipe, - abi::errno::ETIMEDOUT => ErrorKind::TimedOut, + hermit_abi::errno::EACCES => ErrorKind::PermissionDenied, + hermit_abi::errno::EADDRINUSE => ErrorKind::AddrInUse, + hermit_abi::errno::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, + hermit_abi::errno::EAGAIN => ErrorKind::WouldBlock, + hermit_abi::errno::ECONNABORTED => ErrorKind::ConnectionAborted, + hermit_abi::errno::ECONNREFUSED => ErrorKind::ConnectionRefused, + hermit_abi::errno::ECONNRESET => ErrorKind::ConnectionReset, + hermit_abi::errno::EEXIST => ErrorKind::AlreadyExists, + hermit_abi::errno::EINTR => ErrorKind::Interrupted, + hermit_abi::errno::EINVAL => ErrorKind::InvalidInput, + hermit_abi::errno::ENOENT => ErrorKind::NotFound, + hermit_abi::errno::ENOTCONN => ErrorKind::NotConnected, + hermit_abi::errno::EPERM => ErrorKind::PermissionDenied, + hermit_abi::errno::EPIPE => ErrorKind::BrokenPipe, + hermit_abi::errno::ETIMEDOUT => ErrorKind::TimedOut, _ => ErrorKind::Uncategorized, } } diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/pal/hermit/net.rs index 23ac71cb9f29..6016d50eba08 100644 --- a/library/std/src/sys/pal/hermit/net.rs +++ b/library/std/src/sys/pal/hermit/net.rs @@ -175,23 +175,12 @@ impl Socket { } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let mut size: isize = 0; - - for i in bufs.iter_mut() { - let ret: isize = - cvt(unsafe { netc::read(self.0.as_raw_fd(), i.as_mut_ptr(), i.len()) })?; - - if ret != 0 { - size += ret; - } - } - - Ok(size.try_into().unwrap()) + self.0.read_vectored(bufs) } #[inline] pub fn is_read_vectored(&self) -> bool { - true + self.0.is_read_vectored() } fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<(usize, SocketAddr)> { @@ -220,22 +209,15 @@ impl Socket { } pub fn write(&self, buf: &[u8]) -> io::Result { - let sz = cvt(unsafe { netc::write(self.0.as_raw_fd(), buf.as_ptr(), buf.len()) })?; - Ok(sz.try_into().unwrap()) + self.0.write(buf) } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let mut size: isize = 0; - - for i in bufs.iter() { - size += cvt(unsafe { netc::write(self.0.as_raw_fd(), i.as_ptr(), i.len()) })?; - } - - Ok(size.try_into().unwrap()) + self.0.write_vectored(bufs) } pub fn is_write_vectored(&self) -> bool { - true + self.0.is_write_vectored() } pub fn set_timeout(&self, dur: Option, kind: i32) -> io::Result<()> { @@ -282,7 +264,7 @@ impl Socket { Shutdown::Read => netc::SHUT_RD, Shutdown::Both => netc::SHUT_RDWR, }; - cvt(unsafe { netc::shutdown_socket(self.as_raw_fd(), how) })?; + cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; Ok(()) } diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index 1f9affbf7735..a7a73c756f21 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -1,4 +1,4 @@ -use super::abi; +use super::hermit_abi; use crate::collections::HashMap; use crate::error::Error as StdError; use crate::ffi::{CStr, OsStr, OsString}; @@ -14,11 +14,11 @@ use crate::vec; use core::slice::memchr; pub fn errno() -> i32 { - unsafe { abi::get_errno() } + unsafe { hermit_abi::get_errno() } } pub fn error_string(errno: i32) -> String { - abi::error_string(errno).to_string() + hermit_abi::error_string(errno).to_string() } pub fn getcwd() -> io::Result { @@ -172,18 +172,14 @@ pub fn getenv(k: &OsStr) -> Option { unsafe { ENV.as_ref().unwrap().lock().unwrap().get_mut(k).cloned() } } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - unsafe { - let (k, v) = (k.to_owned(), v.to_owned()); - ENV.as_ref().unwrap().lock().unwrap().insert(k, v); - } +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let (k, v) = (k.to_owned(), v.to_owned()); + ENV.as_ref().unwrap().lock().unwrap().insert(k, v); Ok(()) } -pub fn unsetenv(k: &OsStr) -> io::Result<()> { - unsafe { - ENV.as_ref().unwrap().lock().unwrap().remove(k); - } +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { + ENV.as_ref().unwrap().lock().unwrap().remove(k); Ok(()) } @@ -197,10 +193,10 @@ pub fn home_dir() -> Option { pub fn exit(code: i32) -> ! { unsafe { - abi::exit(code); + hermit_abi::exit(code); } } pub fn getpid() -> u32 { - unsafe { abi::getpid() } + unsafe { hermit_abi::getpid() as u32 } } diff --git a/library/std/src/sys/pal/hermit/stdio.rs b/library/std/src/sys/pal/hermit/stdio.rs index ac54385e8cec..3ea00f5cc5ec 100644 --- a/library/std/src/sys/pal/hermit/stdio.rs +++ b/library/std/src/sys/pal/hermit/stdio.rs @@ -1,6 +1,9 @@ -use super::abi; +use super::hermit_abi; use crate::io; use crate::io::{IoSlice, IoSliceMut}; +use crate::mem::ManuallyDrop; +use crate::os::hermit::io::FromRawFd; +use crate::sys::fd::FileDesc; pub struct Stdin; pub struct Stdout; @@ -13,12 +16,14 @@ impl Stdin { } impl io::Read for Stdin { - fn read(&mut self, data: &mut [u8]) -> io::Result { - self.read_vectored(&mut [IoSliceMut::new(data)]) + fn read(&mut self, buf: &mut [u8]) -> io::Result { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDIN_FILENO)).read(buf) } } - fn read_vectored(&mut self, _data: &mut [IoSliceMut<'_>]) -> io::Result { - Ok(0) + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDIN_FILENO)).read_vectored(bufs) + } } #[inline] @@ -34,27 +39,13 @@ impl Stdout { } impl io::Write for Stdout { - fn write(&mut self, data: &[u8]) -> io::Result { - let len; - - unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print")) - } else { - Ok(len as usize) - } + fn write(&mut self, buf: &[u8]) -> io::Result { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDOUT_FILENO)).write(buf) } } - fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - let len; - - unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print")) - } else { - Ok(len as usize) + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDOUT_FILENO)).write_vectored(bufs) } } @@ -75,27 +66,13 @@ impl Stderr { } impl io::Write for Stderr { - fn write(&mut self, data: &[u8]) -> io::Result { - let len; - - unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print")) - } else { - Ok(len as usize) - } + fn write(&mut self, buf: &[u8]) -> io::Result { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDERR_FILENO)).write(buf) } } - fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - let len; - - unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print")) - } else { - Ok(len as usize) + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDERR_FILENO)).write_vectored(bufs) } } @@ -109,10 +86,10 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = 0; +pub const STDIN_BUF_SIZE: usize = 128; -pub fn is_ebadf(_err: &io::Error) -> bool { - true +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(hermit_abi::EBADF) } pub fn panic_output() -> Option { diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index 4fe6b12a95b0..07a843a597ea 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] -use super::abi; +use super::hermit_abi; use super::thread_local_dtor::run_dtors; use crate::ffi::CStr; use crate::io; @@ -9,7 +9,7 @@ use crate::num::NonZero; use crate::ptr; use crate::time::Duration; -pub type Tid = abi::Tid; +pub type Tid = hermit_abi::Tid; pub struct Thread { tid: Tid, @@ -27,10 +27,10 @@ impl Thread { core_id: isize, ) -> io::Result { let p = Box::into_raw(Box::new(p)); - let tid = abi::spawn2( + let tid = hermit_abi::spawn2( thread_start, p.expose_provenance(), - abi::Priority::into(abi::NORMAL_PRIO), + hermit_abi::Priority::into(hermit_abi::NORMAL_PRIO), stack, core_id, ); @@ -62,7 +62,7 @@ impl Thread { #[inline] pub fn yield_now() { unsafe { - abi::yield_now(); + hermit_abi::yield_now(); } } @@ -74,13 +74,13 @@ impl Thread { #[inline] pub fn sleep(dur: Duration) { unsafe { - abi::usleep(dur.as_micros() as u64); + hermit_abi::usleep(dur.as_micros() as u64); } } pub fn join(self) { unsafe { - let _ = abi::join(self.tid); + let _ = hermit_abi::join(self.tid); } } @@ -98,5 +98,5 @@ impl Thread { } pub fn available_parallelism() -> io::Result> { - unsafe { Ok(NonZero::new_unchecked(abi::get_processor_count())) } + unsafe { Ok(NonZero::new_unchecked(hermit_abi::available_parallelism())) } } diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index d6f9e4c1476b..e0cb7c2aa98a 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -1,13 +1,13 @@ #![allow(dead_code)] -use super::abi; -use super::abi::timespec; -use super::abi::{CLOCK_MONOTONIC, CLOCK_REALTIME, NSEC_PER_SEC}; +use super::hermit_abi::{self, timespec, CLOCK_MONOTONIC, CLOCK_REALTIME}; use crate::cmp::Ordering; use crate::ops::{Add, AddAssign, Sub, SubAssign}; use crate::time::Duration; use core::hash::{Hash, Hasher}; +const NSEC_PER_SEC: i32 = 1_000_000_000; + #[derive(Copy, Clone, Debug)] struct Timespec { t: timespec, @@ -18,8 +18,8 @@ impl Timespec { Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } } } - const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { - assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); + const fn new(tv_sec: i64, tv_nsec: i32) -> Timespec { + assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC); // SAFETY: The assert above checks tv_nsec is within the valid range Timespec { t: timespec { tv_sec: tv_sec, tv_nsec: tv_nsec } } } @@ -34,7 +34,7 @@ impl Timespec { } else { Duration::new( (self.t.tv_sec - 1 - other.t.tv_sec) as u64, - self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.t.tv_nsec as u32, + (self.t.tv_nsec + NSEC_PER_SEC - other.t.tv_nsec) as u32, ) }) } else { @@ -50,9 +50,9 @@ impl Timespec { // Nano calculations can't overflow because nanos are <1B which fit // in a u32. - let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; - if nsec >= NSEC_PER_SEC as u32 { - nsec -= NSEC_PER_SEC as u32; + let mut nsec = other.subsec_nanos() + u32::try_from(self.t.tv_nsec).unwrap(); + if nsec >= NSEC_PER_SEC.try_into().unwrap() { + nsec -= u32::try_from(NSEC_PER_SEC).unwrap(); secs = secs.checked_add(1)?; } Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } }) @@ -106,7 +106,8 @@ pub struct Instant(Timespec); impl Instant { pub fn now() -> Instant { let mut time: Timespec = Timespec::zero(); - let _ = unsafe { abi::clock_gettime(CLOCK_MONOTONIC, core::ptr::addr_of_mut!(time.t)) }; + let _ = + unsafe { hermit_abi::clock_gettime(CLOCK_MONOTONIC, core::ptr::addr_of_mut!(time.t)) }; Instant(time) } @@ -201,13 +202,14 @@ pub struct SystemTime(Timespec); pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero()); impl SystemTime { - pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { + pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime { SystemTime(Timespec::new(tv_sec, tv_nsec)) } pub fn now() -> SystemTime { let mut time: Timespec = Timespec::zero(); - let _ = unsafe { abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) }; + let _ = + unsafe { hermit_abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) }; SystemTime(time) } diff --git a/library/std/src/sys/pal/sgx/os.rs b/library/std/src/sys/pal/sgx/os.rs index 86f4c7d3d56d..c021300d4ae3 100644 --- a/library/std/src/sys/pal/sgx/os.rs +++ b/library/std/src/sys/pal/sgx/os.rs @@ -157,13 +157,13 @@ pub fn getenv(k: &OsStr) -> Option { get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned()) } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { let (k, v) = (k.to_owned(), v.to_owned()); create_env_store().lock().unwrap().insert(k, v); Ok(()) } -pub fn unsetenv(k: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { if let Some(env) = get_env_store() { env.lock().unwrap().remove(k); } diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index 7d271e6d2b65..446cdd18b7e4 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -15,7 +15,7 @@ pub use self::task_queue::JoinNotifier; mod task_queue { use super::wait_notify; - use crate::sync::{Mutex, MutexGuard, Once}; + use crate::sync::{Mutex, MutexGuard}; pub type JoinHandle = wait_notify::Waiter; @@ -28,12 +28,12 @@ mod task_queue { } pub(super) struct Task { - p: Box, + p: Box, done: JoinNotifier, } impl Task { - pub(super) fn new(p: Box) -> (Task, JoinHandle) { + pub(super) fn new(p: Box) -> (Task, JoinHandle) { let (done, recv) = wait_notify::new(); let done = JoinNotifier(Some(done)); (Task { p, done }, recv) @@ -45,18 +45,12 @@ mod task_queue { } } - #[cfg_attr(test, linkage = "available_externally")] - #[export_name = "_ZN16__rust_internals3std3sys3sgx6thread15TASK_QUEUE_INITE"] - static TASK_QUEUE_INIT: Once = Once::new(); #[cfg_attr(test, linkage = "available_externally")] #[export_name = "_ZN16__rust_internals3std3sys3sgx6thread10TASK_QUEUEE"] - static mut TASK_QUEUE: Option>> = None; + static TASK_QUEUE: Mutex> = Mutex::new(Vec::new()); pub(super) fn lock() -> MutexGuard<'static, Vec> { - unsafe { - TASK_QUEUE_INIT.call_once(|| TASK_QUEUE = Some(Default::default())); - TASK_QUEUE.as_ref().unwrap().lock().unwrap() - } + TASK_QUEUE.lock().unwrap() } } @@ -101,7 +95,7 @@ pub mod wait_notify { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, p: Box) -> io::Result { + pub unsafe fn new(_stack: usize, p: Box) -> io::Result { let mut queue_lock = task_queue::lock(); unsafe { usercalls::launch_thread()? }; let (task, handle) = task_queue::Task::new(p); diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index ef35d8788a23..ac90aae4ebe4 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -191,7 +191,7 @@ pub fn getenv(k: &OsStr) -> Option { .flatten() } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { run_with_cstr(k.as_bytes(), &|k| { run_with_cstr(v.as_bytes(), &|v| { let _guard = ENV_LOCK.write(); @@ -200,7 +200,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { }) } -pub fn unsetenv(n: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { run_with_cstr(n.as_bytes(), &|nbuf| { let _guard = ENV_LOCK.write(); cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs index e54a92f01f86..3be0846a6dd4 100644 --- a/library/std/src/sys/pal/teeos/os.rs +++ b/library/std/src/sys/pal/teeos/os.rs @@ -109,11 +109,11 @@ pub fn getenv(_: &OsStr) -> Option { None } -pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { Err(io::Error::new(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } -pub fn unsetenv(_: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { Err(io::Error::new(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 58838c5876eb..0b27977df2fd 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -203,11 +203,11 @@ pub fn getenv(_: &OsStr) -> Option { None } -pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } -pub fn unsetenv(_: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/pal/uefi/thread.rs index edc736978a12..7d4006ff4b2f 100644 --- a/library/std/src/sys/pal/uefi/thread.rs +++ b/library/std/src/sys/pal/uefi/thread.rs @@ -7,7 +7,7 @@ use crate::time::Duration; pub struct Thread(!); -pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; +pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements diff --git a/library/std/src/sys/pal/unix/alloc.rs b/library/std/src/sys/pal/unix/alloc.rs index 2f908e3d0e95..eb3a57c212b4 100644 --- a/library/std/src/sys/pal/unix/alloc.rs +++ b/library/std/src/sys/pal/unix/alloc.rs @@ -59,10 +59,9 @@ unsafe impl GlobalAlloc for System { } cfg_if::cfg_if! { - // We use posix_memalign wherever possible, but not all targets have that function. + // We use posix_memalign wherever possible, but some targets have very incomplete POSIX coverage + // so we need a fallback for those. if #[cfg(any( - target_os = "redox", - target_os = "espidf", target_os = "horizon", target_os = "vita", ))] { @@ -74,12 +73,11 @@ cfg_if::cfg_if! { #[inline] unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { let mut out = ptr::null_mut(); - // We prefer posix_memalign over aligned_malloc since with aligned_malloc, - // implementations are making almost arbitrary choices for which alignments are - // "supported", making it hard to use. For instance, some implementations require the - // size to be a multiple of the alignment (wasi emmalloc), while others require the - // alignment to be at least the pointer size (Illumos, macOS) -- which may or may not be - // standards-compliant, but that does not help us. + // We prefer posix_memalign over aligned_alloc since it is more widely available, and + // since with aligned_alloc, implementations are making almost arbitrary choices for + // which alignments are "supported", making it hard to use. For instance, some + // implementations require the size to be a multiple of the alignment (wasi emmalloc), + // while others require the alignment to be at least the pointer size (Illumos, macOS). // posix_memalign only has one, clear requirement: that the alignment be a multiple of // `sizeof(void*)`. Since these are all powers of 2, we can just use max. let align = layout.align().max(crate::mem::size_of::()); diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs index 2a3298e8b4c9..db2ec73148e3 100644 --- a/library/std/src/sys/pal/unix/args.rs +++ b/library/std/src/sys/pal/unix/args.rs @@ -5,8 +5,9 @@ #![allow(dead_code)] // runtime init functions not used during testing -use crate::ffi::OsString; +use crate::ffi::{CStr, OsString}; use crate::fmt; +use crate::os::unix::ffi::OsStringExt; use crate::vec; /// One-time global initialization. @@ -16,7 +17,46 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) { /// Returns the command line arguments pub fn args() -> Args { - imp::args() + let (argc, argv) = imp::argc_argv(); + + let mut vec = Vec::with_capacity(argc as usize); + + for i in 0..argc { + // SAFETY: `argv` is non-null if `argc` is positive, and it is + // guaranteed to be at least as long as `argc`, so reading from it + // should be safe. + let ptr = unsafe { argv.offset(i).read() }; + + // Some C commandline parsers (e.g. GLib and Qt) are replacing already + // handled arguments in `argv` with `NULL` and move them to the end. + // + // Since they can't directly ensure updates to `argc` as well, this + // means that `argc` might be bigger than the actual number of + // non-`NULL` pointers in `argv` at this point. + // + // To handle this we simply stop iterating at the first `NULL` + // argument. `argv` is also guaranteed to be `NULL`-terminated so any + // non-`NULL` arguments after the first `NULL` can safely be ignored. + if ptr.is_null() { + // NOTE: On Apple platforms, `-[NSProcessInfo arguments]` does not + // stop iterating here, but instead `continue`, always iterating + // up until it reached `argc`. + // + // This difference will only matter in very specific circumstances + // where `argc`/`argv` have been modified, but in unexpected ways, + // so it likely doesn't really matter which option we choose. + // See the following PR for further discussion: + // + break; + } + + // SAFETY: Just checked that the pointer is not NULL, and arguments + // are otherwise guaranteed to be valid C strings. + let cstr = unsafe { CStr::from_ptr(ptr) }; + vec.push(OsStringExt::from_vec(cstr.to_bytes().to_vec())); + } + + Args { iter: vec.into_iter() } } pub struct Args { @@ -75,9 +115,7 @@ impl DoubleEndedIterator for Args { target_os = "hurd", ))] mod imp { - use super::Args; - use crate::ffi::{CStr, OsString}; - use crate::os::unix::prelude::*; + use crate::ffi::c_char; use crate::ptr; use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering}; @@ -126,162 +164,78 @@ mod imp { init_wrapper }; - pub fn args() -> Args { - Args { iter: clone().into_iter() } - } + pub fn argc_argv() -> (isize, *const *const c_char) { + // Load ARGC and ARGV, which hold the unmodified system-provided + // argc/argv, so we can read the pointed-to memory without atomics or + // synchronization. + // + // If either ARGC or ARGV is still zero or null, then either there + // really are no arguments, or someone is asking for `args()` before + // initialization has completed, and we return an empty list. + let argv = ARGV.load(Ordering::Relaxed); + let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) }; - fn clone() -> Vec { - unsafe { - // Load ARGC and ARGV, which hold the unmodified system-provided - // argc/argv, so we can read the pointed-to memory without atomics - // or synchronization. - // - // If either ARGC or ARGV is still zero or null, then either there - // really are no arguments, or someone is asking for `args()` - // before initialization has completed, and we return an empty - // list. - let argv = ARGV.load(Ordering::Relaxed); - let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) }; - let mut args = Vec::with_capacity(argc as usize); - for i in 0..argc { - let ptr = *argv.offset(i) as *const libc::c_char; - - // Some C commandline parsers (e.g. GLib and Qt) are replacing already - // handled arguments in `argv` with `NULL` and move them to the end. That - // means that `argc` might be bigger than the actual number of non-`NULL` - // pointers in `argv` at this point. - // - // To handle this we simply stop iterating at the first `NULL` argument. - // - // `argv` is also guaranteed to be `NULL`-terminated so any non-`NULL` arguments - // after the first `NULL` can safely be ignored. - if ptr.is_null() { - break; - } - - let cstr = CStr::from_ptr(ptr); - args.push(OsStringExt::from_vec(cstr.to_bytes().to_vec())); - } - - args - } + // Cast from `*mut *const u8` to `*const *const c_char` + (argc, argv.cast()) } } +// Use `_NSGetArgc` and `_NSGetArgv` on Apple platforms. +// +// Even though these have underscores in their names, they've been available +// since since the first versions of both macOS and iOS, and are declared in +// the header `crt_externs.h`. +// +// NOTE: This header was added to the iOS 13.0 SDK, which has been the source +// of a great deal of confusion in the past about the availability of these +// APIs. +// +// NOTE(madsmtm): This has not strictly been verified to not cause App Store +// rejections; if this is found to be the case, the previous implementation +// of this used `[[NSProcessInfo processInfo] arguments]`. #[cfg(target_vendor = "apple")] mod imp { - use super::Args; - use crate::ffi::CStr; + use crate::ffi::{c_char, c_int}; - pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} - - #[cfg(target_os = "macos")] - pub fn args() -> Args { - use crate::os::unix::prelude::*; - extern "C" { - // These functions are in crt_externs.h. - fn _NSGetArgc() -> *mut libc::c_int; - fn _NSGetArgv() -> *mut *mut *mut libc::c_char; - } - - let vec = unsafe { - let (argc, argv) = - (*_NSGetArgc() as isize, *_NSGetArgv() as *const *const libc::c_char); - (0..argc as isize) - .map(|i| { - let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec(); - OsStringExt::from_vec(bytes) - }) - .collect::>() - }; - Args { iter: vec.into_iter() } + pub unsafe fn init(_argc: isize, _argv: *const *const u8) { + // No need to initialize anything in here, `libdyld.dylib` has already + // done the work for us. } - // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs - // and use underscores in their names - they're most probably - // are considered private and therefore should be avoided. - // Here is another way to get arguments using the Objective-C - // runtime. - // - // In general it looks like: - // res = Vec::new() - // let args = [[NSProcessInfo processInfo] arguments] - // for i in (0..[args count]) - // res.push([args objectAtIndex:i]) - // res - #[cfg(not(target_os = "macos"))] - pub fn args() -> Args { - use crate::ffi::{c_char, c_void, OsString}; - use crate::mem; - use crate::str; - - type Sel = *const c_void; - type NsId = *const c_void; - type NSUInteger = usize; - + pub fn argc_argv() -> (isize, *const *const c_char) { extern "C" { - fn sel_registerName(name: *const c_char) -> Sel; - fn objc_getClass(class_name: *const c_char) -> NsId; - - // This must be transmuted to an appropriate function pointer type before being called. - fn objc_msgSend(); + // These functions are in crt_externs.h. + fn _NSGetArgc() -> *mut c_int; + fn _NSGetArgv() -> *mut *mut *mut c_char; } - const MSG_SEND_PTR: unsafe extern "C" fn() = objc_msgSend; - const MSG_SEND_NO_ARGUMENTS_RETURN_PTR: unsafe extern "C" fn(NsId, Sel) -> *const c_void = - unsafe { mem::transmute(MSG_SEND_PTR) }; - const MSG_SEND_NO_ARGUMENTS_RETURN_NSUINTEGER: unsafe extern "C" fn( - NsId, - Sel, - ) -> NSUInteger = unsafe { mem::transmute(MSG_SEND_PTR) }; - const MSG_SEND_NSINTEGER_ARGUMENT_RETURN_PTR: unsafe extern "C" fn( - NsId, - Sel, - NSUInteger, - ) - -> *const c_void = unsafe { mem::transmute(MSG_SEND_PTR) }; + // SAFETY: The returned pointer points to a static initialized early + // in the program lifetime by `libdyld.dylib`, and as such is always + // valid. + // + // NOTE: Similar to `_NSGetEnviron`, there technically isn't anything + // protecting us against concurrent modifications to this, and there + // doesn't exist a lock that we can take. Instead, it is generally + // expected that it's only modified in `main` / before other code + // runs, so reading this here should be fine. + let argc = unsafe { _NSGetArgc().read() }; + // SAFETY: Same as above. + let argv = unsafe { _NSGetArgv().read() }; - let mut res = Vec::new(); - - unsafe { - let process_info_sel = sel_registerName(c"processInfo".as_ptr()); - let arguments_sel = sel_registerName(c"arguments".as_ptr()); - let count_sel = sel_registerName(c"count".as_ptr()); - let object_at_index_sel = sel_registerName(c"objectAtIndex:".as_ptr()); - let utf8string_sel = sel_registerName(c"UTF8String".as_ptr()); - - let klass = objc_getClass(c"NSProcessInfo".as_ptr()); - // `+[NSProcessInfo processInfo]` returns an object with +0 retain count, so no need to manually `retain/release`. - let info = MSG_SEND_NO_ARGUMENTS_RETURN_PTR(klass, process_info_sel); - - // `-[NSProcessInfo arguments]` returns an object with +0 retain count, so no need to manually `retain/release`. - let args = MSG_SEND_NO_ARGUMENTS_RETURN_PTR(info, arguments_sel); - - let cnt = MSG_SEND_NO_ARGUMENTS_RETURN_NSUINTEGER(args, count_sel); - for i in 0..cnt { - // `-[NSArray objectAtIndex:]` returns an object whose lifetime is tied to the array, so no need to manually `retain/release`. - let ns_string = - MSG_SEND_NSINTEGER_ARGUMENT_RETURN_PTR(args, object_at_index_sel, i); - // The lifetime of this pointer is tied to the NSString, as well as the current autorelease pool, which is why we heap-allocate the string below. - let utf_c_str: *const c_char = - MSG_SEND_NO_ARGUMENTS_RETURN_PTR(ns_string, utf8string_sel).cast(); - let bytes = CStr::from_ptr(utf_c_str).to_bytes(); - res.push(OsString::from(str::from_utf8(bytes).unwrap())) - } - } - - Args { iter: res.into_iter() } + // Cast from `*mut *mut c_char` to `*const *const c_char` + (argc as isize, argv.cast()) } } #[cfg(any(target_os = "espidf", target_os = "vita"))] mod imp { - use super::Args; + use crate::ffi::c_char; + use crate::ptr; #[inline(always)] pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} - pub fn args() -> Args { - Args { iter: Vec::new().into_iter() } + pub fn argc_argv() -> (isize, *const *const c_char) { + (0, ptr::null()) } } diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index fbbd40bfb796..035c92bc84bf 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -22,16 +22,12 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; #[cfg(any(all(target_os = "linux", target_env = "gnu"), target_vendor = "apple"))] use crate::sys::weak::syscall; -#[cfg(any(target_os = "android", target_os = "macos", target_os = "solaris"))] +#[cfg(target_os = "android")] use crate::sys::weak::weak; use libc::{c_int, mode_t}; -#[cfg(any( - target_os = "solaris", - all(target_os = "linux", target_env = "gnu"), - target_vendor = "apple", -))] +#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_vendor = "apple"))] use libc::c_char; #[cfg(any( all(target_os = "linux", not(target_env = "musl")), @@ -1481,29 +1477,33 @@ impl FromRawFd for File { impl fmt::Debug for File { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - #[cfg(any( - target_os = "linux", - target_os = "netbsd", - target_os = "illumos", - target_os = "solaris" - ))] + #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris"))] fn get_path(fd: c_int) -> Option { let mut p = PathBuf::from("/proc/self/fd"); p.push(&fd.to_string()); readlink(&p).ok() } - #[cfg(target_vendor = "apple")] + #[cfg(any(target_vendor = "apple", target_os = "netbsd"))] fn get_path(fd: c_int) -> Option { // FIXME: The use of PATH_MAX is generally not encouraged, but it - // is inevitable in this case because Apple targets define `fcntl` + // is inevitable in this case because Apple targets and NetBSD define `fcntl` // with `F_GETPATH` in terms of `MAXPATHLEN`, and there are no // alternatives. If a better method is invented, it should be used // instead. let mut buf = vec![0; libc::PATH_MAX as usize]; let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }; if n == -1 { - return None; + cfg_if::cfg_if! { + if #[cfg(target_os = "netbsd")] { + // fallback to procfs as last resort + let mut p = PathBuf::from("/proc/self/fd"); + p.push(&fd.to_string()); + return readlink(&p).ok(); + } else { + return None; + } + } } let l = buf.iter().position(|&c| c == 0).unwrap(); buf.truncate(l as usize); @@ -1557,6 +1557,8 @@ impl fmt::Debug for File { target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", + target_os = "solaris", + target_os = "illumos", target_vendor = "apple", ))] fn get_mode(fd: c_int) -> Option<(bool, bool)> { @@ -1579,6 +1581,8 @@ impl fmt::Debug for File { target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", + target_os = "solaris", + target_os = "illumos", target_vendor = "apple", )))] fn get_mode(_fd: c_int) -> Option<(bool, bool)> { @@ -1745,19 +1749,6 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { // Android has `linkat` on newer versions, but we happen to know `link` // always has the correct behavior, so it's here as well. cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; - } else if #[cfg(any(target_os = "macos", target_os = "solaris"))] { - // MacOS (<=10.9) and Solaris 10 lack support for linkat while newer - // versions have it. We want to use linkat if it is available, so we use weak! - // to check. `linkat` is preferable to `link` because it gives us a flag to - // specify how symlinks should be handled. We pass 0 as the flags argument, - // meaning it shouldn't follow symlinks. - weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int); - - if let Some(f) = linkat.get() { - cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?; - } else { - cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; - }; } else { // Where we can, use `linkat` instead of `link`; see the comment above // this one for details on why. @@ -1910,8 +1901,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { // The code below ensures that `FreeOnDrop` is never a null pointer unsafe { // `copyfile_state_free` returns -1 if the `to` or `from` files - // cannot be closed. However, this is not considered this an - // error. + // cannot be closed. However, this is not considered an error. libc::copyfile_state_free(self.0); } } diff --git a/library/std/src/sys/pal/unix/kernel_copy.rs b/library/std/src/sys/pal/unix/kernel_copy.rs index 18acd5ecccd5..cd38b7c07e2b 100644 --- a/library/std/src/sys/pal/unix/kernel_copy.rs +++ b/library/std/src/sys/pal/unix/kernel_copy.rs @@ -560,6 +560,12 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> // We store the availability in a global to avoid unnecessary syscalls static HAS_COPY_FILE_RANGE: AtomicU8 = AtomicU8::new(NOT_PROBED); + let mut have_probed = match HAS_COPY_FILE_RANGE.load(Ordering::Relaxed) { + NOT_PROBED => false, + UNAVAILABLE => return CopyResult::Fallback(0), + _ => true, + }; + syscall! { fn copy_file_range( fd_in: libc::c_int, @@ -571,25 +577,22 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> ) -> libc::ssize_t } - match HAS_COPY_FILE_RANGE.load(Ordering::Relaxed) { - NOT_PROBED => { - // EPERM can indicate seccomp filters or an immutable file. - // To distinguish these cases we probe with invalid file descriptors which should result in EBADF if the syscall is supported - // and some other error (ENOSYS or EPERM) if it's not available - let result = unsafe { - cvt(copy_file_range(INVALID_FD, ptr::null_mut(), INVALID_FD, ptr::null_mut(), 1, 0)) - }; - - if matches!(result.map_err(|e| e.raw_os_error()), Err(Some(EBADF))) { - HAS_COPY_FILE_RANGE.store(AVAILABLE, Ordering::Relaxed); - } else { - HAS_COPY_FILE_RANGE.store(UNAVAILABLE, Ordering::Relaxed); - return CopyResult::Fallback(0); - } + fn probe_copy_file_range_support() -> u8 { + // In some cases, we cannot determine availability from the first + // `copy_file_range` call. In this case, we probe with an invalid file + // descriptor so that the results are easily interpretable. + match unsafe { + cvt(copy_file_range(INVALID_FD, ptr::null_mut(), INVALID_FD, ptr::null_mut(), 1, 0)) + .map_err(|e| e.raw_os_error()) + } { + Err(Some(EPERM | ENOSYS)) => UNAVAILABLE, + Err(Some(EBADF)) => AVAILABLE, + Ok(_) => panic!("unexpected copy_file_range probe success"), + // Treat other errors as the syscall + // being unavailable. + Err(_) => UNAVAILABLE, } - UNAVAILABLE => return CopyResult::Fallback(0), - _ => {} - }; + } let mut written = 0u64; while written < max_len { @@ -604,6 +607,11 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> cvt(copy_file_range(reader, ptr::null_mut(), writer, ptr::null_mut(), bytes_to_copy, 0)) }; + if !have_probed && copy_result.is_ok() { + have_probed = true; + HAS_COPY_FILE_RANGE.store(AVAILABLE, Ordering::Relaxed); + } + match copy_result { Ok(0) if written == 0 => { // fallback to work around several kernel bugs where copy_file_range will fail to @@ -619,7 +627,28 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> return match err.raw_os_error() { // when file offset + max_length > u64::MAX Some(EOVERFLOW) => CopyResult::Fallback(written), - Some(ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF) if written == 0 => { + Some(raw_os_error @ (ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF)) + if written == 0 => + { + if !have_probed { + let available = if matches!(raw_os_error, ENOSYS | EOPNOTSUPP | EPERM) { + // EPERM can indicate seccomp filters or an + // immutable file. To distinguish these + // cases we probe with invalid file + // descriptors which should result in EBADF + // if the syscall is supported and EPERM or + // ENOSYS if it's not available. + // + // For EOPNOTSUPP, see below. In the case of + // ENOSYS, we try to cover for faulty FUSE + // drivers. + probe_copy_file_range_support() + } else { + AVAILABLE + }; + HAS_COPY_FILE_RANGE.store(available, Ordering::Relaxed); + } + // Try fallback io::copy if either: // - Kernel version is < 4.5 (ENOSYS¹) // - Files are mounted on different fs (EXDEV) diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 21f233e22620..735ed96bc7b1 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -399,14 +399,13 @@ cfg_if::cfg_if! { // Use libumem for the (malloc-compatible) allocator #[link(name = "umem")] extern "C" {} - } else if #[cfg(target_os = "macos")] { + } else if #[cfg(target_vendor = "apple")] { + // Link to `libSystem.dylib`. + // + // Don't get confused by the presence of `System.framework`, + // it is a deprecated wrapper over the dynamic library. #[link(name = "System")] extern "C" {} - } else if #[cfg(all(target_vendor = "apple", not(target_os = "macos")))] { - #[link(name = "System")] - #[link(name = "objc")] - #[link(name = "Foundation", kind = "framework")] - extern "C" {} } else if #[cfg(target_os = "fuchsia")] { #[link(name = "zircon")] #[link(name = "fdio")] diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 3a281525f8df..2e71ceceb58b 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -462,21 +462,21 @@ pub fn current_exe() -> io::Result { #[cfg(target_os = "haiku")] pub fn current_exe() -> io::Result { + let mut name = vec![0; libc::PATH_MAX as usize]; unsafe { - let mut info: mem::MaybeUninit = mem::MaybeUninit::uninit(); - let mut cookie: i32 = 0; - // the executable can be found at team id 0 - let result = libc::_get_next_image_info( - 0, - &mut cookie, - info.as_mut_ptr(), - mem::size_of::(), + let result = libc::find_path( + crate::ptr::null_mut(), + libc::path_base_directory::B_FIND_PATH_IMAGE_PATH, + crate::ptr::null_mut(), + name.as_mut_ptr(), + name.len(), ); - if result != 0 { + if result != libc::B_OK { use crate::io::ErrorKind; Err(io::const_io_error!(ErrorKind::Uncategorized, "Error getting executable path")) } else { - let name = CStr::from_ptr((*info.as_ptr()).name.as_ptr()).to_bytes(); + // find_path adds the null terminator. + let name = CStr::from_ptr(name.as_ptr()).to_bytes(); Ok(PathBuf::from(OsStr::from_bytes(name))) } } @@ -576,12 +576,36 @@ impl Iterator for Env { } } -#[cfg(target_os = "macos")] +// Use `_NSGetEnviron` on Apple platforms. +// +// `_NSGetEnviron` is the documented alternative (see `man environ`), and has +// been available since the first versions of both macOS and iOS. +// +// Nowadays, specifically since macOS 10.8, `environ` has been exposed through +// `libdyld.dylib`, which is linked via. `libSystem.dylib`: +// +// +// So in the end, it likely doesn't really matter which option we use, but the +// performance cost of using `_NSGetEnviron` is extremely miniscule, and it +// might be ever so slightly more supported, so let's just use that. +// +// NOTE: The header where this is defined (`crt_externs.h`) was added to the +// iOS 13.0 SDK, which has been the source of a great deal of confusion in the +// past about the availability of this API. +// +// NOTE(madsmtm): Neither this nor using `environ` has been verified to not +// cause App Store rejections; if this is found to be the case, an alternative +// implementation of this is possible using `[NSProcessInfo environment]` +// - which internally uses `_NSGetEnviron` and a system-wide lock on the +// environment variables to protect against `setenv`, so using that might be +// desirable anyhow? Though it also means that we have to link to Foundation. +#[cfg(target_vendor = "apple")] pub unsafe fn environ() -> *mut *const *const c_char { libc::_NSGetEnviron() as *mut *const *const c_char } -#[cfg(not(target_os = "macos"))] +// Use the `environ` static which is part of POSIX. +#[cfg(not(target_vendor = "apple"))] pub unsafe fn environ() -> *mut *const *const c_char { extern "C" { static mut environ: *const *const c_char; @@ -651,19 +675,19 @@ pub fn getenv(k: &OsStr) -> Option { .flatten() } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { run_with_cstr(k.as_bytes(), &|k| { run_with_cstr(v.as_bytes(), &|v| { let _guard = ENV_LOCK.write(); - cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) + cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) }) }) } -pub fn unsetenv(n: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { run_with_cstr(n.as_bytes(), &|nbuf| { let _guard = ENV_LOCK.write(); - cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) + cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) }) } diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs index e2fca8c7e63d..72bda90a9ba7 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -1053,6 +1053,10 @@ fn signal_string(signal: i32) -> &'static str { libc::SIGINFO => " (SIGINFO)", #[cfg(target_os = "hurd")] libc::SIGLOST => " (SIGLOST)", + #[cfg(target_os = "freebsd")] + libc::SIGTHR => " (SIGTHR)", + #[cfg(target_os = "freebsd")] + libc::SIGLIBRT => " (SIGLIBRT)", _ => "", } } diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 26c49257ad00..2e5bd85327a1 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -491,6 +491,14 @@ mod imp { } } +// This is intentionally not enabled on iOS/tvOS/watchOS/visionOS, as it uses +// several symbols that might lead to rejections from the App Store, namely +// `sigaction`, `sigaltstack`, `sysctlbyname`, `mmap`, `munmap` and `mprotect`. +// +// This might be overly cautious, though it is also what Swift does (and they +// usually have fewer qualms about forwards compatibility, since the runtime +// is shipped with the OS): +// #[cfg(not(any( target_os = "linux", target_os = "freebsd", diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 853ef8736de2..1ab54ec57c3b 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -717,5 +717,14 @@ unsafe fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { #[cfg(target_os = "netbsd")] unsafe fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { - 2048 // just a guess + static STACK: crate::sync::OnceLock = crate::sync::OnceLock::new(); + + *STACK.get_or_init(|| { + let mut stack = unsafe { libc::sysconf(libc::_SC_THREAD_STACK_MIN) }; + if stack < 0 { + stack = 2048; // just a guess + } + + stack as usize + }) } diff --git a/library/std/src/sys/pal/unsupported/os.rs b/library/std/src/sys/pal/unsupported/os.rs index 248b34829f2e..3be98898bbeb 100644 --- a/library/std/src/sys/pal/unsupported/os.rs +++ b/library/std/src/sys/pal/unsupported/os.rs @@ -96,11 +96,11 @@ pub fn getenv(_: &OsStr) -> Option { None } -pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } -pub fn unsetenv(_: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/pal/unsupported/thread.rs index ea939247199c..89f8bad7026e 100644 --- a/library/std/src/sys/pal/unsupported/thread.rs +++ b/library/std/src/sys/pal/unsupported/thread.rs @@ -6,7 +6,7 @@ use crate::time::Duration; pub struct Thread(!); -pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; +pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index ee377b6ef791..e96296997e6a 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -244,7 +244,7 @@ pub fn getenv(k: &OsStr) -> Option { .flatten() } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { run_with_cstr(k.as_bytes(), &|k| { run_with_cstr(v.as_bytes(), &|v| unsafe { let _guard = env_write_lock(); @@ -253,7 +253,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { }) } -pub fn unsetenv(n: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { run_with_cstr(n.as_bytes(), &|nbuf| unsafe { let _guard = env_write_lock(); cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index d45fb28b67e6..975eef2451f4 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -66,7 +66,7 @@ cfg_if::cfg_if! { } } -pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; +pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements diff --git a/library/std/src/sys/pal/wasm/atomics/thread.rs b/library/std/src/sys/pal/wasm/atomics/thread.rs index 49f936f14498..484bd08495ee 100644 --- a/library/std/src/sys/pal/wasm/atomics/thread.rs +++ b/library/std/src/sys/pal/wasm/atomics/thread.rs @@ -6,7 +6,7 @@ use crate::time::Duration; pub struct Thread(!); -pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; +pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs index 555ad581b856..17a0e47ad595 100644 --- a/library/std/src/sys/pal/windows/api.rs +++ b/library/std/src/sys/pal/windows/api.rs @@ -251,3 +251,39 @@ pub fn get_last_error() -> WinError { pub struct WinError { pub code: u32, } +impl WinError { + const fn new(code: u32) -> Self { + Self { code } + } +} + +// Error code constants. +// The constant names should be the same as the winapi constants except for the leading `ERROR_`. +// Due to the sheer number of codes, error codes should only be added here on an as-needed basis. +// However, they should never be removed as the assumption is they may be useful again in the future. +#[allow(unused)] +impl WinError { + /// Success is not an error. + /// Some Windows APIs do use this to distinguish between a zero return and an error return + /// but we should never return this to users as an error. + pub const SUCCESS: Self = Self::new(c::ERROR_SUCCESS); + // tidy-alphabetical-start + pub const ACCESS_DENIED: Self = Self::new(c::ERROR_ACCESS_DENIED); + pub const ALREADY_EXISTS: Self = Self::new(c::ERROR_ALREADY_EXISTS); + pub const CANT_ACCESS_FILE: Self = Self::new(c::ERROR_CANT_ACCESS_FILE); + pub const DELETE_PENDING: Self = Self::new(c::ERROR_DELETE_PENDING); + pub const DIRECTORY: Self = Self::new(c::ERROR_DIRECTORY); + pub const FILE_NOT_FOUND: Self = Self::new(c::ERROR_FILE_NOT_FOUND); + pub const INSUFFICIENT_BUFFER: Self = Self::new(c::ERROR_INSUFFICIENT_BUFFER); + pub const INVALID_FUNCTION: Self = Self::new(c::ERROR_INVALID_FUNCTION); + pub const INVALID_HANDLE: Self = Self::new(c::ERROR_INVALID_HANDLE); + pub const INVALID_PARAMETER: Self = Self::new(c::ERROR_INVALID_PARAMETER); + pub const NO_MORE_FILES: Self = Self::new(c::ERROR_NO_MORE_FILES); + pub const NOT_FOUND: Self = Self::new(c::ERROR_NOT_FOUND); + pub const NOT_SUPPORTED: Self = Self::new(c::ERROR_NOT_SUPPORTED); + pub const OPERATION_ABORTED: Self = Self::new(c::ERROR_OPERATION_ABORTED); + pub const PATH_NOT_FOUND: Self = Self::new(c::ERROR_PATH_NOT_FOUND); + pub const SHARING_VIOLATION: Self = Self::new(c::ERROR_SHARING_VIOLATION); + pub const TIMEOUT: Self = Self::new(c::ERROR_TIMEOUT); + // tidy-alphabetical-end +} diff --git a/library/std/src/sys/pal/windows/c/README.md b/library/std/src/sys/pal/windows/c/README.md index d458e55efbcd..efefc5faba7a 100644 --- a/library/std/src/sys/pal/windows/c/README.md +++ b/library/std/src/sys/pal/windows/c/README.md @@ -3,7 +3,7 @@ be edited manually. To add bindings, edit `bindings.txt` then regenerate using the following command: - ./x run generate-windows-sys && ./x fmt library/std + ./x run generate-windows-sys && ./x fmt If you need to override generated functions or types then add them to `library/std/src/sys/pal/windows/c.rs`. diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 1da8871ae44e..19f013d3347e 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.56.0 +// Bindings generated by `windows-bindgen` 0.57.0 #![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] #[link(name = "advapi32")] @@ -841,6 +841,7 @@ extern "system" { pub const ABOVE_NORMAL_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 32768u32; pub type ADDRESS_FAMILY = u16; #[repr(C)] +#[derive(Clone, Copy)] pub struct ADDRINFOA { pub ai_flags: i32, pub ai_family: i32, @@ -851,18 +852,13 @@ pub struct ADDRINFOA { pub ai_addr: *mut SOCKADDR, pub ai_next: *mut ADDRINFOA, } -impl Copy for ADDRINFOA {} -impl Clone for ADDRINFOA { - fn clone(&self) -> Self { - *self - } -} pub const AF_INET: ADDRESS_FAMILY = 2u16; pub const AF_INET6: ADDRESS_FAMILY = 23u16; pub const AF_UNIX: u16 = 1u16; pub const AF_UNSPEC: ADDRESS_FAMILY = 0u16; pub const ALL_PROCESSOR_GROUPS: u16 = 65535u16; #[repr(C)] +#[derive(Clone, Copy)] pub union ARM64_NT_NEON128 { pub Anonymous: ARM64_NT_NEON128_0, pub D: [f64; 2], @@ -870,27 +866,17 @@ pub union ARM64_NT_NEON128 { pub H: [u16; 8], pub B: [u8; 16], } -impl Copy for ARM64_NT_NEON128 {} -impl Clone for ARM64_NT_NEON128 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct ARM64_NT_NEON128_0 { pub Low: u64, pub High: i64, } -impl Copy for ARM64_NT_NEON128_0 {} -impl Clone for ARM64_NT_NEON128_0 { - fn clone(&self) -> Self { - *self - } -} pub const BELOW_NORMAL_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 16384u32; pub type BOOL = i32; pub type BOOLEAN = u8; #[repr(C)] +#[derive(Clone, Copy)] pub struct BY_HANDLE_FILE_INFORMATION { pub dwFileAttributes: u32, pub ftCreationTime: FILETIME, @@ -903,41 +889,26 @@ pub struct BY_HANDLE_FILE_INFORMATION { pub nFileIndexHigh: u32, pub nFileIndexLow: u32, } -impl Copy for BY_HANDLE_FILE_INFORMATION {} -impl Clone for BY_HANDLE_FILE_INFORMATION { - fn clone(&self) -> Self { - *self - } -} pub const CALLBACK_CHUNK_FINISHED: LPPROGRESS_ROUTINE_CALLBACK_REASON = 0u32; pub const CALLBACK_STREAM_SWITCH: LPPROGRESS_ROUTINE_CALLBACK_REASON = 1u32; pub type COMPARESTRING_RESULT = i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct CONDITION_VARIABLE { pub Ptr: *mut core::ffi::c_void, } -impl Copy for CONDITION_VARIABLE {} -impl Clone for CONDITION_VARIABLE { - fn clone(&self) -> Self { - *self - } -} pub type CONSOLE_MODE = u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct CONSOLE_READCONSOLE_CONTROL { pub nLength: u32, pub nInitialChars: u32, pub dwCtrlWakeupMask: u32, pub dwControlKeyState: u32, } -impl Copy for CONSOLE_READCONSOLE_CONTROL {} -impl Clone for CONSOLE_READCONSOLE_CONTROL { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(target_arch = "aarch64")] +#[derive(Clone, Copy)] pub struct CONTEXT { pub ContextFlags: CONTEXT_FLAGS, pub Cpsr: u32, @@ -952,30 +923,16 @@ pub struct CONTEXT { pub Wcr: [u32; 2], pub Wvr: [u64; 2], } -#[cfg(target_arch = "aarch64")] -impl Copy for CONTEXT {} -#[cfg(target_arch = "aarch64")] -impl Clone for CONTEXT { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(target_arch = "aarch64")] +#[derive(Clone, Copy)] pub union CONTEXT_0 { pub Anonymous: CONTEXT_0_0, pub X: [u64; 31], } -#[cfg(target_arch = "aarch64")] -impl Copy for CONTEXT_0 {} -#[cfg(target_arch = "aarch64")] -impl Clone for CONTEXT_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(target_arch = "aarch64")] +#[derive(Clone, Copy)] pub struct CONTEXT_0_0 { pub X0: u64, pub X1: u64, @@ -1009,16 +966,9 @@ pub struct CONTEXT_0_0 { pub Fp: u64, pub Lr: u64, } -#[cfg(target_arch = "aarch64")] -impl Copy for CONTEXT_0_0 {} -#[cfg(target_arch = "aarch64")] -impl Clone for CONTEXT_0_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] pub struct CONTEXT { pub P1Home: u64, pub P2Home: u64, @@ -1067,30 +1017,16 @@ pub struct CONTEXT { pub LastExceptionToRip: u64, pub LastExceptionFromRip: u64, } -#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] -impl Copy for CONTEXT {} -#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] -impl Clone for CONTEXT { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] pub union CONTEXT_0 { pub FltSave: XSAVE_FORMAT, pub Anonymous: CONTEXT_0_0, } -#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] -impl Copy for CONTEXT_0 {} -#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] -impl Clone for CONTEXT_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] pub struct CONTEXT_0_0 { pub Header: [M128A; 2], pub Legacy: [M128A; 8], @@ -1111,16 +1047,9 @@ pub struct CONTEXT_0_0 { pub Xmm14: M128A, pub Xmm15: M128A, } -#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] -impl Copy for CONTEXT_0_0 {} -#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] -impl Clone for CONTEXT_0_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(target_arch = "x86")] +#[derive(Clone, Copy)] pub struct CONTEXT { pub ContextFlags: CONTEXT_FLAGS, pub Dr0: u32, @@ -1148,14 +1077,6 @@ pub struct CONTEXT { pub SegSs: u32, pub ExtendedRegisters: [u8; 512], } -#[cfg(target_arch = "x86")] -impl Copy for CONTEXT {} -#[cfg(target_arch = "x86")] -impl Clone for CONTEXT { - fn clone(&self) -> Self { - *self - } -} pub type CONTEXT_FLAGS = u32; pub const CP_UTF8: u32 = 65001u32; pub const CREATE_ALWAYS: FILE_CREATION_DISPOSITION = 2u32; @@ -3068,6 +2989,7 @@ pub const ERROR_XML_PARSE_ERROR: WIN32_ERROR = 1465u32; pub type EXCEPTION_DISPOSITION = i32; pub const EXCEPTION_MAXIMUM_PARAMETERS: u32 = 15u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct EXCEPTION_RECORD { pub ExceptionCode: NTSTATUS, pub ExceptionFlags: u32, @@ -3076,12 +2998,6 @@ pub struct EXCEPTION_RECORD { pub NumberParameters: u32, pub ExceptionInformation: [usize; 15], } -impl Copy for EXCEPTION_RECORD {} -impl Clone for EXCEPTION_RECORD { - fn clone(&self) -> Self { - *self - } -} pub const EXCEPTION_STACK_OVERFLOW: NTSTATUS = 0xC00000FD_u32 as _; pub const EXTENDED_STARTUPINFO_PRESENT: PROCESS_CREATION_FLAGS = 524288u32; pub const E_NOTIMPL: HRESULT = 0x80004001_u32 as _; @@ -3095,40 +3011,25 @@ pub const FALSE: BOOL = 0i32; pub type FARPROC = Option isize>; pub const FAST_FAIL_FATAL_APP_EXIT: u32 = 7u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FD_SET { pub fd_count: u32, pub fd_array: [SOCKET; 64], } -impl Copy for FD_SET {} -impl Clone for FD_SET { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct FILETIME { pub dwLowDateTime: u32, pub dwHighDateTime: u32, } -impl Copy for FILETIME {} -impl Clone for FILETIME { - fn clone(&self) -> Self { - *self - } -} pub type FILE_ACCESS_RIGHTS = u32; pub const FILE_ADD_FILE: FILE_ACCESS_RIGHTS = 2u32; pub const FILE_ADD_SUBDIRECTORY: FILE_ACCESS_RIGHTS = 4u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_ALLOCATION_INFO { pub AllocationSize: i64, } -impl Copy for FILE_ALLOCATION_INFO {} -impl Clone for FILE_ALLOCATION_INFO { - fn clone(&self) -> Self { - *self - } -} pub const FILE_ALL_ACCESS: FILE_ACCESS_RIGHTS = 2032127u32; pub const FILE_APPEND_DATA: FILE_ACCESS_RIGHTS = 4u32; pub const FILE_ATTRIBUTE_ARCHIVE: FILE_FLAGS_AND_ATTRIBUTES = 32u32; @@ -3151,20 +3052,16 @@ pub const FILE_ATTRIBUTE_REPARSE_POINT: FILE_FLAGS_AND_ATTRIBUTES = 1024u32; pub const FILE_ATTRIBUTE_SPARSE_FILE: FILE_FLAGS_AND_ATTRIBUTES = 512u32; pub const FILE_ATTRIBUTE_SYSTEM: FILE_FLAGS_AND_ATTRIBUTES = 4u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_ATTRIBUTE_TAG_INFO { pub FileAttributes: u32, pub ReparseTag: u32, } -impl Copy for FILE_ATTRIBUTE_TAG_INFO {} -impl Clone for FILE_ATTRIBUTE_TAG_INFO { - fn clone(&self) -> Self { - *self - } -} pub const FILE_ATTRIBUTE_TEMPORARY: FILE_FLAGS_AND_ATTRIBUTES = 256u32; pub const FILE_ATTRIBUTE_UNPINNED: FILE_FLAGS_AND_ATTRIBUTES = 1048576u32; pub const FILE_ATTRIBUTE_VIRTUAL: FILE_FLAGS_AND_ATTRIBUTES = 65536u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_BASIC_INFO { pub CreationTime: i64, pub LastAccessTime: i64, @@ -3172,12 +3069,6 @@ pub struct FILE_BASIC_INFO { pub ChangeTime: i64, pub FileAttributes: u32, } -impl Copy for FILE_BASIC_INFO {} -impl Clone for FILE_BASIC_INFO { - fn clone(&self) -> Self { - *self - } -} pub const FILE_BEGIN: SET_FILE_POINTER_MOVE_METHOD = 0u32; pub const FILE_COMPLETE_IF_OPLOCKED: NTCREATEFILE_CREATE_OPTIONS = 256u32; pub const FILE_CONTAINS_EXTENDED_CREATE_INFORMATION: NTCREATEFILE_CREATE_OPTIONS = 268435456u32; @@ -3197,37 +3088,22 @@ pub const FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE: FILE_DISPOSITION_INFO pub const FILE_DISPOSITION_FLAG_ON_CLOSE: FILE_DISPOSITION_INFO_EX_FLAGS = 8u32; pub const FILE_DISPOSITION_FLAG_POSIX_SEMANTICS: FILE_DISPOSITION_INFO_EX_FLAGS = 2u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_DISPOSITION_INFO { pub DeleteFile: BOOLEAN, } -impl Copy for FILE_DISPOSITION_INFO {} -impl Clone for FILE_DISPOSITION_INFO { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_DISPOSITION_INFO_EX { pub Flags: FILE_DISPOSITION_INFO_EX_FLAGS, } -impl Copy for FILE_DISPOSITION_INFO_EX {} -impl Clone for FILE_DISPOSITION_INFO_EX { - fn clone(&self) -> Self { - *self - } -} pub type FILE_DISPOSITION_INFO_EX_FLAGS = u32; pub const FILE_END: SET_FILE_POINTER_MOVE_METHOD = 2u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_END_OF_FILE_INFO { pub EndOfFile: i64, } -impl Copy for FILE_END_OF_FILE_INFO {} -impl Clone for FILE_END_OF_FILE_INFO { - fn clone(&self) -> Self { - *self - } -} pub const FILE_EXECUTE: FILE_ACCESS_RIGHTS = 32u32; pub type FILE_FLAGS_AND_ATTRIBUTES = u32; pub const FILE_FLAG_BACKUP_SEMANTICS: FILE_FLAGS_AND_ATTRIBUTES = 33554432u32; @@ -3246,6 +3122,7 @@ pub const FILE_GENERIC_EXECUTE: FILE_ACCESS_RIGHTS = 1179808u32; pub const FILE_GENERIC_READ: FILE_ACCESS_RIGHTS = 1179785u32; pub const FILE_GENERIC_WRITE: FILE_ACCESS_RIGHTS = 1179926u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_ID_BOTH_DIR_INFO { pub NextEntryOffset: u32, pub FileIndex: u32, @@ -3263,23 +3140,12 @@ pub struct FILE_ID_BOTH_DIR_INFO { pub FileId: i64, pub FileName: [u16; 1], } -impl Copy for FILE_ID_BOTH_DIR_INFO {} -impl Clone for FILE_ID_BOTH_DIR_INFO { - fn clone(&self) -> Self { - *self - } -} pub type FILE_INFO_BY_HANDLE_CLASS = i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_IO_PRIORITY_HINT_INFO { pub PriorityHint: PRIORITY_HINT, } -impl Copy for FILE_IO_PRIORITY_HINT_INFO {} -impl Clone for FILE_IO_PRIORITY_HINT_INFO { - fn clone(&self) -> Self { - *self - } -} pub const FILE_LIST_DIRECTORY: FILE_ACCESS_RIGHTS = 1u32; pub const FILE_NAME_NORMALIZED: GETFINALPATHNAMEBYHANDLE_FLAGS = 0u32; pub const FILE_NAME_OPENED: GETFINALPATHNAMEBYHANDLE_FLAGS = 8u32; @@ -3310,6 +3176,7 @@ pub const FILE_SHARE_NONE: FILE_SHARE_MODE = 0u32; pub const FILE_SHARE_READ: FILE_SHARE_MODE = 1u32; pub const FILE_SHARE_WRITE: FILE_SHARE_MODE = 2u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_STANDARD_INFO { pub AllocationSize: i64, pub EndOfFile: i64, @@ -3317,12 +3184,6 @@ pub struct FILE_STANDARD_INFO { pub DeletePending: BOOLEAN, pub Directory: BOOLEAN, } -impl Copy for FILE_STANDARD_INFO {} -impl Clone for FILE_STANDARD_INFO { - fn clone(&self) -> Self { - *self - } -} pub const FILE_SUPERSEDE: NTCREATEFILE_CREATE_DISPOSITION = 0u32; pub const FILE_SYNCHRONOUS_IO_ALERT: NTCREATEFILE_CREATE_OPTIONS = 16u32; pub const FILE_SYNCHRONOUS_IO_NONALERT: NTCREATEFILE_CREATE_OPTIONS = 32u32; @@ -3340,6 +3201,7 @@ pub const FILE_WRITE_THROUGH: NTCREATEFILE_CREATE_OPTIONS = 2u32; pub const FIONBIO: i32 = -2147195266i32; #[repr(C)] #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] pub struct FLOATING_SAVE_AREA { pub ControlWord: u32, pub StatusWord: u32, @@ -3351,16 +3213,9 @@ pub struct FLOATING_SAVE_AREA { pub RegisterArea: [u8; 80], pub Cr0NpxState: u32, } -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] -impl Copy for FLOATING_SAVE_AREA {} -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] -impl Clone for FLOATING_SAVE_AREA { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(target_arch = "x86")] +#[derive(Clone, Copy)] pub struct FLOATING_SAVE_AREA { pub ControlWord: u32, pub StatusWord: u32, @@ -3372,14 +3227,6 @@ pub struct FLOATING_SAVE_AREA { pub RegisterArea: [u8; 80], pub Spare0: u32, } -#[cfg(target_arch = "x86")] -impl Copy for FLOATING_SAVE_AREA {} -#[cfg(target_arch = "x86")] -impl Clone for FLOATING_SAVE_AREA { - fn clone(&self) -> Self { - *self - } -} pub const FORMAT_MESSAGE_ALLOCATE_BUFFER: FORMAT_MESSAGE_OPTIONS = 256u32; pub const FORMAT_MESSAGE_ARGUMENT_ARRAY: FORMAT_MESSAGE_OPTIONS = 8192u32; pub const FORMAT_MESSAGE_FROM_HMODULE: FORMAT_MESSAGE_OPTIONS = 2048u32; @@ -3422,18 +3269,13 @@ pub const GENERIC_READ: GENERIC_ACCESS_RIGHTS = 2147483648u32; pub const GENERIC_WRITE: GENERIC_ACCESS_RIGHTS = 1073741824u32; pub type GETFINALPATHNAMEBYHANDLE_FLAGS = u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct GUID { pub data1: u32, pub data2: u16, pub data3: u16, pub data4: [u8; 8], } -impl Copy for GUID {} -impl Clone for GUID { - fn clone(&self) -> Self { - *self - } -} impl GUID { pub const fn from_u128(uuid: u128) -> Self { Self { @@ -3454,112 +3296,67 @@ pub type HMODULE = *mut core::ffi::c_void; pub type HRESULT = i32; pub const IDLE_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 64u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct IN6_ADDR { pub u: IN6_ADDR_0, } -impl Copy for IN6_ADDR {} -impl Clone for IN6_ADDR { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub union IN6_ADDR_0 { pub Byte: [u8; 16], pub Word: [u16; 8], } -impl Copy for IN6_ADDR_0 {} -impl Clone for IN6_ADDR_0 { - fn clone(&self) -> Self { - *self - } -} pub const INFINITE: u32 = 4294967295u32; pub const INHERIT_CALLER_PRIORITY: PROCESS_CREATION_FLAGS = 131072u32; pub const INHERIT_PARENT_AFFINITY: PROCESS_CREATION_FLAGS = 65536u32; #[repr(C)] +#[derive(Clone, Copy)] pub union INIT_ONCE { pub Ptr: *mut core::ffi::c_void, } -impl Copy for INIT_ONCE {} -impl Clone for INIT_ONCE { - fn clone(&self) -> Self { - *self - } -} pub const INIT_ONCE_INIT_FAILED: u32 = 4u32; pub const INVALID_FILE_ATTRIBUTES: u32 = 4294967295u32; pub const INVALID_SOCKET: SOCKET = -1i32 as _; #[repr(C)] +#[derive(Clone, Copy)] pub struct IN_ADDR { pub S_un: IN_ADDR_0, } -impl Copy for IN_ADDR {} -impl Clone for IN_ADDR { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub union IN_ADDR_0 { pub S_un_b: IN_ADDR_0_0, pub S_un_w: IN_ADDR_0_1, pub S_addr: u32, } -impl Copy for IN_ADDR_0 {} -impl Clone for IN_ADDR_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct IN_ADDR_0_0 { pub s_b1: u8, pub s_b2: u8, pub s_b3: u8, pub s_b4: u8, } -impl Copy for IN_ADDR_0_0 {} -impl Clone for IN_ADDR_0_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct IN_ADDR_0_1 { pub s_w1: u16, pub s_w2: u16, } -impl Copy for IN_ADDR_0_1 {} -impl Clone for IN_ADDR_0_1 { - fn clone(&self) -> Self { - *self - } -} pub const IO_REPARSE_TAG_MOUNT_POINT: u32 = 2684354563u32; pub const IO_REPARSE_TAG_SYMLINK: u32 = 2684354572u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct IO_STATUS_BLOCK { pub Anonymous: IO_STATUS_BLOCK_0, pub Information: usize, } -impl Copy for IO_STATUS_BLOCK {} -impl Clone for IO_STATUS_BLOCK { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub union IO_STATUS_BLOCK_0 { pub Status: NTSTATUS, pub Pointer: *mut core::ffi::c_void, } -impl Copy for IO_STATUS_BLOCK_0 {} -impl Clone for IO_STATUS_BLOCK_0 { - fn clone(&self) -> Self { - *self - } -} pub type IPPROTO = i32; pub const IPPROTO_AH: IPPROTO = 51i32; pub const IPPROTO_CBT: IPPROTO = 7i32; @@ -3601,45 +3398,30 @@ pub const IPPROTO_UDP: IPPROTO = 17i32; pub const IPV6_ADD_MEMBERSHIP: i32 = 12i32; pub const IPV6_DROP_MEMBERSHIP: i32 = 13i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct IPV6_MREQ { pub ipv6mr_multiaddr: IN6_ADDR, pub ipv6mr_interface: u32, } -impl Copy for IPV6_MREQ {} -impl Clone for IPV6_MREQ { - fn clone(&self) -> Self { - *self - } -} pub const IPV6_MULTICAST_LOOP: i32 = 11i32; pub const IPV6_V6ONLY: i32 = 27i32; pub const IP_ADD_MEMBERSHIP: i32 = 12i32; pub const IP_DROP_MEMBERSHIP: i32 = 13i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct IP_MREQ { pub imr_multiaddr: IN_ADDR, pub imr_interface: IN_ADDR, } -impl Copy for IP_MREQ {} -impl Clone for IP_MREQ { - fn clone(&self) -> Self { - *self - } -} pub const IP_MULTICAST_LOOP: i32 = 11i32; pub const IP_MULTICAST_TTL: i32 = 10i32; pub const IP_TTL: i32 = 4i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct LINGER { pub l_onoff: u16, pub l_linger: u16, } -impl Copy for LINGER {} -impl Clone for LINGER { - fn clone(&self) -> Self { - *self - } -} pub type LPOVERLAPPED_COMPLETION_ROUTINE = Option< unsafe extern "system" fn( dwerrorcode: u32, @@ -3673,16 +3455,11 @@ pub type LPWSAOVERLAPPED_COMPLETION_ROUTINE = Option< ), >; #[repr(C)] +#[derive(Clone, Copy)] pub struct M128A { pub Low: u64, pub High: i64, } -impl Copy for M128A {} -impl Clone for M128A { - fn clone(&self) -> Self { - *self - } -} pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: u32 = 16384u32; pub const MAX_PATH: u32 = 260u32; pub const MB_COMPOSITE: MULTI_BYTE_TO_WIDE_CHAR_FLAGS = 2u32; @@ -3710,6 +3487,7 @@ pub type NTCREATEFILE_CREATE_DISPOSITION = u32; pub type NTCREATEFILE_CREATE_OPTIONS = u32; pub type NTSTATUS = i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct OBJECT_ATTRIBUTES { pub Length: u32, pub RootDirectory: HANDLE, @@ -3718,50 +3496,29 @@ pub struct OBJECT_ATTRIBUTES { pub SecurityDescriptor: *const core::ffi::c_void, pub SecurityQualityOfService: *const core::ffi::c_void, } -impl Copy for OBJECT_ATTRIBUTES {} -impl Clone for OBJECT_ATTRIBUTES { - fn clone(&self) -> Self { - *self - } -} pub const OBJ_DONT_REPARSE: i32 = 4096i32; pub const OPEN_ALWAYS: FILE_CREATION_DISPOSITION = 4u32; pub const OPEN_EXISTING: FILE_CREATION_DISPOSITION = 3u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct OVERLAPPED { pub Internal: usize, pub InternalHigh: usize, pub Anonymous: OVERLAPPED_0, pub hEvent: HANDLE, } -impl Copy for OVERLAPPED {} -impl Clone for OVERLAPPED { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub union OVERLAPPED_0 { pub Anonymous: OVERLAPPED_0_0, pub Pointer: *mut core::ffi::c_void, } -impl Copy for OVERLAPPED_0 {} -impl Clone for OVERLAPPED_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct OVERLAPPED_0_0 { pub Offset: u32, pub OffsetHigh: u32, } -impl Copy for OVERLAPPED_0_0 {} -impl Clone for OVERLAPPED_0_0 { - fn clone(&self) -> Self { - *self - } -} pub type PCSTR = *const u8; pub type PCWSTR = *const u16; pub type PIO_APC_ROUTINE = Option< @@ -3788,18 +3545,13 @@ pub type PRIORITY_HINT = i32; pub type PROCESSOR_ARCHITECTURE = u16; pub type PROCESS_CREATION_FLAGS = u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct PROCESS_INFORMATION { pub hProcess: HANDLE, pub hThread: HANDLE, pub dwProcessId: u32, pub dwThreadId: u32, } -impl Copy for PROCESS_INFORMATION {} -impl Clone for PROCESS_INFORMATION { - fn clone(&self) -> Self { - *self - } -} pub const PROCESS_MODE_BACKGROUND_BEGIN: PROCESS_CREATION_FLAGS = 1048576u32; pub const PROCESS_MODE_BACKGROUND_END: PROCESS_CREATION_FLAGS = 2097152u32; pub const PROFILE_KERNEL: PROCESS_CREATION_FLAGS = 536870912u32; @@ -3822,17 +3574,12 @@ pub const SD_RECEIVE: WINSOCK_SHUTDOWN_HOW = 0i32; pub const SD_SEND: WINSOCK_SHUTDOWN_HOW = 1i32; pub const SECURITY_ANONYMOUS: FILE_FLAGS_AND_ATTRIBUTES = 0u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct SECURITY_ATTRIBUTES { pub nLength: u32, pub lpSecurityDescriptor: *mut core::ffi::c_void, pub bInheritHandle: BOOL, } -impl Copy for SECURITY_ATTRIBUTES {} -impl Clone for SECURITY_ATTRIBUTES { - fn clone(&self) -> Self { - *self - } -} pub const SECURITY_CONTEXT_TRACKING: FILE_FLAGS_AND_ATTRIBUTES = 262144u32; pub const SECURITY_DELEGATION: FILE_FLAGS_AND_ATTRIBUTES = 196608u32; pub const SECURITY_EFFECTIVE_ONLY: FILE_FLAGS_AND_ATTRIBUTES = 524288u32; @@ -3843,27 +3590,17 @@ pub const SECURITY_VALID_SQOS_FLAGS: FILE_FLAGS_AND_ATTRIBUTES = 2031616u32; pub type SEND_RECV_FLAGS = i32; pub type SET_FILE_POINTER_MOVE_METHOD = u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct SOCKADDR { pub sa_family: ADDRESS_FAMILY, pub sa_data: [i8; 14], } -impl Copy for SOCKADDR {} -impl Clone for SOCKADDR { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct SOCKADDR_UN { pub sun_family: ADDRESS_FAMILY, pub sun_path: [i8; 108], } -impl Copy for SOCKADDR_UN {} -impl Clone for SOCKADDR_UN { - fn clone(&self) -> Self { - *self - } -} pub type SOCKET = usize; pub const SOCKET_ERROR: i32 = -1i32; pub const SOCK_DGRAM: WINSOCK_SOCKET_TYPE = 2i32; @@ -3879,15 +3616,10 @@ pub const SO_RCVTIMEO: i32 = 4102i32; pub const SO_SNDTIMEO: i32 = 4101i32; pub const SPECIFIC_RIGHTS_ALL: FILE_ACCESS_RIGHTS = 65535u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct SRWLOCK { pub Ptr: *mut core::ffi::c_void, } -impl Copy for SRWLOCK {} -impl Clone for SRWLOCK { - fn clone(&self) -> Self { - *self - } -} pub const STACK_SIZE_PARAM_IS_A_RESERVATION: THREAD_CREATION_FLAGS = 65536u32; pub const STANDARD_RIGHTS_ALL: FILE_ACCESS_RIGHTS = 2031616u32; pub const STANDARD_RIGHTS_EXECUTE: FILE_ACCESS_RIGHTS = 131072u32; @@ -3909,17 +3641,13 @@ pub const STARTF_USESHOWWINDOW: STARTUPINFOW_FLAGS = 1u32; pub const STARTF_USESIZE: STARTUPINFOW_FLAGS = 2u32; pub const STARTF_USESTDHANDLES: STARTUPINFOW_FLAGS = 256u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct STARTUPINFOEXW { pub StartupInfo: STARTUPINFOW, pub lpAttributeList: LPPROC_THREAD_ATTRIBUTE_LIST, } -impl Copy for STARTUPINFOEXW {} -impl Clone for STARTUPINFOEXW { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct STARTUPINFOW { pub cb: u32, pub lpReserved: PWSTR, @@ -3940,12 +3668,6 @@ pub struct STARTUPINFOW { pub hStdOutput: HANDLE, pub hStdError: HANDLE, } -impl Copy for STARTUPINFOW {} -impl Clone for STARTUPINFOW { - fn clone(&self) -> Self { - *self - } -} pub type STARTUPINFOW_FLAGS = u32; pub const STATUS_DELETE_PENDING: NTSTATUS = 0xC0000056_u32 as _; pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _; @@ -3964,6 +3686,7 @@ pub const SYMLINK_FLAG_RELATIVE: u32 = 1u32; pub type SYNCHRONIZATION_ACCESS_RIGHTS = u32; pub const SYNCHRONIZE: FILE_ACCESS_RIGHTS = 1048576u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct SYSTEM_INFO { pub Anonymous: SYSTEM_INFO_0, pub dwPageSize: u32, @@ -3976,34 +3699,18 @@ pub struct SYSTEM_INFO { pub wProcessorLevel: u16, pub wProcessorRevision: u16, } -impl Copy for SYSTEM_INFO {} -impl Clone for SYSTEM_INFO { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub union SYSTEM_INFO_0 { pub dwOemId: u32, pub Anonymous: SYSTEM_INFO_0_0, } -impl Copy for SYSTEM_INFO_0 {} -impl Clone for SYSTEM_INFO_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct SYSTEM_INFO_0_0 { pub wProcessorArchitecture: PROCESSOR_ARCHITECTURE, pub wReserved: u16, } -impl Copy for SYSTEM_INFO_0_0 {} -impl Clone for SYSTEM_INFO_0_0 { - fn clone(&self) -> Self { - *self - } -} pub const TCP_NODELAY: i32 = 1i32; pub const THREAD_CREATE_RUN_IMMEDIATELY: THREAD_CREATION_FLAGS = 0u32; pub const THREAD_CREATE_SUSPENDED: THREAD_CREATION_FLAGS = 4u32; @@ -4011,16 +3718,11 @@ pub type THREAD_CREATION_FLAGS = u32; pub const TIMER_ALL_ACCESS: SYNCHRONIZATION_ACCESS_RIGHTS = 2031619u32; pub const TIMER_MODIFY_STATE: SYNCHRONIZATION_ACCESS_RIGHTS = 2u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct TIMEVAL { pub tv_sec: i32, pub tv_usec: i32, } -impl Copy for TIMEVAL {} -impl Clone for TIMEVAL { - fn clone(&self) -> Self { - *self - } -} pub const TLS_OUT_OF_INDEXES: u32 = 4294967295u32; pub type TOKEN_ACCESS_MASK = u32; pub const TOKEN_ACCESS_PSEUDO_HANDLE: TOKEN_ACCESS_MASK = 24u32; @@ -4047,17 +3749,12 @@ pub const TOKEN_WRITE_OWNER: TOKEN_ACCESS_MASK = 524288u32; pub const TRUE: BOOL = 1i32; pub const TRUNCATE_EXISTING: FILE_CREATION_DISPOSITION = 5u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct UNICODE_STRING { pub Length: u16, pub MaximumLength: u16, pub Buffer: PWSTR, } -impl Copy for UNICODE_STRING {} -impl Clone for UNICODE_STRING { - fn clone(&self) -> Self { - *self - } -} pub const VOLUME_NAME_DOS: GETFINALPATHNAMEBYHANDLE_FLAGS = 0u32; pub const VOLUME_NAME_GUID: GETFINALPATHNAMEBYHANDLE_FLAGS = 1u32; pub const VOLUME_NAME_NONE: GETFINALPATHNAMEBYHANDLE_FLAGS = 4u32; @@ -4071,6 +3768,7 @@ pub const WAIT_TIMEOUT: WAIT_EVENT = 258u32; pub const WC_ERR_INVALID_CHARS: u32 = 128u32; pub type WIN32_ERROR = u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct WIN32_FIND_DATAW { pub dwFileAttributes: u32, pub ftCreationTime: FILETIME, @@ -4083,30 +3781,20 @@ pub struct WIN32_FIND_DATAW { pub cFileName: [u16; 260], pub cAlternateFileName: [u16; 14], } -impl Copy for WIN32_FIND_DATAW {} -impl Clone for WIN32_FIND_DATAW { - fn clone(&self) -> Self { - *self - } -} pub type WINSOCK_SHUTDOWN_HOW = i32; pub type WINSOCK_SOCKET_TYPE = i32; pub const WRITE_DAC: FILE_ACCESS_RIGHTS = 262144u32; pub const WRITE_OWNER: FILE_ACCESS_RIGHTS = 524288u32; pub const WSABASEERR: WSA_ERROR = 10000i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct WSABUF { pub len: u32, pub buf: PSTR, } -impl Copy for WSABUF {} -impl Clone for WSABUF { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] pub struct WSADATA { pub wVersion: u16, pub wHighVersion: u16, @@ -4116,16 +3804,9 @@ pub struct WSADATA { pub szDescription: [i8; 257], pub szSystemStatus: [i8; 129], } -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] -impl Copy for WSADATA {} -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] -impl Clone for WSADATA { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(target_arch = "x86")] +#[derive(Clone, Copy)] pub struct WSADATA { pub wVersion: u16, pub wHighVersion: u16, @@ -4135,14 +3816,6 @@ pub struct WSADATA { pub iMaxUdpDg: u16, pub lpVendorInfo: PSTR, } -#[cfg(target_arch = "x86")] -impl Copy for WSADATA {} -#[cfg(target_arch = "x86")] -impl Clone for WSADATA { - fn clone(&self) -> Self { - *self - } -} pub const WSAEACCES: WSA_ERROR = 10013i32; pub const WSAEADDRINUSE: WSA_ERROR = 10048i32; pub const WSAEADDRNOTAVAIL: WSA_ERROR = 10049i32; @@ -4198,17 +3871,13 @@ pub const WSANOTINITIALISED: WSA_ERROR = 10093i32; pub const WSANO_DATA: WSA_ERROR = 11004i32; pub const WSANO_RECOVERY: WSA_ERROR = 11003i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct WSAPROTOCOLCHAIN { pub ChainLen: i32, pub ChainEntries: [u32; 7], } -impl Copy for WSAPROTOCOLCHAIN {} -impl Clone for WSAPROTOCOLCHAIN { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct WSAPROTOCOL_INFOW { pub dwServiceFlags1: u32, pub dwServiceFlags2: u32, @@ -4231,12 +3900,6 @@ pub struct WSAPROTOCOL_INFOW { pub dwProviderReserved: u32, pub szProtocol: [u16; 256], } -impl Copy for WSAPROTOCOL_INFOW {} -impl Clone for WSAPROTOCOL_INFOW { - fn clone(&self) -> Self { - *self - } -} pub const WSASERVICE_NOT_FOUND: WSA_ERROR = 10108i32; pub const WSASYSCALLFAILURE: WSA_ERROR = 10107i32; pub const WSASYSNOTREADY: WSA_ERROR = 10091i32; @@ -4287,6 +3950,7 @@ pub const WSA_WAIT_EVENT_0: WSA_ERROR = 0i32; pub const WSA_WAIT_IO_COMPLETION: WSA_ERROR = 192i32; #[repr(C)] #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] pub struct XSAVE_FORMAT { pub ControlWord: u16, pub StatusWord: u16, @@ -4305,16 +3969,9 @@ pub struct XSAVE_FORMAT { pub XmmRegisters: [M128A; 16], pub Reserved4: [u8; 96], } -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] -impl Copy for XSAVE_FORMAT {} -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] -impl Clone for XSAVE_FORMAT { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(target_arch = "x86")] +#[derive(Clone, Copy)] pub struct XSAVE_FORMAT { pub ControlWord: u16, pub StatusWord: u16, @@ -4333,12 +3990,4 @@ pub struct XSAVE_FORMAT { pub XmmRegisters: [M128A; 8], pub Reserved4: [u8; 224], } -#[cfg(target_arch = "x86")] -impl Copy for XSAVE_FORMAT {} -#[cfg(target_arch = "x86")] -impl Clone for XSAVE_FORMAT { - fn clone(&self) -> Self { - *self - } -} // ignore-tidy-filelength diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index e92c5e80eac9..629ff114b5a8 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -18,7 +18,8 @@ use crate::sys::{c, cvt, Align8}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::thread; -use super::{api, to_u16s, IoResult}; +use super::api::{self, WinError}; +use super::{to_u16s, IoResult}; use crate::sys::path::maybe_verbatim; pub struct File { @@ -130,10 +131,11 @@ impl Iterator for ReadDir { let mut wfd = mem::zeroed(); loop { if c::FindNextFileW(self.handle.0, &mut wfd) == 0 { - if api::get_last_error().code == c::ERROR_NO_MORE_FILES { - return None; - } else { - return Some(Err(Error::last_os_error())); + match api::get_last_error() { + WinError::NO_MORE_FILES => return None, + WinError { code } => { + return Some(Err(Error::from_raw_os_error(code as i32))); + } } } if let Some(e) = DirEntry::new(&self.root, &wfd) { @@ -244,8 +246,6 @@ impl OpenOptions { } fn get_access_mode(&self) -> io::Result { - const ERROR_INVALID_PARAMETER: i32 = 87; - match (self.read, self.write, self.append, self.access_mode) { (.., Some(mode)) => Ok(mode), (true, false, false, None) => Ok(c::GENERIC_READ), @@ -255,23 +255,23 @@ impl OpenOptions { (true, _, true, None) => { Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA)) } - (false, false, false, None) => Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)), + (false, false, false, None) => { + Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)) + } } } fn get_creation_mode(&self) -> io::Result { - const ERROR_INVALID_PARAMETER: i32 = 87; - match (self.write, self.append) { (true, false) => {} (false, false) => { if self.truncate || self.create || self.create_new { - return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)); + return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)); } } (_, true) => { if self.truncate && !self.create_new { - return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)); + return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)); } } } @@ -315,7 +315,7 @@ impl File { // Manual truncation. See #115745. if opts.truncate && creation == c::OPEN_ALWAYS - && unsafe { c::GetLastError() } == c::ERROR_ALREADY_EXISTS + && api::get_last_error() == WinError::ALREADY_EXISTS { unsafe { // This originally used `FileAllocationInfo` instead of @@ -845,7 +845,7 @@ fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result< // We make a special exception for `STATUS_DELETE_PENDING` because // otherwise this will be mapped to `ERROR_ACCESS_DENIED` which is // very unhelpful. - Err(io::Error::from_raw_os_error(c::ERROR_DELETE_PENDING as _)) + Err(io::Error::from_raw_os_error(c::ERROR_DELETE_PENDING as i32)) } else if status == c::STATUS_INVALID_PARAMETER && ATTRIBUTES.load(Ordering::Relaxed) == c::OBJ_DONT_REPARSE { @@ -1097,7 +1097,7 @@ pub fn readdir(p: &Path) -> io::Result { // // See issue #120040: https://github.com/rust-lang/rust/issues/120040. let last_error = api::get_last_error(); - if last_error.code == c::ERROR_FILE_NOT_FOUND { + if last_error == WinError::FILE_NOT_FOUND { return Ok(ReadDir { handle: FindNextFileHandle(find_handle), root: Arc::new(root), diff --git a/library/std/src/sys/pal/windows/futex.rs b/library/std/src/sys/pal/windows/futex.rs index bc19c402d9c1..08b7fe300dc3 100644 --- a/library/std/src/sys/pal/windows/futex.rs +++ b/library/std/src/sys/pal/windows/futex.rs @@ -1,4 +1,4 @@ -use super::api; +use super::api::{self, WinError}; use crate::sys::c; use crate::sys::dur2timeout; use core::ffi::c_void; @@ -72,7 +72,7 @@ pub fn wake_by_address_all(address: &T) { pub fn futex_wait(futex: &W::Atomic, expected: W, timeout: Option) -> bool { // return false only on timeout - wait_on_address(futex, expected, timeout) || api::get_last_error().code != c::ERROR_TIMEOUT + wait_on_address(futex, expected, timeout) || api::get_last_error() != WinError::TIMEOUT } pub fn futex_wake(futex: &T) -> bool { diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs index 64d8b72aed28..62199c16bfed 100644 --- a/library/std/src/sys/pal/windows/os.rs +++ b/library/std/src/sys/pal/windows/os.rs @@ -17,7 +17,8 @@ use crate::ptr; use crate::slice; use crate::sys::{c, cvt}; -use super::{api, to_u16s}; +use super::api::{self, WinError}; +use super::to_u16s; pub fn errno() -> i32 { api::get_last_error().code as i32 @@ -302,16 +303,16 @@ pub fn getenv(k: &OsStr) -> Option { .ok() } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { let k = to_u16s(k)?; let v = to_u16s(v)?; - cvt(unsafe { c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) }).map(drop) + cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop) } -pub fn unsetenv(n: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { let v = to_u16s(n)?; - cvt(unsafe { c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) }).map(drop) + cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop) } pub fn temp_dir() -> PathBuf { @@ -333,7 +334,7 @@ fn home_dir_crt() -> Option { buf, &mut sz, ) { - 0 if api::get_last_error().code != c::ERROR_INSUFFICIENT_BUFFER => 0, + 0 if api::get_last_error() != WinError::INSUFFICIENT_BUFFER => 0, 0 => sz, _ => sz - 1, // sz includes the null terminator } @@ -358,7 +359,7 @@ fn home_dir_crt() -> Option { super::fill_utf16_buf( |buf, mut sz| { match c::GetUserProfileDirectoryW(token, buf, &mut sz) { - 0 if api::get_last_error().code != c::ERROR_INSUFFICIENT_BUFFER => 0, + 0 if api::get_last_error() != WinError::INSUFFICIENT_BUFFER => 0, 0 => sz, _ => sz - 1, // sz includes the null terminator } diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index dfa938d4d576..67ef3ca82da0 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -12,6 +12,7 @@ use crate::sys::c; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; use crate::sys::hashmap_random_keys; +use crate::sys::pal::windows::api::{self, WinError}; use crate::sys_common::{FromInner, IntoInner}; //////////////////////////////////////////////////////////////////////////////// @@ -124,20 +125,19 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res // testing strategy // For more info, see https://github.com/rust-lang/rust/pull/37677. if handle == c::INVALID_HANDLE_VALUE { - let err = io::Error::last_os_error(); - let raw_os_err = err.raw_os_error(); + let error = api::get_last_error(); if tries < 10 { - if raw_os_err == Some(c::ERROR_ACCESS_DENIED as i32) { + if error == WinError::ACCESS_DENIED { continue; } else if reject_remote_clients_flag != 0 - && raw_os_err == Some(c::ERROR_INVALID_PARAMETER as i32) + && error == WinError::INVALID_PARAMETER { reject_remote_clients_flag = 0; tries -= 1; continue; } } - return Err(err); + return Err(io::Error::from_raw_os_error(error.code as i32)); } ours = Handle::from_raw_handle(handle); break; diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs index e4ab2ca7da1c..2da986a1494e 100644 --- a/library/std/src/sys/pal/windows/process.rs +++ b/library/std/src/sys/pal/windows/process.rs @@ -31,6 +31,8 @@ use crate::sys_common::IntoInner; use core::ffi::c_void; +use super::api::{self, WinError}; + //////////////////////////////////////////////////////////////////////////////// // Command //////////////////////////////////////////////////////////////////////////////// @@ -645,12 +647,12 @@ impl Process { pub fn kill(&mut self) -> io::Result<()> { let result = unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) }; if result == c::FALSE { - let error = unsafe { c::GetLastError() }; + let error = api::get_last_error(); // TerminateProcess returns ERROR_ACCESS_DENIED if the process has already been // terminated (by us, or for any other reason). So check if the process was actually // terminated, and if so, do not return an error. - if error != c::ERROR_ACCESS_DENIED || self.try_wait().is_err() { - return Err(crate::io::Error::from_raw_os_error(error as i32)); + if error != WinError::ACCESS_DENIED || self.try_wait().is_err() { + return Err(crate::io::Error::from_raw_os_error(error.code as i32)); } } Ok(()) diff --git a/library/std/src/sys/pal/windows/rand.rs b/library/std/src/sys/pal/windows/rand.rs index e427546222ae..09f527a09bf1 100644 --- a/library/std/src/sys/pal/windows/rand.rs +++ b/library/std/src/sys/pal/windows/rand.rs @@ -1,6 +1,6 @@ +use core::{mem, ptr}; + use crate::sys::c; -use core::mem; -use core::ptr; #[cfg(not(target_vendor = "win7"))] #[inline] diff --git a/library/std/src/sys/pal/windows/stdio.rs b/library/std/src/sys/pal/windows/stdio.rs index 96c23f82aec2..690b60d1decc 100644 --- a/library/std/src/sys/pal/windows/stdio.rs +++ b/library/std/src/sys/pal/windows/stdio.rs @@ -1,6 +1,6 @@ #![unstable(issue = "none", feature = "windows_stdio")] -use super::api; +use super::api::{self, WinError}; use crate::cmp; use crate::io; use crate::mem::MaybeUninit; @@ -370,7 +370,7 @@ fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit]) -> io::Result Option { None } -pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } -pub fn unsetenv(_: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } diff --git a/library/std/src/sys/pal/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs index 759beb2d306b..e7d6cd52a258 100644 --- a/library/std/src/sys/pal/zkvm/os.rs +++ b/library/std/src/sys/pal/zkvm/os.rs @@ -115,11 +115,11 @@ pub fn getenv(varname: &OsStr) -> Option { Some(OsString::from_inner(os_str::Buf { inner: u8s.to_vec() })) } -pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } -pub fn unsetenv(_: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } diff --git a/library/std/src/sys/thread_local/fast_local.rs b/library/std/src/sys/thread_local/fast_local.rs deleted file mode 100644 index 49b51a729e42..000000000000 --- a/library/std/src/sys/thread_local/fast_local.rs +++ /dev/null @@ -1,247 +0,0 @@ -use super::lazy::LazyKeyInner; -use crate::cell::Cell; -use crate::sys::thread_local_dtor::register_dtor; -use crate::{fmt, mem, panic, ptr}; - -#[doc(hidden)] -#[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)] -#[allow_internal_unsafe] -#[unstable(feature = "thread_local_internals", issue = "none")] -#[rustc_macro_transparency = "semitransparent"] -pub macro thread_local_inner { - // used to generate the `LocalKey` value for const-initialized thread locals - (@key $t:ty, const $init:expr) => {{ - #[inline] - #[deny(unsafe_op_in_unsafe_fn)] - unsafe fn __getit( - _init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - const INIT_EXPR: $t = $init; - // If the platform has support for `#[thread_local]`, use it. - #[thread_local] - // We use `UnsafeCell` here instead of `static mut` to ensure any generated TLS shims - // have a nonnull attribute on their return value. - static VAL: $crate::cell::UnsafeCell<$t> = $crate::cell::UnsafeCell::new(INIT_EXPR); - - // If a dtor isn't needed we can do something "very raw" and - // just get going. - if !$crate::mem::needs_drop::<$t>() { - unsafe { - return $crate::option::Option::Some(&*VAL.get()) - } - } - - // 0 == dtor not registered - // 1 == dtor registered, dtor not run - // 2 == dtor registered and is running or has run - #[thread_local] - static STATE: $crate::cell::Cell<$crate::primitive::u8> = $crate::cell::Cell::new(0); - - // Safety: Performs `drop_in_place(ptr as *mut $t)`, and requires - // all that comes with it. - unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) { - $crate::thread::local_impl::abort_on_dtor_unwind(|| { - let old_state = STATE.replace(2); - $crate::debug_assert_eq!(old_state, 1); - // Safety: safety requirement is passed on to caller. - unsafe { $crate::ptr::drop_in_place(ptr.cast::<$t>()); } - }); - } - - unsafe { - match STATE.get() { - // 0 == we haven't registered a destructor, so do - // so now. - 0 => { - $crate::thread::local_impl::Key::<$t>::register_dtor( - VAL.get() as *mut $crate::primitive::u8, - destroy, - ); - STATE.set(1); - $crate::option::Option::Some(&*VAL.get()) - } - // 1 == the destructor is registered and the value - // is valid, so return the pointer. - 1 => $crate::option::Option::Some(&*VAL.get()), - // otherwise the destructor has already run, so we - // can't give access. - _ => $crate::option::Option::None, - } - } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) - } - }}, - - // used to generate the `LocalKey` value for `thread_local!` - (@key $t:ty, $init:expr) => { - { - #[inline] - fn __init() -> $t { $init } - - #[inline] - unsafe fn __getit( - init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - #[thread_local] - static __KEY: $crate::thread::local_impl::Key<$t> = - $crate::thread::local_impl::Key::<$t>::new(); - - unsafe { - __KEY.get(move || { - if let $crate::option::Option::Some(init) = init { - if let $crate::option::Option::Some(value) = init.take() { - return value; - } - if $crate::cfg!(debug_assertions) { - $crate::unreachable!("missing default value"); - } - } - __init() - }) - } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) - } - } - }, - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { - $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); - }, -} - -#[derive(Copy, Clone)] -enum DtorState { - Unregistered, - Registered, - RunningOrHasRun, -} - -// This data structure has been carefully constructed so that the fast path -// only contains one branch on x86. That optimization is necessary to avoid -// duplicated tls lookups on OSX. -// -// LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 -pub struct Key { - // If `LazyKeyInner::get` returns `None`, that indicates either: - // * The value has never been initialized - // * The value is being recursively initialized - // * The value has already been destroyed or is being destroyed - // To determine which kind of `None`, check `dtor_state`. - // - // This is very optimizer friendly for the fast path - initialized but - // not yet dropped. - inner: LazyKeyInner, - - // Metadata to keep track of the state of the destructor. Remember that - // this variable is thread-local, not global. - dtor_state: Cell, -} - -impl fmt::Debug for Key { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Key").finish_non_exhaustive() - } -} -impl Key { - pub const fn new() -> Key { - Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) } - } - - // note that this is just a publicly-callable function only for the - // const-initialized form of thread locals, basically a way to call the - // free `register_dtor` function defined elsewhere in std. - pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - unsafe { - register_dtor(a, dtor); - } - } - - pub unsafe fn get T>(&self, init: F) -> Option<&'static T> { - // SAFETY: See the definitions of `LazyKeyInner::get` and - // `try_initialize` for more information. - // - // The caller must ensure no mutable references are ever active to - // the inner cell or the inner T when this is called. - // The `try_initialize` is dependant on the passed `init` function - // for this. - unsafe { - match self.inner.get() { - Some(val) => Some(val), - None => self.try_initialize(init), - } - } - } - - // `try_initialize` is only called once per fast thread local variable, - // except in corner cases where thread_local dtors reference other - // thread_local's, or it is being recursively initialized. - // - // Macos: Inlining this function can cause two `tlv_get_addr` calls to - // be performed for every call to `Key::get`. - // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 - #[inline(never)] - unsafe fn try_initialize T>(&self, init: F) -> Option<&'static T> { - // SAFETY: See comment above (this function doc). - if !mem::needs_drop::() || unsafe { self.try_register_dtor() } { - // SAFETY: See comment above (this function doc). - Some(unsafe { self.inner.initialize(init) }) - } else { - None - } - } - - // `try_register_dtor` is only called once per fast thread local - // variable, except in corner cases where thread_local dtors reference - // other thread_local's, or it is being recursively initialized. - unsafe fn try_register_dtor(&self) -> bool { - match self.dtor_state.get() { - DtorState::Unregistered => { - // SAFETY: dtor registration happens before initialization. - // Passing `self` as a pointer while using `destroy_value` - // is safe because the function will build a pointer to a - // Key, which is the type of self and so find the correct - // size. - unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::) }; - self.dtor_state.set(DtorState::Registered); - true - } - DtorState::Registered => { - // recursively initialized - true - } - DtorState::RunningOrHasRun => false, - } - } -} - -unsafe extern "C" fn destroy_value(ptr: *mut u8) { - let ptr = ptr as *mut Key; - - // SAFETY: - // - // The pointer `ptr` has been built just above and comes from - // `try_register_dtor` where it is originally a Key coming from `self`, - // making it non-NUL and of the correct type. - // - // Right before we run the user destructor be sure to set the - // `Option` to `None`, and `dtor_state` to `RunningOrHasRun`. This - // causes future calls to `get` to run `try_initialize_drop` again, - // which will now fail, and return `None`. - // - // Wrap the call in a catch to ensure unwinding is caught in the event - // a panic takes place in a destructor. - if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe { - let Key { inner, dtor_state } = &*ptr; - let value = inner.take(); - dtor_state.set(DtorState::RunningOrHasRun); - drop(value); - })) { - rtabort!("thread local panicked on drop"); - } -} diff --git a/library/std/src/sys/thread_local/fast_local/eager.rs b/library/std/src/sys/thread_local/fast_local/eager.rs new file mode 100644 index 000000000000..b97bd9cc88ce --- /dev/null +++ b/library/std/src/sys/thread_local/fast_local/eager.rs @@ -0,0 +1,74 @@ +use crate::cell::{Cell, UnsafeCell}; +use crate::ptr::{self, drop_in_place}; +use crate::sys::thread_local::abort_on_dtor_unwind; +use crate::sys::thread_local_dtor::register_dtor; + +#[derive(Clone, Copy)] +enum State { + Initial, + Alive, + Destroyed, +} + +#[allow(missing_debug_implementations)] +pub struct Storage { + state: Cell, + val: UnsafeCell, +} + +impl Storage { + pub const fn new(val: T) -> Storage { + Storage { state: Cell::new(State::Initial), val: UnsafeCell::new(val) } + } + + /// Get a pointer to the TLS value. If the TLS variable has been destroyed, + /// a null pointer is returned. + /// + /// The resulting pointer may not be used after thread destruction has + /// occurred. + /// + /// # Safety + /// The `self` reference must remain valid until the TLS destructor is run. + #[inline] + pub unsafe fn get(&self) -> *const T { + match self.state.get() { + State::Alive => self.val.get(), + State::Destroyed => ptr::null(), + State::Initial => unsafe { self.initialize() }, + } + } + + #[cold] + unsafe fn initialize(&self) -> *const T { + // Register the destructor + + // SAFETY: + // The caller guarantees that `self` will be valid until thread destruction. + unsafe { + register_dtor(ptr::from_ref(self).cast_mut().cast(), destroy::); + } + + self.state.set(State::Alive); + self.val.get() + } +} + +/// Transition an `Alive` TLS variable into the `Destroyed` state, dropping its +/// value. +/// +/// # Safety +/// * Must only be called at thread destruction. +/// * `ptr` must point to an instance of `Storage` with `Alive` state and be +/// valid for accessing that instance. +unsafe extern "C" fn destroy(ptr: *mut u8) { + // Print a nice abort message if a panic occurs. + abort_on_dtor_unwind(|| { + let storage = unsafe { &*(ptr as *const Storage) }; + // Update the state before running the destructor as it may attempt to + // access the variable. + storage.state.set(State::Destroyed); + unsafe { + drop_in_place(storage.val.get()); + } + }) +} diff --git a/library/std/src/sys/thread_local/fast_local/lazy.rs b/library/std/src/sys/thread_local/fast_local/lazy.rs new file mode 100644 index 000000000000..c1ada35d484d --- /dev/null +++ b/library/std/src/sys/thread_local/fast_local/lazy.rs @@ -0,0 +1,101 @@ +use crate::cell::UnsafeCell; +use crate::hint::unreachable_unchecked; +use crate::ptr; +use crate::sys::thread_local::abort_on_dtor_unwind; +use crate::sys::thread_local_dtor::register_dtor; + +pub unsafe trait DestroyedState: Sized { + fn register_dtor(s: &Storage); +} + +unsafe impl DestroyedState for ! { + fn register_dtor(_: &Storage) {} +} + +unsafe impl DestroyedState for () { + fn register_dtor(s: &Storage) { + unsafe { + register_dtor(ptr::from_ref(s).cast_mut().cast(), destroy::); + } + } +} + +enum State { + Initial, + Alive(T), + Destroyed(D), +} + +#[allow(missing_debug_implementations)] +pub struct Storage { + state: UnsafeCell>, +} + +impl Storage +where + D: DestroyedState, +{ + pub const fn new() -> Storage { + Storage { state: UnsafeCell::new(State::Initial) } + } + + /// Get a pointer to the TLS value, potentially initializing it with the + /// provided parameters. If the TLS variable has been destroyed, a null + /// pointer is returned. + /// + /// The resulting pointer may not be used after reentrant inialialization + /// or thread destruction has occurred. + /// + /// # Safety + /// The `self` reference must remain valid until the TLS destructor is run. + #[inline] + pub unsafe fn get_or_init(&self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { + let state = unsafe { &*self.state.get() }; + match state { + State::Alive(v) => v, + State::Destroyed(_) => ptr::null(), + State::Initial => unsafe { self.initialize(i, f) }, + } + } + + #[cold] + unsafe fn initialize(&self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { + // Perform initialization + + let v = i.and_then(Option::take).unwrap_or_else(f); + + let old = unsafe { self.state.get().replace(State::Alive(v)) }; + match old { + // If the variable is not being recursively initialized, register + // the destructor. This might be a noop if the value does not need + // destruction. + State::Initial => D::register_dtor(self), + // Else, drop the old value. This might be changed to a panic. + val => drop(val), + } + + // SAFETY: the state was just set to `Alive` + unsafe { + let State::Alive(v) = &*self.state.get() else { unreachable_unchecked() }; + v + } + } +} + +/// Transition an `Alive` TLS variable into the `Destroyed` state, dropping its +/// value. +/// +/// # Safety +/// * Must only be called at thread destruction. +/// * `ptr` must point to an instance of `Storage` and be valid for +/// accessing that instance. +unsafe extern "C" fn destroy(ptr: *mut u8) { + // Print a nice abort message if a panic occurs. + abort_on_dtor_unwind(|| { + let storage = unsafe { &*(ptr as *const Storage) }; + // Update the state before running the destructor as it may attempt to + // access the variable. + let val = unsafe { storage.state.get().replace(State::Destroyed(())) }; + drop(val); + }) +} diff --git a/library/std/src/sys/thread_local/fast_local/mod.rs b/library/std/src/sys/thread_local/fast_local/mod.rs new file mode 100644 index 000000000000..575d60de4ee9 --- /dev/null +++ b/library/std/src/sys/thread_local/fast_local/mod.rs @@ -0,0 +1,111 @@ +//! Thread local support for platforms with native TLS. +//! +//! To achieve the best performance, we choose from four different types for +//! the TLS variable, depending on the method of initialization used (`const` +//! or lazy) and the drop requirements of the stored type: +//! +//! | | `Drop` | `!Drop` | +//! |--------:|:--------------------:|:-------------------:| +//! | `const` | `EagerStorage` | `T` | +//! | lazy | `LazyStorage` | `LazyStorage` | +//! +//! For `const` initialization and `!Drop` types, we simply use `T` directly, +//! but for other situations, we implement a state machine to handle +//! initialization of the variable and its destructor and destruction. +//! Upon accessing the TLS variable, the current state is compared: +//! +//! 1. If the state is `Initial`, initialize the storage, transition the state +//! to `Alive` and (if applicable) register the destructor, and return a +//! reference to the value. +//! 2. If the state is `Alive`, initialization was previously completed, so +//! return a reference to the value. +//! 3. If the state is `Destroyed`, the destructor has been run already, so +//! return [`None`]. +//! +//! The TLS destructor sets the state to `Destroyed` and drops the current value. +//! +//! To simplify the code, we make `LazyStorage` generic over the destroyed state +//! and use the `!` type (never type) as type parameter for `!Drop` types. This +//! eliminates the `Destroyed` state for these values, which can allow more niche +//! optimizations to occur for the `State` enum. For `Drop` types, `()` is used. + +#![deny(unsafe_op_in_unsafe_fn)] + +mod eager; +mod lazy; + +pub use eager::Storage as EagerStorage; +pub use lazy::Storage as LazyStorage; + +#[doc(hidden)] +#[allow_internal_unstable( + thread_local_internals, + cfg_target_thread_local, + thread_local, + never_type +)] +#[allow_internal_unsafe] +#[unstable(feature = "thread_local_internals", issue = "none")] +#[rustc_macro_transparency = "semitransparent"] +pub macro thread_local_inner { + // used to generate the `LocalKey` value for const-initialized thread locals + (@key $t:ty, const $init:expr) => {{ + const __INIT: $t = $init; + + unsafe { + use $crate::mem::needs_drop; + use $crate::thread::LocalKey; + use $crate::thread::local_impl::EagerStorage; + + LocalKey::new(const { + if needs_drop::<$t>() { + |_| { + #[thread_local] + static VAL: EagerStorage<$t> = EagerStorage::new(__INIT); + VAL.get() + } + } else { + |_| { + #[thread_local] + static VAL: $t = __INIT; + &VAL + } + } + }) + } + }}, + + // used to generate the `LocalKey` value for `thread_local!` + (@key $t:ty, $init:expr) => {{ + #[inline] + fn __init() -> $t { + $init + } + + unsafe { + use $crate::mem::needs_drop; + use $crate::thread::LocalKey; + use $crate::thread::local_impl::LazyStorage; + + LocalKey::new(const { + if needs_drop::<$t>() { + |init| { + #[thread_local] + static VAL: LazyStorage<$t, ()> = LazyStorage::new(); + VAL.get_or_init(init, __init) + } + } else { + |init| { + #[thread_local] + static VAL: LazyStorage<$t, !> = LazyStorage::new(); + VAL.get_or_init(init, __init) + } + } + }) + } + }}, + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { + $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = + $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); + }, +} diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index 7500c95d8b47..0a78a1a1cf02 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -1,4 +1,5 @@ #![unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")] +#![cfg_attr(test, allow(unused))] // There are three thread-local implementations: "static", "fast", "OS". // The "OS" thread local key type is accessed via platform-specific API calls and is slow, while the @@ -10,12 +11,12 @@ cfg_if::cfg_if! { #[doc(hidden)] mod static_local; #[doc(hidden)] - pub use static_local::{Key, thread_local_inner}; + pub use static_local::{EagerStorage, LazyStorage, thread_local_inner}; } else if #[cfg(target_thread_local)] { #[doc(hidden)] mod fast_local; #[doc(hidden)] - pub use fast_local::{Key, thread_local_inner}; + pub use fast_local::{EagerStorage, LazyStorage, thread_local_inner}; } else { #[doc(hidden)] mod os_local; @@ -24,91 +25,12 @@ cfg_if::cfg_if! { } } -mod lazy { - use crate::cell::UnsafeCell; - use crate::hint; - use crate::mem; - - pub struct LazyKeyInner { - inner: UnsafeCell>, - } - - impl LazyKeyInner { - pub const fn new() -> LazyKeyInner { - LazyKeyInner { inner: UnsafeCell::new(None) } - } - - pub unsafe fn get(&self) -> Option<&'static T> { - // SAFETY: The caller must ensure no reference is ever handed out to - // the inner cell nor mutable reference to the Option inside said - // cell. This make it safe to hand a reference, though the lifetime - // of 'static is itself unsafe, making the get method unsafe. - unsafe { (*self.inner.get()).as_ref() } - } - - /// The caller must ensure that no reference is active: this method - /// needs unique access. - pub unsafe fn initialize T>(&self, init: F) -> &'static T { - // Execute the initialization up front, *then* move it into our slot, - // just in case initialization fails. - let value = init(); - let ptr = self.inner.get(); - - // SAFETY: - // - // note that this can in theory just be `*ptr = Some(value)`, but due to - // the compiler will currently codegen that pattern with something like: - // - // ptr::drop_in_place(ptr) - // ptr::write(ptr, Some(value)) - // - // Due to this pattern it's possible for the destructor of the value in - // `ptr` (e.g., if this is being recursively initialized) to re-access - // TLS, in which case there will be a `&` and `&mut` pointer to the same - // value (an aliasing violation). To avoid setting the "I'm running a - // destructor" flag we just use `mem::replace` which should sequence the - // operations a little differently and make this safe to call. - // - // The precondition also ensures that we are the only one accessing - // `self` at the moment so replacing is fine. - unsafe { - let _ = mem::replace(&mut *ptr, Some(value)); - } - - // SAFETY: With the call to `mem::replace` it is guaranteed there is - // a `Some` behind `ptr`, not a `None` so `unreachable_unchecked` - // will never be reached. - unsafe { - // After storing `Some` we want to get a reference to the contents of - // what we just stored. While we could use `unwrap` here and it should - // always work it empirically doesn't seem to always get optimized away, - // which means that using something like `try_with` can pull in - // panicking code and cause a large size bloat. - match *ptr { - Some(ref x) => x, - None => hint::unreachable_unchecked(), - } - } - } - - /// Watch out: unsynchronized internal mutability! - /// - /// # Safety - /// Causes UB if any reference to the value is used after this. - #[allow(unused)] - pub(crate) unsafe fn take(&self) -> Option { - let mutable: *mut _ = UnsafeCell::get(&self.inner); - // SAFETY: That's the caller's problem. - unsafe { mutable.replace(None) } - } - } -} - /// Run a callback in a scenario which must not unwind (such as a `extern "C" /// fn` declared in a user crate). If the callback unwinds anyway, then /// `rtabort` with a message about thread local panicking on drop. #[inline] -pub fn abort_on_dtor_unwind(f: impl FnOnce()) { +#[allow(dead_code)] +fn abort_on_dtor_unwind(f: impl FnOnce()) { // Using a guard like this is lower cost. let guard = DtorUnwindGuard; f(); diff --git a/library/std/src/sys/thread_local/os_local.rs b/library/std/src/sys/thread_local/os_local.rs index 3edffd7e4437..ee5adef66eac 100644 --- a/library/std/src/sys/thread_local/os_local.rs +++ b/library/std/src/sys/thread_local/os_local.rs @@ -1,7 +1,8 @@ -use super::lazy::LazyKeyInner; +use super::abort_on_dtor_unwind; use crate::cell::Cell; -use crate::sys_common::thread_local_key::StaticKey as OsStaticKey; -use crate::{fmt, marker, panic, ptr}; +use crate::marker::PhantomData; +use crate::ptr; +use crate::sys_common::thread_local_key::StaticKey as OsKey; #[doc(hidden)] #[allow_internal_unstable(thread_local_internals)] @@ -10,73 +11,27 @@ use crate::{fmt, marker, panic, ptr}; #[rustc_macro_transparency = "semitransparent"] pub macro thread_local_inner { // used to generate the `LocalKey` value for const-initialized thread locals - (@key $t:ty, const $init:expr) => {{ - #[inline] - #[deny(unsafe_op_in_unsafe_fn)] - unsafe fn __getit( - _init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - const INIT_EXPR: $t = $init; - - // On platforms without `#[thread_local]` we fall back to the - // same implementation as below for os thread locals. - #[inline] - const fn __init() -> $t { INIT_EXPR } - static __KEY: $crate::thread::local_impl::Key<$t> = - $crate::thread::local_impl::Key::new(); - unsafe { - __KEY.get(move || { - if let $crate::option::Option::Some(init) = _init { - if let $crate::option::Option::Some(value) = init.take() { - return value; - } else if $crate::cfg!(debug_assertions) { - $crate::unreachable!("missing initial value"); - } - } - __init() - }) - } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) - } - }}, + (@key $t:ty, const $init:expr) => { + $crate::thread::local_impl::thread_local_inner!(@key $t, { const INIT_EXPR: $t = $init; INIT_EXPR }) + }, // used to generate the `LocalKey` value for `thread_local!` - (@key $t:ty, $init:expr) => { - { - #[inline] - fn __init() -> $t { $init } + (@key $t:ty, $init:expr) => {{ + #[inline] + fn __init() -> $t { $init } - // `#[inline] does not work on windows-gnu due to linking errors around dllimports. - // See https://github.com/rust-lang/rust/issues/109797. - #[cfg_attr(not(windows), inline)] - unsafe fn __getit( - init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - static __KEY: $crate::thread::local_impl::Key<$t> = - $crate::thread::local_impl::Key::new(); + unsafe { + use $crate::thread::LocalKey; + use $crate::thread::local_impl::Key; - unsafe { - __KEY.get(move || { - if let $crate::option::Option::Some(init) = init { - if let $crate::option::Option::Some(value) = init.take() { - return value; - } else if $crate::cfg!(debug_assertions) { - $crate::unreachable!("missing default value"); - } - } - __init() - }) - } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) - } + // Inlining does not work on windows-gnu due to linking errors around + // dllimports. See https://github.com/rust-lang/rust/issues/109797. + LocalKey::new(#[cfg_attr(windows, inline(never))] |init| { + static VAL: Key<$t> = Key::new(); + VAL.get(init, __init) + }) } - }, + }}, ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); @@ -85,78 +40,73 @@ pub macro thread_local_inner { /// Use a regular global static to store this key; the state provided will then be /// thread-local. +#[allow(missing_debug_implementations)] pub struct Key { - // OS-TLS key that we'll use to key off. - os: OsStaticKey, - marker: marker::PhantomData>, -} - -impl fmt::Debug for Key { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Key").finish_non_exhaustive() - } + os: OsKey, + marker: PhantomData>, } unsafe impl Sync for Key {} struct Value { - inner: LazyKeyInner, + value: T, key: &'static Key, } impl Key { #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] pub const fn new() -> Key { - Key { os: OsStaticKey::new(Some(destroy_value::)), marker: marker::PhantomData } + Key { os: OsKey::new(Some(destroy_value::)), marker: PhantomData } } - /// It is a requirement for the caller to ensure that no mutable - /// reference is active when this method is called. - pub unsafe fn get(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> { - // SAFETY: See the documentation for this method. + /// Get a pointer to the TLS value, potentially initializing it with the + /// provided parameters. If the TLS variable has been destroyed, a null + /// pointer is returned. + /// + /// The resulting pointer may not be used after reentrant inialialization + /// or thread destruction has occurred. + pub fn get(&'static self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { + // SAFETY: (FIXME: get should actually be safe) let ptr = unsafe { self.os.get() as *mut Value }; if ptr.addr() > 1 { // SAFETY: the check ensured the pointer is safe (its destructor // is not running) + it is coming from a trusted source (self). - if let Some(ref value) = unsafe { (*ptr).inner.get() } { - return Some(value); - } + unsafe { &(*ptr).value } + } else { + self.try_initialize(ptr, i, f) } - // SAFETY: At this point we are sure we have no value and so - // initializing (or trying to) is safe. - unsafe { self.try_initialize(init) } } - // `try_initialize` is only called once per os thread local variable, - // except in corner cases where thread_local dtors reference other - // thread_local's, or it is being recursively initialized. - unsafe fn try_initialize(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> { - // SAFETY: No mutable references are ever handed out meaning getting - // the value is ok. - let ptr = unsafe { self.os.get() as *mut Value }; + fn try_initialize( + &'static self, + ptr: *mut Value, + i: Option<&mut Option>, + f: impl FnOnce() -> T, + ) -> *const T { if ptr.addr() == 1 { // destructor is running - return None; + return ptr::null(); } - let ptr = if ptr.is_null() { - // If the lookup returned null, we haven't initialized our own - // local copy, so do that now. - let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self })); - // SAFETY: At this point we are sure there is no value inside - // ptr so setting it will not affect anyone else. - unsafe { - self.os.set(ptr as *mut u8); - } - ptr - } else { - // recursive initialization - ptr - }; + let value = i.and_then(Option::take).unwrap_or_else(f); + let ptr = Box::into_raw(Box::new(Value { value, key: self })); + // SAFETY: (FIXME: get should actually be safe) + let old = unsafe { self.os.get() as *mut Value }; + // SAFETY: `ptr` is a correct pointer that can be destroyed by the key destructor. + unsafe { + self.os.set(ptr as *mut u8); + } + if !old.is_null() { + // If the variable was recursively initialized, drop the old value. + // SAFETY: We cannot be inside a `LocalKey::with` scope, as the + // initializer has already returned and the next scope only starts + // after we return the pointer. Therefore, there can be no references + // to the old value. + drop(unsafe { Box::from_raw(old) }); + } - // SAFETY: ptr has been ensured as non-NUL just above an so can be - // dereferenced safely. - unsafe { Some((*ptr).inner.initialize(init)) } + // SAFETY: We just created this value above. + unsafe { &(*ptr).value } } } @@ -170,16 +120,11 @@ unsafe extern "C" fn destroy_value(ptr: *mut u8) { // // Note that to prevent an infinite loop we reset it back to null right // before we return from the destructor ourselves. - // - // Wrap the call in a catch to ensure unwinding is caught in the event - // a panic takes place in a destructor. - if let Err(_) = panic::catch_unwind(|| unsafe { - let ptr = Box::from_raw(ptr as *mut Value); + abort_on_dtor_unwind(|| { + let ptr = unsafe { Box::from_raw(ptr as *mut Value) }; let key = ptr.key; - key.os.set(ptr::without_provenance_mut(1)); + unsafe { key.os.set(ptr::without_provenance_mut(1)) }; drop(ptr); - key.os.set(ptr::null_mut()); - }) { - rtabort!("thread local panicked on drop"); - } + unsafe { key.os.set(ptr::null_mut()) }; + }); } diff --git a/library/std/src/sys/thread_local/static_local.rs b/library/std/src/sys/thread_local/static_local.rs index 162c3fbd97a0..0f08cab1ae4f 100644 --- a/library/std/src/sys/thread_local/static_local.rs +++ b/library/std/src/sys/thread_local/static_local.rs @@ -1,5 +1,7 @@ -use super::lazy::LazyKeyInner; -use crate::fmt; +//! On some targets like wasm there's no threads, so no need to generate +//! thread locals and we can instead just use plain statics! + +use crate::cell::UnsafeCell; #[doc(hidden)] #[allow_internal_unstable(thread_local_internals)] @@ -9,98 +11,85 @@ use crate::fmt; pub macro thread_local_inner { // used to generate the `LocalKey` value for const-initialized thread locals (@key $t:ty, const $init:expr) => {{ - #[inline] // see comments below - #[deny(unsafe_op_in_unsafe_fn)] - unsafe fn __getit( - _init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - const INIT_EXPR: $t = $init; - - // wasm without atomics maps directly to `static mut`, and dtors - // aren't implemented because thread dtors aren't really a thing - // on wasm right now - // - // FIXME(#84224) this should come after the `target_thread_local` - // block. - static mut VAL: $t = INIT_EXPR; - // SAFETY: we only ever create shared references, so there's no mutable aliasing. - unsafe { $crate::option::Option::Some(&*$crate::ptr::addr_of!(VAL)) } - } + const __INIT: $t = $init; unsafe { - $crate::thread::LocalKey::new(__getit) + use $crate::thread::LocalKey; + use $crate::thread::local_impl::EagerStorage; + + LocalKey::new(|_| { + static VAL: EagerStorage<$t> = EagerStorage { value: __INIT }; + &VAL.value + }) } }}, // used to generate the `LocalKey` value for `thread_local!` - (@key $t:ty, $init:expr) => { - { - #[inline] - fn __init() -> $t { $init } - #[inline] - unsafe fn __getit( - init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - static __KEY: $crate::thread::local_impl::Key<$t> = - $crate::thread::local_impl::Key::new(); + (@key $t:ty, $init:expr) => {{ + #[inline] + fn __init() -> $t { $init } - unsafe { - __KEY.get(move || { - if let $crate::option::Option::Some(init) = init { - if let $crate::option::Option::Some(value) = init.take() { - return value; - } else if $crate::cfg!(debug_assertions) { - $crate::unreachable!("missing default value"); - } - } - __init() - }) - } - } + unsafe { + use $crate::thread::LocalKey; + use $crate::thread::local_impl::LazyStorage; - unsafe { - $crate::thread::LocalKey::new(__getit) - } + LocalKey::new(|init| { + static VAL: LazyStorage<$t> = LazyStorage::new(); + VAL.get(init, __init) + }) } - }, + }}, ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); }, } -/// On some targets like wasm there's no threads, so no need to generate -/// thread locals and we can instead just use plain statics! - -pub struct Key { - inner: LazyKeyInner, +#[allow(missing_debug_implementations)] +pub struct EagerStorage { + pub value: T, } -unsafe impl Sync for Key {} +// SAFETY: the target doesn't have threads. +unsafe impl Sync for EagerStorage {} -impl fmt::Debug for Key { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Key").finish_non_exhaustive() +#[allow(missing_debug_implementations)] +pub struct LazyStorage { + value: UnsafeCell>, +} + +impl LazyStorage { + pub const fn new() -> LazyStorage { + LazyStorage { value: UnsafeCell::new(None) } + } + + /// Get a pointer to the TLS value, potentially initializing it with the + /// provided parameters. + /// + /// The resulting pointer may not be used after reentrant inialialization + /// has occurred. + #[inline] + pub fn get(&'static self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { + let value = unsafe { &*self.value.get() }; + match value { + Some(v) => v, + None => self.initialize(i, f), + } + } + + #[cold] + fn initialize(&'static self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { + let value = i.and_then(Option::take).unwrap_or_else(f); + // Destroy the old value, after updating the TLS variable as the + // destructor might reference it. + // FIXME(#110897): maybe panic on recursive initialization. + unsafe { + self.value.get().replace(Some(value)); + } + // SAFETY: we just set this to `Some`. + unsafe { (*self.value.get()).as_ref().unwrap_unchecked() } } } -impl Key { - pub const fn new() -> Key { - Key { inner: LazyKeyInner::new() } - } - - pub unsafe fn get(&self, init: impl FnOnce() -> T) -> Option<&'static T> { - // SAFETY: The caller must ensure no reference is ever handed out to - // the inner cell nor mutable reference to the Option inside said - // cell. This make it safe to hand a reference, though the lifetime - // of 'static is itself unsafe, making the get method unsafe. - let value = unsafe { - match self.inner.get() { - Some(ref value) => value, - None => self.inner.initialize(init), - } - }; - - Some(value) - } -} +// SAFETY: the target doesn't have threads. +unsafe impl Sync for LazyStorage {} diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 3a38ba1100f0..200ea028a08a 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -20,7 +20,6 @@ #[cfg(test)] mod tests; -pub mod backtrace; pub mod fs; pub mod io; pub mod lazy_box; diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index 38e15f9f5496..84128a4b595f 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -325,6 +325,11 @@ impl Wtf8Buf { self.bytes.shrink_to(min_capacity) } + #[inline] + pub fn leak<'a>(self) -> &'a mut Wtf8 { + unsafe { Wtf8::from_mut_bytes_unchecked(self.bytes.leak()) } + } + /// Returns the number of bytes that this string buffer can hold without reallocating. #[inline] pub fn capacity(&self) -> usize { @@ -472,6 +477,9 @@ impl Wtf8Buf { /// Part of a hack to make PathBuf::push/pop more efficient. #[inline] pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec { + // FIXME: this function should not even exist, as it implies violating Wtf8Buf invariants + // For now, simply assume that is about to happen. + self.is_known_utf8 = false; &mut self.bytes } } diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index c1b4440e5608..f147c5fdcd14 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -62,7 +62,7 @@ use crate::fmt; /// FOO.set(2); /// /// // each thread starts out with the initial value of 1 -/// let t = thread::spawn(move|| { +/// let t = thread::spawn(move || { /// assert_eq!(FOO.get(), 1); /// FOO.set(3); /// }); @@ -123,7 +123,7 @@ pub struct LocalKey { // trivially devirtualizable by LLVM because the value of `inner` never // changes and the constant should be readonly within a crate. This mainly // only runs into problems when TLS statics are exported across crates. - inner: unsafe fn(Option<&mut Option>) -> Option<&'static T>, + inner: fn(Option<&mut Option>) -> *const T, } #[stable(feature = "std_debug", since = "1.16.0")] @@ -238,9 +238,7 @@ impl LocalKey { issue = "none" )] #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] - pub const unsafe fn new( - inner: unsafe fn(Option<&mut Option>) -> Option<&'static T>, - ) -> LocalKey { + pub const unsafe fn new(inner: fn(Option<&mut Option>) -> *const T) -> LocalKey { LocalKey { inner } } @@ -281,8 +279,7 @@ impl LocalKey { where F: FnOnce(&T) -> R, { - // SAFETY: `inner` is safe to call within the lifetime of the thread - let thread_local = unsafe { (self.inner)(None).ok_or(AccessError)? }; + let thread_local = unsafe { (self.inner)(None).as_ref().ok_or(AccessError)? }; Ok(f(thread_local)) } @@ -304,9 +301,8 @@ impl LocalKey { { let mut init = Some(init); - // SAFETY: `inner` is safe to call within the lifetime of the thread let reference = unsafe { - (self.inner)(Some(&mut init)).expect( + (self.inner)(Some(&mut init)).as_ref().expect( "cannot access a Thread Local Storage value \ during or after destruction", ) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 78bc9af6c4d5..c8ee365392f8 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -205,7 +205,7 @@ cfg_if::cfg_if! { #[doc(hidden)] #[unstable(feature = "thread_local_internals", issue = "none")] pub mod local_impl { - pub use crate::sys::thread_local::{thread_local_inner, Key, abort_on_dtor_unwind}; + pub use crate::sys::thread_local::*; } } } @@ -539,7 +539,7 @@ impl Builder { let f = f.into_inner(); set_current(their_thread); let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { - crate::sys_common::backtrace::__rust_begin_short_backtrace(f) + crate::sys::backtrace::__rust_begin_short_backtrace(f) })); // SAFETY: `their_packet` as been built just above and moved by the // closure (it is an Arc<...>) and `my_packet` will be stored in the @@ -561,7 +561,8 @@ impl Builder { let main = Box::new(main); // SAFETY: dynamic size and alignment of the Box remain the same. See below for why the // lifetime change is justified. - let main = unsafe { Box::from_raw(Box::into_raw(main) as *mut (dyn FnOnce() + 'static)) }; + let main = + unsafe { Box::from_raw(Box::into_raw(main) as *mut (dyn FnOnce() + Send + 'static)) }; Ok(JoinInner { // SAFETY: @@ -1544,7 +1545,7 @@ struct Packet<'scope, T> { // The type `T` should already always be Send (otherwise the thread could not // have been created) and the Packet is Sync because all access to the // `UnsafeCell` synchronized (by the `join()` boundary), and `ScopeData` is Sync. -unsafe impl<'scope, T: Sync> Sync for Packet<'scope, T> {} +unsafe impl<'scope, T: Send> Sync for Packet<'scope, T> {} impl<'scope, T> Drop for Packet<'scope, T> { fn drop(&mut self) { diff --git a/library/std/tests/run-time-detect.rs b/library/std/tests/run-time-detect.rs index c9b9c54e3d49..694867056566 100644 --- a/library/std/tests/run-time-detect.rs +++ b/library/std/tests/run-time-detect.rs @@ -121,10 +121,8 @@ fn x86_all() { println!("avx512bw: {:?}", is_x86_feature_detected!("avx512bw")); println!("avx512cd: {:?}", is_x86_feature_detected!("avx512cd")); println!("avx512dq: {:?}", is_x86_feature_detected!("avx512dq")); - println!("avx512er: {:?}", is_x86_feature_detected!("avx512er")); println!("avx512f: {:?}", is_x86_feature_detected!("avx512f")); println!("avx512ifma: {:?}", is_x86_feature_detected!("avx512ifma")); - println!("avx512pf: {:?}", is_x86_feature_detected!("avx512pf")); println!("avx512vbmi2: {:?}", is_x86_feature_detected!("avx512vbmi2")); println!("avx512vbmi: {:?}", is_x86_feature_detected!("avx512vbmi")); println!("avx512vl: {:?}", is_x86_feature_detected!("avx512vl")); diff --git a/library/std/tests/windows.rs b/library/std/tests/windows.rs new file mode 100644 index 000000000000..9f7596f1bc2c --- /dev/null +++ b/library/std/tests/windows.rs @@ -0,0 +1,14 @@ +#![cfg(windows)] +//! An external tests + +use std::{ffi::OsString, os::windows::ffi::OsStringExt, path::PathBuf}; + +#[test] +#[should_panic] +fn os_string_must_know_it_isnt_utf8_issue_126291() { + let mut utf8 = PathBuf::from(OsString::from("utf8".to_owned())); + let non_utf8: OsString = + OsStringExt::from_wide(&[0x6e, 0x6f, 0x6e, 0xd800, 0x75, 0x74, 0x66, 0x38]); + utf8.set_extension(&non_utf8); + utf8.into_os_string().into_string().unwrap(); +} diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index 6ff24a8db59c..1ddacd92e6b9 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -22,6 +22,7 @@ llvm-libunwind = ["std/llvm-libunwind"] system-llvm-libunwind = ["std/system-llvm-libunwind"] panic-unwind = ["std/panic_unwind"] panic_immediate_abort = ["std/panic_immediate_abort"] +optimize_for_size = ["std/optimize_for_size"] profiler = ["std/profiler"] std_detect_file_io = ["std/std_detect_file_io"] std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"] diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml index 0e2409f63ab1..75cc7c00e389 100644 --- a/library/test/Cargo.toml +++ b/library/test/Cargo.toml @@ -7,8 +7,6 @@ edition = "2021" getopts = { version = "0.2.21", features = ['rustc-dep-of-std'] } std = { path = "../std" } core = { path = "../core" } -panic_unwind = { path = "../panic_unwind" } -panic_abort = { path = "../panic_abort" } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] libc = { version = "0.2.150", default-features = false } diff --git a/library/test/src/bench.rs b/library/test/src/bench.rs index 9a5dc351f6d0..64ca13c0d4ed 100644 --- a/library/test/src/bench.rs +++ b/library/test/src/bench.rs @@ -98,7 +98,7 @@ fn fmt_thousands_sep(mut n: f64, sep: char) -> String { (0, true) => write!(output, "{:06.2}", n / base as f64).unwrap(), (0, false) => write!(output, "{:.2}", n / base as f64).unwrap(), (_, true) => write!(output, "{:03}", n as usize / base).unwrap(), - _ => write!(output, "{}", n as usize / base).unwrap() + _ => write!(output, "{}", n as usize / base).unwrap(), } if pow != 0 { output.push(sep); diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index 6ac3b3eaa797..b7d24405b775 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -200,7 +200,7 @@ Test Attributes: pub fn parse_opts(args: &[String]) -> Option { // Parse matches. let opts = optgroups(); - let binary = args.get(0).map(|c| &**c).unwrap_or("..."); + let binary = args.first().map(|c| &**c).unwrap_or("..."); let args = args.get(1..).unwrap_or(args); let matches = match opts.parse(args) { Ok(m) => m, diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 7bd08a0605f8..7aff7fe1fdd6 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -58,7 +58,7 @@ use std::{ env, io, io::prelude::Write, mem::ManuallyDrop, - panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo}, + panic::{self, catch_unwind, AssertUnwindSafe, PanicHookInfo}, process::{self, Command, Termination}, sync::mpsc::{channel, Sender}, sync::{Arc, Mutex}, @@ -123,7 +123,7 @@ pub fn test_main(args: &[String], tests: Vec, options: Option| { + move |info: &'_ PanicHookInfo<'_>| { if !info.can_unwind() { std::mem::forget(std::io::stderr().lock()); let mut stdout = ManuallyDrop::new(std::io::stdout().lock()); @@ -726,7 +726,7 @@ fn spawn_test_subprocess( fn run_test_in_spawned_subprocess(desc: TestDesc, runnable_test: RunnableTest) -> ! { let builtin_panic_hook = panic::take_hook(); - let record_result = Arc::new(move |panic_info: Option<&'_ PanicInfo<'_>>| { + let record_result = Arc::new(move |panic_info: Option<&'_ PanicHookInfo<'_>>| { let test_result = match panic_info { Some(info) => calc_result(&desc, Err(info.payload()), &None, &None), None => calc_result(&desc, Ok(()), &None, &None), diff --git a/library/test/src/term/terminfo/parm.rs b/library/test/src/term/terminfo/parm.rs index 2815f6cfc77f..c5b4ef01893c 100644 --- a/library/test/src/term/terminfo/parm.rs +++ b/library/test/src/term/terminfo/parm.rs @@ -524,7 +524,7 @@ fn format(val: Param, op: FormatOp, flags: Flags) -> Result, String> { } else { let mut s_ = Vec::with_capacity(flags.width); s_.extend(repeat(b' ').take(n)); - s_.extend(s.into_iter()); + s_.extend(s); s = s_; } } diff --git a/library/test/src/term/win.rs b/library/test/src/term/win.rs index 55020141a827..65764c0ffc1b 100644 --- a/library/test/src/term/win.rs +++ b/library/test/src/term/win.rs @@ -22,6 +22,8 @@ type WORD = u16; type DWORD = u32; type BOOL = i32; type HANDLE = *mut u8; +// https://docs.microsoft.com/en-us/windows/console/getstdhandle +const STD_OUTPUT_HANDLE: DWORD = -11 as _; #[allow(non_snake_case)] #[repr(C)] @@ -99,16 +101,13 @@ impl WinConsole { accum |= color_to_bits(self.background) << 4; unsafe { - // Magic -11 means stdout, from - // https://docs.microsoft.com/en-us/windows/console/getstdhandle - // // You may be wondering, "but what about stderr?", and the answer // to that is that setting terminal attributes on the stdout // handle also sets them for stderr, since they go to the same // terminal! Admittedly, this is fragile, since stderr could be // redirected to a different console. This is good enough for // rustc though. See #13400. - let out = GetStdHandle(-11i32 as DWORD); + let out = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(out, accum); } } @@ -120,9 +119,8 @@ impl WinConsole { let bg; unsafe { let mut buffer_info = MaybeUninit::::uninit(); - if GetConsoleScreenBufferInfo(GetStdHandle(-11i32 as DWORD), buffer_info.as_mut_ptr()) - != 0 - { + let handle = GetStdHandle(STD_OUTPUT_HANDLE); + if GetConsoleScreenBufferInfo(handle, buffer_info.as_mut_ptr()) != 0 { let buffer_info = buffer_info.assume_init(); fg = bits_to_color(buffer_info.wAttributes); bg = bits_to_color(buffer_info.wAttributes >> 4); diff --git a/rustfmt.toml b/rustfmt.toml index 850d01ea7cb8..b15ffdca38a0 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -3,25 +3,27 @@ version = "Two" use_small_heuristics = "Max" merge_derives = false -# by default we ignore everything in the repository -# tidy only checks files which are not ignored, each entry follows gitignore style +# Files to ignore. Each entry uses gitignore syntax, but `!` prefixes aren't allowed. ignore = [ "/build/", "/*-build/", "/build-*/", "/vendor/", - # tests for now are not formatted, as they are sometimes pretty-printing constrained - # (and generally rustfmt can move around comments in UI-testing incompatible ways) - "/tests/*", + # Some tests are not formatted, for various reasons. + "/tests/codegen/simd-intrinsic/", # Many types like `u8x64` are better hand-formatted. + "/tests/crashes/", # Many of these tests contain syntax errors. + "/tests/debuginfo/", # These tests are somewhat sensitive to source code layout. + "/tests/incremental/", # These tests are somewhat sensitive to source code layout. + "/tests/pretty/", # These tests are very sensitive to source code layout. + "/tests/run-make/translation/test.rs", # This test contains syntax errors. + "/tests/rustdoc/", # Some have syntax errors, some are whitespace-sensitive. + "/tests/rustdoc-gui/", # Some tests are sensitive to source code layout. + "/tests/rustdoc-ui/", # Some have syntax errors, some are whitespace-sensitive. + "/tests/ui/", # Some have syntax errors, some are whitespace-sensitive. + "/tests/ui-fulldeps/", # Some are whitespace-sensitive (e.g. `// ~ERROR` comments). - # but we still want to format rmake.rs files in tests/run-make/ so we need to do this - # dance to avoid the parent directory from being excluded - "!/tests/run-make/", - "/tests/run-make/*/*.rs", - "!/tests/run-make/*/rmake.rs", - - # do not format submodules + # Do not format submodules. # FIXME: sync submodule list with tidy/bootstrap/etc # tidy/src/walk.rs:filter_dirs "library/backtrace", @@ -40,9 +42,10 @@ ignore = [ "src/tools/clippy", "src/tools/miri", "src/tools/rust-analyzer", + "src/tools/rustc-perf", "src/tools/rustfmt", - # these are ignored by a standard cargo fmt run + # These are ignored by a standard cargo fmt run. "compiler/rustc_codegen_cranelift/scripts", "compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs", # uses edition 2024 ] diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 4a5d768961ff..9c24742cff1f 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -17,12 +17,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - [[package]] name = "bitflags" version = "1.3.2" @@ -181,34 +175,28 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.17" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.18" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" -dependencies = [ - "cfg-if", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crypto-common" @@ -331,19 +319,19 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "junction" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca39ef0d69b18e6a2fd14c2f0a1d593200f4a4ed949b240b5917ab51fac754cb" +checksum = "1c9c415a9b7b1e86cd5738f39d34c9e78c765da7fb1756dbd7d31b3b0d2e7afa" dependencies = [ "scopeguard", - "winapi", + "windows-sys", ] [[package]] name = "libc" -version = "0.2.151" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "linux-raw-sys" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index ca0d1fa5bd0c..32dd3efa7a6e 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -7,6 +7,7 @@ default-run = "bootstrap" [features] build-metrics = ["sysinfo"] +bootstrap-self-test = [] # enabled in the bootstrap unit tests [lib] path = "src/lib.rs" diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index 077db44ae54e..fb3c86270430 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -6,7 +6,7 @@ and some of the technical details of the build system. Note that this README only covers internal information, not how to use the tool. Please check [bootstrapping dev guide][bootstrapping-dev-guide] for further information. -[bootstrapping-dev-guide]: https://rustc-dev-guide.rust-lang.org/building/bootstrapping.html +[bootstrapping-dev-guide]: https://rustc-dev-guide.rust-lang.org/building/bootstrapping/intro.html ## Introduction diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index e60e8f0aa1f7..9861121aac0a 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -599,6 +599,12 @@ class RustBuild(object): print('Choosing a pool size of', pool_size, 'for the unpacking of the tarballs') p = Pool(pool_size) try: + # FIXME: A cheap workaround for https://github.com/rust-lang/rust/issues/125578, + # remove this once the issue is closed. + bootstrap_out = self.bootstrap_out() + if os.path.exists(bootstrap_out): + shutil.rmtree(bootstrap_out) + p.map(unpack_component, tarballs_download_info) finally: p.close() @@ -864,6 +870,16 @@ class RustBuild(object): return line[start + 1:end] return None + def bootstrap_out(self): + """Return the path of the bootstrap build artifacts + + >>> rb = RustBuild() + >>> rb.build_dir = "build" + >>> rb.bootstrap_binary() == os.path.join("build", "bootstrap") + True + """ + return os.path.join(self.build_dir, "bootstrap") + def bootstrap_binary(self): """Return the path of the bootstrap binary @@ -873,7 +889,7 @@ class RustBuild(object): ... "debug", "bootstrap") True """ - return os.path.join(self.build_dir, "bootstrap", "debug", "bootstrap") + return os.path.join(self.bootstrap_out(), "debug", "bootstrap") def build_bootstrap(self): """Build bootstrap""" diff --git a/src/bootstrap/defaults/config.compiler.toml b/src/bootstrap/defaults/config.compiler.toml index fd2da2469908..789586b58f70 100644 --- a/src/bootstrap/defaults/config.compiler.toml +++ b/src/bootstrap/defaults/config.compiler.toml @@ -19,8 +19,6 @@ lto = "off" # Forces frame pointers to be used with `-Cforce-frame-pointers`. # This can be helpful for profiling at a small performance cost. frame-pointers = true -# Build the llvm-bitcode-linker as it is required for running nvptx tests -llvm-bitcode-linker = true [llvm] # Having this set to true disrupts compiler development workflows for people who use `llvm.download-ci-llvm = true` diff --git a/src/bootstrap/defaults/config.dist.toml b/src/bootstrap/defaults/config.dist.toml index f3c6ffc9bd51..d06930f2b9d9 100644 --- a/src/bootstrap/defaults/config.dist.toml +++ b/src/bootstrap/defaults/config.dist.toml @@ -16,7 +16,7 @@ download-ci-llvm = false # Make sure they don't get set when installing from source. channel = "nightly" download-rustc = false -# Build the llvm-bitcode-linker as it is required for running nvptx tests +# Build the llvm-bitcode-linker llvm-bitcode-linker = true [dist] diff --git a/src/bootstrap/defaults/config.library.toml b/src/bootstrap/defaults/config.library.toml index 4a1a49b72752..087544397f5e 100644 --- a/src/bootstrap/defaults/config.library.toml +++ b/src/bootstrap/defaults/config.library.toml @@ -10,8 +10,6 @@ bench-stage = 0 incremental = true # Make the compiler and standard library faster to build, at the expense of a ~20% runtime slowdown. lto = "off" -# Build the llvm-bitcode-linker as it is required for running nvptx tests -llvm-bitcode-linker = true [llvm] # Will download LLVM from CI if available on your platform. diff --git a/src/bootstrap/defaults/config.tools.toml b/src/bootstrap/defaults/config.tools.toml index 94c8b724cbf8..6e6c36600279 100644 --- a/src/bootstrap/defaults/config.tools.toml +++ b/src/bootstrap/defaults/config.tools.toml @@ -12,8 +12,6 @@ incremental = true # Using these defaults will download the stage2 compiler (see `download-rustc` # setting) and the stage2 toolchain should therefore be used for these defaults. download-rustc = "if-unchanged" -# Build the llvm-bitcode-linker as it is required for running nvptx tests -llvm-bitcode-linker = true [build] # Document with the in-tree rustdoc by default, since `download-rustc` makes it quick to compile. diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp index bd1f9699c3c8..e4dbd3a13fee 100644 --- a/src/bootstrap/download-ci-llvm-stamp +++ b/src/bootstrap/download-ci-llvm-stamp @@ -1,4 +1,4 @@ Change this file to make users of the `download-ci-llvm` configuration download a new version of LLVM from CI, even if the LLVM submodule hasn’t changed. -Last change is for: https://github.com/rust-lang/rust/pull/120761 +Last change is for: https://github.com/rust-lang/rust/pull/125141 diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index fc433bc5843b..cab37e0da473 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -59,17 +59,18 @@ check-aux: library/alloc \ --no-doc # Some doctests have intentional memory leaks. + # Some use file system operations to demonstrate dealing with `Result`. $(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" \ $(BOOTSTRAP) miri --stage 2 \ library/core \ library/alloc \ --doc - # In `std` we cannot test everything. - $(Q)MIRIFLAGS="-Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \ + # In `std` we cannot test everything, so we skip some modules. + $(Q)MIRIFLAGS="-Zmiri-disable-isolation" \ $(BOOTSTRAP) miri --stage 2 library/std \ --no-doc -- \ --skip fs:: --skip net:: --skip process:: --skip sys::pal:: - $(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \ + $(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" \ $(BOOTSTRAP) miri --stage 2 library/std \ --doc -- \ --skip fs:: --skip net:: --skip process:: --skip sys::pal:: diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 01b5e99116ff..40a2112b1925 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -322,7 +322,6 @@ lint_any!( RemoteTestServer, "src/tools/remote-test-server", "remote-test-server"; Rls, "src/tools/rls", "rls"; RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer"; - RustDemangler, "src/tools/rust-demangler", "rust-demangler"; Rustdoc, "src/tools/rustdoc", "clippy"; Rustfmt, "src/tools/rustfmt", "rustfmt"; RustInstaller, "src/tools/rust-installer", "rust-installer"; diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 66692a2a2cbb..cc5b00732135 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1008,10 +1008,7 @@ pub fn rustc_cargo( // If the rustc output is piped to e.g. `head -n1` we want the process to be // killed, rather than having an error bubble up and cause a panic. - // FIXME: Synthetic #[cfg(bootstrap)]. Remove when the bootstrap compiler supports it. - if compiler.stage != 0 { - cargo.rustflag("-Zon-broken-pipe=kill"); - } + cargo.rustflag("-Zon-broken-pipe=kill"); // We currently don't support cross-crate LTO in stage0. This also isn't hugely necessary // and may just be a time sink. @@ -1137,7 +1134,9 @@ pub fn rustc_cargo_env( } // Enable rustc's env var for `rust-lld` when requested. - if builder.config.lld_enabled { + if builder.config.lld_enabled + && (builder.config.channel == "dev" || builder.config.channel == "nightly") + { cargo.env("CFG_USE_SELF_CONTAINED_LINKER", "1"); } diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 1f006e1453f4..54136d2aebda 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -78,7 +78,7 @@ impl Step for Docs { let mut tarball = Tarball::new(builder, "rust-docs", &host.triple); tarball.set_product_name("Rust Documentation"); - tarball.add_bulk_dir(&builder.doc_out(host), dest); + tarball.add_bulk_dir(builder.doc_out(host), dest); tarball.add_file(builder.src.join("src/doc/robots.txt"), dest, 0o644); Some(tarball.generate()) } @@ -117,7 +117,7 @@ impl Step for JsonDocs { let mut tarball = Tarball::new(builder, "rust-docs-json", &host.triple); tarball.set_product_name("Rust Documentation In JSON Format"); tarball.is_preview(true); - tarball.add_bulk_dir(&builder.json_doc_out(host), dest); + tarball.add_bulk_dir(builder.json_doc_out(host), dest); Some(tarball.generate()) } } @@ -148,7 +148,7 @@ impl Step for RustcDocs { let mut tarball = Tarball::new(builder, "rustc-docs", &host.triple); tarball.set_product_name("Rustc Documentation"); - tarball.add_bulk_dir(&builder.compiler_doc_out(host), "share/doc/rust/html/rustc"); + tarball.add_bulk_dir(builder.compiler_doc_out(host), "share/doc/rust/html/rustc"); Some(tarball.generate()) } } @@ -1010,6 +1010,9 @@ impl Step for PlainSourceTarball { if builder.rust_info().is_managed_git_subrepository() || builder.rust_info().is_from_tarball() { + // FIXME: This code looks _very_ similar to what we have in `src/core/build_steps/vendor.rs` + // perhaps it should be removed in favor of making `dist` perform the `vendor` step? + // Ensure we have all submodules from src and other directories checked out. for submodule in builder.get_all_submodules() { builder.update_submodule(Path::new(submodule)); @@ -1029,11 +1032,30 @@ impl Step for PlainSourceTarball { .arg(builder.src.join("./compiler/rustc_codegen_gcc/Cargo.toml")) .arg("--sync") .arg(builder.src.join("./src/bootstrap/Cargo.toml")) + .arg("--sync") + .arg(builder.src.join("./src/tools/opt-dist/Cargo.toml")) + .arg("--sync") + .arg(builder.src.join("./src/tools/rustc-perf/Cargo.toml")) // Will read the libstd Cargo.toml // which uses the unstable `public-dependency` feature. .env("RUSTC_BOOTSTRAP", "1") .current_dir(plain_dst_src); + // Vendor packages that are required by opt-dist to collect PGO profiles. + let pkgs_for_pgo_training = build_helper::LLVM_PGO_CRATES + .iter() + .chain(build_helper::RUSTC_PGO_CRATES) + .map(|pkg| { + let mut manifest_path = + builder.src.join("./src/tools/rustc-perf/collector/compile-benchmarks"); + manifest_path.push(pkg); + manifest_path.push("Cargo.toml"); + manifest_path + }); + for manifest_path in pkgs_for_pgo_training { + cmd.arg("--sync").arg(manifest_path); + } + let config = if !builder.config.dry_run() { t!(String::from_utf8(t!(cmd.output()).stdout)) } else { @@ -1437,62 +1459,6 @@ impl Step for Rustfmt { } } -#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] -pub struct RustDemangler { - pub compiler: Compiler, - pub target: TargetSelection, -} - -impl Step for RustDemangler { - type Output = Option; - const DEFAULT: bool = true; - const ONLY_HOSTS: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - // While other tools use `should_build_extended_tool` to decide whether to be run by - // default or not, `rust-demangler` must be build when *either* it's enabled as a tool like - // the other ones or if `profiler = true`. Because we don't know the target at this stage - // we run the step by default when only `extended = true`, and decide whether to actually - // run it or not later. - let default = run.builder.config.extended; - run.alias("rust-demangler").default_condition(default) - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure(RustDemangler { - compiler: run.builder.compiler_for( - run.builder.top_stage, - run.builder.config.build, - run.target, - ), - target: run.target, - }); - } - - fn run(self, builder: &Builder<'_>) -> Option { - let compiler = self.compiler; - let target = self.target; - - // Only build this extended tool if explicitly included in `tools`, or if `profiler = true` - let condition = should_build_extended_tool(builder, "rust-demangler") - || builder.config.profiler_enabled(target); - if builder.config.extended && !condition { - return None; - } - - let rust_demangler = - builder.ensure(tool::RustDemangler { compiler, target, extra_features: Vec::new() }); - - // Prepare the image directory - let mut tarball = Tarball::new(builder, "rust-demangler", &target.triple); - tarball.set_overlay(OverlayKind::RustDemangler); - tarball.is_preview(true); - tarball.add_file(rust_demangler, "bin", 0o755); - tarball.add_legal_and_readme_to("share/doc/rust-demangler"); - Some(tarball.generate()) - } -} - #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] pub struct Extended { stage: u32, @@ -1550,7 +1516,6 @@ impl Step for Extended { add_component!("rust-docs" => Docs { host: target }); add_component!("rust-json-docs" => JsonDocs { host: target }); - add_component!("rust-demangler"=> RustDemangler { compiler, target }); add_component!("cargo" => Cargo { compiler, target }); add_component!("rustfmt" => Rustfmt { compiler, target }); add_component!("rls" => Rls { compiler, target }); @@ -1614,7 +1579,7 @@ impl Step for Extended { let xform = |p: &Path| { let mut contents = t!(fs::read_to_string(p)); - for tool in &["rust-demangler", "miri", "rust-docs"] { + for tool in &["miri", "rust-docs"] { if !built_tools.contains(tool) { contents = filter(&contents, tool); } @@ -1655,7 +1620,7 @@ impl Step for Extended { prepare("rust-analysis"); prepare("clippy"); prepare("rust-analyzer"); - for tool in &["rust-docs", "rust-demangler", "miri", "rustc-codegen-cranelift"] { + for tool in &["rust-docs", "miri", "rustc-codegen-cranelift"] { if built_tools.contains(tool) { prepare(tool); } @@ -1695,8 +1660,6 @@ impl Step for Extended { "rust-analyzer-preview".to_string() } else if name == "clippy" { "clippy-preview".to_string() - } else if name == "rust-demangler" { - "rust-demangler-preview".to_string() } else if name == "miri" { "miri-preview".to_string() } else if name == "rustc-codegen-cranelift" { @@ -1716,7 +1679,7 @@ impl Step for Extended { prepare("cargo"); prepare("rust-analysis"); prepare("rust-std"); - for tool in &["clippy", "rust-analyzer", "rust-docs", "rust-demangler", "miri"] { + for tool in &["clippy", "rust-analyzer", "rust-docs", "miri"] { if built_tools.contains(tool) { prepare(tool); } @@ -1840,25 +1803,6 @@ impl Step for Extended { .arg(etc.join("msi/remove-duplicates.xsl")), ); } - if built_tools.contains("rust-demangler") { - builder.run( - Command::new(&heat) - .current_dir(&exe) - .arg("dir") - .arg("rust-demangler") - .args(heat_flags) - .arg("-cg") - .arg("RustDemanglerGroup") - .arg("-dr") - .arg("RustDemangler") - .arg("-var") - .arg("var.RustDemanglerDir") - .arg("-out") - .arg(exe.join("RustDemanglerGroup.wxs")) - .arg("-t") - .arg(etc.join("msi/remove-duplicates.xsl")), - ); - } if built_tools.contains("miri") { builder.run( Command::new(&heat) @@ -1936,9 +1880,6 @@ impl Step for Extended { if built_tools.contains("rust-docs") { cmd.arg("-dDocsDir=rust-docs"); } - if built_tools.contains("rust-demangler") { - cmd.arg("-dRustDemanglerDir=rust-demangler"); - } if built_tools.contains("rust-analyzer") { cmd.arg("-dRustAnalyzerDir=rust-analyzer"); } @@ -1965,9 +1906,6 @@ impl Step for Extended { if built_tools.contains("miri") { candle("MiriGroup.wxs".as_ref()); } - if built_tools.contains("rust-demangler") { - candle("RustDemanglerGroup.wxs".as_ref()); - } if built_tools.contains("rust-analyzer") { candle("RustAnalyzerGroup.wxs".as_ref()); } @@ -2009,9 +1947,6 @@ impl Step for Extended { if built_tools.contains("rust-analyzer") { cmd.arg("RustAnalyzerGroup.wixobj"); } - if built_tools.contains("rust-demangler") { - cmd.arg("RustDemanglerGroup.wixobj"); - } if built_tools.contains("rust-docs") { cmd.arg("DocsGroup.wixobj"); } diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 38c48bd9570d..6748625f1323 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -29,7 +29,7 @@ macro_rules! submodule_helper { } macro_rules! book { - ($($name:ident, $path:expr, $book_name:expr $(, submodule $(= $submodule:literal)? )? ;)+) => { + ($($name:ident, $path:expr, $book_name:expr, $lang:expr $(, submodule $(= $submodule:literal)? )? ;)+) => { $( #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct $name { @@ -61,6 +61,7 @@ macro_rules! book { name: $book_name.to_owned(), src: builder.src.join($path), parent: Some(self), + languages: $lang.into(), }) } } @@ -74,15 +75,15 @@ macro_rules! book { // FIXME: Make checking for a submodule automatic somehow (maybe by having a list of all submodules // and checking against it?). book!( - CargoBook, "src/tools/cargo/src/doc", "cargo", submodule = "src/tools/cargo"; - ClippyBook, "src/tools/clippy/book", "clippy"; - EditionGuide, "src/doc/edition-guide", "edition-guide", submodule; - EmbeddedBook, "src/doc/embedded-book", "embedded-book", submodule; - Nomicon, "src/doc/nomicon", "nomicon", submodule; - Reference, "src/doc/reference", "reference", submodule; - RustByExample, "src/doc/rust-by-example", "rust-by-example", submodule; - RustdocBook, "src/doc/rustdoc", "rustdoc"; - StyleGuide, "src/doc/style-guide", "style-guide"; + CargoBook, "src/tools/cargo/src/doc", "cargo", &[], submodule = "src/tools/cargo"; + ClippyBook, "src/tools/clippy/book", "clippy", &[]; + EditionGuide, "src/doc/edition-guide", "edition-guide", &[], submodule; + EmbeddedBook, "src/doc/embedded-book", "embedded-book", &[], submodule; + Nomicon, "src/doc/nomicon", "nomicon", &[], submodule; + Reference, "src/doc/reference", "reference", &[], submodule; + RustByExample, "src/doc/rust-by-example", "rust-by-example", &["ja"], submodule; + RustdocBook, "src/doc/rustdoc", "rustdoc", &[]; + StyleGuide, "src/doc/style-guide", "style-guide", &[]; ); #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -110,6 +111,7 @@ impl Step for UnstableBook { name: "unstable-book".to_owned(), src: builder.md_doc_out(self.target).join("unstable-book"), parent: Some(self), + languages: vec![], }) } } @@ -120,6 +122,7 @@ struct RustbookSrc { name: String, src: PathBuf, parent: Option

, + languages: Vec<&'static str>, } impl Step for RustbookSrc

{ @@ -151,7 +154,19 @@ impl Step for RustbookSrc

{ builder.info(&format!("Rustbook ({target}) - {name}")); let _ = fs::remove_dir_all(&out); - builder.run(rustbook_cmd.arg("build").arg(src).arg("-d").arg(out)); + builder.run(rustbook_cmd.arg("build").arg(&src).arg("-d").arg(&out)); + + for lang in &self.languages { + let out = out.join(lang); + + builder.info(&format!("Rustbook ({target}) - {name} - {lang}")); + let _ = fs::remove_dir_all(&out); + + let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook); + builder.run( + rustbook_cmd.arg("build").arg(&src).arg("-d").arg(&out).arg("-l").arg(lang), + ); + } } if self.parent.is_some() { @@ -214,6 +229,7 @@ impl Step for TheBook { name: "book".to_owned(), src: absolute_path.clone(), parent: Some(self), + languages: vec![], }); // building older edition redirects @@ -225,6 +241,7 @@ impl Step for TheBook { // There should only be one book that is marked as the parent for each target, so // treat the other editions as not having a parent. parent: Option::::None, + languages: vec![], }); } @@ -361,7 +378,7 @@ impl Step for Standalone { .arg(&favicon) .arg("--markdown-no-toc") .arg("--index-page") - .arg(&builder.src.join("src/doc/index.md")) + .arg(builder.src.join("src/doc/index.md")) .arg("--markdown-playground-url") .arg("https://play.rust-lang.org/") .arg("-o") @@ -465,7 +482,7 @@ impl Step for Releases { .arg("--markdown-css") .arg("rust.css") .arg("--index-page") - .arg(&builder.src.join("src/doc/index.md")) + .arg(builder.src.join("src/doc/index.md")) .arg("--markdown-playground-url") .arg("https://play.rust-lang.org/") .arg("-o") @@ -1028,6 +1045,14 @@ tool_doc!( is_library = true, crates = ["bootstrap"] ); +tool_doc!( + RunMakeSupport, + "run_make_support", + "src/tools/run-make-support", + rustc_tool = false, + is_library = true, + crates = ["run_make_support"] +); #[derive(Ord, PartialOrd, Debug, Clone, Hash, PartialEq, Eq)] pub struct ErrorIndex { @@ -1200,6 +1225,7 @@ impl Step for RustcBook { name: "rustc".to_owned(), src: out_base, parent: Some(self), + languages: vec![], }); } } diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index d9dc34c01370..f5e346726868 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -1,7 +1,7 @@ //! Runs rustfmt on the repository. use crate::core::builder::Builder; -use crate::utils::helpers::{output, program_out_of_date, t}; +use crate::utils::helpers::{self, output, program_out_of_date, t}; use build_helper::ci::CiEnv; use build_helper::git::get_git_modified_files; use ignore::WalkBuilder; @@ -9,12 +9,13 @@ use std::collections::VecDeque; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::sync::mpsc::SyncSender; +use std::sync::Mutex; fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl FnMut(bool) -> bool { let mut cmd = Command::new(rustfmt); - // avoid the submodule config paths from coming into play, - // we only allow a single global config for the workspace for now - cmd.arg("--config-path").arg(&src.canonicalize().unwrap()); + // Avoid the submodule config paths from coming into play. We only allow a single global config + // for the workspace for now. + cmd.arg("--config-path").arg(src.canonicalize().unwrap()); cmd.arg("--edition").arg("2021"); cmd.arg("--unstable-features"); cmd.arg("--skip-children"); @@ -24,20 +25,23 @@ fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl F cmd.args(paths); let cmd_debug = format!("{cmd:?}"); let mut cmd = cmd.spawn().expect("running rustfmt"); - // poor man's async: return a closure that'll wait for rustfmt's completion + // Poor man's async: return a closure that might wait for rustfmt's completion (depending on + // the value of the `block` argument). move |block: bool| -> bool { - if !block { + let status = if !block { match cmd.try_wait() { - Ok(Some(_)) => {} - _ => return false, + Ok(Some(status)) => Ok(status), + Ok(None) => return false, + Err(err) => Err(err), } - } - let status = cmd.wait().unwrap(); - if !status.success() { + } else { + cmd.wait() + }; + if !status.unwrap().success() { eprintln!( - "Running `{}` failed.\nIf you're running `tidy`, \ - try again with `--bless`. Or, if you just want to format \ - code, run `./x.py fmt` instead.", + "fmt error: Running `{}` failed.\nIf you're running `tidy`, \ + try again with `--bless`. Or, if you just want to format \ + code, run `./x.py fmt` instead.", cmd_debug, ); crate::exit!(1); @@ -72,7 +76,7 @@ fn verify_rustfmt_version(build: &Builder<'_>) -> bool { !program_out_of_date(&stamp_file, &version) } -/// Updates the last rustfmt version used +/// Updates the last rustfmt version used. fn update_rustfmt_version(build: &Builder<'_>) { let Some((version, stamp_file)) = get_rustfmt_version(build) else { return; @@ -89,7 +93,7 @@ fn get_modified_rs_files(build: &Builder<'_>) -> Result>, Str return Ok(None); } - get_git_modified_files(&build.config.git_config(), Some(&build.config.src), &vec!["rs"]) + get_git_modified_files(&build.config.git_config(), Some(&build.config.src), &["rs"]) } #[derive(serde_derive::Deserialize)] @@ -97,31 +101,66 @@ struct RustfmtConfig { ignore: Vec, } -pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) { +// Prints output describing a collection of paths, with lines such as "formatted modified file +// foo/bar/baz" or "skipped 20 untracked files". +fn print_paths(verb: &str, adjective: Option<&str>, paths: &[String]) { + let len = paths.len(); + let adjective = + if let Some(adjective) = adjective { format!("{adjective} ") } else { String::new() }; + if len <= 10 { + for path in paths { + println!("fmt: {verb} {adjective}file {path}"); + } + } else { + println!("fmt: {verb} {len} {adjective}files"); + } +} + +pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { + if !paths.is_empty() { + eprintln!( + "fmt error: path arguments are no longer accepted; use `--all` to format everything" + ); + crate::exit!(1); + }; if build.config.dry_run() { return; } + + // By default, we only check modified files locally to speed up runtime. Exceptions are if + // `--all` is specified or we are in CI. We check all files in CI to avoid bugs in + // `get_modified_rs_files` letting regressions slip through; we also care about CI time less + // since this is still very fast compared to building the compiler. + let all = all || CiEnv::is_ci(); + let mut builder = ignore::types::TypesBuilder::new(); builder.add_defaults(); builder.select("rust"); let matcher = builder.build().unwrap(); let rustfmt_config = build.src.join("rustfmt.toml"); if !rustfmt_config.exists() { - eprintln!("Not running formatting checks; rustfmt.toml does not exist."); - eprintln!("This may happen in distributed tarballs."); + eprintln!("fmt error: Not running formatting checks; rustfmt.toml does not exist."); + eprintln!("fmt error: This may happen in distributed tarballs."); return; } let rustfmt_config = t!(std::fs::read_to_string(&rustfmt_config)); let rustfmt_config: RustfmtConfig = t!(toml::from_str(&rustfmt_config)); - let mut fmt_override = ignore::overrides::OverrideBuilder::new(&build.src); + let mut override_builder = ignore::overrides::OverrideBuilder::new(&build.src); for ignore in rustfmt_config.ignore { - if let Some(ignore) = ignore.strip_prefix('!') { - fmt_override.add(ignore).expect(ignore); + if ignore.starts_with('!') { + // A `!`-prefixed entry could be added as a whitelisted entry in `override_builder`, + // i.e. strip the `!` prefix. But as soon as whitelisted entries are added, an + // `OverrideBuilder` will only traverse those whitelisted entries, and won't traverse + // any files that aren't explicitly mentioned. No bueno! Maybe there's a way to combine + // explicit whitelisted entries and traversal of unmentioned files, but for now just + // forbid such entries. + eprintln!("fmt error: `!`-prefixed entries are not supported in rustfmt.toml, sorry"); + crate::exit!(1); } else { - fmt_override.add(&format!("!{ignore}")).expect(&ignore); + override_builder.add(&format!("!{ignore}")).expect(&ignore); } } - let git_available = match Command::new("git") + let git_available = match helpers::git(None) .arg("--version") .stdout(Stdio::null()) .stderr(Stdio::null()) @@ -131,10 +170,9 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) { Err(_) => false, }; + let mut adjective = None; if git_available { - let in_working_tree = match build - .config - .git() + let in_working_tree = match helpers::git(Some(&build.src)) .arg("rev-parse") .arg("--is-inside-work-tree") .stdout(Stdio::null()) @@ -146,150 +184,80 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) { }; if in_working_tree { let untracked_paths_output = output( - build - .config - .git() + helpers::git(Some(&build.src)) .arg("status") .arg("--porcelain") .arg("-z") .arg("--untracked-files=normal"), ); - let untracked_paths = untracked_paths_output.split_terminator('\0').filter_map( - |entry| entry.strip_prefix("?? "), // returns None if the prefix doesn't match - ); - let mut untracked_count = 0; + let untracked_paths: Vec<_> = untracked_paths_output + .split_terminator('\0') + .filter_map( + |entry| entry.strip_prefix("?? "), // returns None if the prefix doesn't match + ) + .map(|x| x.to_string()) + .collect(); + print_paths("skipped", Some("untracked"), &untracked_paths); + for untracked_path in untracked_paths { - println!("skip untracked path {untracked_path} during rustfmt invocations"); // The leading `/` makes it an exact match against the // repository root, rather than a glob. Without that, if you // have `foo.rs` in the repository root it will also match // against anything like `compiler/rustc_foo/src/foo.rs`, // preventing the latter from being formatted. - untracked_count += 1; - fmt_override.add(&format!("!/{untracked_path}")).expect(untracked_path); + override_builder.add(&format!("!/{untracked_path}")).expect(&untracked_path); } - // Only check modified files locally to speed up runtime. - // We still check all files in CI to avoid bugs in `get_modified_rs_files` letting regressions slip through; - // we also care about CI time less since this is still very fast compared to building the compiler. - if !CiEnv::is_ci() && paths.is_empty() { + if !all { + adjective = Some("modified"); match get_modified_rs_files(build) { Ok(Some(files)) => { - if files.len() <= 10 { - for file in &files { - println!("formatting modified file {file}"); - } - } else { - let pluralized = |count| if count > 1 { "files" } else { "file" }; - let untracked_msg = if untracked_count == 0 { - "".to_string() - } else { - format!( - ", skipped {} untracked {}", - untracked_count, - pluralized(untracked_count), - ) - }; - println!( - "formatting {} modified {}{}", - files.len(), - pluralized(files.len()), - untracked_msg - ); - } for file in files { - fmt_override.add(&format!("/{file}")).expect(&file); + override_builder.add(&format!("/{file}")).expect(&file); } } Ok(None) => {} Err(err) => { - println!( - "WARN: Something went wrong when running git commands:\n{err}\n\ - Falling back to formatting all files." - ); + eprintln!("fmt warning: Something went wrong running git commands:"); + eprintln!("fmt warning: {err}"); + eprintln!("fmt warning: Falling back to formatting all files."); } } } } else { - println!("Not in git tree. Skipping git-aware format checks"); + eprintln!("fmt: warning: Not in git tree. Skipping git-aware format checks"); } } else { - println!("Could not find usable git. Skipping git-aware format checks"); + eprintln!("fmt: warning: Could not find usable git. Skipping git-aware format checks"); } - let fmt_override = fmt_override.build().unwrap(); + let override_ = override_builder.build().unwrap(); // `override` is a reserved keyword let rustfmt_path = build.initial_rustfmt().unwrap_or_else(|| { - eprintln!("./x.py fmt is not supported on this channel"); + eprintln!("fmt error: `x fmt` is not supported on this channel"); crate::exit!(1); }); assert!(rustfmt_path.exists(), "{}", rustfmt_path.display()); let src = build.src.clone(); let (tx, rx): (SyncSender, _) = std::sync::mpsc::sync_channel(128); - let walker = match paths.first() { - Some(first) => { - let find_shortcut_candidates = |p: &PathBuf| { - let mut candidates = Vec::new(); - for entry in - WalkBuilder::new(src.clone()).max_depth(Some(3)).build().map_while(Result::ok) - { - if let Some(dir_name) = p.file_name() { - if entry.path().is_dir() && entry.file_name() == dir_name { - candidates.push(entry.into_path()); - } - } - } - candidates - }; + let walker = WalkBuilder::new(src.clone()).types(matcher).overrides(override_).build_parallel(); - // Only try to look for shortcut candidates for single component paths like - // `std` and not for e.g. relative paths like `../library/std`. - let should_look_for_shortcut_dir = |p: &PathBuf| p.components().count() == 1; - - let mut walker = if should_look_for_shortcut_dir(first) { - if let [single_candidate] = &find_shortcut_candidates(first)[..] { - WalkBuilder::new(single_candidate) - } else { - WalkBuilder::new(first) - } - } else { - WalkBuilder::new(src.join(first)) - }; - - for path in &paths[1..] { - if should_look_for_shortcut_dir(path) { - if let [single_candidate] = &find_shortcut_candidates(path)[..] { - walker.add(single_candidate); - } else { - walker.add(path); - } - } else { - walker.add(src.join(path)); - } - } - - walker - } - None => WalkBuilder::new(src.clone()), - } - .types(matcher) - .overrides(fmt_override) - .build_parallel(); - - // there is a lot of blocking involved in spawning a child process and reading files to format. - // spawn more processes than available concurrency to keep the CPU busy + // There is a lot of blocking involved in spawning a child process and reading files to format. + // Spawn more processes than available concurrency to keep the CPU busy. let max_processes = build.jobs() as usize * 2; - // spawn child processes on a separate thread so we can batch entries we have received from ignore + // Spawn child processes on a separate thread so we can batch entries we have received from + // ignore. let thread = std::thread::spawn(move || { let mut children = VecDeque::new(); while let Ok(path) = rx.recv() { - // try getting more paths from the channel to amortize the overhead of spawning processes + // Try getting more paths from the channel to amortize the overhead of spawning + // processes. let paths: Vec<_> = rx.try_iter().take(63).chain(std::iter::once(path)).collect(); let child = rustfmt(&src, &rustfmt_path, paths.as_slice(), check); children.push_back(child); - // poll completion before waiting + // Poll completion before waiting. for i in (0..children.len()).rev() { if children[i](false) { children.swap_remove_back(i); @@ -298,27 +266,44 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) { } if children.len() >= max_processes { - // await oldest child + // Await oldest child. children.pop_front().unwrap()(true); } } - // await remaining children + // Await remaining children. for mut child in children { child(true); } }); + let formatted_paths = Mutex::new(Vec::new()); + let formatted_paths_ref = &formatted_paths; walker.run(|| { let tx = tx.clone(); Box::new(move |entry| { + let cwd = std::env::current_dir(); let entry = t!(entry); if entry.file_type().map_or(false, |t| t.is_file()) { + formatted_paths_ref.lock().unwrap().push({ + // `into_path` produces an absolute path. Try to strip `cwd` to get a shorter + // relative path. + let mut path = entry.clone().into_path(); + if let Ok(cwd) = cwd { + if let Ok(path2) = path.strip_prefix(cwd) { + path = path2.to_path_buf(); + } + } + path.display().to_string() + }); t!(tx.send(entry.into_path())); } ignore::WalkState::Continue }) }); + let mut paths = formatted_paths.into_inner().unwrap(); + paths.sort(); + print_paths(if check { "checked" } else { "formatted" }, adjective, &paths); drop(tx); diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index 6a75f35c93a6..c47233ca42ab 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -265,22 +265,6 @@ install!((self, builder, _config), ); } }; - RustDemangler, alias = "rust-demangler", Self::should_build(_config), only_hosts: true, { - // NOTE: Even though `should_build` may return true for `extended` default tools, - // dist::RustDemangler may still return None, unless the target-dependent `profiler` config - // is also true, or the `tools` array explicitly includes "rust-demangler". - if let Some(tarball) = builder.ensure(dist::RustDemangler { - compiler: self.compiler, - target: self.target - }) { - install_sh(builder, "rust-demangler", self.compiler.stage, Some(self.target), &tarball); - } else { - builder.info( - &format!("skipping Install RustDemangler stage{} ({})", - self.compiler.stage, self.target), - ); - } - }; Rustc, path = "compiler/rustc", true, only_hosts: true, { let tarball = builder.ensure(dist::Rustc { compiler: builder.compiler(builder.top_stage, self.target), diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 3af1a05caa82..f90403d2ec45 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -159,7 +159,7 @@ pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String { // in that case. let closest_upstream = get_git_merge_base(&config.git_config(), Some(&config.src)) .unwrap_or_else(|_| "HEAD".into()); - let mut rev_list = config.git(); + let mut rev_list = helpers::git(Some(&config.src)); rev_list.args(&[ PathBuf::from("rev-list"), format!("--author={}", config.stage0_metadata.config.git_merge_commit_email).into(), @@ -252,7 +252,7 @@ pub(crate) fn is_ci_llvm_modified(config: &Config) -> bool { // We assume we have access to git, so it's okay to unconditionally pass // `true` here. let llvm_sha = detect_llvm_sha(config, true); - let head_sha = output(config.git().arg("rev-parse").arg("HEAD")); + let head_sha = output(helpers::git(Some(&config.src)).arg("rev-parse").arg("HEAD")); let head_sha = head_sha.trim(); llvm_sha == head_sha } @@ -330,7 +330,7 @@ impl Step for Llvm { let llvm_exp_targets = match builder.config.llvm_experimental_targets { Some(ref s) => s, - None => "AVR;M68k;CSKY", + None => "AVR;M68k;CSKY;Xtensa", }; let assertions = if builder.config.llvm_assertions { "ON" } else { "OFF" }; @@ -508,7 +508,7 @@ impl Step for Llvm { cfg.define("LLVM_VERSION_SUFFIX", suffix); } - configure_cmake(builder, target, &mut cfg, true, ldflags, &[], &[]); + configure_cmake(builder, target, &mut cfg, true, ldflags, &[]); configure_llvm(builder, target, &mut cfg); for (key, val) in &builder.config.llvm_build_config { @@ -597,7 +597,6 @@ fn configure_cmake( cfg: &mut cmake::Config, use_compiler_launcher: bool, mut ldflags: LdFlags, - extra_compiler_flags: &[&str], suppressed_compiler_flag_prefixes: &[&str], ) { // Do not print installation messages for up-to-date files. @@ -751,9 +750,6 @@ fn configure_cmake( if builder.config.llvm_clang_cl.is_some() { cflags.push(&format!(" --target={target}")); } - for flag in extra_compiler_flags { - cflags.push(&format!(" {flag}")); - } cfg.define("CMAKE_C_FLAGS", cflags); let mut cxxflags: OsString = builder .cflags(target, GitRepo::Llvm, CLang::Cxx) @@ -773,9 +769,6 @@ fn configure_cmake( if builder.config.llvm_clang_cl.is_some() { cxxflags.push(&format!(" --target={target}")); } - for flag in extra_compiler_flags { - cxxflags.push(&format!(" {flag}")); - } cfg.define("CMAKE_CXX_FLAGS", cxxflags); if let Some(ar) = builder.ar(target) { if ar.is_absolute() { @@ -944,7 +937,7 @@ impl Step for Lld { ldflags.push_all("-Wl,-rpath,'$ORIGIN/../../../'"); } - configure_cmake(builder, target, &mut cfg, true, ldflags, &[], &[]); + configure_cmake(builder, target, &mut cfg, true, ldflags, &[]); configure_llvm(builder, target, &mut cfg); // Re-use the same flags as llvm to control the level of debug information @@ -1043,8 +1036,6 @@ impl Step for Sanitizers { // Unfortunately sccache currently lacks support to build them successfully. // Disable compiler launcher on Darwin targets to avoid potential issues. let use_compiler_launcher = !self.target.contains("apple-darwin"); - let extra_compiler_flags: &[&str] = - if self.target.contains("apple") { &["-fembed-bitcode=off"] } else { &[] }; // Since v1.0.86, the cc crate adds -mmacosx-version-min to the default // flags on MacOS. A long-standing bug in the CMake rules for compiler-rt // causes architecture detection to be skipped when this flag is present, @@ -1057,7 +1048,6 @@ impl Step for Sanitizers { &mut cfg, use_compiler_launcher, LdFlags::default(), - extra_compiler_flags, suppressed_compiler_flag_prefixes, ); diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 354eb2b60038..9268b335db7c 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -149,12 +149,14 @@ impl Step for Miri { &[], ); miri.add_rustc_lib_path(builder); - // Forward arguments. miri.arg("--").arg("--target").arg(target.rustc_target_arg()); - miri.args(builder.config.args()); // miri tests need to know about the stage sysroot - miri.env("MIRI_SYSROOT", &miri_sysroot); + miri.arg("--sysroot").arg(miri_sysroot); + + // Forward arguments. This may contain further arguments to the program + // after another --, so this must be at the end. + miri.args(builder.config.args()); let mut miri = Command::from(miri); builder.run(&mut miri); diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index df38d6166eb8..947c74f32c99 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -8,7 +8,7 @@ use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::t; use crate::utils::change_tracker::CONFIG_CHANGE_HISTORY; -use crate::utils::helpers::hex_encode; +use crate::utils::helpers::{self, hex_encode}; use crate::Config; use sha2::Digest; use std::env::consts::EXE_SUFFIX; @@ -482,10 +482,13 @@ impl Step for Hook { // install a git hook to automatically run tidy, if they want fn install_git_hook_maybe(config: &Config) -> io::Result<()> { - let git = config.git().args(["rev-parse", "--git-common-dir"]).output().map(|output| { - assert!(output.status.success(), "failed to run `git`"); - PathBuf::from(t!(String::from_utf8(output.stdout)).trim()) - })?; + let git = helpers::git(Some(&config.src)) + .args(["rev-parse", "--git-common-dir"]) + .output() + .map(|output| { + assert!(output.status.success(), "failed to run `git`"); + PathBuf::from(t!(String::from_utf8(output.stdout)).trim()) + })?; let hooks_dir = git.join("hooks"); let dst = hooks_dir.join("pre-push"); if dst.exists() { diff --git a/src/bootstrap/src/core/build_steps/synthetic_targets.rs b/src/bootstrap/src/core/build_steps/synthetic_targets.rs index 89d50b5ffffa..281a9b093b90 100644 --- a/src/bootstrap/src/core/build_steps/synthetic_targets.rs +++ b/src/bootstrap/src/core/build_steps/synthetic_targets.rs @@ -80,8 +80,5 @@ fn create_synthetic_target( customize(spec_map); std::fs::write(&path, serde_json::to_vec_pretty(&spec).unwrap()).unwrap(); - let target = TargetSelection::create_synthetic(&name, path.to_str().unwrap()); - crate::utils::cc_detect::find_target(builder, target); - - target + TargetSelection::create_synthetic(&name, path.to_str().unwrap()) } diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 360bd3840d45..445096e9786d 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -308,7 +308,7 @@ impl Step for Cargo { // Forcibly disable tests using nightly features since any changes to // those features won't be able to land. cargo.env("CARGO_TEST_DISABLE_NIGHTLY", "1"); - cargo.env("PATH", &path_for_cargo(builder, compiler)); + cargo.env("PATH", path_for_cargo(builder, compiler)); #[cfg(feature = "build-metrics")] builder.metrics.begin_test_suite( @@ -432,65 +432,6 @@ impl Step for Rustfmt { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct RustDemangler { - stage: u32, - host: TargetSelection, -} - -impl Step for RustDemangler { - type Output = (); - const ONLY_HOSTS: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/tools/rust-demangler") - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure(RustDemangler { stage: run.builder.top_stage, host: run.target }); - } - - /// Runs `cargo test` for rust-demangler. - fn run(self, builder: &Builder<'_>) { - let stage = self.stage; - let host = self.host; - let compiler = builder.compiler(stage, host); - - let rust_demangler = builder.ensure(tool::RustDemangler { - compiler, - target: self.host, - extra_features: Vec::new(), - }); - let mut cargo = tool::prepare_tool_cargo( - builder, - compiler, - Mode::ToolRustc, - host, - "test", - "src/tools/rust-demangler", - SourceType::InTree, - &[], - ); - - let dir = testdir(builder, compiler.host); - t!(fs::create_dir_all(dir)); - - cargo.env("RUST_DEMANGLER_DRIVER_PATH", rust_demangler); - cargo.add_rustc_lib_path(builder); - - run_cargo_test( - cargo, - &[], - &[], - "rust-demangler", - "rust-demangler", - compiler, - host, - builder, - ); - } -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Miri { target: TargetSelection, @@ -1101,6 +1042,8 @@ impl Step for Tidy { /// Once tidy passes, this step also runs `fmt --check` if tests are being run /// for the `dev` or `nightly` channels. fn run(self, builder: &Builder<'_>) { + builder.build.update_submodule(Path::new("src/tools/rustc-perf")); + let mut cmd = builder.tool_cmd(Tool::Tidy); cmd.arg(&builder.src); cmd.arg(&builder.initial_cargo); @@ -1140,7 +1083,13 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to ); crate::exit!(1); } - crate::core::build_steps::format::format(builder, !builder.config.cmd.bless(), &[]); + let all = false; + crate::core::build_steps::format::format( + builder, + !builder.config.cmd.bless(), + all, + &[], + ); } builder.info("tidy check"); @@ -1481,12 +1430,6 @@ impl Step for RunMake { } } -host_test!(RunMakeFullDeps { - path: "tests/run-make-fulldeps", - mode: "run-make", - suite: "run-make-fulldeps" -}); - default_test!(Assembly { path: "tests/assembly", mode: "assembly", suite: "assembly" }); /// Coverage tests are a bit more complicated than other test suites, because @@ -1775,25 +1718,11 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the .arg(builder.ensure(tool::JsonDocLint { compiler: json_compiler, target })); } - if mode == "coverage-map" { - let coverage_dump = builder.ensure(tool::CoverageDump { - compiler: compiler.with_stage(0), - target: compiler.host, - }); + if matches!(mode, "coverage-map" | "coverage-run") { + let coverage_dump = builder.tool_exe(Tool::CoverageDump); cmd.arg("--coverage-dump-path").arg(coverage_dump); } - if mode == "coverage-run" { - // The demangler doesn't need the current compiler, so we can avoid - // unnecessary rebuilds by using the bootstrap compiler instead. - let rust_demangler = builder.ensure(tool::RustDemangler { - compiler: compiler.with_stage(0), - target: compiler.host, - extra_features: Vec::new(), - }); - cmd.arg("--rust-demangler-path").arg(rust_demangler); - } - cmd.arg("--src-base").arg(builder.src.join("tests").join(suite)); cmd.arg("--build-base").arg(testdir(builder, compiler.host).join(suite)); @@ -1981,9 +1910,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cmd); } - if !builder.config.dry_run() - && (matches!(suite, "run-make" | "run-make-fulldeps") || mode == "coverage-run") - { + if !builder.config.dry_run() && matches!(mode, "run-make" | "coverage-run") { // The llvm/bin directory contains many useful cross-platform // tools. Pass the path to run-make tests so they can use them. // (The coverage-run tests also need these tools to process @@ -1995,7 +1922,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--llvm-bin-dir").arg(llvm_bin_path); } - if !builder.config.dry_run() && matches!(suite, "run-make" | "run-make-fulldeps") { + if !builder.config.dry_run() && mode == "run-make" { // If LLD is available, add it to the PATH if builder.config.lld_enabled { let lld_install_root = @@ -2015,7 +1942,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // Only pass correct values for these flags for the `run-make` suite as it // requires that a C++ compiler was configured which isn't always the case. - if !builder.config.dry_run() && matches!(suite, "run-make" | "run-make-fulldeps") { + if !builder.config.dry_run() && mode == "run-make" { cmd.arg("--cc") .arg(builder.cc(target)) .arg("--cxx") @@ -2573,7 +2500,7 @@ fn prepare_cargo_test( cargo.arg("-p").arg(krate); } - cargo.arg("--").args(&builder.config.test_args()).args(libtest_args); + cargo.arg("--").args(builder.config.test_args()).args(libtest_args); if !builder.config.verbose_tests { cargo.arg("--quiet"); } @@ -3061,6 +2988,7 @@ impl Step for Bootstrap { let mut cmd = Command::new(&builder.initial_cargo); cmd.arg("test") + .args(["--features", "bootstrap-self-test"]) .current_dir(builder.src.join("src/bootstrap")) .env("RUSTFLAGS", "-Cdebuginfo=2") .env("CARGO_TARGET_DIR", builder.out.join("bootstrap")) @@ -3121,7 +3049,7 @@ impl Step for TierCheck { &[], ); cargo.arg(builder.src.join("src/doc/rustc/src/platform-support.md")); - cargo.arg(&builder.rustc(self.compiler)); + cargo.arg(builder.rustc(self.compiler)); if builder.is_verbose() { cargo.arg("--verbose"); } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 21344a4224e4..613484788b60 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1,6 +1,6 @@ use std::env; use std::fs; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; use crate::core::build_steps::compile; @@ -9,7 +9,7 @@ use crate::core::builder; use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; use crate::utils::channel::GitInfo; -use crate::utils::exec::BootstrapCommand; +use crate::utils::helpers::output; use crate::utils::helpers::{add_dylib_path, exe, t}; use crate::Compiler; use crate::Mode; @@ -109,9 +109,8 @@ impl Step for ToolBuild { &self.target, ); - let mut cargo = Command::from(cargo); // we check this below - let build_success = builder.run_cmd(BootstrapCommand::from(&mut cargo).allow_failure()); + let build_success = compile::stream_cargo(builder, cargo, vec![], &mut |_| {}); builder.save_toolstate( tool, @@ -200,7 +199,7 @@ pub fn prepare_tool_cargo( cargo.env("CFG_COMMIT_DATE", date); } if !features.is_empty() { - cargo.arg("--features").arg(&features.join(", ")); + cargo.arg("--features").arg(features.join(", ")); } // Enable internal lints for clippy and rustdoc @@ -313,10 +312,47 @@ bootstrap_tool!( SuggestTests, "src/tools/suggest-tests", "suggest-tests"; GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys"; RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = "test"; - OptimizedDist, "src/tools/opt-dist", "opt-dist"; CoverageDump, "src/tools/coverage-dump", "coverage-dump"; ); +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct OptimizedDist { + pub compiler: Compiler, + pub target: TargetSelection, +} + +impl Step for OptimizedDist { + type Output = PathBuf; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/opt-dist") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(OptimizedDist { + compiler: run.builder.compiler(0, run.builder.config.build), + target: run.target, + }); + } + + fn run(self, builder: &Builder<'_>) -> PathBuf { + // We need to ensure the rustc-perf submodule is initialized when building opt-dist since + // the tool requires it to be in place to run. + builder.update_submodule(Path::new("src/tools/rustc-perf")); + + builder.ensure(ToolBuild { + compiler: self.compiler, + target: self.target, + tool: "opt-dist", + mode: Mode::ToolBootstrap, + path: "src/tools/opt-dist", + source_type: SourceType::InTree, + extra_features: Vec::new(), + allow_features: "", + }) + } +} + #[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] pub struct ErrorIndex { pub compiler: Compiler, @@ -485,10 +521,7 @@ impl Step for Rustdoc { // If the rustdoc output is piped to e.g. `head -n1` we want the process // to be killed, rather than having an error bubble up and cause a // panic. - // FIXME: Synthetic #[cfg(bootstrap)]. Remove when the bootstrap compiler supports it. - if build_compiler.stage > 0 { - cargo.rustflag("-Zon-broken-pipe=kill"); - } + cargo.rustflag("-Zon-broken-pipe=kill"); let _guard = builder.msg_tool( Kind::Build, @@ -767,6 +800,69 @@ impl Step for LlvmBitcodeLinker { } } +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct LibcxxVersionTool { + pub target: TargetSelection, +} + +#[allow(dead_code)] +#[derive(Debug, Clone)] +pub enum LibcxxVersion { + Gnu(usize), + Llvm(usize), +} + +impl Step for LibcxxVersionTool { + type Output = LibcxxVersion; + const DEFAULT: bool = false; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.never() + } + + fn run(self, builder: &Builder<'_>) -> LibcxxVersion { + let out_dir = builder.out.join(self.target.to_string()).join("libcxx-version"); + let executable = out_dir.join(exe("libcxx-version", self.target)); + + // This is a sanity-check specific step, which means it is frequently called (when using + // CI LLVM), and compiling `src/tools/libcxx-version/main.cpp` at the beginning of the bootstrap + // invocation adds a fair amount of overhead to the process (see https://github.com/rust-lang/rust/issues/126423). + // Therefore, we want to avoid recompiling this file unnecessarily. + if !executable.exists() { + if !out_dir.exists() { + t!(fs::create_dir_all(&out_dir)); + } + + let compiler = builder.cxx(self.target).unwrap(); + let mut cmd = Command::new(compiler); + + cmd.arg("-o") + .arg(&executable) + .arg(builder.src.join("src/tools/libcxx-version/main.cpp")); + + builder.run_cmd(&mut cmd); + + if !executable.exists() { + panic!("Something went wrong. {} is not present", executable.display()); + } + } + + let version_output = output(&mut Command::new(executable)); + + let version_str = version_output.split_once("version:").unwrap().1; + let version = version_str.trim().parse::().unwrap(); + + if version_output.starts_with("libstdc++") { + LibcxxVersion::Gnu(version) + } else if version_output.starts_with("libc++") { + LibcxxVersion::Llvm(version) + } else { + panic!("Coudln't recognize the standard library version."); + } + } +} + macro_rules! tool_extended { (($sel:ident, $builder:ident), $($name:ident, @@ -868,7 +964,6 @@ tool_extended!((self, builder), // But `builder.cargo` doesn't know how to handle ToolBootstrap in stages other than 0, // and this is close enough for now. Rls, "src/tools/rls", "rls", stable=true, tool_std=true; - RustDemangler, "src/tools/rust-demangler", "rust-demangler", stable=false, tool_std=true; Rustfmt, "src/tools/rustfmt", "rustfmt", stable=true, add_bins_to_sysroot = ["rustfmt", "cargo-fmt"]; ); diff --git a/src/bootstrap/src/core/build_steps/toolstate.rs b/src/bootstrap/src/core/build_steps/toolstate.rs index ca3756df4d77..9e6d03349b5f 100644 --- a/src/bootstrap/src/core/build_steps/toolstate.rs +++ b/src/bootstrap/src/core/build_steps/toolstate.rs @@ -5,7 +5,7 @@ //! [Toolstate]: https://forge.rust-lang.org/infra/toolstate.html use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; -use crate::utils::helpers::t; +use crate::utils::helpers::{self, t}; use serde_derive::{Deserialize, Serialize}; use std::collections::HashMap; use std::env; @@ -13,7 +13,6 @@ use std::fmt; use std::fs; use std::io::{Seek, SeekFrom}; use std::path::{Path, PathBuf}; -use std::process::Command; use std::time; // Each cycle is 42 days long (6 weeks); the last week is 35..=42 then. @@ -102,12 +101,8 @@ fn print_error(tool: &str, submodule: &str) { fn check_changed_files(toolstates: &HashMap, ToolState>) { // Changed files - let output = std::process::Command::new("git") - .arg("diff") - .arg("--name-status") - .arg("HEAD") - .arg("HEAD^") - .output(); + let output = + helpers::git(None).arg("diff").arg("--name-status").arg("HEAD").arg("HEAD^").output(); let output = match output { Ok(o) => o, Err(e) => { @@ -324,7 +319,7 @@ fn checkout_toolstate_repo() { t!(fs::remove_dir_all(TOOLSTATE_DIR)); } - let status = Command::new("git") + let status = helpers::git(None) .arg("clone") .arg("--depth=1") .arg(toolstate_repo()) @@ -342,7 +337,7 @@ fn checkout_toolstate_repo() { /// Sets up config and authentication for modifying the toolstate repo. fn prepare_toolstate_config(token: &str) { fn git_config(key: &str, value: &str) { - let status = Command::new("git").arg("config").arg("--global").arg(key).arg(value).status(); + let status = helpers::git(None).arg("config").arg("--global").arg(key).arg(value).status(); let success = match status { Ok(s) => s.success(), Err(_) => false, @@ -406,8 +401,7 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData) { publish_test_results(current_toolstate); // `git commit` failing means nothing to commit. - let status = t!(Command::new("git") - .current_dir(TOOLSTATE_DIR) + let status = t!(helpers::git(Some(Path::new(TOOLSTATE_DIR))) .arg("commit") .arg("-a") .arg("-m") @@ -418,8 +412,7 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData) { break; } - let status = t!(Command::new("git") - .current_dir(TOOLSTATE_DIR) + let status = t!(helpers::git(Some(Path::new(TOOLSTATE_DIR))) .arg("push") .arg("origin") .arg("master") @@ -431,15 +424,13 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData) { } eprintln!("Sleeping for 3 seconds before retrying push"); std::thread::sleep(std::time::Duration::from_secs(3)); - let status = t!(Command::new("git") - .current_dir(TOOLSTATE_DIR) + let status = t!(helpers::git(Some(Path::new(TOOLSTATE_DIR))) .arg("fetch") .arg("origin") .arg("master") .status()); assert!(status.success()); - let status = t!(Command::new("git") - .current_dir(TOOLSTATE_DIR) + let status = t!(helpers::git(Some(Path::new(TOOLSTATE_DIR))) .arg("reset") .arg("--hard") .arg("origin/master") @@ -458,7 +449,7 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData) { /// `publish_toolstate.py` script if the PR passes all tests and is merged to /// master. fn publish_test_results(current_toolstate: &ToolstateData) { - let commit = t!(std::process::Command::new("git").arg("rev-parse").arg("HEAD").output()); + let commit = t!(helpers::git(None).arg("rev-parse").arg("HEAD").output()); let commit = t!(String::from_utf8(commit.stdout)); let toolstate_serialized = t!(serde_json::to_string(¤t_toolstate)); diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 045cde56f411..3c4806a13117 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -332,7 +332,6 @@ const PATH_REMAP: &[(&str, &[&str])] = &[ "tests/mir-opt", "tests/pretty", "tests/run-make", - "tests/run-make-fulldeps", "tests/run-pass-valgrind", "tests/rustdoc", "tests/rustdoc-gui", @@ -737,7 +736,6 @@ impl<'a> Builder<'a> { tool::Rls, tool::RustAnalyzer, tool::RustAnalyzerProcMacroSrv, - tool::RustDemangler, tool::Rustdoc, tool::Clippy, tool::CargoClippy, @@ -775,7 +773,6 @@ impl<'a> Builder<'a> { clippy::RemoteTestServer, clippy::Rls, clippy::RustAnalyzer, - clippy::RustDemangler, clippy::Rustdoc, clippy::Rustfmt, clippy::RustInstaller, @@ -828,7 +825,6 @@ impl<'a> Builder<'a> { test::RustAnalyzer, test::ErrorIndex, test::Distcheck, - test::RunMakeFullDeps, test::Nomicon, test::Reference, test::RustdocBook, @@ -844,7 +840,6 @@ impl<'a> Builder<'a> { test::Miri, test::CargoMiri, test::Clippy, - test::RustDemangler, test::CompiletestTest, test::CrateRunMakeSupport, test::RustdocJSStd, @@ -888,6 +883,7 @@ impl<'a> Builder<'a> { doc::Tidy, doc::Bootstrap, doc::Releases, + doc::RunMakeSupport, ), Kind::Dist => describe!( dist::Docs, @@ -904,7 +900,6 @@ impl<'a> Builder<'a> { dist::Rls, dist::RustAnalyzer, dist::Rustfmt, - dist::RustDemangler, dist::Clippy, dist::Miri, dist::LlvmTools, @@ -931,7 +926,6 @@ impl<'a> Builder<'a> { install::Cargo, install::RustAnalyzer, install::Rustfmt, - install::RustDemangler, install::Clippy, install::Miri, install::LlvmTools, @@ -1044,7 +1038,8 @@ impl<'a> Builder<'a> { // custom build of rustdoc maybe? link to the latest stable docs just in case _ => "stable", }; - "https://doc.rust-lang.org/".to_owned() + channel + + format!("https://doc.rust-lang.org/{channel}") } fn run_step_descriptions(&self, v: &[StepDescription], paths: &[PathBuf]) { @@ -1555,7 +1550,6 @@ impl<'a> Builder<'a> { // features but cargo isn't involved in the #[path] process and so cannot pass the // complete list of features, so for that reason we don't enable checking of // features for std crates. - cargo.arg("-Zcheck-cfg"); if mode == Mode::Std { rustflags.arg("--check-cfg=cfg(feature,values(any()))"); } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 9710365ef114..276fd0b11d64 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -60,7 +60,14 @@ fn check_cli(paths: [&str; N]) { macro_rules! std { ($host:ident => $target:ident, stage = $stage:literal) => { compile::Std::new( - Compiler { host: TargetSelection::from_user(concat!(stringify!($host), "-", stringify!($host))), stage: $stage }, + Compiler { + host: TargetSelection::from_user(concat!( + stringify!($host), + "-", + stringify!($host) + )), + stage: $stage, + }, TargetSelection::from_user(concat!(stringify!($target), "-", stringify!($target))), ) }; @@ -83,7 +90,14 @@ macro_rules! doc_std { macro_rules! rustc { ($host:ident => $target:ident, stage = $stage:literal) => { compile::Rustc::new( - Compiler { host: TargetSelection::from_user(concat!(stringify!($host), "-", stringify!($host))), stage: $stage }, + Compiler { + host: TargetSelection::from_user(concat!( + stringify!($host), + "-", + stringify!($host) + )), + stage: $stage, + }, TargetSelection::from_user(concat!(stringify!($target), "-", stringify!($target))), ) }; @@ -141,10 +155,14 @@ fn check_missing_paths_for_x_test_tests() { // Skip if not a test directory. if path.ends_with("tests/auxiliary") || !path.is_dir() { - continue + continue; } - assert!(tests_remap_paths.iter().any(|item| path.ends_with(*item)), "{} is missing in PATH_REMAP tests list.", path.display()); + assert!( + tests_remap_paths.iter().any(|item| path.ends_with(*item)), + "{} is missing in PATH_REMAP tests list.", + path.display() + ); } } @@ -185,7 +203,8 @@ fn alias_and_path_for_library() { &[std!(A => A, stage = 0), std!(A => A, stage = 1)] ); - let mut cache = run_build(&["library".into(), "core".into()], configure("doc", &["A-A"], &["A-A"])); + let mut cache = + run_build(&["library".into(), "core".into()], configure("doc", &["A-A"], &["A-A"])); assert_eq!(first(cache.all::()), &[doc_std!(A => A, stage = 0)]); } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 19119a073c5f..0438dee7241f 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -10,7 +10,7 @@ use std::env; use std::fmt::{self, Display}; use std::fs; use std::io::IsTerminal; -use std::path::{Path, PathBuf}; +use std::path::{absolute, Path, PathBuf}; use std::process::Command; use std::str::FromStr; use std::sync::OnceLock; @@ -20,10 +20,8 @@ use crate::core::build_steps::llvm; use crate::core::config::flags::{Color, Flags, Warnings}; use crate::utils::cache::{Interned, INTERNER}; use crate::utils::channel::{self, GitInfo}; -use crate::utils::helpers::{exe, output, t}; +use crate::utils::helpers::{self, exe, output, t}; use build_helper::exit; -use build_helper::util::fail; -use semver::Version; use serde::{Deserialize, Deserializer}; use serde_derive::Deserialize; @@ -314,7 +312,6 @@ pub struct Config { pub save_toolstates: Option, pub print_step_timings: bool, pub print_step_rusage: bool, - pub missing_tools: bool, // FIXME: Deprecated field. Remove it at 2024. // Fallback musl-root for all targets pub musl_root: Option, @@ -360,7 +357,7 @@ pub enum RustfmtState { LazyEvaluated, } -#[derive(Debug, Default, Clone, Copy, PartialEq)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum LlvmLibunwind { #[default] No, @@ -381,7 +378,7 @@ impl FromStr for LlvmLibunwind { } } -#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum SplitDebuginfo { Packed, Unpacked, @@ -542,7 +539,7 @@ impl PartialEq<&str> for TargetSelection { } /// Per-target configuration stored in the global configuration structure. -#[derive(Default, Clone)] +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct Target { /// Some(path to llvm-config) if using an external LLVM. pub llvm_config: Option, @@ -644,7 +641,20 @@ impl Merge for TomlConfig { do_merge(&mut self.llvm, llvm, replace); do_merge(&mut self.rust, rust, replace); do_merge(&mut self.dist, dist, replace); - assert!(target.is_none(), "merging target-specific config is not currently supported"); + + match (self.target.as_mut(), target) { + (_, None) => {} + (None, Some(target)) => self.target = Some(target), + (Some(original_target), Some(new_target)) => { + for (triple, new) in new_target { + if let Some(original) = original_target.get_mut(&triple) { + original.merge(new, replace); + } else { + original_target.insert(triple, new); + } + } + } + } } } @@ -892,14 +902,13 @@ define_config! { sign_folder: Option = "sign-folder", upload_addr: Option = "upload-addr", src_tarball: Option = "src-tarball", - missing_tools: Option = "missing-tools", compression_formats: Option> = "compression-formats", compression_profile: Option = "compression-profile", include_mingw_linker: Option = "include-mingw-linker", } } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] #[serde(untagged)] pub enum StringOrBool { String(String), @@ -1239,7 +1248,7 @@ impl Config { // Infer the source directory. This is non-trivial because we want to support a downloaded bootstrap binary, // running on a completely different machine from where it was compiled. - let mut cmd = Command::new("git"); + let mut cmd = helpers::git(None); // NOTE: we cannot support running from outside the repository because the only other path we have available // is set at compile time, which can be wrong if bootstrap was downloaded rather than compiled locally. // We still support running outside the repository if we find we aren't in a git directory. @@ -1428,7 +1437,7 @@ impl Config { // To avoid writing to random places on the file system, `config.out` needs to be an absolute path. if !config.out.is_absolute() { // `canonicalize` requires the path to already exist. Use our vendored copy of `absolute` instead. - config.out = crate::utils::helpers::absolute(&config.out); + config.out = absolute(&config.out).expect("can't make empty path absolute"); } config.initial_rustc = if let Some(rustc) = rustc { @@ -1599,19 +1608,8 @@ impl Config { set(&mut config.channel, channel); config.download_rustc_commit = config.download_ci_rustc_commit(download_rustc); - // This list is incomplete, please help by expanding it! - if config.download_rustc_commit.is_some() { - // We need the channel used by the downloaded compiler to match the one we set for rustdoc; - // otherwise rustdoc-ui tests break. - if config.channel != ci_channel - && !(config.channel == "dev" && ci_channel == "nightly") - { - panic!( - "setting rust.channel={} is incompatible with download-rustc", - config.channel - ); - } - } + + // FIXME: handle download-rustc incompatible options. debug = debug_toml; debug_assertions = debug_assertions_toml; @@ -1720,7 +1718,23 @@ impl Config { config.omit_git_hash = omit_git_hash.unwrap_or(default); config.rust_info = GitInfo::new(config.omit_git_hash, &config.src); - if config.rust_info.is_from_tarball() && !is_user_configured_rust_channel { + // We need to override `rust.channel` if it's manually specified when using the CI rustc. + // This is because if the compiler uses a different channel than the one specified in config.toml, + // tests may fail due to using a different channel than the one used by the compiler during tests. + if let Some(commit) = &config.download_rustc_commit { + if is_user_configured_rust_channel { + println!( + "WARNING: `rust.download-rustc` is enabled. The `rust.channel` option will be overridden by the CI rustc's channel." + ); + + let channel = config + .read_file_by_commit(&PathBuf::from("src/ci/channel"), commit) + .trim() + .to_owned(); + + config.channel = channel; + } + } else if config.rust_info.is_from_tarball() && !is_user_configured_rust_channel { ci_channel.clone_into(&mut config.channel); } @@ -1923,7 +1937,6 @@ impl Config { sign_folder, upload_addr, src_tarball, - missing_tools, compression_formats, compression_profile, include_mingw_linker, @@ -1933,7 +1946,6 @@ impl Config { config.dist_compression_formats = compression_formats; set(&mut config.dist_compression_profile, compression_profile); set(&mut config.rust_dist_src, src_tarball); - set(&mut config.missing_tools, missing_tools); set(&mut config.dist_include_mingw_linker, include_mingw_linker) } @@ -2094,15 +2106,6 @@ impl Config { build_helper::util::try_run(cmd, self.is_verbose()) } - /// A git invocation which runs inside the source directory. - /// - /// Use this rather than `Command::new("git")` in order to support out-of-tree builds. - pub(crate) fn git(&self) -> Command { - let mut git = Command::new("git"); - git.current_dir(&self.src); - git - } - pub(crate) fn test_args(&self) -> Vec<&str> { let mut test_args = match self.cmd { Subcommand::Test { ref test_args, .. } @@ -2127,17 +2130,29 @@ impl Config { args } + /// Returns the content of the given file at a specific commit. + pub(crate) fn read_file_by_commit(&self, file: &Path, commit: &str) -> String { + assert!( + self.rust_info.is_managed_git_subrepository(), + "`Config::read_file_by_commit` is not supported in non-git sources." + ); + + let mut git = helpers::git(Some(&self.src)); + git.arg("show").arg(format!("{commit}:{}", file.to_str().unwrap())); + output(&mut git) + } + /// Bootstrap embeds a version number into the name of shared libraries it uploads in CI. /// Return the version it would have used for the given commit. pub(crate) fn artifact_version_part(&self, commit: &str) -> String { let (channel, version) = if self.rust_info.is_managed_git_subrepository() { - let mut channel = self.git(); - channel.arg("show").arg(format!("{commit}:src/ci/channel")); - let channel = output(&mut channel); - let mut version = self.git(); - version.arg("show").arg(format!("{commit}:src/version")); - let version = output(&mut version); - (channel.trim().to_owned(), version.trim().to_owned()) + let channel = self + .read_file_by_commit(&PathBuf::from("src/ci/channel"), commit) + .trim() + .to_owned(); + let version = + self.read_file_by_commit(&PathBuf::from("src/version"), commit).trim().to_owned(); + (channel, version) } else { let channel = fs::read_to_string(self.src.join("src/ci/channel")); let version = fs::read_to_string(self.src.join("src/version")); @@ -2373,8 +2388,14 @@ impl Config { } } - // check rustc/cargo version is same or lower with 1 apart from the building one + #[cfg(feature = "bootstrap-self-test")] + pub fn check_stage0_version(&self, _program_path: &Path, _component_name: &'static str) {} + + /// check rustc/cargo version is same or lower with 1 apart from the building one + #[cfg(not(feature = "bootstrap-self-test"))] pub fn check_stage0_version(&self, program_path: &Path, component_name: &'static str) { + use build_helper::util::fail; + if self.dry_run() { return; } @@ -2391,11 +2412,12 @@ impl Config { } let stage0_version = - Version::parse(stage0_output.next().unwrap().split('-').next().unwrap().trim()) - .unwrap(); - let source_version = - Version::parse(fs::read_to_string(self.src.join("src/version")).unwrap().trim()) + semver::Version::parse(stage0_output.next().unwrap().split('-').next().unwrap().trim()) .unwrap(); + let source_version = semver::Version::parse( + fs::read_to_string(self.src.join("src/version")).unwrap().trim(), + ) + .unwrap(); if !(source_version == stage0_version || (source_version.major == stage0_version.major && (source_version.minor == stage0_version.minor @@ -2421,7 +2443,8 @@ impl Config { }; // Handle running from a directory other than the top level - let top_level = output(self.git().args(["rev-parse", "--show-toplevel"])); + let top_level = + output(helpers::git(Some(&self.src)).args(["rev-parse", "--show-toplevel"])); let top_level = top_level.trim_end(); let compiler = format!("{top_level}/compiler/"); let library = format!("{top_level}/library/"); @@ -2429,7 +2452,7 @@ impl Config { // Look for a version to compare to based on the current commit. // Only commits merged by bors will have CI artifacts. let merge_base = output( - self.git() + helpers::git(Some(&self.src)) .arg("rev-list") .arg(format!("--author={}", self.stage0_metadata.config.git_merge_commit_email)) .args(["-n1", "--first-parent", "HEAD"]), @@ -2444,8 +2467,7 @@ impl Config { } // Warn if there were changes to the compiler or standard library since the ancestor commit. - let has_changes = !t!(self - .git() + let has_changes = !t!(helpers::git(Some(&self.src)) .args(["diff-index", "--quiet", commit, "--", &compiler, &library]) .status()) .success(); @@ -2518,13 +2540,14 @@ impl Config { if_unchanged: bool, ) -> Option { // Handle running from a directory other than the top level - let top_level = output(self.git().args(["rev-parse", "--show-toplevel"])); + let top_level = + output(helpers::git(Some(&self.src)).args(["rev-parse", "--show-toplevel"])); let top_level = top_level.trim_end(); // Look for a version to compare to based on the current commit. // Only commits merged by bors will have CI artifacts. let merge_base = output( - self.git() + helpers::git(Some(&self.src)) .arg("rev-list") .arg(format!("--author={}", self.stage0_metadata.config.git_merge_commit_email)) .args(["-n1", "--first-parent", "HEAD"]), @@ -2539,7 +2562,7 @@ impl Config { } // Warn if there were changes to the compiler or standard library since the ancestor commit. - let mut git = self.git(); + let mut git = helpers::git(Some(&self.src)); git.args(["diff-index", "--quiet", commit, "--"]); for path in modified_paths { diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index f4ed7e76fba1..83def0c6df0c 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -284,8 +284,8 @@ pub enum Subcommand { name = "fmt", long_about = "\n Arguments: - This subcommand optionally accepts a `--check` flag which succeeds if formatting is correct and - fails if it is not. For example: + This subcommand optionally accepts a `--check` flag which succeeds if + formatting is correct and fails if it is not. For example: ./x.py fmt ./x.py fmt --check" )] @@ -294,6 +294,10 @@ pub enum Subcommand { /// check formatting instead of applying #[arg(long)] check: bool, + + /// apply to all appropriate files, not just those that have been modified + #[arg(long)] + all: bool, }, #[command(aliases = ["d"], long_about = "\n Arguments: diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 59e16b654272..bfb2c02860d2 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -1,5 +1,7 @@ use super::{flags::Flags, ChangeIdWrapper, Config}; use crate::core::build_steps::clippy::get_clippy_rules_in_order; +use crate::core::config::Target; +use crate::core::config::TargetSelection; use crate::core::config::{LldMode, TomlConfig}; use clap::CommandFactory; @@ -12,16 +14,9 @@ use std::{ }; fn parse(config: &str) -> Config { - Config::parse_inner( - &[ - "check".to_string(), - "--set=build.rustc=/does/not/exist".to_string(), - "--set=build.cargo=/does/not/exist".to_string(), - "--config=/does/not/exist".to_string(), - "--skip-stage0-validation".to_string(), - ], - |&_| toml::from_str(&config).unwrap(), - ) + Config::parse_inner(&["check".to_string(), "--config=/does/not/exist".to_string()], |&_| { + toml::from_str(&config).unwrap() + }) } #[test] @@ -124,6 +119,10 @@ fn override_toml() { "--set=build.gdb=\"bar\"".to_owned(), "--set=build.tools=[\"cargo\"]".to_owned(), "--set=llvm.build-config={\"foo\" = \"bar\"}".to_owned(), + "--set=target.x86_64-unknown-linux-gnu.runner=bar".to_owned(), + "--set=target.x86_64-unknown-linux-gnu.rpath=false".to_owned(), + "--set=target.aarch64-unknown-linux-gnu.sanitizers=false".to_owned(), + "--set=target.aarch64-apple-darwin.runner=apple".to_owned(), ], |&_| { toml::from_str( @@ -140,6 +139,17 @@ tools = [] [llvm] download-ci-llvm = false build-config = {} + +[target.aarch64-unknown-linux-gnu] +sanitizers = true +rpath = true +runner = "aarch64-runner" + +[target.x86_64-unknown-linux-gnu] +sanitizers = true +rpath = true +runner = "x86_64-runner" + "#, ) .unwrap() @@ -163,6 +173,30 @@ build-config = {} [("foo".to_string(), "bar".to_string())].into_iter().collect(), "setting dictionary value" ); + + let x86_64 = TargetSelection::from_user("x86_64-unknown-linux-gnu"); + let x86_64_values = Target { + sanitizers: Some(true), + rpath: Some(false), + runner: Some("bar".into()), + ..Default::default() + }; + let aarch64 = TargetSelection::from_user("aarch64-unknown-linux-gnu"); + let aarch64_values = Target { + sanitizers: Some(false), + rpath: Some(true), + runner: Some("aarch64-runner".into()), + ..Default::default() + }; + let darwin = TargetSelection::from_user("aarch64-apple-darwin"); + let darwin_values = Target { runner: Some("apple".into()), ..Default::default() }; + assert_eq!( + config.target_config, + [(x86_64, x86_64_values), (aarch64, aarch64_values), (darwin, darwin_values)] + .into_iter() + .collect(), + "setting dictionary value" + ); } #[test] @@ -171,10 +205,7 @@ fn override_toml_duplicate() { Config::parse_inner( &[ "check".to_owned(), - "--set=build.rustc=/does/not/exist".to_string(), - "--set=build.cargo=/does/not/exist".to_string(), - "--config=/does/not/exist".to_owned(), - "--skip-stage0-validation".to_owned(), + "--config=/does/not/exist".to_string(), "--set=change-id=1".to_owned(), "--set=change-id=2".to_owned(), ], @@ -197,15 +228,7 @@ fn profile_user_dist() { .and_then(|table: toml::Value| TomlConfig::deserialize(table)) .unwrap() } - Config::parse_inner( - &[ - "check".to_owned(), - "--set=build.rustc=/does/not/exist".to_string(), - "--set=build.cargo=/does/not/exist".to_string(), - "--skip-stage0-validation".to_string(), - ], - get_toml, - ); + Config::parse_inner(&["check".to_owned()], get_toml); } #[test] diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 60f48c5923e1..2b11b8c3d4f2 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -9,11 +9,10 @@ use std::{ }; use build_helper::ci::CiEnv; -use build_helper::stage0_parser::VersionMetadata; use xz2::bufread::XzDecoder; +use crate::utils::helpers::hex_encode; use crate::utils::helpers::{check_run, exe, move_file, program_out_of_date}; -use crate::{core::build_steps::llvm::detect_llvm_sha, utils::helpers::hex_encode}; use crate::{t, Config}; static SHOULD_FIX_BINS_AND_DYLIBS: OnceLock = OnceLock::new(); @@ -405,9 +404,17 @@ impl Config { cargo_clippy } + #[cfg(feature = "bootstrap-self-test")] + pub(crate) fn maybe_download_rustfmt(&self) -> Option { + None + } + /// NOTE: rustfmt is a completely different toolchain than the bootstrap compiler, so it can't /// reuse target directories or artifacts + #[cfg(not(feature = "bootstrap-self-test"))] pub(crate) fn maybe_download_rustfmt(&self) -> Option { + use build_helper::stage0_parser::VersionMetadata; + let VersionMetadata { date, version } = self.stage0_metadata.rustfmt.as_ref()?; let channel = format!("{version}-{date}"); @@ -487,6 +494,10 @@ impl Config { ); } + #[cfg(feature = "bootstrap-self-test")] + pub(crate) fn download_beta_toolchain(&self) {} + + #[cfg(not(feature = "bootstrap-self-test"))] pub(crate) fn download_beta_toolchain(&self) { self.verbose(|| println!("downloading stage0 beta artifacts")); @@ -665,7 +676,13 @@ download-rustc = false self.unpack(&tarball, &bin_root, prefix); } + #[cfg(feature = "bootstrap-self-test")] + pub(crate) fn maybe_download_ci_llvm(&self) {} + + #[cfg(not(feature = "bootstrap-self-test"))] pub(crate) fn maybe_download_ci_llvm(&self) { + use crate::core::build_steps::llvm::detect_llvm_sha; + if !self.llvm_from_ci { return; } @@ -707,6 +724,7 @@ download-rustc = false } } + #[cfg(not(feature = "bootstrap-self-test"))] fn download_ci_llvm(&self, llvm_sha: &str) { let llvm_assertions = self.llvm_assertions; diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 493ad99cc705..5a0be2948a19 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -14,7 +14,13 @@ use std::ffi::{OsStr, OsString}; use std::fs; use std::path::PathBuf; use std::process::Command; -use walkdir::WalkDir; + +#[cfg(not(feature = "bootstrap-self-test"))] +use crate::builder::Builder; +#[cfg(not(feature = "bootstrap-self-test"))] +use crate::core::build_steps::tool; +#[cfg(not(feature = "bootstrap-self-test"))] +use std::collections::HashSet; use crate::builder::Kind; use crate::core::config::Target; @@ -31,12 +37,16 @@ pub struct Finder { // it might not yet be included in stage0. In such cases, we handle the targets missing from stage0 in this list. // // Targets can be removed from this list once they are present in the stage0 compiler (usually by updating the beta compiler of the bootstrap). +#[cfg(not(feature = "bootstrap-self-test"))] const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined - "aarch64-apple-visionos", - "aarch64-apple-visionos-sim", ]; +/// Minimum version threshold for libstdc++ required when using prebuilt LLVM +/// from CI (with`llvm.download-ci-llvm` option). +#[cfg(not(feature = "bootstrap-self-test"))] +const LIBSTDCXX_MIN_VERSION_THRESHOLD: usize = 8; + impl Finder { pub fn new() -> Self { Self { cache: HashMap::new(), path: env::var_os("PATH").unwrap_or_default() } @@ -101,20 +111,49 @@ pub fn check(build: &mut Build) { cmd_finder.must_have("git"); } + // Ensure that a compatible version of libstdc++ is available on the system when using `llvm.download-ci-llvm`. + #[cfg(not(feature = "bootstrap-self-test"))] + if !build.config.dry_run() && !build.build.is_msvc() && build.config.llvm_from_ci { + let builder = Builder::new(build); + let libcxx_version = builder.ensure(tool::LibcxxVersionTool { target: build.build }); + + match libcxx_version { + tool::LibcxxVersion::Gnu(version) => { + if LIBSTDCXX_MIN_VERSION_THRESHOLD > version { + eprintln!( + "\nYour system's libstdc++ version is too old for the `llvm.download-ci-llvm` option." + ); + eprintln!("Current version detected: '{}'", version); + eprintln!("Minimum required version: '{}'", LIBSTDCXX_MIN_VERSION_THRESHOLD); + eprintln!( + "Consider upgrading libstdc++ or disabling the `llvm.download-ci-llvm` option." + ); + eprintln!( + "If you choose to upgrade libstdc++, run `x clean` or delete `build/host/libcxx-version` manually after the upgrade." + ); + } + } + tool::LibcxxVersion::Llvm(_) => { + // FIXME: Handle libc++ version check. + } + } + } + // We need cmake, but only if we're actually building LLVM or sanitizers. - let building_llvm = build - .hosts - .iter() - .map(|host| { - build.config.llvm_enabled(*host) - && build - .config - .target_config - .get(host) - .map(|config| config.llvm_config.is_none()) - .unwrap_or(true) - }) - .any(|build_llvm_ourselves| build_llvm_ourselves); + let building_llvm = !build.config.llvm_from_ci + && build + .hosts + .iter() + .map(|host| { + build.config.llvm_enabled(*host) + && build + .config + .target_config + .get(host) + .map(|config| config.llvm_config.is_none()) + .unwrap_or(true) + }) + .any(|build_llvm_ourselves| build_llvm_ourselves); let need_cmake = building_llvm || build.config.any_sanitizers_to_build(); if need_cmake && cmd_finder.maybe_have("cmake").is_none() { @@ -169,6 +208,13 @@ than building it. .map(|p| cmd_finder.must_have(p)) .or_else(|| cmd_finder.maybe_have("reuse")); + #[cfg(not(feature = "bootstrap-self-test"))] + let stage0_supported_target_list: HashSet = + output(Command::new(&build.config.initial_rustc).args(["--print", "target-list"])) + .lines() + .map(|s| s.to_string()) + .collect(); + // We're gonna build some custom C code here and there, host triples // also build some C++ shims for LLVM so we need a C++ compiler. for target in &build.targets { @@ -189,17 +235,29 @@ than building it. continue; } - let target_str = target.to_string(); - // Ignore fake targets that are only used for unit tests in bootstrap. - if !["A-A", "B-B", "C-C"].contains(&target_str.as_str()) { + #[cfg(not(feature = "bootstrap-self-test"))] + { let mut has_target = false; + let target_str = target.to_string(); - let supported_target_list = - output(Command::new(&build.config.initial_rustc).args(["--print", "target-list"])); + let missing_targets_hashset: HashSet<_> = + STAGE0_MISSING_TARGETS.iter().map(|t| t.to_string()).collect(); + let duplicated_targets: Vec<_> = + stage0_supported_target_list.intersection(&missing_targets_hashset).collect(); + + if !duplicated_targets.is_empty() { + println!( + "Following targets supported from the stage0 compiler, please remove them from STAGE0_MISSING_TARGETS list." + ); + for duplicated_target in duplicated_targets { + println!(" {duplicated_target}"); + } + std::process::exit(1); + } // Check if it's a built-in target. - has_target |= supported_target_list.contains(&target_str); + has_target |= stage0_supported_target_list.contains(&target_str); has_target |= STAGE0_MISSING_TARGETS.contains(&target_str.as_str()); if !has_target { @@ -210,7 +268,7 @@ than building it. target_filename.push(".json"); // Recursively traverse through nested directories. - let walker = WalkDir::new(custom_target_path).into_iter(); + let walker = walkdir::WalkDir::new(custom_target_path).into_iter(); for entry in walker.filter_map(|e| e.ok()) { has_target |= entry.file_name() == target_filename; } diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 698a576effa6..449d8c128ec6 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -84,13 +84,14 @@ const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ (Some(Mode::ToolRustc), "rust_analyzer", None), (Some(Mode::ToolStd), "rust_analyzer", None), (Some(Mode::Codegen), "parallel_compiler", None), + // NOTE: consider updating `check-cfg` entries in `std/Cargo.toml` too. + // cfg(bootstrap) remove these once the bootstrap compiler supports + // `lints.rust.unexpected_cfgs.check-cfg` (Some(Mode::Std), "stdarch_intel_sde", None), (Some(Mode::Std), "no_fp_fmt_parse", None), (Some(Mode::Std), "no_global_oom_handling", None), (Some(Mode::Std), "no_rc", None), (Some(Mode::Std), "no_sync", None), - (Some(Mode::Std), "netbsd10", None), - (Some(Mode::Std), "backtrace_in_libstd", None), /* Extra values not defined in the built-in targets yet, but used in std */ (Some(Mode::Std), "target_env", Some(&["libnx", "p2"])), (Some(Mode::Std), "target_os", Some(&["visionos"])), @@ -468,7 +469,8 @@ impl Build { // Make sure we update these before gathering metadata so we don't get an error about missing // Cargo.toml files. - let rust_submodules = ["src/tools/cargo", "library/backtrace", "library/stdarch"]; + let rust_submodules = + ["src/tools/cargo", "src/doc/book", "library/backtrace", "library/stdarch"]; for s in rust_submodules { build.update_submodule(Path::new(s)); } @@ -518,33 +520,27 @@ impl Build { return; } - // check_submodule - let checked_out_hash = - output(Command::new("git").args(["rev-parse", "HEAD"]).current_dir(&absolute_path)); - // update_submodules - let recorded = output( - Command::new("git") - .args(["ls-tree", "HEAD"]) - .arg(relative_path) - .current_dir(&self.config.src), - ); + let submodule_git = || helpers::git(Some(&absolute_path)); + + // Determine commit checked out in submodule. + let checked_out_hash = output(submodule_git().args(["rev-parse", "HEAD"])); + let checked_out_hash = checked_out_hash.trim_end(); + // Determine commit that the submodule *should* have. + let recorded = + output(helpers::git(Some(&self.src)).args(["ls-tree", "HEAD"]).arg(relative_path)); let actual_hash = recorded .split_whitespace() .nth(2) .unwrap_or_else(|| panic!("unexpected output `{}`", recorded)); - // update_submodule - if actual_hash == checked_out_hash.trim_end() { + if actual_hash == checked_out_hash { // already checked out return; } println!("Updating submodule {}", relative_path.display()); self.run( - Command::new("git") - .args(["submodule", "-q", "sync"]) - .arg(relative_path) - .current_dir(&self.config.src), + helpers::git(Some(&self.src)).args(["submodule", "-q", "sync"]).arg(relative_path), ); // Try passing `--progress` to start, then run git again without if that fails. @@ -552,9 +548,7 @@ impl Build { // Git is buggy and will try to fetch submodules from the tracking branch for *this* repository, // even though that has no relation to the upstream for the submodule. let current_branch = { - let output = self - .config - .git() + let output = helpers::git(Some(&self.src)) .args(["symbolic-ref", "--short", "HEAD"]) .stderr(Stdio::inherit()) .output(); @@ -566,7 +560,7 @@ impl Build { } }; - let mut git = self.config.git(); + let mut git = helpers::git(Some(&self.src)); if let Some(branch) = current_branch { // If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name. // This syntax isn't accepted by `branch.{branch}`. Strip it. @@ -588,26 +582,22 @@ impl Build { // Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error). // diff-index reports the modifications through the exit status let has_local_modifications = !self.run_cmd( - BootstrapCommand::from( - Command::new("git") - .args(["diff-index", "--quiet", "HEAD"]) - .current_dir(&absolute_path), - ) - .allow_failure() - .output_mode(match self.is_verbose() { - true => OutputMode::PrintAll, - false => OutputMode::PrintOutput, - }), + BootstrapCommand::from(submodule_git().args(["diff-index", "--quiet", "HEAD"])) + .allow_failure() + .output_mode(match self.is_verbose() { + true => OutputMode::PrintAll, + false => OutputMode::PrintOutput, + }), ); if has_local_modifications { - self.run(Command::new("git").args(["stash", "push"]).current_dir(&absolute_path)); + self.run(submodule_git().args(["stash", "push"])); } - self.run(Command::new("git").args(["reset", "-q", "--hard"]).current_dir(&absolute_path)); - self.run(Command::new("git").args(["clean", "-qdfx"]).current_dir(&absolute_path)); + self.run(submodule_git().args(["reset", "-q", "--hard"])); + self.run(submodule_git().args(["clean", "-qdfx"])); if has_local_modifications { - self.run(Command::new("git").args(["stash", "pop"]).current_dir(absolute_path)); + self.run(submodule_git().args(["stash", "pop"])); } } @@ -619,10 +609,9 @@ impl Build { return; } let output = output( - self.config - .git() + helpers::git(Some(&self.src)) .args(["config", "--file"]) - .arg(&self.config.src.join(".gitmodules")) + .arg(self.config.src.join(".gitmodules")) .args(["--get-regexp", "path"]), ); for line in output.lines() { @@ -659,10 +648,11 @@ impl Build { // hardcoded subcommands match &self.config.cmd { - Subcommand::Format { check } => { + Subcommand::Format { check, all } => { return core::build_steps::format::format( &builder::Builder::new(self), *check, + *all, &self.config.paths, ); } @@ -1560,10 +1550,14 @@ impl Build { // Figure out how many merge commits happened since we branched off master. // That's our beta number! // (Note that we use a `..` range, not the `...` symmetric difference.) - output(self.config.git().arg("rev-list").arg("--count").arg("--merges").arg(format!( - "refs/remotes/origin/{}..HEAD", - self.config.stage0_metadata.config.nightly_branch - ))) + output( + helpers::git(Some(&self.src)).arg("rev-list").arg("--count").arg("--merges").arg( + format!( + "refs/remotes/origin/{}..HEAD", + self.config.stage0_metadata.config.nightly_branch + ), + ), + ) }); let n = count.trim().parse().unwrap(); self.prerelease_version.set(Some(n)); @@ -1981,15 +1975,13 @@ fn envify(s: &str) -> String { /// In case of errors during `git` command execution (e.g., in tarball sources), default values /// are used to prevent panics. pub fn generate_smart_stamp_hash(dir: &Path, additional_input: &str) -> String { - let diff = Command::new("git") - .current_dir(dir) + let diff = helpers::git(Some(dir)) .arg("diff") .output() .map(|o| String::from_utf8(o.stdout).unwrap_or_default()) .unwrap_or_default(); - let status = Command::new("git") - .current_dir(dir) + let status = helpers::git(Some(dir)) .arg("status") .arg("--porcelain") .arg("-z") diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 2f9eaf51c341..bfe3622e40d3 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -190,4 +190,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "`rust.lld` has a new default value of `true` on `x86_64-unknown-linux-gnu`. Starting at stage1, `rust-lld` will thus be this target's default linker. No config changes should be necessary.", }, + ChangeInfo { + change_id: 125535, + severity: ChangeSeverity::Warning, + summary: "Removed `dist.missing-tools` configuration as it was deprecated long time ago.", + }, ]; diff --git a/src/bootstrap/src/utils/channel.rs b/src/bootstrap/src/utils/channel.rs index 88988c339161..ce82c52f049e 100644 --- a/src/bootstrap/src/utils/channel.rs +++ b/src/bootstrap/src/utils/channel.rs @@ -7,11 +7,12 @@ use std::fs; use std::path::Path; -use std::process::Command; use crate::utils::helpers::{output, t}; use crate::Build; +use super::helpers; + #[derive(Clone, Default)] pub enum GitInfo { /// This is not a git repository. @@ -44,7 +45,7 @@ impl GitInfo { } // Make sure git commands work - match Command::new("git").arg("rev-parse").current_dir(dir).output() { + match helpers::git(Some(dir)).arg("rev-parse").output() { Ok(ref out) if out.status.success() => {} _ => return GitInfo::Absent, } @@ -57,17 +58,15 @@ impl GitInfo { // Ok, let's scrape some info let ver_date = output( - Command::new("git") - .current_dir(dir) + helpers::git(Some(dir)) .arg("log") .arg("-1") .arg("--date=short") .arg("--pretty=format:%cd"), ); - let ver_hash = output(Command::new("git").current_dir(dir).arg("rev-parse").arg("HEAD")); - let short_ver_hash = output( - Command::new("git").current_dir(dir).arg("rev-parse").arg("--short=9").arg("HEAD"), - ); + let ver_hash = output(helpers::git(Some(dir)).arg("rev-parse").arg("HEAD")); + let short_ver_hash = + output(helpers::git(Some(dir)).arg("rev-parse").arg("--short=9").arg("HEAD")); GitInfo::Present(Some(Info { commit_date: ver_date.trim().to_string(), sha: ver_hash.trim().to_string(), diff --git a/src/bootstrap/src/utils/dylib.rs b/src/bootstrap/src/utils/dylib.rs index b6e7aec1756b..90bcff59a647 100644 --- a/src/bootstrap/src/utils/dylib.rs +++ b/src/bootstrap/src/utils/dylib.rs @@ -5,7 +5,7 @@ pub fn dylib_path_var() -> &'static str { if cfg!(target_os = "windows") { "PATH" - } else if cfg!(target_os = "macos") { + } else if cfg!(target_vendor = "apple") { "DYLD_LIBRARY_PATH" } else if cfg!(target_os = "haiku") { "LIBRARY_PATH" diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 278359cb08e3..4b6dc45b436d 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -331,115 +331,6 @@ fn dir_up_to_date(src: &Path, threshold: SystemTime) -> bool { }) } -/// Copied from `std::path::absolute` until it stabilizes. -/// -/// FIXME: this shouldn't exist. -pub(crate) fn absolute(path: &Path) -> PathBuf { - if path.as_os_str().is_empty() { - panic!("can't make empty path absolute"); - } - #[cfg(unix)] - { - t!(absolute_unix(path), format!("could not make path absolute: {}", path.display())) - } - #[cfg(windows)] - { - t!(absolute_windows(path), format!("could not make path absolute: {}", path.display())) - } - #[cfg(not(any(unix, windows)))] - { - println!("WARNING: bootstrap is not supported on non-unix platforms"); - t!(std::fs::canonicalize(t!(std::env::current_dir()))).join(path) - } -} - -#[cfg(unix)] -/// Make a POSIX path absolute without changing its semantics. -fn absolute_unix(path: &Path) -> io::Result { - // This is mostly a wrapper around collecting `Path::components`, with - // exceptions made where this conflicts with the POSIX specification. - // See 4.13 Pathname Resolution, IEEE Std 1003.1-2017 - // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 - - use std::os::unix::prelude::OsStrExt; - let mut components = path.components(); - let path_os = path.as_os_str().as_bytes(); - - let mut normalized = if path.is_absolute() { - // "If a pathname begins with two successive characters, the - // first component following the leading characters may be - // interpreted in an implementation-defined manner, although more than - // two leading characters shall be treated as a single - // character." - if path_os.starts_with(b"//") && !path_os.starts_with(b"///") { - components.next(); - PathBuf::from("//") - } else { - PathBuf::new() - } - } else { - env::current_dir()? - }; - normalized.extend(components); - - // "Interfaces using pathname resolution may specify additional constraints - // when a pathname that does not name an existing directory contains at - // least one non- character and contains one or more trailing - // characters". - // A trailing is also meaningful if "a symbolic link is - // encountered during pathname resolution". - - if path_os.ends_with(b"/") { - normalized.push(""); - } - - Ok(normalized) -} - -#[cfg(windows)] -fn absolute_windows(path: &std::path::Path) -> std::io::Result { - use std::ffi::OsString; - use std::io::Error; - use std::os::windows::ffi::{OsStrExt, OsStringExt}; - use std::ptr::null_mut; - #[link(name = "kernel32")] - extern "system" { - fn GetFullPathNameW( - lpFileName: *const u16, - nBufferLength: u32, - lpBuffer: *mut u16, - lpFilePart: *mut *const u16, - ) -> u32; - } - - unsafe { - // encode the path as UTF-16 - let path: Vec = path.as_os_str().encode_wide().chain([0]).collect(); - let mut buffer = Vec::new(); - // Loop until either success or failure. - loop { - // Try to get the absolute path - let len = GetFullPathNameW( - path.as_ptr(), - buffer.len().try_into().unwrap(), - buffer.as_mut_ptr(), - null_mut(), - ); - match len as usize { - // Failure - 0 => return Err(Error::last_os_error()), - // Buffer is too small, resize. - len if len > buffer.len() => buffer.resize(len, 0), - // Success! - len => { - buffer.truncate(len); - return Ok(OsString::from_wide(&buffer).into()); - } - } - } - } -} - /// Adapted from /// /// When `clang-cl` is used with instrumentation, we need to add clang's runtime library resource @@ -598,3 +489,29 @@ pub fn check_cfg_arg(name: &str, values: Option<&[&str]>) -> String { }; format!("--check-cfg=cfg({name}{next})") } + +/// Prepares `Command` that runs git inside the source directory if given. +/// +/// Whenever a git invocation is needed, this function should be preferred over +/// manually building a git `Command`. This approach allows us to manage bootstrap-specific +/// needs/hacks from a single source, rather than applying them on next to every `Command::new("git")`, +/// which is painful to ensure that the required change is applied on each one of them correctly. +pub fn git(source_dir: Option<&Path>) -> Command { + let mut git = Command::new("git"); + + if let Some(source_dir) = source_dir { + git.current_dir(source_dir); + // If we are running inside git (e.g. via a hook), `GIT_DIR` is set and takes precedence + // over the current dir. Un-set it to make the current dir matter. + git.env_remove("GIT_DIR"); + // Also un-set some other variables, to be on the safe side (based on cargo's + // `fetch_with_cli`). In particular un-setting `GIT_INDEX_FILE` is required to fix some odd + // misbehavior. + git.env_remove("GIT_WORK_TREE") + .env_remove("GIT_INDEX_FILE") + .env_remove("GIT_OBJECT_DIRECTORY") + .env_remove("GIT_ALTERNATE_OBJECT_DIRECTORIES"); + } + + git +} diff --git a/src/bootstrap/src/utils/helpers/tests.rs b/src/bootstrap/src/utils/helpers/tests.rs index 9cfaa3eb67b5..2ab3952ae5a1 100644 --- a/src/bootstrap/src/utils/helpers/tests.rs +++ b/src/bootstrap/src/utils/helpers/tests.rs @@ -25,27 +25,6 @@ fn test_make() { } } -#[cfg(unix)] -#[test] -fn test_absolute_unix() { - use crate::utils::helpers::absolute_unix; - - // Test an absolute path - let path = PathBuf::from("/home/user/file.txt"); - assert_eq!(absolute_unix(&path).unwrap(), PathBuf::from("/home/user/file.txt")); - - // Test an absolute path with double leading slashes - let path = PathBuf::from("//root//file.txt"); - assert_eq!(absolute_unix(&path).unwrap(), PathBuf::from("//root/file.txt")); - - // Test a relative path - let path = PathBuf::from("relative/path"); - assert_eq!( - absolute_unix(&path).unwrap(), - std::env::current_dir().unwrap().join("relative/path") - ); -} - #[test] fn test_beta_rev_parsing() { // single digit revision diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs index 57cdf7473a19..fd934f18de23 100644 --- a/src/bootstrap/src/utils/tarball.rs +++ b/src/bootstrap/src/utils/tarball.rs @@ -23,7 +23,6 @@ pub(crate) enum OverlayKind { Clippy, Miri, Rustfmt, - RustDemangler, Rls, RustAnalyzer, RustcCodegenCranelift, @@ -58,9 +57,6 @@ impl OverlayKind { "src/tools/rustfmt/LICENSE-APACHE", "src/tools/rustfmt/LICENSE-MIT", ], - OverlayKind::RustDemangler => { - &["src/tools/rust-demangler/README.md", "LICENSE-APACHE", "LICENSE-MIT"] - } OverlayKind::Rls => &["src/tools/rls/README.md", "LICENSE-APACHE", "LICENSE-MIT"], OverlayKind::RustAnalyzer => &[ "src/tools/rust-analyzer/README.md", @@ -85,7 +81,6 @@ impl OverlayKind { match self { OverlayKind::Rust => builder.rust_version(), OverlayKind::Llvm => builder.rust_version(), - OverlayKind::RustDemangler => builder.release_num("rust-demangler"), OverlayKind::Cargo => { builder.cargo_info.version(builder, &builder.release_num("cargo")) } diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index 9af368ef4450..824c904e17f7 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -233,7 +233,7 @@ For targets: `aarch64-unknown-linux-gnu` - Operating System > Linux kernel version = 4.1.49 - Binary utilities > Version of binutils = 2.29.1 - C-library > glibc version = 2.17 -- aarch64 support was introduced in this version -- C compiler > gcc version = 8.5.0 +- C compiler > gcc version = 13.2.0 - C compiler > C++ = ENABLE -- to cross compile LLVM ### `i586-linux-gnu.defconfig` diff --git a/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile b/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile similarity index 54% rename from src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile rename to src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile index 07260be35877..a9ffa5918b5b 100644 --- a/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/Dockerfile @@ -1,39 +1,36 @@ # based on armhf-gnu/Dockerfile -FROM ubuntu:20.04 +FROM ubuntu:22.04 -RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections +ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update -y && apt-get install -y --no-install-recommends \ - bc \ - bison \ - ca-certificates \ - cmake \ - cpio \ - curl \ - debian-ports-archive-keyring \ - debootstrap \ - flex \ - gcc \ - gcc-riscv64-linux-gnu \ - git \ - g++-riscv64-linux-gnu \ - g++ \ - libc6-dev \ - libc6-dev-riscv64-cross \ - libssl-dev \ - make \ - ninja-build \ - patch \ - python3 \ - qemu-system-misc \ - xz-utils + bc \ + bzip2 \ + ca-certificates \ + cmake \ + cpio \ + curl \ + file \ + flex \ + bison \ + g++ \ + g++-riscv64-linux-gnu \ + git \ + libc6-dev \ + libc6-dev-riscv64-cross \ + libssl-dev \ + make \ + ninja-build \ + python3 \ + qemu-system-riscv64 \ + xz-utils -ENV ARCH=riscv -ENV CROSS_COMPILE=riscv64-linux-gnu- +ENV ARCH=riscv \ + CROSS_COMPILE=riscv64-linux-gnu- WORKDIR /build # From https://github.com/michaeljclark/busybear-linux/blob/master/conf/linux.config -COPY host-x86_64/riscv64gc-linux/linux.config /build +COPY host-x86_64/riscv64gc-gnu/linux.config /build # Compile the kernel that we're going to be emulating with. This is # basically just done to be compatible with the QEMU target that we're going @@ -49,29 +46,22 @@ RUN curl https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.6.16.tar.xz | tar # Compile an instance of busybox as this provides a lightweight system and init # binary which we will boot into. Only trick here is configuring busybox to # build static binaries. -RUN curl https://busybox.net/downloads/busybox-1.31.1.tar.bz2 | tar xjf - -COPY host-x86_64/riscv64gc-linux/0001-Remove-stime-function-calls.patch /build/busybox-1.31.1/ -RUN cd /build/busybox-1.31.1 && \ - patch -p1 -i 0001-Remove-stime-function-calls.patch && \ - make defconfig && \ - sed -i 's/.*CONFIG_STATIC.*/CONFIG_STATIC=y/' .config && \ - make -j$(nproc) && \ - make install && \ - mv _install /tmp/rootfs && \ - cd /build && \ - rm -rf busybox-1.31.1 +RUN curl https://www.busybox.net/downloads/busybox-1.32.1.tar.bz2 | tar xjf - && \ + cd busybox-1.32.1 && \ + make defconfig && \ + sed -i 's/.*CONFIG_STATIC.*/CONFIG_STATIC=y/' .config && \ + make -j$(nproc) && \ + make install && \ + mv _install /tmp/rootfs && \ + cd /build && \ + rm -rf busybox-1.32.1 -# Download the ubuntu rootfs, which we'll use as a chroot for all our tests -# This is only needed to provide /lib/* and /usr/lib/* +# Download the ubuntu rootfs, which we'll use as a chroot for all our tests. WORKDIR /tmp -RUN debootstrap --variant=minbase --arch=riscv64 --foreign focal /tmp/rootfs/ubuntu -RUN cd rootfs && mkdir proc sys dev etc etc/init.d -# rootfs/ubuntu/proc is in a weird state (access fails with ELOOP) until -# rootfs/ubuntu/debootstrap/debootstrap --second-stage is run (under emulation), -# but this takes ages. Instead hack it into a good enough state. -# /proc is used by std::env::current_exe() (which is roughly -# `readlink /proc/self/exe`) -RUN cd rootfs/ubuntu && rm -rf proc && mkdir proc +RUN mkdir rootfs/ubuntu +RUN curl https://cdimage.ubuntu.com/ubuntu-base/releases/22.04/release/ubuntu-base-22.04.2-base-riscv64.tar.gz | \ + tar xzf - -C rootfs/ubuntu && \ + cd rootfs && mkdir proc sys dev etc etc/init.d # Copy over our init script, which starts up our test server and also a few other # misc tasks @@ -95,12 +85,12 @@ RUN mkdir build && cd build && \ WORKDIR /tmp RUN rm -rf /tmp/riscv-pk -COPY scripts/cmake.sh /scripts/ -RUN /scripts/cmake.sh - COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh +# Avoid "fatal: detected dubious ownership in repository at '/checkout'" error +RUN git config --global --add safe.directory /checkout + ENV RUST_CONFIGURE_ARGS --qemu-riscv64-rootfs=/tmp/rootfs ENV SCRIPT python3 ../x.py --stage 2 test --host='' --target riscv64gc-unknown-linux-gnu diff --git a/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/linux.config b/src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/linux.config similarity index 100% rename from src/ci/docker/host-x86_64/disabled/riscv64gc-linux/linux.config rename to src/ci/docker/host-x86_64/disabled/riscv64gc-gnu/linux.config diff --git a/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/0001-Remove-stime-function-calls.patch b/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/0001-Remove-stime-function-calls.patch deleted file mode 100644 index 4437a870b207..000000000000 --- a/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/0001-Remove-stime-function-calls.patch +++ /dev/null @@ -1,96 +0,0 @@ -From c820da85c65c7f3aa9e9cb3ed71ada69bf9b783e Mon Sep 17 00:00:00 2001 -From: Alistair Francis -Date: Tue, 19 Nov 2019 13:06:40 +0100 -Subject: [PATCH] Remove stime() function calls - -stime() has been deprecated in glibc 2.31 and replaced with -clock_settime(). Let's replace the stime() function calls with -clock_settime() in preparation. - -function old new delta -rdate_main 197 224 +27 -clock_settime - 27 +27 -date_main 926 941 +15 -stime 37 - -37 ------------------------------------------------------------------------------- -(add/remove: 2/2 grow/shrink: 2/0 up/down: 69/-37) Total: 32 bytes - -Signed-off-by: Alistair Francis -Signed-off-by: Denys Vlasenko - -[Tom Eccles: adjust patch context to apply on top of 1.31.1-stable] -Signed-off-by: Tom Eccles ---- - coreutils/date.c | 6 +++++- - libbb/missing_syscalls.c | 8 -------- - util-linux/rdate.c | 8 ++++++-- - 3 files changed, 11 insertions(+), 11 deletions(-) - -diff --git a/coreutils/date.c b/coreutils/date.c -index 3414d38ae..4ade6abb4 100644 ---- a/coreutils/date.c -+++ b/coreutils/date.c -@@ -279,6 +279,9 @@ int date_main(int argc UNUSED_PARAM, char **argv) - time(&ts.tv_sec); - #endif - } -+#if !ENABLE_FEATURE_DATE_NANO -+ ts.tv_nsec = 0; -+#endif - localtime_r(&ts.tv_sec, &tm_time); - - /* If date string is given, update tm_time, and maybe set date */ -@@ -301,9 +304,10 @@ int date_main(int argc UNUSED_PARAM, char **argv) - if (date_str[0] != '@') - tm_time.tm_isdst = -1; - ts.tv_sec = validate_tm_time(date_str, &tm_time); -+ ts.tv_nsec = 0; - - /* if setting time, set it */ -- if ((opt & OPT_SET) && stime(&ts.tv_sec) < 0) { -+ if ((opt & OPT_SET) && clock_settime(CLOCK_REALTIME, &ts) < 0) { - bb_perror_msg("can't set date"); - } - } -diff --git a/libbb/missing_syscalls.c b/libbb/missing_syscalls.c -index 87cf59b3d..dc40d9155 100644 ---- a/libbb/missing_syscalls.c -+++ b/libbb/missing_syscalls.c -@@ -15,14 +15,6 @@ pid_t getsid(pid_t pid) - return syscall(__NR_getsid, pid); - } - --int stime(const time_t *t) --{ -- struct timeval tv; -- tv.tv_sec = *t; -- tv.tv_usec = 0; -- return settimeofday(&tv, NULL); --} -- - int sethostname(const char *name, size_t len) - { - return syscall(__NR_sethostname, name, len); -diff --git a/util-linux/rdate.c b/util-linux/rdate.c -index 70f829e7f..878375d78 100644 ---- a/util-linux/rdate.c -+++ b/util-linux/rdate.c -@@ -95,9 +95,13 @@ int rdate_main(int argc UNUSED_PARAM, char **argv) - if (!(flags & 2)) { /* no -p (-s may be present) */ - if (time(NULL) == remote_time) - bb_error_msg("current time matches remote time"); -- else -- if (stime(&remote_time) < 0) -+ else { -+ struct timespec ts; -+ ts.tv_sec = remote_time; -+ ts.tv_nsec = 0; -+ if (clock_settime(CLOCK_REALTIME, &ts) < 0) - bb_perror_msg_and_die("can't set time of day"); -+ } - } - - if (flags != 1) /* not lone -s */ --- -2.25.1 - diff --git a/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig index 47e984ef85a2..520b1667c8be 100644 --- a/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig @@ -6,7 +6,5 @@ CT_ARCH_ARM=y CT_ARCH_64=y CT_KERNEL_LINUX=y CT_LINUX_V_4_1=y -CT_BINUTILS_V_2_29=y CT_GLIBC_V_2_17=y -CT_GCC_V_8=y CT_CC_LANG_CXX=y diff --git a/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile index 1704bef1e4ed..414bcc52484c 100644 --- a/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile @@ -6,8 +6,12 @@ FROM centos:7 WORKDIR /build +# CentOS 7 EOL is June 30, 2024, but the repos remain in the vault. +RUN sed -i /etc/yum.repos.d/*.repo -e 's!^mirrorlist!#mirrorlist!' \ + -e 's!^#baseurl=http://mirror.centos.org/!baseurl=https://vault.centos.org/!' +RUN sed -i 's/enabled=1/enabled=0/' /etc/yum/pluginconf.d/fastestmirror.conf + RUN yum upgrade -y && \ - yum install -y epel-release && \ yum install -y \ automake \ bzip2 \ diff --git a/src/ci/docker/host-x86_64/dist-various-2/build-solaris-toolchain.sh b/src/ci/docker/host-x86_64/dist-various-2/build-solaris-toolchain.sh index 3939b4b7c41c..d046b539036d 100755 --- a/src/ci/docker/host-x86_64/dist-various-2/build-solaris-toolchain.sh +++ b/src/ci/docker/host-x86_64/dist-various-2/build-solaris-toolchain.sh @@ -54,7 +54,7 @@ apt-get clean # This makes all those symlinks. for lib in $(find -name '*.so.*'); do target=${lib%.so.*}.so - [ -e $target ] || ln -s ${lib##*/} $target + ln -s ${lib##*/} $target || echo "warning: silenced error symlinking $lib" done # Remove Solaris 11 functions that are optionally used by libbacktrace. diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index fe84c23a11c1..4aa1a3ccc2a5 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -6,8 +6,12 @@ FROM centos:7 WORKDIR /build +# CentOS 7 EOL is June 30, 2024, but the repos remain in the vault. +RUN sed -i /etc/yum.repos.d/*.repo -e 's!^mirrorlist!#mirrorlist!' \ + -e 's!^#baseurl=http://mirror.centos.org/!baseurl=https://vault.centos.org/!' +RUN sed -i 's/enabled=1/enabled=0/' /etc/yum/pluginconf.d/fastestmirror.conf + RUN yum upgrade -y && \ - yum install -y epel-release && \ yum install -y \ automake \ bzip2 \ @@ -58,14 +62,6 @@ COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/ RUN ./build-clang.sh ENV CC=clang CXX=clang++ -# rustc-perf version from 2023-10-22 -# Should also be changed in the opt-dist tool for other environments. -ENV PERF_COMMIT 4f313add609f43e928e98132358e8426ed3969ae -RUN curl -LS -o perf.zip https://ci-mirrors.rust-lang.org/rustc/rustc-perf-$PERF_COMMIT.zip && \ - unzip perf.zip && \ - mv rustc-perf-$PERF_COMMIT rustc-perf && \ - rm perf.zip - COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index d0da7d306601..0d9c21c4487b 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -61,8 +61,8 @@ ENV SCRIPT python3 ../x.py check --stage 0 --set build.optimized-compiler-builti /scripts/validate-toolstate.sh && \ /scripts/validate-error-codes.sh && \ reuse --include-submodules lint && \ - # Runs checks to ensure that there are no ES5 issues in our JS code. - es-check es8 ../src/librustdoc/html/static/js/*.js && \ + # Runs checks to ensure that there are no issues in our JS code. + es-check es2019 ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \ eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js diff --git a/src/ci/docker/host-x86_64/rfl/Dockerfile b/src/ci/docker/host-x86_64/rfl/Dockerfile new file mode 100644 index 000000000000..97298519cf20 --- /dev/null +++ b/src/ci/docker/host-x86_64/rfl/Dockerfile @@ -0,0 +1,40 @@ +FROM ubuntu:22.04 + +ARG DEBIAN_FRONTEND=noninteractive + +# libclang1 is required for libclang.so, required by bindgen +# clang, llvm and lld is required by RfL to compile host code +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + flex \ + bison \ + bc \ + clang-15 \ + libclang1-15 \ + llvm-15 \ + lld-15 \ + libelf-dev \ + libedit-dev \ + libssl-dev \ + pkg-config \ + zlib1g-dev \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +# RfL needs access to cland, lld and llvm tools +ENV PATH="${PATH}:/usr/lib/llvm-15/bin" + +ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu + +COPY /scripts/rfl-build.sh /tmp/rfl-build.sh +ENV SCRIPT bash /tmp/rfl-build.sh diff --git a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock index b0c17d9a296c..dacf531e4048 100644 --- a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock +++ b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "r-efi" -version = "4.4.0" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c47196f636c4cc0634b73b0405323d177753c2e15e866952c64ea22902567a34" +checksum = "e9e935efc5854715dfc0a4c9ef18dc69dee0ec3bf9cc3ab740db831c0fdd86a3" [[package]] name = "uefi_qemu_test" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile index e4534d0f8408..fa31801269af 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile @@ -44,6 +44,14 @@ ENV RUST_CONFIGURE_ARGS \ --set target.x86_64-unknown-linux-gnu.cc=clang \ --set target.x86_64-unknown-linux-gnu.cxx=clang++ +# This job appears to be checking two separate things: +# - That we can build the compiler with `--enable-debug` +# (without necessarily testing the result). +# - That the tests with `//@ needs-force-clang-based-tests` pass, since they +# don't run by default unless RUSTBUILD_FORCE_CLANG_BASED_TESTS is set. +# - FIXME(https://github.com/rust-lang/rust/pull/126155#issuecomment-2156314273): +# Currently we only run the subset of tests with "clang" in their name. + ENV SCRIPT \ python3 ../x.py --stage 2 build && \ - python3 ../x.py --stage 2 test tests/run-make-fulldeps --test-args clang + python3 ../x.py --stage 2 test tests/run-make --test-args clang diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile index c8c754914aa6..a944f370c6b3 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile @@ -1,3 +1,6 @@ +# This job builds a toolchain capable of building Fuchsia, and then builds +# Fuchsia. See the build-fuchsia.sh script in this directory for more details. + FROM ubuntu:22.04 ARG DEBIAN_FRONTEND=noninteractive @@ -23,27 +26,27 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ # Duplicated in dist-various-2 Dockerfile. # FIXME: Move to canonical triple ENV \ - AR_x86_64_fuchsia=x86_64-unknown-fuchsia-ar \ - CC_x86_64_fuchsia=x86_64-unknown-fuchsia-clang \ - CFLAGS_x86_64_fuchsia="--target=x86_64-unknown-fuchsia --sysroot=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot -I/usr/local/core-linux-amd64-fuchsia-sdk/pkg/fdio/include" \ - CXX_x86_64_fuchsia=x86_64-unknown-fuchsia-clang++ \ - CXXFLAGS_x86_64_fuchsia="--target=x86_64-unknown-fuchsia --sysroot=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot -I/usr/local/core-linux-amd64-fuchsia-sdk/pkg/fdio/include" \ - LDFLAGS_x86_64_fuchsia="--target=x86_64-unknown-fuchsia --sysroot=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot -L/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/lib" + AR_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-ar \ + CC_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang \ + CFLAGS_x86_64_unknown_fuchsia="--target=x86_64-unknown-fuchsia --sysroot=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot -I/usr/local/core-linux-amd64-fuchsia-sdk/pkg/fdio/include" \ + CXX_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang++ \ + CXXFLAGS_x86_64_unknown_fuchsia="--target=x86_64-unknown-fuchsia --sysroot=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot -I/usr/local/core-linux-amd64-fuchsia-sdk/pkg/fdio/include" \ + LDFLAGS_x86_64_unknown_fuchsia="--target=x86_64-unknown-fuchsia --sysroot=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot -L/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/lib" WORKDIR /tmp COPY scripts/shared.sh /tmp/ COPY scripts/build-fuchsia-toolchain.sh /tmp/ RUN /tmp/build-fuchsia-toolchain.sh -ENV CARGO_TARGET_X86_64_FUCHSIA_AR /usr/local/bin/llvm-ar -ENV CARGO_TARGET_X86_64_FUCHSIA_RUSTFLAGS \ +ENV CARGO_TARGET_X86_64_UNKNOWN_FUCHSIA_AR /usr/local/bin/llvm-ar +ENV CARGO_TARGET_X86_64_UNKNOWN_FUCHSIA_RUSTFLAGS \ -C panic=abort \ -C force-unwind-tables=yes \ -C link-arg=--sysroot=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot \ -Lnative=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/sysroot/lib \ -Lnative=/usr/local/core-linux-amd64-fuchsia-sdk/arch/x64/lib -ENV TARGETS=x86_64-fuchsia +ENV TARGETS=x86_64-unknown-fuchsia ENV TARGETS=$TARGETS,x86_64-unknown-linux-gnu ENV TARGETS=$TARGETS,wasm32-unknown-unknown @@ -66,11 +69,11 @@ ENV RUST_CONFIGURE_ARGS \ --llvm-libunwind=in-tree \ --enable-extended \ --disable-docs \ - --set target.x86_64-fuchsia.cc=/usr/local/bin/clang \ - --set target.x86_64-fuchsia.cxx=/usr/local/bin/clang++ \ - --set target.x86_64-fuchsia.ar=/usr/local/bin/llvm-ar \ - --set target.x86_64-fuchsia.ranlib=/usr/local/bin/llvm-ranlib \ - --set target.x86_64-fuchsia.linker=/usr/local/bin/ld.lld + --set target.x86_64-unknown-fuchsia.cc=/usr/local/bin/clang \ + --set target.x86_64-unknown-fuchsia.cxx=/usr/local/bin/clang++ \ + --set target.x86_64-unknown-fuchsia.ar=/usr/local/bin/llvm-ar \ + --set target.x86_64-unknown-fuchsia.ranlib=/usr/local/bin/llvm-ranlib \ + --set target.x86_64-unknown-fuchsia.linker=/usr/local/bin/ld.lld ENV SCRIPT \ python3 ../x.py install --target $TARGETS compiler/rustc library/std clippy && \ bash ../src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh b/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh index 9cc508fe9288..2bb1d0a63387 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh @@ -2,50 +2,87 @@ # Downloads and builds the Fuchsia operating system using a toolchain installed # in $RUST_INSTALL_DIR. +# +# You may run this script locally using Docker with the following command: +# +# $ src/ci/docker/run.sh x86_64-gnu-integration +# +# Alternatively, from within the container with --dev, assuming you have made it +# as far as building the toolchain with the above command: +# +# $ src/ci/docker/run.sh --dev x86_64-gnu-integration +# docker# git config --global --add safe.directory /checkout/obj/fuchsia +# docker# ../src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh +# +# Also see the docs in the rustc-dev-guide for more info: +# https://github.com/rust-lang/rustc-dev-guide/pull/1989 set -euf -o pipefail -INTEGRATION_SHA=1011e3298775ee7cdf6f6dc73e808d6a86e33bd6 +# Set this variable to 1 to disable updating the Fuchsia checkout. This is +# useful for making local changes. You can find the Fuchsia checkout in +# `obj/x86_64-gnu-integration/fuchsia` in your local checkout after running this +# job for the first time. +KEEP_CHECKOUT= + +# Any upstream refs that should be cherry-picked. This can be used to include +# Gerrit changes from https://fxrev.dev during development (click the "Download" +# button on a changelist to see the cherry pick ref). Example: +# PICK_REFS=(refs/changes/71/1054071/2 refs/changes/74/1054574/2) PICK_REFS=() +# The commit hash of Fuchsia's integration.git to check out. This controls the +# commit hash of fuchsia.git and some other repos in the "monorepo" checkout, in +# addition to versions of prebuilts. It should be bumped regularly by the +# Fuchsia team – we aim for every 1-2 months. +INTEGRATION_SHA=737ebdd83afa47b742ca8325fad0176952fcefbd + checkout=fuchsia jiri=.jiri_root/bin/jiri set -x -# This script will: -# - create a directory named "fuchsia" if it does not exist -# - download "jiri" to "fuchsia/.jiri_root/bin" -curl -s "https://fuchsia.googlesource.com/jiri/+/HEAD/scripts/bootstrap_jiri?format=TEXT" \ - | base64 --decode \ - | bash -s $checkout +if [ -z "$KEEP_CHECKOUT" ]; then + # This script will: + # - create a directory named "fuchsia" if it does not exist + # - download "jiri" to "fuchsia/.jiri_root/bin" + curl -s "https://fuchsia.googlesource.com/jiri/+/HEAD/scripts/bootstrap_jiri?format=TEXT" \ + | base64 --decode \ + | bash -s $checkout -cd $checkout + cd $checkout -$jiri init \ - -partial=true \ - -analytics-opt=false \ - . + $jiri init \ + -partial=true \ + -analytics-opt=false \ + . -$jiri import \ - -name=integration \ - -revision=$INTEGRATION_SHA \ - -overwrite=true \ - flower \ - "https://fuchsia.googlesource.com/integration" + $jiri import \ + -name=integration \ + -revision=$INTEGRATION_SHA \ + -overwrite=true \ + flower \ + "https://fuchsia.googlesource.com/integration" -if [ -d ".git" ]; then - # Wipe out any local changes if we're reusing a checkout. - git checkout --force JIRI_HEAD + if [ -d ".git" ]; then + # Wipe out any local changes if we're reusing a checkout. + git checkout --force JIRI_HEAD + fi + + $jiri update -autoupdate=false + + echo integration commit = $(git -C integration rev-parse HEAD) + + for git_ref in "${PICK_REFS[@]}"; do + git fetch https://fuchsia.googlesource.com/fuchsia $git_ref + git cherry-pick --no-commit FETCH_HEAD + done +else + echo Reusing existing Fuchsia checkout + cd $checkout fi -$jiri update -autoupdate=false - -echo integration commit = $(git -C integration rev-parse HEAD) - -for git_ref in "${PICK_REFS[@]}"; do - git fetch https://fuchsia.googlesource.com/fuchsia $git_ref - git cherry-pick --no-commit FETCH_HEAD -done - +# Run the script inside the Fuchsia checkout responsible for building Fuchsia. +# You can change arguments to the build by setting KEEP_CHECKOUT=1 above and +# modifying them in build_fuchsia_from_rust_ci.sh. bash scripts/rust/build_fuchsia_from_rust_ci.sh diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile index 538962802c96..275acb47c33a 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile @@ -45,10 +45,6 @@ RUN sh /scripts/sccache.sh ENV NO_DOWNLOAD_CI_LLVM 1 ENV EXTERNAL_LLVM 1 -# This is not the latest LLVM version, so some components required by tests may -# be missing. -ENV IS_NOT_LATEST_LLVM 1 - # Using llvm-link-shared due to libffi issues -- see #34486 ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index 22dcb808c743..a4c59b3067ea 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -270,7 +270,6 @@ else args="$args --volume $root_dir:/checkout$SRC_MOUNT_OPTION" 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" id=$(id -u) diff --git a/src/ci/docker/scripts/build-fuchsia-toolchain.sh b/src/ci/docker/scripts/build-fuchsia-toolchain.sh index 7a0d4fcffc15..027d412d2506 100755 --- a/src/ci/docker/scripts/build-fuchsia-toolchain.sh +++ b/src/ci/docker/scripts/build-fuchsia-toolchain.sh @@ -4,13 +4,13 @@ set -ex source shared.sh FUCHSIA_SDK_URL=https://chrome-infra-packages.appspot.com/dl/fuchsia/sdk/core/linux-amd64 -FUCHSIA_SDK_ID=version:20.20240412.3.1 -FUCHSIA_SDK_SHA256=cc52f3497487dd813c89d9316e6967efcea89c7759edccf3e40fcf3662e53f19 +FUCHSIA_SDK_ID=version:21.20240610.2.1 +FUCHSIA_SDK_SHA256=2d2d057fc3f0404197cced2200f88cbcdaaf5fbf6475955045091f8676791ce7 FUCHSIA_SDK_USR_DIR=/usr/local/core-linux-amd64-fuchsia-sdk CLANG_DOWNLOAD_URL=\ https://chrome-infra-packages.appspot.com/dl/fuchsia/third_party/clang/linux-amd64 -CLANG_DOWNLOAD_ID=git_revision:c777c011a709dffd4fa5e79cad7947b7c3405d02 -CLANG_DOWNLOAD_SHA256=779167422ad73c292f049dcea5569f84577af9292189ed2749518b966a4d0844 +CLANG_DOWNLOAD_ID=git_revision:3809e20afc68d7d03821f0ec59b928dcf9befbf4 +CLANG_DOWNLOAD_SHA256=3c2c442b61cd9e8f1b567738f6d53cffe11b3fc820e7dae87a82a0859be8f204 install_clang() { mkdir -p clang_download diff --git a/src/ci/docker/scripts/fuchsia-test-runner.py b/src/ci/docker/scripts/fuchsia-test-runner.py index 4f504341d526..115ee69a5891 100755 --- a/src/ci/docker/scripts/fuchsia-test-runner.py +++ b/src/ci/docker/scripts/fuchsia-test-runner.py @@ -8,34 +8,137 @@ https://doc.rust-lang.org/stable/rustc/platform-support/fuchsia.html#aarch64-unk """ import argparse +from concurrent.futures import ThreadPoolExecutor from dataclasses import dataclass import glob -import hashlib +import io import json +import logging import os import platform +import shlex import shutil import subprocess import sys -from typing import ClassVar, List +from pathlib import Path +from typing import ClassVar, List, Optional -@dataclass +def check_call_with_logging( + args, *, stdout_handler, stderr_handler, check=True, text=True, **kwargs +): + stdout_handler(f"Subprocess: {shlex.join(str(arg) for arg in args)}") + + with subprocess.Popen( + args, + text=text, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + **kwargs, + ) as process: + with ThreadPoolExecutor(max_workers=2) as executor: + + def exhaust_pipe(handler, pipe): + for line in pipe: + handler(line.rstrip()) + + executor_out = executor.submit( + exhaust_pipe, stdout_handler, process.stdout + ) + executor_err = executor.submit( + exhaust_pipe, stderr_handler, process.stderr + ) + executor_out.result() + executor_err.result() + retcode = process.poll() + if check and retcode: + raise subprocess.CalledProcessError(retcode, process.args) + return subprocess.CompletedProcess(process.args, retcode) + + +def check_output_with_logging( + args, *, stdout_handler, stderr_handler, check=True, text=True, **kwargs +): + stdout_handler(f"Subprocess: {shlex.join(str(arg) for arg in args)}") + + buf = io.StringIO() + + with subprocess.Popen( + args, + text=text, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + **kwargs, + ) as process: + with ThreadPoolExecutor(max_workers=2) as executor: + + def exhaust_stdout(handler, buf, pipe): + for line in pipe: + handler(line.rstrip()) + buf.write(line) + buf.write("\n") + + def exhaust_stderr(handler, pipe): + for line in pipe: + handler(line.rstrip()) + + executor_out = executor.submit( + exhaust_stdout, stdout_handler, buf, process.stdout + ) + executor_err = executor.submit( + exhaust_stderr, stderr_handler, process.stderr + ) + executor_out.result() + executor_err.result() + retcode = process.poll() + if check and retcode: + raise subprocess.CalledProcessError(retcode, process.args) + + return buf.getvalue() + + +def atomic_link(link: Path, target: Path): + link_dir = link.parent + os.makedirs(link_dir, exist_ok=True) + link_file = link.name + tmp_file = link_dir.joinpath(link_file + "_tmp") + os.link(target, tmp_file) + try: + os.rename(tmp_file, link) + except Exception as e: + raise e + finally: + if tmp_file.exists(): + os.remove(tmp_file) + + +@dataclass(kw_only=True) class TestEnvironment: - rust_build_dir: str - sdk_dir: str + rust_build_dir: Path + sdk_dir: Path target: str + toolchain_dir: Path + local_pb_path: Optional[Path] + use_local_pb: bool verbose: bool = False - @staticmethod - def tmp_dir(): - tmp_dir = os.environ.get("TEST_TOOLCHAIN_TMP_DIR") - if tmp_dir is not None: - return os.path.abspath(tmp_dir) - return os.path.join(os.path.dirname(__file__), "tmp~") + env_logger = logging.getLogger("env") + subprocess_logger = logging.getLogger("env.subprocess") + __tmp_dir = None @staticmethod - def triple_to_arch(triple): + def tmp_dir() -> Path: + if TestEnvironment.__tmp_dir: + return TestEnvironment.__tmp_dir + tmp_dir = os.environ.get("TEST_TOOLCHAIN_TMP_DIR") + if tmp_dir is not None: + TestEnvironment.__tmp_dir = Path(tmp_dir).absolute() + else: + TestEnvironment.__tmp_dir = Path(__file__).parent.joinpath("tmp~") + return TestEnvironment.__tmp_dir + + @staticmethod + def triple_to_arch(triple) -> str: if "x86_64" in triple: return "x64" elif "aarch64" in triple: @@ -44,61 +147,175 @@ class TestEnvironment: raise Exception(f"Unrecognized target triple {triple}") @classmethod - def env_file_path(cls): - return os.path.join(cls.tmp_dir(), "test_env.json") + def env_file_path(cls) -> Path: + return cls.tmp_dir().joinpath("test_env.json") @classmethod def from_args(cls, args): + local_pb_path = args.local_product_bundle_path + if local_pb_path is not None: + local_pb_path = Path(local_pb_path).absolute() + return cls( - os.path.abspath(args.rust_build), - os.path.abspath(args.sdk), - args.target, + rust_build_dir=Path(args.rust_build).absolute(), + sdk_dir=Path(args.sdk).absolute(), + target=args.target, + toolchain_dir=Path(args.toolchain_dir).absolute(), + local_pb_path=local_pb_path, + use_local_pb=args.use_local_product_bundle_if_exists, verbose=args.verbose, ) @classmethod def read_from_file(cls): with open(cls.env_file_path(), encoding="utf-8") as f: - test_env = json.loads(f.read()) + test_env = json.load(f) + local_pb_path = test_env["local_pb_path"] + if local_pb_path is not None: + local_pb_path = Path(local_pb_path) + return cls( - test_env["rust_build_dir"], - test_env["sdk_dir"], - test_env["target"], + rust_build_dir=Path(test_env["rust_build_dir"]), + sdk_dir=Path(test_env["sdk_dir"]), + target=test_env["target"], + toolchain_dir=Path(test_env["toolchain_dir"]), + local_pb_path=local_pb_path, + use_local_pb=test_env["use_local_pb"], verbose=test_env["verbose"], ) + def build_id(self, binary): + llvm_readelf = Path(self.toolchain_dir).joinpath("bin", "llvm-readelf") + process = subprocess.run( + args=[ + llvm_readelf, + "-n", + "--elf-output-style=JSON", + binary, + ], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + if process.returncode: + self.env_logger.error( + f"llvm-readelf failed for binary {binary} with output {process.stdout}" + ) + raise Exception(f"Unreadable build-id for binary {binary}") + data = json.loads(process.stdout) + if len(data) != 1: + raise Exception( + f"Unreadable output from llvm-readelf for binary {binary}" + ) + notes = data[0]["Notes"] + for note in notes: + note_section = note["NoteSection"] + if note_section["Name"] == ".note.gnu.build-id": + return note_section["Note"]["Build ID"] + raise Exception(f"Build ID not found for binary {binary}") + + def generate_buildid_dir( + self, + binary: Path, + build_id_dir: Path, + build_id: str, + log_handler: logging.Logger, + ): + os.makedirs(build_id_dir, exist_ok=True) + suffix = ".debug" + # Hardlink the original binary + build_id_prefix_dir = build_id_dir.joinpath(build_id[:2]) + unstripped_binary = build_id_prefix_dir.joinpath(build_id[2:] + suffix) + build_id_prefix_dir.mkdir(parents=True, exist_ok=True) + atomic_link(unstripped_binary, binary) + assert unstripped_binary.exists() + stripped_binary = unstripped_binary.with_suffix("") + llvm_objcopy = Path(self.toolchain_dir).joinpath("bin", "llvm-objcopy") + strip_mode = "--strip-sections" + check_call_with_logging( + [ + llvm_objcopy, + strip_mode, + unstripped_binary, + stripped_binary, + ], + stdout_handler=log_handler.info, + stderr_handler=log_handler.error, + ) + return stripped_binary + def write_to_file(self): with open(self.env_file_path(), "w", encoding="utf-8") as f: - f.write(json.dumps(self.__dict__)) + local_pb_path = self.local_pb_path + if local_pb_path is not None: + local_pb_path = str(local_pb_path) - def package_server_log_path(self): - return os.path.join(self.tmp_dir(), "package_server_log") + json.dump( + { + "rust_build_dir": str(self.rust_build_dir), + "sdk_dir": str(self.sdk_dir), + "target": self.target, + "toolchain_dir": str(self.toolchain_dir), + "local_pb_path": local_pb_path, + "use_local_pb": self.use_local_pb, + "verbose": self.verbose, + }, + f, + ) - def emulator_log_path(self): - return os.path.join(self.tmp_dir(), "emulator_log") + def setup_logging(self, log_to_file=False): + fs = logging.Formatter("%(asctime)s %(levelname)s:%(name)s:%(message)s") + if log_to_file: + logfile_handler = logging.FileHandler( + self.tmp_dir().joinpath("log") + ) + logfile_handler.setLevel(logging.DEBUG) + logfile_handler.setFormatter(fs) + logging.getLogger().addHandler(logfile_handler) + stream_handler = logging.StreamHandler(sys.stdout) + stream_handler.setFormatter(fs) + if self.verbose: + stream_handler.setLevel(logging.DEBUG) + else: + stream_handler.setLevel(logging.INFO) + logging.getLogger().addHandler(stream_handler) + logging.getLogger().setLevel(logging.DEBUG) - def packages_dir(self): - return os.path.join(self.tmp_dir(), "packages") + @property + def package_server_log_path(self) -> Path: + return self.tmp_dir().joinpath("package_server_log") - def output_dir(self): - return os.path.join(self.tmp_dir(), "output") + @property + def emulator_log_path(self) -> Path: + return self.tmp_dir().joinpath("emulator_log") + + @property + def packages_dir(self) -> Path: + return self.tmp_dir().joinpath("packages") + + @property + def output_dir(self) -> Path: + return self.tmp_dir().joinpath("output") + + def read_sdk_version(self): + meta_json_path = Path(self.sdk_dir).joinpath("meta", "manifest.json") + with open(meta_json_path, encoding="utf-8") as f: + meta_json = json.load(f) + return meta_json["id"] TEST_REPO_NAME: ClassVar[str] = "rust-testing" - def repo_dir(self): - return os.path.join(self.tmp_dir(), self.TEST_REPO_NAME) + def repo_dir(self) -> Path: + return self.tmp_dir().joinpath(self.TEST_REPO_NAME) - def libs_dir(self): - return os.path.join( - self.rust_build_dir, + def libs_dir(self) -> Path: + return self.rust_build_dir.joinpath( "host", "stage2", "lib", ) - def rustlibs_dir(self): - return os.path.join( - self.libs_dir(), + def rustlibs_dir(self) -> Path: + return self.libs_dir().joinpath( "rustlib", self.target, "lib", @@ -112,8 +329,8 @@ class TestEnvironment: return "a64" raise Exception(f"Unrecognized host architecture {machine}") - def tool_path(self, tool): - return os.path.join(self.sdk_dir, "tools", self.sdk_arch(), tool) + def tool_path(self, tool) -> Path: + return Path(self.sdk_dir).joinpath("tools", self.sdk_arch(), tool) def host_arch_triple(self): machine = platform.machine() @@ -123,45 +340,25 @@ class TestEnvironment: return "aarch64-unknown-linux-gnu" raise Exception(f"Unrecognized host architecture {machine}") - def zxdb_script_path(self): - return os.path.join(self.tmp_dir(), "zxdb_script") - - def pm_lockfile_path(self): - return os.path.join(self.tmp_dir(), "pm.lock") - - def log_info(self, msg): - print(msg) - - def log_debug(self, msg): - if self.verbose: - print(msg) - - def subprocess_output(self): - if self.verbose: - return sys.stdout - return subprocess.DEVNULL - - def check_call(self, args, **kwargs): - self.log_info(f"Running: {' '.join(args)}") - return subprocess.check_call(args, **kwargs) - - def check_output(self, args, **kwargs): - self.log_info(f"Running: {' '.join(args)}") - return subprocess.check_output(args, **kwargs) + def zxdb_script_path(self) -> Path: + return Path(self.tmp_dir(), "zxdb_script") + @property def ffx_daemon_log_path(self): - return os.path.join(self.tmp_dir(), "ffx_daemon_log") + return self.tmp_dir().joinpath("ffx_daemon_log") + @property def ffx_isolate_dir(self): - return os.path.join(self.tmp_dir(), "ffx_isolate") + return self.tmp_dir().joinpath("ffx_isolate") + @property def home_dir(self): - return os.path.join(self.tmp_dir(), "user-home") + return self.tmp_dir().joinpath("user-home") def start_ffx_isolation(self): # Most of this is translated directly from ffx's isolate library - os.mkdir(self.ffx_isolate_dir()) - os.mkdir(self.home_dir()) + os.mkdir(self.ffx_isolate_dir) + os.mkdir(self.home_dir) ffx_path = self.tool_path("ffx") ffx_env = self.ffx_cmd_env() @@ -170,7 +367,7 @@ class TestEnvironment: # We want this to be a long-running process that persists after the script finishes # pylint: disable=consider-using-with with open( - self.ffx_daemon_log_path(), "w", encoding="utf-8" + self.ffx_daemon_log_path, "w", encoding="utf-8" ) as ffx_daemon_log_file: subprocess.Popen( [ @@ -184,7 +381,7 @@ class TestEnvironment: ) # Disable analytics - self.check_call( + check_call_with_logging( [ ffx_path, "config", @@ -192,8 +389,8 @@ class TestEnvironment: "disable", ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) # Set configs @@ -203,7 +400,7 @@ class TestEnvironment: "test.experimental_structured_output": "true", } for key, value in configs.items(): - self.check_call( + check_call_with_logging( [ ffx_path, "config", @@ -212,14 +409,14 @@ class TestEnvironment: value, ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) def ffx_cmd_env(self): return { - "HOME": self.home_dir(), - "FFX_ISOLATE_DIR": self.ffx_isolate_dir(), + "HOME": self.home_dir, + "FFX_ISOLATE_DIR": self.ffx_isolate_dir, # We want to use our own specified temp directory "TMP": self.tmp_dir(), "TEMP": self.tmp_dir(), @@ -228,16 +425,15 @@ class TestEnvironment: } def stop_ffx_isolation(self): - self.check_call( + check_call_with_logging( [ self.tool_path("ffx"), "daemon", "stop", - "-w", ], env=self.ffx_cmd_env(), - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) def start(self): @@ -256,22 +452,23 @@ class TestEnvironment: """ # Initialize temp directory - if not os.path.exists(self.tmp_dir()): - os.mkdir(self.tmp_dir()) - elif len(os.listdir(self.tmp_dir())) != 0: - raise Exception(f"Temp directory is not clean (in {self.tmp_dir()})") - - os.mkdir(self.output_dir()) + os.makedirs(self.tmp_dir(), exist_ok=True) + if len(os.listdir(self.tmp_dir())) != 0: + raise Exception( + f"Temp directory is not clean (in {self.tmp_dir()})" + ) + self.setup_logging(log_to_file=True) + os.mkdir(self.output_dir) ffx_path = self.tool_path("ffx") ffx_env = self.ffx_cmd_env() # Start ffx isolation - self.log_info("Starting ffx isolation...") + self.env_logger.info("Starting ffx isolation...") self.start_ffx_isolation() # Stop any running emulators (there shouldn't be any) - self.check_call( + check_call_with_logging( [ ffx_path, "emu", @@ -279,79 +476,95 @@ class TestEnvironment: "--all", ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) - # Look up the product bundle transfer manifest. - self.log_info("Looking up the product bundle transfer manifest...") - product_name = "minimal." + self.triple_to_arch(self.target) - fuchsia_version = "20.20240412.3.1" + if not self.local_pb_path: + self.local_pb_path = os.path.join(self.tmp_dir(), "local_pb") + else: + self.local_pb_path = os.path.abspath(self.local_pb_path) - out = self.check_output( - [ - ffx_path, - "--machine", - "json", - "product", - "lookup", - product_name, - fuchsia_version, - "--base-url", - "gs://fuchsia/development/" + fuchsia_version, - ], - env=ffx_env, - stderr=self.subprocess_output(), - ) + if self.use_local_pb and os.path.exists(self.local_pb_path): + self.env_logger.info( + 'Using existing emulator image at "%s"' % self.local_pb_path + ) + else: + shutil.rmtree(self.local_pb_path, ignore_errors=True) - self.log_debug(out) + # Look up the product bundle transfer manifest. + self.env_logger.info( + "Looking up the product bundle transfer manifest..." + ) + product_name = "minimal." + self.triple_to_arch(self.target) + sdk_version = self.read_sdk_version() - try: - transfer_manifest_url = json.loads(out)["transfer_manifest_url"] - except Exception as e: - print(e) - raise Exception("Unable to parse transfer manifest") from e + output = check_output_with_logging( + [ + ffx_path, + "--machine", + "json", + "product", + "lookup", + product_name, + sdk_version, + "--base-url", + "gs://fuchsia/development/" + sdk_version, + ], + env=ffx_env, + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, + ) - # Download the product bundle. - product_bundle_dir = os.path.join(self.tmp_dir(), 'product-bundle') - self.check_call( - [ - ffx_path, - "product", - "download", - transfer_manifest_url, - product_bundle_dir, - "--force", - ], - env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), - ) + try: + transfer_manifest_url = json.loads(output)[ + "transfer_manifest_url" + ] + except Exception as e: + print(e) + raise Exception("Unable to parse transfer manifest") from e + + # Download the product bundle. + self.env_logger.info("Downloading the product bundle...") + check_call_with_logging( + [ + ffx_path, + "product", + "download", + transfer_manifest_url, + self.local_pb_path, + ], + env=ffx_env, + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, + ) # Start emulator + self.env_logger.info("Starting emulator...") + # FIXME: condition --accel hyper on target arch matching host arch - self.check_call( + check_call_with_logging( [ ffx_path, "emu", "start", - product_bundle_dir, + self.local_pb_path, "--headless", "--log", - self.emulator_log_path(), + self.emulator_log_path, "--net", - "tap", + "auto", "--accel", - "hyper", + "auto", ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) # Create new package repo - self.log_info("Creating package repo...") - self.check_call( + self.env_logger.info("Creating package repo...") + check_call_with_logging( [ ffx_path, "repository", @@ -359,11 +572,12 @@ class TestEnvironment: self.repo_dir(), ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) - self.check_call( + # Add repository + check_call_with_logging( [ ffx_path, "repository", @@ -373,15 +587,12 @@ class TestEnvironment: self.repo_dir(), ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) - # Write to file - self.write_to_file() - # Start repository server - self.check_call( + check_call_with_logging( [ ffx_path, "repository", @@ -391,12 +602,12 @@ class TestEnvironment: "[::]:0", ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) # Register with newly-started emulator - self.check_call( + check_call_with_logging( [ ffx_path, "target", @@ -406,11 +617,14 @@ class TestEnvironment: self.TEST_REPO_NAME, ], env=ffx_env, - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) - self.log_info("Success! Your environment is ready to run tests.") + # Write to file + self.write_to_file() + + self.env_logger.info("Success! Your environment is ready to run tests.") # FIXME: shardify this # `facet` statement required for TCP testing via @@ -481,7 +695,7 @@ class TestEnvironment: - Forward the test's stdout and stderr as this script's stdout and stderr """ - bin_path = os.path.abspath(args.bin_path) + bin_path = Path(args.bin_path).absolute() # Find libstd and libtest libstd_paths = glob.glob(os.path.join(self.rustlibs_dir(), "libstd-*.so")) @@ -490,233 +704,240 @@ class TestEnvironment: if not libstd_paths: raise Exception(f"Failed to locate libstd (in {self.rustlibs_dir()})") - # Build a unique, deterministic name for the test using the name of the - # binary and the last 6 hex digits of the hash of the full path - def path_checksum(path): - m = hashlib.sha256() - m.update(path.encode("utf-8")) - return m.hexdigest()[0:6] - base_name = os.path.basename(os.path.dirname(args.bin_path)) exe_name = base_name.lower().replace(".", "_") - package_name = f"{exe_name}_{path_checksum(bin_path)}" + build_id = self.build_id(bin_path) + package_name = f"{exe_name}_{build_id}" - package_dir = os.path.join(self.packages_dir(), package_name) - cml_path = os.path.join(package_dir, "meta", f"{package_name}.cml") - cm_path = os.path.join(package_dir, "meta", f"{package_name}.cm") - manifest_path = os.path.join(package_dir, f"{package_name}.manifest") - manifest_json_path = os.path.join(package_dir, "package_manifest.json") - far_path = os.path.join(package_dir, f"{package_name}-0.far") + package_dir = self.packages_dir.joinpath(package_name) + package_dir.mkdir(parents=True, exist_ok=True) + meta_dir = package_dir.joinpath("meta") + meta_dir.mkdir(parents=True, exist_ok=True) + meta_package_path = meta_dir.joinpath("package") + cml_path = meta_dir.joinpath(f"{package_name}.cml") + cm_path = meta_dir.joinpath(f"{package_name}.cm") + manifest_path = package_dir.joinpath(f"{package_name}.manifest") shared_libs = args.shared_libs[: args.n] arguments = args.shared_libs[args.n :] - test_output_dir = os.path.join(self.output_dir(), package_name) + test_output_dir = self.output_dir.joinpath(package_name) # Clean and create temporary output directory - if os.path.exists(test_output_dir): + if test_output_dir.exists(): shutil.rmtree(test_output_dir) - - os.mkdir(test_output_dir) + test_output_dir.mkdir(parents=True) # Open log file - log_path = os.path.join(test_output_dir, "log") - with open(log_path, "w", encoding="utf-8") as log_file: + runner_logger = logging.getLogger(f"env.package.{package_name}") + runner_logger.setLevel(logging.DEBUG) + logfile_handler = logging.FileHandler(test_output_dir.joinpath("log")) + logfile_handler.setLevel(logging.DEBUG) + logfile_handler.setFormatter( + logging.Formatter("%(levelname)s:%(name)s:%(message)s") + ) + runner_logger.addHandler(logfile_handler) - def log(msg): - print(msg, file=log_file) - log_file.flush() + runner_logger.info(f"Bin path: {bin_path}") + runner_logger.info("Setting up package...") - log(f"Bin path: {bin_path}") + # Link binary to build-id dir and strip it. + build_id_dir = self.tmp_dir().joinpath(".build-id") + stripped_binary = self.generate_buildid_dir( + binary=bin_path, + build_id_dir=build_id_dir, + build_id=build_id, + log_handler=runner_logger, + ) + runner_logger.info(f"Stripped Bin path: {stripped_binary}") - log("Writing CML...") + runner_logger.info("Writing CML...") - # Write and compile CML - with open(cml_path, "w", encoding="utf-8") as cml: - # Collect environment variables - env_vars = "" - for var_name in self.TEST_ENV_VARS: - var_value = os.getenv(var_name) - if var_value is not None: - env_vars += f'\n "{var_name}={var_value}",' + # Write and compile CML + with open(cml_path, "w", encoding="utf-8") as cml: + # Collect environment variables + env_vars = "" + for var_name in self.TEST_ENV_VARS: + var_value = os.getenv(var_name) + if var_value is not None: + env_vars += f'\n "{var_name}={var_value}",' - # Default to no backtrace for test suite - if os.getenv("RUST_BACKTRACE") is None: - env_vars += '\n "RUST_BACKTRACE=0",' + # Default to no backtrace for test suite + if os.getenv("RUST_BACKTRACE") is None: + env_vars += '\n "RUST_BACKTRACE=0",' - # Use /tmp as the test temporary directory - env_vars += '\n "RUST_TEST_TMPDIR=/tmp",' + # Use /tmp as the test temporary directory + env_vars += '\n "RUST_TEST_TMPDIR=/tmp",' - cml.write( - self.CML_TEMPLATE.format(env_vars=env_vars, exe_name=exe_name) - ) - - log("Compiling CML...") - - self.check_call( - [ - self.tool_path("cmc"), - "compile", - cml_path, - "--includepath", - ".", - "--output", - cm_path, - ], - stdout=log_file, - stderr=log_file, + cml.write( + self.CML_TEMPLATE.format(env_vars=env_vars, exe_name=exe_name) ) - log("Writing manifest...") + runner_logger.info("Compiling CML...") - # Write, build, and archive manifest - with open(manifest_path, "w", encoding="utf-8") as manifest: + check_call_with_logging( + [ + self.tool_path("cmc"), + "compile", + cml_path, + "--includepath", + ".", + "--output", + cm_path, + ], + stdout_handler=runner_logger.info, + stderr_handler=runner_logger.warning, + ) + + runner_logger.info("Writing meta/package...") + with open(meta_package_path, "w", encoding="utf-8") as f: + json.dump({"name": package_name, "version": "0"}, f) + + runner_logger.info("Writing manifest...") + + # Write package manifest + with open(manifest_path, "w", encoding="utf-8") as manifest: + manifest.write( + self.MANIFEST_TEMPLATE.format( + bin_path=stripped_binary, + exe_name=exe_name, + package_dir=package_dir, + package_name=package_name, + target=self.target, + sdk_dir=self.sdk_dir, + libstd_name=os.path.basename(libstd_paths[0]), + libstd_path=libstd_paths[0], + target_arch=self.triple_to_arch(self.target), + ) + ) + # `libtest`` was historically a shared library, but now seems to be (sometimes?) + # statically linked. If we find it as a shared library, include it in the manifest. + if libtest_paths: manifest.write( - self.MANIFEST_TEMPLATE.format( - bin_path=bin_path, - exe_name=exe_name, - package_dir=package_dir, - package_name=package_name, - target=self.target, - sdk_dir=self.sdk_dir, - libstd_name=os.path.basename(libstd_paths[0]), - libstd_path=libstd_paths[0], - target_arch=self.triple_to_arch(self.target), - ) + f"lib/{os.path.basename(libtest_paths[0])}={libtest_paths[0]}\n" ) - # `libtest`` was historically a shared library, but now seems to be (sometimes?) - # statically linked. If we find it as a shared library, include it in the manifest. - if libtest_paths: - manifest.write( - f"lib/{os.path.basename(libtest_paths[0])}={libtest_paths[0]}\n" - ) - for shared_lib in shared_libs: - manifest.write(f"lib/{os.path.basename(shared_lib)}={shared_lib}\n") + for shared_lib in shared_libs: + manifest.write(f"lib/{os.path.basename(shared_lib)}={shared_lib}\n") - log("Determining API level...") - out = self.check_output( - [ - self.tool_path("ffx"), - "--machine", - "json", - "version", - ], - env=self.ffx_cmd_env(), - stderr=log_file, - ) - api_level = json.loads(out)["tool_version"]["api_level"] + runner_logger.info("Determining API level...") + out = check_output_with_logging( + [ + self.tool_path("ffx"), + "--machine", + "json", + "version", + ], + env=self.ffx_cmd_env(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, + ) + api_level = json.loads(out)["tool_version"]["api_level"] - log("Compiling and archiving manifest...") + runner_logger.info("Compiling manifest...") - self.check_call( - [ - self.tool_path("ffx"), - "package", - "build", - manifest_path, - "-o", - package_dir, - "--api-level", - str(api_level), - ], - env=self.ffx_cmd_env(), - stdout=log_file, - stderr=log_file, - ) + check_call_with_logging( + [ + self.tool_path("ffx"), + "package", + "build", + manifest_path, + "-o", + package_dir, + "--api-level", + str(api_level), + ], + env=self.ffx_cmd_env(), + stdout_handler=runner_logger.info, + stderr_handler=runner_logger.warning, + ) - self.check_call( - [ - self.tool_path("ffx"), - "package", - "archive", - "create", - "-o", - far_path, - manifest_json_path, - ], - env=self.ffx_cmd_env(), - stdout=log_file, - stderr=log_file, - ) + runner_logger.info("Publishing package to repo...") - log("Publishing package to repo...") + # Publish package to repo + check_call_with_logging( + [ + self.tool_path("ffx"), + "repository", + "publish", + "--package", + os.path.join(package_dir, "package_manifest.json"), + self.repo_dir(), + ], + env=self.ffx_cmd_env(), + stdout_handler=runner_logger.info, + stderr_handler=runner_logger.warning, + ) - # Publish package to repo - self.check_call( - [ - self.tool_path("ffx"), - "repository", - "publish", - "--package", - os.path.join(package_dir, "package_manifest.json"), - self.repo_dir(), - ], - stdout=log_file, - stderr=log_file, - ) + runner_logger.info("Running ffx test...") - log("Running ffx test...") + # Run test on emulator + check_call_with_logging( + [ + self.tool_path("ffx"), + "test", + "run", + f"fuchsia-pkg://{self.TEST_REPO_NAME}/{package_name}#meta/{package_name}.cm", + "--min-severity-logs", + "TRACE", + "--output-directory", + test_output_dir, + "--", + ] + + arguments, + env=self.ffx_cmd_env(), + check=False, + stdout_handler=runner_logger.info, + stderr_handler=runner_logger.warning, + ) - # Run test on emulator - subprocess.run( - [ - self.tool_path("ffx"), - "test", - "run", - f"fuchsia-pkg://{self.TEST_REPO_NAME}/{package_name}#meta/{package_name}.cm", - "--min-severity-logs", - "TRACE", - "--output-directory", - test_output_dir, - "--", - ] - + arguments, - env=self.ffx_cmd_env(), - check=False, - stdout=log_file, - stderr=log_file, - ) + runner_logger.info("Reporting test suite output...") - log("Reporting test suite output...") + # Read test suite output + run_summary_path = test_output_dir.joinpath("run_summary.json") + if not run_summary_path.exists(): + runner_logger.error("Failed to open test run summary") + return 254 - # Read test suite output - run_summary_path = os.path.join(test_output_dir, "run_summary.json") - if os.path.exists(run_summary_path): - with open(run_summary_path, encoding="utf-8") as f: - run_summary = json.loads(f.read()) + with open(run_summary_path, encoding="utf-8") as f: + run_summary = json.load(f) - suite = run_summary["data"]["suites"][0] - case = suite["cases"][0] + suite = run_summary["data"]["suites"][0] + case = suite["cases"][0] - return_code = 0 if case["outcome"] == "PASSED" else 1 + return_code = 0 if case["outcome"] == "PASSED" else 1 - artifacts = case["artifacts"] - artifact_dir = case["artifact_dir"] - stdout_path = None - stderr_path = None + artifacts = case["artifacts"] + artifact_dir = case["artifact_dir"] + stdout_path = None + stderr_path = None - for path, artifact in artifacts.items(): - artifact_path = os.path.join(test_output_dir, artifact_dir, path) - artifact_type = artifact["artifact_type"] + for path, artifact in artifacts.items(): + artifact_path = os.path.join(test_output_dir, artifact_dir, path) + artifact_type = artifact["artifact_type"] - if artifact_type == "STDERR": - stderr_path = artifact_path - elif artifact_type == "STDOUT": - stdout_path = artifact_path + if artifact_type == "STDERR": + stderr_path = artifact_path + elif artifact_type == "STDOUT": + stdout_path = artifact_path - if stdout_path is not None and os.path.exists(stdout_path): - with open(stdout_path, encoding="utf-8") as f: - print(f.read(), file=sys.stdout, end="") - - if stderr_path is not None and os.path.exists(stderr_path): - with open(stderr_path, encoding="utf-8") as f: - print(f.read(), file=sys.stderr, end="") + if stdout_path is not None: + if not os.path.exists(stdout_path): + runner_logger.error( + f"stdout file {stdout_path} does not exist." + ) else: - log("Failed to open test run summary") - return_code = 254 - - log("Done!") + with open(stdout_path, encoding="utf-8", errors="ignore") as f: + runner_logger.info(f.read()) + if stderr_path is not None: + if not os.path.exists(stderr_path): + runner_logger.error( + f"stderr file {stderr_path} does not exist." + ) + else: + with open(stderr_path, encoding="utf-8", errors="ignore") as f: + runner_logger.error(f.read()) + runner_logger.info("Done!") return return_code def stop(self): @@ -730,65 +951,65 @@ class TestEnvironment: During cleanup, this function will stop the emulator, package server, and update server, then delete all temporary files. If an error is encountered while stopping any running processes, the temporary files will not be deleted. - Passing --delete-tmp will force the process to delete the files anyway. + Passing --cleanup will force the process to delete the files anyway. """ - self.log_debug("Reporting logs...") + self.env_logger.debug("Reporting logs...") # Print test log files - for test_dir in os.listdir(self.output_dir()): - log_path = os.path.join(self.output_dir(), test_dir, "log") - self.log_debug(f"\n---- Logs for test '{test_dir}' ----\n") + for test_dir in os.listdir(self.output_dir): + log_path = os.path.join(self.output_dir, test_dir, "log") + self.env_logger.debug(f"\n---- Logs for test '{test_dir}' ----\n") if os.path.exists(log_path): - with open(log_path, encoding="utf-8") as log: - self.log_debug(log.read()) + with open(log_path, encoding="utf-8", errors="ignore") as log: + self.env_logger.debug(log.read()) else: - self.log_debug("No logs found") + self.env_logger.debug("No logs found") # Print the emulator log - self.log_debug("\n---- Emulator logs ----\n") - if os.path.exists(self.emulator_log_path()): - with open(self.emulator_log_path(), encoding="utf-8") as log: - self.log_debug(log.read()) + self.env_logger.debug("\n---- Emulator logs ----\n") + if os.path.exists(self.emulator_log_path): + with open(self.emulator_log_path, encoding="utf-8") as log: + self.env_logger.debug(log.read()) else: - self.log_debug("No emulator logs found") + self.env_logger.debug("No emulator logs found") # Print the package server log - self.log_debug("\n---- Package server log ----\n") - if os.path.exists(self.package_server_log_path()): - with open(self.package_server_log_path(), encoding="utf-8") as log: - self.log_debug(log.read()) + self.env_logger.debug("\n---- Package server log ----\n") + if os.path.exists(self.package_server_log_path): + with open(self.package_server_log_path, encoding="utf-8") as log: + self.env_logger.debug(log.read()) else: - self.log_debug("No package server log found") + self.env_logger.debug("No package server log found") # Print the ffx daemon log - self.log_debug("\n---- ffx daemon log ----\n") - if os.path.exists(self.ffx_daemon_log_path()): - with open(self.ffx_daemon_log_path(), encoding="utf-8") as log: - self.log_debug(log.read()) + self.env_logger.debug("\n---- ffx daemon log ----\n") + if os.path.exists(self.ffx_daemon_log_path): + with open(self.ffx_daemon_log_path, encoding="utf-8") as log: + self.env_logger.debug(log.read()) else: - self.log_debug("No ffx daemon log found") + self.env_logger.debug("No ffx daemon log found") # Shut down the emulator - self.log_info("Stopping emulator...") - self.check_call( + self.env_logger.info("Stopping emulator...") + check_call_with_logging( [ self.tool_path("ffx"), "emu", "stop", ], env=self.ffx_cmd_env(), - stdout=self.subprocess_output(), - stderr=self.subprocess_output(), + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, ) # Stop ffx isolation - self.log_info("Stopping ffx isolation...") + self.env_logger.info("Stopping ffx isolation...") self.stop_ffx_isolation() - def delete_tmp(self): + def cleanup(self): # Remove temporary files - self.log_info("Deleting temporary files...") + self.env_logger.info("Deleting temporary files...") shutil.rmtree(self.tmp_dir(), ignore_errors=True) def debug(self, args): @@ -816,7 +1037,7 @@ class TestEnvironment: f"--symbol-path={self.rust_dir}/lib/rustlib/{self.target}/lib", ] - # Add rust source if it's available + # Add rust source if it's available rust_src_map = None if args.rust_src is not None: # This matches the remapped prefix used by compiletest. There's no @@ -908,21 +1129,24 @@ def start(args): def run(args): test_env = TestEnvironment.read_from_file() + test_env.setup_logging(log_to_file=True) return test_env.run(args) def stop(args): test_env = TestEnvironment.read_from_file() + test_env.setup_logging(log_to_file=False) test_env.stop() - if not args.no_delete: - test_env.delete_tmp() + if not args.no_cleanup: + test_env.cleanup() return 0 -def delete_tmp(args): +def cleanup(args): del args test_env = TestEnvironment.read_from_file() - test_env.delete_tmp() + test_env.setup_logging(log_to_file=False) + test_env.cleanup() return 0 @@ -934,6 +1158,7 @@ def debug(args): def syslog(args): test_env = TestEnvironment.read_from_file() + test_env.setup_logging(log_to_file=True) test_env.syslog(args) return 0 @@ -973,6 +1198,21 @@ def main(): help="the target platform to test", required=True, ) + start_parser.add_argument( + "--toolchain-dir", + help="the toolchain directory", + required=True, + ) + start_parser.add_argument( + "--local-product-bundle-path", + help="the path where the product-bundle should be downloaded to", + ) + start_parser.add_argument( + "--use-local-product-bundle-if-exists", + help="if the product bundle already exists in the local path, use " + "it instead of downloading it again", + action="store_true", + ) start_parser.set_defaults(func=start) run_parser = subparsers.add_parser( @@ -993,18 +1233,23 @@ def main(): "stop", help="shuts down and cleans up the testing environment" ) stop_parser.add_argument( - "--no-delete", + "--no-cleanup", default=False, action="store_true", help="don't delete temporary files after stopping", ) stop_parser.set_defaults(func=stop) - delete_parser = subparsers.add_parser( - "delete-tmp", + cleanup_parser = subparsers.add_parser( + "cleanup", help="deletes temporary files after the testing environment has been manually cleaned up", ) - delete_parser.set_defaults(func=delete_tmp) + cleanup_parser.set_defaults(func=cleanup) + + syslog_parser = subparsers.add_parser( + "syslog", help="prints the device syslog" + ) + syslog_parser.set_defaults(func=syslog) debug_parser = subparsers.add_parser( "debug", @@ -1033,9 +1278,6 @@ def main(): ) debug_parser.set_defaults(func=debug) - syslog_parser = subparsers.add_parser("syslog", help="prints the device syslog") - syslog_parser.set_defaults(func=syslog) - args = parser.parse_args() return args.func(args) diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh new file mode 100755 index 000000000000..da7b029ca732 --- /dev/null +++ b/src/ci/docker/scripts/rfl-build.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +set -euo pipefail + +LINUX_VERSION=c13320499ba0efd93174ef6462ae8a7a2933f6e7 + +# Build rustc, rustdoc and cargo +../x.py build --stage 1 library rustdoc +../x.py build --stage 0 cargo + +# Install rustup so that we can use the built toolchain easily, and also +# install bindgen in an easy way. +curl --proto '=https' --tlsv1.2 -sSf -o rustup.sh https://sh.rustup.rs +sh rustup.sh -y --default-toolchain none + +source /cargo/env + +BUILD_DIR=$(realpath ./build) +rustup toolchain link local "${BUILD_DIR}"/x86_64-unknown-linux-gnu/stage1 +rustup default local + +mkdir -p rfl +cd rfl + +# Remove existing directory to make local builds easier +rm -rf linux || true + +# Download Linux at a specific commit +mkdir -p linux +git -C linux init +git -C linux remote add origin https://github.com/torvalds/linux.git +git -C linux fetch --depth 1 origin ${LINUX_VERSION} +git -C linux checkout FETCH_HEAD + +# Install bindgen +"${BUILD_DIR}"/x86_64-unknown-linux-gnu/stage0/bin/cargo install \ + --version $(linux/scripts/min-tool-version.sh bindgen) \ + bindgen-cli + +# Configure Rust for Linux +cat < linux/kernel/configs/rfl-for-rust-ci.config +# CONFIG_WERROR is not set + +CONFIG_RUST=y + +CONFIG_SAMPLES=y +CONFIG_SAMPLES_RUST=y + +CONFIG_SAMPLE_RUST_MINIMAL=m +CONFIG_SAMPLE_RUST_PRINT=y + +CONFIG_RUST_PHYLIB_ABSTRACTIONS=y +CONFIG_AX88796B_PHY=y +CONFIG_AX88796B_RUST_PHY=y + +CONFIG_KUNIT=y +CONFIG_RUST_KERNEL_DOCTESTS=y +EOF + +make -C linux LLVM=1 -j$(($(nproc) + 1)) \ + rustavailable \ + defconfig \ + rfl-for-rust-ci.config + +make -C linux LLVM=1 -j$(($(nproc) + 1)) \ + samples/rust/rust_minimal.o \ + samples/rust/rust_print.o \ + drivers/net/phy/ax88796b_rust.o diff --git a/src/ci/docker/scripts/x86_64-gnu-llvm.sh b/src/ci/docker/scripts/x86_64-gnu-llvm.sh index 2eb751ca3766..b3921f114217 100755 --- a/src/ci/docker/scripts/x86_64-gnu-llvm.sh +++ b/src/ci/docker/scripts/x86_64-gnu-llvm.sh @@ -4,13 +4,7 @@ set -ex # Only run the stage 1 tests on merges, not on PR CI jobs. if [[ -z "${PR_CI_JOB}" ]]; then - # When running gcc backend tests, we need to install `libgccjit` and to not run llvm codegen - # tests as it will fail them. - if [[ "${ENABLE_GCC_CODEGEN}" == "1" ]]; then - ../x.py --stage 1 test --skip src/tools/tidy --skip tests/codegen - else - ../x.py --stage 1 test --skip src/tools/tidy - fi + ../x.py --stage 1 test --skip src/tools/tidy # Run the `mir-opt` tests again but this time for a 32-bit target. # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have @@ -23,16 +17,14 @@ if [[ -z "${PR_CI_JOB}" ]]; then # Run `ui-fulldeps` in `--stage=1`, which actually uses the stage0 # compiler, and is sensitive to the addition of new flags. ../x.py --stage 1 test tests/ui-fulldeps + + # The tests are run a second time with the size optimizations enabled. + ../x.py --stage 1 test library/std library/alloc library/core \ + --rustc-args "--cfg feature=\"optimize_for_size\"" fi -# When running gcc backend tests, we need to install `libgccjit` and to not run llvm codegen -# tests as it will fail them. # NOTE: intentionally uses all of `x.py`, `x`, and `x.ps1` to make sure they all work on Linux. -if [[ "${ENABLE_GCC_CODEGEN}" == "1" ]]; then - ../x.py --stage 2 test --skip src/tools/tidy --skip tests/codegen -else - ../x.py --stage 2 test --skip src/tools/tidy -fi +../x.py --stage 2 test --skip src/tools/tidy # Run the `mir-opt` tests again but this time for a 32-bit target. # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 04888dc09b50..4366a92fbcdf 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -33,11 +33,11 @@ runners: <<: *base-job - &job-aarch64-linux - os: [ self-hosted, ARM64, linux ] + os: ubuntu-22.04-arm64-8core-32gb envs: env-x86_64-apple-tests: &env-x86_64-apple-tests - SCRIPT: ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc --skip tests/run-make-fulldeps + SCRIPT: ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 MACOSX_DEPLOYMENT_TARGET: 10.12 @@ -50,8 +50,6 @@ envs: production: &production DEPLOY_BUCKET: rust-lang-ci2 - TOOLSTATE_ISSUES_API_URL: https://api.github.com/repos/rust-lang/rust/issues - TOOLSTATE_PUBLISH: 1 # AWS_SECRET_ACCESS_KEYs are stored in GitHub's secrets storage, named # AWS_SECRET_ACCESS_KEY_. Including the key id in the name allows to # rotate them in a single branch while keeping the old key in another @@ -60,9 +58,16 @@ envs: CACHES_AWS_ACCESS_KEY_ID: AKIA46X5W6CZI5DHEBFL ARTIFACTS_AWS_ACCESS_KEY_ID: AKIA46X5W6CZN24CBO55 AWS_REGION: us-west-1 + TOOLSTATE_PUBLISH: 1 try: <<: *production + # The following env var activates faster `try` builds in `opt-dist` by, e.g. + # - building only the more commonly useful components (we rarely need e.g. rust-docs in try + # builds) + # - not running `opt-dist`'s post-optimization smoke tests on the resulting toolchain + # + # If you *want* these to happen however, temporarily uncomment it before triggering a try build. DIST_TRY_BUILD: 1 auto: @@ -108,66 +113,66 @@ auto: <<: *job-aarch64-linux - image: arm-android - <<: *job-linux-8c + <<: *job-linux-4c - image: armhf-gnu - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-aarch64-linux env: CODEGEN_BACKENDS: llvm,cranelift - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-android - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-arm-linux - <<: *job-linux-16c + <<: *job-linux-8c - image: dist-armhf-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-armv7-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-i586-gnu-i586-i686-musl - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-i686-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-loongarch64-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-ohos - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-powerpc-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-powerpc64-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-powerpc64le-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-riscv64-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-s390x-linux - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-various-1 - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-various-2 - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-x86_64-freebsd - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-x86_64-illumos - <<: *job-linux-8c + <<: *job-linux-4c - image: dist-x86_64-linux env: @@ -186,7 +191,7 @@ auto: <<: *job-linux-8c - image: dist-x86_64-netbsd - <<: *job-linux-8c + <<: *job-linux-4c - image: i686-gnu <<: *job-linux-8c @@ -198,7 +203,7 @@ auto: <<: *job-linux-4c - image: test-various - <<: *job-linux-8c + <<: *job-linux-4c - image: x86_64-gnu <<: *job-linux-4c @@ -229,7 +234,7 @@ auto: <<: *job-linux-8c - image: x86_64-gnu-debug - <<: *job-linux-8c + <<: *job-linux-4c - image: x86_64-gnu-distcheck <<: *job-linux-8c @@ -289,7 +294,7 @@ auto: - image: x86_64-apple-2 env: - SCRIPT: ./x.py --stage 2 test tests/ui tests/rustdoc tests/run-make-fulldeps + SCRIPT: ./x.py --stage 2 test tests/ui tests/rustdoc <<: *env-x86_64-apple-tests <<: *job-macos-xl @@ -380,19 +385,15 @@ auto: # We are intentionally allowing an old toolchain on this builder (and that's # incompatible with LLVM downloads today). NO_DOWNLOAD_CI_LLVM: 1 - CUSTOM_MINGW: 1 <<: *job-windows-8c - image: x86_64-mingw env: SCRIPT: make ci-mingw - RUST_CONFIGURE_ARGS: >- - --build=x86_64-pc-windows-gnu - --enable-profiler + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu # We are intentionally allowing an old toolchain on this builder (and that's # incompatible with LLVM downloads today). NO_DOWNLOAD_CI_LLVM: 1 - CUSTOM_MINGW: 1 <<: *job-windows-8c - image: dist-x86_64-msvc @@ -425,6 +426,7 @@ auto: RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-msvc --host=aarch64-pc-windows-msvc + --target=aarch64-pc-windows-msvc,arm64ec-pc-windows-msvc --enable-full-tools --enable-profiler SCRIPT: python x.py dist bootstrap --include-default-paths @@ -436,12 +438,10 @@ auto: RUST_CONFIGURE_ARGS: >- --build=i686-pc-windows-gnu --enable-full-tools - --enable-profiler # We are intentionally allowing an old toolchain on this builder (and that's # incompatible with LLVM downloads today). NO_DOWNLOAD_CI_LLVM: 1 SCRIPT: python x.py dist bootstrap --include-default-paths - CUSTOM_MINGW: 1 DIST_REQUIRE_ALL_TOOLS: 1 <<: *job-windows-8c @@ -451,11 +451,9 @@ auto: RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-gnu --enable-full-tools - --enable-profiler # We are intentionally allowing an old toolchain on this builder (and that's # incompatible with LLVM downloads today). NO_DOWNLOAD_CI_LLVM: 1 - CUSTOM_MINGW: 1 DIST_REQUIRE_ALL_TOOLS: 1 <<: *job-windows-8c @@ -464,3 +462,8 @@ auto: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler SCRIPT: python x.py dist bootstrap --include-default-paths <<: *job-windows-8c + + # Tests integration with Rust for Linux. + # Builds stage 1 compiler and tries to compile a few RfL examples with it. + - image: rfl + <<: *job-linux-8c diff --git a/src/ci/publish_toolstate.sh b/src/ci/publish_toolstate.sh index 691df04e754a..e828365c416f 100755 --- a/src/ci/publish_toolstate.sh +++ b/src/ci/publish_toolstate.sh @@ -24,8 +24,8 @@ cd rust-toolstate FAILURE=1 for RETRY_COUNT in 1 2 3 4 5; do # The purpose of this is to publish the new "current" toolstate in the toolstate repo. - # This happens post-landing, on master. - # (Publishing the per-commit test results happens pre-landing in src/bootstrap/toolstate.rs). + # This happens at the end of auto builds. + # (Publishing the per-commit test results happens in src/bootstrap/toolstate.rs). "$(ciCheckoutPath)/src/tools/publish_toolstate.py" "$GIT_COMMIT" \ "$GIT_COMMIT_MSG" \ "$MESSAGE_FILE" \ diff --git a/src/ci/run.sh b/src/ci/run.sh index 3ad04c73d3da..efaf70078c4a 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -89,6 +89,8 @@ RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --dist-compression-formats=xz" # (to avoid spending a lot of time cloning llvm) if [ "$EXTERNAL_LLVM" = "" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.optimized-compiler-builtins" + # Likewise, only demand we test all LLVM components if we know we built LLVM with them + export COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS=1 elif [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then echo "error: dist builds should always use optimized compiler-rt!" >&2 exit 1 @@ -169,12 +171,6 @@ else fi fi -# Unless we're using an older version of LLVM, check that all LLVM components -# used by tests are available. -if [ "$IS_NOT_LATEST_LLVM" = "" ]; then - export COMPILETEST_NEEDS_ALL_LLVM_COMPONENTS=1 -fi - if [ "$ENABLE_GCC_CODEGEN" = "1" ]; then # If `ENABLE_GCC_CODEGEN` is set and not empty, we add the `--enable-new-symbol-mangling` # argument to `RUST_CONFIGURE_ARGS` and set the `GCC_EXEC_PREFIX` environment variable. diff --git a/src/ci/scripts/install-mingw.sh b/src/ci/scripts/install-mingw.sh index 87b835b63db5..31aa3785bc36 100755 --- a/src/ci/scripts/install-mingw.sh +++ b/src/ci/scripts/install-mingw.sh @@ -1,8 +1,5 @@ #!/bin/bash -# If we need to download a custom MinGW, do so here and set the path -# appropriately. -# -# Otherwise install MinGW through `pacman` +# For mingw builds use a vendored mingw. set -euo pipefail IFS=$'\n\t' @@ -12,23 +9,20 @@ source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" MINGW_ARCHIVE_32="i686-12.2.0-release-posix-dwarf-rt_v10-rev0.7z" MINGW_ARCHIVE_64="x86_64-12.2.0-release-posix-seh-rt_v10-rev0.7z" -if isWindows; then +if isWindows && isKnownToBeMingwBuild; then case "${CI_JOB_NAME}" in *i686*) bits=32 - arch=i686 mingw_archive="${MINGW_ARCHIVE_32}" ;; *x86_64*) bits=64 - arch=x86_64 mingw_archive="${MINGW_ARCHIVE_64}" ;; *aarch64*) # aarch64 is a cross-compiled target. Use the x86_64 # mingw, since that's the host architecture. bits=64 - arch=x86_64 mingw_archive="${MINGW_ARCHIVE_64}" ;; *) @@ -38,16 +32,9 @@ if isWindows; then ;; esac - if [[ "${CUSTOM_MINGW:-0}" == 0 ]]; then - pacboy -S --noconfirm toolchain:p - # According to the comment in the Windows part of install-clang.sh, in the future we might - # want to do this instead: - # pacboy -S --noconfirm clang:p ... - else - mingw_dir="mingw${bits}" + mingw_dir="mingw${bits}" - curl -o mingw.7z "${MIRRORS_BASE}/${mingw_archive}" - 7z x -y mingw.7z > /dev/null - ciCommandAddPath "$(pwd)/${mingw_dir}/bin" - fi + curl -o mingw.7z "${MIRRORS_BASE}/${mingw_archive}" + 7z x -y mingw.7z > /dev/null + ciCommandAddPath "$(pwd)/${mingw_dir}/bin" fi diff --git a/src/ci/scripts/install-msys2.sh b/src/ci/scripts/install-msys2.sh deleted file mode 100755 index 2ae782356047..000000000000 --- a/src/ci/scripts/install-msys2.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash -# Clean up and prepare the MSYS2 installation. MSYS2 is needed primarily for -# the test suite (run-make), but is also used by the MinGW toolchain for assembling things. - -set -euo pipefail -IFS=$'\n\t' - -source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" -if isWindows; then - # Detect the native Python version installed on the agent. On GitHub - # Actions, the C:\hostedtoolcache\windows\Python directory contains a - # subdirectory for each installed Python version. - # - # The -V flag of the sort command sorts the input by version number. - native_python_version="$(ls /c/hostedtoolcache/windows/Python | sort -Vr | head -n 1)" - - # 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. - python_home="/c/hostedtoolcache/windows/Python/${native_python_version}/x64" - if ! [[ -f "${python_home}/python3.exe" ]]; then - cp "${python_home}/python.exe" "${python_home}/python3.exe" - fi - ciCommandAddPath "C:\\hostedtoolcache\\windows\\Python\\${native_python_version}\\x64" - ciCommandAddPath "C:\\hostedtoolcache\\windows\\Python\\${native_python_version}\\x64\\Scripts" - - # Install pacboy for easily installing packages - pacman -S --noconfirm pactoys - - # Remove these pre-installed tools so we can't accidentally use them, because we are using the - # MSYS2 setup action versions instead. Because `rm -r`-ing them is slow, we mv them off path - # instead. - # Remove pre-installed version of MSYS2 - echo "Cleaning up existing tools in PATH" - notpath="/c/NOT/ON/PATH/" - mkdir --parents "$notpath" - mv -t "$notpath" "/c/msys64/" - # Remove Strawberry Perl, which contains a version of mingw - mv -t "$notpath" "/c/Strawberry/" - # Remove these other copies of mingw, I don't even know where they come from. - mv -t "$notpath" "/c/mingw64/" - mv -t "$notpath" "/c/mingw32/" - echo "Finished cleaning up tools in PATH" - - if isKnownToBeMingwBuild; then - # Use the mingw version of CMake for mingw builds. - # However, the MSVC build needs native CMake, as it fails with the mingw one. - # Delete native CMake - rm -r "/c/Program Files/CMake/" - # Install mingw-w64-$arch-cmake - pacboy -S --noconfirm cmake:p - - # It would be nice to use MSYS's git in MinGW builds so that it's tested and known to - # work. But it makes everything extremely slow, so it's commented out for now. - # # Delete Windows-Git - # rm -r "/c/Program Files/Git/" - # # Install MSYS2 git - # pacman -S --noconfirm git - fi -fi diff --git a/src/ci/scripts/verify-line-endings.sh b/src/ci/scripts/verify-line-endings.sh index f3cac13ea480..5f4b4aeb0e4e 100755 --- a/src/ci/scripts/verify-line-endings.sh +++ b/src/ci/scripts/verify-line-endings.sh @@ -4,21 +4,21 @@ # 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 +# print out the git configuration so we can better investigate failures in +# the following +git config --list --show-origin +# -U is necessary on Windows to stop grep automatically converting the line ending +endings=$(grep -Ul $(printf '\r$') Cargo.lock src/tools/cargo/Cargo.lock) || true +# if endings has non-zero length, error out +if [[ -n $endings ]]; then + echo "Error: found DOS line endings" + # Print the files with DOS line endings + echo "$endings" + exit 1 fi diff --git a/src/doc/book b/src/doc/book index bebcf527e677..45c1a6d69edf 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit bebcf527e67755a989a1739b7cfaa8f0e6b30040 +Subproject commit 45c1a6d69edfd1fc91fb7504cb73958dbd09441e diff --git a/src/doc/edition-guide b/src/doc/edition-guide index 0c68e90acaae..cb58c430b4e8 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit 0c68e90acaae5a611f8f5098a3c2980de9845ab2 +Subproject commit cb58c430b4e8054c2cb81d2d4434092c482a93d8 diff --git a/src/doc/embedded-book b/src/doc/embedded-book index 17842ebb050f..b10c6acaf0f4 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit 17842ebb050f62e40a4618edeb8e8ee86e758707 +Subproject commit b10c6acaf0f43481f6600e95d4b5013446e29f7a diff --git a/src/doc/nomicon b/src/doc/nomicon index 0d5f88475fe2..0ebdacadbda8 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 0d5f88475fe285affa6dbbc806e9e44d730797c0 +Subproject commit 0ebdacadbda8ce2cd8fbf93985e15af61a7ab895 diff --git a/src/doc/reference b/src/doc/reference index 51817951d0d2..0b805c658040 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 51817951d0d213a0011f82b62aae02c3b3f2472e +Subproject commit 0b805c65804019b0ac8f2fe3117afad82a6069b8 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 229ad13b64d9..b1d97bd6113a 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 229ad13b64d919b12e548d560f06d88963b25cd3 +Subproject commit b1d97bd6113aba732b2091ce093c76f2d05bb8a0 diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index 2d1947ff34d5..aec82168dd31 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit 2d1947ff34d50ca46dfe242ad75531a4c429bb52 +Subproject commit aec82168dd3121289a194b381f56076fc789a4d2 diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index c7e3293e35ad..e76ebb8f8aa8 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -16,20 +16,29 @@ - [Platform Support](platform-support.md) - [Target Tier Policy](target-tier-policy.md) - [Template for Target-specific Documentation](platform-support/TEMPLATE.md) - - [arm64e-apple-ios.md](platform-support/arm64e-apple-ios.md) - - [arm64e-apple-darwin.md](platform-support/arm64e-apple-darwin.md) - - [aarch64-apple-ios-sim](platform-support/aarch64-apple-ios-sim.md) - [arm64ec-pc-windows-msvc](platform-support/arm64ec-pc-windows-msvc.md) + - [\*-apple-darwin](platform-support/apple-darwin.md) + - [i686-apple-darwin](platform-support/i686-apple-darwin.md) + - [x86_64h-apple-darwin](platform-support/x86_64h-apple-darwin.md) + - [arm64e-apple-darwin](platform-support/arm64e-apple-darwin.md) + - [\*-apple-ios](platform-support/apple-ios.md) + - [\*-apple-ios-macabi](platform-support/apple-ios-macabi.md) + - [arm64e-apple-ios](platform-support/arm64e-apple-ios.md) - [\*-apple-tvos](platform-support/apple-tvos.md) - - [\*-apple-watchos\*](platform-support/apple-watchos.md) - - [aarch64-apple-visionos\*](platform-support/apple-visionos.md) + - [\*-apple-watchos](platform-support/apple-watchos.md) + - [\*-apple-visionos](platform-support/apple-visionos.md) - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md) - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md) - [arm-none-eabi](platform-support/arm-none-eabi.md) - - [armv4t-none-eabi](platform-support/armv4t-none-eabi.md) - - [armv5te-none-eabi](platform-support/armv5te-none-eabi.md) - - [armv7r-none-eabi](platform-support/armv7r-none-eabi.md) - - [armv8r-none-eabihf](platform-support/armv8r-none-eabihf.md) + - [armv4t-none-eabi](platform-support/armv4t-none-eabi.md) + - [armv5te-none-eabi](platform-support/armv5te-none-eabi.md) + - [armv7r-none-eabi](platform-support/armv7r-none-eabi.md) + - [armv8r-none-eabihf](platform-support/armv8r-none-eabihf.md) + - [thumbv6m-none-eabi](./platform-support/thumbv6m-none-eabi.md) + - [thumbv7em-none-eabi\*](./platform-support/thumbv7em-none-eabi.md) + - [thumbv7m-none-eabi](./platform-support/thumbv7m-none-eabi.md) + - [thumbv8m.base-none-eabi](./platform-support/thumbv8m.base-none-eabi.md) + - [thumbv8m.main-none-eabi\*](./platform-support/thumbv8m.main-none-eabi.md) - [armv6k-nintendo-3ds](platform-support/armv6k-nintendo-3ds.md) - [armv7-sony-vita-newlibeabihf](platform-support/armv7-sony-vita-newlibeabihf.md) - [armv7-unknown-linux-uclibceabi](platform-support/armv7-unknown-linux-uclibceabi.md) @@ -56,17 +65,13 @@ - [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md) - [riscv32*-unknown-none-elf](platform-support/riscv32-unknown-none-elf.md) - [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md) - - [thumbv6m-none-eabi](./platform-support/thumbv6m-none-eabi.md) - - [thumbv7m-none-eabi](./platform-support/thumbv7m-none-eabi.md) - - [thumbv7em-none-eabi\*](./platform-support/thumbv7em-none-eabi.md) - - [thumbv8m.base-none-eabi](./platform-support/thumbv8m.base-none-eabi.md) - - [thumbv8m.main-none-eabi\*](./platform-support/thumbv8m.main-none-eabi.md) - [*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) - [\*-nto-qnx-\*](platform-support/nto-qnx.md) - [*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md) - [*-unknown-hermit](platform-support/hermit.md) - [\*-unknown-netbsd\*](platform-support/netbsd.md) - [*-unknown-openbsd](platform-support/openbsd.md) + - [*-unknown-redox](platform-support/redox.md) - [\*-unknown-uefi](platform-support/unknown-uefi.md) - [wasm32-wasip1](platform-support/wasm32-wasip1.md) - [wasm32-wasip1-threads](platform-support/wasm32-wasip1-threads.md) @@ -76,7 +81,6 @@ - [x86_64-fortanix-unknown-sgx](platform-support/x86_64-fortanix-unknown-sgx.md) - [x86_64-unknown-linux-none.md](platform-support/x86_64-unknown-linux-none.md) - [x86_64-unknown-none](platform-support/x86_64-unknown-none.md) - - [x86_64h-apple-darwin](platform-support/x86_64h-apple-darwin.md) - [Targets](targets/index.md) - [Built-in Targets](targets/built-in.md) - [Custom Targets](targets/custom.md) @@ -84,7 +88,8 @@ - [Profile-guided Optimization](profile-guided-optimization.md) - [Instrumentation-based Code Coverage](instrument-coverage.md) - [Linker-plugin-based LTO](linker-plugin-lto.md) -- [Checking conditional configurations](check-cfg.md) +- [Checking Conditional Configurations](check-cfg.md) + - [Cargo Specifics](check-cfg/cargo-specifics.md) - [Exploit Mitigations](exploit-mitigations.md) - [Symbol Mangling](symbol-mangling/index.md) - [v0 Symbol Format](symbol-mangling/v0.md) diff --git a/src/doc/rustc/src/check-cfg.md b/src/doc/rustc/src/check-cfg.md index 8e39adaa4385..dfc4871b924d 100644 --- a/src/doc/rustc/src/check-cfg.md +++ b/src/doc/rustc/src/check-cfg.md @@ -11,8 +11,8 @@ development process. In order to accomplish that goal, `rustc` accepts the `--check-cfg` flag, which specifies whether to check conditions and how to check them. -> **Note:** No implicit expectation is added when using `--cfg`. Users are expected to -pass all expected names and values using the _check cfg specification_. +> **Note:** For interacting with this through Cargo, +see [Cargo Specifics](check-cfg/cargo-specifics.md) page. [^reachable]: `rustc` promises to at least check reachable `#[cfg]`, and while non-reachable `#[cfg]` are not currently checked, they may well be checked in the future without it being a @@ -23,6 +23,9 @@ breaking change. To specify expected names and values, the _check cfg specification_ provides the `cfg(...)` option which enables specifying for an expected config name and it's expected values. +> **Note:** No implicit expectation is added when using `--cfg`. Users are expected to +pass all expected names and values using the _check cfg specification_. + It has this basic form: ```bash diff --git a/src/doc/rustc/src/check-cfg/cargo-specifics.md b/src/doc/rustc/src/check-cfg/cargo-specifics.md new file mode 100644 index 000000000000..bd4bebbc874a --- /dev/null +++ b/src/doc/rustc/src/check-cfg/cargo-specifics.md @@ -0,0 +1,73 @@ +# Cargo Specifics - Checking Conditional Configurations + + + +This document is intended to summarize the principal ways Cargo interacts with +the `unexpected_cfgs` lint and `--check-cfg` flag. It is not intended to provide +individual details, for that refer to the [`--check-cfg` documentation](../check-cfg.md) and +to the [Cargo book](../../cargo/index.html). + +## Cargo feature + +*See the [`[features]` section in the Cargo book][cargo-features] for more details.* + +With the `[features]` table, Cargo provides a mechanism to express conditional compilation and +optional dependencies. Cargo *automatically* declares corresponding cfgs for every feature as +expected. + +`Cargo.toml`: +```toml +[features] +serde = ["dep:serde"] +my_feature = [] +``` + +[cargo-features]: ../../cargo/reference/features.html + +## `check-cfg` in `[lints.rust]` table + + + +*See the [`[lints]` section in the Cargo book][cargo-lints-table] for more details.* + +When using a statically known custom config (i.e., not dependent on a build-script), Cargo provides +the custom lint config `check-cfg` under `[lints.rust.unexpected_cfgs]`. + +It can be used to set custom static [`--check-cfg`](../check-cfg.md) args, it is mainly useful when +the list of expected cfgs is known in advance. + +`Cargo.toml`: +```toml +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(has_foo)'] } +``` + +[cargo-lints-table]: ../../cargo/reference/manifest.html#the-lints-section + +## `cargo::rustc-check-cfg` for `build.rs`/build-script + +*See the [`cargo::rustc-check-cfg` section in the Cargo book][cargo-rustc-check-cfg] for more details.* + +When setting a custom config with [`cargo::rustc-cfg`][cargo-rustc-cfg], Cargo provides the +corollary instruction: [`cargo::rustc-check-cfg`][cargo-rustc-check-cfg] to expect custom configs. + +`build.rs`: +```rust,ignore (cannot-test-this-because-has_foo-isnt-declared) +fn main() { + println!("cargo::rustc-check-cfg=cfg(has_foo)"); + // ^^^^^^^^^^^^^^^^^^^^^^ new with Cargo 1.80 + if has_foo() { + println!("cargo::rustc-cfg=has_foo"); + } +} +``` + +[cargo-rustc-cfg]: ../../cargo/reference/build-scripts.html#rustc-cfg +[cargo-rustc-check-cfg]: ../../cargo/reference/build-scripts.html#rustc-check-cfg diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 3e1ac2fa8ad7..72ab8ab5ce9b 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -213,21 +213,39 @@ The valid emit kinds are: `CRATE_NAME.o`. The output filename can be set with the [`-o` flag](#option-o-output). A -suffix may be added to the filename with the [`-C extra-filename` -flag](codegen-options/index.md#extra-filename). The files are written to the -current directory unless the [`--out-dir` flag](#option-out-dir) is used. Each -emission type may also specify the output filename with the form `KIND=PATH`, -which takes precedence over the `-o` flag. -Specifying `-o -` or `--emit KIND=-` asks rustc to emit to stdout. -Text output types (`asm`, `dep-info`, `llvm-ir` and `mir`) can be written to -stdout despite it being a tty or not. This will result in an error if any -binary output type is written to stdout that is a tty. -This will also result in an error if multiple output types -would be written to stdout, because they would be all mixed together. +suffix may be added to the filename with the +[`-C extra-filename` flag](codegen-options/index.md#extra-filename). + +Output files are written to the current directory unless the +[`--out-dir` flag](#option-out-dir) is used. [LLVM bitcode]: https://llvm.org/docs/BitCodeFormat.html [LLVM IR]: https://llvm.org/docs/LangRef.html +### Custom paths for individual emit kinds + +Each emit type can optionally be followed by `=` to specify an explicit output +path that only applies to the output of that type. For example: + +- `--emit=link,dep-info=/path/to/dep-info.d` + - Emit the crate itself as normal, + and also emit dependency info to the specified path. +- `--emit=llvm-ir=-,mir` + - Emit MIR to the default filename (based on crate name), + and emit LLVM IR to stdout. + +### Emitting to stdout + +When using `--emit` or [`-o`](#option-o-output), output can be sent to stdout +by specifying `-` as the path (e.g. `-o -`). + +Binary output types can only be written to stdout if it is not a tty. +Text output types (`asm`, `dep-info`, `llvm-ir` and `mir`) can be written to +stdout regardless of whether it is a tty or not. + +Only one type of output can be written to stdout. Attempting to write multiple +types to stdout at the same time will result in an error. + ## `--print`: print compiler information diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index 32dc992c42fa..ed091d8fc571 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -49,12 +49,6 @@ One option for a Rust demangler is [`rustfilt`], which can be installed with: cargo install rustfilt ``` -Another option, if you are building from the Rust compiler source distribution, is to use the `rust-demangler` tool included in the Rust source distribution, which can be built with: - -```shell -$ ./x.py build rust-demangler -``` - [`rustfilt`]: https://crates.io/crates/rustfilt ## Compiling with coverage enabled @@ -164,7 +158,7 @@ $ llvm-cov show -Xdemangler=rustfilt target/debug/examples/formatjson5 \ Some of the more notable options in this example include: -- `--Xdemangler=rustfilt` - the command name or path used to demangle Rust symbols (`rustfilt` in the example, but this could also be a path to the `rust-demangler` tool) +- `--Xdemangler=rustfilt` - the command name or path used to demangle Rust symbols (`rustfilt` in the example) - `target/debug/examples/formatjson5` - the instrumented binary (from which to extract the coverage map) - `--instr-profile=.profdata` - the location of the `.profdata` file created by `llvm-profdata merge` (from the `.profraw` file generated by the instrumented binary) - `--name=` - to show coverage for a specific function (or, consider using another filter option, such as `--name-regex=`) diff --git a/src/doc/rustc/src/json.md b/src/doc/rustc/src/json.md index 32083b2f731d..c853f34ee036 100644 --- a/src/doc/rustc/src/json.md +++ b/src/doc/rustc/src/json.md @@ -217,7 +217,8 @@ Diagnostics have the following format: 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. +flag][option-emit] documentation. Notifications can contain more than one file +for each type, for example when using multiple codegen units. ```javascript { @@ -229,6 +230,11 @@ flag][option-emit] documentation. - "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. + - "asm": The `.s` file with generated assembly + - "llvm-ir": The `.ll` file with generated textual LLVM IR + - "llvm-bc": The `.bc` file with generated LLVM bitcode + - "mir": The `.mir` file with rustc's mid-level intermediate representation. + - "obj": The `.o` file with generated native object code */ "emit": "link" } diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 8adc410455ed..834e909c0654 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -33,12 +33,12 @@ All tier 1 targets with host tools support the full standard library. target | notes -------|------- `aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1, glibc 2.17+) -`i686-pc-windows-gnu` | 32-bit MinGW (Windows 10+) [^x86_32-floats-return-ABI] -`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+) [^x86_32-floats-return-ABI] +`i686-pc-windows-gnu` | 32-bit MinGW (Windows 10+, Windows Server 2016+) [^x86_32-floats-return-ABI] +`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+) [^x86_32-floats-return-ABI] `i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+) [^x86_32-floats-return-ABI] -`x86_64-apple-darwin` | 64-bit macOS (10.12+, Sierra+) -`x86_64-pc-windows-gnu` | 64-bit MinGW (Windows 10+) -`x86_64-pc-windows-msvc` | 64-bit MSVC (Windows 10+) +[`x86_64-apple-darwin`](platform-support/apple-darwin.md) | 64-bit macOS (10.12+, Sierra+) +`x86_64-pc-windows-gnu` | 64-bit MinGW (Windows 10+, Windows Server 2016+) +`x86_64-pc-windows-msvc` | 64-bit MSVC (Windows 10+, Windows Server 2016+) `x86_64-unknown-linux-gnu` | 64-bit Linux (kernel 3.2+, glibc 2.17+) [^x86_32-floats-return-ABI]: Due to limitations of the C ABI, floating-point support on `i686` targets is non-compliant: floating-point return values are passed via an x87 register, so NaN payload bits can be lost. See [issue #114479][x86-32-float-issue]. @@ -86,7 +86,7 @@ so Rustup may install the documentation for a similar tier 1 target instead. target | notes -------|------- -`aarch64-apple-darwin` | ARM64 macOS (11.0+, Big Sur+) +[`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+) `aarch64-pc-windows-msvc` | ARM64 Windows MSVC `aarch64-unknown-linux-musl` | ARM64 Linux with musl 1.2.3 `arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2, glibc 2.17) @@ -133,8 +133,8 @@ so Rustup may install the documentation for a similar tier 1 target instead. target | std | notes -------|:---:|------- -`aarch64-apple-ios` | ✓ | ARM64 iOS -[`aarch64-apple-ios-sim`](platform-support/aarch64-apple-ios-sim.md) | ✓ | Apple iOS Simulator on ARM64 +[`aarch64-apple-ios`](platform-support/apple-ios.md) | ✓ | ARM64 iOS +[`aarch64-apple-ios-sim`](platform-support/apple-ios.md) | ✓ | Apple iOS Simulator on ARM64 `aarch64-fuchsia` | ✓ | Alias for `aarch64-unknown-fuchsia` [`aarch64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | ARM64 Fuchsia [`aarch64-linux-android`](platform-support/android.md) | ✓ | ARM64 Android @@ -146,6 +146,7 @@ target | std | notes [`arm-linux-androideabi`](platform-support/android.md) | ✓ | Armv6 Android `arm-unknown-linux-musleabi` | ✓ | Armv6 Linux with musl 1.2.3 `arm-unknown-linux-musleabihf` | ✓ | Armv6 Linux with musl 1.2.3, hardfloat +[`arm64ec-pc-windows-msvc`](platform-support/arm64ec-pc-windows-msvc.md) | ✓ | Arm64EC Windows MSVC [`armebv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian [`armebv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian, hardfloat `armv5te-unknown-linux-gnueabi` | ✓ | Armv5TE Linux (kernel 4.4, glibc 2.23) @@ -192,7 +193,7 @@ target | std | notes `wasm32-wasi` | ✓ | WebAssembly with WASI (undergoing a [rename to `wasm32-wasip1`][wasi-rename]) [`wasm32-wasip1`](platform-support/wasm32-wasip1.md) | ✓ | WebAssembly with WASI [`wasm32-wasip1-threads`](platform-support/wasm32-wasip1-threads.md) | ✓ | | WebAssembly with WASI Preview 1 and threads -`x86_64-apple-ios` | ✓ | 64-bit x86 iOS +[`x86_64-apple-ios`](platform-support/apple-ios.md) | ✓ | 64-bit x86 iOS [`x86_64-fortanix-unknown-sgx`](platform-support/x86_64-fortanix-unknown-sgx.md) | ✓ | [Fortanix ABI] for 64-bit Intel SGX `x86_64-fuchsia` | ✓ | Alias for `x86_64-unknown-fuchsia` [`x86_64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | 64-bit x86 Fuchsia @@ -202,7 +203,7 @@ target | std | notes `x86_64-unknown-linux-gnux32` | ✓ | 64-bit Linux (x32 ABI) (kernel 4.15, glibc 2.27) [`x86_64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | x86_64 OpenHarmony [`x86_64-unknown-none`](platform-support/x86_64-unknown-none.md) | * | Freestanding/bare-metal x86_64, softfloat -`x86_64-unknown-redox` | ✓ | Redox OS +[`x86_64-unknown-redox`](platform-support/redox.md) | ✓ | Redox OS [`x86_64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 64-bit UEFI [^x86_32-floats-x87]: Floating-point support on `i586` targets is non-compliant: the `x87` registers and instructions used for these targets do not provide IEEE-754-compliant behavior, in particular when it comes to rounding and NaN payload bits. See [issue #114479][x86-32-float-issue]. @@ -240,10 +241,9 @@ target | std | host | notes -------|:---:|:----:|------- [`arm64e-apple-ios`](platform-support/arm64e-apple-ios.md) | ✓ | | ARM64e Apple iOS [`arm64e-apple-darwin`](platform-support/arm64e-apple-darwin.md) | ✓ | ✓ | ARM64e Apple Darwin -[`arm64ec-pc-windows-msvc`](platform-support/arm64ec-pc-windows-msvc.md) | ? | | Arm64EC Windows MSVC -`aarch64-apple-ios-macabi` | ? | | Apple Catalyst on ARM64 -[`aarch64-apple-tvos`](platform-support/apple-tvos.md) | ? | | ARM64 tvOS -[`aarch64-apple-tvos-sim`](platform-support/apple-tvos.md) | ? | | ARM64 tvOS Simulator +[`aarch64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | | Apple Catalyst on ARM64 +[`aarch64-apple-tvos`](platform-support/apple-tvos.md) | ✓ | | ARM64 tvOS +[`aarch64-apple-tvos-sim`](platform-support/apple-tvos.md) | ✓ | | ARM64 tvOS Simulator [`aarch64-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | ARM64 Apple WatchOS [`aarch64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | ARM64 Apple WatchOS Simulator [`aarch64-apple-visionos`](platform-support/apple-visionos.md) | ✓ | | ARM64 Apple visionOS @@ -258,8 +258,8 @@ target | std | host | notes `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI) [`aarch64-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD [`aarch64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | ARM64 OpenBSD -`aarch64-unknown-redox` | ? | | ARM64 Redox OS -`aarch64-uwp-windows-msvc` | ? | | +[`aarch64-unknown-redox`](platform-support/redox.md) | ✓ | | ARM64 Redox OS +`aarch64-uwp-windows-msvc` | ✓ | | `aarch64-wrs-vxworks` | ? | | `aarch64_be-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (big-endian, ILP32 ABI) `aarch64_be-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (big-endian) @@ -283,7 +283,7 @@ target | std | host | notes [`armv7a-kmc-solid_asp3-eabihf`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3, hardfloat [`armv7a-none-eabihf`](platform-support/arm-none-eabi.md) | * | | Bare Armv7-A, hardfloat [`armv7k-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | Armv7-A Apple WatchOS -`armv7s-apple-ios` | ✓ | | Armv7-A Apple-A6 Apple iOS +[`armv7s-apple-ios`](platform-support/apple-ios.md) | ✓ | | Armv7-A Apple-A6 Apple iOS [`armv8r-none-eabihf`](platform-support/armv8r-none-eabihf.md) | * | | Bare Armv8-R, hardfloat `avr-unknown-gnu-atmega328` | * | | AVR. Requires `-Z build-std=core` `bpfeb-unknown-none` | * | | BPF (big endian) @@ -292,16 +292,17 @@ target | std | host | notes `csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian) [`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | | Bare Hexagon (v60+, HVX) [`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux with musl 1.2.3 -`i386-apple-ios` | ✓ | | 32-bit x86 iOS [^x86_32-floats-return-ABI] +[`i386-apple-ios`](platform-support/apple-ios.md) | ✓ | | 32-bit x86 iOS [^x86_32-floats-return-ABI] [`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS [^x86_32-floats-return-ABI] [`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | | 32-bit x86, restricted to Pentium -`i686-apple-darwin` | ✓ | ✓ | 32-bit macOS (10.12+, Sierra+) [^x86_32-floats-return-ABI] +[`i686-apple-darwin`](platform-support/apple-darwin.md) | ✓ | ✓ | 32-bit macOS (10.12+, Sierra+) [^x86_32-floats-return-ABI] `i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku [^x86_32-floats-return-ABI] [`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd [^x86_32-floats-return-ABI] [`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 with SSE2 [^x86_32-floats-return-ABI] [`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD [^x86_32-floats-return-ABI] -`i686-uwp-windows-gnu` | ? | | [^x86_32-floats-return-ABI] -`i686-uwp-windows-msvc` | ? | | [^x86_32-floats-return-ABI] +[`i686-unknown-redox`](platform-support/redox.md) | ✓ | | i686 Redox OS +`i686-uwp-windows-gnu` | ✓ | | [^x86_32-floats-return-ABI] +`i686-uwp-windows-msvc` | ✓ | | [^x86_32-floats-return-ABI] [`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] `i686-wrs-vxworks` | ? | | [^x86_32-floats-return-ABI] [`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | ? | | LoongArch64 Linux (LP64D ABI) with musl 1.2.3 @@ -362,13 +363,13 @@ target | std | host | notes [`sparc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/sparc64 [`thumbv4t-none-eabi`](platform-support/armv4t-none-eabi.md) | * | | Thumb-mode Bare Armv4T [`thumbv5te-none-eabi`](platform-support/armv5te-none-eabi.md) | * | | Thumb-mode Bare Armv5TE -`thumbv7a-pc-windows-msvc` | ? | | +`thumbv7a-pc-windows-msvc` | ✓ | | `thumbv7a-uwp-windows-msvc` | ✓ | | `thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode Armv7-A Linux with NEON, musl 1.2.3 [`wasm32-wasip2`](platform-support/wasm32-wasip2.md) | ✓ | | WebAssembly [`wasm64-unknown-unknown`](platform-support/wasm64-unknown-unknown.md) | ? | | WebAssembly -`x86_64-apple-ios-macabi` | ✓ | | Apple Catalyst on x86_64 -[`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ? | | x86 64-bit tvOS +[`x86_64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | | Apple Catalyst on x86_64 +[`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ✓ | | x86 64-bit tvOS [`x86_64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | x86 64-bit Apple WatchOS simulator [`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS | [`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl 1.2.3 @@ -383,5 +384,8 @@ target | std | host | notes `x86_64-wrs-vxworks` | ? | | [`x86_64h-apple-darwin`](platform-support/x86_64h-apple-darwin.md) | ✓ | ✓ | macOS with late-gen Intel (at least Haswell) [`x86_64-unknown-linux-none`](platform-support/x86_64-unknown-linux-none.md) | * | | 64-bit Linux with no libc +`xtensa-esp32-none-elf` | | | Xtensa ESP32 +`xtensa-esp32s2-none-elf` | | | Xtensa ESP32-S2 +`xtensa-esp32s3-none-elf` | | | Xtensa ESP32-S3 [runs on NVIDIA GPUs]: https://github.com/japaric-archived/nvptx#targets diff --git a/src/doc/rustc/src/platform-support/aarch64-apple-ios-sim.md b/src/doc/rustc/src/platform-support/aarch64-apple-ios-sim.md deleted file mode 100644 index 3f29e2c5e1f7..000000000000 --- a/src/doc/rustc/src/platform-support/aarch64-apple-ios-sim.md +++ /dev/null @@ -1,55 +0,0 @@ -# aarch64-apple-ios-sim - -**Tier: 2** - -Apple iOS Simulator on ARM64. - -## Designated Developers - -* [@badboy](https://github.com/badboy) -* [@deg4uss3r](https://github.com/deg4uss3r) - -## Requirements - -This target is cross-compiled. -To build this target Xcode 12 or higher on macOS is required. - -## Building - -The target can be built by enabling it for a `rustc` build: - -```toml -[build] -build-stage = 1 -target = ["aarch64-apple-ios-sim"] -``` - -## Cross-compilation - -This target can be cross-compiled from `x86_64` or `aarch64` macOS hosts. - -Other hosts are not supported for cross-compilation, but might work when also providing the required Xcode SDK. - -## Testing - -Currently there is no support to run the rustc test suite for this target. - - -## Building Rust programs - -*Note: Building for this target requires the corresponding iOS SDK, as provided by Xcode 12+.* - -From Rust Nightly 1.56.0 (2021-08-03) on the artifacts are shipped pre-compiled: - -```text -rustup target add aarch64-apple-ios-sim --toolchain nightly -``` - -Rust programs can be built for that target: - -```text -rustc --target aarch64-apple-ios-sim your-code.rs -``` - -There is no easy way to run simple programs in the iOS simulator. -Static library builds can be embedded into iOS applications. diff --git a/src/doc/rustc/src/platform-support/apple-darwin.md b/src/doc/rustc/src/platform-support/apple-darwin.md new file mode 100644 index 000000000000..0fb86949a4b7 --- /dev/null +++ b/src/doc/rustc/src/platform-support/apple-darwin.md @@ -0,0 +1,59 @@ +# `*-apple-darwin` + +Apple macOS targets. + +**Tier: 1** + +- `x86_64-apple-darwin`: macOS on 64-bit x86. + +**Tier: 2 (with Host Tools)** + +- `aarch64-apple-darwin`: macOS on ARM64 (M1-family or later Apple Silicon CPUs). + +## Target maintainers + +- [@thomcc](https://github.com/thomcc) +- [@madsmtm](https://github.com/madsmtm) + +## Requirements + +### OS version + +The minimum supported version is macOS 10.12 Sierra on x86, and macOS 11.0 Big +Sur on ARM64. + +This version can be raised per-binary by changing the [deployment target], +which might yield more performance optimizations. `rustc` respects the common +environment variables used by Xcode to do so, in this case +`MACOSX_DEPLOYMENT_TARGET`. + +The current default deployment target for `rustc` can be retrieved with +[`rustc --print=deployment-target`][rustc-print]. + +[deployment target]: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/cross_development/Configuring/configuring.html +[rustc-print]: ../command-line-arguments.md#option-print + +### Binary format + +The default binary format is Mach-O, the executable format used on Apple's +platforms. + +## Building + +These targets are distributed through `rustup`, and otherwise require no +special configuration. + +## Testing + +There are no special requirements for testing and running this target. + +x86 binaries can be run on Apple Silicon by using Rosetta. + +## Cross-compilation toolchains and C code + +Cross-compilation of these targets are supported using Clang, but may require +Xcode or the macOS SDK (`MacOSX.sdk`) to be available to compile C code and +to link. + +The path to the SDK can be passed to `rustc` using the common `SDKROOT` +environment variable. diff --git a/src/doc/rustc/src/platform-support/apple-ios-macabi.md b/src/doc/rustc/src/platform-support/apple-ios-macabi.md new file mode 100644 index 000000000000..15ba31e0f064 --- /dev/null +++ b/src/doc/rustc/src/platform-support/apple-ios-macabi.md @@ -0,0 +1,60 @@ +# `*-apple-ios-macabi` + +Apple Mac Catalyst targets. + +**Tier: 3** + +- `aarch64-apple-ios-macabi`: Mac Catalyst on ARM64. +- `x86_64-apple-ios-macabi`: Mac Catalyst on 64-bit x86. + +## Target maintainers + +- [@badboy](https://github.com/badboy) +- [@BlackHoleFox](https://github.com/BlackHoleFox) +- [@madsmtm](https://github.com/madsmtm) + +## Requirements + +These targets are cross-compiled, and require the corresponding macOS SDK +(`MacOSX.sdk`) which contain `./System/iOSSupport` headers to allow linking to +iOS-specific headers, as provided by Xcode 11 or higher. + +The path to the SDK can be passed to `rustc` using the common `SDKROOT` +environment variable. + +### OS version + +The minimum supported version is iOS 13.1. + +This can be raised per-binary by changing the deployment target. `rustc` +respects the common environment variables used by Xcode to do so, in this +case `IPHONEOS_DEPLOYMENT_TARGET`. + +## Building the target + +The targets can be built by enabling them for a `rustc` build in +`config.toml`, by adding, for example: + +```toml +[build] +target = ["aarch64-apple-ios-macabi", "x86_64-apple-ios-macabi"] +``` + +Using the unstable `-Zbuild-std` with a nightly Cargo may also work. + +## Building Rust programs + +Rust programs can be built for these targets by specifying `--target`, if +`rustc` has been built with support for them. For example: + +```console +$ rustc --target aarch64-apple-ios-macabi your-code.rs +``` + +## Testing + +Mac Catalyst binaries can be run directly on macOS 10.15 Catalina or newer. + +x86 binaries can be run on Apple Silicon by using Rosetta. + +Note that using certain UIKit functionality requires the binary to be bundled. diff --git a/src/doc/rustc/src/platform-support/apple-ios.md b/src/doc/rustc/src/platform-support/apple-ios.md new file mode 100644 index 000000000000..5045f810400f --- /dev/null +++ b/src/doc/rustc/src/platform-support/apple-ios.md @@ -0,0 +1,74 @@ +# `*-apple-ios` + +Apple iOS / iPadOS targets. + +**Tier: 2 (without Host Tools)** + +- `aarch64-apple-ios`: Apple iOS on ARM64. +- `aarch64-apple-ios-sim`: Apple iOS Simulator on ARM64. +- `x86_64-apple-ios`: Apple iOS Simulator on 64-bit x86. + +**Tier: 3** + +- `armv7s-apple-ios`: Apple iOS on Armv7-A. +- `i386-apple-ios`: Apple iOS Simulator on 32-bit x86. + +## Target maintainers + +- [@badboy](https://github.com/badboy) +- [@deg4uss3r](https://github.com/deg4uss3r) +- [@madsmtm](https://github.com/madsmtm) + +## Requirements + +These targets are cross-compiled, and require the corresponding iOS SDK +(`iPhoneOS.sdk` or `iPhoneSimulator.sdk`), as provided by Xcode. To build the +ARM64 targets, Xcode 12 or higher is required. + +The path to the SDK can be passed to `rustc` using the common `SDKROOT` +environment variable. + +### OS version + +The minimum supported version is iOS 10.0. + +This can be raised per-binary by changing the deployment target. `rustc` +respects the common environment variables used by Xcode to do so, in this +case `IPHONEOS_DEPLOYMENT_TARGET`. + +## Building the target + +The tier 2 targets are distributed through `rustup`, and can be installed +using one of: +```console +$ rustup target add aarch64-apple-ios +$ rustup target add aarch64-apple-ios-sim +$ rustup target add x86_64-apple-ios +``` + +The tier 3 targets can be built by enabling them for a `rustc` build in +`config.toml`, by adding, for example: + +```toml +[build] +target = ["armv7s-apple-ios", "i386-apple-ios"] +``` + +Using the unstable `-Zbuild-std` with a nightly Cargo may also work. + +## Building Rust programs + +Rust programs can be built for these targets by specifying `--target`, if +`rustc` has been built with support for them. For example: + +```console +$ rustc --target aarch64-apple-ios your-code.rs +``` + +## Testing + +There is no support for running the Rust or standard library testsuite at the +moment. Testing has mostly been done manually with builds of static libraries +embedded into applications called from Xcode or a simulator. + +It hopefully will be possible to improve this in the future. diff --git a/src/doc/rustc/src/platform-support/apple-tvos.md b/src/doc/rustc/src/platform-support/apple-tvos.md index e7ea109df1ba..7a3b601579a0 100644 --- a/src/doc/rustc/src/platform-support/apple-tvos.md +++ b/src/doc/rustc/src/platform-support/apple-tvos.md @@ -1,40 +1,44 @@ # `*-apple-tvos` -- aarch64-apple-tvos -- x86_64-apple-tvos + +Apple tvOS targets. **Tier: 3** -Apple tvOS targets: -- Apple tvOS on aarch64 -- Apple tvOS Simulator on x86_64 +- `aarch64-apple-tvos`: Apple tvOS on ARM64. +- `aarch64-apple-tvos-sim`: Apple tvOS Simulator on ARM64. +- `x86_64-apple-tvos`: Apple tvOS Simulator on x86_64. ## Target maintainers -* [@thomcc](https://github.com/thomcc) +- [@thomcc](https://github.com/thomcc) +- [@madsmtm](https://github.com/madsmtm) ## Requirements -These targets are cross-compiled. You will need appropriate versions of Xcode -and the SDKs for tvOS (`AppleTVOS.sdk`) and/or the tvOS Simulator -(`AppleTVSimulator.sdk`) to build a toolchain and target these platforms. +These targets are cross-compiled, and require the corresponding tvOS SDK +(`AppleTVOS.sdk` or `AppleTVSimulator.sdk`), as provided by Xcode. To build the +ARM64 targets, Xcode 12 or higher is required. -The targets support most (see below) of the standard library including the -allocator to the best of my knowledge, however they are very new, not yet -well-tested, and it is possible that there are various bugs. +The path to the SDK can be passed to `rustc` using the common `SDKROOT` +environment variable. -In theory we support back to tvOS version 7.0, although the actual minimum -version you can target may be newer than this, for example due to the versions -of Xcode and your SDKs. +### OS version -As with the other Apple targets, `rustc` respects the common environment -variables used by Xcode to configure this, in this case -`TVOS_DEPLOYMENT_TARGET`. +The minimum supported version is tvOS 10.0, although the actual minimum version +you can target may be newer than this, for example due to the versions of Xcode +and your SDKs. -#### Incompletely supported library functionality +The version can be raised per-binary by changing the deployment target. `rustc` +respects the common environment variables used by Xcode to do so, in this +case `TVOS_DEPLOYMENT_TARGET`. -As mentioned, "most" of the standard library is supported, which means that some portions -are known to be unsupported. The following APIs are currently known to have -missing or incomplete support: +### Incompletely supported library functionality + +The targets support most of the standard library including the allocator to the +best of my knowledge, however they are very new, not yet well-tested, and it is +possible that there are various bugs. + +The following APIs are currently known to have missing or incomplete support: - `std::process::Command`'s API will return an error if it is configured in a manner which cannot be performed using `posix_spawn` -- this is because the @@ -47,41 +51,30 @@ missing or incomplete support: ## Building the target -The targets can be built by enabling them for a `rustc` build in `config.toml`, by adding, for example: +The targets can be built by enabling them for a `rustc` build in +`config.toml`, by adding, for example: ```toml [build] build-stage = 1 -target = ["aarch64-apple-tvos", "x86_64-apple-tvos", "aarch64-apple-tvos-sim"] +target = ["aarch64-apple-tvos", "aarch64-apple-tvos-sim"] ``` -It's possible that cargo under `-Zbuild-std` may also be used to target them. +Using the unstable `-Zbuild-std` with a nightly Cargo may also work. ## Building Rust programs -*Note: Building for this target requires the corresponding TVOS SDK, as provided by Xcode.* +Rust programs can be built for these targets by specifying `--target`, if +`rustc` has been built with support for them. For example: -Rust programs can be built for these targets - -```text +```console $ rustc --target aarch64-apple-tvos your-code.rs -... -$ rustc --target x86_64-apple-tvos your-code.rs -... -$ rustc --target aarch64-apple-tvos-sim your-code.rs ``` ## Testing -There is no support for running the Rust or standard library testsuite on tvOS -or the simulators at the moment. Testing has mostly been done manually with -builds of static libraries called from Xcode or a simulator. +There is no support for running the Rust or standard library testsuite at the +moment. Testing has mostly been done manually with builds of static libraries +embedded into applications called from Xcode or a simulator. It hopefully will be possible to improve this in the future. - -## Cross-compilation toolchains and C code - -This target can be cross-compiled from x86_64 or aarch64 macOS hosts. - -Other hosts are not supported for cross-compilation, but might work when also -providing the required Xcode SDK. diff --git a/src/doc/rustc/src/platform-support/apple-visionos.md b/src/doc/rustc/src/platform-support/apple-visionos.md index 9874126e42fe..56224d7e20d4 100644 --- a/src/doc/rustc/src/platform-support/apple-visionos.md +++ b/src/doc/rustc/src/platform-support/apple-visionos.md @@ -1,53 +1,67 @@ -# aarch64-apple-visionos\* +# `*-apple-visionos` -- aarch64-apple-visionos -- aarch64-apple-visionos-sim +Apple visionOS / xrOS targets. **Tier: 3** -Apple visionOS targets: - -- Apple visionOS on arm64 -- Apple visionOS Simulator on arm64 +- `aarch64-apple-visionos`: Apple visionOS on arm64. +- `aarch64-apple-visionos-sim`: Apple visionOS Simulator on arm64. ## Target maintainers -- [@agg23](https://github.com/agg23) -- [@madsmtm](https://github.com/madsmtm) +- [@agg23](https://github.com/agg23) +- [@madsmtm](https://github.com/madsmtm) ## Requirements -These targets are cross-compiled. -To build these targets Xcode 15 or higher on macOS is required, along with LLVM 18. +These targets are cross-compiled, and require the corresponding visionOS SDK +(`XROS.sdk` or `XRSimulator.sdk`), as provided by Xcode 15 or newer. + +The path to the SDK can be passed to `rustc` using the common `SDKROOT` +environment variable. + +### OS version + +The minimum supported version is visionOS 1.0. + +This can be raised per-binary by changing the deployment target. `rustc` +respects the common environment variables used by Xcode to do so, in this +case `XROS_DEPLOYMENT_TARGET`. ## Building the target -The targets can be built by enabling them for a `rustc` build, for example: +The targets can be built by enabling them for a `rustc` build in +`config.toml`, by adding, for example: ```toml [build] -build-stage = 1 -target = ["aarch64-apple-visionos-sim"] +target = ["aarch64-apple-visionos", "aarch64-apple-visionos-sim"] ``` +Using the unstable `-Zbuild-std` with a nightly Cargo may also work. + +Note: Currently, a newer version of `libc` and `cc` may be required, this will +be fixed in [#124560](https://github.com/rust-lang/rust/pull/124560). + ## Building Rust programs -_Note: Building for this target requires the corresponding visionOS SDK, as provided by Xcode 15+._ +Rust programs can be built for these targets by specifying `--target`, if +`rustc` has been built with support for them. For example: -Rust programs can be built for these targets, if `rustc` has been built with support for them, for example: - -```text -rustc --target aarch64-apple-visionos-sim your-code.rs +```console +$ rustc --target aarch64-apple-visionos-sim your-code.rs ``` ## Testing -There is no support for running the Rust testsuite on visionOS or the simulators. +There is no support for running the Rust or standard library testsuite at the +moment. Testing has mostly been done manually with builds of static libraries +embedded into applications called from Xcode or a simulator. -There is no easy way to run simple programs on visionOS or the visionOS simulators. Static library builds can be embedded into visionOS applications. +It hopefully will be possible to improve this in the future. ## Cross-compilation toolchains and C code -This target can be cross-compiled from x86_64 or aarch64 macOS hosts. +The Clang target is suffixed with `-xros` for historical reasons. -Other hosts are not supported for cross-compilation, but might work when also providing the required Xcode SDK. +LLVM 18 or newer is required to build this target. diff --git a/src/doc/rustc/src/platform-support/apple-watchos.md b/src/doc/rustc/src/platform-support/apple-watchos.md index 7be2467352c2..8ba35f70b857 100644 --- a/src/doc/rustc/src/platform-support/apple-watchos.md +++ b/src/doc/rustc/src/platform-support/apple-watchos.md @@ -1,58 +1,65 @@ -# *-apple-watchos -- arm64_32-apple-watchos -- armv7k-apple-watchos -- aarch64-apple-watchos -- aarch64-apple-watchos-sim -- x86_64-apple-watchos-sim +# `*-apple-watchos` + +Apple watchOS targets. **Tier: 3** -Apple WatchOS targets: -- Apple WatchOS on Arm 64_32 -- Apple WatchOS on Arm v7k -- Apple WatchOS on Arm 64 -- Apple WatchOS Simulator on arm64 -- Apple WatchOS Simulator on x86_64 +- `aarch64-apple-watchos`: Apple WatchOS on ARM64. +- `aarch64-apple-watchos-sim`: Apple WatchOS Simulator on ARM64. +- `x86_64-apple-watchos-sim`: Apple WatchOS Simulator on 64-bit x86. +- `arm64_32-apple-watchos`: Apple WatchOS on Arm 64_32. +- `armv7k-apple-watchos`: Apple WatchOS on Armv7k. ## Target maintainers -* [@deg4uss3r](https://github.com/deg4uss3r) -* [@vladimir-ea](https://github.com/vladimir-ea) -* [@leohowell](https://github.com/leohowell) +- [@deg4uss3r](https://github.com/deg4uss3r) +- [@vladimir-ea](https://github.com/vladimir-ea) +- [@leohowell](https://github.com/leohowell) +- [@madsmtm](https://github.com/madsmtm) ## Requirements -These targets are cross-compiled. -To build these targets Xcode 12 or higher on macOS is required. +These targets are cross-compiled, and require the corresponding watchOS SDK +(`WatchOS.sdk` or `WatchSimulator.sdk`), as provided by Xcode. To build the +ARM64 targets, Xcode 12 or higher is required. + +The path to the SDK can be passed to `rustc` using the common `SDKROOT` +environment variable. + +### OS version + +The minimum supported version is watchOS 5.0. + +This can be raised per-binary by changing the deployment target. `rustc` +respects the common environment variables used by Xcode to do so, in this +case `WATCHOS_DEPLOYMENT_TARGET`. ## Building the target -The targets can be built by enabling them for a `rustc` build, for example: +The targets can be built by enabling them for a `rustc` build in +`config.toml`, by adding, for example: ```toml [build] build-stage = 1 -target = ["aarch64-apple-watchos-sim"] +target = ["aarch64-apple-watchos", "aarch64-apple-watchos-sim"] ``` +Using the unstable `-Zbuild-std` with a nightly Cargo may also work. + ## Building Rust programs -*Note: Building for this target requires the corresponding WatchOS SDK, as provided by Xcode 12+.* +Rust programs can be built for these targets by specifying `--target`, if +`rustc` has been built with support for them. For example: -Rust programs can be built for these targets, if `rustc` has been built with support for them, for example: - -```text -rustc --target aarch64-apple-watchos-sim your-code.rs +```console +$ rustc --target aarch64-apple-watchos-sim your-code.rs ``` ## Testing -There is no support for running the Rust testsuite on WatchOS or the simulators. +There is no support for running the Rust or standard library testsuite at the +moment. Testing has mostly been done manually with builds of static libraries +embedded into applications called from Xcode or a simulator. -There is no easy way to run simple programs on WatchOS or the WatchOS simulators. Static library builds can be embedded into WatchOS applications. - -## Cross-compilation toolchains and C code - -This target can be cross-compiled from x86_64 or aarch64 macOS hosts. - -Other hosts are not supported for cross-compilation, but might work when also providing the required Xcode SDK. +It hopefully will be possible to improve this in the future. diff --git a/src/doc/rustc/src/platform-support/arm-none-eabi.md b/src/doc/rustc/src/platform-support/arm-none-eabi.md index 0b1b10e4762e..de0ef322fa67 100644 --- a/src/doc/rustc/src/platform-support/arm-none-eabi.md +++ b/src/doc/rustc/src/platform-support/arm-none-eabi.md @@ -1,6 +1,15 @@ # `{arm,thumb}*-none-eabi(hf)?` -## Tier 2 Target List +## Common Target Details + +This documentation covers details that apply to a range of bare-metal targets +for 32-bit Arm CPUs. The `arm-none-eabi` flavor of the GNU compiler toolchain is +often used to assist compilation to these targets. + +Details that apply only to only a specific target in this group are covered in +their own document. + +### Tier 2 Target List - Arm A-Profile Architectures - `armv7a-none-eabi` @@ -16,7 +25,7 @@ - *Legacy* Arm Architectures - None -## Tier 3 Target List +### Tier 3 Target List - Arm A-Profile Architectures - `armv7a-none-eabihf` @@ -28,24 +37,21 @@ - [`armv4t-none-eabi` and `thumbv4t-none-eabi`](armv4t-none-eabi.md) - [`armv5te-none-eabi` and `thumbv5te-none-eabi`](armv5te-none-eabi.md) -## Common Target Details - -This documentation covers details that apply to a range of bare-metal targets -for 32-bit Arm CPUs. In addition, target specific details may be covered in -their own document. +## Instruction Sets There are two 32-bit instruction set architectures (ISAs) defined by Arm: - The [*A32 ISA*][a32-isa], with fixed-width 32-bit instructions. Previously - known as the *Arm* ISA, this originated with the original ARM1 of 1985 and has + known as the *Arm* ISA, this originated with the original Arm1 of 1985 and has been updated by various revisions to the architecture specifications ever since. - The [*T32 ISA*][t32-isa], with a mix of 16-bit and 32-bit width instructions. Note that this term includes both the original 16-bit width *Thumb* ISA introduced with the Armv4T architecture in 1994, and the later 16/32-bit sized - *Thumb-2* ISA introduced with the Armv6T2 architecture in 2003. Again, these - ISAs have been revised by subsequent revisions to the relevant Arm - architecture specifications. + *Thumb-2* ISA introduced with the Armv6T2 architecture in 2003. + +Again, these ISAs have been revised by subsequent revisions to the relevant Arm +architecture specifications. There is also a 64-bit ISA with fixed-width 32-bit instructions called the *A64 ISA*, but targets which implement that instruction set generally start with diff --git a/src/doc/rustc/src/platform-support/arm64e-apple-darwin.md b/src/doc/rustc/src/platform-support/arm64e-apple-darwin.md index d9b9aeae1ae6..4d98b3a60986 100644 --- a/src/doc/rustc/src/platform-support/arm64e-apple-darwin.md +++ b/src/doc/rustc/src/platform-support/arm64e-apple-darwin.md @@ -12,6 +12,8 @@ ARM64e macOS (11.0+, Big Sur+) Target for `macOS` on late-generation `M` series Apple chips. +See the docs on [`*-apple-darwin`](apple-darwin.md) for general macOS requirements. + ## Building the target You can build Rust with support for the targets by adding it to the `target` list in `config.toml`: diff --git a/src/doc/rustc/src/platform-support/arm64e-apple-ios.md b/src/doc/rustc/src/platform-support/arm64e-apple-ios.md index 0215621be3d0..3c878f7250e9 100644 --- a/src/doc/rustc/src/platform-support/arm64e-apple-ios.md +++ b/src/doc/rustc/src/platform-support/arm64e-apple-ios.md @@ -10,8 +10,7 @@ ARM64e iOS (12.0+) ## Requirements -These targets only support cross-compilation. -The targets do support `std`. +See the docs on [`*-apple-ios`](apple-ios.md) for general iOS requirements. ## Building the target diff --git a/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md b/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md index 2e8275358626..dcabd21a83eb 100644 --- a/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md +++ b/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md @@ -1,6 +1,6 @@ # `arm64ec-pc-windows-msvc` -**Tier: 3** +**Tier: 2** Arm64EC ("Emulation Compatible") for mixed architecture (AArch64 and x86_64) applications on AArch64 Windows 11. See . @@ -21,6 +21,9 @@ Only supported backend is LLVM 18 or above: * 18.1.4 fixed linking issue for some intrinsics implemented in `compiler_builtins`. +Visual Studio 2022 (or above) with the "ARM64/ARM64EC built tools" component and +the Windows 11 SDK are required. + ### Reusing code from other architectures - x86_64 or AArch64? Arm64EC uses `arm64ec` as its `target_arch`, but it is possible to reuse @@ -62,10 +65,8 @@ target = [ "arm64ec-pc-windows-msvc" ] ## Building Rust programs -Rust does not yet ship pre-compiled artifacts for this target. To compile for -this target, you will either need to build Rust with the target enabled (see -"Building the target" above), or build your own copy using `build-std` or -similar. +These targets are distributed through `rustup`, and otherwise require no +special configuration. ## Testing diff --git a/src/doc/rustc/src/platform-support/armv4t-none-eabi.md b/src/doc/rustc/src/platform-support/armv4t-none-eabi.md index f4c8dd46f1d0..0c5129d0efbb 100644 --- a/src/doc/rustc/src/platform-support/armv4t-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv4t-none-eabi.md @@ -1,20 +1,18 @@ -# armv4t-none-eabi +# armv4t-none-eabi / thumbv4t-none-eabi Tier 3 -Bare-metal target for any cpu in the Armv4T architecture family, supporting -ARM/Thumb code interworking (aka `A32`/`T32`), with ARM code as the default code -generation. +These two targets are part of the [`arm-none-eabi`](arm-none-eabi.md) target +group, and all the information there applies. -In particular this supports the Game Boy Advance (GBA), but there's nothing -GBA-specific with this target, so any Armv4T device should work fine. - -See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all -`arm-none-eabi` targets. +Both of these targets can be used on the Game Boy Advance (GBA), among other +things. On the GBA, one should usually use the `thumb` target to get the best +overall performance. ## Target Maintainers * [@Lokathor](https://github.com/lokathor) +* [@corwinkuiper](https://github.com/corwinkuiper) ## Testing @@ -23,6 +21,6 @@ This is a cross-compiled target that you will need to emulate during testing. Because this is a device-agnostic target, and the exact emulator that you'll need depends on the specific device you want to run your code on. -For example, when programming for the Gameboy Advance, the -[mgba-test-runner](https://github.com/agbrs/agb) program could be used to make a -normal set of rust tests be run within the `mgba` emulator. +* When building for the GBA, [mgba-test-runner](https://github.com/agbrs/agb) + can be used to make a normal set of rust tests be run within the `mgba` + emulator. diff --git a/src/doc/rustc/src/platform-support/fuchsia.md b/src/doc/rustc/src/platform-support/fuchsia.md index 3e1db692f50b..5643c6a0188a 100644 --- a/src/doc/rustc/src/platform-support/fuchsia.md +++ b/src/doc/rustc/src/platform-support/fuchsia.md @@ -10,9 +10,9 @@ updatable, and performant. The [Fuchsia team]: - Tyler Mandry ([@tmandry](https://github.com/tmandry)) -- Dan Johnson ([@computerdruid](https://github.com/computerdruid)) - David Koloski ([@djkoloski](https://github.com/djkoloski)) -- Joseph Ryan ([@P1n3appl3](https://github.com/P1n3appl3)) +- Julia Ryan ([@P1n3appl3](https://github.com/P1n3appl3)) +- Erick Tryzelaar ([@erickt](https://github.com/erickt)) As the team evolves over time, the specific members listed here may differ from the members reported by the API. The API should be considered to be diff --git a/src/doc/rustc/src/platform-support/i686-apple-darwin.md b/src/doc/rustc/src/platform-support/i686-apple-darwin.md new file mode 100644 index 000000000000..d69fa97ce637 --- /dev/null +++ b/src/doc/rustc/src/platform-support/i686-apple-darwin.md @@ -0,0 +1,41 @@ +# `i686-apple-darwin` + +Apple macOS on 32-bit x86. + +## Target maintainers + +- [@thomcc](https://github.com/thomcc) +- [@madsmtm](https://github.com/madsmtm) + +## Requirements + +See the docs on [`*-apple-darwin`](apple-darwin.md) for general macOS requirements. + +## Building the target + +You'll need the macOS 10.13 SDK shipped with Xcode 9. The location of the SDK +can be passed to `rustc` using the common `SDKROOT` environment variable. + +Once you have that, you can build Rust with support for the target by adding +it to the `target` list in `config.toml`: + +```toml +[build] +target = ["i686-apple-darwin"] +``` + +Using the unstable `-Zbuild-std` with a nightly Cargo may also work. + +## Building Rust programs + +Rust [no longer] ships pre-compiled artifacts for this target. To compile for +this target, you will either need to build Rust with the target enabled (see +"Building the target" above), or build your own copy using `build-std` or +similar. + +[no longer]: https://blog.rust-lang.org/2020/01/03/reducing-support-for-32-bit-apple-targets.html + +## Testing + +Running this target requires an Intel Macbook running macOS 10.14 or earlier, +as later versions removed support for running 32-bit binaries. diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md index 56070c2ec34e..51a397a38d20 100644 --- a/src/doc/rustc/src/platform-support/nto-qnx.md +++ b/src/doc/rustc/src/platform-support/nto-qnx.md @@ -160,8 +160,7 @@ export exclude_tests=' --exclude src/tools/linkchecker --exclude tests/ui-fulldeps --exclude rustc - --exclude rustdoc - --exclude tests/run-make-fulldeps' + --exclude rustdoc' env $build_env \ ./x.py test \ diff --git a/src/doc/rustc/src/platform-support/redox.md b/src/doc/rustc/src/platform-support/redox.md new file mode 100644 index 000000000000..1b3321956ef7 --- /dev/null +++ b/src/doc/rustc/src/platform-support/redox.md @@ -0,0 +1,53 @@ +# `*-unknown-redox` + +**Tier: 2/3** + +Targets for the [Redox OS](https://redox-os.org/) operating +system. + +Target triplets available so far: + +- `x86_64-unknown-redox` (tier 2) +- `aarch64-unknown-redox` (tier 3) +- `i686-unknown-redox` (tier 3) + +## Target maintainers + +- Jeremy Soller ([@jackpot51](https://github.com/jackpot51)) + +## Requirements + +These targets are natively compiled and can be cross-compiled. Std is fully supported. + +The targets are only expected to work with the latest version of Redox OS as the ABI is not yet stable. + +`extern "C"` uses the official calling convention of the respective architectures. + +Redox OS binaries use ELF as file format. + +## Building the target + +You can build Rust with support for the targets by adding it to the `target` list in `config.toml`. In addition a copy of [relibc] needs to be present in the linker search path. + +```toml +[build] +build-stage = 1 +target = [ + "", + "x86_64-unknown-redox", + "aarch64-unknown-redox", + "i686-unknown-redox", +] +``` + +[relibc]: https://gitlab.redox-os.org/redox-os/relibc + +## Building Rust programs and testing + +Rust does not yet ship pre-compiled artifacts for Redox OS except for x86_64-unknown-redox. + +The easiest way to build and test programs for Redox OS is using [redoxer](https://gitlab.redox-os.org/redox-os/redoxer) which sets up the required compiler toolchain for building as well as runs programs inside a Redox OS VM using QEMU. + +## Cross-compilation toolchains and C code + +The target supports C code. Pre-compiled C toolchains can be found at . diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip1-threads.md b/src/doc/rustc/src/platform-support/wasm32-wasip1-threads.md index 519e9cc7cc49..2b3d15e93c8c 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip1-threads.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip1-threads.md @@ -150,3 +150,15 @@ or another engine that supports `wasi-threads` is installed and can be found in 5. Apply such [a change](https://github.com/g0djan/rust/compare/godjan/wasi-threads...g0djan:rust:godjan/wasi-run-ui-tests?expand=1) with an engine from the step 1. 6. Run `./x.py test --target wasm32-wasip1-threads tests/ui` and save the list of failed tests. 7. For both lists of failed tests run `cat list | sort > sorted_list` and compare it with `diff sorted_list1 sorted_list2`. + +## Conditionally compiling code + +It's recommended to conditionally compile code for this target with: + +```text +#[cfg(all(target_os = "wasi", target_env = "p1", target_feature = "atomics"))] +``` + +Prior to Rust 1.80 the `target_env = "p1"` key was not set. Currently the +`target_feature = "atomics"` is Nightly-only. Note that the precise `#[cfg]` +necessary to detect this target may change as the target becomes more stable. diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip1.md b/src/doc/rustc/src/platform-support/wasm32-wasip1.md index 4faa19887353..fb70bbdc2b40 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip1.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip1.md @@ -121,3 +121,14 @@ can be tested locally, for example, with: ```text ./x.py test --target wasm32-wasip1 tests/ui ``` + +## Conditionally compiling code + +It's recommended to conditionally compile code for this target with: + +```text +#[cfg(all(target_os = "wasi", target_env = "p1"))] +``` + +Note that the `target_env = "p1"` condition first appeared in Rust 1.80. Prior +to Rust 1.80 the `target_env` condition was not set. diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip2.md b/src/doc/rustc/src/platform-support/wasm32-wasip2.md index a385600cc224..1e53fbc178e2 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip2.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip2.md @@ -53,3 +53,11 @@ This target is not tested in CI at this time. Locally it can be tested with a ```text ./x.py test --target wasm32-wasip2 tests/ui ``` + +## Conditionally compiling code + +It's recommended to conditionally compile code for this target with: + +```text +#[cfg(all(target_os = "wasi", target_env = "p2"))] +``` diff --git a/src/doc/rustc/src/platform-support/x86_64h-apple-darwin.md b/src/doc/rustc/src/platform-support/x86_64h-apple-darwin.md index 0fe9d4edaca1..6c2a6a41101d 100644 --- a/src/doc/rustc/src/platform-support/x86_64h-apple-darwin.md +++ b/src/doc/rustc/src/platform-support/x86_64h-apple-darwin.md @@ -23,9 +23,8 @@ default or user-defined allocators). This target is probably most useful when targeted via cross-compilation (including from `x86_64-apple-darwin`), but if built manually, the host tools work. -It is similar to `x86_64-apple-darwin` in nearly all respects, although the -minimum supported OS version is slightly higher (it requires 10.8 rather than -`x86_64-apple-darwin`'s 10.7). +It is similar to [`x86_64-apple-darwin`](apple-darwin.md) in nearly all +respects. ## Building the target diff --git a/src/doc/rustc/src/platform-support/xtensa.md b/src/doc/rustc/src/platform-support/xtensa.md new file mode 100644 index 000000000000..7785977466e4 --- /dev/null +++ b/src/doc/rustc/src/platform-support/xtensa.md @@ -0,0 +1,25 @@ +# `xtensa-*` + +**Tier: 3** + +Targets for Xtensa CPUs. + +## Target maintainers + +- Scott Mabin [@MabezDev](https://github.com/MabezDev) +- Sergio Gasquez [@SergioGasquez](https://github.com/SergioGasquez) + +## Requirements + +The target names follow this format: `xtensa-$CPU`, where `$CPU` specifies the target chip. The following targets are currently defined: + +| Target name | Target CPU(s) | +| ------------------------- | --------------------------------------------------------------- | +| `xtensa-esp32-none-elf` | [ESP32](https://www.espressif.com/en/products/socs/esp32) | +| `xtensa-esp32s2-none-elf` | [ESP32-S2](https://www.espressif.com/en/products/socs/esp32-s2) | +| `xtensa-esp32s3-none-elf` | [ESP32-S3](https://www.espressif.com/en/products/socs/esp32-s3) | + + +## Building the target + +The targets can be built by installing the [Xtensa enabled Rust channel](https://github.com/esp-rs/rust/). See instructions in the [RISC-V and Xtensa Targets section of the The Rust on ESP Book](https://docs.esp-rs.org/book/installation/riscv-and-xtensa.html). diff --git a/src/doc/rustc/src/symbol-mangling/v0.md b/src/doc/rustc/src/symbol-mangling/v0.md index 763694a9fdac..6329e878c5c8 100644 --- a/src/doc/rustc/src/symbol-mangling/v0.md +++ b/src/doc/rustc/src/symbol-mangling/v0.md @@ -150,6 +150,15 @@ the *[disambiguator]* is used to make the name unique across the crate graph. > ``` > > Recommended demangling: `mycrate::example` +> +> Note: The compiler may re-use the *crate-root* form to express arbitrary +> unscoped, undisambiguated identifiers, such as for new basic types that have +> not been added to the grammar yet. To achieve that, it will emit a *crate-root* +> without an explicit disambiguator, relying on the fact that such an +> undisambiguated crate name cannot occur in practice. For example, the basic +> type `f128` would be encode as `C4f128`. For this to have the desired effect, +> demanglers are expected to never render zero disambiguators of crate roots. +> I.e. `C4f128` is expected to be displayed as `f128` and not `f128[0]`. ### Path: Inherent impl [inherent-impl]: #path-inherent-impl @@ -539,6 +548,10 @@ This allows disambiguators that are encoded sequentially to use minimal bytes. > **Recommended Demangling** > > The *disambiguator* may or may not be displayed; see recommendations for rules that use *disambiguator*. +> Generally, it is recommended that zero disambiguators are never displayed unless their accompanying +> identifier is empty (like is the case for unnamed items such as closures). +> When rendering a disambiguator, it can be shortened to a length reasonable for the context, +> similar to how git commit hashes are rarely displayed in full. ## Lifetime [lifetime]: #lifetime diff --git a/src/doc/rustc/src/target-tier-policy.md b/src/doc/rustc/src/target-tier-policy.md index 48b58f6f06ad..e9cf2a0d1ae5 100644 --- a/src/doc/rustc/src/target-tier-policy.md +++ b/src/doc/rustc/src/target-tier-policy.md @@ -247,7 +247,8 @@ approved by the appropriate team for that shared code before acceptance. target may not have; use conditional compilation or runtime detection, as appropriate, to let each target run code supported by that target. - Tier 3 targets must be able to produce assembly using at least one of - rustc's supported backends from any host target. + rustc's supported backends from any host target. (Having support in a fork + of the backend is not sufficient, it must be upstream.) If a tier 3 target stops meeting these requirements, or the target maintainers no longer have interest or time, or the target shows no signs of activity and diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index bdb55de8d634..39f24a13143c 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -624,47 +624,3 @@ add the `--scrape-tests` flag. This flag enables the generation of links in the source code pages which allow the reader to jump to a type definition. - -### Custom CSS classes for code blocks - -```rust -#![feature(custom_code_classes_in_docs)] - -/// ```custom,{class=language-c} -/// int main(void) { return 0; } -/// ``` -pub struct Bar; -``` - -The text `int main(void) { return 0; }` is rendered without highlighting in a code block -with the class `language-c`. This can be used to highlight other languages through JavaScript -libraries for example. - -Without the `custom` attribute, it would be generated as a Rust code example with an additional -`language-C` CSS class. Therefore, if you specifically don't want it to be a Rust code example, -don't forget to add the `custom` attribute. - -To be noted that you can replace `class=` with `.` to achieve the same result: - -```rust -#![feature(custom_code_classes_in_docs)] - -/// ```custom,{.language-c} -/// int main(void) { return 0; } -/// ``` -pub struct Bar; -``` - -To be noted, `rust` and `.rust`/`class=rust` have different effects: `rust` indicates that this is -a Rust code block whereas the two others add a "rust" CSS class on the code block. - -You can also use double quotes: - -```rust -#![feature(custom_code_classes_in_docs)] - -/// ```"not rust" {."hello everyone"} -/// int main(void) { return 0; } -/// ``` -pub struct Bar; -``` diff --git a/src/doc/rustdoc/src/write-documentation/documentation-tests.md b/src/doc/rustdoc/src/write-documentation/documentation-tests.md index a7d3186fb78b..9526f33359e4 100644 --- a/src/doc/rustdoc/src/write-documentation/documentation-tests.md +++ b/src/doc/rustdoc/src/write-documentation/documentation-tests.md @@ -376,6 +376,44 @@ that the code sample should be compiled using the respective edition of Rust. # fn foo() {} ``` +### Custom CSS classes for code blocks + +```rust +/// ```custom,{class=language-c} +/// int main(void) { return 0; } +/// ``` +pub struct Bar; +``` + +The text `int main(void) { return 0; }` is rendered without highlighting in a code block +with the class `language-c`. This can be used to highlight other languages through JavaScript +libraries for example. + +Without the `custom` attribute, it would be generated as a Rust code example with an additional +`language-C` CSS class. Therefore, if you specifically don't want it to be a Rust code example, +don't forget to add the `custom` attribute. + +To be noted that you can replace `class=` with `.` to achieve the same result: + +```rust +/// ```custom,{.language-c} +/// int main(void) { return 0; } +/// ``` +pub struct Bar; +``` + +To be noted, `rust` and `.rust`/`class=rust` have different effects: `rust` indicates that this is +a Rust code block whereas the two others add a "rust" CSS class on the code block. + +You can also use double quotes: + +```rust +/// ```"not rust" {."hello everyone"} +/// int main(void) { return 0; } +/// ``` +pub struct Bar; +``` + ## Syntax reference The *exact* syntax for code blocks, including the edge cases, can be found diff --git a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md index 669dc9358ebf..ff033aa14b82 100644 --- a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md +++ b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md @@ -144,10 +144,10 @@ it will not. ### `test(attr(...))` This form of the `doc` attribute allows you to add arbitrary attributes to all your doctests. For -example, if you want your doctests to fail if they produce any warnings, you could add this: +example, if you want your doctests to fail if they have dead code, you could add this: ```rust,no_run -#![doc(test(attr(deny(warnings))))] +#![doc(test(attr(deny(dead_code))))] ``` ## At the item level diff --git a/src/doc/unstable-book/src/compiler-flags/codegen-backend.md b/src/doc/unstable-book/src/compiler-flags/codegen-backend.md index 67634be69931..7e4be9841f47 100644 --- a/src/doc/unstable-book/src/compiler-flags/codegen-backend.md +++ b/src/doc/unstable-book/src/compiler-flags/codegen-backend.md @@ -12,8 +12,9 @@ backend. The library must be of crate type `dylib` and must contain a function named `__rustc_codegen_backend` with a signature of `fn() -> Box`. ## Example -See also the [`hotplug_codegen_backend`](https://github.com/rust-lang/rust/tree/master/tests/run-make-fulldeps/hotplug_codegen_backend) test -for a full example. +See also the [`codegen-backend/hotplug`] test for a working example. + +[`codegen-backend/hotplug`]: https://github.com/rust-lang/rust/tree/master/tests/ui-fulldeps/codegen-backend/hotplug.rs ```rust,ignore (partial-example) use rustc_codegen_ssa::traits::CodegenBackend; diff --git a/src/doc/unstable-book/src/compiler-flags/coverage-options.md b/src/doc/unstable-book/src/compiler-flags/coverage-options.md index 212788335502..87a9a00b3c0f 100644 --- a/src/doc/unstable-book/src/compiler-flags/coverage-options.md +++ b/src/doc/unstable-book/src/compiler-flags/coverage-options.md @@ -5,13 +5,16 @@ This option controls details of the coverage instrumentation performed by Multiple options can be passed, separated by commas. Valid options are: -- `block`, `branch`, `mcdc`: +- `block`, `branch`, `condition`, `mcdc`: Sets the level of coverage instrumentation. Setting the level will override any previously-specified level. - `block` (default): Blocks in the control-flow graph will be instrumented for coverage. - `branch`: In addition to block coverage, also enables branch coverage instrumentation. + - `condition`: + In addition to branch coverage, also instruments some boolean expressions + as branches, even if they are not directly used as branch conditions. - `mcdc`: - In addition to block and branch coverage, also enables MC/DC instrumentation. + In addition to condition coverage, also enables MC/DC instrumentation. (Branch coverage instrumentation may differ in some cases.) diff --git a/src/doc/unstable-book/src/compiler-flags/fixed-x18.md b/src/doc/unstable-book/src/compiler-flags/fixed-x18.md new file mode 100644 index 000000000000..8c8bff5fa296 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/fixed-x18.md @@ -0,0 +1,32 @@ +# `fixed-x18` + +This option prevents the compiler from using the x18 register. It is only +supported on aarch64. + +From the [ABI spec][arm-abi]: + +> X18 is the platform register and is reserved for the use of platform ABIs. +> This is an additional temporary register on platforms that don't assign a +> special meaning to it. + +This flag only has an effect when the x18 register would otherwise be considered +a temporary register. When the flag is applied, x18 is always a reserved +register. + +This flag is intended for use with the shadow call stack sanitizer. Generally, +when that sanitizer is enabled, the x18 register is used to store a pointer to +the shadow stack. Enabling this flag prevents the compiler from overwriting the +shadow stack pointer with temporary data, which is necessary for the sanitizer +to work correctly. + +Currently, the `-Zsanitizer=shadow-call-stack` flag is only supported on +platforms that always treat x18 as a reserved register, and the `-Zfixed-x18` +flag is not required to use the sanitizer on such platforms. However, the +sanitizer may be supported on targets where this is not the case in the future. + +It is undefined behavior for `-Zsanitizer=shadow-call-stack` code to call into +code where x18 is a temporary register. On the other hand, when you are *not* +using the shadow call stack sanitizer, compilation units compiled with and +without the `-Zfixed-x18` flag are compatible with each other. + +[arm-abi]: https://developer.arm.com/documentation/den0024/a/The-ABI-for-ARM-64-bit-Architecture/Register-use-in-the-AArch64-Procedure-Call-Standard/Parameters-in-general-purpose-registers diff --git a/src/doc/unstable-book/src/compiler-flags/print-check-cfg.md b/src/doc/unstable-book/src/compiler-flags/print-check-cfg.md new file mode 100644 index 000000000000..ab63c986e856 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/print-check-cfg.md @@ -0,0 +1,26 @@ +# `print=check-cfg` + +The tracking issue for this feature is: [#125704](https://github.com/rust-lang/rust/issues/125704). + +------------------------ + +This option of the `--print` flag print the list of expected cfgs. + +This is related to the `--check-cfg` flag which allows specifying arbitrary expected +names and values. + +This print option works similarly to `--print=cfg` (modulo check-cfg specifics): + - *check_cfg syntax*: *output of --print=check-cfg* + - `cfg(windows)`: `windows` + - `cfg(feature, values("foo", "bar"))`: `feature="foo"` and `feature="bar"` + - `cfg(feature, values(none(), ""))`: `feature` and `feature=""` + - `cfg(feature, values(any()))`: `feature=any()` + - `cfg(feature, values())`: `feature=` + - `cfg(any())`: `any()` + - *nothing*: `any()=any()` + +To be used like this: + +```bash +rustc --print=check-cfg -Zunstable-options lib.rs +``` diff --git a/src/doc/unstable-book/src/language-features/abi-vectorcall.md b/src/doc/unstable-book/src/language-features/abi-vectorcall.md new file mode 100644 index 000000000000..56273bfdb791 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/abi-vectorcall.md @@ -0,0 +1,19 @@ +# `abi_vectorcall` + +The tracking issue for this feature is: [#124485] + +[#124485]: https://github.com/rust-lang/rust/issues/124485 + +------------------------ + +Adds support for the Windows `"vectorcall"` ABI, the equivalent of `__vectorcall` in MSVC. + +```rust,ignore (only-windows-or-x86-or-x86-64) +extern "vectorcall" { + fn add_f64s(x: f64, y: f64) -> f64; +} + +fn main() { + println!("{}", add_f64s(2.0, 4.0)); +} +``` diff --git a/src/etc/completions/x.py.fish b/src/etc/completions/x.py.fish index 40a25f13fcbd..7343f3147eed 100644 --- a/src/etc/completions/x.py.fish +++ b/src/etc/completions/x.py.fish @@ -216,6 +216,7 @@ complete -c x.py -n "__fish_seen_subcommand_from fmt" -l llvm-profile-use -d 'us complete -c x.py -n "__fish_seen_subcommand_from fmt" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r complete -c x.py -n "__fish_seen_subcommand_from fmt" -l set -d 'override options in config.toml' -r -f complete -c x.py -n "__fish_seen_subcommand_from fmt" -l check -d 'check formatting instead of applying' +complete -c x.py -n "__fish_seen_subcommand_from fmt" -l all -d 'apply to all appropriate files, not just those that have been modified' complete -c x.py -n "__fish_seen_subcommand_from fmt" -s v -l verbose -d 'use verbose output (-vv for very verbose)' complete -c x.py -n "__fish_seen_subcommand_from fmt" -s i -l incremental -d 'use incremental compilation' complete -c x.py -n "__fish_seen_subcommand_from fmt" -l include-default-paths -d 'include default paths in addition to the provided ones' diff --git a/src/etc/completions/x.py.ps1 b/src/etc/completions/x.py.ps1 index f3d1d372c733..d9adb1778f2f 100644 --- a/src/etc/completions/x.py.ps1 +++ b/src/etc/completions/x.py.ps1 @@ -275,6 +275,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--reproducible-artifact', 'reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') [CompletionResult]::new('--check', 'check', [CompletionResultType]::ParameterName, 'check formatting instead of applying') + [CompletionResult]::new('--all', 'all', [CompletionResultType]::ParameterName, 'apply to all appropriate files, not just those that have been modified') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') [CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'use incremental compilation') diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index 82cacb52ffee..6cb9e95c8c10 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -1077,7 +1077,7 @@ _x.py() { return 0 ;; x.py__fmt) - opts="-v -i -j -h --check --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --check --all --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/src/etc/completions/x.py.zsh b/src/etc/completions/x.py.zsh index 12e96dbd40a3..24ddd1c4b7cc 100644 --- a/src/etc/completions/x.py.zsh +++ b/src/etc/completions/x.py.zsh @@ -271,6 +271,7 @@ _arguments "${_arguments_options[@]}" \ '*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \ '*--set=[override options in config.toml]:section.option=value:( )' \ '--check[check formatting instead of applying]' \ +'--all[apply to all appropriate files, not just those that have been modified]' \ '*-v[use verbose output (-vv for very verbose)]' \ '*--verbose[use verbose output (-vv for very verbose)]' \ '-i[use incremental compilation]' \ diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index bccad29e0a98..ac927e9a1942 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -11,7 +11,7 @@ use thin_vec::ThinVec; use crate::clean::{self, simplify, Lifetime}; use crate::clean::{ - clean_generic_param_def, clean_middle_ty, clean_predicate, clean_trait_ref_with_bindings, + clean_generic_param_def, clean_middle_ty, clean_predicate, clean_trait_ref_with_constraints, clean_ty_generics, }; use crate::core::DocContext; @@ -121,7 +121,7 @@ fn synthesize_auto_trait_impl<'tcx>( kind: Box::new(clean::ImplItem(Box::new(clean::Impl { safety: hir::Safety::Safe, generics, - trait_: Some(clean_trait_ref_with_bindings(cx, trait_ref, ThinVec::new())), + trait_: Some(clean_trait_ref_with_constraints(cx, trait_ref, ThinVec::new())), for_: clean_middle_ty(ty::Binder::dummy(ty), cx, None, None), items: Vec::new(), polarity, diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 29be3a70d54b..4c7a2ecdb53f 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -10,7 +10,7 @@ use thin_vec::ThinVec; use crate::clean; use crate::clean::{ - clean_middle_assoc_item, clean_middle_ty, clean_trait_ref_with_bindings, clean_ty_generics, + clean_middle_assoc_item, clean_middle_ty, clean_trait_ref_with_constraints, clean_ty_generics, }; use crate::core::DocContext; @@ -95,7 +95,7 @@ pub(crate) fn synthesize_blanket_impls( ), // FIXME(eddyb) compute both `trait_` and `for_` from // the post-inference `trait_ref`, as it's more accurate. - trait_: Some(clean_trait_ref_with_bindings( + trait_: Some(clean_trait_ref_with_constraints( cx, ty::Binder::dummy(trait_ref.instantiate_identity()), ThinVec::new(), diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index 20bcf1abf417..2857f74c744f 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -1,6 +1,6 @@ use super::*; -use rustc_ast::{MetaItemLit, Path, StrStyle}; +use rustc_ast::{MetaItemLit, Path, Safety, StrStyle}; use rustc_span::create_default_session_globals_then; use rustc_span::symbol::{kw, Ident}; use rustc_span::DUMMY_SP; @@ -16,6 +16,7 @@ fn name_value_cfg(name: &str, value: &str) -> Cfg { fn dummy_meta_item_word(name: &str) -> MetaItem { MetaItem { + unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(name)), kind: MetaItemKind::Word, span: DUMMY_SP, @@ -25,6 +26,7 @@ fn dummy_meta_item_word(name: &str) -> MetaItem { fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> MetaItem { let lit = MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP }; MetaItem { + unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(name)), kind: MetaItemKind::NameValue(lit), span: DUMMY_SP, @@ -34,6 +36,7 @@ fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> Meta macro_rules! dummy_meta_item_list { ($name:ident, [$($list:ident),* $(,)?]) => { MetaItem { + unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(stringify!($name))), kind: MetaItemKind::List(thin_vec![ $( @@ -48,6 +51,7 @@ macro_rules! dummy_meta_item_list { ($name:ident, [$($list:expr),* $(,)?]) => { MetaItem { + unsafety: Safety::Default, path: Path::from_ident(Ident::from_str(stringify!($name))), kind: MetaItemKind::List(thin_vec![ $( diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 2919a4c4beb0..0024e246ef00 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -20,7 +20,7 @@ use rustc_span::symbol::{kw, sym, Symbol}; use crate::clean::{ self, clean_bound_vars, clean_generics, clean_impl_item, clean_middle_assoc_item, - clean_middle_field, clean_middle_ty, clean_poly_fn_sig, clean_trait_ref_with_bindings, + clean_middle_field, clean_middle_ty, clean_poly_fn_sig, clean_trait_ref_with_constraints, clean_ty, clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes, AttributesExt, ImplKind, ItemId, Type, }; @@ -130,7 +130,10 @@ pub(crate) fn try_inline( } Res::Def(DefKind::Const, did) => { record_extern_fqn(cx, did, ItemType::Constant); - cx.with_param_env(did, |cx| clean::ConstantItem(build_const(cx, did))) + cx.with_param_env(did, |cx| { + let (generics, ty, ct) = build_const_item(cx, did); + clean::ConstantItem(generics, Box::new(ty), ct) + }) } Res::Def(DefKind::Macro(kind), did) => { let is_doc_hidden = cx.tcx.is_doc_hidden(did) @@ -566,7 +569,7 @@ pub(crate) fn build_impl( }; let polarity = tcx.impl_polarity(did); let trait_ = associated_trait - .map(|t| clean_trait_ref_with_bindings(cx, ty::Binder::dummy(t), ThinVec::new())); + .map(|t| clean_trait_ref_with_constraints(cx, ty::Binder::dummy(t), ThinVec::new())); if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() { super::build_deref_target_impls(cx, &trait_items, ret); } @@ -688,7 +691,7 @@ fn build_module_items( name: prim_ty.as_sym(), args: clean::GenericArgs::AngleBracketed { args: Default::default(), - bindings: ThinVec::new(), + constraints: ThinVec::new(), }, }], }, @@ -717,21 +720,20 @@ pub(crate) fn print_inlined_const(tcx: TyCtxt<'_>, did: DefId) -> String { } } -fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant { +fn build_const_item( + cx: &mut DocContext<'_>, + def_id: DefId, +) -> (clean::Generics, clean::Type, clean::Constant) { let mut generics = clean_ty_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id)); clean::simplify::move_bounds_to_generic_parameters(&mut generics); - - clean::Constant { - type_: Box::new(clean_middle_ty( - ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()), - cx, - Some(def_id), - None, - )), - generics, - kind: clean::ConstantKind::Extern { def_id }, - } + let ty = clean_middle_ty( + ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()), + cx, + None, + None, + ); + (generics, ty, clean::Constant { kind: clean::ConstantKind::Extern { def_id } }) } fn build_static(cx: &mut DocContext<'_>, did: DefId, mutable: bool) -> clean::Static { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 73737da482d0..da41f974068e 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -228,13 +228,15 @@ fn clean_generic_bound<'tcx>( GenericBound::TraitBound(clean_poly_trait_ref(t, cx), modifier) } + // FIXME(precise_capturing): Implement rustdoc support + hir::GenericBound::Use(..) => return None, }) } -pub(crate) fn clean_trait_ref_with_bindings<'tcx>( +pub(crate) fn clean_trait_ref_with_constraints<'tcx>( cx: &mut DocContext<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, - bindings: ThinVec, + constraints: ThinVec, ) -> Path { let kind = cx.tcx.def_kind(trait_ref.def_id()).into(); if !matches!(kind, ItemType::Trait | ItemType::TraitAlias) { @@ -245,7 +247,7 @@ pub(crate) fn clean_trait_ref_with_bindings<'tcx>( cx, trait_ref.def_id(), true, - bindings, + constraints, trait_ref.map_bound(|tr| tr.args), ); @@ -254,14 +256,14 @@ pub(crate) fn clean_trait_ref_with_bindings<'tcx>( path } -fn clean_poly_trait_ref_with_bindings<'tcx>( +fn clean_poly_trait_ref_with_constraints<'tcx>( cx: &mut DocContext<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, - bindings: ThinVec, + constraints: ThinVec, ) -> GenericBound { GenericBound::TraitBound( PolyTrait { - trait_: clean_trait_ref_with_bindings(cx, poly_trait_ref, bindings), + trait_: clean_trait_ref_with_constraints(cx, poly_trait_ref, constraints), generic_params: clean_bound_vars(poly_trait_ref.bound_vars()), }, hir::TraitBoundModifier::None, @@ -283,31 +285,17 @@ fn clean_lifetime<'tcx>(lifetime: &hir::Lifetime, cx: &mut DocContext<'tcx>) -> pub(crate) fn clean_const<'tcx>( constant: &hir::ConstArg<'_>, - cx: &mut DocContext<'tcx>, + _cx: &mut DocContext<'tcx>, ) -> Constant { - let def_id = cx.tcx.hir().body_owner_def_id(constant.value.body).to_def_id(); - Constant { - type_: Box::new(clean_middle_ty( - ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()), - cx, - Some(def_id), - None, - )), - generics: Generics::default(), - kind: ConstantKind::Anonymous { body: constant.value.body }, - } + Constant { kind: ConstantKind::Anonymous { body: constant.value.body } } } pub(crate) fn clean_middle_const<'tcx>( constant: ty::Binder<'tcx, ty::Const<'tcx>>, - cx: &mut DocContext<'tcx>, + _cx: &mut DocContext<'tcx>, ) -> Constant { // FIXME: instead of storing the stringified expression, store `self` directly instead. - Constant { - type_: Box::new(clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None, None)), - generics: Generics::default(), - kind: ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() }, - } + Constant { kind: ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() } } } pub(crate) fn clean_middle_region<'tcx>(region: ty::Region<'tcx>) -> Option { @@ -395,7 +383,7 @@ fn clean_poly_trait_predicate<'tcx>( let poly_trait_ref = pred.map_bound(|pred| pred.trait_ref); Some(WherePredicate::BoundPredicate { ty: clean_middle_ty(poly_trait_ref.self_ty(), cx, None, None), - bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, ThinVec::new())], + bounds: vec![clean_poly_trait_ref_with_constraints(cx, poly_trait_ref, ThinVec::new())], bound_params: Vec::new(), }) } @@ -481,8 +469,11 @@ fn clean_projection<'tcx>( return clean_middle_opaque_bounds(cx, bounds); } - let trait_ = - clean_trait_ref_with_bindings(cx, ty.map_bound(|ty| ty.trait_ref(cx.tcx)), ThinVec::new()); + let trait_ = clean_trait_ref_with_constraints( + cx, + ty.map_bound(|ty| ty.trait_ref(cx.tcx)), + ThinVec::new(), + ); let self_type = clean_middle_ty(ty.map_bound(|ty| ty.self_ty()), cx, None, None); let self_def_id = if let Some(def_id) = def_id { cx.tcx.opt_parent(def_id).or(Some(def_id)) @@ -522,7 +513,7 @@ fn projection_to_path_segment<'tcx>( def_id, ) .into(), - bindings: Default::default(), + constraints: Default::default(), }, } } @@ -1453,8 +1444,8 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( _ => return true, } match &assoc.args { - GenericArgs::AngleBracketed { args, bindings } => { - if !bindings.is_empty() + GenericArgs::AngleBracketed { args, constraints } => { + if !constraints.is_empty() || generics .params .iter() @@ -2135,7 +2126,7 @@ pub(crate) fn clean_middle_ty<'tcx>( let bindings = obj .projection_bounds() - .map(|pb| TypeBinding { + .map(|pb| AssocItemConstraint { assoc: projection_to_path_segment( pb.map_bound(|pb| { pb @@ -2149,7 +2140,7 @@ pub(crate) fn clean_middle_ty<'tcx>( }), cx, ), - kind: TypeBindingKind::Equality { + kind: AssocItemConstraintKind::Equality { term: clean_middle_term(pb.map_bound(|pb| pb.term), cx), }, }) @@ -2198,7 +2189,7 @@ pub(crate) fn clean_middle_ty<'tcx>( def_id, ) .into(), - bindings: Default::default(), + constraints: Default::default(), }, }, should_show_cast: false, @@ -2304,14 +2295,14 @@ fn clean_middle_opaque_bounds<'tcx>( .filter_map(|bound| { if let ty::ClauseKind::Projection(proj) = bound.kind().skip_binder() { if proj.projection_term.trait_ref(cx.tcx) == trait_ref.skip_binder() { - Some(TypeBinding { + Some(AssocItemConstraint { assoc: projection_to_path_segment( // FIXME: This needs to be made resilient for `AliasTerm`s that // are associated consts. bound.kind().rebind(proj.projection_term.expect_ty(cx.tcx)), cx, ), - kind: TypeBindingKind::Equality { + kind: AssocItemConstraintKind::Equality { term: clean_middle_term(bound.kind().rebind(proj.term), cx), }, }) @@ -2324,7 +2315,7 @@ fn clean_middle_opaque_bounds<'tcx>( }) .collect(); - Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, bindings)) + Some(clean_poly_trait_ref_with_constraints(cx, trait_ref, bindings)) }) .collect::>(); @@ -2505,11 +2496,12 @@ fn clean_generic_args<'tcx>( cx: &mut DocContext<'tcx>, ) -> GenericArgs { // FIXME(return_type_notation): Fix RTN parens rendering - if generic_args.parenthesized == hir::GenericArgsParentheses::ParenSugar { - let output = clean_ty(generic_args.bindings[0].ty(), cx); - let output = if output != Type::Tuple(Vec::new()) { Some(Box::new(output)) } else { None }; - let inputs = - generic_args.inputs().iter().map(|x| clean_ty(x, cx)).collect::>().into(); + if let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() { + let inputs = inputs.iter().map(|x| clean_ty(x, cx)).collect::>().into(); + let output = match output.kind { + hir::TyKind::Tup(&[]) => None, + _ => Some(Box::new(clean_ty(output, cx))), + }; GenericArgs::Parenthesized { inputs, output } } else { let args = generic_args @@ -2536,9 +2528,12 @@ fn clean_generic_args<'tcx>( }) .collect::>() .into(); - let bindings = - generic_args.bindings.iter().map(|x| clean_type_binding(x, cx)).collect::>(); - GenericArgs::AngleBracketed { args, bindings } + let constraints = generic_args + .constraints + .iter() + .map(|c| clean_assoc_item_constraint(c, cx)) + .collect::>(); + GenericArgs::AngleBracketed { args, constraints } } } @@ -2731,11 +2726,11 @@ fn clean_maybe_renamed_item<'tcx>( ItemKind::Static(ty, mutability, body_id) => { StaticItem(Static { type_: clean_ty(ty, cx), mutability, expr: Some(body_id) }) } - ItemKind::Const(ty, generics, body_id) => ConstantItem(Constant { - type_: Box::new(clean_ty(ty, cx)), - generics: clean_generics(generics, cx), - kind: ConstantKind::Local { body: body_id, def_id }, - }), + ItemKind::Const(ty, generics, body_id) => ConstantItem( + clean_generics(generics, cx), + Box::new(clean_ty(ty, cx)), + Constant { kind: ConstantKind::Local { body: body_id, def_id } }, + ), ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy { bounds: ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), generics: clean_generics(ty.generics, cx), @@ -3082,7 +3077,9 @@ fn clean_maybe_renamed_foreign_item<'tcx>( let def_id = item.owner_id.to_def_id(); cx.with_param_env(def_id, |cx| { let kind = match item.kind { - hir::ForeignItemKind::Fn(decl, names, generics) => { + // FIXME(missing_unsafe_on_extern) handle safety of foreign fns. + // Safety was added as part of the implementation of unsafe extern blocks PR #124482 + hir::ForeignItemKind::Fn(decl, names, generics, _) => { let (generics, decl) = enter_impl_trait(cx, |cx| { // NOTE: generics must be cleaned before args let generics = clean_generics(generics, cx); @@ -3092,7 +3089,9 @@ fn clean_maybe_renamed_foreign_item<'tcx>( }); ForeignFunctionItem(Box::new(Function { decl, generics })) } - hir::ForeignItemKind::Static(ty, mutability) => { + // FIXME(missing_unsafe_on_extern) handle safety of foreign statics. + // Safety was added as part of the implementation of unsafe extern blocks PR #124482 + hir::ForeignItemKind::Static(ty, mutability, _) => { ForeignStaticItem(Static { type_: clean_ty(ty, cx), mutability, expr: None }) } hir::ForeignItemKind::Type => ForeignTypeItem, @@ -3107,20 +3106,20 @@ fn clean_maybe_renamed_foreign_item<'tcx>( }) } -fn clean_type_binding<'tcx>( - type_binding: &hir::TypeBinding<'tcx>, +fn clean_assoc_item_constraint<'tcx>( + constraint: &hir::AssocItemConstraint<'tcx>, cx: &mut DocContext<'tcx>, -) -> TypeBinding { - TypeBinding { +) -> AssocItemConstraint { + AssocItemConstraint { assoc: PathSegment { - name: type_binding.ident.name, - args: clean_generic_args(type_binding.gen_args, cx), + name: constraint.ident.name, + args: clean_generic_args(constraint.gen_args, cx), }, - kind: match type_binding.kind { - hir::TypeBindingKind::Equality { ref term } => { - TypeBindingKind::Equality { term: clean_hir_term(term, cx) } + kind: match constraint.kind { + hir::AssocItemConstraintKind::Equality { ref term } => { + AssocItemConstraintKind::Equality { term: clean_hir_term(term, cx) } } - hir::TypeBindingKind::Constraint { bounds } => TypeBindingKind::Constraint { + hir::AssocItemConstraintKind::Bound { bounds } => AssocItemConstraintKind::Bound { bounds: bounds.iter().filter_map(|b| clean_generic_bound(b, cx)).collect(), }, }, diff --git a/src/librustdoc/clean/render_macro_matchers.rs b/src/librustdoc/clean/render_macro_matchers.rs index 5ac1b742c386..995919f73f83 100644 --- a/src/librustdoc/clean/render_macro_matchers.rs +++ b/src/librustdoc/clean/render_macro_matchers.rs @@ -65,7 +65,7 @@ fn snippet_equal_to_token(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Option parser, Err(errs) => { errs.into_iter().for_each(|err| err.cancel()); diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 5a3ccb6239a0..af61eb6ae8de 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -90,10 +90,10 @@ pub(crate) fn merge_bounds( let last = trait_ref.trait_.segments.last_mut().expect("segments were empty"); match last.args { - PP::AngleBracketed { ref mut bindings, .. } => { - bindings.push(clean::TypeBinding { + PP::AngleBracketed { ref mut constraints, .. } => { + constraints.push(clean::AssocItemConstraint { assoc: assoc.clone(), - kind: clean::TypeBindingKind::Equality { term: rhs.clone() }, + kind: clean::AssocItemConstraintKind::Equality { term: rhs.clone() }, }); } PP::Parenthesized { ref mut output, .. } => match output { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index b54ec6245247..69678b727c11 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -627,7 +627,7 @@ impl Item { ) -> hir::FnHeader { let sig = tcx.fn_sig(def_id).skip_binder(); let constness = - if tcx.is_const_fn(def_id) && is_unstable_const_fn(tcx, def_id).is_none() { + if tcx.is_const_fn(def_id) || is_unstable_const_fn(tcx, def_id).is_some() { hir::Constness::Const } else { hir::Constness::NotConst @@ -649,9 +649,8 @@ impl Item { hir::Safety::Unsafe }, abi, - constness: if abi == Abi::RustIntrinsic - && tcx.is_const_fn(def_id) - && is_unstable_const_fn(tcx, def_id).is_none() + constness: if tcx.is_const_fn(def_id) + || is_unstable_const_fn(tcx, def_id).is_some() { hir::Constness::Const } else { @@ -831,7 +830,6 @@ pub(crate) enum ItemKind { TypeAliasItem(Box), OpaqueTyItem(OpaqueTy), StaticItem(Static), - ConstantItem(Constant), TraitItem(Box), TraitAliasItem(TraitAlias), ImplItem(Box), @@ -854,6 +852,7 @@ pub(crate) enum ItemKind { PrimitiveItem(PrimitiveType), /// A required associated constant in a trait declaration. TyAssocConstItem(Generics, Box), + ConstantItem(Generics, Box, Constant), /// An associated constant in a trait impl or a provided one in a trait declaration. AssocConstItem(Generics, Box, ConstantKind), /// A required associated type in a trait declaration. @@ -889,7 +888,7 @@ impl ItemKind { | TypeAliasItem(_) | OpaqueTyItem(_) | StaticItem(_) - | ConstantItem(_) + | ConstantItem(_, _, _) | TraitAliasItem(_) | TyMethodItem(_) | MethodItem(_, _) @@ -923,7 +922,7 @@ impl ItemKind { | TypeAliasItem(_) | OpaqueTyItem(_) | StaticItem(_) - | ConstantItem(_) + | ConstantItem(_, _, _) | TraitAliasItem(_) | ForeignFunctionItem(_) | ForeignStaticItem(_) @@ -1452,7 +1451,7 @@ impl Trait { tcx.trait_def(self.def_id).safety } pub(crate) fn is_object_safe(&self, tcx: TyCtxt<'_>) -> bool { - tcx.check_is_object_safe(self.def_id) + tcx.is_object_safe(self.def_id) } } @@ -1627,9 +1626,9 @@ impl Type { if let Type::ImplTrait(mut v) = self && let Some(GenericBound::TraitBound(PolyTrait { mut trait_, .. }, _)) = v.pop() && let Some(segment) = trait_.segments.pop() - && let GenericArgs::AngleBracketed { mut bindings, .. } = segment.args - && let Some(binding) = bindings.pop() - && let TypeBindingKind::Equality { term } = binding.kind + && let GenericArgs::AngleBracketed { mut constraints, .. } = segment.args + && let Some(constraint) = constraints.pop() + && let AssocItemConstraintKind::Equality { term } = constraint.kind && let Term::Type(ty) = term { ty @@ -2134,7 +2133,9 @@ impl Discriminant { /// Will be `None` in the case of cross-crate reexports, and may be /// simplified pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> Option { - self.expr.map(|body| rendered_const(tcx, body)) + self.expr.map(|body| { + rendered_const(tcx, tcx.hir().body(body), tcx.hir().body_owner_def_id(body)) + }) } pub(crate) fn value(&self, tcx: TyCtxt<'_>, with_underscores: bool) -> String { print_evaluated_const(tcx, self.value, with_underscores, false).unwrap() @@ -2259,34 +2260,38 @@ impl GenericArg { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) enum GenericArgs { - AngleBracketed { args: Box<[GenericArg]>, bindings: ThinVec }, + AngleBracketed { args: Box<[GenericArg]>, constraints: ThinVec }, Parenthesized { inputs: Box<[Type]>, output: Option> }, } impl GenericArgs { pub(crate) fn is_empty(&self) -> bool { match self { - GenericArgs::AngleBracketed { args, bindings } => { - args.is_empty() && bindings.is_empty() + GenericArgs::AngleBracketed { args, constraints } => { + args.is_empty() && constraints.is_empty() } GenericArgs::Parenthesized { inputs, output } => inputs.is_empty() && output.is_none(), } } - pub(crate) fn bindings<'a>(&'a self) -> Box + 'a> { + pub(crate) fn constraints<'a>(&'a self) -> Box + 'a> { match self { - GenericArgs::AngleBracketed { bindings, .. } => Box::new(bindings.iter().cloned()), + GenericArgs::AngleBracketed { constraints, .. } => { + Box::new(constraints.iter().cloned()) + } GenericArgs::Parenthesized { output, .. } => Box::new( output .as_ref() - .map(|ty| TypeBinding { + .map(|ty| AssocItemConstraint { assoc: PathSegment { name: sym::Output, args: GenericArgs::AngleBracketed { args: Vec::new().into_boxed_slice(), - bindings: ThinVec::new(), + constraints: ThinVec::new(), }, }, - kind: TypeBindingKind::Equality { term: Term::Type((**ty).clone()) }, + kind: AssocItemConstraintKind::Equality { + term: Term::Type((**ty).clone()), + }, }) .into_iter(), ), @@ -2359,8 +2364,6 @@ pub(crate) struct Static { #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct Constant { - pub(crate) type_: Box, - pub(crate) generics: Generics, pub(crate) kind: ConstantKind, } @@ -2420,7 +2423,7 @@ impl ConstantKind { ConstantKind::TyConst { ref expr } => expr.to_string(), ConstantKind::Extern { def_id } => print_inlined_const(tcx, def_id), ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => { - rendered_const(tcx, body) + rendered_const(tcx, tcx.hir().body(body), tcx.hir().body_owner_def_id(body)) } } } @@ -2545,18 +2548,27 @@ pub(crate) struct ProcMacro { pub(crate) helpers: Vec, } -/// An type binding on an associated type (e.g., `A = Bar` in `Foo` or -/// `A: Send + Sync` in `Foo`). +/// A constraint on an associated item. +/// +/// ### Examples +/// +/// * the `A = Ty` and `B = Ty` in `Trait` +/// * the `G = Ty` in `Trait = Ty>` +/// * the `A: Bound` in `Trait` +/// * the `RetTy` in `Trait(ArgTy, ArgTy) -> RetTy` +/// * the `C = { Ct }` in `Trait` (feature `associated_const_equality`) +/// * the `f(): Bound` in `Trait` (feature `return_type_notation`) #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub(crate) struct TypeBinding { +pub(crate) struct AssocItemConstraint { pub(crate) assoc: PathSegment, - pub(crate) kind: TypeBindingKind, + pub(crate) kind: AssocItemConstraintKind, } +/// The kind of [associated item constraint][AssocItemConstraint]. #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub(crate) enum TypeBindingKind { +pub(crate) enum AssocItemConstraintKind { Equality { term: Term }, - Constraint { bounds: Vec }, + Bound { bounds: Vec }, } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index aa923cc61173..7c83d4387193 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -2,9 +2,10 @@ use crate::clean::auto_trait::synthesize_auto_trait_impls; use crate::clean::blanket_impl::synthesize_blanket_impls; use crate::clean::render_macro_matchers::render_macro_matcher; use crate::clean::{ - clean_doc_module, clean_middle_const, clean_middle_region, clean_middle_ty, inline, Crate, - ExternalCrate, Generic, GenericArg, GenericArgs, ImportSource, Item, ItemKind, Lifetime, Path, - PathSegment, Primitive, PrimitiveType, Term, Type, TypeBinding, TypeBindingKind, + clean_doc_module, clean_middle_const, clean_middle_region, clean_middle_ty, inline, + AssocItemConstraint, AssocItemConstraintKind, Crate, ExternalCrate, Generic, GenericArg, + GenericArgs, ImportSource, Item, ItemKind, Lifetime, Path, PathSegment, Primitive, + PrimitiveType, Term, Type, }; use crate::core::DocContext; use crate::html::format::visibility_to_src_with_space; @@ -200,11 +201,11 @@ fn can_elide_generic_arg<'tcx>( actual.skip_binder() == default.skip_binder() } -fn clean_middle_generic_args_with_bindings<'tcx>( +fn clean_middle_generic_args_with_constraints<'tcx>( cx: &mut DocContext<'tcx>, did: DefId, has_self: bool, - bindings: ThinVec, + constraints: ThinVec, ty_args: ty::Binder<'tcx, GenericArgsRef<'tcx>>, ) -> GenericArgs { let args = clean_middle_generic_args(cx, ty_args.map_bound(|args| &args[..]), has_self, did); @@ -219,17 +220,19 @@ fn clean_middle_generic_args_with_bindings<'tcx>( // The trait's first substitution is the one after self, if there is one. match ty.skip_binder().kind() { ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(ty.rebind(t), cx, None, None)).collect::>().into(), - _ => return GenericArgs::AngleBracketed { args: args.into(), bindings }, + _ => return GenericArgs::AngleBracketed { args: args.into(), constraints }, }; - let output = bindings.into_iter().next().and_then(|binding| match binding.kind { - TypeBindingKind::Equality { term: Term::Type(ty) } if ty != Type::Tuple(Vec::new()) => { + let output = constraints.into_iter().next().and_then(|binding| match binding.kind { + AssocItemConstraintKind::Equality { term: Term::Type(ty) } + if ty != Type::Tuple(Vec::new()) => + { Some(Box::new(ty)) } _ => None, }); GenericArgs::Parenthesized { inputs, output } } else { - GenericArgs::AngleBracketed { args: args.into(), bindings } + GenericArgs::AngleBracketed { args: args.into(), constraints } } } @@ -237,7 +240,7 @@ pub(super) fn clean_middle_path<'tcx>( cx: &mut DocContext<'tcx>, did: DefId, has_self: bool, - bindings: ThinVec, + constraints: ThinVec, args: ty::Binder<'tcx, GenericArgsRef<'tcx>>, ) -> Path { let def_kind = cx.tcx.def_kind(did); @@ -246,7 +249,7 @@ pub(super) fn clean_middle_path<'tcx>( res: Res::Def(def_kind, did), segments: thin_vec![PathSegment { name, - args: clean_middle_generic_args_with_bindings(cx, did, has_self, bindings, args), + args: clean_middle_generic_args_with_constraints(cx, did, has_self, constraints, args), }], } } @@ -342,7 +345,7 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String { match n.kind() { ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args: _ }) => { let s = if let Some(def) = def.as_local() { - rendered_const(cx.tcx, cx.tcx.hir().body_owned_by(def)) + rendered_const(cx.tcx, &cx.tcx.hir().body_owned_by(def), def) } else { inline::print_inlined_const(cx.tcx, def) }; @@ -350,8 +353,8 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String { s } // array lengths are obviously usize - ty::ConstKind::Value(ty::ValTree::Leaf(scalar)) - if *n.ty().kind() == ty::Uint(ty::UintTy::Usize) => + ty::ConstKind::Value(ty, ty::ValTree::Leaf(scalar)) + if *ty.kind() == ty::Uint(ty::UintTy::Usize) => { scalar.to_string() } @@ -428,8 +431,7 @@ fn print_const_with_custom_print_scalar<'tcx>( (mir::Const::Val(mir::ConstValue::Scalar(int), _), ty::Int(i)) => { let ty = ct.ty(); let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size; - let data = int.assert_bits(size); - let sign_extended_data = size.sign_extend(data) as i128; + let sign_extended_data = int.assert_scalar_int().to_int(size); let mut output = if with_underscores { format_integer_with_underscore_sep(&sign_extended_data.to_string()) } else { diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 012afada1e5e..45bd1616e83c 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -8,6 +8,7 @@ use std::path::PathBuf; use std::str::FromStr; use rustc_data_structures::fx::FxHashMap; +use rustc_errors::DiagCtxtHandle; use rustc_session::config::{ self, parse_crate_types_from_list, parse_externs, parse_target_triple, CrateType, }; @@ -128,6 +129,8 @@ pub(crate) struct Options { pub(crate) enable_per_target_ignores: bool, /// Do not run doctests, compile them if should_test is active. pub(crate) no_run: bool, + /// What sources are being mapped. + pub(crate) remap_path_prefix: Vec<(PathBuf, PathBuf)>, /// The path to a rustc-like binary to build tests with. If not set, we /// default to loading from `$sysroot/bin/rustc`. @@ -211,6 +214,7 @@ impl fmt::Debug for Options { .field("run_check", &self.run_check) .field("no_run", &self.no_run) .field("test_builder_wrappers", &self.test_builder_wrappers) + .field("remap-file-prefix", &self.remap_path_prefix) .field("nocapture", &self.nocapture) .field("scrape_examples_options", &self.scrape_examples_options) .field("unstable_features", &self.unstable_features) @@ -372,10 +376,18 @@ impl Options { let codegen_options = CodegenOptions::build(early_dcx, matches); let unstable_opts = UnstableOptions::build(early_dcx, matches); + let remap_path_prefix = match parse_remap_path_prefix(&matches) { + Ok(prefix_mappings) => prefix_mappings, + Err(err) => { + early_dcx.early_fatal(err); + } + }; + let dcx = new_dcx(error_format, None, diagnostic_width, &unstable_opts); + let dcx = dcx.handle(); // check for deprecated options - check_deprecated_options(matches, &dcx); + check_deprecated_options(matches, dcx); if matches.opt_strs("passes") == ["list"] { println!("Available passes for running rustdoc:"); @@ -448,7 +460,7 @@ impl Options { println!("rustdoc: [check-theme] Starting tests! (Ignoring all other arguments)"); for theme_file in to_check.iter() { print!(" - Checking \"{theme_file}\"..."); - let (success, differences) = theme::test_theme_against(theme_file, &paths, &dcx); + let (success, differences) = theme::test_theme_against(theme_file, &paths, dcx); if !differences.is_empty() || !success { println!(" FAILED"); errors += 1; @@ -593,7 +605,7 @@ impl Options { .with_help("arguments to --theme must have a .css extension") .emit(); } - let (success, ret) = theme::test_theme_against(&theme_file, &paths, &dcx); + let (success, ret) = theme::test_theme_against(&theme_file, &paths, dcx); if !success { dcx.fatal(format!("error loading theme file: \"{theme_s}\"")); } else if !ret.is_empty() { @@ -620,7 +632,7 @@ impl Options { &matches.opt_strs("markdown-before-content"), &matches.opt_strs("markdown-after-content"), nightly_options::match_is_nightly_build(matches), - &dcx, + dcx, &mut id_map, edition, &None, @@ -731,9 +743,9 @@ impl Options { ); } - let scrape_examples_options = ScrapeExamplesOptions::new(matches, &dcx); + let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx); let with_examples = matches.opt_strs("with-examples"); - let call_locations = crate::scrape_examples::load_call_locations(with_examples, &dcx); + let call_locations = crate::scrape_examples::load_call_locations(with_examples, dcx); let unstable_features = rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref()); @@ -772,6 +784,7 @@ impl Options { run_check, no_run, test_builder_wrappers, + remap_path_prefix, nocapture, crate_name, output_format, @@ -820,8 +833,23 @@ impl Options { } } +fn parse_remap_path_prefix( + matches: &getopts::Matches, +) -> Result, &'static str> { + matches + .opt_strs("remap-path-prefix") + .into_iter() + .map(|remap| { + remap + .rsplit_once('=') + .ok_or("--remap-path-prefix must contain '=' between FROM and TO") + .map(|(from, to)| (PathBuf::from(from), PathBuf::from(to))) + }) + .collect() +} + /// Prints deprecation warnings for deprecated options -fn check_deprecated_options(matches: &getopts::Matches, dcx: &rustc_errors::DiagCtxt) { +fn check_deprecated_options(matches: &getopts::Matches, dcx: DiagCtxtHandle<'_>) { let deprecated_flags = []; for &flag in deprecated_flags.iter() { diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index feb03b9a823e..5d8e61f9fa0d 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -3,7 +3,7 @@ use rustc_data_structures::sync::Lrc; use rustc_data_structures::unord::UnordSet; use rustc_errors::emitter::{stderr_destination, DynEmitter, HumanEmitter}; use rustc_errors::json::JsonEmitter; -use rustc_errors::{codes::*, ErrorGuaranteed, TerminalUrl}; +use rustc_errors::{codes::*, DiagCtxtHandle, ErrorGuaranteed, TerminalUrl}; use rustc_feature::UnstableFeatures; use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId}; @@ -284,9 +284,9 @@ pub(crate) fn create_config( } let hir = tcx.hir(); - let body = hir.body(hir.body_owned_by(def_id)); + let body = hir.body_owned_by(def_id); debug!("visiting body for {def_id:?}"); - EmitIgnoredResolutionErrors::new(tcx).visit_body(body); + EmitIgnoredResolutionErrors::new(tcx).visit_body(&body); (rustc_interface::DEFAULT_QUERY_PROVIDERS.typeck)(tcx, def_id) }; }), @@ -372,14 +372,14 @@ pub(crate) fn run_global_ctxt( tcx.node_lint( crate::lint::MISSING_CRATE_LEVEL_DOCS, DocContext::as_local_hir_id(tcx, krate.module.item_id).unwrap(), - "no documentation found for this crate's top-level module", |lint| { + lint.primary_message("no documentation found for this crate's top-level module"); lint.help(help); }, ); } - fn report_deprecated_attr(name: &str, dcx: &rustc_errors::DiagCtxt, sp: Span) { + fn report_deprecated_attr(name: &str, dcx: DiagCtxtHandle<'_>, sp: Span) { let mut msg = dcx.struct_span_warn(sp, format!("the `#![doc({name})]` attribute is deprecated")); msg.note( diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 82f9cf1feaeb..40cc4a9d4412 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1,27 +1,23 @@ +mod make; +mod markdown; +mod rust; + +pub(crate) use make::make_test; +pub(crate) use markdown::test as test_markdown; + use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::sync::Lrc; -use rustc_errors::emitter::stderr_destination; -use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError}; -use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; -use rustc_hir::{self as hir, intravisit, CRATE_HIR_ID}; +use rustc_errors::{ColorConfig, DiagCtxtHandle, ErrorGuaranteed, FatalError}; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_hir::CRATE_HIR_ID; use rustc_interface::interface; -use rustc_middle::hir::map::Map; -use rustc_middle::hir::nested_filter; -use rustc_middle::ty::TyCtxt; -use rustc_parse::maybe_new_parser_from_source_str; -use rustc_parse::parser::attr::InnerAttrPolicy; -use rustc_resolve::rustdoc::span_of_fragments; use rustc_session::config::{self, CrateType, ErrorOutputType}; -use rustc_session::parse::ParseSess; -use rustc_session::{lint, Session}; +use rustc_session::lint; use rustc_span::edition::Edition; -use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; -use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; +use rustc_span::FileName; use rustc_target::spec::{Target, TargetTriple}; -use std::env; use std::fs::File; use std::io::{self, Write}; use std::panic; @@ -33,14 +29,17 @@ use std::sync::{Arc, Mutex}; use tempfile::{Builder as TempFileBuilder, TempDir}; -use crate::clean::{types::AttributesExt, Attributes}; use crate::config::Options as RustdocOptions; -use crate::html::markdown::{self, ErrorCodes, Ignore, LangString}; +use crate::html::markdown::{ErrorCodes, Ignore, LangString, MdRelLine}; use crate::lint::init_lints; +use self::rust::HirCollector; + /// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`). -#[derive(Clone, Default)] +#[derive(Clone)] pub(crate) struct GlobalTestOptions { + /// Name of the crate (for regular `rustdoc`) or Markdown file (for `rustdoc foo.md`). + pub(crate) crate_name: String, /// Whether to disable the default `extern crate my_crate;` when creating doctests. pub(crate) no_crate_inject: bool, /// Whether inserting extra indent spaces in code block, @@ -48,6 +47,8 @@ pub(crate) struct GlobalTestOptions { pub(crate) insert_indent_space: bool, /// Additional crate-level attributes to add to doctests. pub(crate) attrs: Vec, + /// Path to file containing arguments for the invocation of rustc. + pub(crate) args_file: PathBuf, } pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> Result<(), String> { @@ -80,7 +81,7 @@ pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> let content = content.join("\n"); - file.write(content.as_bytes()) + file.write_all(content.as_bytes()) .map_err(|error| format!("failed to write arguments to temporary file: {error:?}"))?; Ok(()) } @@ -89,10 +90,7 @@ fn get_doctest_dir() -> io::Result { TempFileBuilder::new().prefix("rustdoctest").tempdir() } -pub(crate) fn run( - dcx: &rustc_errors::DiagCtxt, - options: RustdocOptions, -) -> Result<(), ErrorGuaranteed> { +pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<(), ErrorGuaranteed> { let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name; // See core::create_config for what's going on here. @@ -128,6 +126,7 @@ pub(crate) fn run( edition: options.edition, target_triple: options.target.clone(), crate_name: options.crate_name.clone(), + remap_path_prefix: options.remap_path_prefix.clone(), ..config::Options::default() }; @@ -166,43 +165,28 @@ pub(crate) fn run( Ok(temp_dir) => temp_dir, Err(error) => return crate::wrap_return(dcx, Err(error)), }; - let file_path = temp_dir.path().join("rustdoc-cfgs"); - crate::wrap_return(dcx, generate_args_file(&file_path, &options))?; + let args_path = temp_dir.path().join("rustdoc-cfgs"); + crate::wrap_return(dcx, generate_args_file(&args_path, &options))?; let (tests, unused_extern_reports, compiling_test_count) = interface::run_compiler(config, |compiler| { compiler.enter(|queries| { let collector = queries.global_ctxt()?.enter(|tcx| { + let crate_name = tcx.crate_name(LOCAL_CRATE).to_string(); let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID); - - let opts = scrape_test_config(crate_attrs); + let opts = scrape_test_config(crate_name, crate_attrs, args_path); let enable_per_target_ignores = options.enable_per_target_ignores; - let mut collector = Collector::new( - tcx.crate_name(LOCAL_CRATE).to_string(), - options, - false, - opts, - Some(compiler.sess.psess.clone_source_map()), - None, - enable_per_target_ignores, - file_path, - ); - let mut hir_collector = HirCollector { - sess: &compiler.sess, - collector: &mut collector, - map: tcx.hir(), - codes: ErrorCodes::from( - compiler.sess.opts.unstable_features.is_nightly_build(), - ), + let mut collector = CreateRunnableDoctests::new(options, opts); + let hir_collector = HirCollector::new( + &compiler.sess, + tcx.hir(), + ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()), + enable_per_target_ignores, tcx, - }; - hir_collector.visit_testable( - "".to_string(), - CRATE_DEF_ID, - tcx.hir().span(CRATE_HIR_ID), - |this| tcx.hir().walk_toplevel_module(this), ); + let tests = hir_collector.collect_crate(); + tests.into_iter().for_each(|t| collector.add_test(t)); collector }); @@ -273,11 +257,20 @@ pub(crate) fn run_tests( } // Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade. -fn scrape_test_config(attrs: &[ast::Attribute]) -> GlobalTestOptions { +fn scrape_test_config( + crate_name: String, + attrs: &[ast::Attribute], + args_file: PathBuf, +) -> GlobalTestOptions { use rustc_ast_pretty::pprust; - let mut opts = - GlobalTestOptions { no_crate_inject: false, attrs: Vec::new(), insert_indent_space: false }; + let mut opts = GlobalTestOptions { + crate_name, + no_crate_inject: false, + attrs: Vec::new(), + insert_indent_space: false, + args_file, + }; let test_attrs: Vec<_> = attrs .iter() @@ -370,30 +363,25 @@ fn wrapped_rustc_command(rustc_wrappers: &[PathBuf], rustc_binary: &Path) -> Com command } +struct RunnableDoctest { + full_test_code: String, + full_test_line_offset: usize, + test_opts: IndividualTestOptions, + global_opts: GlobalTestOptions, + scraped_test: ScrapedDoctest, +} + fn run_test( - test: &str, - crate_name: &str, - line: usize, - rustdoc_options: IndividualTestOptions, - mut lang_string: LangString, - no_run: bool, - opts: &GlobalTestOptions, - edition: Edition, - path: PathBuf, + doctest: RunnableDoctest, + rustdoc_options: &RustdocOptions, + supports_color: bool, report_unused_externs: impl Fn(UnusedExterns), ) -> Result<(), TestFailure> { - let (test, line_offset, supports_color) = make_test( - test, - Some(crate_name), - lang_string.test_harness, - opts, - edition, - Some(&rustdoc_options.test_id), - ); - + let scraped_test = &doctest.scraped_test; + let langstr = &scraped_test.langstr; // Make sure we emit well-formed executable names for our target. let rust_out = add_exe_suffix("rust_out".to_owned(), &rustdoc_options.target); - let output_file = rustdoc_options.outdir.path().join(rust_out); + let output_file = doctest.test_opts.outdir.path().join(rust_out); let rustc_binary = rustdoc_options .test_builder @@ -401,33 +389,41 @@ fn run_test( .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); - compiler.arg(&format!("@{}", rustdoc_options.arg_file.display())); + compiler.arg(&format!("@{}", doctest.global_opts.args_file.display())); if let Some(sysroot) = &rustdoc_options.maybe_sysroot { compiler.arg(format!("--sysroot={}", sysroot.display())); } - compiler.arg("--edition").arg(&edition.to_string()); - compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", path); - compiler.env("UNSTABLE_RUSTDOC_TEST_LINE", format!("{}", line as isize - line_offset as isize)); + compiler.arg("--edition").arg(&scraped_test.edition(rustdoc_options).to_string()); + compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path); + compiler.env( + "UNSTABLE_RUSTDOC_TEST_LINE", + format!("{}", scraped_test.line as isize - doctest.full_test_line_offset as isize), + ); compiler.arg("-o").arg(&output_file); - if lang_string.test_harness { + if langstr.test_harness { compiler.arg("--test"); } - if rustdoc_options.is_json_unused_externs_enabled && !lang_string.compile_fail { + if rustdoc_options.json_unused_externs.is_enabled() && !langstr.compile_fail { compiler.arg("--error-format=json"); compiler.arg("--json").arg("unused-externs"); compiler.arg("-W").arg("unused_crate_dependencies"); compiler.arg("-Z").arg("unstable-options"); } - if no_run && !lang_string.compile_fail && rustdoc_options.should_persist_doctests { + if scraped_test.no_run(rustdoc_options) + && !langstr.compile_fail + && rustdoc_options.persist_doctests.is_none() + { + // FIXME: why does this code check if it *shouldn't* persist doctests + // -- shouldn't it be the negation? compiler.arg("--emit=metadata"); } - compiler.arg("--target").arg(match rustdoc_options.target { + compiler.arg("--target").arg(match &rustdoc_options.target { TargetTriple::TargetTriple(s) => s, TargetTriple::TargetJson { path_for_rustdoc, .. } => { - path_for_rustdoc.to_str().expect("target path must be valid unicode").to_string() + path_for_rustdoc.to_str().expect("target path must be valid unicode") } }); if let ErrorOutputType::HumanReadable(kind) = rustdoc_options.error_format { @@ -459,7 +455,7 @@ fn run_test( let mut child = compiler.spawn().expect("Failed to spawn rustc process"); { let stdin = child.stdin.as_mut().expect("Failed to open stdin"); - stdin.write_all(test.as_bytes()).expect("could write out test sources"); + stdin.write_all(doctest.full_test_code.as_bytes()).expect("could write out test sources"); } let output = child.wait_with_output().expect("Failed to read stdout"); @@ -490,20 +486,26 @@ fn run_test( } let _bomb = Bomb(&out); - match (output.status.success(), lang_string.compile_fail) { + match (output.status.success(), langstr.compile_fail) { (true, true) => { return Err(TestFailure::UnexpectedCompilePass); } (true, false) => {} (false, true) => { - if !lang_string.error_codes.is_empty() { + if !langstr.error_codes.is_empty() { // We used to check if the output contained "error[{}]: " but since we added the // colored output, we can't anymore because of the color escape characters before // the ":". - lang_string.error_codes.retain(|err| !out.contains(&format!("error[{err}]"))); + let missing_codes: Vec = scraped_test + .langstr + .error_codes + .iter() + .filter(|err| !out.contains(&format!("error[{err}]"))) + .cloned() + .collect(); - if !lang_string.error_codes.is_empty() { - return Err(TestFailure::MissingErrorCodes(lang_string.error_codes)); + if !missing_codes.is_empty() { + return Err(TestFailure::MissingErrorCodes(missing_codes)); } } } @@ -512,7 +514,7 @@ fn run_test( } } - if no_run { + if scraped_test.no_run(rustdoc_options) { return Ok(()); } @@ -520,15 +522,15 @@ fn run_test( let mut cmd; let output_file = make_maybe_absolute_path(output_file); - if let Some(tool) = rustdoc_options.runtool { + if let Some(tool) = &rustdoc_options.runtool { let tool = make_maybe_absolute_path(tool.into()); cmd = Command::new(tool); - cmd.args(rustdoc_options.runtool_args); + cmd.args(&rustdoc_options.runtool_args); cmd.arg(output_file); } else { cmd = Command::new(output_file); } - if let Some(run_directory) = rustdoc_options.test_run_directory { + if let Some(run_directory) = &rustdoc_options.test_run_directory { cmd.current_dir(run_directory); } @@ -544,9 +546,9 @@ fn run_test( match result { Err(e) => return Err(TestFailure::ExecutionError(e)), Ok(out) => { - if lang_string.should_panic && out.status.success() { + if langstr.should_panic && out.status.success() { return Err(TestFailure::UnexpectedRunPass); - } else if !lang_string.should_panic && !out.status.success() { + } else if !langstr.should_panic && !out.status.success() { return Err(TestFailure::ExecutionFailure(out)); } } @@ -568,389 +570,14 @@ fn make_maybe_absolute_path(path: PathBuf) -> PathBuf { std::env::current_dir().map(|c| c.join(&path)).unwrap_or_else(|_| path) } } - -/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of -/// lines before the test code begins as well as if the output stream supports colors or not. -pub(crate) fn make_test( - s: &str, - crate_name: Option<&str>, - dont_insert_main: bool, - opts: &GlobalTestOptions, - edition: Edition, - test_id: Option<&str>, -) -> (String, usize, bool) { - let (crate_attrs, everything_else, crates) = partition_source(s, edition); - let everything_else = everything_else.trim(); - let mut line_offset = 0; - let mut prog = String::new(); - let mut supports_color = false; - - if opts.attrs.is_empty() { - // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some - // lints that are commonly triggered in doctests. The crate-level test attributes are - // commonly used to make tests fail in case they trigger warnings, so having this there in - // that case may cause some tests to pass when they shouldn't have. - prog.push_str("#![allow(unused)]\n"); - line_offset += 1; - } - - // Next, any attributes that came from the crate root via #![doc(test(attr(...)))]. - for attr in &opts.attrs { - prog.push_str(&format!("#![{attr}]\n")); - line_offset += 1; - } - - // Now push any outer attributes from the example, assuming they - // are intended to be crate attributes. - prog.push_str(&crate_attrs); - prog.push_str(&crates); - - // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern - // crate already is included. - let result = rustc_driver::catch_fatal_errors(|| { - rustc_span::create_session_if_not_set_then(edition, |_| { - use rustc_errors::emitter::{Emitter, HumanEmitter}; - use rustc_errors::DiagCtxt; - use rustc_parse::parser::ForceCollect; - use rustc_span::source_map::FilePathMapping; - - let filename = FileName::anon_source_code(s); - let source = crates + everything_else; - - // Any errors in parsing should also appear when the doctest is compiled for real, so just - // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), - false, - ); - supports_color = - HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone()) - .supports_color(); - - let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); - - // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser - let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); - let psess = ParseSess::with_dcx(dcx, sm); - - let mut found_main = false; - let mut found_extern_crate = crate_name.is_none(); - let mut found_macro = false; - - let mut parser = match maybe_new_parser_from_source_str(&psess, filename, source) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); - return (found_main, found_extern_crate, found_macro); - } - }; - - loop { - match parser.parse_item(ForceCollect::No) { - Ok(Some(item)) => { - if !found_main - && let ast::ItemKind::Fn(..) = item.kind - && item.ident.name == sym::main - { - found_main = true; - } - - if !found_extern_crate - && let ast::ItemKind::ExternCrate(original) = item.kind - { - // This code will never be reached if `crate_name` is none because - // `found_extern_crate` is initialized to `true` if it is none. - let crate_name = crate_name.unwrap(); - - match original { - Some(name) => found_extern_crate = name.as_str() == crate_name, - None => found_extern_crate = item.ident.as_str() == crate_name, - } - } - - if !found_macro && let ast::ItemKind::MacCall(..) = item.kind { - found_macro = true; - } - - if found_main && found_extern_crate { - break; - } - } - Ok(None) => break, - Err(e) => { - e.cancel(); - break; - } - } - - // The supplied item is only used for diagnostics, - // which are swallowed here anyway. - parser.maybe_consume_incorrect_semicolon(None); - } - - // Reset errors so that they won't be reported as compiler bugs when dropping the - // dcx. Any errors in the tests will be reported when the test file is compiled, - // Note that we still need to cancel the errors above otherwise `Diag` will panic on - // drop. - psess.dcx.reset_err_count(); - - (found_main, found_extern_crate, found_macro) - }) - }); - let Ok((already_has_main, already_has_extern_crate, found_macro)) = result else { - // If the parser panicked due to a fatal error, pass the test code through unchanged. - // The error will be reported during compilation. - return (s.to_owned(), 0, false); - }; - - // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't - // see it. In that case, run the old text-based scan to see if they at least have a main - // function written inside a macro invocation. See - // https://github.com/rust-lang/rust/issues/56898 - let already_has_main = if found_macro && !already_has_main { - s.lines() - .map(|line| { - let comment = line.find("//"); - if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line } - }) - .any(|code| code.contains("fn main")) - } else { - already_has_main - }; - - // Don't inject `extern crate std` because it's already injected by the - // compiler. - if !already_has_extern_crate && !opts.no_crate_inject && crate_name != Some("std") { - if let Some(crate_name) = crate_name { - // Don't inject `extern crate` if the crate is never used. - // NOTE: this is terribly inaccurate because it doesn't actually - // parse the source, but only has false positives, not false - // negatives. - if s.contains(crate_name) { - // rustdoc implicitly inserts an `extern crate` item for the own crate - // which may be unused, so we need to allow the lint. - prog.push_str("#[allow(unused_extern_crates)]\n"); - - prog.push_str(&format!("extern crate r#{crate_name};\n")); - line_offset += 1; - } - } - } - - // FIXME: This code cannot yet handle no_std test cases yet - if dont_insert_main || already_has_main || prog.contains("![no_std]") { - prog.push_str(everything_else); - } else { - let returns_result = everything_else.trim_end().ends_with("(())"); - // Give each doctest main function a unique name. - // This is for example needed for the tooling around `-C instrument-coverage`. - let inner_fn_name = if let Some(test_id) = test_id { - format!("_doctest_main_{test_id}") - } else { - "_inner".into() - }; - let inner_attr = if test_id.is_some() { "#[allow(non_snake_case)] " } else { "" }; - let (main_pre, main_post) = if returns_result { - ( - format!( - "fn main() {{ {inner_attr}fn {inner_fn_name}() -> Result<(), impl core::fmt::Debug> {{\n", - ), - format!("\n}} {inner_fn_name}().unwrap() }}"), - ) - } else if test_id.is_some() { - ( - format!("fn main() {{ {inner_attr}fn {inner_fn_name}() {{\n",), - format!("\n}} {inner_fn_name}() }}"), - ) - } else { - ("fn main() {\n".into(), "\n}".into()) - }; - // Note on newlines: We insert a line/newline *before*, and *after* - // the doctest and adjust the `line_offset` accordingly. - // In the case of `-C instrument-coverage`, this means that the generated - // inner `main` function spans from the doctest opening codeblock to the - // closing one. For example - // /// ``` <- start of the inner main - // /// <- code under doctest - // /// ``` <- end of the inner main - line_offset += 1; - - // add extra 4 spaces for each line to offset the code block - let content = if opts.insert_indent_space { - everything_else - .lines() - .map(|line| format!(" {}", line)) - .collect::>() - .join("\n") - } else { - everything_else.to_string() - }; - prog.extend([&main_pre, content.as_str(), &main_post].iter().cloned()); - } - - debug!("final doctest:\n{prog}"); - - (prog, line_offset, supports_color) -} - -fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { - if source.is_empty() { - // Empty content so nothing to check in here... - return true; - } - rustc_driver::catch_fatal_errors(|| { - rustc_span::create_session_if_not_set_then(edition, |_| { - use rustc_errors::emitter::HumanEmitter; - use rustc_errors::DiagCtxt; - use rustc_span::source_map::FilePathMapping; - - let filename = FileName::anon_source_code(source); - // Any errors in parsing should also appear when the doctest is compiled for real, so just - // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), - false, - ); - - let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); - - let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); - let psess = ParseSess::with_dcx(dcx, sm); - let mut parser = - match maybe_new_parser_from_source_str(&psess, filename, source.to_owned()) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); - // If there is an unclosed delimiter, an error will be returned by the - // tokentrees. - return false; - } - }; - // If a parsing error happened, it's very likely that the attribute is incomplete. - if let Err(e) = parser.parse_attribute(InnerAttrPolicy::Permitted) { - e.cancel(); - return false; - } - true - }) - }) - .unwrap_or(false) -} - -fn partition_source(s: &str, edition: Edition) -> (String, String, String) { - #[derive(Copy, Clone, PartialEq)] - enum PartitionState { - Attrs, - Crates, - Other, - } - let mut state = PartitionState::Attrs; - let mut before = String::new(); - let mut crates = String::new(); - let mut after = String::new(); - - let mut mod_attr_pending = String::new(); - - for line in s.lines() { - let trimline = line.trim(); - - // FIXME(misdreavus): if a doc comment is placed on an extern crate statement, it will be - // shunted into "everything else" - match state { - PartitionState::Attrs => { - state = if trimline.starts_with("#![") { - if !check_if_attr_is_complete(line, edition) { - mod_attr_pending = line.to_owned(); - } else { - mod_attr_pending.clear(); - } - PartitionState::Attrs - } else if trimline.chars().all(|c| c.is_whitespace()) - || (trimline.starts_with("//") && !trimline.starts_with("///")) - { - PartitionState::Attrs - } else if trimline.starts_with("extern crate") - || trimline.starts_with("#[macro_use] extern crate") - { - PartitionState::Crates - } else { - // First we check if the previous attribute was "complete"... - if !mod_attr_pending.is_empty() { - // If not, then we append the new line into the pending attribute to check - // if this time it's complete... - mod_attr_pending.push_str(line); - if !trimline.is_empty() - && check_if_attr_is_complete(&mod_attr_pending, edition) - { - // If it's complete, then we can clear the pending content. - mod_attr_pending.clear(); - } - // In any case, this is considered as `PartitionState::Attrs` so it's - // prepended before rustdoc's inserts. - PartitionState::Attrs - } else { - PartitionState::Other - } - }; - } - PartitionState::Crates => { - state = if trimline.starts_with("extern crate") - || trimline.starts_with("#[macro_use] extern crate") - || trimline.chars().all(|c| c.is_whitespace()) - || (trimline.starts_with("//") && !trimline.starts_with("///")) - { - PartitionState::Crates - } else { - PartitionState::Other - }; - } - PartitionState::Other => {} - } - - match state { - PartitionState::Attrs => { - before.push_str(line); - before.push('\n'); - } - PartitionState::Crates => { - crates.push_str(line); - crates.push('\n'); - } - PartitionState::Other => { - after.push_str(line); - after.push('\n'); - } - } - } - - debug!("before:\n{before}"); - debug!("crates:\n{crates}"); - debug!("after:\n{after}"); - - (before, after, crates) -} - -pub(crate) struct IndividualTestOptions { - test_builder: Option, - test_builder_wrappers: Vec, - is_json_unused_externs_enabled: bool, - should_persist_doctests: bool, - error_format: ErrorOutputType, - test_run_directory: Option, - nocapture: bool, - arg_file: PathBuf, +struct IndividualTestOptions { outdir: DirState, - runtool: Option, - runtool_args: Vec, - target: TargetTriple, test_id: String, - maybe_sysroot: Option, + path: PathBuf, } impl IndividualTestOptions { - fn new(options: &RustdocOptions, arg_file: &Path, test_id: String) -> Self { + fn new(options: &RustdocOptions, test_id: String, test_path: PathBuf) -> Self { let outdir = if let Some(ref path) = options.persist_doctests { let mut path = path.clone(); path.push(&test_id); @@ -965,148 +592,75 @@ impl IndividualTestOptions { DirState::Temp(get_doctest_dir().expect("rustdoc needs a tempdir")) }; - Self { - test_builder: options.test_builder.clone(), - test_builder_wrappers: options.test_builder_wrappers.clone(), - is_json_unused_externs_enabled: options.json_unused_externs.is_enabled(), - should_persist_doctests: options.persist_doctests.is_none(), - error_format: options.error_format, - test_run_directory: options.test_run_directory.clone(), - nocapture: options.nocapture, - arg_file: arg_file.into(), - outdir, - runtool: options.runtool.clone(), - runtool_args: options.runtool_args.clone(), - target: options.target.clone(), - test_id, - maybe_sysroot: options.maybe_sysroot.clone(), - } + Self { outdir, test_id, path: test_path } } } -pub(crate) trait Tester { - fn add_test(&mut self, test: String, config: LangString, line: usize); - fn get_line(&self) -> usize { - 0 - } - fn register_header(&mut self, _name: &str, _level: u32) {} +/// A doctest scraped from the code, ready to be turned into a runnable test. +struct ScrapedDoctest { + filename: FileName, + line: usize, + logical_path: Vec, + langstr: LangString, + text: String, } -pub(crate) struct Collector { - pub(crate) tests: Vec, +impl ScrapedDoctest { + fn edition(&self, opts: &RustdocOptions) -> Edition { + self.langstr.edition.unwrap_or(opts.edition) + } - // The name of the test displayed to the user, separated by `::`. - // - // In tests from Rust source, this is the path to the item - // e.g., `["std", "vec", "Vec", "push"]`. - // - // In tests from a markdown file, this is the titles of all headers (h1~h6) - // of the sections that contain the code block, e.g., if the markdown file is - // written as: - // - // ``````markdown - // # Title - // - // ## Subtitle - // - // ```rust - // assert!(true); - // ``` - // `````` - // - // the `names` vector of that test will be `["Title", "Subtitle"]`. - names: Vec, + fn no_run(&self, opts: &RustdocOptions) -> bool { + self.langstr.no_run || opts.no_run + } +} - rustdoc_options: RustdocOptions, - use_headers: bool, - enable_per_target_ignores: bool, - crate_name: String, +pub(crate) trait DoctestVisitor { + fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine); + fn visit_header(&mut self, _name: &str, _level: u32) {} +} + +struct CreateRunnableDoctests { + tests: Vec, + + rustdoc_options: Arc, opts: GlobalTestOptions, - position: Span, - source_map: Option>, - filename: Option, visited_tests: FxHashMap<(String, usize), usize>, unused_extern_reports: Arc>>, compiling_test_count: AtomicUsize, - arg_file: PathBuf, } -impl Collector { - pub(crate) fn new( - crate_name: String, - rustdoc_options: RustdocOptions, - use_headers: bool, - opts: GlobalTestOptions, - source_map: Option>, - filename: Option, - enable_per_target_ignores: bool, - arg_file: PathBuf, - ) -> Collector { - Collector { +impl CreateRunnableDoctests { + fn new(rustdoc_options: RustdocOptions, opts: GlobalTestOptions) -> CreateRunnableDoctests { + CreateRunnableDoctests { tests: Vec::new(), - names: Vec::new(), - rustdoc_options, - use_headers, - enable_per_target_ignores, - crate_name, + rustdoc_options: Arc::new(rustdoc_options), opts, - position: DUMMY_SP, - source_map, - filename, visited_tests: FxHashMap::default(), unused_extern_reports: Default::default(), compiling_test_count: AtomicUsize::new(0), - arg_file, } } - fn generate_name(&self, line: usize, filename: &FileName) -> String { - let mut item_path = self.names.join("::"); + fn generate_name(&self, filename: &FileName, line: usize, logical_path: &[String]) -> String { + let mut item_path = logical_path.join("::"); item_path.retain(|c| c != ' '); if !item_path.is_empty() { item_path.push(' '); } - format!("{} - {item_path}(line {line})", filename.prefer_local()) + format!("{} - {item_path}(line {line})", filename.prefer_remapped_unconditionaly()) } - pub(crate) fn set_position(&mut self, position: Span) { - self.position = position; - } - - fn get_filename(&self) -> FileName { - if let Some(ref source_map) = self.source_map { - let filename = source_map.span_to_filename(self.position); - if let FileName::Real(ref filename) = filename - && let Ok(cur_dir) = env::current_dir() - && let Some(local_path) = filename.local_path() - && let Ok(path) = local_path.strip_prefix(&cur_dir) - { - return path.to_owned().into(); - } - filename - } else if let Some(ref filename) = self.filename { - filename.clone().into() - } else { - FileName::Custom("input".to_owned()) - } - } -} - -impl Tester for Collector { - fn add_test(&mut self, test: String, config: LangString, line: usize) { - let filename = self.get_filename(); - let name = self.generate_name(line, &filename); - let crate_name = self.crate_name.clone(); + fn add_test(&mut self, test: ScrapedDoctest) { + let name = self.generate_name(&test.filename, test.line, &test.logical_path); let opts = self.opts.clone(); - let edition = config.edition.unwrap_or(self.rustdoc_options.edition); let target_str = self.rustdoc_options.target.to_string(); let unused_externs = self.unused_extern_reports.clone(); - let no_run = config.no_run || self.rustdoc_options.no_run; - if !config.compile_fail { + if !test.langstr.compile_fail { self.compiling_test_count.fetch_add(1, Ordering::SeqCst); } - let path = match &filename { + let path = match &test.filename { FileName::Real(path) => { if let Some(local_path) = path.local_path() { local_path.to_path_buf() @@ -1119,7 +673,8 @@ impl Tester for Collector { }; // For example `module/file.rs` would become `module_file_rs` - let file = filename + let file = test + .filename .prefer_local() .to_string_lossy() .chars() @@ -1128,22 +683,25 @@ impl Tester for Collector { let test_id = format!( "{file}_{line}_{number}", file = file, - line = line, + line = test.line, number = { // Increases the current test number, if this file already // exists or it creates a new entry with a test number of 0. - self.visited_tests.entry((file.clone(), line)).and_modify(|v| *v += 1).or_insert(0) + self.visited_tests + .entry((file.clone(), test.line)) + .and_modify(|v| *v += 1) + .or_insert(0) }, ); - let rustdoc_test_options = - IndividualTestOptions::new(&self.rustdoc_options, &self.arg_file, test_id); + let rustdoc_options = self.rustdoc_options.clone(); + let rustdoc_test_options = IndividualTestOptions::new(&self.rustdoc_options, test_id, path); - debug!("creating test {name}: {test}"); + debug!("creating test {name}: {}", test.text); self.tests.push(test::TestDescAndFn { desc: test::TestDesc { name: test::DynTestName(name), - ignore: match config.ignore { + ignore: match test.langstr.ignore { Ignore::All => true, Ignore::None => false, Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)), @@ -1156,253 +714,103 @@ impl Tester for Collector { end_col: 0, // compiler failures are test failures should_panic: test::ShouldPanic::No, - compile_fail: config.compile_fail, - no_run, + compile_fail: test.langstr.compile_fail, + no_run: test.no_run(&rustdoc_options), test_type: test::TestType::DocTest, }, testfn: test::DynTestFn(Box::new(move || { - let report_unused_externs = |uext| { - unused_externs.lock().unwrap().push(uext); - }; - let res = run_test( - &test, - &crate_name, - line, - rustdoc_test_options, - config, - no_run, - &opts, - edition, - path, - report_unused_externs, - ); - - if let Err(err) = res { - match err { - TestFailure::CompileError => { - eprint!("Couldn't compile the test."); - } - TestFailure::UnexpectedCompilePass => { - eprint!("Test compiled successfully, but it's marked `compile_fail`."); - } - TestFailure::UnexpectedRunPass => { - eprint!("Test executable succeeded, but it's marked `should_panic`."); - } - TestFailure::MissingErrorCodes(codes) => { - eprint!("Some expected error codes were not found: {codes:?}"); - } - TestFailure::ExecutionError(err) => { - eprint!("Couldn't run the test: {err}"); - if err.kind() == io::ErrorKind::PermissionDenied { - eprint!(" - maybe your tempdir is mounted with noexec?"); - } - } - TestFailure::ExecutionFailure(out) => { - eprintln!("Test executable failed ({reason}).", reason = out.status); - - // FIXME(#12309): An unfortunate side-effect of capturing the test - // executable's output is that the relative ordering between the test's - // stdout and stderr is lost. However, this is better than the - // alternative: if the test executable inherited the parent's I/O - // handles the output wouldn't be captured at all, even on success. - // - // The ordering could be preserved if the test process' stderr was - // redirected to stdout, but that functionality does not exist in the - // standard library, so it may not be portable enough. - let stdout = str::from_utf8(&out.stdout).unwrap_or_default(); - let stderr = str::from_utf8(&out.stderr).unwrap_or_default(); - - if !stdout.is_empty() || !stderr.is_empty() { - eprintln!(); - - if !stdout.is_empty() { - eprintln!("stdout:\n{stdout}"); - } - - if !stderr.is_empty() { - eprintln!("stderr:\n{stderr}"); - } - } - } - } - - panic::resume_unwind(Box::new(())); - } - Ok(()) + doctest_run_fn(rustdoc_test_options, opts, test, rustdoc_options, unused_externs) })), }); } +} - fn get_line(&self) -> usize { - if let Some(ref source_map) = self.source_map { - let line = self.position.lo().to_usize(); - let line = source_map.lookup_char_pos(BytePos(line as u32)).line; - if line > 0 { line - 1 } else { line } - } else { - 0 - } - } +fn doctest_run_fn( + test_opts: IndividualTestOptions, + global_opts: GlobalTestOptions, + scraped_test: ScrapedDoctest, + rustdoc_options: Arc, + unused_externs: Arc>>, +) -> Result<(), String> { + let report_unused_externs = |uext| { + unused_externs.lock().unwrap().push(uext); + }; + let edition = scraped_test.edition(&rustdoc_options); + let (full_test_code, full_test_line_offset, supports_color) = make_test( + &scraped_test.text, + Some(&global_opts.crate_name), + scraped_test.langstr.test_harness, + &global_opts, + edition, + Some(&test_opts.test_id), + ); + let runnable_test = RunnableDoctest { + full_test_code, + full_test_line_offset, + test_opts, + global_opts, + scraped_test, + }; + let res = run_test(runnable_test, &rustdoc_options, supports_color, report_unused_externs); - fn register_header(&mut self, name: &str, level: u32) { - if self.use_headers { - // We use these headings as test names, so it's good if - // they're valid identifiers. - let name = name - .chars() - .enumerate() - .map(|(i, c)| { - if (i == 0 && rustc_lexer::is_id_start(c)) - || (i != 0 && rustc_lexer::is_id_continue(c)) - { - c - } else { - '_' - } - }) - .collect::(); - - // Here we try to efficiently assemble the header titles into the - // test name in the form of `h1::h2::h3::h4::h5::h6`. - // - // Suppose that originally `self.names` contains `[h1, h2, h3]`... - let level = level as usize; - if level <= self.names.len() { - // ... Consider `level == 2`. All headers in the lower levels - // are irrelevant in this new level. So we should reset - // `self.names` to contain headers until

, and replace that - // slot with the new name: `[h1, name]`. - self.names.truncate(level); - self.names[level - 1] = name; - } else { - // ... On the other hand, consider `level == 5`. This means we - // need to extend `self.names` to contain five headers. We fill - // in the missing level (

) with `_`. Thus `self.names` will - // become `[h1, h2, h3, "_", name]`. - if level - 1 > self.names.len() { - self.names.resize(level - 1, "_".to_owned()); + if let Err(err) = res { + match err { + TestFailure::CompileError => { + eprint!("Couldn't compile the test."); + } + TestFailure::UnexpectedCompilePass => { + eprint!("Test compiled successfully, but it's marked `compile_fail`."); + } + TestFailure::UnexpectedRunPass => { + eprint!("Test executable succeeded, but it's marked `should_panic`."); + } + TestFailure::MissingErrorCodes(codes) => { + eprint!("Some expected error codes were not found: {codes:?}"); + } + TestFailure::ExecutionError(err) => { + eprint!("Couldn't run the test: {err}"); + if err.kind() == io::ErrorKind::PermissionDenied { + eprint!(" - maybe your tempdir is mounted with noexec?"); + } + } + TestFailure::ExecutionFailure(out) => { + eprintln!("Test executable failed ({reason}).", reason = out.status); + + // FIXME(#12309): An unfortunate side-effect of capturing the test + // executable's output is that the relative ordering between the test's + // stdout and stderr is lost. However, this is better than the + // alternative: if the test executable inherited the parent's I/O + // handles the output wouldn't be captured at all, even on success. + // + // The ordering could be preserved if the test process' stderr was + // redirected to stdout, but that functionality does not exist in the + // standard library, so it may not be portable enough. + let stdout = str::from_utf8(&out.stdout).unwrap_or_default(); + let stderr = str::from_utf8(&out.stderr).unwrap_or_default(); + + if !stdout.is_empty() || !stderr.is_empty() { + eprintln!(); + + if !stdout.is_empty() { + eprintln!("stdout:\n{stdout}"); + } + + if !stderr.is_empty() { + eprintln!("stderr:\n{stderr}"); + } } - self.names.push(name); } } + + panic::resume_unwind(Box::new(())); } + Ok(()) } #[cfg(test)] // used in tests -impl Tester for Vec { - fn add_test(&mut self, _test: String, _config: LangString, line: usize) { - self.push(line); - } -} - -struct HirCollector<'a, 'hir, 'tcx> { - sess: &'a Session, - collector: &'a mut Collector, - map: Map<'hir>, - codes: ErrorCodes, - tcx: TyCtxt<'tcx>, -} - -impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { - fn visit_testable( - &mut self, - name: String, - def_id: LocalDefId, - sp: Span, - nested: F, - ) { - let ast_attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id)); - if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) { - if !cfg.matches(&self.sess.psess, Some(self.tcx.features())) { - return; - } - } - - let has_name = !name.is_empty(); - if has_name { - self.collector.names.push(name); - } - - // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with - // anything else, this will combine them for us. - let attrs = Attributes::from_ast(ast_attrs); - if let Some(doc) = attrs.opt_doc_value() { - // Use the outermost invocation, so that doctest names come from where the docs were written. - let span = ast_attrs - .iter() - .find(|attr| attr.doc_str().is_some()) - .map(|attr| attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span)) - .unwrap_or(DUMMY_SP); - self.collector.set_position(span); - markdown::find_testable_code( - &doc, - self.collector, - self.codes, - self.collector.enable_per_target_ignores, - Some(&crate::html::markdown::ExtraInfo::new( - self.tcx, - def_id.to_def_id(), - span_of_fragments(&attrs.doc_strings).unwrap_or(sp), - )), - self.tcx.features().custom_code_classes_in_docs, - ); - } - - nested(self); - - if has_name { - self.collector.names.pop(); - } - } -} - -impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> { - type NestedFilter = nested_filter::All; - - fn nested_visit_map(&mut self) -> Self::Map { - self.map - } - - fn visit_item(&mut self, item: &'hir hir::Item<'_>) { - let name = match &item.kind { - hir::ItemKind::Impl(impl_) => { - rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id) - } - _ => item.ident.to_string(), - }; - - self.visit_testable(name, item.owner_id.def_id, item.span, |this| { - intravisit::walk_item(this, item); - }); - } - - fn visit_trait_item(&mut self, item: &'hir hir::TraitItem<'_>) { - self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { - intravisit::walk_trait_item(this, item); - }); - } - - fn visit_impl_item(&mut self, item: &'hir hir::ImplItem<'_>) { - self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { - intravisit::walk_impl_item(this, item); - }); - } - - fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem<'_>) { - self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { - intravisit::walk_foreign_item(this, item); - }); - } - - fn visit_variant(&mut self, v: &'hir hir::Variant<'_>) { - self.visit_testable(v.ident.to_string(), v.def_id, v.span, |this| { - intravisit::walk_variant(this, v); - }); - } - - fn visit_field_def(&mut self, f: &'hir hir::FieldDef<'_>) { - self.visit_testable(f.ident.to_string(), f.def_id, f.span, |this| { - intravisit::walk_field_def(this, f); - }); +impl DoctestVisitor for Vec { + fn visit_test(&mut self, _test: String, _config: LangString, rel_line: MdRelLine) { + self.push(1 + rel_line.offset()); } } diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs new file mode 100644 index 000000000000..74833c113623 --- /dev/null +++ b/src/librustdoc/doctest/make.rs @@ -0,0 +1,393 @@ +//! Logic for transforming the raw code given by the user into something actually +//! runnable, e.g. by adding a `main` function if it doesn't already exist. + +use std::io; + +use rustc_ast as ast; +use rustc_data_structures::sync::Lrc; +use rustc_errors::emitter::stderr_destination; +use rustc_errors::{ColorConfig, FatalError}; +use rustc_parse::new_parser_from_source_str; +use rustc_parse::parser::attr::InnerAttrPolicy; +use rustc_session::parse::ParseSess; +use rustc_span::edition::Edition; +use rustc_span::source_map::SourceMap; +use rustc_span::symbol::sym; +use rustc_span::FileName; + +use super::GlobalTestOptions; + +/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of +/// lines before the test code begins as well as if the output stream supports colors or not. +pub(crate) fn make_test( + s: &str, + crate_name: Option<&str>, + dont_insert_main: bool, + opts: &GlobalTestOptions, + edition: Edition, + test_id: Option<&str>, +) -> (String, usize, bool) { + let (crate_attrs, everything_else, crates) = partition_source(s, edition); + let everything_else = everything_else.trim(); + let mut line_offset = 0; + let mut prog = String::new(); + let mut supports_color = false; + + if opts.attrs.is_empty() { + // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some + // lints that are commonly triggered in doctests. The crate-level test attributes are + // commonly used to make tests fail in case they trigger warnings, so having this there in + // that case may cause some tests to pass when they shouldn't have. + prog.push_str("#![allow(unused)]\n"); + line_offset += 1; + } + + // Next, any attributes that came from the crate root via #![doc(test(attr(...)))]. + for attr in &opts.attrs { + prog.push_str(&format!("#![{attr}]\n")); + line_offset += 1; + } + + // Now push any outer attributes from the example, assuming they + // are intended to be crate attributes. + prog.push_str(&crate_attrs); + prog.push_str(&crates); + + // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern + // crate already is included. + let Ok((already_has_main, already_has_extern_crate)) = + check_for_main_and_extern_crate(crate_name, s.to_owned(), edition, &mut supports_color) + else { + // If the parser panicked due to a fatal error, pass the test code through unchanged. + // The error will be reported during compilation. + return (s.to_owned(), 0, false); + }; + + // Don't inject `extern crate std` because it's already injected by the + // compiler. + if !already_has_extern_crate && !opts.no_crate_inject && crate_name != Some("std") { + if let Some(crate_name) = crate_name { + // Don't inject `extern crate` if the crate is never used. + // NOTE: this is terribly inaccurate because it doesn't actually + // parse the source, but only has false positives, not false + // negatives. + if s.contains(crate_name) { + // rustdoc implicitly inserts an `extern crate` item for the own crate + // which may be unused, so we need to allow the lint. + prog.push_str("#[allow(unused_extern_crates)]\n"); + + prog.push_str(&format!("extern crate r#{crate_name};\n")); + line_offset += 1; + } + } + } + + // FIXME: This code cannot yet handle no_std test cases yet + if dont_insert_main || already_has_main || prog.contains("![no_std]") { + prog.push_str(everything_else); + } else { + let returns_result = everything_else.trim_end().ends_with("(())"); + // Give each doctest main function a unique name. + // This is for example needed for the tooling around `-C instrument-coverage`. + let inner_fn_name = if let Some(test_id) = test_id { + format!("_doctest_main_{test_id}") + } else { + "_inner".into() + }; + let inner_attr = if test_id.is_some() { "#[allow(non_snake_case)] " } else { "" }; + let (main_pre, main_post) = if returns_result { + ( + format!( + "fn main() {{ {inner_attr}fn {inner_fn_name}() -> Result<(), impl core::fmt::Debug> {{\n", + ), + format!("\n}} {inner_fn_name}().unwrap() }}"), + ) + } else if test_id.is_some() { + ( + format!("fn main() {{ {inner_attr}fn {inner_fn_name}() {{\n",), + format!("\n}} {inner_fn_name}() }}"), + ) + } else { + ("fn main() {\n".into(), "\n}".into()) + }; + // Note on newlines: We insert a line/newline *before*, and *after* + // the doctest and adjust the `line_offset` accordingly. + // In the case of `-C instrument-coverage`, this means that the generated + // inner `main` function spans from the doctest opening codeblock to the + // closing one. For example + // /// ``` <- start of the inner main + // /// <- code under doctest + // /// ``` <- end of the inner main + line_offset += 1; + + // add extra 4 spaces for each line to offset the code block + let content = if opts.insert_indent_space { + everything_else + .lines() + .map(|line| format!(" {}", line)) + .collect::>() + .join("\n") + } else { + everything_else.to_string() + }; + prog.extend([&main_pre, content.as_str(), &main_post].iter().cloned()); + } + + debug!("final doctest:\n{prog}"); + + (prog, line_offset, supports_color) +} + +fn check_for_main_and_extern_crate( + crate_name: Option<&str>, + source: String, + edition: Edition, + supports_color: &mut bool, +) -> Result<(bool, bool), FatalError> { + let result = rustc_driver::catch_fatal_errors(|| { + rustc_span::create_session_if_not_set_then(edition, |_| { + use rustc_errors::emitter::{Emitter, HumanEmitter}; + use rustc_errors::DiagCtxt; + use rustc_parse::parser::ForceCollect; + use rustc_span::source_map::FilePathMapping; + + let filename = FileName::anon_source_code(&source); + + // Any errors in parsing should also appear when the doctest is compiled for real, so just + // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let fallback_bundle = rustc_errors::fallback_fluent_bundle( + rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), + false, + ); + *supports_color = + HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone()) + .supports_color(); + + let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); + + // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser + let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); + let psess = ParseSess::with_dcx(dcx, sm); + + let mut found_main = false; + let mut found_extern_crate = crate_name.is_none(); + let mut found_macro = false; + + let mut parser = match new_parser_from_source_str(&psess, filename, source.clone()) { + Ok(p) => p, + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + return (found_main, found_extern_crate, found_macro); + } + }; + + loop { + match parser.parse_item(ForceCollect::No) { + Ok(Some(item)) => { + if !found_main + && let ast::ItemKind::Fn(..) = item.kind + && item.ident.name == sym::main + { + found_main = true; + } + + if !found_extern_crate + && let ast::ItemKind::ExternCrate(original) = item.kind + { + // This code will never be reached if `crate_name` is none because + // `found_extern_crate` is initialized to `true` if it is none. + let crate_name = crate_name.unwrap(); + + match original { + Some(name) => found_extern_crate = name.as_str() == crate_name, + None => found_extern_crate = item.ident.as_str() == crate_name, + } + } + + if !found_macro && let ast::ItemKind::MacCall(..) = item.kind { + found_macro = true; + } + + if found_main && found_extern_crate { + break; + } + } + Ok(None) => break, + Err(e) => { + e.cancel(); + break; + } + } + + // The supplied item is only used for diagnostics, + // which are swallowed here anyway. + parser.maybe_consume_incorrect_semicolon(None); + } + + // Reset errors so that they won't be reported as compiler bugs when dropping the + // dcx. Any errors in the tests will be reported when the test file is compiled, + // Note that we still need to cancel the errors above otherwise `Diag` will panic on + // drop. + psess.dcx().reset_err_count(); + + (found_main, found_extern_crate, found_macro) + }) + }); + let (already_has_main, already_has_extern_crate, found_macro) = result?; + + // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't + // see it. In that case, run the old text-based scan to see if they at least have a main + // function written inside a macro invocation. See + // https://github.com/rust-lang/rust/issues/56898 + let already_has_main = if found_macro && !already_has_main { + source + .lines() + .map(|line| { + let comment = line.find("//"); + if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line } + }) + .any(|code| code.contains("fn main")) + } else { + already_has_main + }; + + Ok((already_has_main, already_has_extern_crate)) +} + +fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { + if source.is_empty() { + // Empty content so nothing to check in here... + return true; + } + rustc_driver::catch_fatal_errors(|| { + rustc_span::create_session_if_not_set_then(edition, |_| { + use rustc_errors::emitter::HumanEmitter; + use rustc_errors::DiagCtxt; + use rustc_span::source_map::FilePathMapping; + + let filename = FileName::anon_source_code(source); + // Any errors in parsing should also appear when the doctest is compiled for real, so just + // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let fallback_bundle = rustc_errors::fallback_fluent_bundle( + rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), + false, + ); + + let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); + + let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); + let psess = ParseSess::with_dcx(dcx, sm); + let mut parser = match new_parser_from_source_str(&psess, filename, source.to_owned()) { + Ok(p) => p, + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + // If there is an unclosed delimiter, an error will be returned by the + // tokentrees. + return false; + } + }; + // If a parsing error happened, it's very likely that the attribute is incomplete. + if let Err(e) = parser.parse_attribute(InnerAttrPolicy::Permitted) { + e.cancel(); + return false; + } + true + }) + }) + .unwrap_or(false) +} + +fn partition_source(s: &str, edition: Edition) -> (String, String, String) { + #[derive(Copy, Clone, PartialEq)] + enum PartitionState { + Attrs, + Crates, + Other, + } + let mut state = PartitionState::Attrs; + let mut before = String::new(); + let mut crates = String::new(); + let mut after = String::new(); + + let mut mod_attr_pending = String::new(); + + for line in s.lines() { + let trimline = line.trim(); + + // FIXME(misdreavus): if a doc comment is placed on an extern crate statement, it will be + // shunted into "everything else" + match state { + PartitionState::Attrs => { + state = if trimline.starts_with("#![") { + if !check_if_attr_is_complete(line, edition) { + mod_attr_pending = line.to_owned(); + } else { + mod_attr_pending.clear(); + } + PartitionState::Attrs + } else if trimline.chars().all(|c| c.is_whitespace()) + || (trimline.starts_with("//") && !trimline.starts_with("///")) + { + PartitionState::Attrs + } else if trimline.starts_with("extern crate") + || trimline.starts_with("#[macro_use] extern crate") + { + PartitionState::Crates + } else { + // First we check if the previous attribute was "complete"... + if !mod_attr_pending.is_empty() { + // If not, then we append the new line into the pending attribute to check + // if this time it's complete... + mod_attr_pending.push_str(line); + if !trimline.is_empty() + && check_if_attr_is_complete(&mod_attr_pending, edition) + { + // If it's complete, then we can clear the pending content. + mod_attr_pending.clear(); + } + // In any case, this is considered as `PartitionState::Attrs` so it's + // prepended before rustdoc's inserts. + PartitionState::Attrs + } else { + PartitionState::Other + } + }; + } + PartitionState::Crates => { + state = if trimline.starts_with("extern crate") + || trimline.starts_with("#[macro_use] extern crate") + || trimline.chars().all(|c| c.is_whitespace()) + || (trimline.starts_with("//") && !trimline.starts_with("///")) + { + PartitionState::Crates + } else { + PartitionState::Other + }; + } + PartitionState::Other => {} + } + + match state { + PartitionState::Attrs => { + before.push_str(line); + before.push('\n'); + } + PartitionState::Crates => { + crates.push_str(line); + crates.push('\n'); + } + PartitionState::Other => { + after.push_str(line); + after.push('\n'); + } + } + } + + debug!("before:\n{before}"); + debug!("crates:\n{crates}"); + debug!("after:\n{after}"); + + (before, after, crates) +} diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs new file mode 100644 index 000000000000..b8ab7adb36e8 --- /dev/null +++ b/src/librustdoc/doctest/markdown.rs @@ -0,0 +1,125 @@ +//! Doctest functionality used only for doctests in `.md` Markdown files. + +use std::fs::read_to_string; + +use rustc_span::FileName; +use tempfile::tempdir; + +use super::{ + generate_args_file, CreateRunnableDoctests, DoctestVisitor, GlobalTestOptions, ScrapedDoctest, +}; +use crate::config::Options; +use crate::html::markdown::{find_testable_code, ErrorCodes, LangString, MdRelLine}; + +struct MdCollector { + tests: Vec, + cur_path: Vec, + filename: FileName, +} + +impl DoctestVisitor for MdCollector { + fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine) { + let filename = self.filename.clone(); + // First line of Markdown is line 1. + let line = 1 + rel_line.offset(); + self.tests.push(ScrapedDoctest { + filename, + line, + logical_path: self.cur_path.clone(), + langstr: config, + text: test, + }); + } + + fn visit_header(&mut self, name: &str, level: u32) { + // We use these headings as test names, so it's good if + // they're valid identifiers. + let name = name + .chars() + .enumerate() + .map(|(i, c)| { + if (i == 0 && rustc_lexer::is_id_start(c)) + || (i != 0 && rustc_lexer::is_id_continue(c)) + { + c + } else { + '_' + } + }) + .collect::(); + + // Here we try to efficiently assemble the header titles into the + // test name in the form of `h1::h2::h3::h4::h5::h6`. + // + // Suppose that originally `self.cur_path` contains `[h1, h2, h3]`... + let level = level as usize; + if level <= self.cur_path.len() { + // ... Consider `level == 2`. All headers in the lower levels + // are irrelevant in this new level. So we should reset + // `self.names` to contain headers until

, and replace that + // slot with the new name: `[h1, name]`. + self.cur_path.truncate(level); + self.cur_path[level - 1] = name; + } else { + // ... On the other hand, consider `level == 5`. This means we + // need to extend `self.names` to contain five headers. We fill + // in the missing level (

) with `_`. Thus `self.names` will + // become `[h1, h2, h3, "_", name]`. + if level - 1 > self.cur_path.len() { + self.cur_path.resize(level - 1, "_".to_owned()); + } + self.cur_path.push(name); + } + } +} + +/// Runs any tests/code examples in the markdown file `options.input`. +pub(crate) fn test(options: Options) -> Result<(), String> { + use rustc_session::config::Input; + let input_str = match &options.input { + Input::File(path) => { + read_to_string(&path).map_err(|err| format!("{}: {err}", path.display()))? + } + Input::Str { name: _, input } => input.clone(), + }; + + // Obviously not a real crate name, but close enough for purposes of doctests. + let crate_name = options.input.filestem().to_string(); + let temp_dir = + tempdir().map_err(|error| format!("failed to create temporary directory: {error:?}"))?; + let args_file = temp_dir.path().join("rustdoc-cfgs"); + generate_args_file(&args_file, &options)?; + + let opts = GlobalTestOptions { + crate_name, + no_crate_inject: true, + insert_indent_space: false, + attrs: vec![], + args_file, + }; + + let mut md_collector = MdCollector { + tests: vec![], + cur_path: vec![], + filename: options + .input + .opt_path() + .map(ToOwned::to_owned) + .map(FileName::from) + .unwrap_or(FileName::Custom("input".to_owned())), + }; + let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); + + find_testable_code( + &input_str, + &mut md_collector, + codes, + options.enable_per_target_ignores, + None, + ); + + let mut collector = CreateRunnableDoctests::new(options.clone(), opts); + md_collector.tests.into_iter().for_each(|t| collector.add_test(t)); + crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests); + Ok(()) +} diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs new file mode 100644 index 000000000000..fc8e119ccc23 --- /dev/null +++ b/src/librustdoc/doctest/rust.rs @@ -0,0 +1,200 @@ +//! Doctest functionality used only for doctests in `.rs` source files. + +use std::env; + +use rustc_data_structures::{fx::FxHashSet, sync::Lrc}; +use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; +use rustc_hir::{self as hir, intravisit, CRATE_HIR_ID}; +use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; +use rustc_middle::ty::TyCtxt; +use rustc_resolve::rustdoc::span_of_fragments; +use rustc_session::Session; +use rustc_span::source_map::SourceMap; +use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; + +use super::{DoctestVisitor, ScrapedDoctest}; +use crate::clean::{types::AttributesExt, Attributes}; +use crate::html::markdown::{self, ErrorCodes, LangString, MdRelLine}; + +struct RustCollector { + source_map: Lrc, + tests: Vec, + cur_path: Vec, + position: Span, +} + +impl RustCollector { + fn get_filename(&self) -> FileName { + let filename = self.source_map.span_to_filename(self.position); + if let FileName::Real(ref filename) = filename { + let path = filename.remapped_path_if_available(); + // Strip the cwd prefix from the path. This will likely exist if + // the path was not remapped. + let path = env::current_dir() + .map(|cur_dir| path.strip_prefix(&cur_dir).unwrap_or(path)) + .unwrap_or(path); + return path.to_owned().into(); + } + filename + } + + fn get_base_line(&self) -> usize { + let sp_lo = self.position.lo().to_usize(); + let loc = self.source_map.lookup_char_pos(BytePos(sp_lo as u32)); + loc.line + } +} + +impl DoctestVisitor for RustCollector { + fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine) { + let line = self.get_base_line() + rel_line.offset(); + self.tests.push(ScrapedDoctest { + filename: self.get_filename(), + line, + logical_path: self.cur_path.clone(), + langstr: config, + text: test, + }); + } + + fn visit_header(&mut self, _name: &str, _level: u32) {} +} + +pub(super) struct HirCollector<'a, 'tcx> { + sess: &'a Session, + map: Map<'tcx>, + codes: ErrorCodes, + tcx: TyCtxt<'tcx>, + enable_per_target_ignores: bool, + collector: RustCollector, +} + +impl<'a, 'tcx> HirCollector<'a, 'tcx> { + pub fn new( + sess: &'a Session, + map: Map<'tcx>, + codes: ErrorCodes, + enable_per_target_ignores: bool, + tcx: TyCtxt<'tcx>, + ) -> Self { + let collector = RustCollector { + source_map: sess.psess.clone_source_map(), + cur_path: vec![], + position: DUMMY_SP, + tests: vec![], + }; + Self { sess, map, codes, enable_per_target_ignores, tcx, collector } + } + + pub fn collect_crate(mut self) -> Vec { + let tcx = self.tcx; + self.visit_testable("".to_string(), CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID), |this| { + tcx.hir().walk_toplevel_module(this) + }); + self.collector.tests + } +} + +impl<'a, 'tcx> HirCollector<'a, 'tcx> { + fn visit_testable( + &mut self, + name: String, + def_id: LocalDefId, + sp: Span, + nested: F, + ) { + let ast_attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id)); + if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) { + if !cfg.matches(&self.sess.psess, Some(self.tcx.features())) { + return; + } + } + + let has_name = !name.is_empty(); + if has_name { + self.collector.cur_path.push(name); + } + + // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with + // anything else, this will combine them for us. + let attrs = Attributes::from_ast(ast_attrs); + if let Some(doc) = attrs.opt_doc_value() { + // Use the outermost invocation, so that doctest names come from where the docs were written. + let span = ast_attrs + .iter() + .find(|attr| attr.doc_str().is_some()) + .map(|attr| attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span)) + .unwrap_or(DUMMY_SP); + self.collector.position = span; + markdown::find_testable_code( + &doc, + &mut self.collector, + self.codes, + self.enable_per_target_ignores, + Some(&crate::html::markdown::ExtraInfo::new( + self.tcx, + def_id.to_def_id(), + span_of_fragments(&attrs.doc_strings).unwrap_or(sp), + )), + ); + } + + nested(self); + + if has_name { + self.collector.cur_path.pop(); + } + } +} + +impl<'a, 'tcx> intravisit::Visitor<'tcx> for HirCollector<'a, 'tcx> { + type NestedFilter = nested_filter::All; + + fn nested_visit_map(&mut self) -> Self::Map { + self.map + } + + fn visit_item(&mut self, item: &'tcx hir::Item<'_>) { + let name = match &item.kind { + hir::ItemKind::Impl(impl_) => { + rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id) + } + _ => item.ident.to_string(), + }; + + self.visit_testable(name, item.owner_id.def_id, item.span, |this| { + intravisit::walk_item(this, item); + }); + } + + fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem<'_>) { + self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { + intravisit::walk_trait_item(this, item); + }); + } + + fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem<'_>) { + self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { + intravisit::walk_impl_item(this, item); + }); + } + + fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'_>) { + self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| { + intravisit::walk_foreign_item(this, item); + }); + } + + fn visit_variant(&mut self, v: &'tcx hir::Variant<'_>) { + self.visit_testable(v.ident.to_string(), v.def_id, v.span, |this| { + intravisit::walk_variant(this, v); + }); + } + + fn visit_field_def(&mut self, f: &'tcx hir::FieldDef<'_>) { + self.visit_testable(f.ident.to_string(), f.def_id, f.span, |this| { + intravisit::walk_field_def(this, f); + }); + } +} diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index 9629acb31eb6..9124ec63267c 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -1,10 +1,23 @@ +use std::path::PathBuf; + use super::{make_test, GlobalTestOptions}; use rustc_span::edition::DEFAULT_EDITION; +/// Default [`GlobalTestOptions`] for these unit tests. +fn default_global_opts(crate_name: impl Into) -> GlobalTestOptions { + GlobalTestOptions { + crate_name: crate_name.into(), + no_crate_inject: false, + insert_indent_space: false, + attrs: vec![], + args_file: PathBuf::new(), + } +} + #[test] fn make_test_basic() { //basic use: wraps with `fn main`, adds `#![allow(unused)]` - let opts = GlobalTestOptions::default(); + let opts = default_global_opts(""); let input = "assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] fn main() { @@ -19,7 +32,7 @@ assert_eq!(2+2, 4); fn make_test_crate_name_no_use() { // If you give a crate name but *don't* use it within the test, it won't bother inserting // the `extern crate` statement. - let opts = GlobalTestOptions::default(); + let opts = default_global_opts("asdf"); let input = "assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] fn main() { @@ -34,7 +47,7 @@ assert_eq!(2+2, 4); fn make_test_crate_name() { // If you give a crate name and use it within the test, it will insert an `extern crate` // statement before `fn main`. - let opts = GlobalTestOptions::default(); + let opts = default_global_opts("asdf"); let input = "use asdf::qwop; assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -53,8 +66,7 @@ assert_eq!(2+2, 4); fn make_test_no_crate_inject() { // Even if you do use the crate within the test, setting `opts.no_crate_inject` will skip // adding it anyway. - let opts = - GlobalTestOptions { no_crate_inject: true, attrs: vec![], insert_indent_space: false }; + let opts = GlobalTestOptions { no_crate_inject: true, ..default_global_opts("asdf") }; let input = "use asdf::qwop; assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -72,7 +84,7 @@ fn make_test_ignore_std() { // Even if you include a crate name, and use it in the doctest, we still won't include an // `extern crate` statement if the crate is "std" -- that's included already by the // compiler! - let opts = GlobalTestOptions::default(); + let opts = default_global_opts("std"); let input = "use std::*; assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -89,7 +101,7 @@ assert_eq!(2+2, 4); fn make_test_manual_extern_crate() { // When you manually include an `extern crate` statement in your doctest, `make_test` // assumes you've included one for your own crate too. - let opts = GlobalTestOptions::default(); + let opts = default_global_opts("asdf"); let input = "extern crate asdf; use asdf::qwop; assert_eq!(2+2, 4);"; @@ -106,7 +118,7 @@ assert_eq!(2+2, 4); #[test] fn make_test_manual_extern_crate_with_macro_use() { - let opts = GlobalTestOptions::default(); + let opts = default_global_opts("asdf"); let input = "#[macro_use] extern crate asdf; use asdf::qwop; assert_eq!(2+2, 4);"; @@ -125,7 +137,7 @@ assert_eq!(2+2, 4); fn make_test_opts_attrs() { // If you supplied some doctest attributes with `#![doc(test(attr(...)))]`, it will use // those instead of the stock `#![allow(unused)]`. - let mut opts = GlobalTestOptions::default(); + let mut opts = default_global_opts("asdf"); opts.attrs.push("feature(sick_rad)".to_string()); let input = "use asdf::qwop; assert_eq!(2+2, 4);"; @@ -159,7 +171,7 @@ assert_eq!(2+2, 4); fn make_test_crate_attrs() { // Including inner attributes in your doctest will apply them to the whole "crate", pasting // them outside the generated main function. - let opts = GlobalTestOptions::default(); + let opts = default_global_opts(""); let input = "#![feature(sick_rad)] assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -175,7 +187,7 @@ assert_eq!(2+2, 4); #[test] fn make_test_with_main() { // Including your own `fn main` wrapper lets the test use it verbatim. - let opts = GlobalTestOptions::default(); + let opts = default_global_opts(""); let input = "fn main() { assert_eq!(2+2, 4); }"; @@ -191,7 +203,7 @@ fn main() { #[test] fn make_test_fake_main() { // ... but putting it in a comment will still provide a wrapper. - let opts = GlobalTestOptions::default(); + let opts = default_global_opts(""); let input = "//Ceci n'est pas une `fn main` assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -207,7 +219,7 @@ assert_eq!(2+2, 4); #[test] fn make_test_dont_insert_main() { // Even with that, if you set `dont_insert_main`, it won't create the `fn main` wrapper. - let opts = GlobalTestOptions::default(); + let opts = default_global_opts(""); let input = "//Ceci n'est pas une `fn main` assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -219,8 +231,8 @@ assert_eq!(2+2, 4);" } #[test] -fn make_test_issues_21299_33731() { - let opts = GlobalTestOptions::default(); +fn make_test_issues_21299() { + let opts = default_global_opts(""); let input = "// fn main assert_eq!(2+2, 4);"; @@ -234,6 +246,11 @@ assert_eq!(2+2, 4); let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 2)); +} + +#[test] +fn make_test_issues_33731() { + let opts = default_global_opts("asdf"); let input = "extern crate hella_qwop; assert_eq!(asdf::foo, 4);"; @@ -253,7 +270,7 @@ assert_eq!(asdf::foo, 4); #[test] fn make_test_main_in_macro() { - let opts = GlobalTestOptions::default(); + let opts = default_global_opts("my_crate"); let input = "#[macro_use] extern crate my_crate; test_wrapper! { fn main() {} @@ -272,7 +289,7 @@ test_wrapper! { #[test] fn make_test_returns_result() { // creates an inner function and unwraps it - let opts = GlobalTestOptions::default(); + let opts = default_global_opts(""); let input = "use std::io; let mut input = String::new(); io::stdin().read_line(&mut input)?; @@ -292,7 +309,7 @@ Ok::<(), io:Error>(()) #[test] fn make_test_named_wrapper() { // creates an inner function with a specific name - let opts = GlobalTestOptions::default(); + let opts = default_global_opts(""); let input = "assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] fn main() { #[allow(non_snake_case)] fn _doctest_main__some_unique_name() { @@ -307,8 +324,7 @@ assert_eq!(2+2, 4); #[test] fn make_test_insert_extra_space() { // will insert indent spaces in the code block if `insert_indent_space` is true - let opts = - GlobalTestOptions { no_crate_inject: false, attrs: vec![], insert_indent_space: true }; + let opts = GlobalTestOptions { insert_indent_space: true, ..default_global_opts("") }; let input = "use std::*; assert_eq!(2+2, 4); eprintln!(\"hello anan\"); @@ -327,8 +343,7 @@ fn main() { #[test] fn make_test_insert_extra_space_fn_main() { // if input already has a fn main, it should insert a space before it - let opts = - GlobalTestOptions { no_crate_inject: false, attrs: vec![], insert_indent_space: true }; + let opts = GlobalTestOptions { insert_indent_space: true, ..default_global_opts("") }; let input = "use std::*; fn main() { assert_eq!(2+2, 4); diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs index 6beca8bdb3a6..62cdc0bd5a60 100644 --- a/src/librustdoc/externalfiles.rs +++ b/src/librustdoc/externalfiles.rs @@ -1,4 +1,5 @@ use crate::html::markdown::{ErrorCodes, HeadingOffset, IdMap, Markdown, Playground}; +use rustc_errors::DiagCtxtHandle; use rustc_span::edition::Edition; use std::fs; use std::path::Path; @@ -27,7 +28,7 @@ impl ExternalHtml { md_before_content: &[String], md_after_content: &[String], nightly_build: bool, - dcx: &rustc_errors::DiagCtxt, + dcx: DiagCtxtHandle<'_>, id_map: &mut IdMap, edition: Edition, playground: &Option, @@ -46,8 +47,6 @@ impl ExternalHtml { edition, playground, heading_offset: HeadingOffset::H2, - // For external files, it'll be disabled until the feature is enabled by default. - custom_code_classes_in_docs: false, } .into_string() ); @@ -63,8 +62,6 @@ impl ExternalHtml { edition, playground, heading_offset: HeadingOffset::H2, - // For external files, it'll be disabled until the feature is enabled by default. - custom_code_classes_in_docs: false, } .into_string() ); @@ -79,7 +76,7 @@ pub(crate) enum LoadStringError { pub(crate) fn load_string>( file_path: P, - dcx: &rustc_errors::DiagCtxt, + dcx: DiagCtxtHandle<'_>, ) -> Result { let file_path = file_path.as_ref(); let contents = match fs::read(file_path) { @@ -102,7 +99,7 @@ pub(crate) fn load_string>( } } -fn load_external_files(names: &[String], dcx: &rustc_errors::DiagCtxt) -> Option { +fn load_external_files(names: &[String], dcx: DiagCtxtHandle<'_>) -> Option { let mut out = String::new(); for name in names { let Ok(s) = load_string(name, dcx) else { return None }; diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index cf11e2d7899e..c85b955d4c57 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -79,7 +79,7 @@ pub(crate) trait DocFolder: Sized { | FunctionItem(_) | OpaqueTyItem(_) | StaticItem(_) - | ConstantItem(_) + | ConstantItem(_, _, _) | TraitAliasItem(_) | TyMethodItem(_) | MethodItem(_, _) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 587c464b0ed1..4268fadd6c59 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -13,7 +13,7 @@ use std::fmt::{self, Display, Write}; use std::iter::{self, once}; use rustc_ast as ast; -use rustc_attr::{ConstStability, StabilityLevel}; +use rustc_attr::{ConstStability, StabilityLevel, StableSince}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; @@ -420,8 +420,8 @@ impl clean::GenericArgs { fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { match self { - clean::GenericArgs::AngleBracketed { args, bindings } => { - if !args.is_empty() || !bindings.is_empty() { + clean::GenericArgs::AngleBracketed { args, constraints } => { + if !args.is_empty() || !constraints.is_empty() { if f.alternate() { f.write_str("<")?; } else { @@ -439,15 +439,15 @@ impl clean::GenericArgs { write!(f, "{}", arg.print(cx))?; } } - for binding in bindings.iter() { + for constraint in constraints.iter() { if comma { f.write_str(", ")?; } comma = true; if f.alternate() { - write!(f, "{:#}", binding.print(cx))?; + write!(f, "{:#}", constraint.print(cx))?; } else { - write!(f, "{}", binding.print(cx))?; + write!(f, "{}", constraint.print(cx))?; } } if f.alternate() { @@ -1438,13 +1438,9 @@ impl clean::FnDecl { { write!(f, "\n{}", Indent(n + 4))?; } + + let last_input_index = self.inputs.values.len().checked_sub(1); for (i, input) in self.inputs.values.iter().enumerate() { - if i > 0 { - match line_wrapping_indent { - None => write!(f, ", ")?, - Some(n) => write!(f, ",\n{}", Indent(n + 4))?, - }; - } if let Some(selfty) = input.to_self() { match selfty { clean::SelfValue => { @@ -1477,18 +1473,25 @@ impl clean::FnDecl { write!(f, "{}: ", input.name)?; input.type_.print(cx).fmt(f)?; } + match (line_wrapping_indent, last_input_index) { + (_, None) => (), + (None, Some(last_i)) if i != last_i => write!(f, ", ")?, + (None, Some(_)) => (), + (Some(n), Some(last_i)) if i != last_i => write!(f, ",\n{}", Indent(n + 4))?, + (Some(_), Some(_)) => write!(f, ",\n")?, + } } if self.c_variadic { match line_wrapping_indent { None => write!(f, ", ...")?, - Some(n) => write!(f, "\n{}...", Indent(n + 4))?, + Some(n) => write!(f, "{}...\n", Indent(n + 4))?, }; } match line_wrapping_indent { None => write!(f, ")")?, - Some(n) => write!(f, "\n{})", Indent(n))?, + Some(n) => write!(f, "{})", Indent(n))?, }; self.print_output(cx).fmt(f) @@ -1633,17 +1636,24 @@ impl PrintWithSpace for hir::Mutability { pub(crate) fn print_constness_with_space( c: &hir::Constness, - s: Option, + overall_stab: Option, + const_stab: Option, ) -> &'static str { - match (c, s) { - // const stable or when feature(staged_api) is not set - ( - hir::Constness::Const, - Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }), - ) - | (hir::Constness::Const, None) => "const ", - // const unstable or not const - _ => "", + match c { + hir::Constness::Const => match (overall_stab, const_stab) { + // const stable... + (_, Some(ConstStability { level: StabilityLevel::Stable { .. }, .. })) + // ...or when feature(staged_api) is not set... + | (_, None) + // ...or when const unstable, but overall unstable too + | (None, Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. })) => { + "const " + } + // const unstable (and overall stable) + (Some(_), Some(ConstStability { level: StabilityLevel::Unstable { .. }, .. })) => "", + }, + // not const + hir::Constness::NotConst => "", } } @@ -1699,7 +1709,7 @@ impl clean::ImportSource { } } -impl clean::TypeBinding { +impl clean::AssocItemConstraint { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, @@ -1708,11 +1718,11 @@ impl clean::TypeBinding { f.write_str(self.assoc.name.as_str())?; self.assoc.args.print(cx).fmt(f)?; match self.kind { - clean::TypeBindingKind::Equality { ref term } => { + clean::AssocItemConstraintKind::Equality { ref term } => { f.write_str(" = ")?; term.print(cx).fmt(f)?; } - clean::TypeBindingKind::Constraint { ref bounds } => { + clean::AssocItemConstraintKind::Bound { ref bounds } => { if !bounds.is_empty() { f.write_str(": ")?; print_generic_bounds(bounds, cx).fmt(f)?; diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 5c5651f3ef0e..bae929c64eab 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -20,7 +20,6 @@ //! edition: Edition::Edition2015, //! playground: &None, //! heading_offset: HeadingOffset::H2, -//! custom_code_classes_in_docs: true, //! }; //! let html = md.into_string(); //! // ... something using html @@ -40,6 +39,7 @@ use std::collections::VecDeque; use std::fmt::Write; use std::iter::Peekable; use std::ops::{ControlFlow, Range}; +use std::path::PathBuf; use std::str::{self, CharIndices}; use std::sync::OnceLock; @@ -97,8 +97,6 @@ pub struct Markdown<'a> { /// Offset at which we render headings. /// E.g. if `heading_offset: HeadingOffset::H2`, then `# something` renders an `

`. pub heading_offset: HeadingOffset, - /// `true` if the `custom_code_classes_in_docs` feature is enabled. - pub custom_code_classes_in_docs: bool, } /// A struct like `Markdown` that renders the markdown with a table of contents. pub(crate) struct MarkdownWithToc<'a> { @@ -107,8 +105,6 @@ pub(crate) struct MarkdownWithToc<'a> { pub(crate) error_codes: ErrorCodes, pub(crate) edition: Edition, pub(crate) playground: &'a Option, - /// `true` if the `custom_code_classes_in_docs` feature is enabled. - pub(crate) custom_code_classes_in_docs: bool, } /// A tuple struct like `Markdown` that renders the markdown escaping HTML tags /// and includes no paragraph tags. @@ -209,7 +205,6 @@ struct CodeBlocks<'p, 'a, I: Iterator>> { // Information about the playground if a URL has been specified, containing an // optional crate name and the URL. playground: &'p Option, - custom_code_classes_in_docs: bool, } impl<'p, 'a, I: Iterator>> CodeBlocks<'p, 'a, I> { @@ -218,15 +213,8 @@ impl<'p, 'a, I: Iterator>> CodeBlocks<'p, 'a, I> { error_codes: ErrorCodes, edition: Edition, playground: &'p Option, - custom_code_classes_in_docs: bool, ) -> Self { - CodeBlocks { - inner: iter, - check_error_codes: error_codes, - edition, - playground, - custom_code_classes_in_docs, - } + CodeBlocks { inner: iter, check_error_codes: error_codes, edition, playground } } } @@ -253,12 +241,8 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { let LangString { added_classes, compile_fail, should_panic, ignore, edition, .. } = match kind { CodeBlockKind::Fenced(ref lang) => { - let parse_result = LangString::parse_without_check( - lang, - self.check_error_codes, - false, - self.custom_code_classes_in_docs, - ); + let parse_result = + LangString::parse_without_check(lang, self.check_error_codes, false); if !parse_result.rust { let added_classes = parse_result.added_classes; let lang_string = if let Some(lang) = parse_result.unknown.first() { @@ -304,8 +288,15 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { .collect::(); let krate = krate.as_ref().map(|s| s.as_str()); - let mut opts: GlobalTestOptions = Default::default(); - opts.insert_indent_space = true; + // FIXME: separate out the code to make a code block into runnable code + // from the complicated doctest logic + let opts = GlobalTestOptions { + crate_name: krate.map(String::from).unwrap_or_default(), + no_crate_inject: false, + insert_indent_space: true, + attrs: vec![], + args_file: PathBuf::new(), + }; let (test, _, _) = doctest::make_test(&test, krate, false, &opts, edition, None); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; @@ -727,33 +718,45 @@ impl<'a, I: Iterator>> Iterator for Footnotes<'a, I> { } } -pub(crate) fn find_testable_code( +/// A newtype that represents a relative line number in Markdown. +/// +/// In other words, this represents an offset from the first line of Markdown +/// in a doc comment or other source. If the first Markdown line appears on line 32, +/// and the `MdRelLine` is 3, then the absolute line for this one is 35. I.e., it's +/// a zero-based offset. +pub(crate) struct MdRelLine { + offset: usize, +} + +impl MdRelLine { + /// See struct docs. + pub(crate) const fn new(offset: usize) -> Self { + Self { offset } + } + + /// See struct docs. + pub(crate) const fn offset(self) -> usize { + self.offset + } +} + +pub(crate) fn find_testable_code( doc: &str, tests: &mut T, error_codes: ErrorCodes, enable_per_target_ignores: bool, extra_info: Option<&ExtraInfo<'_>>, - custom_code_classes_in_docs: bool, ) { - find_codes( - doc, - tests, - error_codes, - enable_per_target_ignores, - extra_info, - false, - custom_code_classes_in_docs, - ) + find_codes(doc, tests, error_codes, enable_per_target_ignores, extra_info, false) } -pub(crate) fn find_codes( +pub(crate) fn find_codes( doc: &str, tests: &mut T, error_codes: ErrorCodes, enable_per_target_ignores: bool, extra_info: Option<&ExtraInfo<'_>>, include_non_rust: bool, - custom_code_classes_in_docs: bool, ) { let mut parser = Parser::new(doc).into_offset_iter(); let mut prev_offset = 0; @@ -772,7 +775,6 @@ pub(crate) fn find_codes( error_codes, enable_per_target_ignores, extra_info, - custom_code_classes_in_docs, ) } } @@ -800,8 +802,8 @@ pub(crate) fn find_codes( if nb_lines != 0 && !&doc[prev_offset..offset.start].ends_with('\n') { nb_lines -= 1; } - let line = tests.get_line() + nb_lines + 1; - tests.add_test(text, block_info, line); + let line = MdRelLine::new(nb_lines); + tests.visit_test(text, block_info, line); prev_offset = offset.start; } Event::Start(Tag::Heading(level, _, _)) => { @@ -809,7 +811,7 @@ pub(crate) fn find_codes( } Event::Text(ref s) if register_header.is_some() => { let level = register_header.unwrap(); - tests.register_header(s, level); + tests.visit_header(s, level); register_header = None; } _ => {} @@ -834,8 +836,9 @@ impl<'tcx> ExtraInfo<'tcx> { crate::lint::INVALID_CODEBLOCK_ATTRIBUTES, self.tcx.local_def_id_to_hir_id(def_id), self.sp, - msg, - |_| {}, + |lint| { + lint.primary_message(msg); + }, ); } } @@ -850,8 +853,10 @@ impl<'tcx> ExtraInfo<'tcx> { crate::lint::INVALID_CODEBLOCK_ATTRIBUTES, self.tcx.local_def_id_to_hir_id(def_id), self.sp, - msg, - f, + |lint| { + lint.primary_message(msg); + f(lint); + }, ); } } @@ -1167,29 +1172,6 @@ impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> { } } -fn tokens(string: &str) -> impl Iterator> { - // Pandoc, which Rust once used for generating documentation, - // expects lang strings to be surrounded by `{}` and for each token - // to be proceeded by a `.`. Since some of these lang strings are still - // loose in the wild, we strip a pair of surrounding `{}` from the lang - // string and a leading `.` from each token. - - let string = string.trim(); - - let first = string.chars().next(); - let last = string.chars().last(); - - let string = - if first == Some('{') && last == Some('}') { &string[1..string.len() - 1] } else { string }; - - string - .split(|c| c == ',' || c == ' ' || c == '\t') - .map(str::trim) - .map(|token| token.strip_prefix('.').unwrap_or(token)) - .filter(|token| !token.is_empty()) - .map(|token| LangStringToken::LangToken(token)) -} - impl Default for LangString { fn default() -> Self { Self { @@ -1213,15 +1195,8 @@ impl LangString { string: &str, allow_error_code_check: ErrorCodes, enable_per_target_ignores: bool, - custom_code_classes_in_docs: bool, ) -> Self { - Self::parse( - string, - allow_error_code_check, - enable_per_target_ignores, - None, - custom_code_classes_in_docs, - ) + Self::parse(string, allow_error_code_check, enable_per_target_ignores, None) } fn parse( @@ -1229,7 +1204,6 @@ impl LangString { allow_error_code_check: ErrorCodes, enable_per_target_ignores: bool, extra: Option<&ExtraInfo<'_>>, - custom_code_classes_in_docs: bool, ) -> Self { let allow_error_code_check = allow_error_code_check.as_bool(); let mut seen_rust_tags = false; @@ -1266,11 +1240,7 @@ impl LangString { seen_rust_tags = true; } LangStringToken::LangToken("custom") => { - if custom_code_classes_in_docs { - seen_custom_tag = true; - } else { - seen_other_tags = true; - } + seen_custom_tag = true; } LangStringToken::LangToken("test_harness") => { data.test_harness = true; @@ -1361,16 +1331,12 @@ impl LangString { data.unknown.push(x.to_owned()); } LangStringToken::KeyValueAttribute(key, value) => { - if custom_code_classes_in_docs { - if key == "class" { - data.added_classes.push(value.to_owned()); - } else if let Some(extra) = extra { - extra.error_invalid_codeblock_attr(format!( - "unsupported attribute `{key}`" - )); - } - } else { - seen_other_tags = true; + if key == "class" { + data.added_classes.push(value.to_owned()); + } else if let Some(extra) = extra { + extra.error_invalid_codeblock_attr(format!( + "unsupported attribute `{key}`" + )); } } LangStringToken::ClassAttribute(class) => { @@ -1380,11 +1346,7 @@ impl LangString { } }; - if custom_code_classes_in_docs { - call(&mut TagIterator::new(string, extra)) - } else { - call(&mut tokens(string)) - } + call(&mut TagIterator::new(string, extra)); // ignore-foo overrides ignore if !ignores.is_empty() { @@ -1407,7 +1369,6 @@ impl Markdown<'_> { edition, playground, heading_offset, - custom_code_classes_in_docs, } = self; // This is actually common enough to special-case @@ -1430,7 +1391,7 @@ impl Markdown<'_> { let p = Footnotes::new(p); let p = LinkReplacer::new(p.map(|(ev, _)| ev), links); let p = TableWrapper::new(p); - let p = CodeBlocks::new(p, codes, edition, playground, custom_code_classes_in_docs); + let p = CodeBlocks::new(p, codes, edition, playground); html::push_html(&mut s, p); s @@ -1439,14 +1400,7 @@ impl Markdown<'_> { impl MarkdownWithToc<'_> { pub(crate) fn into_string(self) -> String { - let MarkdownWithToc { - content: md, - ids, - error_codes: codes, - edition, - playground, - custom_code_classes_in_docs, - } = self; + let MarkdownWithToc { content: md, ids, error_codes: codes, edition, playground } = self; let p = Parser::new_ext(md, main_body_opts()).into_offset_iter(); @@ -1458,7 +1412,7 @@ impl MarkdownWithToc<'_> { let p = HeadingLinks::new(p, Some(&mut toc), ids, HeadingOffset::H1); let p = Footnotes::new(p); let p = TableWrapper::new(p.map(|(ev, _)| ev)); - let p = CodeBlocks::new(p, codes, edition, playground, custom_code_classes_in_docs); + let p = CodeBlocks::new(p, codes, edition, playground); html::push_html(&mut s, p); } @@ -1899,11 +1853,7 @@ pub(crate) struct RustCodeBlock { /// Returns a range of bytes for each code block in the markdown that is tagged as `rust` or /// untagged (and assumed to be rust). -pub(crate) fn rust_code_blocks( - md: &str, - extra_info: &ExtraInfo<'_>, - custom_code_classes_in_docs: bool, -) -> Vec { +pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec { let mut code_blocks = vec![]; if md.is_empty() { @@ -1920,13 +1870,7 @@ pub(crate) fn rust_code_blocks( let lang_string = if syntax.is_empty() { Default::default() } else { - LangString::parse( - &*syntax, - ErrorCodes::Yes, - false, - Some(extra_info), - custom_code_classes_in_docs, - ) + LangString::parse(&*syntax, ErrorCodes::Yes, false, Some(extra_info)) }; if !lang_string.rust { continue; diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index 1de97e49b830..fb74c079c9a4 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -49,7 +49,7 @@ fn test_unique_id() { fn test_lang_string_parse() { fn t(lg: LangString) { let s = &lg.original; - assert_eq!(LangString::parse(s, ErrorCodes::Yes, true, None, true), lg) + assert_eq!(LangString::parse(s, ErrorCodes::Yes, true, None), lg) } t(Default::default()); @@ -305,7 +305,6 @@ fn test_header() { edition: DEFAULT_EDITION, playground: &None, heading_offset: HeadingOffset::H2, - custom_code_classes_in_docs: true, } .into_string(); assert_eq!(output, expect, "original: {}", input); @@ -357,7 +356,6 @@ fn test_header_ids_multiple_blocks() { edition: DEFAULT_EDITION, playground: &None, heading_offset: HeadingOffset::H2, - custom_code_classes_in_docs: true, } .into_string(); assert_eq!(output, expect, "original: {}", input); @@ -481,7 +479,7 @@ fn test_markdown_html_escape() { fn test_find_testable_code_line() { fn t(input: &str, expect: &[usize]) { let mut lines = Vec::::new(); - find_testable_code(input, &mut lines, ErrorCodes::No, false, None, true); + find_testable_code(input, &mut lines, ErrorCodes::No, false, None); assert_eq!(lines, expect); } @@ -506,7 +504,6 @@ fn test_ascii_with_prepending_hashtag() { edition: DEFAULT_EDITION, playground: &None, heading_offset: HeadingOffset::H2, - custom_code_classes_in_docs: true, } .into_string(); assert_eq!(output, expect, "original: {}", input); diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index f3ae4b76883e..877a00e206d1 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -506,7 +506,6 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String { edition: shared.edition(), playground: &shared.playground, heading_offset: HeadingOffset::H1, - custom_code_classes_in_docs: false, } .into_string() ) @@ -540,7 +539,6 @@ fn render_markdown<'a, 'cx: 'a>( heading_offset: HeadingOffset, ) -> impl fmt::Display + 'a + Captures<'cx> { display_fn(move |f| { - let custom_code_classes_in_docs = cx.tcx().features().custom_code_classes_in_docs; write!( f, "
{}
", @@ -552,7 +550,6 @@ fn render_markdown<'a, 'cx: 'a>( edition: cx.shared.edition(), playground: &cx.shared.playground, heading_offset, - custom_code_classes_in_docs, } .into_string() ) @@ -928,9 +925,11 @@ fn assoc_method( // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove // this condition. let constness = match render_mode { - RenderMode::Normal => { - print_constness_with_space(&header.constness, meth.const_stability(tcx)) - } + RenderMode::Normal => print_constness_with_space( + &header.constness, + meth.stable_since(tcx), + meth.const_stability(tcx), + ), RenderMode::ForDeref { .. } => "", }; let asyncness = header.asyncness.print_with_space(); @@ -1016,18 +1015,23 @@ fn render_stability_since_raw_with_extra( .map(|since| (format!("const since {since}"), format!("const: {since}"))) } Some(ConstStability { level: StabilityLevel::Unstable { issue, .. }, feature, .. }) => { - let unstable = if let Some(n) = issue { - format!( - "
unstable" - ) - } else { - String::from("unstable") - }; + ) + } else { + String::from("unstable") + }; - Some((String::from("const unstable"), format!("const: {unstable}"))) + Some((String::from("const unstable"), format!("const: {unstable}"))) + } } _ => None, }; @@ -1878,7 +1882,6 @@ fn render_impl(

", ); } - let custom_code_classes_in_docs = cx.tcx().features().custom_code_classes_in_docs; write!( w, "
{}
", @@ -1890,7 +1893,6 @@ fn render_impl( edition: cx.shared.edition(), playground: &cx.shared.playground, heading_offset: HeadingOffset::H4, - custom_code_classes_in_docs, } .into_string() ); diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index c7a23aa8503a..e1f79254b248 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -266,7 +266,7 @@ pub(super) fn print_item(cx: &mut Context<'_>, item: &clean::Item, buf: &mut Buf clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m), clean::PrimitiveItem(_) => item_primitive(buf, cx, item), clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => item_static(buf, cx, item, i), - clean::ConstantItem(ref c) => item_constant(buf, cx, item, c), + clean::ConstantItem(generics, ty, c) => item_constant(buf, cx, item, generics, ty, c), clean::ForeignTypeItem => item_foreign_type(buf, cx, item), clean::KeywordItem => item_keyword(buf, cx, item), clean::OpaqueTyItem(ref e) => item_opaque_ty(buf, cx, item, e), @@ -615,7 +615,18 @@ fn extra_info_tags<'a, 'tcx: 'a>( fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &clean::Function) { let tcx = cx.tcx(); let header = it.fn_header(tcx).expect("printing a function which isn't a function"); - let constness = print_constness_with_space(&header.constness, it.const_stability(tcx)); + debug!( + "item_function/const: {:?} {:?} {:?} {:?}", + it.name, + &header.constness, + it.stable_since(tcx), + it.const_stability(tcx), + ); + let constness = print_constness_with_space( + &header.constness, + it.stable_since(tcx), + it.const_stability(tcx), + ); let safety = header.safety.print_with_space(); let abi = print_abi_with_space(header.abi).to_string(); let asyncness = header.asyncness.print_with_space(); @@ -1833,7 +1844,14 @@ fn item_primitive(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Ite } } -fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) { +fn item_constant( + w: &mut Buffer, + cx: &mut Context<'_>, + it: &clean::Item, + generics: &clean::Generics, + ty: &clean::Type, + c: &clean::Constant, +) { wrap_item(w, |w| { let tcx = cx.tcx(); render_attributes_in_code(w, it, cx); @@ -1843,9 +1861,9 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle "{vis}const {name}{generics}: {typ}{where_clause}", vis = visibility_print_with_space(it, cx), name = it.name.unwrap(), - generics = c.generics.print(cx), - typ = c.type_.print(cx), - where_clause = print_where_clause(&c.generics, cx, 0, Ending::NoNewline), + generics = generics.print(cx), + typ = ty.print(cx), + where_clause = print_where_clause(&generics, cx, 0, Ending::NoNewline), ); // FIXME: The code below now prints diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index e635c1e611de..bc8bdffc3314 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -727,7 +727,7 @@ pub(crate) fn get_function_type_for_search<'tcx>( name: *name, args: clean::GenericArgs::AngleBracketed { args: Vec::new().into_boxed_slice(), - bindings: ThinVec::new(), + constraints: ThinVec::new(), }, }) .collect(), @@ -1049,7 +1049,7 @@ fn simplify_fn_type<'tcx, 'a>( // So in here, we can add it directly and look for its own type parameters (so for `Option`, // we will look for them but not for `T`). let mut ty_generics = Vec::new(); - let mut ty_bindings = Vec::new(); + let mut ty_constraints = Vec::new(); if let Some(arg_generics) = arg.generic_args() { for ty in arg_generics.into_iter().filter_map(|gen| match gen { clean::GenericArg::Type(ty) => Some(ty), @@ -1067,14 +1067,14 @@ fn simplify_fn_type<'tcx, 'a>( cache, ); } - for binding in arg_generics.bindings() { - simplify_fn_binding( + for constraint in arg_generics.constraints() { + simplify_fn_constraint( self_, generics, - &binding, + &constraint, tcx, recurse + 1, - &mut ty_bindings, + &mut ty_constraints, rgen, is_return, cache, @@ -1137,7 +1137,7 @@ fn simplify_fn_type<'tcx, 'a>( *stored_bounds = type_bounds; } } - ty_bindings.push(( + ty_constraints.push(( RenderTypeId::AssociatedType(name), vec![RenderType { id: Some(RenderTypeId::Index(idx)), @@ -1152,17 +1152,17 @@ fn simplify_fn_type<'tcx, 'a>( if id.is_some() || !ty_generics.is_empty() { res.push(RenderType { id, - bindings: if ty_bindings.is_empty() { None } else { Some(ty_bindings) }, + bindings: if ty_constraints.is_empty() { None } else { Some(ty_constraints) }, generics: if ty_generics.is_empty() { None } else { Some(ty_generics) }, }); } } } -fn simplify_fn_binding<'tcx, 'a>( +fn simplify_fn_constraint<'tcx, 'a>( self_: Option<&'a Type>, generics: &Generics, - binding: &'a clean::TypeBinding, + constraint: &'a clean::AssocItemConstraint, tcx: TyCtxt<'tcx>, recurse: usize, res: &mut Vec<(RenderTypeId, Vec)>, @@ -1170,9 +1170,9 @@ fn simplify_fn_binding<'tcx, 'a>( is_return: bool, cache: &Cache, ) { - let mut ty_binding_constraints = Vec::new(); - let ty_binding_assoc = RenderTypeId::AssociatedType(binding.assoc.name); - for gen in &binding.assoc.args { + let mut ty_constraints = Vec::new(); + let ty_constrained_assoc = RenderTypeId::AssociatedType(constraint.assoc.name); + for gen in &constraint.assoc.args { match gen { clean::GenericArg::Type(arg) => simplify_fn_type( self_, @@ -1180,7 +1180,7 @@ fn simplify_fn_binding<'tcx, 'a>( &arg, tcx, recurse + 1, - &mut ty_binding_constraints, + &mut ty_constraints, rgen, is_return, cache, @@ -1190,11 +1190,11 @@ fn simplify_fn_binding<'tcx, 'a>( | clean::GenericArg::Infer => {} } } - for binding in binding.assoc.args.bindings() { - simplify_fn_binding( + for constraint in constraint.assoc.args.constraints() { + simplify_fn_constraint( self_, generics, - &binding, + &constraint, tcx, recurse + 1, res, @@ -1203,8 +1203,8 @@ fn simplify_fn_binding<'tcx, 'a>( cache, ); } - match &binding.kind { - clean::TypeBindingKind::Equality { term } => { + match &constraint.kind { + clean::AssocItemConstraintKind::Equality { term } => { if let clean::Term::Type(arg) = &term { simplify_fn_type( self_, @@ -1212,14 +1212,14 @@ fn simplify_fn_binding<'tcx, 'a>( arg, tcx, recurse + 1, - &mut ty_binding_constraints, + &mut ty_constraints, rgen, is_return, cache, ); } } - clean::TypeBindingKind::Constraint { bounds } => { + clean::AssocItemConstraintKind::Bound { bounds } => { for bound in &bounds[..] { if let Some(path) = bound.get_trait_path() { let ty = Type::Path { path }; @@ -1229,7 +1229,7 @@ fn simplify_fn_binding<'tcx, 'a>( &ty, tcx, recurse + 1, - &mut ty_binding_constraints, + &mut ty_constraints, rgen, is_return, cache, @@ -1238,7 +1238,7 @@ fn simplify_fn_binding<'tcx, 'a>( } } } - res.push((ty_binding_assoc, ty_binding_constraints)); + res.push((ty_constrained_assoc, ty_constraints)); } /// Return the full list of types when bounds have been resolved. diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 8ee35db56f8d..0535246ae10f 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -162,9 +162,7 @@ impl<'tcx> SpanMapVisitor<'tcx> { // compiled (because of cfg)! // // See discussion in https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352 - let typeck_results = self - .tcx - .typeck_body(hir.maybe_body_owned_by(body_id).expect("a body which isn't a body")); + let typeck_results = self.tcx.typeck_body(hir.body_owned_by(body_id).id()); // Interestingly enough, for method calls, we need the whole expression whereas for static // method/function calls, we need the call expression specifically. if let Some(def_id) = typeck_results.type_dependent_def_id(expr_hir_id.unwrap_or(hir_id)) { diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js index a1e9cc6dfa14..fbb096fe9c75 100644 --- a/src/librustdoc/html/static/.eslintrc.js +++ b/src/librustdoc/html/static/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": 8, + "ecmaVersion": 2019, "sourceType": "module" }, "rules": { diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js index 8cebce7ae861..6fd60d6cc346 100644 --- a/src/librustdoc/html/static/js/externs.js +++ b/src/librustdoc/html/static/js/externs.js @@ -41,8 +41,9 @@ let ParserState; * foundElems: number, * totalElems: number, * literalSearch: boolean, - * corrections: Array<{from: string, to: integer}>, + * corrections: Array<{from: string, to: integer}> | null, * typeFingerprint: Uint32Array, + * error: Array | null, * }} */ let ParsedQuery; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 76a6fc9008ea..a0ab262bf0b0 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -89,6 +89,10 @@ const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../"; // of permutations we need to check. const UNBOXING_LIMIT = 5; +// used for search query verification +const REGEX_IDENT = /\p{ID_Start}\p{ID_Continue}*|_\p{ID_Continue}+/uy; +const REGEX_INVALID_TYPE_FILTER = /[^a-z]/ui; + // In the search display, allows to switch between tabs. function printTab(nb) { let iter = 0; @@ -410,18 +414,21 @@ function initSearch(rawSearchIndex) { } /** - * Returns `true` if the given `c` character is valid for an ident. + * If the current parser position is at the beginning of an identifier, + * move the position to the end of it and return `true`. Otherwise, return `false`. * - * @param {string} c + * @param {ParserState} parserState * * @return {boolean} */ - function isIdentCharacter(c) { - return ( - c === "_" || - (c >= "0" && c <= "9") || - (c >= "a" && c <= "z") || - (c >= "A" && c <= "Z")); + function consumeIdent(parserState) { + REGEX_IDENT.lastIndex = parserState.pos; + const match = parserState.userQuery.match(REGEX_IDENT); + if (match) { + parserState.pos += match[0].length; + return true; + } + return false; } /** @@ -618,70 +625,62 @@ function initSearch(rawSearchIndex) { * @return {integer} */ function getIdentEndPosition(parserState) { - const start = parserState.pos; + let afterIdent = consumeIdent(parserState); let end = parserState.pos; - let foundExclamation = -1; + let macroExclamation = -1; while (parserState.pos < parserState.length) { const c = parserState.userQuery[parserState.pos]; - if (!isIdentCharacter(c)) { - if (c === "!") { - if (foundExclamation !== -1) { - throw ["Cannot have more than one ", "!", " in an ident"]; - } else if (parserState.pos + 1 < parserState.length && - isIdentCharacter(parserState.userQuery[parserState.pos + 1]) - ) { + if (c === "!") { + if (macroExclamation !== -1) { + throw ["Cannot have more than one ", "!", " in an ident"]; + } else if (parserState.pos + 1 < parserState.length) { + const pos = parserState.pos; + parserState.pos++; + const beforeIdent = consumeIdent(parserState); + parserState.pos = pos; + if (beforeIdent) { throw ["Unexpected ", "!", ": it can only be at the end of an ident"]; } - foundExclamation = parserState.pos; - } else if (isPathSeparator(c)) { - if (c === ":") { - if (!isPathStart(parserState)) { + } + if (afterIdent) macroExclamation = parserState.pos; + } else if (isPathSeparator(c)) { + if (c === ":") { + if (!isPathStart(parserState)) { + break; + } + // Skip current ":". + parserState.pos += 1; + } else { + while (parserState.pos + 1 < parserState.length) { + const next_c = parserState.userQuery[parserState.pos + 1]; + if (next_c !== " ") { break; } - // Skip current ":". parserState.pos += 1; - } else { - while (parserState.pos + 1 < parserState.length) { - const next_c = parserState.userQuery[parserState.pos + 1]; - if (next_c !== " ") { - break; - } - parserState.pos += 1; - } } - if (foundExclamation !== -1) { - if (foundExclamation !== start && - isIdentCharacter(parserState.userQuery[foundExclamation - 1]) - ) { - throw ["Cannot have associated items in macros"]; - } else { - // while the never type has no associated macros, we still - // can parse a path like that - foundExclamation = -1; - } - } - } else if ( - c === "[" || - c === "(" || - isEndCharacter(c) || - isSpecialStartCharacter(c) || - isSeparatorCharacter(c) - ) { - break; - } else if (parserState.pos > 0) { - throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1]]; - } else { - throw ["Unexpected ", c]; } + if (macroExclamation !== -1) { + throw ["Cannot have associated items in macros"]; + } + } else if ( + c === "[" || + c === "(" || + isEndCharacter(c) || + isSpecialStartCharacter(c) || + isSeparatorCharacter(c) + ) { + break; + } else if (parserState.pos > 0) { + throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1], + " (not a valid identifier)"]; + } else { + throw ["Unexpected ", c, " (not a valid identifier)"]; } parserState.pos += 1; + afterIdent = consumeIdent(parserState); end = parserState.pos; } - // if start == end - 1, we got the never type - if (foundExclamation !== -1 && - foundExclamation !== start && - isIdentCharacter(parserState.userQuery[foundExclamation - 1]) - ) { + if (macroExclamation !== -1) { if (parserState.typeFilter === null) { parserState.typeFilter = "macro"; } else if (parserState.typeFilter !== "macro") { @@ -693,7 +692,7 @@ function initSearch(rawSearchIndex) { " both specified", ]; } - end = foundExclamation; + end = macroExclamation; } return end; } @@ -1071,16 +1070,15 @@ function initSearch(rawSearchIndex) { function checkExtraTypeFilterCharacters(start, parserState) { const query = parserState.userQuery.slice(start, parserState.pos).trim(); - for (const c in query) { - if (!isIdentCharacter(query[c])) { - throw [ - "Unexpected ", - query[c], - " in type filter (before ", - ":", - ")", - ]; - } + const match = query.match(REGEX_INVALID_TYPE_FILTER); + if (match) { + throw [ + "Unexpected ", + match[0], + " in type filter (before ", + ":", + ")", + ]; } } @@ -2127,7 +2125,7 @@ function initSearch(rawSearchIndex) { }; } - function handleAliases(ret, query, filterCrates, currentCrate) { + async function handleAliases(ret, query, filterCrates, currentCrate) { const lowerQuery = query.toLowerCase(); // We separate aliases and crate aliases because we want to have current crate // aliases to be before the others in the displayed results. @@ -2163,6 +2161,15 @@ function initSearch(rawSearchIndex) { crateAliases.sort(sortFunc); aliases.sort(sortFunc); + const fetchDesc = alias => { + return searchIndexEmptyDesc.get(alias.crate).contains(alias.bitIndex) ? + "" : searchState.loadDesc(alias); + }; + const [crateDescs, descs] = await Promise.all([ + Promise.all(crateAliases.map(fetchDesc)), + Promise.all(aliases.map(fetchDesc)), + ]); + const pushFunc = alias => { alias.alias = query; const res = buildHrefAndPath(alias); @@ -2176,7 +2183,13 @@ function initSearch(rawSearchIndex) { } }; + aliases.forEach((alias, i) => { + alias.desc = descs[i]; + }); aliases.forEach(pushFunc); + crateAliases.forEach((alias, i) => { + alias.desc = crateDescs[i]; + }); crateAliases.forEach(pushFunc); } @@ -2386,15 +2399,19 @@ function initSearch(rawSearchIndex) { * @param {boolean} isAssocType */ function convertNameToId(elem, isAssocType) { - if (typeNameIdMap.has(elem.normalizedPathLast) && - (isAssocType || !typeNameIdMap.get(elem.normalizedPathLast).assocOnly)) { - elem.id = typeNameIdMap.get(elem.normalizedPathLast).id; + const loweredName = elem.pathLast.toLowerCase(); + if (typeNameIdMap.has(loweredName) && + (isAssocType || !typeNameIdMap.get(loweredName).assocOnly)) { + elem.id = typeNameIdMap.get(loweredName).id; } else if (!parsedQuery.literalSearch) { let match = null; let matchDist = maxEditDistance + 1; let matchName = ""; for (const [name, {id, assocOnly}] of typeNameIdMap) { - const dist = editDistance(name, elem.normalizedPathLast, maxEditDistance); + const dist = Math.min( + editDistance(name, loweredName, maxEditDistance), + editDistance(name, elem.normalizedPathLast, maxEditDistance), + ); if (dist <= matchDist && dist <= maxEditDistance && (isAssocType || !assocOnly)) { if (dist === matchDist && matchName > name) { @@ -2538,7 +2555,8 @@ function initSearch(rawSearchIndex) { sorted_returned, sorted_others, parsedQuery); - handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate); + await handleAliases(ret, parsedQuery.original.replace(/"/g, ""), + filterCrates, currentCrate); await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => { const descs = await Promise.all(list.map(result => { return searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ? diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index f856b4e9f16c..afafb4fbe4b0 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -153,9 +153,9 @@ impl FromWithTcx for GenericArgs { fn from_tcx(args: clean::GenericArgs, tcx: TyCtxt<'_>) -> Self { use clean::GenericArgs::*; match args { - AngleBracketed { args, bindings } => GenericArgs::AngleBracketed { + AngleBracketed { args, constraints } => GenericArgs::AngleBracketed { args: args.into_vec().into_tcx(tcx), - bindings: bindings.into_tcx(tcx), + bindings: constraints.into_tcx(tcx), }, Parenthesized { inputs, output } => GenericArgs::Parenthesized { inputs: inputs.into_vec().into_tcx(tcx), @@ -183,26 +183,26 @@ impl FromWithTcx for Constant { let expr = constant.expr(tcx); let value = constant.value(tcx); let is_literal = constant.is_literal(tcx); - Constant { type_: (*constant.type_).into_tcx(tcx), expr, value, is_literal } + Constant { expr, value, is_literal } } } -impl FromWithTcx for TypeBinding { - fn from_tcx(binding: clean::TypeBinding, tcx: TyCtxt<'_>) -> Self { +impl FromWithTcx for TypeBinding { + fn from_tcx(constraint: clean::AssocItemConstraint, tcx: TyCtxt<'_>) -> Self { TypeBinding { - name: binding.assoc.name.to_string(), - args: binding.assoc.args.into_tcx(tcx), - binding: binding.kind.into_tcx(tcx), + name: constraint.assoc.name.to_string(), + args: constraint.assoc.args.into_tcx(tcx), + binding: constraint.kind.into_tcx(tcx), } } } -impl FromWithTcx for TypeBindingKind { - fn from_tcx(kind: clean::TypeBindingKind, tcx: TyCtxt<'_>) -> Self { - use clean::TypeBindingKind::*; +impl FromWithTcx for TypeBindingKind { + fn from_tcx(kind: clean::AssocItemConstraintKind, tcx: TyCtxt<'_>) -> Self { + use clean::AssocItemConstraintKind::*; match kind { Equality { term } => TypeBindingKind::Equality(term.into_tcx(tcx)), - Constraint { bounds } => TypeBindingKind::Constraint(bounds.into_tcx(tcx)), + Bound { bounds } => TypeBindingKind::Constraint(bounds.into_tcx(tcx)), } } } @@ -321,7 +321,10 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum { ForeignTypeItem => ItemEnum::ForeignType, TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_tcx(tcx)), OpaqueTyItem(t) => ItemEnum::OpaqueTy(t.into_tcx(tcx)), - ConstantItem(c) => ItemEnum::Constant(c.into_tcx(tcx)), + // FIXME(generic_const_items): Add support for generic free consts + ConstantItem(_generics, t, c) => { + ItemEnum::Constant { type_: (*t).into_tcx(tcx), const_: c.into_tcx(tcx) } + } MacroItem(m) => ItemEnum::Macro(m.source), ProcMacroItem(m) => ItemEnum::ProcMacro(m.into_tcx(tcx)), PrimitiveItem(p) => { @@ -820,7 +823,10 @@ impl FromWithTcx for Static { Static { type_: stat.type_.into_tcx(tcx), mutable: stat.mutability == ast::Mutability::Mut, - expr: stat.expr.map(|e| rendered_const(tcx, e)).unwrap_or_default(), + expr: stat + .expr + .map(|e| rendered_const(tcx, tcx.hir().body(e), tcx.hir().body_owner_def_id(e))) + .unwrap_or_default(), } } } diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index c1d90020e87e..0ef24818515c 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -186,7 +186,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { | types::ItemEnum::Impl(_) | types::ItemEnum::TypeAlias(_) | types::ItemEnum::OpaqueTy(_) - | types::ItemEnum::Constant(_) + | types::ItemEnum::Constant { .. } | types::ItemEnum::Static(_) | types::ItemEnum::ForeignType | types::ItemEnum::Macro(_) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 0650afb90c7e..fb4cd218b84c 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -8,7 +8,6 @@ #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] -#![feature(lazy_cell)] #![feature(let_chains)] #![feature(never_type)] #![feature(round_char_boundary)] @@ -78,7 +77,7 @@ use std::io::{self, IsTerminal}; use std::process; use std::sync::{atomic::AtomicBool, Arc}; -use rustc_errors::{ErrorGuaranteed, FatalError}; +use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError}; use rustc_interface::interface; use rustc_middle::ty::TyCtxt; use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup}; @@ -201,7 +200,6 @@ fn init_logging(early_dcx: &EarlyDiagCtxt) { let filter = tracing_subscriber::EnvFilter::from_env("RUSTDOC_LOG"); let layer = tracing_tree::HierarchicalLayer::default() .with_writer(io::stderr) - .with_indent_lines(true) .with_ansi(color_logs) .with_targets(true) .with_wraparound(10) @@ -557,6 +555,14 @@ fn opts() -> Vec { unstable("no-run", |o| { o.optflagmulti("", "no-run", "Compile doctests without running them") }), + unstable("remap-path-prefix", |o| { + o.optmulti( + "", + "remap-path-prefix", + "Remap source names in compiler messages", + "FROM=TO", + ) + }), unstable("show-type-layout", |o| { o.optflagmulti("", "show-type-layout", "Include the memory layout of types in the docs") }), @@ -664,7 +670,7 @@ fn usage(argv0: &str) { /// A result type used by several functions under `main()`. type MainResult = Result<(), ErrorGuaranteed>; -pub(crate) fn wrap_return(dcx: &rustc_errors::DiagCtxt, res: Result<(), String>) -> MainResult { +pub(crate) fn wrap_return(dcx: DiagCtxtHandle<'_>, res: Result<(), String>) -> MainResult { match res { Ok(()) => dcx.has_errors().map_or(Ok(()), Err), Err(err) => Err(dcx.err(err)), @@ -726,12 +732,13 @@ fn main_args( None => return Ok(()), }; - let diag = + let dcx = core::new_dcx(options.error_format, None, options.diagnostic_width, &options.unstable_opts); + let dcx = dcx.handle(); match (options.should_test, options.markdown_input()) { - (true, Some(_)) => return wrap_return(&diag, markdown::test(options)), - (true, None) => return doctest::run(&diag, options), + (true, Some(_)) => return wrap_return(dcx, doctest::test_markdown(options)), + (true, None) => return doctest::run(dcx, options), (false, Some(input)) => { let input = input.to_owned(); let edition = options.edition; @@ -741,7 +748,7 @@ fn main_args( // requires session globals and a thread pool, so we use // `run_compiler`. return wrap_return( - &diag, + dcx, interface::run_compiler(config, |_compiler| { markdown::render(&input, render_options, edition) }), diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 7289ed56dc7a..a98f81d011e8 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -3,18 +3,12 @@ use std::fs::{create_dir_all, read_to_string, File}; use std::io::prelude::*; use std::path::Path; -use tempfile::tempdir; - use rustc_span::edition::Edition; -use rustc_span::DUMMY_SP; -use crate::config::{Options, RenderOptions}; -use crate::doctest::{generate_args_file, Collector, GlobalTestOptions}; +use crate::config::RenderOptions; use crate::html::escape::Escape; use crate::html::markdown; -use crate::html::markdown::{ - find_testable_code, ErrorCodes, HeadingOffset, IdMap, Markdown, MarkdownWithToc, -}; +use crate::html::markdown::{ErrorCodes, HeadingOffset, IdMap, Markdown, MarkdownWithToc}; /// Separate any lines at the start of the file that begin with `# ` or `%`. fn extract_leading_metadata(s: &str) -> (Vec<&str>, &str) { @@ -82,8 +76,6 @@ pub(crate) fn render>( error_codes, edition, playground: &playground, - // For markdown files, it'll be disabled until the feature is enabled by default. - custom_code_classes_in_docs: false, } .into_string() } else { @@ -95,8 +87,6 @@ pub(crate) fn render>( edition, playground: &playground, heading_offset: HeadingOffset::H1, - // For markdown files, it'll be disabled until the feature is enabled by default. - custom_code_classes_in_docs: false, } .into_string() }; @@ -141,48 +131,3 @@ pub(crate) fn render>( Ok(_) => Ok(()), } } - -/// Runs any tests/code examples in the markdown file `input`. -pub(crate) fn test(options: Options) -> Result<(), String> { - use rustc_session::config::Input; - let input_str = match &options.input { - Input::File(path) => { - read_to_string(&path).map_err(|err| format!("{}: {err}", path.display()))? - } - Input::Str { name: _, input } => input.clone(), - }; - - let mut opts = GlobalTestOptions::default(); - opts.no_crate_inject = true; - - let temp_dir = - tempdir().map_err(|error| format!("failed to create temporary directory: {error:?}"))?; - let file_path = temp_dir.path().join("rustdoc-cfgs"); - generate_args_file(&file_path, &options)?; - - let mut collector = Collector::new( - options.input.filestem().to_string(), - options.clone(), - true, - opts, - None, - options.input.opt_path().map(ToOwned::to_owned), - options.enable_per_target_ignores, - file_path, - ); - collector.set_position(DUMMY_SP); - let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); - - // For markdown files, custom code classes will be disabled until the feature is enabled by default. - find_testable_code( - &input_str, - &mut collector, - codes, - options.enable_per_target_ignores, - None, - false, - ); - - crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests); - Ok(()) -} diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 60def40588aa..592dd0a145cf 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -208,14 +208,7 @@ impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> { let has_docs = !i.attrs.doc_strings.is_empty(); let mut tests = Tests { found_tests: 0 }; - find_testable_code( - &i.doc_value(), - &mut tests, - ErrorCodes::No, - false, - None, - self.ctx.tcx.features().custom_code_classes_in_docs, - ); + find_testable_code(&i.doc_value(), &mut tests, ErrorCodes::No, false, None); let has_doc_example = tests.found_tests != 0; let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap(); diff --git a/src/librustdoc/passes/check_custom_code_classes.rs b/src/librustdoc/passes/check_custom_code_classes.rs deleted file mode 100644 index 524795ed77c2..000000000000 --- a/src/librustdoc/passes/check_custom_code_classes.rs +++ /dev/null @@ -1,93 +0,0 @@ -//! NIGHTLY & UNSTABLE CHECK: custom_code_classes_in_docs -//! -//! This pass will produce errors when finding custom classes outside of -//! nightly + relevant feature active. - -use super::Pass; -use crate::clean::{Crate, Item}; -use crate::core::DocContext; -use crate::fold::DocFolder; -use crate::html::markdown::{find_codes, ErrorCodes, LangString}; - -use rustc_errors::StashKey; -use rustc_feature::GateIssue; -use rustc_session::parse::add_feature_diagnostics_for_issue; -use rustc_span::symbol::sym; - -pub(crate) const CHECK_CUSTOM_CODE_CLASSES: Pass = Pass { - name: "check-custom-code-classes", - run: check_custom_code_classes, - description: "check for custom code classes without the feature-gate enabled", -}; - -pub(crate) fn check_custom_code_classes(krate: Crate, cx: &mut DocContext<'_>) -> Crate { - if cx.tcx.features().custom_code_classes_in_docs { - // Nothing to check here if the feature is enabled. - return krate; - } - let mut coll = CustomCodeClassLinter { cx }; - - coll.fold_crate(krate) -} - -struct CustomCodeClassLinter<'a, 'tcx> { - cx: &'a DocContext<'tcx>, -} - -impl<'a, 'tcx> DocFolder for CustomCodeClassLinter<'a, 'tcx> { - fn fold_item(&mut self, item: Item) -> Option { - look_for_custom_classes(&self.cx, &item); - Some(self.fold_item_recur(item)) - } -} - -#[derive(Debug)] -struct TestsWithCustomClasses { - custom_classes_found: Vec, -} - -impl crate::doctest::Tester for TestsWithCustomClasses { - fn add_test(&mut self, _: String, config: LangString, _: usize) { - self.custom_classes_found.extend(config.added_classes); - } -} - -pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item) { - if !item.item_id.is_local() { - // If non-local, no need to check anything. - return; - } - - let mut tests = TestsWithCustomClasses { custom_classes_found: vec![] }; - - let dox = item.attrs.doc_value(); - find_codes(&dox, &mut tests, ErrorCodes::No, false, None, true, true); - - if !tests.custom_classes_found.is_empty() { - let span = item.attr_span(cx.tcx); - let sess = &cx.tcx.sess; - let mut err = sess - .dcx() - .struct_span_warn(span, "custom classes in code blocks will change behaviour"); - add_feature_diagnostics_for_issue( - &mut err, - sess, - sym::custom_code_classes_in_docs, - GateIssue::Language, - false, - None, - ); - - err.note( - // This will list the wrong items to make them more easily searchable. - // To ensure the most correct hits, it adds back the 'class:' that was stripped. - format!( - "found these custom classes: class={}", - tests.custom_classes_found.join(",class=") - ), - ); - - // A later feature_err call can steal and cancel this warning. - err.stash(span, StashKey::EarlySyntaxWarning); - } -} diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index e85b998bfbe1..0437f5e5fd81 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -10,7 +10,7 @@ use crate::clean; use crate::clean::utils::inherits_doc_hidden; use crate::clean::*; use crate::core::DocContext; -use crate::html::markdown::{find_testable_code, ErrorCodes, Ignore, LangString}; +use crate::html::markdown::{find_testable_code, ErrorCodes, Ignore, LangString, MdRelLine}; use crate::visit::DocVisitor; use rustc_hir as hir; use rustc_middle::lint::LintLevelSource; @@ -44,8 +44,8 @@ pub(crate) struct Tests { pub(crate) found_tests: usize, } -impl crate::doctest::Tester for Tests { - fn add_test(&mut self, _: String, config: LangString, _: usize) { +impl crate::doctest::DoctestVisitor for Tests { + fn visit_test(&mut self, _: String, config: LangString, _: MdRelLine) { if config.rust && config.ignore == Ignore::None { self.found_tests += 1; } @@ -62,7 +62,7 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) - | clean::AssocTypeItem(..) | clean::TypeAliasItem(_) | clean::StaticItem(_) - | clean::ConstantItem(_) + | clean::ConstantItem(_, _, _) | clean::ExternCrateItem { .. } | clean::ImportItem(_) | clean::PrimitiveItem(_) @@ -112,26 +112,15 @@ pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item let mut tests = Tests { found_tests: 0 }; - find_testable_code( - dox, - &mut tests, - ErrorCodes::No, - false, - None, - cx.tcx.features().custom_code_classes_in_docs, - ); + find_testable_code(dox, &mut tests, ErrorCodes::No, false, None); if tests.found_tests == 0 && cx.tcx.features().rustdoc_missing_doc_code_examples { if should_have_doc_example(cx, item) { debug!("reporting error for {item:?} (hir_id={hir_id:?})"); let sp = item.attr_span(cx.tcx); - cx.tcx.node_span_lint( - crate::lint::MISSING_DOC_CODE_EXAMPLES, - hir_id, - sp, - "missing code example in this documentation", - |_| {}, - ); + cx.tcx.node_span_lint(crate::lint::MISSING_DOC_CODE_EXAMPLES, hir_id, sp, |lint| { + lint.primary_message("missing code example in this documentation"); + }); } } else if tests.found_tests > 0 && !cx.cache.effective_visibilities.is_exported(cx.tcx, item.item_id.expect_def_id()) @@ -140,8 +129,9 @@ pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item crate::lint::PRIVATE_DOC_TESTS, hir_id, item.attr_span(cx.tcx), - "documentation test in private item", - |_| {}, + |lint| { + lint.primary_message("documentation test in private item"); + }, ); } } diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 747f5c0a8356..440b02a1fa75 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -12,7 +12,7 @@ use rustc_errors::{Applicability, Diag, DiagMessage}; use rustc_hir::def::Namespace::*; use rustc_hir::def::{DefKind, Namespace, PerNS}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; -use rustc_hir::Mutability; +use rustc_hir::{Mutability, Safety}; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_middle::{bug, span_bug, ty}; use rustc_resolve::rustdoc::{has_primitive_or_keyword_docs, prepare_to_doc_link_resolution}; @@ -1517,7 +1517,11 @@ impl Disambiguator { "union" => Kind(DefKind::Union), "module" | "mod" => Kind(DefKind::Mod), "const" | "constant" => Kind(DefKind::Const), - "static" => Kind(DefKind::Static { mutability: Mutability::Not, nested: false }), + "static" => Kind(DefKind::Static { + mutability: Mutability::Not, + nested: false, + safety: Safety::Safe, + }), "function" | "fn" | "method" => Kind(DefKind::Fn), "derive" => Kind(DefKind::Macro(MacroKind::Derive)), "type" => NS(Namespace::TypeNS), @@ -1689,7 +1693,9 @@ fn report_diagnostic( let sp = item.attr_span(tcx); - tcx.node_span_lint(lint, hir_id, sp, msg, |lint| { + tcx.node_span_lint(lint, hir_id, sp, |lint| { + lint.primary_message(msg); + let (span, link_range) = match link_range { MarkdownLinkRange::Destination(md_range) => { let mut md_range = md_range.clone(); diff --git a/src/librustdoc/passes/lint/bare_urls.rs b/src/librustdoc/passes/lint/bare_urls.rs index c6989fbbf255..8f68f6ff4764 100644 --- a/src/librustdoc/passes/lint/bare_urls.rs +++ b/src/librustdoc/passes/lint/bare_urls.rs @@ -24,8 +24,9 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item) { let sp = source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs.doc_strings) .unwrap_or_else(|| item.attr_span(cx.tcx)); - cx.tcx.node_span_lint(crate::lint::BARE_URLS, hir_id, sp, msg, |lint| { - lint.note("bare URLs are not automatically turned into clickable links") + cx.tcx.node_span_lint(crate::lint::BARE_URLS, hir_id, sp, |lint| { + lint.primary_message(msg) + .note("bare URLs are not automatically turned into clickable links") .span_suggestion( sp, "use an automatic link instead", diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs index 7b81b5e63be6..c185442fd55d 100644 --- a/src/librustdoc/passes/lint/check_code_block_syntax.rs +++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs @@ -5,7 +5,7 @@ use rustc_errors::{ translation::{to_fluent_args, Translate}, Applicability, DiagCtxt, DiagInner, LazyFallbackBundle, }; -use rustc_parse::parse_stream_from_source_str; +use rustc_parse::{source_str_to_stream, unwrap_or_emit_fatal}; use rustc_resolve::rustdoc::source_span_for_markdown_range; use rustc_session::parse::ParseSess; use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId, Transparency}; @@ -20,9 +20,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &clean::Item) { if let Some(dox) = &item.opt_doc_value() { let sp = item.attr_span(cx.tcx); let extra = crate::html::markdown::ExtraInfo::new(cx.tcx, item.item_id.expect_def_id(), sp); - for code_block in - markdown::rust_code_blocks(dox, &extra, cx.tcx.features().custom_code_classes_in_docs) - { + for code_block in markdown::rust_code_blocks(dox, &extra) { check_rust_syntax(cx, item, dox, code_block); } } @@ -53,12 +51,12 @@ fn check_rust_syntax( let span = DUMMY_SP.apply_mark(expn_id.to_expn_id(), Transparency::Transparent); let is_empty = rustc_driver::catch_fatal_errors(|| { - parse_stream_from_source_str( + unwrap_or_emit_fatal(source_str_to_stream( + &psess, FileName::Custom(String::from("doctest")), source, - &psess, Some(span), - ) + )) .is_empty() }) .unwrap_or(false); @@ -99,7 +97,9 @@ fn check_rust_syntax( // All points of divergence have been handled earlier so this can be // done the same way whether the span is precise or not. let hir_id = cx.tcx.local_def_id_to_hir_id(local_id); - cx.tcx.node_span_lint(crate::lint::INVALID_RUST_CODEBLOCKS, hir_id, sp, msg, |lint| { + cx.tcx.node_span_lint(crate::lint::INVALID_RUST_CODEBLOCKS, hir_id, sp, |lint| { + lint.primary_message(msg); + let explanation = if is_ignore { "`ignore` code blocks require valid Rust code for syntax highlighting; \ mark blocks that do not contain Rust code as text" diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs index da3770aa927a..25b0c61b826f 100644 --- a/src/librustdoc/passes/lint/html_tags.rs +++ b/src/librustdoc/passes/lint/html_tags.rs @@ -25,8 +25,11 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) { Some(sp) => sp, None => item.attr_span(tcx), }; - tcx.node_span_lint(crate::lint::INVALID_HTML_TAGS, hir_id, sp, msg, |lint| { + tcx.node_span_lint(crate::lint::INVALID_HTML_TAGS, hir_id, sp, |lint| { use rustc_lint_defs::Applicability; + + lint.primary_message(msg); + // If a tag looks like ``, it might actually be a generic. // We don't try to detect stuff `` because that's not valid HTML, // and we don't try to detect stuff `` because that's not valid Rust. diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs index 098860245951..7ab974046b9c 100644 --- a/src/librustdoc/passes/lint/redundant_explicit_links.rs +++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs @@ -188,8 +188,9 @@ fn check_inline_or_reference_unknown_redundancy( &item.attrs.doc_strings, )?; - cx.tcx.node_span_lint(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, "redundant explicit link target", |lint| { - lint.span_label(explicit_span, "explicit target is redundant") + cx.tcx.node_span_lint(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, |lint| { + lint.primary_message("redundant explicit link target") + .span_label(explicit_span, "explicit target is redundant") .span_label(display_span, "because label contains path that resolves to same destination") .note("when a link's destination is not specified,\nthe label is used to resolve intra-doc links") .span_suggestion_with_style(link_span, "remove explicit link target", format!("[{}]", link_data.display_link), Applicability::MaybeIncorrect, SuggestionStyle::ShowAlways); @@ -238,8 +239,9 @@ fn check_reference_redundancy( &item.attrs.doc_strings, )?; - cx.tcx.node_span_lint(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, "redundant explicit link target", |lint| { - lint.span_label(explicit_span, "explicit target is redundant") + cx.tcx.node_span_lint(crate::lint::REDUNDANT_EXPLICIT_LINKS, hir_id, explicit_span, |lint| { + lint.primary_message("redundant explicit link target") + .span_label(explicit_span, "explicit target is redundant") .span_label(display_span, "because label contains path that resolves to same destination") .span_note(def_span, "referenced explicit link target defined here") .note("when a link's destination is not specified,\nthe label is used to resolve intra-doc links") diff --git a/src/librustdoc/passes/lint/unescaped_backticks.rs b/src/librustdoc/passes/lint/unescaped_backticks.rs index 4ea926cb79aa..be9670077d17 100644 --- a/src/librustdoc/passes/lint/unescaped_backticks.rs +++ b/src/librustdoc/passes/lint/unescaped_backticks.rs @@ -56,17 +56,28 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) { ) .unwrap_or_else(|| item.attr_span(tcx)); - tcx.node_span_lint(crate::lint::UNESCAPED_BACKTICKS, hir_id, span, "unescaped backtick", |lint| { + tcx.node_span_lint(crate::lint::UNESCAPED_BACKTICKS, hir_id, span, |lint| { + lint.primary_message("unescaped backtick"); + let mut help_emitted = false; match element.prev_code_guess { PrevCodeGuess::None => {} PrevCodeGuess::Start { guess, .. } => { // "foo` `bar`" -> "`foo` `bar`" - if let Some(suggest_index) = clamp_start(guess, &element.suggestible_ranges) + if let Some(suggest_index) = + clamp_start(guess, &element.suggestible_ranges) && can_suggest_backtick(&dox, suggest_index) { - suggest_insertion(cx, item, &dox, lint, suggest_index, '`', "the opening backtick of a previous inline code may be missing"); + suggest_insertion( + cx, + item, + &dox, + lint, + suggest_index, + '`', + "the opening backtick of a previous inline code may be missing", + ); help_emitted = true; } } @@ -76,7 +87,15 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) { // an inline code node and we intentionally "break" the inline code here. let suggest_index = guess; if can_suggest_backtick(&dox, suggest_index) { - suggest_insertion(cx, item, &dox, lint, suggest_index, '`', "a previous inline code might be longer than expected"); + suggest_insertion( + cx, + item, + &dox, + lint, + suggest_index, + '`', + "a previous inline code might be longer than expected", + ); help_emitted = true; } } @@ -84,11 +103,21 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) { if !element.prev_code_guess.is_confident() { // "`foo` bar`" -> "`foo` `bar`" - if let Some(guess) = guess_start_of_code(&dox, element.element_range.start..backtick_index) - && let Some(suggest_index) = clamp_start(guess, &element.suggestible_ranges) + if let Some(guess) = + guess_start_of_code(&dox, element.element_range.start..backtick_index) + && let Some(suggest_index) = + clamp_start(guess, &element.suggestible_ranges) && can_suggest_backtick(&dox, suggest_index) { - suggest_insertion(cx, item, &dox, lint, suggest_index, '`', "the opening backtick of an inline code may be missing"); + suggest_insertion( + cx, + item, + &dox, + lint, + suggest_index, + '`', + "the opening backtick of an inline code may be missing", + ); help_emitted = true; } @@ -96,21 +125,41 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) { // Don't suggest closing backtick after single trailing char, // if we already suggested opening backtick. For example: // "foo`." -> "`foo`." or "foo`s" -> "`foo`s". - if let Some(guess) = guess_end_of_code(&dox, backtick_index + 1..element.element_range.end) - && let Some(suggest_index) = clamp_end(guess, &element.suggestible_ranges) + if let Some(guess) = + guess_end_of_code(&dox, backtick_index + 1..element.element_range.end) + && let Some(suggest_index) = + clamp_end(guess, &element.suggestible_ranges) && can_suggest_backtick(&dox, suggest_index) && (!help_emitted || suggest_index - backtick_index > 2) { - suggest_insertion(cx, item, &dox, lint, suggest_index, '`', "the closing backtick of an inline code may be missing"); + suggest_insertion( + cx, + item, + &dox, + lint, + suggest_index, + '`', + "the closing backtick of an inline code may be missing", + ); help_emitted = true; } } if !help_emitted { - lint.help("the opening or closing backtick of an inline code may be missing"); + lint.help( + "the opening or closing backtick of an inline code may be missing", + ); } - suggest_insertion(cx, item, &dox, lint, backtick_index, '\\', "if you meant to use a literal backtick, escape it"); + suggest_insertion( + cx, + item, + &dox, + lint, + backtick_index, + '\\', + "if you meant to use a literal backtick, escape it", + ); }); } Event::Code(_) => { diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 3a71dd82db88..b1dc766049d5 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -38,9 +38,6 @@ pub(crate) use self::calculate_doc_coverage::CALCULATE_DOC_COVERAGE; mod lint; pub(crate) use self::lint::RUN_LINTS; -mod check_custom_code_classes; -pub(crate) use self::check_custom_code_classes::CHECK_CUSTOM_CODE_CLASSES; - /// A single pass over the cleaned documentation. /// /// Runs in the compiler context, so it has access to types and traits and the like. @@ -72,7 +69,6 @@ pub(crate) enum Condition { /// The full list of passes. pub(crate) const PASSES: &[Pass] = &[ - CHECK_CUSTOM_CODE_CLASSES, CHECK_DOC_TEST_VISIBILITY, STRIP_ALIASED_NON_LOCAL, STRIP_HIDDEN, @@ -87,7 +83,6 @@ pub(crate) const PASSES: &[Pass] = &[ /// The list of passes run by default. pub(crate) const DEFAULT_PASSES: &[ConditionalPass] = &[ - ConditionalPass::always(CHECK_CUSTOM_CODE_CLASSES), ConditionalPass::always(COLLECT_TRAIT_IMPLS), ConditionalPass::always(CHECK_DOC_TEST_VISIBILITY), ConditionalPass::always(STRIP_ALIASED_NON_LOCAL), diff --git a/src/librustdoc/passes/strip_aliased_non_local.rs b/src/librustdoc/passes/strip_aliased_non_local.rs index 848cbd5ed99f..ac7d422ec800 100644 --- a/src/librustdoc/passes/strip_aliased_non_local.rs +++ b/src/librustdoc/passes/strip_aliased_non_local.rs @@ -46,8 +46,13 @@ impl<'tcx> DocFolder for NonLocalStripper<'tcx> { // the field and not the one given by the user for the currrent crate. // // FIXME(#125009): Not-local should probably consider same Cargo workspace - if !i.def_id().map_or(true, |did| did.is_local()) { - if i.visibility(self.tcx) != Some(Visibility::Public) || i.is_doc_hidden() { + if let Some(def_id) = i.def_id() + && !def_id.is_local() + { + if i.is_doc_hidden() + // Default to *not* stripping items with inherited visibility. + || i.visibility(self.tcx).map_or(false, |viz| viz != Visibility::Public) + { return Some(strip_item(i)); } } diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index 9c9b386edda7..5a595e039535 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -7,6 +7,7 @@ use crate::formats::renderer::FormatRenderer; use crate::html::render::Context; use rustc_data_structures::fx::FxHashMap; +use rustc_errors::DiagCtxtHandle; use rustc_hir::{ self as hir, intravisit::{self, Visitor}, @@ -38,7 +39,7 @@ pub(crate) struct ScrapeExamplesOptions { } impl ScrapeExamplesOptions { - pub(crate) fn new(matches: &getopts::Matches, dcx: &rustc_errors::DiagCtxt) -> Option { + pub(crate) fn new(matches: &getopts::Matches, dcx: DiagCtxtHandle<'_>) -> Option { let output_path = matches.opt_str("scrape-examples-output-path"); let target_crates = matches.opt_strs("scrape-examples-target-crate"); let scrape_tests = matches.opt_present("scrape-tests"); @@ -336,7 +337,7 @@ pub(crate) fn run( // options. pub(crate) fn load_call_locations( with_examples: Vec, - dcx: &rustc_errors::DiagCtxt, + dcx: DiagCtxtHandle<'_>, ) -> AllCallLocations { let mut all_calls: AllCallLocations = FxHashMap::default(); for path in with_examples { @@ -344,7 +345,9 @@ pub(crate) fn load_call_locations( Ok(bytes) => bytes, Err(e) => dcx.fatal(format!("failed to load examples: {e}")), }; - let mut decoder = MemDecoder::new(&bytes, 0); + let Ok(mut decoder) = MemDecoder::new(&bytes, 0) else { + dcx.fatal(format!("Corrupt metadata encountered in {path}")) + }; let calls = AllCallLocations::decode(&mut decoder); for (function, fn_calls) in calls.into_iter() { diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs index 31d32e23f8eb..2fa54a9cd812 100644 --- a/src/librustdoc/theme.rs +++ b/src/librustdoc/theme.rs @@ -5,7 +5,7 @@ use std::iter::Peekable; use std::path::Path; use std::str::Chars; -use rustc_errors::DiagCtxt; +use rustc_errors::DiagCtxtHandle; #[cfg(test)] mod tests; @@ -236,7 +236,7 @@ pub(crate) fn get_differences( pub(crate) fn test_theme_against>( f: &P, origin: &FxHashMap, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, ) -> (bool, Vec) { let against = match fs::read_to_string(f) .map_err(|e| e.to_string()) diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs index 01e6cb4b93bf..0660037e4d83 100644 --- a/src/librustdoc/visit.rs +++ b/src/librustdoc/visit.rs @@ -28,7 +28,7 @@ pub(crate) trait DocVisitor: Sized { | TypeAliasItem(_) | OpaqueTyItem(_) | StaticItem(_) - | ConstantItem(_) + | ConstantItem(_, _, _) | TraitAliasItem(_) | TyMethodItem(_) | MethodItem(_, _) diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 907ea6d309c9..886df82e5b69 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -614,7 +614,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RustdocVisitor<'a, 'tcx> { // Unneeded. } - fn visit_body(&mut self, b: &'tcx hir::Body<'tcx>) { + fn visit_body(&mut self, b: &hir::Body<'tcx>) { let prev = mem::replace(&mut self.inside_body, true); walk_body(self, b); self.inside_body = prev; diff --git a/src/llvm-project b/src/llvm-project index 5399a24c66cb..5a5152f65395 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 5399a24c66cb6164cf32280e7d300488c90d5765 +Subproject commit 5a5152f653959d14d68613a3a8a033fb65eec021 diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 1c5a6dcfb1f2..68030493e9cf 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use std::path::PathBuf; /// rustdoc format-version. -pub const FORMAT_VERSION: u32 = 29; +pub const FORMAT_VERSION: u32 = 30; /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information /// about the language items in the local crate, as well as info about external items to allow @@ -167,8 +167,6 @@ pub enum GenericArg { #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Constant { - #[serde(rename = "type")] - pub type_: Type, pub expr: String, pub value: Option, pub is_literal: bool, @@ -256,7 +254,12 @@ pub enum ItemEnum { TypeAlias(TypeAlias), OpaqueTy(OpaqueTy), - Constant(Constant), + Constant { + #[serde(rename = "type")] + type_: Type, + #[serde(rename = "const")] + const_: Constant, + }, Static(Static), diff --git a/src/stage0 b/src/stage0 index 5ec8f5b715e0..f36010fa4b45 100644 --- a/src/stage0 +++ b/src/stage0 @@ -14,434 +14,434 @@ nightly_branch=master # All changes below this comment will be overridden the next time the # tool is executed. -compiler_date=2024-04-29 +compiler_date=2024-06-11 compiler_version=beta -rustfmt_date=2024-04-29 +rustfmt_date=2024-06-11 rustfmt_version=nightly -dist/2024-04-29/cargo-beta-aarch64-apple-darwin.tar.gz=5a8c5e48a88e7c7b41eb720d60fbd2e879b97639c7ff83710526e8e6caaf8afb -dist/2024-04-29/cargo-beta-aarch64-apple-darwin.tar.xz=0d237535ae8d435d99104fa5b9dbf41878e2304bb0f2eb574bf17dd685caadc2 -dist/2024-04-29/cargo-beta-aarch64-pc-windows-msvc.tar.gz=c56733bb6198af0a9b0df9a44ef979150e00de33b70853c239cccfcce23c328f -dist/2024-04-29/cargo-beta-aarch64-pc-windows-msvc.tar.xz=7da5f887151215ddec640684077d98551fe2aed75a3ece2c73b20698754a70bb -dist/2024-04-29/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=73851e304a539d41bedc0d8a5d98800c8279ae623d3e58e863f8c1f8b458b01c -dist/2024-04-29/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=db9c28841344b0154756e19a21795ef6e0c4e27c7844be9996824f1039edaa81 -dist/2024-04-29/cargo-beta-aarch64-unknown-linux-musl.tar.gz=a706c8c7e37b9e80d7faa000c5d179a772746eef071387fb2879fdeab1f1f891 -dist/2024-04-29/cargo-beta-aarch64-unknown-linux-musl.tar.xz=2060634afe1b4a19bae874c6ce3cf4256e613af26e06104b45da5bd71cfb133c -dist/2024-04-29/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=7af61e74faea669fdd41793e4b88eb6a37bfacf845af364ee02bb7cf08c612c7 -dist/2024-04-29/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=4759fb3e3d89ead605c4eeba23be5cb9b3ac98086a9de20f8dbfdfa9282ee486 -dist/2024-04-29/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=4cab18df2d94702e8b551357373bcae60d1023e644148f0f82e8971023362121 -dist/2024-04-29/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=7de4f0d72b4e5770376ede82b02d6bcfd450788a40375fad34d75524c941d72c -dist/2024-04-29/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=6401391a426cf33d6c58f07e7b2828b178720cb4f2b8577ae932b5f5b7d6744e -dist/2024-04-29/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=c3f6729bc769325046f0f62c51b5bed30068c37dc2a36a6283e50565d8cb7d5c -dist/2024-04-29/cargo-beta-i686-pc-windows-gnu.tar.gz=d116c97c1242220c7972b63010aee1ed36bf5353e84a06d3561cd5fe9d7dae84 -dist/2024-04-29/cargo-beta-i686-pc-windows-gnu.tar.xz=65eba577f7775b3eef36e7f000b5007264392b20a7759f8ed567f3a45b2877db -dist/2024-04-29/cargo-beta-i686-pc-windows-msvc.tar.gz=d418a3371b3631328bde2b1e0c3159700f12424e83b1d8ece1349fea90f9e403 -dist/2024-04-29/cargo-beta-i686-pc-windows-msvc.tar.xz=23ae73c776fdb0795944656d743444e3b4c440f45084028206c1aec52333b1ba -dist/2024-04-29/cargo-beta-i686-unknown-linux-gnu.tar.gz=b6bbdeb7c8bfac2e8a083adb4782caf5321799f47acb4eaf81da32bd11730e9c -dist/2024-04-29/cargo-beta-i686-unknown-linux-gnu.tar.xz=6b409691da6ddb8c04409667b2c3d9d6429c6b5bf53ad18177248406a5f06cb9 -dist/2024-04-29/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=24cd888d14a788e8fb5b886735f3c07a028a8681df48a777b2bb971c62a175ae -dist/2024-04-29/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=e8eece6412936fe4dc863a5e19e6766fbb20e81da0069ad7831465e638db23da -dist/2024-04-29/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=8f007a2aa02e35c5ddb2152cc7589092a0e3083211c6aa23e676e3a3ad5a4b8d -dist/2024-04-29/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=3e423e693dd0813f5d87d9eded94894076258ece56684f3598321cd013aeef3c -dist/2024-04-29/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=2eec5e45e389a52b526a5cf683d56a9df92004f6095936b16cd8d7d63722cc6c -dist/2024-04-29/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=64c5135cbff9d4fa9575074c55e79d85f72cb1783498a72e1f77865b9b2d1ba3 -dist/2024-04-29/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=d64552a80ca386728e42f00d7f1c700b5e30e5a6939f32ffa15a7ce715d4c8e9 -dist/2024-04-29/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=fe91adce8ba35bf06251448b5214ed112556dc8814de92e66bc5dc51193c442f -dist/2024-04-29/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=77aafa8b63a4bf4475e82cd777646be5254e1f62d44b2a8fbd40066fdd7020d3 -dist/2024-04-29/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=c38f0b4adcc8e48f70b475636bbd5851406bba296d66df12e1ba54888a4bf21a -dist/2024-04-29/cargo-beta-s390x-unknown-linux-gnu.tar.gz=c05df24d7e8dff26c01055ad40f9e81e6fcb3ae634ecc1f7cc43c3108677fa0e -dist/2024-04-29/cargo-beta-s390x-unknown-linux-gnu.tar.xz=47e8f4ec4d996600e60ddc49daeeb43d4c21e0583a86c12395c16eddc7db76ee -dist/2024-04-29/cargo-beta-x86_64-apple-darwin.tar.gz=f024bd225b77160dc2fabde78002c8deac5cbb9a35345340964c3b988b0d1791 -dist/2024-04-29/cargo-beta-x86_64-apple-darwin.tar.xz=96c9e44bd9f0c85c793e3dd6043cc4f89fbeeab5ddf0fdb5daefca8f690bce05 -dist/2024-04-29/cargo-beta-x86_64-pc-windows-gnu.tar.gz=517889f222b62150fe16bcfd3a0eb5f353956b0084d85713480197bff4558145 -dist/2024-04-29/cargo-beta-x86_64-pc-windows-gnu.tar.xz=a6653ea4aec51569c1300c044d8bf2517a1f5111f710d12cd352190425b8f317 -dist/2024-04-29/cargo-beta-x86_64-pc-windows-msvc.tar.gz=4cb5b5054dffe6721efbbf29192a67e59cda69ea4ab4791aaec6f314eefa5a5e -dist/2024-04-29/cargo-beta-x86_64-pc-windows-msvc.tar.xz=08bc45be22e9e4f615d1c9e70500046c8db89045f5d40dcde853c610591712a7 -dist/2024-04-29/cargo-beta-x86_64-unknown-freebsd.tar.gz=9661357ee8ea8973016fdbaa2de3cb98713136dcd25f07aa9f9d101180276815 -dist/2024-04-29/cargo-beta-x86_64-unknown-freebsd.tar.xz=7fab806227d1a3be817602abb121ac7e039ba0bbf81e0a1d47bdcccca74203c6 -dist/2024-04-29/cargo-beta-x86_64-unknown-illumos.tar.gz=4c79bb48cfe64bd38af7fe3660cd8bdc99ec90738a0d8fdf80843ecda910dab0 -dist/2024-04-29/cargo-beta-x86_64-unknown-illumos.tar.xz=0fb9edfdafde1820ccb25c22369cafb0e75e68795effeb615cb284a5837c75ba -dist/2024-04-29/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=c1902a072e61ab5ae9737a1092732e3972deee426424bc85fcf8702adffdd41d -dist/2024-04-29/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=d39ea1195dcc95e428bd540abd2db5b5d4c997a7661a41a4c0ca41cbdd18d27e -dist/2024-04-29/cargo-beta-x86_64-unknown-linux-musl.tar.gz=0edfdb6f6bb2a4a1a96a5e95cec897c444c936e6624bb4a530ffed4847b97445 -dist/2024-04-29/cargo-beta-x86_64-unknown-linux-musl.tar.xz=70c264b7845febdee45d0c6e44b65d47ba7f367ef33ec906a9fd7f992ba7cc13 -dist/2024-04-29/cargo-beta-x86_64-unknown-netbsd.tar.gz=f1bd6417a54f3b53d572ce4af799242db7c11265c71201cc09c78d71be38c13a -dist/2024-04-29/cargo-beta-x86_64-unknown-netbsd.tar.xz=53569810469c483785333759f86434706ee5368d5e18270ee13a17817ad57d40 -dist/2024-04-29/clippy-beta-aarch64-apple-darwin.tar.gz=7b693bde61a090854527a145455ff774314c65ec0cd47d25a19c76c6a166d96c -dist/2024-04-29/clippy-beta-aarch64-apple-darwin.tar.xz=2494e9fdd8d342b6bc3e55eecfd555c43e3cca8421f3236df2d5a366288fec62 -dist/2024-04-29/clippy-beta-aarch64-pc-windows-msvc.tar.gz=90307f09c6fcb0c1fbe3ad1522a5381a17e2f69637c6d00f4a2cb5f3149bf736 -dist/2024-04-29/clippy-beta-aarch64-pc-windows-msvc.tar.xz=f7e0dec4a4862bd85d894252366152b3b6a7627e7e5a25ce323fa2db3bd87c2b -dist/2024-04-29/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=7c719e38f2a1030ae61985205df52f9a0c37b659463a5e2dea8e60e632de2d73 -dist/2024-04-29/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=181ff4ae6adced6522a4c29869be3cc5dac8b961c7c88f2957cd31f831490807 -dist/2024-04-29/clippy-beta-aarch64-unknown-linux-musl.tar.gz=4e0e63e6f200386995e369a2673867d1bc3005d51d6a57c00ca80056dd85316b -dist/2024-04-29/clippy-beta-aarch64-unknown-linux-musl.tar.xz=3d5b22a13aed6821482e60d9cc8571e2da9d95d82104284b77c56985a35a9c4e -dist/2024-04-29/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=9f788db76a5d55b3ecdd04a70b0e2be466959f76ae9fd3497ca2c503504e0c03 -dist/2024-04-29/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=f4d8fc103807fba61d71d88b8e25a7016bfbd1a2905330f9a9fb3d7ba082713a -dist/2024-04-29/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=d61bec3d017dd0be43e48350190ad18c0a0269e43d964600b62e1f7fd4f84399 -dist/2024-04-29/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=c00cbdc41a4da0c313a1a282b0158b059dd34f640b582cb7ca18e3d290ca8fa5 -dist/2024-04-29/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=52143a530ca5274fbb760beecddff16f860ea787443d3dc708dda7c8f32ca9bd -dist/2024-04-29/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=c6d2dfeac6f40811bc9b4cec3c23f9c3bb46f761e006257b9313aa7c1a647b5a -dist/2024-04-29/clippy-beta-i686-pc-windows-gnu.tar.gz=325d39e426b1907fa17d93c0752d3d73bd95750f4f967c2a84aab2c5dac8a588 -dist/2024-04-29/clippy-beta-i686-pc-windows-gnu.tar.xz=536f591d4da455302029384ed196932d71119ef0160ac5415617d6b777c51123 -dist/2024-04-29/clippy-beta-i686-pc-windows-msvc.tar.gz=c3684c9bf471669d444853bf484880d17e150ecb0e7505de90883382023e343b -dist/2024-04-29/clippy-beta-i686-pc-windows-msvc.tar.xz=0b00e6132f73d5dc762e359b0005fceab0cf7b01337d8f4aa9eacfb4552f9245 -dist/2024-04-29/clippy-beta-i686-unknown-linux-gnu.tar.gz=c91c1eadfc4cbae360a0eecf11c32d2509b68aca86c7b1de3b102944f43e1511 -dist/2024-04-29/clippy-beta-i686-unknown-linux-gnu.tar.xz=6f7a5a287dd6226c203bb674ff02ec773e5d0813091b2af744b88ecd6997a304 -dist/2024-04-29/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=58383f094995823ea6db6a87b9ad4b33bdae2264d29bab88ab71ec60ccab3b93 -dist/2024-04-29/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=dbf4680a6fd4dca54acca5503a7fd94502b8e85819bc02346ae9cecd275e4514 -dist/2024-04-29/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=e28eb32cda42654c0f0247aa8e15f01f73770b36f7626c8d6f1b7659accc56e6 -dist/2024-04-29/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=fcc48a83b748e1e46f9daef40563f8e5abbb0e3f014a168b04f3c700c2ace2b8 -dist/2024-04-29/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=b626faf3275fcd196cd627e8a36c67721bae16a56f61cd080c79d137b3ec7737 -dist/2024-04-29/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=2c599d2dc719d69f67625f3c6573fcc4f1ea3266801557dd3892bdb7c761b4cf -dist/2024-04-29/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=0bc1f546fe0cef2b9516231ab608de68d55f72022fbc9ced5101b600e005f8c4 -dist/2024-04-29/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=993294f2ae5202785ab242c1c6567df9c8ab1ef44ad35748c526b7fe854fb94e -dist/2024-04-29/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=210a4f0d208e0c8e13a57fb3b3e6c98ab5f620e4988d10a127ff1424ac1d5ca9 -dist/2024-04-29/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=f10f7df41a13ee2ecdc25d60e697cba2342129a912ef20d8bfca5f611a9ec97f -dist/2024-04-29/clippy-beta-s390x-unknown-linux-gnu.tar.gz=3e24d2af65f0c9667c9997ce091711b2be48e673de3707cddfd8cda455dfecc7 -dist/2024-04-29/clippy-beta-s390x-unknown-linux-gnu.tar.xz=0e7b8fbd0207489e38c78c2ae1aa0df4fcbdd84741aa50a86379e4d7ede286b1 -dist/2024-04-29/clippy-beta-x86_64-apple-darwin.tar.gz=9c0c47fd97ce72abcd6126315834c62aa7297fe09d447ee4cefa1eb46a116326 -dist/2024-04-29/clippy-beta-x86_64-apple-darwin.tar.xz=49dd65c5340fd804399edfa2402cf255fd9bfce1f4aa7fbb3c193c11bc03f8af -dist/2024-04-29/clippy-beta-x86_64-pc-windows-gnu.tar.gz=6c1c3bdf097a1846ae08b098c555c0c5e9e9b646c744d6bb5a855789196b8bf6 -dist/2024-04-29/clippy-beta-x86_64-pc-windows-gnu.tar.xz=0a7319d1062f73af1c8f0efe6ad970d10d02259162c5bc84bb1f3a10f3911bcb -dist/2024-04-29/clippy-beta-x86_64-pc-windows-msvc.tar.gz=52fef3f8a64fa58934a633bd4944e8ba9e15f2c2766d0f302dea1a6523864dab -dist/2024-04-29/clippy-beta-x86_64-pc-windows-msvc.tar.xz=8fdbe7590e62ab68a2e463b14da2595e8c4592744f578a813f64d430ed7db4b6 -dist/2024-04-29/clippy-beta-x86_64-unknown-freebsd.tar.gz=509bf535622bd26385184ee0c17e4e27a5061a8aeebf5759f24bd578692b2f5d -dist/2024-04-29/clippy-beta-x86_64-unknown-freebsd.tar.xz=2fcd10ada329ba7633616bebc584dca13f11c465e7cf513e76efeb0c3174486f -dist/2024-04-29/clippy-beta-x86_64-unknown-illumos.tar.gz=ea8cea0d4a2379bcd0693f6174b25bc1f90a016dbe8280159cbb61d859806fb0 -dist/2024-04-29/clippy-beta-x86_64-unknown-illumos.tar.xz=5a243df8d1345db6bd18e4386ba628e6d302bce1cc572fb447cca4264fda3ee9 -dist/2024-04-29/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=2ee560d3c1e306e103eb06d8e8033cd1489b3f6ff9df3bd8a95e25e977befa27 -dist/2024-04-29/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=aaf6e54184a65ad6592bf03955a84ad12b561afd86064b1ac5fa03cf637052f8 -dist/2024-04-29/clippy-beta-x86_64-unknown-linux-musl.tar.gz=1b3877424a0a0eb507675a50e9d2c793f00ab85f6f12b1e27f871331070325b8 -dist/2024-04-29/clippy-beta-x86_64-unknown-linux-musl.tar.xz=6df5eaae5afb64557ba5c3a53ee3e56dab85455838a6044c7671c1180acfeaf9 -dist/2024-04-29/clippy-beta-x86_64-unknown-netbsd.tar.gz=1ac05ed7b607fff8b77ff203a663e9f4f2487779bc25e2dcd454cdf5b7583328 -dist/2024-04-29/clippy-beta-x86_64-unknown-netbsd.tar.xz=da502375b3cee8b254ab5999809f522692c2d1d90ea0544ad03c0ca514c65ef4 -dist/2024-04-29/rust-std-beta-aarch64-apple-darwin.tar.gz=2fdd35ca3b3e3d6f548f11c93337f5bf2e3c088bc78a79881e6f8e230b38b9a5 -dist/2024-04-29/rust-std-beta-aarch64-apple-darwin.tar.xz=bc16b3a1ab6ed69f0121a117c50cbcd201500dae4d72ad0dab148913d04cc529 -dist/2024-04-29/rust-std-beta-aarch64-apple-ios-sim.tar.gz=9375c786703c17baae1c2066f8d972ac316bc840e478ecd1b94288a1d428324e -dist/2024-04-29/rust-std-beta-aarch64-apple-ios-sim.tar.xz=50d6818a8dd3ab7a3ddbbd7a062b538d9ff3ceb6eada031d1c22ab1dc7ba512c -dist/2024-04-29/rust-std-beta-aarch64-apple-ios.tar.gz=56c3a01e8fd5c2ed75df811993b0b724709fb5473cc308ac09e7f5644468f751 -dist/2024-04-29/rust-std-beta-aarch64-apple-ios.tar.xz=3527d1f2c99c806479fb4b3801335dc921b514f171b82cd252cbbfc9ed30b163 -dist/2024-04-29/rust-std-beta-aarch64-linux-android.tar.gz=bf8cae7c66489f1aa27f1dea1b37f0d0ae514a6e21b93ff2dc6400dc88feca03 -dist/2024-04-29/rust-std-beta-aarch64-linux-android.tar.xz=46799f0bc1b3c13877f6cb732774cb3b33e0d8a081bfb56d0f877e79482aa1de -dist/2024-04-29/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=9f90fadab5104e1d415edf3b4edfaf7222f9f0d55f849851efdec74ffee16f8d -dist/2024-04-29/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=87ed6774202b18691bd6884df6944c7e9fe9c944b57a2837e7a7647518bf94e8 -dist/2024-04-29/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=4a0692ad28f7f130b472ffa4aa766b745ba01fb75aa921f2da6622c9c68750df -dist/2024-04-29/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=a3d45962489a1e18a87e567cbbc8d3665f38809d0ad2ef15bcf0ff9fb9f470a4 -dist/2024-04-29/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=c724f4eb135f73b9c79618f27a1ab35dc7b9d26ca62ed796accce68f9e747a66 -dist/2024-04-29/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=8eab25782d16bcee75f86ecbb826346beb4a7525b220b45b3ba05a567c6d4391 -dist/2024-04-29/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=33ab1f8410edf590570d7468dbe2ebb5a0907125bbc8d360a928dcb355f0d0e6 -dist/2024-04-29/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=d3d870209a55ac96391affaa347c04f48cf98c089ac5056f340b8bb38bcc8e60 -dist/2024-04-29/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=4d2bb72b898c30a2fc8d5d3333c2e99a8e30c15891fab641b6a519dc9f0cb611 -dist/2024-04-29/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=fa343e6b6110fcd0c5dae4287ff1a799de5d7e4a805dbf4e9a034bbaed2bf269 -dist/2024-04-29/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=f1ec4139783169fd83e1b0184518ed25d26cee7b21f196deecc74e83a1d78725 -dist/2024-04-29/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=d100be2f6f0346c4b1f5b41aec0c13a47426bf4d49127f2341c8332903e4e782 -dist/2024-04-29/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=bab6051e1071a58cd126580f6644decf16edb4473fe4be6a34791610d820a294 -dist/2024-04-29/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=d9b68f06ff23629063e92dfc42aa3115a858238d368e4b52b35c1ea4491b1402 -dist/2024-04-29/rust-std-beta-aarch64-unknown-none.tar.gz=96804c2d9accd3242bdc22dad688b2ccee071952477b9c592f099377aee6c591 -dist/2024-04-29/rust-std-beta-aarch64-unknown-none.tar.xz=3fed6812d84bdaf787e85c37e23ba729b81a6d25a2b33fed75320e66e6641c89 -dist/2024-04-29/rust-std-beta-aarch64-unknown-uefi.tar.gz=8da5f301bff35fc067ec7cfb878ebfa5607af7dbc276a6b34a77404432c700d2 -dist/2024-04-29/rust-std-beta-aarch64-unknown-uefi.tar.xz=80d643189dc9af98b6410a01261ce6ad34b1325f3aebf3ff61fb43f1261b41ff -dist/2024-04-29/rust-std-beta-arm-linux-androideabi.tar.gz=2e86b54b0d1f7fefead11d6383bdc80fe0a7b3ccf58381d2a731e6f1c62926de -dist/2024-04-29/rust-std-beta-arm-linux-androideabi.tar.xz=9831a0270457cad2798b5ae4fe956c257c7e10ce5ad211793dc467577cdec29e -dist/2024-04-29/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=f96bc303c0c2be9cf589f00aa63b2cf3fb8585ca9dd8860fe525821bfa1fe19a -dist/2024-04-29/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=e57a053b1c2bb6fad93dfaffedce7f48fa292196fc8ba6fd2f0c74dc810a13a9 -dist/2024-04-29/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=49b2cb2ba5296871b5fac5ad9a74a2891e8b78321078a455ba4a65e003bebd40 -dist/2024-04-29/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=0f9c15d834a9d282a4018934759f7b48ef3d275e09679a68c5fd1b3f047d02e4 -dist/2024-04-29/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=e59f92827241e670c1aa92b35235ad12340869d59327fb83084b5f4149acbe06 -dist/2024-04-29/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=ad1cf96bb1fcceaa016e29e8ad34b4cfd711d2e0bd7cabb9cd7cc28abf64d894 -dist/2024-04-29/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=14a6d318af85bb9fa5c229e45a88a32a71f44ed02cd90a24bb67921eb64dee41 -dist/2024-04-29/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=ed6b48502ab9169818bceb300b4e6b4fd63366ad5808b047bf9988dae04c2729 -dist/2024-04-29/rust-std-beta-armebv7r-none-eabi.tar.gz=345e8a023be55e3b88a0c2677ea28c7bb4fcc5f3ab707638de76065c7592c2d5 -dist/2024-04-29/rust-std-beta-armebv7r-none-eabi.tar.xz=6d9b11d08f2d62611327a893b45ba07c36df11f077250496ab0881eb7ac84c65 -dist/2024-04-29/rust-std-beta-armebv7r-none-eabihf.tar.gz=a2ae1bf003a8a12b2ecb6bde9868a978820f184af523f0e4c3fc935edd509423 -dist/2024-04-29/rust-std-beta-armebv7r-none-eabihf.tar.xz=3d1dcf8308f9d4590b429f6abbf8f42f04496ab490ccf4ed8c9e381e6d886cae -dist/2024-04-29/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=a54106d27e4ce97463e7867ceff9dd22ba456f840ec23229e6909b37d48ad554 -dist/2024-04-29/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=e6abfaa0905a00efeaee85b9f93793bab93e2cf4e172c9d829c5ba85006c688a -dist/2024-04-29/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=cbed18e5dc61fcecb2920affc3890c3b8ae46b7fe5a80b3288689e18d490f3f4 -dist/2024-04-29/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=2b58bb0dd5cd2c5f7f93f4c6e9135090b931e0ffa27ff9efe2b8ff9fbbb7e48c -dist/2024-04-29/rust-std-beta-armv7-linux-androideabi.tar.gz=6a371c2ececd349dfa76a02563069912fc91577ac4446d36c22f96723d7f5e9f -dist/2024-04-29/rust-std-beta-armv7-linux-androideabi.tar.xz=9325daf41ddab02fa845971c10a5e0538a18c7bea14e66fa0f5f6fb16654c7ae -dist/2024-04-29/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=7f5ba76cfb7c85333c8dab76fb4ad3f12ddc254b95f9ee07fadb8e1270a4f767 -dist/2024-04-29/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=f853b7f929b7a309ed6c08ff8c57d583ce0ccb19270674fb30e63a873834dc87 -dist/2024-04-29/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=0680005d0a12498b687afc583d4f36bd67d0877cd9d3323bfd2df50d15c27afe -dist/2024-04-29/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=a494b78fcad01c83df9522d460ac2d35d2d00736a921381f2c611dc516edaa16 -dist/2024-04-29/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=cfa555db31b5470e878b0f53d86617e7342e8bf018fe62cb0271dfe13db95f51 -dist/2024-04-29/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=0a8ccd6d88cbe79a855111fbda45aa1a728de655b6927f3d429d901d2afc6503 -dist/2024-04-29/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=eac53424001c884a540c42f0b68447349ec5d0601a030c060aaed76d54895728 -dist/2024-04-29/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=42d78fca62361ff28db5bc43bb01cef7af5c6f4ab2110fe6170c3dce4707aab8 -dist/2024-04-29/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=c88de9f2e667da73177fb9c9309d7f1f467e31c18e3ae50d722c71ec8dd876a4 -dist/2024-04-29/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=24b3c04a42d511cdc8c6107b597be38981114f0574eced493d0e90fc748094bc -dist/2024-04-29/rust-std-beta-armv7a-none-eabi.tar.gz=cd4ad182a098c61550265879ccc04733c39110827f7ef62eecfb8c120ae4ece8 -dist/2024-04-29/rust-std-beta-armv7a-none-eabi.tar.xz=8499a014dfdf448f474a58f148784c2eef245dc909587d876d2fb9ddc6a4ec3f -dist/2024-04-29/rust-std-beta-armv7r-none-eabi.tar.gz=e8e1870e5b12b3d8643d712efb91eb86b2081284cada4a140c1526692ab183c4 -dist/2024-04-29/rust-std-beta-armv7r-none-eabi.tar.xz=d6029121eacc44bd4dcd9ef6dd3cd0d775cb6e9a3d99f3d62d746fcbf8981cab -dist/2024-04-29/rust-std-beta-armv7r-none-eabihf.tar.gz=1e0fc42c3802e205130c01ca90f92d793c1c5427b34da66fe77b97cf67b4a5c1 -dist/2024-04-29/rust-std-beta-armv7r-none-eabihf.tar.xz=4c8cfdb11bb686111fa4842d13430c86d9d14ada30e9df334b3777fe899233e0 -dist/2024-04-29/rust-std-beta-i586-pc-windows-msvc.tar.gz=ff895c1b39b84587f10903f4be13d275b545e690da6761190d12c01acc25c6d8 -dist/2024-04-29/rust-std-beta-i586-pc-windows-msvc.tar.xz=fdcbcff7b740235bb16e44174fff9080a7c0a31be358c8abc41805c02c20c3b2 -dist/2024-04-29/rust-std-beta-i586-unknown-linux-gnu.tar.gz=6b227f3b9001e148b66b7001f753a6f88fef9677e39d8fcf4d9c35fe8d345568 -dist/2024-04-29/rust-std-beta-i586-unknown-linux-gnu.tar.xz=1e29297beb8de3778ba958731294823d9a93aac1e0d8833abc5aa99e2935965b -dist/2024-04-29/rust-std-beta-i586-unknown-linux-musl.tar.gz=26481ad5f22a319830d42f69b1c0195bd65900ebe112e659432334b3468f3d0e -dist/2024-04-29/rust-std-beta-i586-unknown-linux-musl.tar.xz=c8a837e0d9da8ad976fc1539541c085365aac9dd28b34e4a289d38a823d1b065 -dist/2024-04-29/rust-std-beta-i686-linux-android.tar.gz=f05e28a52f17e22f36ffc70018012a1fe6a07f4b461e774b36464f32bc8f8dea -dist/2024-04-29/rust-std-beta-i686-linux-android.tar.xz=f9501b2691c51e54a6f4cc6fb72e41901eb551d3a7be5f82a94ce2d3e217828b -dist/2024-04-29/rust-std-beta-i686-pc-windows-gnu.tar.gz=8d9a782d4f7450bca536aab45147c6ef08bc3847b43fdd171c6449e29762eda0 -dist/2024-04-29/rust-std-beta-i686-pc-windows-gnu.tar.xz=4008712e03fb6494eaba3d79051c5e3fdd93d4c52ae8d86cf8f344b5f051cbca -dist/2024-04-29/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=cfb23242e495834a3d0f7ffa3da4a0b206dcae35872b1455b11faeee5511ba5f -dist/2024-04-29/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=95415742c0171945ffc2b67c913ebd1330e29634af238f5ccc843a965340374a -dist/2024-04-29/rust-std-beta-i686-pc-windows-msvc.tar.gz=e9354d69e39ecfac1d2928664d17d73f808256a4076b849171a9667705c0aa08 -dist/2024-04-29/rust-std-beta-i686-pc-windows-msvc.tar.xz=a34bb0a91170d84195f35ba52afa4c9be8a2f2706dbeea02bd6e8908e08ac65e -dist/2024-04-29/rust-std-beta-i686-unknown-freebsd.tar.gz=d65f286de399ccc9e9acaf7a4dc4f885357c750231d54a144ba9a59181814f11 -dist/2024-04-29/rust-std-beta-i686-unknown-freebsd.tar.xz=4c93a7da70a69b2ebbac01df64af16344e523d16470b29e57237b1d0925f7721 -dist/2024-04-29/rust-std-beta-i686-unknown-linux-gnu.tar.gz=1b978bfd1a9234be7ef197c8c98c5a6b625f6fbb7b0fca58695986768bdca176 -dist/2024-04-29/rust-std-beta-i686-unknown-linux-gnu.tar.xz=98d4eb5b89a593c8c4f86244c9a7c737d9c18c0168aebe5923b8d9145adcf89a -dist/2024-04-29/rust-std-beta-i686-unknown-linux-musl.tar.gz=dbf9b3c5b54b3eb0727f976f5632c5b0fcb2f90ac7453962d6cef20f7dae4284 -dist/2024-04-29/rust-std-beta-i686-unknown-linux-musl.tar.xz=f209ade093753342dda6e710ee832a538dbdaa08a24d606f9a2a1bc59b83da29 -dist/2024-04-29/rust-std-beta-i686-unknown-uefi.tar.gz=3c3ca7f34569b2c70c6b223754418a535dd7dfa087ab6e28ed2ec78d20065887 -dist/2024-04-29/rust-std-beta-i686-unknown-uefi.tar.xz=72a7cd0f430ab40d80e93f409b8e26a181010ab4bb75d151e829d51ccdcf8c62 -dist/2024-04-29/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=b7dfa59bb05cf806c87854d6fce5ef0f160697052fdf6e5a0cad121499108608 -dist/2024-04-29/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=88bc22f68bab3367cdfa91676418ce1ffc0ec002afb32aed7def880bdd4be402 -dist/2024-04-29/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=d61019048b941064a99d19e46ff3236a88a2e8fcfb963cedd1d9d1c47963c170 -dist/2024-04-29/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=7474bda08134c676d74afe5263317af3f271963d8ceaa5efbfa1b657f885c572 -dist/2024-04-29/rust-std-beta-loongarch64-unknown-none.tar.gz=e089c77d433d838ca02d7531d6f4a1770fb4a0568acbd96c8f43034d76f2990b -dist/2024-04-29/rust-std-beta-loongarch64-unknown-none.tar.xz=364ae6c89c7a930098286e55193d2f5ee3d5ea80b7cca73046e41725f4a8a2f9 -dist/2024-04-29/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=c17bfad87d16f3a8d26646525dc59a352718db9e7572acb583b68a18cfdc338a -dist/2024-04-29/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=f5c4ecef1c08d19ba6fddbd359a0ce94e46888021cae057fce969276026d086c -dist/2024-04-29/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=3e7e13b0d2e804d228e1e3a9dac0205d294ae29dcc37132f15fb1e218861eb39 -dist/2024-04-29/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=ea31b7678e6f64c2f9c28a9af120be04ed6f2a25a496e40afbf6e9aa0dd20b60 -dist/2024-04-29/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=f0e1b314c3d5ad1676c68c112581dce62fa06ad557cd5f61034e147b064ed270 -dist/2024-04-29/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=8ab7bbea6e2f72df1286facc7d306d01809a4dd9f8901dfdec7e50b594658d49 -dist/2024-04-29/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=48f6abda1c7dac185858744aa2cdc3513cdfb6552535282ee83cf9c5365573c7 -dist/2024-04-29/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=d920d97f15b56ba6ea81e08b3c29dc7f44f5f30b7513c53446edf95843c332af -dist/2024-04-29/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=0a198a770f6e6043e923b0ab1a508fd8b190612d0370c33c8dd2c5f63b6f19dd -dist/2024-04-29/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=424a93313cfe2d85acf956be3d9ac71ea8e34ee61617a550ad6ff5360e1dff52 -dist/2024-04-29/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=2e2b0a8e41f4ea774d665d6248cbc2fdbe3e582206efeb87d250786ebaad0b1a -dist/2024-04-29/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=2da372c091017b7096e473e5c7016a504d2e041e14173d2520086cb43e0a615a -dist/2024-04-29/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=69d3b21403181b2df14243816388169db2466477ec34bcca5693fb017703686c -dist/2024-04-29/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=281b9c20f8641a3d1b349e762b7f713fb0b91da0d21eec798e639e36a0ea3dab -dist/2024-04-29/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=dd9bfd3fd8446d35180fe781139dfb4e04dd658b112eb2a749e8f4aea14f0271 -dist/2024-04-29/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=b1366375e0c5f53da581741dec91972b0c46d7d466052539207e8feaab0ba3ec -dist/2024-04-29/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=7c6650d8cf8abd51547010e8211af3ef3195099ef43a563460ad4780de20ba17 -dist/2024-04-29/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=bab46f3c0078ce346de563bb7a248ca92f15dbdc73bf5a3bc520486118442320 -dist/2024-04-29/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=01735b4ad5bc0a53087dd0ccaef2cf174b27e45bf4d2e3c15e64f7522f059c63 -dist/2024-04-29/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=0bb272c2c235583ed3e9ec151b3bfc601f8cd07822c2fe47a1258b358be507f0 -dist/2024-04-29/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=b2c7f8ee0efe6d0812e4b5dd0979f60f105b84d34d4f600ef75f2eacd954893d -dist/2024-04-29/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=0d5301fc553a6911af6643ab7f57b6438bf649ffcd050d486278c0c5fe38eeba -dist/2024-04-29/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=0d1d35ecb88ee717720ad8e74bd5b602fd6011fe321baddb939f3b161e9cd8c5 -dist/2024-04-29/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=a5cf0b98596e68e6f72be2e83c61b8aaa19ead42f248ee2408a3b8f4e97a6657 -dist/2024-04-29/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=629ed749cdae110668ad9ddbc5c61e99e8d400f3dd0981146c3820deadc360f6 -dist/2024-04-29/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=192819438ed27a565cdb67b51d2f5caeb6ae258de86191d6922574327f132acd -dist/2024-04-29/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=84286f6cf6f00f3c92dc881f64a31e1ec5910102d8d3d4faf6fc7e2ddf1544a7 -dist/2024-04-29/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=304b5f876b47dcbb7c3483c49295b822e8ba83234bb568ce67896ae4773ae2fa -dist/2024-04-29/rust-std-beta-sparcv9-sun-solaris.tar.gz=25062159b859e21dda76ca22d4a31d3aba4fcdb0def78bc5b5cf9887c07c1be9 -dist/2024-04-29/rust-std-beta-sparcv9-sun-solaris.tar.xz=5d557ee86457f288462603fe53bcc2e092d84faee543659419fa68c1bd88f554 -dist/2024-04-29/rust-std-beta-thumbv6m-none-eabi.tar.gz=a9663048aad82ef832b2cf82fa9fb94be047f77e283e8aa3e2df6ad957d0782d -dist/2024-04-29/rust-std-beta-thumbv6m-none-eabi.tar.xz=4c4b703a846b4123d09c1eace6322e82784a004b278f1f3b1ca1279e96207f18 -dist/2024-04-29/rust-std-beta-thumbv7em-none-eabi.tar.gz=32907c33f240abb1cb17ac438da42c5fa3932b270ad08fd6914775c5b59a02f5 -dist/2024-04-29/rust-std-beta-thumbv7em-none-eabi.tar.xz=112583227d2b6abfef6eeb78d980bf2efef392f3b66e433c4959a642d72ffc7b -dist/2024-04-29/rust-std-beta-thumbv7em-none-eabihf.tar.gz=7ba0084527a18479c4b6f6a0dba8ae23a0ed50e9fc5fbfce23cae1babb5a1e12 -dist/2024-04-29/rust-std-beta-thumbv7em-none-eabihf.tar.xz=49eb4e2efe3a76713ce1fecacaf915717eeed8552912b92895c7fee068a85a36 -dist/2024-04-29/rust-std-beta-thumbv7m-none-eabi.tar.gz=518a532b52f2dad2825158614cd00b12aac6c6e1983a1ad53e2b0e26d1f1b845 -dist/2024-04-29/rust-std-beta-thumbv7m-none-eabi.tar.xz=2895e5796a29fd016462694d880e38eb191cb92c9bdb14414c1d6e63b23d3394 -dist/2024-04-29/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=2af590c063344c4c3f65d704fa255232b5f5954872d03c4c55d48662cbe6bb17 -dist/2024-04-29/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=a09df5f38183d9fe6116c807619f812410763ddedf06055bfe8040b5794104d3 -dist/2024-04-29/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=eac22c4972bde3a57cf2ec4e31b43db3c4b7d961ae31475d8942e898c07640cc -dist/2024-04-29/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=104f2c6490e30cc47833edbd806c2efe6256d1194600b2278339612f94704d45 -dist/2024-04-29/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=b3c9c9d7ce8c1db6f20e8ede542e67aacd6047c52882a5d06c4f96a40a7304d9 -dist/2024-04-29/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=76bfb114bc7674792934a4892d2db41fdc8f5bd30c3aa96c43e8055199157476 -dist/2024-04-29/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=1308335fe80dcafaba511ee589959d461145533de5f76118fee29a7e9a15841f -dist/2024-04-29/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=cb8acdb8920983c03b9495cf3506a3014384b4d2f6a53e7907924d38a0baf7f0 -dist/2024-04-29/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=9dd2e5ce7534ab4fbb93ff652196e877f4e9eea3863920c3d34a05d9a3598c81 -dist/2024-04-29/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=4b6e962facf7c54846965a8d6880e4a980804459151f2e22ac5af79bc79e26bb -dist/2024-04-29/rust-std-beta-wasm32-unknown-emscripten.tar.gz=731603392b6e3d36b3a4956928d084e293ef18c8b8593efa756e753a2a309709 -dist/2024-04-29/rust-std-beta-wasm32-unknown-emscripten.tar.xz=8b681b3af47855eb63c4ffe06a2bc6bc4f365354ffbc171ce8cbd8c2a3588a07 -dist/2024-04-29/rust-std-beta-wasm32-unknown-unknown.tar.gz=7b87e59391493c3147c03794061111e25bdae669aea58190a951cdef111e75e0 -dist/2024-04-29/rust-std-beta-wasm32-unknown-unknown.tar.xz=d15eaadb101027906c2fce15b95a3f820bdbc4cf145b705bafc2ac5291289c3b -dist/2024-04-29/rust-std-beta-wasm32-wasi.tar.gz=07390ec742b79ec11b2c3ec65f60efe5d7c616f50c33058fce346f6e9ad21af3 -dist/2024-04-29/rust-std-beta-wasm32-wasi.tar.xz=79e34d46621c298cadb98c00ce3b25d97474aec300d85255153b47e21b7bb744 -dist/2024-04-29/rust-std-beta-wasm32-wasip1-threads.tar.gz=b916dc9051b0278f820ea0b093db3ecae2e27de641ef67a9b508df75dc92c938 -dist/2024-04-29/rust-std-beta-wasm32-wasip1-threads.tar.xz=2867922a39da3b02ebdb93fb78b010695daf468f87485ad8ab79c7f3eeb18b11 -dist/2024-04-29/rust-std-beta-wasm32-wasip1.tar.gz=792b718c0a72e97ba85a17ba67ee09e55b85de829fe4021f828ce54ff8cb31e0 -dist/2024-04-29/rust-std-beta-wasm32-wasip1.tar.xz=abff86499119bddfeda9059004549941dbcd3d911702d4a9c198b94f60e60f4e -dist/2024-04-29/rust-std-beta-x86_64-apple-darwin.tar.gz=0bcc7698efafb42a37f20815f5660e39829a42a2776304e7129d0a4ec0c7520b -dist/2024-04-29/rust-std-beta-x86_64-apple-darwin.tar.xz=c437626e250b0d06c05dc828ab81d0d2c543ffce4b100567910508974ea50045 -dist/2024-04-29/rust-std-beta-x86_64-apple-ios.tar.gz=7c98c9f491bfc837111769a45c10ce2f1ef73c22377158ef9ae80b38034892c0 -dist/2024-04-29/rust-std-beta-x86_64-apple-ios.tar.xz=f4bda724e6e382e02ddf4e4e7a479120420666a5a1ad3c87a85d4d3c763f2cb2 -dist/2024-04-29/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=01efbb2e48045318e18bfc7b6c190b461a219e81fc1cca6c855bf0c658aef556 -dist/2024-04-29/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=9bff316c6d2fbb3c0889f9ffe4eae496b293fb3afaf8d597155e6badbf0c6a8e -dist/2024-04-29/rust-std-beta-x86_64-linux-android.tar.gz=5da713547a8af2c86da7db5d8aa4c27188dea1089fded81ffbbeb0f78952a10f -dist/2024-04-29/rust-std-beta-x86_64-linux-android.tar.xz=9d6a45d6af395360c63ce97bcfc2f9a2967c708afcd979f17fa447239703a92b -dist/2024-04-29/rust-std-beta-x86_64-pc-solaris.tar.gz=d1a71110bee002c8edfbcc00e0f5eede5afa005b09944bb2cde469c658049e70 -dist/2024-04-29/rust-std-beta-x86_64-pc-solaris.tar.xz=6b8d18c83b9fffdddf9e55c807dc7d5784cc6d7ae90a57c29b87d707f0656964 -dist/2024-04-29/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=28921ee14426f54aa09523547516437130654b2d9814120d286f209666c88533 -dist/2024-04-29/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=7c3125cce30978ca2619e9aab150cb5b9b2535fbb6274d4ac1b1d4342c7b0220 -dist/2024-04-29/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=ee5c237f092f8a4ba797c4c7769dfd4da81b5c86d2f4b88704d127642d222a22 -dist/2024-04-29/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=30c84b04bd2d4d33abf1875cfee5f227ef6484edc67b3cc4c9c96d92c8406d6f -dist/2024-04-29/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=81274050e72c5a8ffdead83f7be62434f35a65517a1b3c6f7d9d14d0d59da710 -dist/2024-04-29/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=215e20c78a2a4edf9b8368a29a09af5f4cf8d0edd1995de3bbf2eff01127cab7 -dist/2024-04-29/rust-std-beta-x86_64-unknown-freebsd.tar.gz=340131cba121827a9753e19cb3a4b9ba2ebe30569fb20d7f9300b4dbe2a15cf4 -dist/2024-04-29/rust-std-beta-x86_64-unknown-freebsd.tar.xz=69626178bc5309afc8a02c941bd77e70e1aa6917ffb6bf0d67a57d921b5c664a -dist/2024-04-29/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=22c6c90533dad3a731ad8a6696e6cdc1b15579e25c658fa2b094185e1893934f -dist/2024-04-29/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=30d7ef6684fa98e28037b69d4220cba40489c23e80fe7793c98b388dc161757d -dist/2024-04-29/rust-std-beta-x86_64-unknown-illumos.tar.gz=9d7192d32eaa6b6ccb0f615da0f4cd80827ba6484eabeaf401d8217678f1e313 -dist/2024-04-29/rust-std-beta-x86_64-unknown-illumos.tar.xz=7a3fb35e0bb252d5f90773136d1417c26d5601beadb77d6da6f5ad3081977f07 -dist/2024-04-29/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=e987635519c1edc8a1d147ca4a86283637e4dbd0a49736b01d605e45a3a14e8f -dist/2024-04-29/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=c3ab6b97dccc0038c68494b03b6d444d534e447226a2b2e140af54c94fca0b2d -dist/2024-04-29/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=72e8113687be8f947c50befb42b0957dd564f01693cf4d68d749a9e074032ada -dist/2024-04-29/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=867b24f33b19f40727c71818c8a002718d44d4cd4ceca44314331e19c1adc1a4 -dist/2024-04-29/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=f9b0fd9605bd4e264f5303bd740d9a0195bc147132969965b221f9da0d7875bf -dist/2024-04-29/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=022dcf4887df41d776ba2666858b9aaab479758134a71f7c6b2172ed7c1a1752 -dist/2024-04-29/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=b5ff4a0ecd7e0f71a9557b6096bb907e5cbc8982431f0d9b01d8e1a895d8b37e -dist/2024-04-29/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=e40d5bfb46aadf6faf849df548154db3f35f356f8b98037a056802a235922b8a -dist/2024-04-29/rust-std-beta-x86_64-unknown-netbsd.tar.gz=57cfb1fa472dd9c01fc0caf605a55b7248375d616acf84ec12f6430d5e07e2ee -dist/2024-04-29/rust-std-beta-x86_64-unknown-netbsd.tar.xz=e4121f060b917c811d971e85ce02495e83150ddcceb2204615edff24bd55bfa6 -dist/2024-04-29/rust-std-beta-x86_64-unknown-none.tar.gz=d63559803c8eb47e0d10d9f3a2284477b570a2536bb541762774271451e1f0ce -dist/2024-04-29/rust-std-beta-x86_64-unknown-none.tar.xz=5388cf8ecaa234d507e505e8c6d433c5de8811b2717aa254e4caac9f4aa6cd97 -dist/2024-04-29/rust-std-beta-x86_64-unknown-redox.tar.gz=0f027163f37618df4330ecd82afece432b0a509ab20333d7b787c0d139ea89c2 -dist/2024-04-29/rust-std-beta-x86_64-unknown-redox.tar.xz=b1c722e894b145c2183183fa58762c64402fac077419dc7874f8b08eee665651 -dist/2024-04-29/rust-std-beta-x86_64-unknown-uefi.tar.gz=24e2ac0d44619ef9b76cb1af6178103d65ab12e2677b366e8aee0604798fe5f1 -dist/2024-04-29/rust-std-beta-x86_64-unknown-uefi.tar.xz=1d8a45f1bfe6650edc5ddfc8683190eff5a74384abcb2f73eb3d1e88d566ccfc -dist/2024-04-29/rustc-beta-aarch64-apple-darwin.tar.gz=ea113c567692d54983ab6c376761651b6dcf9bedad5b5d822d28c0d0d0733cf2 -dist/2024-04-29/rustc-beta-aarch64-apple-darwin.tar.xz=e36363f1ea531d2fd563f471758e387de37a34e7ef6f4c12175979657333c5b4 -dist/2024-04-29/rustc-beta-aarch64-pc-windows-msvc.tar.gz=52d77d540fc3f83d82f35f358ccd9055fb75453af3e3bee4b11636742559db13 -dist/2024-04-29/rustc-beta-aarch64-pc-windows-msvc.tar.xz=843c56f5431c1feda85ceaeef0daf988e8ae020b3556326fb1f75ea7968bf2df -dist/2024-04-29/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=ba2fe37dda1a487a2c75151895f4f6e886e9476a992272ce26e9b5fd7adb11f9 -dist/2024-04-29/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=ccb7be3935de1920509d2061d38f92f1fb8d2a5dd6cef392492242a929363fa9 -dist/2024-04-29/rustc-beta-aarch64-unknown-linux-musl.tar.gz=40636e0936bd311803317825c5fb6b446cdb5536ada1db097b567df04a86d7dd -dist/2024-04-29/rustc-beta-aarch64-unknown-linux-musl.tar.xz=804ef68f24bc0ba5150177d8b8515daa54aa82fcb61472385ef1a1d897c5c3e1 -dist/2024-04-29/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=a320c2869d1d2c92b698397d4467c8498cad9481f38d28ac810bd165399ca46b -dist/2024-04-29/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=7ce92211d87068d9c223806929adc34ca611a1321cd58b5bd81aabb0ec3ff085 -dist/2024-04-29/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=79c16b902884301882d16be36fe75ecb32a8e49abde0038ce21cfbee883c2c3a -dist/2024-04-29/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=9384eb9bdbb585b414b6c04c592a79e90a0c0ebfeeb970e5e1b920cb638807cc -dist/2024-04-29/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=ff99de5b819a4fb9adce9386a309b9841bd33632eb7d5079415a6ca6fc86b9dd -dist/2024-04-29/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=55635cde13af11dd8cc007d2e0499bfee493bdfba87b6efd7b1fa4115f5728dc -dist/2024-04-29/rustc-beta-i686-pc-windows-gnu.tar.gz=de82ac745275f069225b84574ed145afaf9f54abde5246592e49d5d1cf40cac1 -dist/2024-04-29/rustc-beta-i686-pc-windows-gnu.tar.xz=a8a7bf64d33c95a2f94265fba8dd9ac50bbb727f4bc3e79be5bf61212cb5d22b -dist/2024-04-29/rustc-beta-i686-pc-windows-msvc.tar.gz=88967a99c993d6e0c3c7948308510644286ac826266dbd3d89aaa083100711db -dist/2024-04-29/rustc-beta-i686-pc-windows-msvc.tar.xz=1804f75786482946258ff0e827274357c49e90a7f2f568add7353249f2ab78b9 -dist/2024-04-29/rustc-beta-i686-unknown-linux-gnu.tar.gz=3cb7e02c61d4a21d8289289b874b25e8b020c1d553e5af950160bffc14f51c18 -dist/2024-04-29/rustc-beta-i686-unknown-linux-gnu.tar.xz=2ad4b1311a0e39c359798375912faa91b4e13cd473bd59efd1e4f721777d254f -dist/2024-04-29/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=ab19efb741a127615b9022dedf1d895b53c69740cc3da745f9b9888bade9d98b -dist/2024-04-29/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=492cc11d54df410c2547890803930fc65950e6b81ced512e24bef56c3e70f3d2 -dist/2024-04-29/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=c5a631a41f417336f3f65c85adefd1fb0bacc02465485f37d29fc1223c9f6cec -dist/2024-04-29/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=0701183b615d9eec9daea724d4cd8fa98dede2260cfb6b137d6cbf8ad6b29a4f -dist/2024-04-29/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=cb70e92d5275862b500614d79eaea3d19319b96798f4850cb19dea9a8038a651 -dist/2024-04-29/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=908cbe562d82cca1bf176fdc99af867966ea423d244c4a50e14bad19f6878201 -dist/2024-04-29/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=8580a3eb6d6df1774f4b6ca06dc1195c42b1e2a463488a5d851e99b0ca6d0249 -dist/2024-04-29/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=2627948036e905f2e280663c56c86c172e2b0d057311ade7ca238953b7e0c36a -dist/2024-04-29/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=526e4f129fdb4b2c8f4317c57105a09ff03e71771d6d6bbbc9380917b5440d71 -dist/2024-04-29/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=fd9dcf60838376478d7cc505ec7fc39f86f9d042646a3b836e9c06825927c7eb -dist/2024-04-29/rustc-beta-s390x-unknown-linux-gnu.tar.gz=664c1255a9435d1ad086329a3c215974b9302d481762240cc9d0328d9f1b8c9a -dist/2024-04-29/rustc-beta-s390x-unknown-linux-gnu.tar.xz=a585ce7684e4174f03adb09df17221e1729e8179dbc91b9a0f8813c3ecc0822d -dist/2024-04-29/rustc-beta-x86_64-apple-darwin.tar.gz=59a1d91009b506a5bce3c276993cb8acfd71f73d01f9eaf4195b36114ac822c3 -dist/2024-04-29/rustc-beta-x86_64-apple-darwin.tar.xz=f86f3309cf2784b076f14e7da9e921c294a7701ea92d378c609061deccbc6bff -dist/2024-04-29/rustc-beta-x86_64-pc-windows-gnu.tar.gz=f5c074461409b33a9791325d4014e6861ad36f99b9e48e54ecceb73986450be1 -dist/2024-04-29/rustc-beta-x86_64-pc-windows-gnu.tar.xz=045431eec6f839b1c40b5a75c5000f80bd6351274a59b29d962833495324ecb6 -dist/2024-04-29/rustc-beta-x86_64-pc-windows-msvc.tar.gz=a3abfb68e60544170f47209bbf048f1374e5bb75901a529e2ac2f315758155f8 -dist/2024-04-29/rustc-beta-x86_64-pc-windows-msvc.tar.xz=398c41a3219781c7cf1a907406506526b672abca6d3ab59c30556390a5f992c9 -dist/2024-04-29/rustc-beta-x86_64-unknown-freebsd.tar.gz=38895e615efd0bf75ca14b0ab0a085527cca64fae17631d1780a8f51acd26d17 -dist/2024-04-29/rustc-beta-x86_64-unknown-freebsd.tar.xz=786f40030dbe5e6897aafe4bda44770920b2010b93fc5ce86574774e531e2eff -dist/2024-04-29/rustc-beta-x86_64-unknown-illumos.tar.gz=7003cab7650dae7e3d29032422a57782a2c146024c437a6466ae1dd2b61a6618 -dist/2024-04-29/rustc-beta-x86_64-unknown-illumos.tar.xz=bed3cc10203e8bd4d43b6245928c8a607acc5b6e633635caea45eb4eef4bda56 -dist/2024-04-29/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=84cdea91c9f1e848ea17f554229ca80d18d093fc609641d8f003c4f2d4871866 -dist/2024-04-29/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=a20fce7512f7c8cc6230a0f63f12855b04370d25e621183f71aa444c90c36b4b -dist/2024-04-29/rustc-beta-x86_64-unknown-linux-musl.tar.gz=87e0c484ade99efab57c655ef96dbabf7a02314540575b65a14372ab5496c36c -dist/2024-04-29/rustc-beta-x86_64-unknown-linux-musl.tar.xz=469757d8f35c9f4210aefd2ba660ee249e4409d47b908a6c68c1e650ee81ae67 -dist/2024-04-29/rustc-beta-x86_64-unknown-netbsd.tar.gz=4a38000480fe78fd5da7f9b71d36f296a6ae103254d932c4de6b902354e86bbf -dist/2024-04-29/rustc-beta-x86_64-unknown-netbsd.tar.xz=45945d6af237fe4c91fde7db02ca19e99bac56a911b8db79be9b6ab8bb3934e1 -dist/2024-04-29/rustc-nightly-aarch64-apple-darwin.tar.gz=0b07375a9a6507fd4932a05b5aaf28ed349fe2040103f1cb69c8a2494437258f -dist/2024-04-29/rustc-nightly-aarch64-apple-darwin.tar.xz=143bd7ed3ca7b913ddd0cea7cda8d1a0e4c29cc2ccbb7d29f0e45c2a87c3ec46 -dist/2024-04-29/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=9404c111b91fd092367b88adbc37dce10a98c443bd8d9e13a860e1fb4e3af96e -dist/2024-04-29/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=f9f432907c276edcae5ad8ade0264f3c03109be02e791a814edc8ad3d229637a -dist/2024-04-29/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=33425c90427424f0b30fa2a6331a3b59c680f1c1bd0d8845d7e6bc1e2f80292d -dist/2024-04-29/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=03792890c64c72f30143849894b15f0eb3d6ad735fceede9092abd900ee733e4 -dist/2024-04-29/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=cf6f2bffa0db1b4b9b8e95801bf415dcce413f902e26f4c1831dff1a00752b99 -dist/2024-04-29/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=9192fdb668df8d4cab776623db7d01e35af42fea94098c1d4ba53190825d81a8 -dist/2024-04-29/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=a174e7e08da2abc6b84499360670188f5cc61b6d055967e04bf602ff3d831f45 -dist/2024-04-29/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=5a59811027586863852b15fc2b603e7e69b19841f4c10d2527ef1fc5b77d8af2 -dist/2024-04-29/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=645bb5dd7a96bb9292b9956cb9705e9aed2408e47728f245564f1f7404ede783 -dist/2024-04-29/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=1fe34911b082c3a5ca4f24656675c095d2cf56f8005be9ca2517d0ef7d0a2b37 -dist/2024-04-29/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=f2d6403d81bb0afe2e14956828987a0bb044c95f2d9566e1d792dd922dad7914 -dist/2024-04-29/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=d93fdafcbbfd50c88c3f4feb4c68b053882ccae02c45e1615aeeae5a86f4aa98 -dist/2024-04-29/rustc-nightly-i686-pc-windows-gnu.tar.gz=a9e997b03559b3dfa2a0eba6ed7a142d7651ea7f4ba4e788d9de807b50558e58 -dist/2024-04-29/rustc-nightly-i686-pc-windows-gnu.tar.xz=4412b5fbfab8c5b31e57cf8c4ce9a9d13cfc9c0a8174ea1fc8a7c05281e1cb54 -dist/2024-04-29/rustc-nightly-i686-pc-windows-msvc.tar.gz=1725c41500dbf6bea554f3d4acaba70167f0e89087aaa3eb3c0f8a99047c55c4 -dist/2024-04-29/rustc-nightly-i686-pc-windows-msvc.tar.xz=27db022494afebbe05605f134191e8b2e78bfdeaa638d4215174038ca9dd2fd7 -dist/2024-04-29/rustc-nightly-i686-unknown-linux-gnu.tar.gz=dc1a05d49b773dba06808c1c50653ecac506b3433f0f6dfa307109a7c621cc1a -dist/2024-04-29/rustc-nightly-i686-unknown-linux-gnu.tar.xz=cc58ce3af8f5481ada4dc129079cd558664717526b2f7f9a02bde6bafb6f45ad -dist/2024-04-29/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=34cfe803126ae9218b17adfe833a55c697dfa50729ac83b642529f3682d12d15 -dist/2024-04-29/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=c752dc8962656c09047151fd24166f3134fbeed85006b5d22496691079c7fb9c -dist/2024-04-29/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=386b086b8aad922050c813dd58bb79a52ef806b2d1413e2e9cc46d6e43b81d7c -dist/2024-04-29/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=d9eec9ab7c265444ac5f04d4ec9e77d4c0c3c2e34c5804db8abf5f94c8fd2272 -dist/2024-04-29/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=91df129046443554bfb836d25886aa9807b917acbc9dcf30f6531cde7bf912fa -dist/2024-04-29/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=ca9b574b9f2e914b5a6d9e011aba805d1e6f9b687dc1a1868e88f0e4d9e4401c -dist/2024-04-29/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=8b44f96a1ccd6d501b0af3960edb2c1a6c93957676a1c2cdb831e614de398f8b -dist/2024-04-29/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=f8a10a6767b80bf24f73223b9e46e1b18b6bf6746ad2115eb8968a0e482f0e4e -dist/2024-04-29/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=a197208807503a9cfbc6df938d614a192da48884b2e4892f7b1d234579091be1 -dist/2024-04-29/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=af3a1a33942bd8a3417593dc118abb1db0373f5410f54771713c05bb86724fed -dist/2024-04-29/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=5a3a3aa73b6a0f21c63b9a40bdbd0bb4dc59bd75add0a06e292ced791fad31be -dist/2024-04-29/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=6d7903f1c9fc95a23448717326d667dce59e54aaff821443d3cd9137cf3537fb -dist/2024-04-29/rustc-nightly-x86_64-apple-darwin.tar.gz=64eede54da4bf88b0a42ecf7f7a4bf8002b5550e60a64e1e48244c7f5b04768c -dist/2024-04-29/rustc-nightly-x86_64-apple-darwin.tar.xz=0a8f95e3bb0bebf9bcc8116b91bab3ba97cb6ff4021713586280aaceed9da030 -dist/2024-04-29/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=58f9e0dd9c1aadde2dfd869528adadd4acc29ab0850236f3cee5f023d4211939 -dist/2024-04-29/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=a211a962093e0d09358d51a6eb48da0966a02383c6b00c8acc077b5663d7d707 -dist/2024-04-29/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=5631926874dc54204c319137a77a89de5e6f408de2a832109e2be71ea79f27d1 -dist/2024-04-29/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=a8cf87bc663b5e3dbcc97b0eb58bb1b9b5b0100aacb47dc0c372fe1612517244 -dist/2024-04-29/rustc-nightly-x86_64-unknown-freebsd.tar.gz=1011f98197a9fe82d6095f4521934a06eea5f7e9719a6e4c9e3bf13d68f799ca -dist/2024-04-29/rustc-nightly-x86_64-unknown-freebsd.tar.xz=79599b3f91f9372262e97a417f4e104ef5192c0f6f8df204aea9c8b3ee39430e -dist/2024-04-29/rustc-nightly-x86_64-unknown-illumos.tar.gz=7179a453bdcb17e401c0af8f4ab86cb5a4752a8ec80b0cbdd4cf1854c7f36a35 -dist/2024-04-29/rustc-nightly-x86_64-unknown-illumos.tar.xz=d565fc366fdbc305fbfe59e72b971c58f201d69e03a9ffa667d2ca0735cdb7f3 -dist/2024-04-29/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=ae6bd8e20560d48519290d78e3d21f84b983403ca1f8f466a85496276d7866da -dist/2024-04-29/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=c7c0f8f44b0275456a27952178caa04c32eb9a1507056ddc05926a0730e17359 -dist/2024-04-29/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=3246797ddbc9118de819b13b005b83748338f3c825a7436ebd5aa79ca55539c0 -dist/2024-04-29/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=c7e784e77dbabedad88d24d2ae6dc4abb68bc04b1cd6c9d45f6dc28b6d0e2edc -dist/2024-04-29/rustc-nightly-x86_64-unknown-netbsd.tar.gz=c9452de4b3f15f0cf0b7d737b217b2cc7b88a96543bd8ce587ee14be1e21a90a -dist/2024-04-29/rustc-nightly-x86_64-unknown-netbsd.tar.xz=de9b05278a5c69d53ccbb31223526ea2aa2275c0fb3f046d1c1b4d67c0b0c275 -dist/2024-04-29/rustfmt-nightly-aarch64-apple-darwin.tar.gz=3734353a58dbf6c3831cc6b4ea606357140c191c89e8dfca1d7aa2f3fb8ac53d -dist/2024-04-29/rustfmt-nightly-aarch64-apple-darwin.tar.xz=e5e3a6e609fbfd537aa4acfefd3681d4b6c8029e2801a1ef23e8b09cf5a47bfe -dist/2024-04-29/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=22f54857e01d759301d099b67547cdc485596499088d0d749d38058c28e0f752 -dist/2024-04-29/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=db48a9d45dc7c7aad4c9bb0d20789dd35fb6ef7a966948c44fbbae132de4c16b -dist/2024-04-29/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=fa5d1fb9f3627e7d59269a1f8008d780c685ea04975473f1808287134e7bc5a7 -dist/2024-04-29/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=61ab625b47fa9097af90a79a1e75a2f2492a415f4009c9043cf453bd4128f031 -dist/2024-04-29/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=cc79969341fc60991059d0e3f13a69489c1e0915ea5787a88b8605ed5b7f3cd0 -dist/2024-04-29/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=bbdb75f922b4b1716b033d91c76c4c0aa53061d6e7fa53a9bf16fe076814bbb2 -dist/2024-04-29/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=13ca68afc3f3970a37951504664b58035102e1ae06d10a744389603b2f7499f5 -dist/2024-04-29/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=04b174aa724945b6359a555892506c6a742a7c427464e8206433bb3f9a65fc02 -dist/2024-04-29/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=430333380a590a9de211c8d735989fedb89321cf9f5f9a0b1ef651ec8b598691 -dist/2024-04-29/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=924713e648806945cd56e54d4c11dc74b65241c8dbf6cc7b401c5c93d0f7ffdb -dist/2024-04-29/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=bb6e5a8b5cc88099e613aa5f4d926165976d8e4a7fccbecf4ac3b0eb966d7a0f -dist/2024-04-29/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=34d5e970304e1734aacb26c095e926c27a07e1a41fe70db9fa2997bef97ad3ec -dist/2024-04-29/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=835447a1d9d60659e99903275f327641809fc0148f35149f980d1a17ff87cc9a -dist/2024-04-29/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=ddd84a7f900aa239f93711f7da71e57aaedeeba2c9c8a8f23608acc7e48613c0 -dist/2024-04-29/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=02f0af2bdae167c6091099a9b54ceb150c22b0f20dc861587a02cac78deb0b39 -dist/2024-04-29/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=822f78f39dcbe3282bf7888a8cdae04efe7b023ded026a7e7f430e4ff15e7942 -dist/2024-04-29/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=68a6189652c11a2c142c5339e2f5fb09d5f3e85d860bff063f62d5d3a3d111bf -dist/2024-04-29/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=6740ea882effa2fb87dd72744c08888ce5ec59c9797c00369156b24847bb180e -dist/2024-04-29/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=0bb365e2d895ef3c39c4899a01187a23f9b7c5195c30bf845da3917f62f5eafd -dist/2024-04-29/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=2e54c9887bc6ed1eb09b9f69c8425da843ea12bf33248fa0ccdc0d14387c1c57 -dist/2024-04-29/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=b0c0fe437921d17e2f50cbff87beeac067efa3d5211a241fb6f4c10b8ab500ac -dist/2024-04-29/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=64e7282c0cf4a714b11eed1d28be3a64ba0ccc6d899211a872a5a7809d514c08 -dist/2024-04-29/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=100cfde057c81460b8cfde2047fe83ddde360a6df1ff178da5a968b17ecc9df8 -dist/2024-04-29/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=38e8712e98fa0bc6962ab2fd2e3b96a2a5dcaa6c16161d8caf71131a1ca5031e -dist/2024-04-29/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=4dab52b50e19348fb39fdad39ab44189c27c10f80b5fbe2cc4723b644611fa87 -dist/2024-04-29/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=36d1b2c9150fafc5976c296200ba3fac3e923df3e6f18032068200e2a887146c -dist/2024-04-29/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=7cb4a536320c23d305ce3bd3b7a954d951bf4d358ef3732be75b9b290c4818a5 -dist/2024-04-29/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=ede1afc7dc5892ef6f780e987737e145c4b4d00495da8c2e9902182a3a174e20 -dist/2024-04-29/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=e2d2d561cbfa0add0e5349682976216d3a7cff4094372c1ed26854bb4e4d93fd -dist/2024-04-29/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=e0380e65e83e4131f6aa7ee4e185689add4372b0c1653312e2ffd56072fdd0fe -dist/2024-04-29/rustfmt-nightly-x86_64-apple-darwin.tar.gz=73a140c7ed9c80f209ff976c63b0a34d625d651553c38692c91f048f4e0ae470 -dist/2024-04-29/rustfmt-nightly-x86_64-apple-darwin.tar.xz=9946b7120465181e05916b8023bc075b32bd85cf45a3b1d8bfba2f94ac78d927 -dist/2024-04-29/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=eda273f27714b1e45adcc2388149f48de0e32a9104db0b9d1a02f6d37de43fef -dist/2024-04-29/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=f2955a4b696d050c219a96c093162b42a2fab921f9f3cb7570f8462928306c6d -dist/2024-04-29/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=ec900cc2d3c6d45ef039653f4418f9b9a4a5fddd5d7e8077c3fb08b36c539a28 -dist/2024-04-29/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=04e6999a3405acc79f5fdcafeeab52880e5eeeedd3909b5f3c57e7647c86ef99 -dist/2024-04-29/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=0730c5ebd576fec5371085f9fac0adde9424e1d7626456ed33bc66351b0ad307 -dist/2024-04-29/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=90cbd84b8d48f0235a1954166f5edd53dc3031532ec6dfcb364f9a9624c9ce11 -dist/2024-04-29/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=5f5c62d321db27eb495f6ea312ef8bea0bf17a7a501a44e062986c416951700f -dist/2024-04-29/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=a3bf64e2f22436e4484fc818f69d2f750fddd05a96463fd4abfcf655edce36b9 -dist/2024-04-29/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=a847a6f9c7a3ce71c7cd8d81bdfcfcd8e4d128aa28ba0dafea89b0cc37c6c36c -dist/2024-04-29/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=21fa794456566c64d08f629a385f89b3cfe9d9b69f317ae85fbe7425419108ff -dist/2024-04-29/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=b3f792f10a98993b4b55d1df951727a4422102d51b1145e51824268d48587ad0 -dist/2024-04-29/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=d791f0ec3c004e7baa0381962bf8ca2f18a3c861152702de5301d0149260e7fa -dist/2024-04-29/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=9807b2887e976d29f0c04484f8459175b4f6b70ef000037cdc4dada48e3cbd74 -dist/2024-04-29/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=019920d64778af62879e2146c2c13d9f6e2165a38bbfa1982694bfb48864d308 \ No newline at end of file +dist/2024-06-11/rustc-beta-x86_64-unknown-freebsd.tar.gz=4bf5a3ae2656a992beedec2f41dc98c791426a1a6a88f3503b003b4d6f0ddc1f +dist/2024-06-11/rustc-beta-x86_64-unknown-freebsd.tar.xz=feaa1484586e78d77225734328d460277dde14baaf1299a30b30ef91e9a26a82 +dist/2024-06-11/rustc-beta-x86_64-unknown-illumos.tar.gz=9c692a71916f7ca9d41a97416f51d463b0495311491ba92b4d3cdb0f6c4e7c87 +dist/2024-06-11/rustc-beta-x86_64-unknown-illumos.tar.xz=97dece0cc2e4856a49f9ae17f09171fb55f140987acd0f988bbd0aa1e8971936 +dist/2024-06-11/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz=6ad2e87e637302e8458d47dc1af923e38bffd7332775cfa5520c29110594d9b9 +dist/2024-06-11/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz=84f6755c1377ced8ed2700b5ca9a2d38dd2e26a03e1fd636841ad46d3cf63895 +dist/2024-06-11/rustc-beta-i686-unknown-linux-gnu.tar.gz=11b439045379be2722cb42409b8bc342ff4b5f7df85e42566006cd72bb1a4e8a +dist/2024-06-11/rustc-beta-i686-unknown-linux-gnu.tar.xz=4e5103702b4e43ea71914e9c508aeb0db6382ea331c31981142163b2a89e19e1 +dist/2024-06-11/rustc-beta-powerpc-unknown-linux-gnu.tar.gz=396a3aaefca9511086f7e5442209b70622082d133b96783b4237c3f01f42bf87 +dist/2024-06-11/rustc-beta-powerpc-unknown-linux-gnu.tar.xz=66e46ed344af9f165b7c4fde2c576340fa81ecade61c45ecc1562de70a9203da +dist/2024-06-11/rustc-beta-aarch64-unknown-linux-gnu.tar.gz=831420b262c5a2f44459e0daceebe271f298179c37aa5b7a1704a15bbbb1fb97 +dist/2024-06-11/rustc-beta-aarch64-unknown-linux-gnu.tar.xz=691a6c4d0276add72cfab40968520ff48f683d4e660a50d1626ba9564dbf4914 +dist/2024-06-11/rustc-beta-aarch64-unknown-linux-musl.tar.gz=ebe1efe3c5d74e37d1c40dbd585ada7a7e256ce76911746f54994dfa15f9907f +dist/2024-06-11/rustc-beta-aarch64-unknown-linux-musl.tar.xz=3bb37fcab069713fe8aa5ca72291a439eba5af4a3cc0be9d152e5f29ccaef864 +dist/2024-06-11/rustc-beta-s390x-unknown-linux-gnu.tar.gz=c00c2ecb5aae552069395d03f4135f051da073efc68ed645348f026c1884799b +dist/2024-06-11/rustc-beta-s390x-unknown-linux-gnu.tar.xz=2c72347688058091a4bcb0c2fbcff49020df2b2c1733bc19d93b6e5149d5b13a +dist/2024-06-11/rustc-beta-aarch64-pc-windows-msvc.tar.gz=517977edaa51470d460aa9ec09309eea2082cffa6adb13571a8f36efa9351dff +dist/2024-06-11/rustc-beta-aarch64-pc-windows-msvc.tar.xz=0cc0b740cb267fde814b4971591b5797faa528b1e6250ef7b2d5220f0916208d +dist/2024-06-11/rustc-beta-x86_64-pc-windows-gnu.tar.gz=9fe7d85fdbca12fbfdb776789c336f726303c8845a6f6c93b2dc6ea8487585d4 +dist/2024-06-11/rustc-beta-x86_64-pc-windows-gnu.tar.xz=38130e3d9779ad54bd890c4001b34b7c58368ed1940e7e676947e9c05a8f6649 +dist/2024-06-11/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz=acca3e045de3c0d908caec4249e3905d207d96b4a1722644457b3cbe5707422c +dist/2024-06-11/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz=e4e92fc4b672f72b1f03db62ae712d9f944b982db89cf0d8dcb94adb1d560b3e +dist/2024-06-11/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz=63274e707bd0d2b2f08c49f170f26773f802db833347d2aa694d345d11c841f5 +dist/2024-06-11/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz=13702b24228d983710d5678e0a1003e4f77e2244fbdf599015f52adbbb77bfcc +dist/2024-06-11/rustc-beta-x86_64-pc-windows-msvc.tar.gz=ca877491bf7bf50989b9ac21c12e335fd0ff002ccf4a2c07316ae1df1a3d7e6b +dist/2024-06-11/rustc-beta-x86_64-pc-windows-msvc.tar.xz=d757a328595887039d8017e9e3faf79448850c3dd9bd3c26d309a313ccc09a38 +dist/2024-06-11/rustc-beta-x86_64-unknown-linux-musl.tar.gz=155497f2abe036f09f6aa014969d58132a9e2e4e148fea485b483e0dc3705a39 +dist/2024-06-11/rustc-beta-x86_64-unknown-linux-musl.tar.xz=6204d1fa08beeadcf1564e334cbb65a7317c571557cbc5517eb6bf08f5b0ce75 +dist/2024-06-11/rustc-beta-aarch64-apple-darwin.tar.gz=3bd7db536344c6a3d0d0bdf95986136d5db8996228802444b561783f2e7c3c9c +dist/2024-06-11/rustc-beta-aarch64-apple-darwin.tar.xz=b425561b3e7d9130fae94e78522bb262c634304ced32030828743fb332b11ecb +dist/2024-06-11/rustc-beta-x86_64-unknown-linux-gnu.tar.gz=e2f78198d33f98aa34bedd4f5920868a8f68620e29eca2917833f6a8f23f787c +dist/2024-06-11/rustc-beta-x86_64-unknown-linux-gnu.tar.xz=22f3b45678081fe52a9c8e89234a9939aad5623eb43ec54921effc28c0bdcd80 +dist/2024-06-11/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz=beb5ec95069cbac4e53c51ea3e785ab5f25122132c8b78b196372bb2a31ed46f +dist/2024-06-11/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz=17242f74c96cf581f2e40a82f6d7fbd585af2c2990cc73ad4ba7a3503dd279a3 +dist/2024-06-11/rustc-beta-x86_64-apple-darwin.tar.gz=d952d90397c70c5b53caa98b2e5d48e35780e1e5b342775c10fcbf504717d2ec +dist/2024-06-11/rustc-beta-x86_64-apple-darwin.tar.xz=14bd4336eb396a133ddbb2acd01ae799f2f68a7ac723a00a9db8c3e06bfa8027 +dist/2024-06-11/rustc-beta-loongarch64-unknown-linux-gnu.tar.gz=a2118f80091d62ebb762df0bdd159d19befb440d754034da7348aae50d63628f +dist/2024-06-11/rustc-beta-loongarch64-unknown-linux-gnu.tar.xz=a4b9d1a04d8f9cfd4daaf57fbeed3615d000c5107c6fa1b2992dad6ca552e35a +dist/2024-06-11/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz=d5c16c5621e43cd3007ba4d2bc0f6e8a3a2376bc2a911e42bd2f2670ff94c12c +dist/2024-06-11/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz=202662492afd863b6b6428a3bc5ed822c62ac2aeb8a0264df1d69cc5a3728d8f +dist/2024-06-11/rustc-beta-i686-pc-windows-msvc.tar.gz=a9fb64cac53595444bdbe8f152e52214812498e0c6e2b664db10f1f693a92dca +dist/2024-06-11/rustc-beta-i686-pc-windows-msvc.tar.xz=fe78c5ac51d96c3c3e222828dca3d105a2d4d7f8acd6e0d86ecd4b27ea70a4c3 +dist/2024-06-11/rustc-beta-arm-unknown-linux-gnueabi.tar.gz=8c584010c964ef1b02e1014ca1c6ed9584c1ff781f8af115ee001e3a13213954 +dist/2024-06-11/rustc-beta-arm-unknown-linux-gnueabi.tar.xz=4a407fb59db284ddf6fc0ccd487116fafbcd7c227f3dfd4e0b7b64dd560dc113 +dist/2024-06-11/rustc-beta-x86_64-unknown-netbsd.tar.gz=2f07a283bb0e57ad50366b67d216503a423867eae205e4354b74b748e9d3ce85 +dist/2024-06-11/rustc-beta-x86_64-unknown-netbsd.tar.xz=8008331b32e9d0daeb275327082e38671dcc91864a3ac95be67bd15ecaa852ad +dist/2024-06-11/rustc-beta-i686-pc-windows-gnu.tar.gz=c42637a8aa8666d746a45c9f47b437a9720d355b5bc6db42c2a90fb03d2c4a26 +dist/2024-06-11/rustc-beta-i686-pc-windows-gnu.tar.xz=eabe3a7e72829e0e5904ef381157cc3da169d9f5942e0367b2d1b1781c387560 +dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-musl.tar.gz=f4010eea815cbab895c96311a456c4e3aa24c5f0d55f09052bcde59de7f286e6 +dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-musl.tar.xz=a8f555c713cbe4a1860ee5bc812eb624f2775e034da54bd0e37c325453d70448 +dist/2024-06-11/rust-std-beta-aarch64-unknown-linux-musl.tar.gz=c89619ed4ec28d4f48ef2f7e51155716483a7f4bb51fe38235e69d4694cfcbe5 +dist/2024-06-11/rust-std-beta-aarch64-unknown-linux-musl.tar.xz=89aa3d1add14298bd40e3bcbc6b41df91312258ac6f203fff9eca4422f0c5e9f +dist/2024-06-11/rust-std-beta-x86_64-apple-darwin.tar.gz=2303e7926bcc4fa2c67c7063fcac3a7b581fdd91fb89123cb6cdf514ff38afed +dist/2024-06-11/rust-std-beta-x86_64-apple-darwin.tar.xz=9ddf392d8c696e902c2924ff938a4bd099b9a54834dc62710e42527e464e776f +dist/2024-06-11/rust-std-beta-x86_64-pc-windows-msvc.tar.gz=6016b465a0cfc628f2c978ed18bc19a4a27e20a5ac11a170c40d01a857a5f50a +dist/2024-06-11/rust-std-beta-x86_64-pc-windows-msvc.tar.xz=8e3d6f7ef6efead7d08b1c889bcc6e520d0a6d29226ade1b150aeb625fdb9abd +dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz=5f53b7630fa7703fc6752c3fb8c8eba8e25b9f77a3b91e14982510a0091d9e44 +dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz=8ed3c2f0b0f81d197d0dbd01aa777eb17ebc7303176601be2b2b05ebe1bcc971 +dist/2024-06-11/rust-std-beta-loongarch64-unknown-none-softfloat.tar.gz=dbc1a1bb8cfb4472739d7f3a52bcd571357d2d2761f3723aa4c7d69abe9a5dfc +dist/2024-06-11/rust-std-beta-loongarch64-unknown-none-softfloat.tar.xz=58f46f20d58669d497ff683d6ea85e18ff68d51a442f6e21bc20ddee779e1c4f +dist/2024-06-11/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz=d71947b5ddcfe538162b14d6b785c365c34f2b6ac2f1abdc90b020c4a446a313 +dist/2024-06-11/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz=ca0a665e566b3ff74c5f552e7e4e030d3cae9080dbb6055dce3aa41206b5b111 +dist/2024-06-11/rust-std-beta-x86_64-unknown-fuchsia.tar.gz=e927c0890d2c5135d86b1960b6b182d5c47853b53eeb196b4748e7b8ec9362e4 +dist/2024-06-11/rust-std-beta-x86_64-unknown-fuchsia.tar.xz=df33672f826ce8681d6c18ec2129ef47623b7cbaf6515cd921d39dfc05cb7a06 +dist/2024-06-11/rust-std-beta-i686-linux-android.tar.gz=2559fdcf250e7e0a1b5589e8f2bc69340f5610b7e6ddf08185936fa2e6f10534 +dist/2024-06-11/rust-std-beta-i686-linux-android.tar.xz=442f0aa53f343eec2b6984934bbf0651ff45f314d1f46dad2aa11a2f5c5a5a5b +dist/2024-06-11/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz=cb657852d05766e1c18b11d99a94f3570b6c6147dbf9b2113b80236566d2d4ac +dist/2024-06-11/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz=943de72c556821b67d59afd9dd128bfb01b3ba743dd3e7561c50559afd3768f4 +dist/2024-06-11/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz=a7c6d3f54d925be073007e3737173b0def7d781b6bfd5345971a69300aeee9c3 +dist/2024-06-11/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz=cb9115328bf6f5ae661ce5bc9cc9165c6c9e6f759f3cc5038154531b314e6013 +dist/2024-06-11/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz=2a2dac9fba84958818f6b31f30f91fcfef521111827937c88aa696b646f5b075 +dist/2024-06-11/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz=d16b28da0196a815f028d432dfa0957d0bfb512b259129969548bd050afac872 +dist/2024-06-11/rust-std-beta-armv7a-none-eabi.tar.gz=d78f96102490f9025598b11cb72e31e96d3f5fd566e8f7b2b2c503a37b0dc6c3 +dist/2024-06-11/rust-std-beta-armv7a-none-eabi.tar.xz=a439af58e90970b4dadaa04d8f94ea53c6c7a159117926c46b7da0eaa0d0ca5b +dist/2024-06-11/rust-std-beta-i686-pc-windows-msvc.tar.gz=d7847f3d9b1a2f50541ef3d2c25eeda469b28412817cadca52756aeb4f2c57b1 +dist/2024-06-11/rust-std-beta-i686-pc-windows-msvc.tar.xz=622cafc347ec1d3f0ef70354a0713cd255029730b17ad1f1ee951d9b0cb515e5 +dist/2024-06-11/rust-std-beta-x86_64-pc-windows-gnullvm.tar.gz=5fee2542ee8ee8d155edc0ac36ec8822896e6f93f9f573b9284d70aeb186a05d +dist/2024-06-11/rust-std-beta-x86_64-pc-windows-gnullvm.tar.xz=dfe9ca3e3f217922b65db0d65826c0962369fefd3d5d72d6f03039acd710f122 +dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz=b0937c5dcd458f313aabe07668ea338f2629bfc9c45d6139211b6923a7c7eb0e +dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz=65973a26c69f8ca7d3022180e2729161f7a618706cd9974fc54b14cf48f1af79 +dist/2024-06-11/rust-std-beta-aarch64-unknown-none.tar.gz=def2fc3aca20a74d023156606067f88ea8f1c8b602fb628b8f27ee43e3b62cfd +dist/2024-06-11/rust-std-beta-aarch64-unknown-none.tar.xz=43cdb54af35a6c00ee10759a3f6ff6ca5c9080ba817aaf2eab7df8f239d77196 +dist/2024-06-11/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz=ae2cbe53d304442e112585dd968f72454cc33bb75d3b4d7854a04575ff7b5301 +dist/2024-06-11/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz=b3192290a45ff978126f4f08e7ddaf6de8720a2f6abbc5b41536fad6f994d7ae +dist/2024-06-11/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz=0e2dc167dbb66366252100fc87b94a67cca3360343302697c2989beb2eb14704 +dist/2024-06-11/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz=6144604269f533eed11471b4c5d2f31a9f8340d9edf0f398a42e6e970c95b991 +dist/2024-06-11/rust-std-beta-riscv32i-unknown-none-elf.tar.gz=b57e11248924ac54500d80525b8fe823e7dc74bc579c2b7f3f076c0a4c85ee2a +dist/2024-06-11/rust-std-beta-riscv32i-unknown-none-elf.tar.xz=9e473a5de0d02e6d8278a9e2430af135e3d6d4ecf5ce59827b384c8f20af39c2 +dist/2024-06-11/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz=1a62b5af52767e68146a45a47384b33c93b32a4579e51d41f66746e38dfa6eba +dist/2024-06-11/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz=81bcf448ec54470edf60c2079ad9ef03f34052aef5fe9d8f5af933c565fdfd29 +dist/2024-06-11/rust-std-beta-x86_64-pc-windows-gnu.tar.gz=88eb49f86265a7fdb49b455cf5a9a373ac1e0f7cbcac08ef2194eaefbb039b22 +dist/2024-06-11/rust-std-beta-x86_64-pc-windows-gnu.tar.xz=bf9a278615765e199786d16f154006007ddfca0d5a5869c55a05e513a7b06580 +dist/2024-06-11/rust-std-beta-thumbv7em-none-eabihf.tar.gz=13f16402dea66facba6e10a9556e0afdbd6b004e8aa5d08e106cf76ea59b8288 +dist/2024-06-11/rust-std-beta-thumbv7em-none-eabihf.tar.xz=7ecd66dc65758cdf32cdebc38f6f399392788a7085d69ec7e8fbd63be7b3e904 +dist/2024-06-11/rust-std-beta-aarch64-pc-windows-gnullvm.tar.gz=96ccb2252defcd5bcd9eca19380b9d4e4115ffb135efebe6717b1527ee440bf8 +dist/2024-06-11/rust-std-beta-aarch64-pc-windows-gnullvm.tar.xz=c31f4ef95857a445bc480b73babb886e259443b3137efd694c4b67f913838212 +dist/2024-06-11/rust-std-beta-thumbv7em-none-eabi.tar.gz=0637fe1ab1284334ed4f9aec87a4b51b6983418997844e10ffe20326078f6d18 +dist/2024-06-11/rust-std-beta-thumbv7em-none-eabi.tar.xz=ac7f13ba1a107d03c1b7bb0d2e6753b926a26554c82de38263113e2d5dfeca21 +dist/2024-06-11/rust-std-beta-s390x-unknown-linux-gnu.tar.gz=0e8a21970d4ea6331968b10e990cb6dbe3d38af393e4dc8d603efec1b59aa0bd +dist/2024-06-11/rust-std-beta-s390x-unknown-linux-gnu.tar.xz=04b9663cef2d3507c96b77b18aff7c9beee0f6bd0e0cb9ef37b8c9cacadbb198 +dist/2024-06-11/rust-std-beta-wasm32-unknown-emscripten.tar.gz=01d4da0efebf4615da7c92c6cfdaedaf283d3f5e66c58a1b1e2e17488d86b3d3 +dist/2024-06-11/rust-std-beta-wasm32-unknown-emscripten.tar.xz=c203ec9216407f90d0bcc451e40a29e50279dae8ee161f05d9245e3cc8e1b30a +dist/2024-06-11/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz=3621e0a16f7e6d9f292894a343941399a54b5d76a858fe5a69b83c95f15ada07 +dist/2024-06-11/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz=5944208b88c6924f8586304abea6827cdf756066158dc64200e207027143e843 +dist/2024-06-11/rust-std-beta-aarch64-unknown-uefi.tar.gz=1887e342d5c6453a63336023e51c124838762f89e9289d1abd8226a717bb96d3 +dist/2024-06-11/rust-std-beta-aarch64-unknown-uefi.tar.xz=ba0ca831ed66a72a14c8c9dd591e97d7d6cbc55762c961283088e897de4a8669 +dist/2024-06-11/rust-std-beta-aarch64-unknown-fuchsia.tar.gz=59790fb6982ebfefc40d352d411604840b7540a6ddfb26ad14d70e139df3ab99 +dist/2024-06-11/rust-std-beta-aarch64-unknown-fuchsia.tar.xz=c022c80da2debecdbea32ce34ef040bdebc8566a78e2867512815532746e1c95 +dist/2024-06-11/rust-std-beta-armebv7r-none-eabihf.tar.gz=4d4a380bed233e86ea957c173412b819fce00d66d369657a0778da5e4f244034 +dist/2024-06-11/rust-std-beta-armebv7r-none-eabihf.tar.xz=577763a4758c42bcf0c875d606a6dd538c07866e9a65c2b73271f0e9d0762830 +dist/2024-06-11/rust-std-beta-i586-unknown-linux-gnu.tar.gz=29f8e4dc5d88bfd66f600402022267ad85d4afcffd7142107d0fd28d0cae4cd8 +dist/2024-06-11/rust-std-beta-i586-unknown-linux-gnu.tar.xz=d030db59dd1788fa8438c52b9a8cbc6754eaf529de60fa4fa9693a687e644dfb +dist/2024-06-11/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz=df978c1519ea4b50fcec2ec18d2cd9a6b813982dc50f6442af9ce340b98a8c46 +dist/2024-06-11/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz=13585b56ce162922dbf03d0b6432a6af9b9059a664063a6adca957301c23b905 +dist/2024-06-11/rust-std-beta-wasm32-unknown-unknown.tar.gz=6365c96d363a345c4882ad47a3e9242f37c930a10bdc89d137e5e05b1b599351 +dist/2024-06-11/rust-std-beta-wasm32-unknown-unknown.tar.xz=480e07d3f110ff9f201315f5a01bf674ead911773241d80d9005772cfe4a3d88 +dist/2024-06-11/rust-std-beta-armv7r-none-eabi.tar.gz=0514e7022540078895be3000fa0405157bf1165762e77672f78230e62ef2c8ec +dist/2024-06-11/rust-std-beta-armv7r-none-eabi.tar.xz=659cec4d3e3f0783bf9a8b42a245b995e00b36a8ff04a9b6b458ace016d825d1 +dist/2024-06-11/rust-std-beta-i686-pc-windows-gnu.tar.gz=2c6b17e0fa22a9d528217fdb8231aefd21839e29babad0fd975fd3654a9706f2 +dist/2024-06-11/rust-std-beta-i686-pc-windows-gnu.tar.xz=c2c98b0c6b21618d5c49283f19ccdd3ba76159afd904c9683251d36317582615 +dist/2024-06-11/rust-std-beta-x86_64-pc-solaris.tar.gz=9433aed1f5f87157cf02d13e7ad375e9a2e9a260afb8419a05eeb1856069ab26 +dist/2024-06-11/rust-std-beta-x86_64-pc-solaris.tar.xz=2a39a792676fd60f47db90979fd56f3fee709d05d91b5c8bf4e0a01f39fed14e +dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-ohos.tar.gz=29e4553e9889c3e202f550042555b5c24cf52ce68979870fe33d9e9eaad37db0 +dist/2024-06-11/rust-std-beta-x86_64-unknown-linux-ohos.tar.xz=7d56ef2eb64550f95b2ec1e1cfb9f153e9c6b412dcceef35f76db44eccd4d8a1 +dist/2024-06-11/rust-std-beta-i586-pc-windows-msvc.tar.gz=da7c08348c6834793263e136747e0e3dfcd5cd805c0c6ca25323fab8a30de427 +dist/2024-06-11/rust-std-beta-i586-pc-windows-msvc.tar.xz=3d60ea1671800cb8f271f1869186ed099f35ee8171d23df7c6ef0c79b4567540 +dist/2024-06-11/rust-std-beta-riscv32im-unknown-none-elf.tar.gz=f3cfa8625f3586ddf48c301b7016d49d216324daaa3b76168a97f15506346c3a +dist/2024-06-11/rust-std-beta-riscv32im-unknown-none-elf.tar.xz=2e5388097f10a3640514d1a4040f6fec090e1c4aba0259cbdd900ead7fcdfd94 +dist/2024-06-11/rust-std-beta-i686-unknown-freebsd.tar.gz=cf38eeced5efe6139043e123b217a5c3f43bef3d86001a9a51fa21b012c9b468 +dist/2024-06-11/rust-std-beta-i686-unknown-freebsd.tar.xz=e078625da24308d78603400e96454e60606bed23f1132b64efbbbc815791d58c +dist/2024-06-11/rust-std-beta-aarch64-apple-ios.tar.gz=2f1dcaa67df6adacbb850bf17ed5df8258fd2087668d028814874fc8292bf420 +dist/2024-06-11/rust-std-beta-aarch64-apple-ios.tar.xz=2a11e0abb8b162a12f72825cac59cc0df993b5582d911bdf6b97252078887651 +dist/2024-06-11/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz=d7f4366df7f3599f031b9654d3c0bebd9c21ec530330283e6560cceccc17365b +dist/2024-06-11/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz=cb9e8470fd9934870cbb881f7611a0bd614416faa42cfa6096a1ed19abdf9cac +dist/2024-06-11/rust-std-beta-wasm32-wasi.tar.gz=5e7d7b535a7c1449a4dcac4022de279c7676852a7da3e63dc063595dd3fa1b54 +dist/2024-06-11/rust-std-beta-wasm32-wasi.tar.xz=c4db5ad4aa261a01ba3fd8a191c7ac6323e6134b2e17522b3fe0891a642d4c87 +dist/2024-06-11/rust-std-beta-x86_64-apple-ios.tar.gz=a27f3ff22b233c72673e32bf257f766ad8e11abfaf9f83fc7c3e756f785ee83e +dist/2024-06-11/rust-std-beta-x86_64-apple-ios.tar.xz=8592a2f8b1d6c3cab199cabe8b6e9a668b90bd77bf6a5e0869e2b4f3cc6d1170 +dist/2024-06-11/rust-std-beta-riscv32imafc-unknown-none-elf.tar.gz=ffd9d5acd9a2a33e6a699b850d1776521da443b52504ba77fc71d6dd7353e18d +dist/2024-06-11/rust-std-beta-riscv32imafc-unknown-none-elf.tar.xz=e92eec9cd55b37f135237c98cfe00c4ee23d2ef400cc3470deb8f1a0010e94c3 +dist/2024-06-11/rust-std-beta-x86_64-unknown-illumos.tar.gz=b1d2e427120295375eecc3d8fc647fe57e1195210d7d0243b20d156f173c3155 +dist/2024-06-11/rust-std-beta-x86_64-unknown-illumos.tar.xz=b97fe83bb5050460465c57c37456868368d0ce18e5134fedad07ae00df8b7432 +dist/2024-06-11/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz=25ad2d1a91456394427a5db691ebd903e56e60daa89d73598155655caf6592b6 +dist/2024-06-11/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz=88b0a7fb002922f78af0f8e9580acdfeb4c341c9fad35e53daf4d6073c73ab8f +dist/2024-06-11/rust-std-beta-x86_64-unknown-netbsd.tar.gz=b5e129c655bdc051eaa5aca8c8a577914100137a5bca753df619edd22889a1c2 +dist/2024-06-11/rust-std-beta-x86_64-unknown-netbsd.tar.xz=5fc42453c00c3498af2fd60fa4345c2047f302113666bf4a1d044539e3c1e66d +dist/2024-06-11/rust-std-beta-thumbv8m.main-none-eabi.tar.gz=afb1e9e37af72ba42b71f2a8a78ef7ba8593dbf8e35501e20a81e2aae204722e +dist/2024-06-11/rust-std-beta-thumbv8m.main-none-eabi.tar.xz=3b7f63b544a5e8aad8a18097b915e01c1fb880d48514bd1fefe3a5d4872eae28 +dist/2024-06-11/rust-std-beta-arm-unknown-linux-musleabi.tar.gz=bc151debda651b76573e77c489c3b9c2be4af0f632e0061e067889f21ab254fb +dist/2024-06-11/rust-std-beta-arm-unknown-linux-musleabi.tar.xz=f2f9225460e2e7fa7d28cee2dfdab483698c6f51b0eeafe49e2d8e44381a727c +dist/2024-06-11/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz=5418a327d270c603ddc903ae6061fcb6bc9bca14593f13fe10ab3fe0f4a656ca +dist/2024-06-11/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz=423e7f6fd6555c6d850e7317a8677d4778bc1270d3cfefc05614ef1acc61fd96 +dist/2024-06-11/rust-std-beta-thumbv8m.base-none-eabi.tar.gz=d30d36a72c1e874a7f9df7f805f5cea3617f912b5ad5e7163616b07022e0f7ae +dist/2024-06-11/rust-std-beta-thumbv8m.base-none-eabi.tar.xz=4afd0b4ab3787dcbb2b2f686d56844c6aa3af69fbd72d04bd57901fdd58b6333 +dist/2024-06-11/rust-std-beta-loongarch64-unknown-linux-gnu.tar.gz=0a9b8bd1d39d346a20803ede808815dbf0cd55d9167561c8aabe7db3300a3dda +dist/2024-06-11/rust-std-beta-loongarch64-unknown-linux-gnu.tar.xz=e36372611dd3cdabd1f32fb942c79bd31b5fc13946448cbf9a178ad5005dc7b5 +dist/2024-06-11/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz=2fe6221704614b167d837f52cfd439d9586e27ae82a34577659f86382c3192fe +dist/2024-06-11/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz=55ffdc97b8b6d309b1aa8443b49a5fd6133f5d7653069eb7f32625c1c0bf86ea +dist/2024-06-11/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz=87386371b64d4d922dc2fd01a0db28191748002b74a59437ddbab572b0d6fce5 +dist/2024-06-11/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz=6c1f9a6683a9d3ad8203b9d08ed829e4990675cb07fdc2214fc1f7970dee7e60 +dist/2024-06-11/rust-std-beta-nvptx64-nvidia-cuda.tar.gz=525a64f28aa0d757e82558737db3150ff90ecb885c3da8e0215370e47fb2ff23 +dist/2024-06-11/rust-std-beta-nvptx64-nvidia-cuda.tar.xz=af49d1730949036a48c05a147a3c67885326df5bea314fef2828e04433f33ef0 +dist/2024-06-11/rust-std-beta-armebv7r-none-eabi.tar.gz=eba00bc621ee0235444698e5bc67780bc93765e70bed11634a8555d8d645635b +dist/2024-06-11/rust-std-beta-armebv7r-none-eabi.tar.xz=236fe5262d76fd8ab7c12ab6c231608c7e704e5fc0d95210b47e0c86201d8a8d +dist/2024-06-11/rust-std-beta-aarch64-pc-windows-msvc.tar.gz=ac98cc34543e6faa0429cb9756d1b0d30dc13d8d8a44380b5feecc0e7d083e40 +dist/2024-06-11/rust-std-beta-aarch64-pc-windows-msvc.tar.xz=ed8fffbe7899a7c35e5985adae1b35b7eabd076970a8eac280e834b7877f272c +dist/2024-06-11/rust-std-beta-x86_64-linux-android.tar.gz=fa53ef5ea66f93f9fa46ac84d3cf6a16743b46a941910b092c9c71208a15defd +dist/2024-06-11/rust-std-beta-x86_64-linux-android.tar.xz=66b394876549c4faaf872d4103ee49ef59cfba5c78777c01ad1df00dc6babd14 +dist/2024-06-11/rust-std-beta-sparcv9-sun-solaris.tar.gz=63794d1cc16a541cc07b56870d86db2212c2c8e3f06e11fd9bdca90fd93aa56f +dist/2024-06-11/rust-std-beta-sparcv9-sun-solaris.tar.xz=4d419a3c4402c303b9437c7564e2bea79575623eaa0b300c2fe688d9d8f9db95 +dist/2024-06-11/rust-std-beta-aarch64-linux-android.tar.gz=b82186abf1bb250753a2640def13aa477b606e0601c5c1fdb25af0e37ea638b0 +dist/2024-06-11/rust-std-beta-aarch64-linux-android.tar.xz=57065c7b4f1f5d51d1366164b62b4114ab95f1a68cf551d12db8e9a2dad044dd +dist/2024-06-11/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz=bef7ed6391f906226a20f8d2d842fbc335891d7fb92f91b1c5873ce778d70675 +dist/2024-06-11/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz=d80843f5f25923605681541b285ebf6868c0368841efacd376aac4dc9f01d0ce +dist/2024-06-11/rust-std-beta-armv7r-none-eabihf.tar.gz=5b7d75f5f9294327ffb3c441b2e99fbc8402d1dfe0afaa20b2981a987e96801b +dist/2024-06-11/rust-std-beta-armv7r-none-eabihf.tar.xz=ab56542b3be354d6c4f19fefe90af2daf59507e969a4144b6553a900e37e2a4e +dist/2024-06-11/rust-std-beta-armv7-unknown-linux-ohos.tar.gz=7365d0ccde4cd000b44992c80cf0e48ead99c2a00e18cde4446d9401792606a3 +dist/2024-06-11/rust-std-beta-armv7-unknown-linux-ohos.tar.xz=69124687ee3ff876f557b7b21c8f78f7015c5f3b6537b65480613469772e00ec +dist/2024-06-11/rust-std-beta-i686-pc-windows-gnullvm.tar.gz=d1307c1b3fab3dcb6b1e5cfd5c30e1ef1c7b7e1e831ccecb00ce35bc2c1d8b9c +dist/2024-06-11/rust-std-beta-i686-pc-windows-gnullvm.tar.xz=8685907906feb9e16e34d4ea72e3ad338df9d6407dca6da81739b100926efcd2 +dist/2024-06-11/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz=d8c1c3d800eaedcf23c25f436e30892df1938a618200f0f041fc1921a5b517ac +dist/2024-06-11/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz=53493d039721d5a2f54eb49ba7e16a205dd846bee559519182ca0af30111d182 +dist/2024-06-11/rust-std-beta-x86_64-unknown-redox.tar.gz=3f9842f81f45cbf35fd923ea69f1c88696070edfec0dcecac286519820741078 +dist/2024-06-11/rust-std-beta-x86_64-unknown-redox.tar.xz=73bc6b9b72bf6a9ba7dabe63b7a7d426eb47716466000d05802ef78ccb9128e4 +dist/2024-06-11/rust-std-beta-i686-unknown-uefi.tar.gz=c9954b477c329c3706891f77e9163d98aeadd96b1aa8a71f1eac20f316a9269f +dist/2024-06-11/rust-std-beta-i686-unknown-uefi.tar.xz=88876cb6cf03707065fe172540e82faaf5dd55332c939891253476d4da3f8a00 +dist/2024-06-11/rust-std-beta-aarch64-unknown-linux-ohos.tar.gz=e731faa24568e97821c5960229dd9a489ea7ead964a5aa052a6db322bcc55c18 +dist/2024-06-11/rust-std-beta-aarch64-unknown-linux-ohos.tar.xz=47b529a6e19c9d609c41809fd62906c34f23a5c515bf62d91e88a87cf7cc0c85 +dist/2024-06-11/rust-std-beta-loongarch64-unknown-none.tar.gz=c6150b3b7481f2396b305188a16c4ce2206c440e9d7639446c76566d00dc5159 +dist/2024-06-11/rust-std-beta-loongarch64-unknown-none.tar.xz=e641747ef4ae46e4d0a8a3050d00b1b8c21c0f25da53a54de8e540a9953468db +dist/2024-06-11/rust-std-beta-wasm32-wasip1-threads.tar.gz=37d141ba5beeaefd08834154f66219df734c379c9315c094433aebb75b82bca7 +dist/2024-06-11/rust-std-beta-wasm32-wasip1-threads.tar.xz=2045a0b765de96eb17e18d8028ed2e553cc998fb71a4811e3d848db320f6b9a3 +dist/2024-06-11/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz=82de12e6a2b01c0b98c83b4264a4e00ceefc424f673c97826bb28729c3f31382 +dist/2024-06-11/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz=c97b8a57ed8eefd60bbfe2df54121c548d9e271d9691def843185bac9d82b703 +dist/2024-06-11/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz=6903175fa7cc3fdda5545ce11f4cc504e4183e56c626e25ff2b0d20a4f5190c4 +dist/2024-06-11/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz=df06eee91ba57ff2c0a779da5d746db02f682514b8c03de875f03c96d05188e5 +dist/2024-06-11/rust-std-beta-thumbv7m-none-eabi.tar.gz=d7023edb8c723803e114e5b8ed316a6839e0c37d92cf6a2a09b767ee5ea2b7b9 +dist/2024-06-11/rust-std-beta-thumbv7m-none-eabi.tar.xz=41e19659bc0e0445ebafc49740579f0ac23a9af8e233c529ba2132f3d111faa9 +dist/2024-06-11/rust-std-beta-arm-linux-androideabi.tar.gz=42c0a6bfed8ca53b8833dbee78f2eab92bee061c42a4d629e870c8e655bc3728 +dist/2024-06-11/rust-std-beta-arm-linux-androideabi.tar.xz=5f85c99315a18bfc7fcc3f0caa7218ddc3b6548eb04be0ded835b5c45ec2ae89 +dist/2024-06-11/rust-std-beta-wasm32-wasip1.tar.gz=97abd2b9a68973263da0116eb5a0b9ead2145c1550f3190d02b35606bb9dcf58 +dist/2024-06-11/rust-std-beta-wasm32-wasip1.tar.xz=4ba0ef9b136761e739ab8ef125ffd993ccf71161d69e7640d94de6d5df601dff +dist/2024-06-11/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz=886562504ee3e73ceedce6cfcc8d128f22a80f0a33c84b9c29920d7760a8a208 +dist/2024-06-11/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz=a8f28e60aa1938cdc7b7d46a1fa770d587be9534a291f5367a80a52772a0b458 +dist/2024-06-11/rust-std-beta-x86_64-unknown-none.tar.gz=642e7fbeaa76403ccfaf6c3c1bfaa8b94ded7311abfece44c863df231c282406 +dist/2024-06-11/rust-std-beta-x86_64-unknown-none.tar.xz=b1441ef88a1a97ecb6d7b674b571beeefab9f59fc8799d9e63b8a01078042f15 +dist/2024-06-11/rust-std-beta-i686-unknown-linux-gnu.tar.gz=fba54c97da35d0f11af99dca7cb0f43784da2104c461518149974da1f2248301 +dist/2024-06-11/rust-std-beta-i686-unknown-linux-gnu.tar.xz=e7acfab1f7bb8b2c2a0f7558b813f627a3f6b6dadcd05f62c5895f753c2f3ffa +dist/2024-06-11/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz=e4d5ec39520d069609f32c4dc7dd4114623bcd112a729529a096c992fc19f61e +dist/2024-06-11/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz=94d508cc6c86516ae5e3bba9366cb466635a1e3f41fd00ba6b1a44a87175fea5 +dist/2024-06-11/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz=76e17967c09311f9e0c684531f21588d5bfbbdd4c707e454068df240fcfa56ab +dist/2024-06-11/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz=e627a81855b3d93497b071138917dcf6382b60793a4a76073769537531d01789 +dist/2024-06-11/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz=aa5a1b430e31bd5f912d378eab8447ecd920a5840c03b01799a410d86a7c4850 +dist/2024-06-11/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz=20f6fc50caaff58aa2883696c61031e9cec5cc24aea94026cf9b84a7642c4d31 +dist/2024-06-11/rust-std-beta-i686-unknown-linux-musl.tar.gz=62d2daf4c1a0a6b4630559c64bc22ceccd658ea0e9e74ebf58a9b791226964fd +dist/2024-06-11/rust-std-beta-i686-unknown-linux-musl.tar.xz=34681e4cac9a6e18f1952f51d6192c8b71b970a0edaf0a06766e1a576a7898d9 +dist/2024-06-11/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz=da45a5c9a7c7c1db32d6664358699ceeb27f59247862190a93ac203e5f49f116 +dist/2024-06-11/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz=f881c61a8ed04f02576374a82f28505be2ad4b694254503d30c1ab415b6b5a74 +dist/2024-06-11/rust-std-beta-i586-unknown-linux-musl.tar.gz=2056b8d9d54baeab1fcf7dcf65916645f8404413bff60851fd97108b7822a54b +dist/2024-06-11/rust-std-beta-i586-unknown-linux-musl.tar.xz=d06fd5dcb976c5331185d308e2ac0e53597275a57603aa171a753b3d005f0667 +dist/2024-06-11/rust-std-beta-armv7-linux-androideabi.tar.gz=27fb1ba34b2331810174f88d0e75075d6af26ef3fb7cd3ec820408e819f36cc8 +dist/2024-06-11/rust-std-beta-armv7-linux-androideabi.tar.xz=f44ff932cecc16cb292a6149ac42fb440b5e4a8a6b56f55634d9031002eb56f7 +dist/2024-06-11/rust-std-beta-aarch64-apple-darwin.tar.gz=3ab3178d3c1d4b9908dc2d0b6236c147e8f787f323c0c02fb924e2d692ec839e +dist/2024-06-11/rust-std-beta-aarch64-apple-darwin.tar.xz=4b67844f0413cd1b0fde9702542eb2ce297020c29531230c37d3e2f65d56b0e3 +dist/2024-06-11/rust-std-beta-aarch64-apple-ios-sim.tar.gz=317035259b48845882a4561bd1c61108331ce68231b3362fa6e9b0a323ea3fc5 +dist/2024-06-11/rust-std-beta-aarch64-apple-ios-sim.tar.xz=04585ea6b3e53cdf62372dc64f00237d26991a2f7ca6ce06f921065957c2ffc8 +dist/2024-06-11/rust-std-beta-x86_64-unknown-freebsd.tar.gz=048684f3b437110dee704d8ef58f166806d69504a0fbf661ad71d19575cc8436 +dist/2024-06-11/rust-std-beta-x86_64-unknown-freebsd.tar.xz=43762fd9d2cf4dda5e8b64dccbabf5637dde37685530eceba060faeba8212b62 +dist/2024-06-11/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz=40b3feff4834beb31125cffff479288adb2cc8a2d9b8963943907ba2b5478589 +dist/2024-06-11/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz=f088ad7f9697570c251655adbf6921341f74bb8deaa445cdb0d15e5ab556699c +dist/2024-06-11/rust-std-beta-thumbv6m-none-eabi.tar.gz=94b65456fa327c6d8acd59f1fd004feea8572374e8eadc022338de75c45a6c03 +dist/2024-06-11/rust-std-beta-thumbv6m-none-eabi.tar.xz=9ca368829aaab88a93abb580a0ecc1195ce5fa3877bd556c856ab52af51095ec +dist/2024-06-11/rust-std-beta-x86_64-unknown-uefi.tar.gz=1a9892b1efce5e6aaa361675e008c623c47056a64deb73246c4211f5194f31a9 +dist/2024-06-11/rust-std-beta-x86_64-unknown-uefi.tar.xz=e9a2df6f754ff4c1ced354250de51939efb1e003843f1d0d8962c2a25e7c0f46 +dist/2024-06-11/cargo-beta-x86_64-unknown-illumos.tar.gz=d6f794f092c47e97e822dd6526ac2622f8a7b3a3cfa8b8462b3378eb16dc4801 +dist/2024-06-11/cargo-beta-x86_64-unknown-illumos.tar.xz=bcf8204f4750c6ea9ff5667ae07de7d06e5b68e78176adb79fc1bc433f7d90c0 +dist/2024-06-11/cargo-beta-x86_64-unknown-linux-gnu.tar.gz=27ba1abe717de9e96b802c8026a17a6b5f5dc728ba4813c705a03d54c234be3f +dist/2024-06-11/cargo-beta-x86_64-unknown-linux-gnu.tar.xz=55cbb9abf863c05f13049bc59e99ea3e64523f0193ba1dd155b21cb732db50c2 +dist/2024-06-11/cargo-beta-i686-pc-windows-msvc.tar.gz=ceb0b610395fb2519bc6207b7916f402a17fac9685cf8b5c2ee354b350b396ff +dist/2024-06-11/cargo-beta-i686-pc-windows-msvc.tar.xz=73f1cca9028510331477a0299e214d50a3b9ad02ac035d056acce110f52a1463 +dist/2024-06-11/cargo-beta-s390x-unknown-linux-gnu.tar.gz=eb0dbf227a1ad39436af2334ddb6e90831881c75c24855e9c040bcbb6ce8d876 +dist/2024-06-11/cargo-beta-s390x-unknown-linux-gnu.tar.xz=1b95d58699e8f534b5d93ad02c4281d8e791e8c266f10820de887fe13b1d88c6 +dist/2024-06-11/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz=b078cbb4e2e18b6da3618a1048b038c52bf37a398618179b1c03f25d5e154ce0 +dist/2024-06-11/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz=80bd1e79864e21ed914a6a2b8ee5d66867734252002789c6d76c3f7e82133eab +dist/2024-06-11/cargo-beta-x86_64-unknown-netbsd.tar.gz=e2fb5140e90588e6ab158352e3f198ea71107a1aa01785f692d5ef64925af58d +dist/2024-06-11/cargo-beta-x86_64-unknown-netbsd.tar.xz=6bf7fc08f593ef8847b3b14a33783fb3c54c542fe61a8a92c0e462a1bbf34b9c +dist/2024-06-11/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz=6cc72fe175632e68fefaea683c0569e9fe8f036488154e4f1ccd6a3135479cfa +dist/2024-06-11/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz=b27004517d35019ead69bcd1a09c81aa20d2b00b57ad42655f1f434f8d66eed3 +dist/2024-06-11/cargo-beta-x86_64-pc-windows-msvc.tar.gz=e5d5945884554970344d16e90f9eed0dd60f61ed57e48b40fac2ee1d7c035475 +dist/2024-06-11/cargo-beta-x86_64-pc-windows-msvc.tar.xz=421ee0fca436d4d4d3bcd03c242f9312fa620da854b906618a1d8ab27979d609 +dist/2024-06-11/cargo-beta-aarch64-unknown-linux-musl.tar.gz=7406ba1c14e53da0a9ca696dc6c90fb816c072734f569c01f49c6b8f7d542d81 +dist/2024-06-11/cargo-beta-aarch64-unknown-linux-musl.tar.xz=917a2e63fcf03be81375143692306afb54fc2addef9f5508dd58fc1729119752 +dist/2024-06-11/cargo-beta-i686-unknown-linux-gnu.tar.gz=bdbd9c4e2808d9dd0acd219c3ba9972634d73fc4f177468bc196c66be620df65 +dist/2024-06-11/cargo-beta-i686-unknown-linux-gnu.tar.xz=e64d3405f99f8ad74984b81238a088b250be4e079e2c05aa68be1cf84f04154c +dist/2024-06-11/cargo-beta-powerpc-unknown-linux-gnu.tar.gz=bf7c374c02676f9e5a50f4964e6deb4d3aab19ecd66c4d246de0c9a06ebd23bd +dist/2024-06-11/cargo-beta-powerpc-unknown-linux-gnu.tar.xz=52e7907ca99467c71429d03012a3f6cdd95711b9cf002d3aa06ba25b3e53d2b1 +dist/2024-06-11/cargo-beta-x86_64-unknown-freebsd.tar.gz=bd9aace135befd19104b193e09bf982c79c8cb9cce301ff709cde2ee2dc82821 +dist/2024-06-11/cargo-beta-x86_64-unknown-freebsd.tar.xz=c0d2c3a2e56a1f43f62fb73e41b07def93baa278172d822a2a33f571d54f0463 +dist/2024-06-11/cargo-beta-x86_64-unknown-linux-musl.tar.gz=61526c6e4f0ce1a57d4831ac67252cf5a6cef3c106c6953b329bc0aa9c685e6c +dist/2024-06-11/cargo-beta-x86_64-unknown-linux-musl.tar.xz=82bcddea6b3d976e7dee07afe7f805f5a6ff66f0721d281d2c896f352f395c89 +dist/2024-06-11/cargo-beta-aarch64-unknown-linux-gnu.tar.gz=70c5bd18c756123f033a7cec027631f71d7400cb4d0c85d7602a8a6309379070 +dist/2024-06-11/cargo-beta-aarch64-unknown-linux-gnu.tar.xz=f8a6c7dd8ba408a5fe623a0ca1d2f8bfe287b13bc09b1d8ea636ebbdec06c557 +dist/2024-06-11/cargo-beta-x86_64-pc-windows-gnu.tar.gz=fe26e842e6e4186437ea679cc75cd37d70a98b1ed0f17ed9ac876aa0501ebd5d +dist/2024-06-11/cargo-beta-x86_64-pc-windows-gnu.tar.xz=f3fcb2b698736b3df87c06ba829a1f09c218e37b51ff1c59f165aaf8078c619f +dist/2024-06-11/cargo-beta-i686-pc-windows-gnu.tar.gz=607f623f30e25541d9e6c8412bffcb9c16f7180250a244dfe1e2606edd45db42 +dist/2024-06-11/cargo-beta-i686-pc-windows-gnu.tar.xz=604369c997e05719d0fae92f7904e6b66f662bb75add1d836cb100e0032526a3 +dist/2024-06-11/cargo-beta-aarch64-pc-windows-msvc.tar.gz=de283125b79d17f4cf24bb608819177ac1637525b8c7e67a66e798f0c0b1024a +dist/2024-06-11/cargo-beta-aarch64-pc-windows-msvc.tar.xz=8228a47ce003faf1edb4a10cf800d3316ba421a97176c97f6952743f7eddf734 +dist/2024-06-11/cargo-beta-aarch64-apple-darwin.tar.gz=8525a4015fa8078921f8bc5f67012c0d5747b7e7111079bffce79f2b696fbd4a +dist/2024-06-11/cargo-beta-aarch64-apple-darwin.tar.xz=038c700f1b0ab543bb765a4b86638fc7eb1e44257a19d5a5457fcff4fc08f280 +dist/2024-06-11/cargo-beta-loongarch64-unknown-linux-gnu.tar.gz=16c75d17531e10f1bb9e95c79d8c3aa05549db3029663c69d2f56b2d2f3214d8 +dist/2024-06-11/cargo-beta-loongarch64-unknown-linux-gnu.tar.xz=113d97f781bf8d54f457e07fbe3de1d3a0ce0a1f8054a652083b67b81f4cb642 +dist/2024-06-11/cargo-beta-arm-unknown-linux-gnueabi.tar.gz=7856fc9731c3dd7b360eacf422ce358acb2d33a7fd8b16fa190dc77cd9bdb3de +dist/2024-06-11/cargo-beta-arm-unknown-linux-gnueabi.tar.xz=85c930785fe9857458c9256c5a2849ae223c5dd162bd20ccc8021b2d2ff0a6ba +dist/2024-06-11/cargo-beta-x86_64-apple-darwin.tar.gz=aa4aec2cf417a7446c2990d03cfb3dcaab9d99f5ac249cbb955a59a64b5b7b0f +dist/2024-06-11/cargo-beta-x86_64-apple-darwin.tar.xz=01d4bd8dd8a58f16ee31df775d0d7046ce671dc86f8907514e3713a62aadbb12 +dist/2024-06-11/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz=6e5c68d0665add368a6574528184dc1d182e7d72b8da0827b69eb9f9aecbc4ac +dist/2024-06-11/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz=180a6148f5c5be222d4e2626eed9cd7de2e0f62d0b456e520d1ab7c4931e1f4e +dist/2024-06-11/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz=6af892aa858f35708bf9db322abf35967b1e92725e05f6974e1d9ee3cb4c7b84 +dist/2024-06-11/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz=40ffc9713504366978bb6f2eea12741400164318945df22290c7b669d3b77878 +dist/2024-06-11/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz=9989b0189f7a4c5aa39c39c78be04e7291cc1358ebed597636f404f56980c199 +dist/2024-06-11/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz=8c7d7dc2530f25cc771c75d454fd11f3e94c7954bb869e43736860be1f253d2b +dist/2024-06-11/clippy-beta-i686-unknown-linux-gnu.tar.gz=94505902c45770c45a89ea12759bf46cd97efed657b43322d5dd09c13ed26331 +dist/2024-06-11/clippy-beta-i686-unknown-linux-gnu.tar.xz=14830f6fa92b0d814527a9aa5575d0cd529e76dbe2481533b819c735470ad684 +dist/2024-06-11/clippy-beta-x86_64-unknown-netbsd.tar.gz=5cc5f4202003b940a6a62c2384c749b741a9f6d62461ba13a6179464f6c3e6ea +dist/2024-06-11/clippy-beta-x86_64-unknown-netbsd.tar.xz=01f4f0ef78f17e4991ddf3d974ef7b1dc9d56702ee2d65ede4918e9db99ec222 +dist/2024-06-11/clippy-beta-aarch64-pc-windows-msvc.tar.gz=ee6b64a7a0fc8d8482c38d2a263f9a9ed0b9462e6bd748a72e33c5cd7dac015b +dist/2024-06-11/clippy-beta-aarch64-pc-windows-msvc.tar.xz=f91457f5dccfc81a601b075da340bf521e3cc17ba05d12f90c31c7f5b215a7bf +dist/2024-06-11/clippy-beta-aarch64-unknown-linux-gnu.tar.gz=7da546ff13b3e340f2ba22e6995566cb26e487271e3f1b68b3b7dc0bd5122623 +dist/2024-06-11/clippy-beta-aarch64-unknown-linux-gnu.tar.xz=01807b62c65fe33acd05d10191cb16ee1bdeb1bd7b410a9c0cfd8f53006624da +dist/2024-06-11/clippy-beta-arm-unknown-linux-gnueabihf.tar.gz=64f41430de84569a7b933db7f0c573fecf99f5d0c6900e35598c94a433c0296c +dist/2024-06-11/clippy-beta-arm-unknown-linux-gnueabihf.tar.xz=b30a73278552ddf511ae82620f14ec7324f281582fa4fa9df3fe984510835aa3 +dist/2024-06-11/clippy-beta-loongarch64-unknown-linux-gnu.tar.gz=e6b0ac4bcec2871d2a50e644733399e684d7fa86798131243c4022cbe2ee5222 +dist/2024-06-11/clippy-beta-loongarch64-unknown-linux-gnu.tar.xz=edc629f04503ce88443ca6cce0b74b5833ce50f428e00a02b544f422577abb1e +dist/2024-06-11/clippy-beta-s390x-unknown-linux-gnu.tar.gz=ac52f8b4a1b6332aca1ad2363195daf571f7f1870ffa6237441dc7928a408167 +dist/2024-06-11/clippy-beta-s390x-unknown-linux-gnu.tar.xz=96b366fa989d2f739a92ee03bcd141b666b942e0b26922acd620e0fea6d44d8d +dist/2024-06-11/clippy-beta-aarch64-unknown-linux-musl.tar.gz=8ab584ad05a7836bebb20be8d84b0dd3a29d1823dcee3897abde3a47a10e582b +dist/2024-06-11/clippy-beta-aarch64-unknown-linux-musl.tar.xz=e7b669a2936a22ed6d2a36b70a6593657488e5ed8d00a911bf33ce51c4c0cc51 +dist/2024-06-11/clippy-beta-x86_64-unknown-illumos.tar.gz=419ef233cfdbe9011d74796e8413a8b6c1f8e6281248969eaf7c9157542c1b70 +dist/2024-06-11/clippy-beta-x86_64-unknown-illumos.tar.xz=673a2f16268fba4d9bf7dca2c0758f3ee60f9754cdcefa36032b8140e6fcddbf +dist/2024-06-11/clippy-beta-i686-pc-windows-msvc.tar.gz=a3c3bd3b90c951ce404a26a59421f00a03e0dc3351d9dd2876346ff096b7361a +dist/2024-06-11/clippy-beta-i686-pc-windows-msvc.tar.xz=c3e7c1cc91f48003e1b68fdf753011711ee516bc2ff8c7227d427dd8ee98a72e +dist/2024-06-11/clippy-beta-aarch64-apple-darwin.tar.gz=ad7b4ac9cfacd860e03f8a41da51b4d279d76b7f3f2f8ac8a048a45f89a7ed04 +dist/2024-06-11/clippy-beta-aarch64-apple-darwin.tar.xz=ca5d2d63b8d7cae00c86383d9075b2292a28d1a85864877a6c8282f5596631f6 +dist/2024-06-11/clippy-beta-powerpc-unknown-linux-gnu.tar.gz=12820c3e8b952dfa931783b550f882dd96c48d3b776fa9cdb29b02d66ad14d96 +dist/2024-06-11/clippy-beta-powerpc-unknown-linux-gnu.tar.xz=394873c6b2499ebc385a11b512c7378b95478a0b6f530c5b313fe86b65a18eaa +dist/2024-06-11/clippy-beta-powerpc64le-unknown-linux-gnu.tar.gz=8d9353baa4388d02d89b42403e07d6a61f314fc975b8fcd737d8f4edb0ab027e +dist/2024-06-11/clippy-beta-powerpc64le-unknown-linux-gnu.tar.xz=3cf0511bc5b14619dd6ca1cfeed003effa7fbfecb42fc096a45a211d1ccf419a +dist/2024-06-11/clippy-beta-x86_64-pc-windows-msvc.tar.gz=cfae39e5d5cbc817aa3ba892822fe2ff122df8a648106aaf0ec18b1ce1a02632 +dist/2024-06-11/clippy-beta-x86_64-pc-windows-msvc.tar.xz=1f1f0596bef154eb3fb4e73993794850f8ad903f4dd0ef9697aa8d5b4d6ec2b7 +dist/2024-06-11/clippy-beta-x86_64-pc-windows-gnu.tar.gz=0909caa93377b2aaf2a05a345e8cfea2c69dc27bd7f7d7a9515576e90cd0aad3 +dist/2024-06-11/clippy-beta-x86_64-pc-windows-gnu.tar.xz=9125bfbaae2103f176b1e1b444fd38c7e74d3414536370037ffc6a85d8567551 +dist/2024-06-11/clippy-beta-arm-unknown-linux-gnueabi.tar.gz=3721470fcc17f8d37c66281bb66c65a66181c80775571520fcc2dfdb583dc640 +dist/2024-06-11/clippy-beta-arm-unknown-linux-gnueabi.tar.xz=79ec48af2925aaad12be405fb72dc9bd9e2ca89fd3a6017111020570ddeb6585 +dist/2024-06-11/clippy-beta-powerpc64-unknown-linux-gnu.tar.gz=d957f503dd4d9d228b6a445eab1183dbc6509a49ccbd475d25143cf1dd28f03d +dist/2024-06-11/clippy-beta-powerpc64-unknown-linux-gnu.tar.xz=da7ec767b4a85fa327c957888b228671b2311286043165926942504239008d3b +dist/2024-06-11/clippy-beta-i686-pc-windows-gnu.tar.gz=51ff631084254be43d79d7791000a14ad9f11239e42be73f48383f774109c2cb +dist/2024-06-11/clippy-beta-i686-pc-windows-gnu.tar.xz=52079acb4f908150d207a838cc7d3c15a725c1d61be35d0d0b2ed85b3386f3fc +dist/2024-06-11/clippy-beta-x86_64-unknown-linux-musl.tar.gz=99a534de9e5fb552a4e7a10eca2645ad260a723f42e15df3b4e2bacf82e90348 +dist/2024-06-11/clippy-beta-x86_64-unknown-linux-musl.tar.xz=7f9e6da187bb71c384f5040298c43bd303fcbc5e3034dd7fdc7b251c5d7b3a34 +dist/2024-06-11/clippy-beta-riscv64gc-unknown-linux-gnu.tar.gz=acf05b62fc219ddc2fc4e4bca2bc4c2cca24398d6276bd7e7d9f8504627094c4 +dist/2024-06-11/clippy-beta-riscv64gc-unknown-linux-gnu.tar.xz=c737aaf9c4bc3a743752b13159902fc5a8ce746f44221799bbcbcb1f9d04dc4c +dist/2024-06-11/clippy-beta-x86_64-apple-darwin.tar.gz=422e97d9081e66a46ec3f4c2a7ccbf9b6443c7b343e0a2e90845f233536f545c +dist/2024-06-11/clippy-beta-x86_64-apple-darwin.tar.xz=c9d02836c9d0e48684f0a343d6935848ec75e1faaf7007a6a3e0718b71e77f7b +dist/2024-06-11/clippy-beta-x86_64-unknown-freebsd.tar.gz=f207fbfca76eb22f7097eb98ccda1da513fed417371a391e43e8d19fad9b76b4 +dist/2024-06-11/clippy-beta-x86_64-unknown-freebsd.tar.xz=61e39b0aa88a1d8e131a461cb3cebd05154c261058b735f29dc6ed603464b5a1 +dist/2024-06-11/clippy-beta-x86_64-unknown-linux-gnu.tar.gz=c68d27b08f9699c7c2cff9e9bdb7485991d8445074e8b84c2e2222fbff209f83 +dist/2024-06-11/clippy-beta-x86_64-unknown-linux-gnu.tar.xz=d5961f82b9688b9c1ccdcaf578b8325e8ac1e0cc2ffa02131fcc3281cc88dcfd +dist/2024-06-11/clippy-beta-armv7-unknown-linux-gnueabihf.tar.gz=12ccb5b1d82ad22292b337c49d1c5b0c0d584cfb4e649faf6c53471915df2473 +dist/2024-06-11/clippy-beta-armv7-unknown-linux-gnueabihf.tar.xz=80fa7cb902135006046c10db64d9060103dfb1452e8e0ef256f8344fe0a9c8c5 +dist/2024-06-11/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz=c08499ebec188f67477d0c4eac2e172b59f6d66ad4becb36330b5066af6547d8 +dist/2024-06-11/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz=b3f29c79fc8bd64a2e62101468bbaa28338f444839659d4f99df34ccccad5eee +dist/2024-06-11/rustfmt-nightly-aarch64-apple-darwin.tar.gz=4866e6d478424fb1c01b2b98678107b9c89e67a2cce3db49833bf34007f868b3 +dist/2024-06-11/rustfmt-nightly-aarch64-apple-darwin.tar.xz=e8e6fd846f70a99e6fad735ce4f4c8bca5f6b5f2d63364c2f8a7df85c13b63f4 +dist/2024-06-11/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz=f14077408a763f0fa6218ed97d7697499c2bded289a5953336c727e2de93bd92 +dist/2024-06-11/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz=27324f62ecfdeeb4a66ce3c62219c491f04ac3822305ea771df0381a5f4e5418 +dist/2024-06-11/rustfmt-nightly-i686-pc-windows-msvc.tar.gz=1147008dfa30891e264bf5f469c9528fe47bdfc729a12215e5a985179513cc66 +dist/2024-06-11/rustfmt-nightly-i686-pc-windows-msvc.tar.xz=95fd945a6c9bcbc9549913906a8f2de20adf54b84b82fe8d981dbb5b767f0cfe +dist/2024-06-11/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz=4f423c5ce278204dce918ece2af84bdb55429b7a11f630de95a1516e7b113f53 +dist/2024-06-11/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz=c86fe94c9b6cbc56285478ffa715877d234af19f1b58b3fd4b2af23a0267b657 +dist/2024-06-11/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz=f1200f077402474049f6b817688435db0ebef7fb52ef645d5b5d2f7ceea52a08 +dist/2024-06-11/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz=a0d801ec3aa93389d5c6ae2a95be25ba3a50f53182c0a84a00c38a2f708447e4 +dist/2024-06-11/rustfmt-nightly-x86_64-unknown-illumos.tar.gz=d4e28ad9b054cdd19fce6bbc37dc86210b0536aee5c23bca53a6bf1340fdb5bf +dist/2024-06-11/rustfmt-nightly-x86_64-unknown-illumos.tar.xz=b0958ab975026e09ba921234353f5a81b3227d23c10ded4be4fe37831e6abd12 +dist/2024-06-11/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.gz=f1caa15f16aa1c93ee783bf273d0ae6a7abd0b38c93c717f2d49af5444cd4e09 +dist/2024-06-11/rustfmt-nightly-loongarch64-unknown-linux-gnu.tar.xz=130687b9de22fb1d0c4c845cff5475e5959278718e3b6a1af7f1b9e8869b527f +dist/2024-06-11/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz=32e3881c61abfff7f9c006083045ffa5765c0b3a5a6ec46607e5428423ab76da +dist/2024-06-11/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz=c1859b8a44306b5b3ca27b9226bbf351cd3ff3cacb4faf61200d7baa76009367 +dist/2024-06-11/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz=4f5c23ba3426719c39a7c767be8db83382c84ff7798c5194aefbeda821462269 +dist/2024-06-11/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz=18d9a2084cd5e55792f3841fc7d6602bd4a973726662b516289385b7908ffa4c +dist/2024-06-11/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz=df5478f63a49c55ffe9cf62a1882feeca019290c0cc9a9ed5a4122b8aa1325cd +dist/2024-06-11/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz=f54c3b8febfa4023a0334dfeee59d75a44f5c1d3a02d2d57959473d6ad51205a +dist/2024-06-11/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz=dd2d83a7bcd45a0d5de312a2a4a28b2471a9597d318135214b45a69e1e449f91 +dist/2024-06-11/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz=02a8ada9d737e115033d26b0691f2ef22428ba2f67b7fbab7c4205df8162a96b +dist/2024-06-11/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz=20bde0cb3ddca790c0d126d5d590b9a35a1c8631b42fbc2d4e85cfe5aa880d04 +dist/2024-06-11/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz=dd29e278f79ce1be14ad44d61039e5a6a63e0e597c4736ab153beb758ad37fb9 +dist/2024-06-11/rustfmt-nightly-i686-pc-windows-gnu.tar.gz=09d98f5c61def16b62b980a6cef3b1b3704cde5c0ad2fdd7d23521db410b6b8d +dist/2024-06-11/rustfmt-nightly-i686-pc-windows-gnu.tar.xz=ef43e61c2d3d90b09494a8dcbb5d2375223fcba6078f2bfb02ea43750b1a76a3 +dist/2024-06-11/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz=80318fdf7d55c71beb8ff1434b79776c59008f5d62c63530da9f19a67cc18484 +dist/2024-06-11/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz=05ae6d4f8ed07cc75b333e606af14f239978abe197b70409a5f2aadc7de43a4d +dist/2024-06-11/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz=a079a478e9a89c3a24864b44670bdffcebc139bbec9c54e7aab6394c08bcaab4 +dist/2024-06-11/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz=243c2d7c23e8aeddf9729e1f7f9add6f88f50d012efa02644030110cba111713 +dist/2024-06-11/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz=76b69b4b25e8c0d47b80491bcbb724a25c02d95e7f9cfe7a9c3c3570c24363c1 +dist/2024-06-11/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz=027e075c94f53403f03a4fb29d4575928e1cba141fc0713c93edaa4ea5917e42 +dist/2024-06-11/rustfmt-nightly-x86_64-apple-darwin.tar.gz=b34d3f33e7ae81a5a95927997658d738a3d39db01408dad2d9fa72efb539c73f +dist/2024-06-11/rustfmt-nightly-x86_64-apple-darwin.tar.xz=7efe129502b732219d1d24d59584b5ad002e7dc5288e047b06bbc81fab36e054 +dist/2024-06-11/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz=231f11ec1c6e4f57556aa32226d22328656d2ceab075cff67c8ab0a1ec32a823 +dist/2024-06-11/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz=b1cd7aa45a516df3a68814ce52753eeead4134943d610449456a30c682079ad0 +dist/2024-06-11/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz=3fb86c9281ae8203e1119bc00e8001afe7e894360adc791a9a0ca1d50e6a9032 +dist/2024-06-11/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz=6380fcfb015a40c4605ff18f8ba54c3be9a8e6a0115ede378565d9cc9943c627 +dist/2024-06-11/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz=f3ff749dedcfb259a4b2d66ae30245caeb79fdec3b41a3bb4f65d50b1cf18120 +dist/2024-06-11/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz=1ca50ca8ff36df7433a4ab4c8a63c3c91e214f6c7889843edfb9120c1f4f68d4 +dist/2024-06-11/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz=a7f5f171dfef5b67498c93354d9dc5a7443d69f3f9bc0bc56b660d1450e9b7a5 +dist/2024-06-11/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz=dff02f7a7e9ae58c41faf4c5731862d11fee06d17099c1ed18863219a4a82815 +dist/2024-06-11/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz=71dc189c1d29d3fec849b47f64deb831569515e56821511b1221983eb9ed99a9 +dist/2024-06-11/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz=17736f80644c8cdf8239940812962e6f55c1924896f79bd2581d20582a9c752a +dist/2024-06-11/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz=b77e4a1e42c7169d91e268972a9195f7c8494e5cffa5fc71720df799a4eaf74a +dist/2024-06-11/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz=8cf89f982aaa5559f2cd1721edfdc25f5e99518c418e1a6818c566d668c8f842 +dist/2024-06-11/rustc-nightly-loongarch64-unknown-linux-gnu.tar.gz=7703c27800aa2d871624563380596a18deb90081e38cdf6c05cab8e794e5805c +dist/2024-06-11/rustc-nightly-loongarch64-unknown-linux-gnu.tar.xz=ca9dcda51c8945db32e004424eaf26d4f541a51a78be87be0063e5a0b06fdd4f +dist/2024-06-11/rustc-nightly-powerpc64-unknown-linux-gnu.tar.gz=59a2173462baaea0cef3e0f088b4e6ef64e2fe469d1833798d92e844f0126902 +dist/2024-06-11/rustc-nightly-powerpc64-unknown-linux-gnu.tar.xz=0cab2fde15bbfab741eead0b084886f8b640aeb181251368464bd833fd08df73 +dist/2024-06-11/rustc-nightly-arm-unknown-linux-gnueabi.tar.gz=8473ef60ff1d5c7aee90b8e0a984c9be7e095446d87e0f13ebcf10f245c0ef0f +dist/2024-06-11/rustc-nightly-arm-unknown-linux-gnueabi.tar.xz=657a55f27794460bad174c4f9412e771c679c6b608cc3dbe32903efb075418da +dist/2024-06-11/rustc-nightly-s390x-unknown-linux-gnu.tar.gz=bea7aff88f7d7622a23e91c437d9616a9f1adfbd56ddcb42ee33589d064d5161 +dist/2024-06-11/rustc-nightly-s390x-unknown-linux-gnu.tar.xz=a5ab0850c71e322b1042815c443c79505f1daab2b4a69f8cce05e3761a24f10c +dist/2024-06-11/rustc-nightly-aarch64-unknown-linux-musl.tar.gz=8c00db9c85fa8053f78285c8a546afb32d6b864795388f1ee7a6a49d12643625 +dist/2024-06-11/rustc-nightly-aarch64-unknown-linux-musl.tar.xz=c137f5f943d1c3f14a4a8b5eca2b541c43c82738452646df524e57a6ef0cbd3c +dist/2024-06-11/rustc-nightly-arm-unknown-linux-gnueabihf.tar.gz=a4087ded3076e4d487efcd4a957554255ea1fad5052a01529ea1847559f7b40f +dist/2024-06-11/rustc-nightly-arm-unknown-linux-gnueabihf.tar.xz=ed2b01ad985668564ba2c3f87dcac4f5c4bcf7ea0b5ecca6a30c1f1c4b63a4ef +dist/2024-06-11/rustc-nightly-i686-pc-windows-msvc.tar.gz=41db0331bc8f9d164d4198c93234dae41c1b9d2f53d466ee8cdfba4f9704c45b +dist/2024-06-11/rustc-nightly-i686-pc-windows-msvc.tar.xz=49766cd2ad581f253f3fc0ba4f755a4aeaae954c75d41fa29a9302568e78aa75 +dist/2024-06-11/rustc-nightly-powerpc-unknown-linux-gnu.tar.gz=b57be1a1dc9e70c47564253877bc0cbbdde8a8064a6a4bcf68f8adc6b4c5d4df +dist/2024-06-11/rustc-nightly-powerpc-unknown-linux-gnu.tar.xz=cf2c4651297993671f033eb9bb9877b157638bc41cb40ccac3294c3fd13786ef +dist/2024-06-11/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.gz=619c2eb3ed3f153d2c0ba6cbeb7f0e72c67fbb1e40c025752f94b46c9b740426 +dist/2024-06-11/rustc-nightly-armv7-unknown-linux-gnueabihf.tar.xz=105560769c3672133ecb063ac0a8ef541229490401470180af75151403287d3a +dist/2024-06-11/rustc-nightly-i686-unknown-linux-gnu.tar.gz=211498f7eb884908c8110ce922183bbd6cc05d96d9c8b5c9068dcfc4d29010cd +dist/2024-06-11/rustc-nightly-i686-unknown-linux-gnu.tar.xz=61beb47b917ce644defed991afa6d82028cb8f1e6a191cf5c84e8be883ad3870 +dist/2024-06-11/rustc-nightly-x86_64-pc-windows-msvc.tar.gz=06036f5bfc00950e03a66e8dcab0eb7420fd055aadb85bd42e11845eef122122 +dist/2024-06-11/rustc-nightly-x86_64-pc-windows-msvc.tar.xz=39d3dc95b1e0cfcc381d27540d100b8325728ef9a6a8b72a0e4ad85e2e957a63 +dist/2024-06-11/rustc-nightly-x86_64-unknown-freebsd.tar.gz=9b7badc1c8443571aec7b403e890d14846c3972b02159c8aacf3157ede69af9a +dist/2024-06-11/rustc-nightly-x86_64-unknown-freebsd.tar.xz=a1fdcbe1ca277b22c2dc8aced8ac0f34f841e7a7512fe421c2b045608c4ce07d +dist/2024-06-11/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.gz=5e6105bc6304ea630a8170c785df11cd435521a2ac90348284898c4aab6ed5f2 +dist/2024-06-11/rustc-nightly-powerpc64le-unknown-linux-gnu.tar.xz=97dc7091b1ffa6a23a4b609a680c1f2f63debab80b5c01bc90fe271d6599bf89 +dist/2024-06-11/rustc-nightly-x86_64-apple-darwin.tar.gz=64769377c3714749acf6404d75f46b00ede0107840e736432d759dbfff15b6f9 +dist/2024-06-11/rustc-nightly-x86_64-apple-darwin.tar.xz=8ce829c39fdf21467307ee540c6b06b510d45c76c395947afdc2e5592d775977 +dist/2024-06-11/rustc-nightly-i686-pc-windows-gnu.tar.gz=4c9425ef2d5fdf9badec75783fe61ce0a251fde672c993c5b0069182449e2d7c +dist/2024-06-11/rustc-nightly-i686-pc-windows-gnu.tar.xz=442467ab4199e2fb62c69a96cc1c9f03478a415877996f0fef20c54760b2e8b9 +dist/2024-06-11/rustc-nightly-x86_64-unknown-illumos.tar.gz=0b4c3520ff6e5b053a6b37f7d3708fb115fbb99981f337a1786d675e70034acc +dist/2024-06-11/rustc-nightly-x86_64-unknown-illumos.tar.xz=d192e69dd328e6f930ad5b578065ebab12913762845f68ee55b43ddc9ba074a1 +dist/2024-06-11/rustc-nightly-x86_64-unknown-netbsd.tar.gz=0828f2d9395cdac8e9db6ad5c6b3cab3587fc82bccdec9962cd096e43e4f7793 +dist/2024-06-11/rustc-nightly-x86_64-unknown-netbsd.tar.xz=542f61a4c5a719f2583b0ba820207dc9d09cb9a2bb802da7cfbee5273bbfddfc +dist/2024-06-11/rustc-nightly-x86_64-pc-windows-gnu.tar.gz=289b1bee22b8d9d84e92461694c6558476f944ac07caf2d22f0c4ccf3d29dbcf +dist/2024-06-11/rustc-nightly-x86_64-pc-windows-gnu.tar.xz=23c86fc06ed9e7bcf8da72085ff88ec224ccddeca78ddc31032b314cb254ad64 +dist/2024-06-11/rustc-nightly-aarch64-apple-darwin.tar.gz=cf045620e63a5e623047490af956e0daf0bb321ed6a215ae355b8829c047e58e +dist/2024-06-11/rustc-nightly-aarch64-apple-darwin.tar.xz=0abec976185b9573dbc91868ab4fb84ba2837d25f3fa1d74b3a193609387f3fa +dist/2024-06-11/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.gz=0b1146e24e4e59e4577b1e40d3ca45096864e87bcf9f258a34b9d9cf5abc3eef +dist/2024-06-11/rustc-nightly-riscv64gc-unknown-linux-gnu.tar.xz=289e97314fd22da08c8943d1240fa94b1cfd2cdf061dea15bfb582a9d482d6a6 +dist/2024-06-11/rustc-nightly-x86_64-unknown-linux-musl.tar.gz=8957493cf92d57381b35d6ce5bbdb408a05c2a6becc8e2cd37b0502e3ef35ffb +dist/2024-06-11/rustc-nightly-x86_64-unknown-linux-musl.tar.xz=9015e492f773b91054c53aca92ac704336504959779e4b916d42fc966e6058ea +dist/2024-06-11/rustc-nightly-aarch64-unknown-linux-gnu.tar.gz=aea2dc3831864dc5ea2c389c8208fc82baf1566f4e5688511495db25525e6020 +dist/2024-06-11/rustc-nightly-aarch64-unknown-linux-gnu.tar.xz=9b6f1c3b1656ee2037afe170d6909aa037d484897a83679e2115392a35559062 +dist/2024-06-11/rustc-nightly-x86_64-unknown-linux-gnu.tar.gz=3a962a42e427675df5d8fbc4e638949ec0669a95ecabcdddf5103a9c19f291ac +dist/2024-06-11/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz=6fe48c92425b41ad7c3e812abe2e5decb117be178a16f6910bd60000fe8f355b +dist/2024-06-11/rustc-nightly-aarch64-pc-windows-msvc.tar.gz=0a3147dd45cbf54e66b92baec479a5550c63327185baef3df9638c5a740e0921 +dist/2024-06-11/rustc-nightly-aarch64-pc-windows-msvc.tar.xz=d35948d49fa1e526434ca89c2f7542fcfeb86b4ec35bb92f87f387ec37f4ccda \ No newline at end of file diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index bed76263b451..a709aab7ce22 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -71,6 +71,7 @@ static TARGETS: &[&str] = &[ "arm-unknown-linux-gnueabihf", "arm-unknown-linux-musleabi", "arm-unknown-linux-musleabihf", + "arm64ec-pc-windows-msvc", "armv5te-unknown-linux-gnueabi", "armv5te-unknown-linux-musleabi", "armv7-linux-androideabi", @@ -102,6 +103,7 @@ static TARGETS: &[&str] = &[ "i686-unknown-freebsd", "i686-unknown-linux-gnu", "i686-unknown-linux-musl", + "i686-unknown-redox", "i686-unknown-uefi", "loongarch64-unknown-linux-gnu", "loongarch64-unknown-none", @@ -494,7 +496,7 @@ impl Builder { Some(p) => p, None => return false, }; - pkg.target.get(&c.target).is_some() + pkg.target.contains_key(&c.target) }; extensions.retain(&has_component); components.retain(&has_component); diff --git a/src/tools/build_helper/README.md b/src/tools/build_helper/README.md new file mode 100644 index 000000000000..f81b631c3fdb --- /dev/null +++ b/src/tools/build_helper/README.md @@ -0,0 +1 @@ +Types and functions shared across tools in this workspace. diff --git a/src/tools/build_helper/src/git.rs b/src/tools/build_helper/src/git.rs index a3c857b0268c..b4522de6897d 100644 --- a/src/tools/build_helper/src/git.rs +++ b/src/tools/build_helper/src/git.rs @@ -21,7 +21,7 @@ fn output_result(cmd: &mut Command) -> Result { String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))? )); } - Ok(String::from_utf8(output.stdout).map_err(|err| format!("{err:?}"))?) + String::from_utf8(output.stdout).map_err(|err| format!("{err:?}")) } /// Finds the remote for rust-lang/rust. @@ -64,18 +64,14 @@ pub fn rev_exists(rev: &str, git_dir: Option<&Path>) -> Result { match output.status.code() { Some(0) => Ok(true), Some(128) => Ok(false), - None => { - return Err(format!( - "git didn't exit properly: {}", - String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))? - )); - } - Some(code) => { - return Err(format!( - "git command exited with status code: {code}: {}", - String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))? - )); - } + None => Err(format!( + "git didn't exit properly: {}", + String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))? + )), + Some(code) => Err(format!( + "git command exited with status code: {code}: {}", + String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))? + )), } } @@ -96,7 +92,7 @@ pub fn updated_master_branch( } } - Err(format!("Cannot find any suitable upstream master branch")) + Err("Cannot find any suitable upstream master branch".to_owned()) } pub fn get_git_merge_base( @@ -118,7 +114,7 @@ pub fn get_git_merge_base( pub fn get_git_modified_files( config: &GitConfig<'_>, git_dir: Option<&Path>, - extensions: &Vec<&str>, + extensions: &[&str], ) -> Result>, String> { let merge_base = get_git_merge_base(config, git_dir)?; diff --git a/src/tools/build_helper/src/lib.rs b/src/tools/build_helper/src/lib.rs index 6a4e86eb1dfe..15807d1c0d8f 100644 --- a/src/tools/build_helper/src/lib.rs +++ b/src/tools/build_helper/src/lib.rs @@ -1,5 +1,30 @@ +//! Types and functions shared across tools in this workspace. + pub mod ci; pub mod git; pub mod metrics; -pub mod util; pub mod stage0_parser; +pub mod util; + +/// The default set of crates for opt-dist to collect LLVM profiles. +pub const LLVM_PGO_CRATES: &[&str] = &[ + "syn-1.0.89", + "cargo-0.60.0", + "serde-1.0.136", + "ripgrep-13.0.0", + "regex-1.5.5", + "clap-3.1.6", + "hyper-0.14.18", +]; + +/// The default set of crates for opt-dist to collect rustc profiles. +pub const RUSTC_PGO_CRATES: &[&str] = &[ + "externs", + "ctfe-stress-5", + "cargo-0.60.0", + "token-stream-stress", + "match-stress", + "tuple-stress", + "diesel-1.4.8", + "bitmaps-3.1.0", +]; diff --git a/src/tools/cargo b/src/tools/cargo index 0de7f2ec6c39..3ed207e416fb 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 0de7f2ec6c39d68022e6b97a39559d2f4dbf3930 +Subproject commit 3ed207e416fb2f678a40cc79c02dcf4f936a21ce diff --git a/src/tools/clippy/.github/driver.sh b/src/tools/clippy/.github/driver.sh index 2eafdd0fbc87..09202b1878b2 100755 --- a/src/tools/clippy/.github/driver.sh +++ b/src/tools/clippy/.github/driver.sh @@ -2,15 +2,18 @@ set -ex -# Check sysroot handling -sysroot=$(./target/debug/clippy-driver --print sysroot) -test "$sysroot" = "$(rustc --print sysroot)" +sysroot="$(rustc --print sysroot)" +case $OS in + Linux) export LD_LIBRARY_PATH="$sysroot/lib" ;; + macOS) export DYLD_FALLBACK_LIBRARY_PATH="$sysroot/lib" ;; + Windows) export PATH="$(cygpath "$sysroot")/bin:$PATH" ;; + *) exit 1 +esac -if [[ ${OS} == "Windows" ]]; then - desired_sysroot=C:/tmp -else - desired_sysroot=/tmp -fi +# Check sysroot handling +test "$(./target/debug/clippy-driver --print sysroot)" = "$sysroot" + +desired_sysroot="target/sysroot" # Set --sysroot in command line sysroot=$(./target/debug/clippy-driver --sysroot $desired_sysroot --print sysroot) test "$sysroot" = $desired_sysroot diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml index 8179e3e65b54..06bf3b6fdbfa 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -69,6 +69,6 @@ jobs: working-directory: clippy_dev - name: Test clippy-driver - run: | - TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ') - rustup run $TOOLCHAIN bash .github/driver.sh + run: .github/driver.sh + env: + OS: ${{ runner.os }} diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index 94515987eba4..1f4bec929182 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -116,9 +116,7 @@ jobs: working-directory: clippy_dev - name: Test clippy-driver - run: | - TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ') - rustup run $TOOLCHAIN bash .github/driver.sh + run: .github/driver.sh env: OS: ${{ runner.os }} diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 9c9ea1140814..d7bcd7a19687 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,69 @@ document. ## Unreleased / Beta / In Rust Nightly -[93f0a9a9...master](https://github.com/rust-lang/rust-clippy/compare/93f0a9a9...master) +[ca3b3937...master](https://github.com/rust-lang/rust-clippy/compare/ca3b3937...master) + +## Rust 1.79 + +Current stable, released 2024-06-13 + +[View all 102 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-03-08T11%3A13%3A58Z..2024-04-18T15%3A50%3A50Z+base%3Amaster) + +### New Lints + +* Added [`legacy_numeric_constants`] to `style` + [#12312](https://github.com/rust-lang/rust-clippy/pull/12312) +* Added [`missing_transmute_annotations`] to `suspicious` + [#12239](https://github.com/rust-lang/rust-clippy/pull/12239) +* Added [`integer_division_remainder_used`] to `restriction` + [#12451](https://github.com/rust-lang/rust-clippy/pull/12451) +* Added [`duplicated_attributes`] to `suspicious` + [#12378](https://github.com/rust-lang/rust-clippy/pull/12378) +* Added [`manual_unwrap_or_default`] to `suspicious` + [#12440](https://github.com/rust-lang/rust-clippy/pull/12440) +* Added [`zero_repeat_side_effects`] to `suspicious` + [#12449](https://github.com/rust-lang/rust-clippy/pull/12449) +* Added [`const_is_empty`] to `suspicious` + [#12310](https://github.com/rust-lang/rust-clippy/pull/12310) + +### Moves and Deprecations + +* Moved [`box_default`] to `style` (From `perf`) + [#12601](https://github.com/rust-lang/rust-clippy/pull/12601) +* Moved [`manual_clamp`] to `complexity` (From `nursery` now warn-by-default) + [#12543](https://github.com/rust-lang/rust-clippy/pull/12543) +* Moved [`readonly_write_lock`] to `perf` (From `nursery` now warn-by-default) + [#12479](https://github.com/rust-lang/rust-clippy/pull/12479) + +### Enhancements + +* [`module_name_repetitions`]: Added the [`allowed-prefixes`] configuration to allow common prefixes. + [#12573](https://github.com/rust-lang/rust-clippy/pull/12573) +* [`cast_sign_loss`], [`cast_possible_truncation`], [`cast_lossless`]: Are now allowed in macros + [#12631](https://github.com/rust-lang/rust-clippy/pull/12631) +* [`manual_clamp`]: Now only lints on constant min and max values + [#12543](https://github.com/rust-lang/rust-clippy/pull/12543) +* [`assigning_clones`]: Now considers the [`msrv`] configuration + [#12511](https://github.com/rust-lang/rust-clippy/pull/12511) +* [`needless_return`], [`useless_let_if_seq`], [`mut_mut`], [`read_zero_byte_vec`], [`unused_io_amount`], + [`unused_peekable`]: Now respects `#[allow]` attributes on the affected statement instead + [#12446](https://github.com/rust-lang/rust-clippy/pull/12446) + +### False Positive Fixes + +* [`cast_lossless`]: No longer lints when casting to `u128` + [#12496](https://github.com/rust-lang/rust-clippy/pull/12496) +* [`std_instead_of_core`] No longer lints on modules that are only in `std` + [#12447](https://github.com/rust-lang/rust-clippy/pull/12447) + +### ICE Fixes + +* [`needless_return`]: No longer crashes on non-ascii characters + [#12493](https://github.com/rust-lang/rust-clippy/pull/12493) ## Rust 1.78 -Current stable, released 2024-05-02 +Released 2024-05-02 [View all 112 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-01-26T05%3A46%3A23Z..2024-03-07T16%3A25%3A52Z+base%3Amaster) @@ -5249,6 +5307,7 @@ Released 2018-09-13 [`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression +[`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation [`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons @@ -5447,6 +5506,7 @@ Released 2018-09-13 [`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal +[`macro_metavars_in_unsafe`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe [`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert @@ -5472,6 +5532,7 @@ Released 2018-09-13 [`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or +[`manual_pattern_char_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns [`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid @@ -5565,6 +5626,7 @@ Released 2018-09-13 [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference [`needless_borrows_for_generic_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrows_for_generic_args +[`needless_character_iteration`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_character_iteration [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main @@ -5574,6 +5636,7 @@ Released 2018-09-13 [`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match +[`needless_maybe_sized`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_maybe_sized [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref [`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take [`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals @@ -5702,6 +5765,7 @@ Released 2018-09-13 [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro +[`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`repeat_vec_with_capacity`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts @@ -5908,6 +5972,7 @@ Released 2018-09-13 [`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads [`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons [`waker_clone_wake`]: https://rust-lang.github.io/rust-clippy/master/index.html#waker_clone_wake +[`while_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_float [`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition [`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop [`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator @@ -5939,8 +6004,10 @@ Released 2018-09-13 [`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests [`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings +[`allow-panic-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-panic-in-tests [`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests [`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception +[`allow-renamed-params-for`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-renamed-params-for [`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests [`allow-useless-vec-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-useless-vec-in-tests [`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles @@ -6002,4 +6069,5 @@ Released 2018-09-13 [`vec-box-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#vec-box-size-threshold [`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold [`warn-on-all-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-on-all-wildcard-imports +[`warn-unsafe-macro-metavars-in-private-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-unsafe-macro-metavars-in-private-macros diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index b48f3ab3919c..437884990551 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.80" +version = "0.1.81" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index fa18447090c1..ec76a6dfb08e 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -172,7 +172,7 @@ You can add options to your code to `allow`/`warn`/`deny` Clippy lints: Note: `allow` means to suppress the lint for your code. With `warn` the lint will only emit a warning, while with `deny` the lint will emit an error, when -triggering for your code. An error causes clippy to exit with an error code, so +triggering for your code. An error causes Clippy to exit with an error code, so is useful in scripts like CI/CD. If you do not want to include your lint levels in your code, you can globally @@ -238,7 +238,7 @@ define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. ### Specifying the minimum supported Rust version Projects that intend to support old versions of Rust can disable lints pertaining to newer features by -specifying the minimum supported Rust version (MSRV) in the clippy configuration file. +specifying the minimum supported Rust version (MSRV) in the Clippy configuration file. ```toml msrv = "1.30.0" diff --git a/src/tools/clippy/book/src/configuration.md b/src/tools/clippy/book/src/configuration.md index 9eb067abd91e..b13054431898 100644 --- a/src/tools/clippy/book/src/configuration.md +++ b/src/tools/clippy/book/src/configuration.md @@ -99,7 +99,7 @@ For more details and options, refer to the Cargo documentation. ### Specifying the minimum supported Rust version Projects that intend to support old versions of Rust can disable lints pertaining to newer features by specifying the -minimum supported Rust version (MSRV) in the clippy configuration file. +minimum supported Rust version (MSRV) in the Clippy configuration file. ```toml msrv = "1.30.0" @@ -133,7 +133,7 @@ Very rarely, you may wish to prevent Clippy from evaluating certain sections of `clippy` cfg is not set. You may need to provide a stub so that the code compiles: ```rust -#[cfg(not(clippy)] +#[cfg(not(clippy))] include!(concat!(env!("OUT_DIR"), "/my_big_function-generated.rs")); #[cfg(clippy)] diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md index 415022612caa..48c00bcbf341 100644 --- a/src/tools/clippy/book/src/development/adding_lints.md +++ b/src/tools/clippy/book/src/development/adding_lints.md @@ -587,6 +587,11 @@ declare_clippy_lint! { } ``` +If the lint is in the `restriction` group because it lints things that are not +necessarily “bad” but are more of a style choice, then replace the +“Why is this bad?” section heading with “Why restrict this?”, to avoid writing +“Why is this bad? It isn't, but ...”. + Once your lint is merged, this documentation will show up in the [lint list][lint_list]. diff --git a/src/tools/clippy/book/src/development/basics.md b/src/tools/clippy/book/src/development/basics.md index f4c109ff1191..166b6aab9fb3 100644 --- a/src/tools/clippy/book/src/development/basics.md +++ b/src/tools/clippy/book/src/development/basics.md @@ -107,7 +107,7 @@ More about [intellij] command usage and reasons. ## lintcheck -`cargo lintcheck` will build and run clippy on a fixed set of crates and +`cargo lintcheck` will build and run Clippy on a fixed set of crates and generate a log of the results. You can `git diff` the updated log against its previous version and see what impact your lint made on a small set of crates. If you add a new lint, please audit the resulting warnings and make sure there diff --git a/src/tools/clippy/book/src/development/defining_lints.md b/src/tools/clippy/book/src/development/defining_lints.md index 806ed0845f03..ceabb255e2d0 100644 --- a/src/tools/clippy/book/src/development/defining_lints.md +++ b/src/tools/clippy/book/src/development/defining_lints.md @@ -163,11 +163,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // example code where clippy issues a warning + /// // example code where Clippy issues a warning /// ``` /// Use instead: /// ```rust - /// // example code which does not raise clippy warning + /// // example code which does not raise Clippy warning /// ``` #[clippy::version = "1.70.0"] // <- In which version was this implemented, keep it up to date! pub LINT_NAME, // <- The lint name IN_ALL_CAPS diff --git a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md index 285488cec55c..92fbf733a8fa 100644 --- a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md +++ b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md @@ -428,7 +428,7 @@ selection of possible matches is produced by the pattern syntax. In the second stage, the named subpattern references can be used to do additional tests like asserting that a node hasn't been created as part of a macro expansion. -## Implementing clippy lints using patterns +## Implementing Clippy lints using patterns As a "real-world" example, I re-implemented the `collapsible_if` lint using patterns. The code can be found @@ -572,7 +572,7 @@ The pattern syntax and the *PatternTree* are independent of specific syntax tree implementations (rust ast / hir, syn, ...). When looking at the different pattern examples in the previous sections, it can be seen that the patterns don't contain any information specific to a certain syntax tree implementation. -In contrast, clippy lints currently match against ast / hir syntax tree nodes +In contrast, Clippy lints currently match against ast / hir syntax tree nodes and therefore directly depend on their implementation. The connection between the *PatternTree* and specific syntax tree @@ -690,7 +690,7 @@ change, only the `IsMatch` trait implementations need to be adapted and existing lints can remain unchanged. This also means that if the `IsMatch` trait implementations were integrated into the compiler, updating the `IsMatch` implementations would be required for the compiler to compile successfully. This -could reduce the number of times clippy breaks because of changes in the +could reduce the number of times Clippy breaks because of changes in the compiler. Another advantage of the pattern's independence is that converting an `EarlyLintPass` lint into a `LatePassLint` wouldn't require rewriting the whole pattern matching code. In fact, the pattern might work just fine without any @@ -777,7 +777,7 @@ complexity to solve a relatively minor problem. The issue of users not knowing about the *PatternTree* structure could be solved by a tool that, given a rust program, generates a pattern that matches only this -program (similar to the clippy author lint). +program (similar to the Clippy author lint). For some simple cases (like the first example above), it might be possible to successfully mix Rust and pattern syntax. This space could be further explored @@ -789,7 +789,7 @@ The pattern syntax is heavily inspired by regular expressions (repetitions, alternatives, sequences, ...). From what I've seen until now, other linters also implement lints that directly -work on syntax tree data structures, just like clippy does currently. I would +work on syntax tree data structures, just like Clippy does currently. I would therefore consider the pattern syntax to be *new*, but please correct me if I'm wrong. @@ -982,5 +982,5 @@ pattern!{ } ``` -In the future, clippy could use this system to also provide lints for custom +In the future, Clippy could use this system to also provide lints for custom syntaxes like those found in macros. diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index f6af9810ca16..c8223007df7b 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -101,6 +101,16 @@ Whether to allow `r#""#` when `r""` can be used * [`unnecessary_raw_string_hashes`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_raw_string_hashes) +## `allow-panic-in-tests` +Whether `panic` should be allowed in test functions or `#[cfg(test)]` + +**Default Value:** `false` + +--- +**Affected lints:** +* [`panic`](https://rust-lang.github.io/rust-clippy/master/index.html#panic) + + ## `allow-print-in-tests` Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` @@ -122,6 +132,28 @@ Whether to allow module inception if it's not public. * [`module_inception`](https://rust-lang.github.io/rust-clippy/master/index.html#module_inception) +## `allow-renamed-params-for` +List of trait paths to ignore when checking renamed function parameters. + +#### Example + +```toml +allow-renamed-params-for = [ "std::convert::From" ] +``` + +#### Noteworthy + +- By default, the following traits are ignored: `From`, `TryFrom`, `FromStr` +- `".."` can be used as part of the list to indicate that the configured values should be appended to the +default configuration of Clippy. By default, any configuration will replace the default value. + +**Default Value:** `["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"]` + +--- +**Affected lints:** +* [`renamed_function_params`](https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params) + + ## `allow-unwrap-in-tests` Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` @@ -900,3 +932,13 @@ Whether to allow certain wildcard imports (prelude, super in tests). * [`wildcard_imports`](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports) +## `warn-unsafe-macro-metavars-in-private-macros` +Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. + +**Default Value:** `false` + +--- +**Affected lints:** +* [`macro_metavars_in_unsafe`](https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe) + + diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index 7f7dc9d6cfb0..be0b048ac0c7 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.80" +version = "0.1.81" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 5cfcbdb57d73..cfdf620b7d07 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -40,6 +40,8 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"]; const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"]; const DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"]; +const DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS: &[&str] = + &["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"]; /// Conf with parse errors #[derive(Default)] @@ -455,6 +457,10 @@ define_Conf! { /// /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` (allow_unwrap_in_tests: bool = false), + /// Lint: PANIC. + /// + /// Whether `panic` should be allowed in test functions or `#[cfg(test)]` + (allow_panic_in_tests: bool = false), /// Lint: DBG_MACRO. /// /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` @@ -613,6 +619,27 @@ define_Conf! { /// - Use `".."` as part of the list to indicate that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value (allowed_prefixes: Vec = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect()), + /// Lint: RENAMED_FUNCTION_PARAMS. + /// + /// List of trait paths to ignore when checking renamed function parameters. + /// + /// #### Example + /// + /// ```toml + /// allow-renamed-params-for = [ "std::convert::From" ] + /// ``` + /// + /// #### Noteworthy + /// + /// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr` + /// - `".."` can be used as part of the list to indicate that the configured values should be appended to the + /// default configuration of Clippy. By default, any configuration will replace the default value. + (allow_renamed_params_for: Vec = + DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect()), + /// Lint: MACRO_METAVARS_IN_UNSAFE. + /// + /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. + (warn_unsafe_macro_metavars_in_private_macros: bool = false), } /// Search for the configuration file. @@ -674,6 +701,10 @@ fn deserialize(file: &SourceFile) -> TryConf { extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS); extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES); extend_vec_if_indicator_present(&mut conf.conf.allowed_prefixes, DEFAULT_ALLOWED_PREFIXES); + extend_vec_if_indicator_present( + &mut conf.conf.allow_renamed_params_for, + DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS, + ); // TODO: THIS SHOULD BE TESTED, this comment will be gone soon if conf.conf.allowed_idents_below_min_chars.contains("..") { conf.conf diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs index 14808440d48d..a3e7d0c3fa5f 100644 --- a/src/tools/clippy/clippy_config/src/msrvs.rs +++ b/src/tools/clippy/clippy_config/src/msrvs.rs @@ -26,7 +26,8 @@ msrv_aliases! { 1,63,0 { CLONE_INTO } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE } 1,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST } - 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY } + 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF } + 1,56,0 { CONST_FN_UNION } 1,55,0 { SEEK_REWIND } 1,54,0 { INTO_KEYS } 1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR } diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index 42a953039b1c..4104e7d94f14 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "clippy_dev" +description = "Clippy developer tooling" version = "0.0.1" edition = "2021" [dependencies] aho-corasick = "1.0" -clap = "4.1.4" +clap = { version = "4.4", features = ["derive"] } indoc = "1.0" itertools = "0.12" opener = "0.6" diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index 385191e0361b..3aa43dbe23ed 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(lazy_cell)] #![feature(let_chains)] #![feature(rustc_private)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 397a0e990829..366b52b25dfc 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -2,350 +2,292 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use clap::{Arg, ArgAction, ArgMatches, Command}; +use clap::{Args, Parser, Subcommand}; use clippy_dev::{dogfood, fmt, lint, new_lint, serve, setup, update_lints}; -use indoc::indoc; use std::convert::Infallible; fn main() { - let matches = get_clap_config(); + let dev = Dev::parse(); - match matches.subcommand() { - Some(("bless", _)) => { + match dev.command { + DevCommand::Bless => { eprintln!("use `cargo bless` to automatically replace `.stderr` and `.fixed` files as tests are being run"); }, - Some(("dogfood", matches)) => { - dogfood::dogfood( - matches.get_flag("fix"), - matches.get_flag("allow-dirty"), - matches.get_flag("allow-staged"), - ); - }, - Some(("fmt", matches)) => { - fmt::run(matches.get_flag("check"), matches.get_flag("verbose")); - }, - Some(("update_lints", matches)) => { - if matches.get_flag("print-only") { + DevCommand::Dogfood { + fix, + allow_dirty, + allow_staged, + } => dogfood::dogfood(fix, allow_dirty, allow_staged), + DevCommand::Fmt { check, verbose } => fmt::run(check, verbose), + DevCommand::UpdateLints { print_only, check } => { + if print_only { update_lints::print_lints(); - } else if matches.get_flag("check") { + } else if check { update_lints::update(update_lints::UpdateMode::Check); } else { update_lints::update(update_lints::UpdateMode::Change); } }, - Some(("new_lint", matches)) => { - match new_lint::create( - matches.get_one::("pass").unwrap(), - matches.get_one::("name"), - matches.get_one::("category").map(String::as_str), - matches.get_one::("type").map(String::as_str), - matches.get_flag("msrv"), - ) { - Ok(()) => update_lints::update(update_lints::UpdateMode::Change), - Err(e) => eprintln!("Unable to create lint: {e}"), - } + DevCommand::NewLint { + pass, + name, + category, + r#type, + msrv, + } => match new_lint::create(&pass, &name, &category, r#type.as_deref(), msrv) { + Ok(()) => update_lints::update(update_lints::UpdateMode::Change), + Err(e) => eprintln!("Unable to create lint: {e}"), }, - Some(("setup", sub_command)) => match sub_command.subcommand() { - Some(("git-hook", matches)) => { - if matches.get_flag("remove") { - setup::git_hook::remove_hook(); - } else { - setup::git_hook::install_hook(matches.get_flag("force-override")); - } - }, - Some(("intellij", matches)) => { - if matches.get_flag("remove") { + DevCommand::Setup(SetupCommand { subcommand }) => match subcommand { + SetupSubcommand::Intellij { remove, repo_path } => { + if remove { setup::intellij::remove_rustc_src(); } else { - setup::intellij::setup_rustc_src( - matches - .get_one::("rustc-repo-path") - .expect("this field is mandatory and therefore always valid"), - ); + setup::intellij::setup_rustc_src(&repo_path); } }, - Some(("toolchain", matches)) => { - setup::toolchain::create( - matches.get_flag("force"), - matches.get_flag("release"), - matches.get_one::("name").unwrap(), - ); + SetupSubcommand::GitHook { remove, force_override } => { + if remove { + setup::git_hook::remove_hook(); + } else { + setup::git_hook::install_hook(force_override); + } }, - Some(("vscode-tasks", matches)) => { - if matches.get_flag("remove") { + SetupSubcommand::Toolchain { force, release, name } => setup::toolchain::create(force, release, &name), + SetupSubcommand::VscodeTasks { remove, force_override } => { + if remove { setup::vscode::remove_tasks(); } else { - setup::vscode::install_tasks(matches.get_flag("force-override")); + setup::vscode::install_tasks(force_override); } }, - _ => {}, }, - Some(("remove", sub_command)) => match sub_command.subcommand() { - Some(("git-hook", _)) => setup::git_hook::remove_hook(), - Some(("intellij", _)) => setup::intellij::remove_rustc_src(), - Some(("vscode-tasks", _)) => setup::vscode::remove_tasks(), - _ => {}, + DevCommand::Remove(RemoveCommand { subcommand }) => match subcommand { + RemoveSubcommand::Intellij => setup::intellij::remove_rustc_src(), + RemoveSubcommand::GitHook => setup::git_hook::remove_hook(), + RemoveSubcommand::VscodeTasks => setup::vscode::remove_tasks(), }, - Some(("serve", matches)) => { - let port = *matches.get_one::("port").unwrap(); - let lint = matches.get_one::("lint"); - serve::run(port, lint); - }, - Some(("lint", matches)) => { - let path = matches.get_one::("path").unwrap(); - let args = matches.get_many::("args").into_iter().flatten(); - lint::run(path, args); - }, - Some(("rename_lint", matches)) => { - let old_name = matches.get_one::("old_name").unwrap(); - let new_name = matches.get_one::("new_name").unwrap_or(old_name); - let uplift = matches.get_flag("uplift"); - update_lints::rename(old_name, new_name, uplift); - }, - Some(("deprecate", matches)) => { - let name = matches.get_one::("name").unwrap(); - let reason = matches.get_one("reason"); - update_lints::deprecate(name, reason); - }, - _ => {}, + DevCommand::Serve { port, lint } => serve::run(port, lint), + DevCommand::Lint { path, args } => lint::run(&path, args.iter()), + DevCommand::RenameLint { + old_name, + new_name, + uplift, + } => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift), + DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, reason.as_deref()), } } -fn get_clap_config() -> ArgMatches { - Command::new("Clippy developer tooling") - .arg_required_else_help(true) - .subcommands([ - Command::new("bless").about("bless the test output changes").arg( - Arg::new("ignore-timestamp") - .long("ignore-timestamp") - .action(ArgAction::SetTrue) - .help("Include files updated before clippy was built"), - ), - Command::new("dogfood").about("Runs the dogfood test").args([ - Arg::new("fix") - .long("fix") - .action(ArgAction::SetTrue) - .help("Apply the suggestions when possible"), - Arg::new("allow-dirty") - .long("allow-dirty") - .action(ArgAction::SetTrue) - .help("Fix code even if the working directory has changes") - .requires("fix"), - Arg::new("allow-staged") - .long("allow-staged") - .action(ArgAction::SetTrue) - .help("Fix code even if the working directory has staged changes") - .requires("fix"), - ]), - Command::new("fmt") - .about("Run rustfmt on all projects and tests") - .args([ - Arg::new("check") - .long("check") - .action(ArgAction::SetTrue) - .help("Use the rustfmt --check option"), - Arg::new("verbose") - .short('v') - .long("verbose") - .action(ArgAction::SetTrue) - .help("Echo commands run"), - ]), - Command::new("update_lints") - .about("Updates lint registration and information from the source code") - .long_about( - "Makes sure that:\n \ - * the lint count in README.md is correct\n \ - * the changelog contains markdown link references at the bottom\n \ - * all lint groups include the correct lints\n \ - * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \ - * all lints are registered in the lint store", - ) - .args([ - Arg::new("print-only") - .long("print-only") - .action(ArgAction::SetTrue) - .help( - "Print a table of lints to STDOUT. \ - This does not include deprecated and internal lints. \ - (Does not modify any files)", - ), - Arg::new("check") - .long("check") - .action(ArgAction::SetTrue) - .help("Checks that `cargo dev update_lints` has been run. Used on CI."), - ]), - Command::new("new_lint") - .about("Create new lint and run `cargo dev update_lints`") - .args([ - Arg::new("pass") - .short('p') - .long("pass") - .help("Specify whether the lint runs during the early or late pass") - .value_parser(["early", "late"]) - .conflicts_with("type") - .default_value("late"), - Arg::new("name") - .short('n') - .long("name") - .help("Name of the new lint in snake case, ex: fn_too_long") - .required(true) - .value_parser(|name: &str| Ok::<_, Infallible>(name.replace('-', "_"))), - Arg::new("category") - .short('c') - .long("category") - .help("What category the lint belongs to") - .default_value("nursery") - .value_parser([ - "style", - "correctness", - "suspicious", - "complexity", - "perf", - "pedantic", - "restriction", - "cargo", - "nursery", - "internal", - ]), - Arg::new("type").long("type").help("What directory the lint belongs in"), - Arg::new("msrv") - .long("msrv") - .action(ArgAction::SetTrue) - .help("Add MSRV config code to the lint"), - ]), - Command::new("setup") - .about("Support for setting up your personal development environment") - .arg_required_else_help(true) - .subcommands([ - Command::new("git-hook") - .about("Add a pre-commit git hook that formats your code to make it look pretty") - .args([ - Arg::new("remove") - .long("remove") - .action(ArgAction::SetTrue) - .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"), - Arg::new("force-override") - .long("force-override") - .short('f') - .action(ArgAction::SetTrue) - .help("Forces the override of an existing git pre-commit hook"), - ]), - Command::new("intellij") - .about("Alter dependencies so Intellij Rust can find rustc internals") - .args([ - Arg::new("remove") - .long("remove") - .action(ArgAction::SetTrue) - .help("Remove the dependencies added with 'cargo dev setup intellij'"), - Arg::new("rustc-repo-path") - .long("repo-path") - .short('r') - .help("The path to a rustc repo that will be used for setting the dependencies") - .value_name("path") - .conflicts_with("remove") - .required(true), - ]), - Command::new("toolchain") - .about("Install a rustup toolchain pointing to the local clippy build") - .args([ - Arg::new("force") - .long("force") - .short('f') - .action(ArgAction::SetTrue) - .help("Override an existing toolchain"), - Arg::new("release") - .long("release") - .short('r') - .action(ArgAction::SetTrue) - .help("Point to --release clippy binaries"), - Arg::new("name") - .long("name") - .default_value("clippy") - .help("The name of the created toolchain"), - ]), - Command::new("vscode-tasks") - .about("Add several tasks to vscode for formatting, validation and testing") - .args([ - Arg::new("remove") - .long("remove") - .action(ArgAction::SetTrue) - .help("Remove the tasks added with 'cargo dev setup vscode-tasks'"), - Arg::new("force-override") - .long("force-override") - .short('f') - .action(ArgAction::SetTrue) - .help("Forces the override of existing vscode tasks"), - ]), - ]), - Command::new("remove") - .about("Support for undoing changes done by the setup command") - .arg_required_else_help(true) - .subcommands([ - Command::new("git-hook").about("Remove any existing pre-commit git hook"), - Command::new("vscode-tasks").about("Remove any existing vscode tasks"), - Command::new("intellij").about("Removes rustc source paths added via `cargo dev setup intellij`"), - ]), - Command::new("serve") - .about("Launch a local 'ALL the Clippy Lints' website in a browser") - .args([ - Arg::new("port") - .long("port") - .short('p') - .help("Local port for the http server") - .default_value("8000") - .value_parser(clap::value_parser!(u16)), - Arg::new("lint").help("Which lint's page to load initially (optional)"), - ]), - Command::new("lint") - .about("Manually run clippy on a file or package") - .after_help(indoc! {" - EXAMPLES - Lint a single file: - cargo dev lint tests/ui/attrs.rs - - Lint a package directory: - cargo dev lint tests/ui-cargo/wildcard_dependencies/fail - cargo dev lint ~/my-project - - Run rustfix: - cargo dev lint ~/my-project -- --fix - - Set lint levels: - cargo dev lint file.rs -- -W clippy::pedantic - cargo dev lint ~/my-project -- -- -W clippy::pedantic - "}) - .args([ - Arg::new("path") - .required(true) - .help("The path to a file or package directory to lint"), - Arg::new("args") - .action(ArgAction::Append) - .help("Pass extra arguments to cargo/clippy-driver"), - ]), - Command::new("rename_lint").about("Renames the given lint").args([ - Arg::new("old_name") - .index(1) - .required(true) - .help("The name of the lint to rename"), - Arg::new("new_name") - .index(2) - .required_unless_present("uplift") - .help("The new name of the lint"), - Arg::new("uplift") - .long("uplift") - .action(ArgAction::SetTrue) - .help("This lint will be uplifted into rustc"), - ]), - Command::new("deprecate").about("Deprecates the given lint").args([ - Arg::new("name") - .index(1) - .required(true) - .help("The name of the lint to deprecate"), - Arg::new("reason") - .long("reason") - .short('r') - .help("The reason for deprecation"), - ]), - ]) - .get_matches() +#[derive(Parser)] +#[command(name = "dev", about)] +struct Dev { + #[command(subcommand)] + command: DevCommand, +} + +#[derive(Subcommand)] +enum DevCommand { + /// Bless the test output changes + Bless, + /// Runs the dogfood test + Dogfood { + #[arg(long)] + /// Apply the suggestions when possible + fix: bool, + #[arg(long, requires = "fix")] + /// Fix code even if the working directory has changes + allow_dirty: bool, + #[arg(long, requires = "fix")] + /// Fix code even if the working directory has staged changes + allow_staged: bool, + }, + /// Run rustfmt on all projects and tests + Fmt { + #[arg(long)] + /// Use the rustfmt --check option + check: bool, + #[arg(short, long)] + /// Echo commands run + verbose: bool, + }, + #[command(name = "update_lints")] + /// Updates lint registration and information from the source code + /// + /// Makes sure that: {n} + /// * the lint count in README.md is correct {n} + /// * the changelog contains markdown link references at the bottom {n} + /// * all lint groups include the correct lints {n} + /// * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod` {n} + /// * all lints are registered in the lint store + UpdateLints { + #[arg(long)] + /// Print a table of lints to STDOUT + /// + /// This does not include deprecated and internal lints. (Does not modify any files) + print_only: bool, + #[arg(long)] + /// Checks that `cargo dev update_lints` has been run. Used on CI. + check: bool, + }, + #[command(name = "new_lint")] + /// Create a new lint and run `cargo dev update_lints` + NewLint { + #[arg(short, long, value_parser = ["early", "late"], conflicts_with = "type", default_value = "late")] + /// Specify whether the lint runs during the early or late pass + pass: String, + #[arg( + short, + long, + value_parser = |name: &str| Ok::<_, Infallible>(name.replace('-', "_")), + )] + /// Name of the new lint in snake case, ex: `fn_too_long` + name: String, + #[arg( + short, + long, + value_parser = [ + "style", + "correctness", + "suspicious", + "complexity", + "perf", + "pedantic", + "restriction", + "cargo", + "nursery", + "internal", + ], + default_value = "nursery", + )] + /// What category the lint belongs to + category: String, + #[arg(long)] + /// What directory the lint belongs in + r#type: Option, + #[arg(long)] + /// Add MSRV config code to the lint + msrv: bool, + }, + /// Support for setting up your personal development environment + Setup(SetupCommand), + /// Support for removing changes done by the setup command + Remove(RemoveCommand), + /// Launch a local 'ALL the Clippy Lints' website in a browser + Serve { + #[arg(short, long, default_value = "8000")] + /// Local port for the http server + port: u16, + #[arg(long)] + /// Which lint's page to load initially (optional) + lint: Option, + }, + #[allow(clippy::doc_markdown)] + /// Manually run clippy on a file or package + /// + /// ## Examples + /// + /// Lint a single file: {n} + /// cargo dev lint tests/ui/attrs.rs + /// + /// Lint a package directory: {n} + /// cargo dev lint tests/ui-cargo/wildcard_dependencies/fail {n} + /// cargo dev lint ~/my-project + /// + /// Run rustfix: {n} + /// cargo dev lint ~/my-project -- --fix + /// + /// Set lint levels: {n} + /// cargo dev lint file.rs -- -W clippy::pedantic {n} + /// cargo dev lint ~/my-project -- -- -W clippy::pedantic + Lint { + /// The path to a file or package directory to lint + path: String, + /// Pass extra arguments to cargo/clippy-driver + args: Vec, + }, + #[command(name = "rename_lint")] + /// Rename a lint + RenameLint { + /// The name of the lint to rename + old_name: String, + #[arg(required_unless_present = "uplift")] + /// The new name of the lint + new_name: Option, + #[arg(long)] + /// This lint will be uplifted into rustc + uplift: bool, + }, + /// Deprecate the given lint + Deprecate { + /// The name of the lint to deprecate + name: String, + #[arg(long, short)] + /// The reason for deprecation + reason: Option, + }, +} + +#[derive(Args)] +struct SetupCommand { + #[command(subcommand)] + subcommand: SetupSubcommand, +} + +#[derive(Subcommand)] +enum SetupSubcommand { + /// Alter dependencies so Intellij Rust can find rustc internals + Intellij { + #[arg(long)] + /// Remove the dependencies added with 'cargo dev setup intellij' + remove: bool, + #[arg(long, short, conflicts_with = "remove")] + /// The path to a rustc repo that will be used for setting the dependencies + repo_path: String, + }, + /// Add a pre-commit git hook that formats your code to make it look pretty + GitHook { + #[arg(long)] + /// Remove the pre-commit hook added with 'cargo dev setup git-hook' + remove: bool, + #[arg(long, short)] + /// Forces the override of an existing git pre-commit hook + force_override: bool, + }, + /// Install a rustup toolchain pointing to the local clippy build + Toolchain { + #[arg(long, short)] + /// Override an existing toolchain + force: bool, + #[arg(long, short)] + /// Point to --release clippy binary + release: bool, + #[arg(long, default_value = "clippy")] + /// Name of the toolchain + name: String, + }, + /// Add several tasks to vscode for formatting, validation and testing + VscodeTasks { + #[arg(long)] + /// Remove the tasks added with 'cargo dev setup vscode-tasks' + remove: bool, + #[arg(long, short)] + /// Forces the override of existing vscode tasks + force_override: bool, + }, +} + +#[derive(Args)] +struct RemoveCommand { + #[command(subcommand)] + subcommand: RemoveSubcommand, +} + +#[derive(Subcommand)] +enum RemoveSubcommand { + /// Remove the dependencies added with 'cargo dev setup intellij' + Intellij, + /// Remove the pre-commit git hook + GitHook, + /// Remove the tasks added with 'cargo dev setup vscode-tasks' + VscodeTasks, } diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 2940d56350f4..2e56eb8ec15f 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -36,22 +36,16 @@ impl Context for io::Result { /// # Errors /// /// This function errors out if the files couldn't be created or written to. -pub fn create( - pass: &String, - lint_name: Option<&String>, - category: Option<&str>, - mut ty: Option<&str>, - msrv: bool, -) -> io::Result<()> { - if category == Some("cargo") && ty.is_none() { +pub fn create(pass: &str, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> { + if category == "cargo" && ty.is_none() { // `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo` ty = Some("cargo"); } let lint = LintData { pass, - name: lint_name.expect("`name` argument is validated by clap"), - category: category.expect("`category` argument is validated by clap"), + name, + category, ty, project_root: clippy_project_root(), }; @@ -337,12 +331,17 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { } fn get_lint_declaration(name_upper: &str, category: &str) -> String { + let justification_heading = if category == "restriction" { + "Why restrict this?" + } else { + "Why is this bad?" + }; formatdoc!( r#" declare_clippy_lint! {{ /// ### What it does /// - /// ### Why is this bad? + /// ### {justification_heading} /// /// ### Example /// ```no_run diff --git a/src/tools/clippy/clippy_dev/src/serve.rs b/src/tools/clippy/clippy_dev/src/serve.rs index ea925f6709f9..4a4261d1a1e6 100644 --- a/src/tools/clippy/clippy_dev/src/serve.rs +++ b/src/tools/clippy/clippy_dev/src/serve.rs @@ -8,7 +8,7 @@ use std::{env, thread}; /// # Panics /// /// Panics if the python commands could not be spawned -pub fn run(port: u16, lint: Option<&String>) -> ! { +pub fn run(port: u16, lint: Option) -> ! { let mut url = Some(match lint { None => format!("http://localhost:{port}"), Some(lint) => format!("http://localhost:{port}/#{lint}"), diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 625b13395913..45353901c98f 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -314,7 +314,7 @@ const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note"; /// # Panics /// /// If a file path could not read from or written to -pub fn deprecate(name: &str, reason: Option<&String>) { +pub fn deprecate(name: &str, reason: Option<&str>) { fn finish( (lints, mut deprecated_lints, renamed_lints): (Vec, Vec, Vec), name: &str, @@ -335,7 +335,7 @@ pub fn deprecate(name: &str, reason: Option<&String>) { println!("note: you must run `cargo uitest` to update the test results"); } - let reason = reason.map_or(DEFAULT_DEPRECATION_REASON, String::as_str); + let reason = reason.unwrap_or(DEFAULT_DEPRECATION_REASON); let name_lower = name.to_lowercase(); let name_upper = name.to_uppercase(); diff --git a/src/tools/clippy/clippy_dummy/PUBLISH.md b/src/tools/clippy/clippy_dummy/PUBLISH.md index 8e420ec959a2..f0021f1594f0 100644 --- a/src/tools/clippy/clippy_dummy/PUBLISH.md +++ b/src/tools/clippy/clippy_dummy/PUBLISH.md @@ -1,5 +1,5 @@ This is a dummy crate to publish to crates.io. It primarily exists to ensure -that folks trying to install clippy from crates.io get redirected to the +that folks trying to install Clippy from crates.io get redirected to the `rustup` technique. Before publishing, be sure to rename `clippy_dummy` to `clippy` in `Cargo.toml`, diff --git a/src/tools/clippy/clippy_dummy/crates-readme.md b/src/tools/clippy/clippy_dummy/crates-readme.md index 0decae8b9103..a8ec0a1c36cd 100644 --- a/src/tools/clippy/clippy_dummy/crates-readme.md +++ b/src/tools/clippy/clippy_dummy/crates-readme.md @@ -1,9 +1,9 @@ -Installing clippy via crates.io is deprecated. Please use the following: +Installing Clippy via crates.io is deprecated. Please use the following: ```terminal rustup component add clippy ``` -on a Rust version 1.29 or later. You may need to run `rustup self update` if it complains about a missing clippy binary. +on a Rust version 1.29 or later. You may need to run `rustup self update` if it complains about a missing Clippy binary. See [the homepage](https://github.com/rust-lang/rust-clippy/#clippy) for more information diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 5e3a119337cc..5708ffba08fd 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.80" +version = "0.1.81" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/clippy_lints/src/absolute_paths.rs b/src/tools/clippy/clippy_lints/src/absolute_paths.rs index 8ba661afeeb6..461117cf965d 100644 --- a/src/tools/clippy/clippy_lints/src/absolute_paths.rs +++ b/src/tools/clippy/clippy_lints/src/absolute_paths.rs @@ -12,7 +12,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for usage of items through absolute paths, like `std::env::current_dir`. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Many codebases have their own style when it comes to importing, but one that is seldom used /// is using absolute paths *everywhere*. This is generally considered unidiomatic, and you /// should add a `use` statement. diff --git a/src/tools/clippy/clippy_lints/src/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/allow_attributes.rs index 39fc49dee377..123d0e51eeee 100644 --- a/src/tools/clippy/clippy_lints/src/allow_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/allow_attributes.rs @@ -19,10 +19,11 @@ declare_clippy_lint! { /// This lint only warns outer attributes (`#[allow]`), as inner attributes /// (`#![allow]`) are usually used to enable or disable lints on a global scale. /// - /// ### Why is this bad? - /// `#[expect]` attributes suppress the lint emission, but emit a warning, if + /// ### Why restrict this? + /// `#[allow]` attributes can linger after their reason for existence is gone. + /// `#[expect]` attributes suppress the lint emission, but emit a warning if /// the expectation is unfulfilled. This can be useful to be notified when the - /// lint is no longer triggered. + /// lint is no longer triggered, which may indicate the attribute can be removed. /// /// ### Example /// ```rust,ignore diff --git a/src/tools/clippy/clippy_lints/src/as_conversions.rs b/src/tools/clippy/clippy_lints/src/as_conversions.rs index e3daf75c3eb6..cfa25005a05e 100644 --- a/src/tools/clippy/clippy_lints/src/as_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/as_conversions.rs @@ -17,7 +17,7 @@ declare_clippy_lint! { /// There is a good explanation the reason why this lint should work in this way and how it is useful /// [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122). /// - /// ### Why is this bad? + /// ### Why restrict this? /// `as` conversions will perform many kinds of /// conversions, including silently lossy conversions and dangerous coercions. /// There are cases when it makes sense to use `as`, so the lint is diff --git a/src/tools/clippy/clippy_lints/src/asm_syntax.rs b/src/tools/clippy/clippy_lints/src/asm_syntax.rs index 7c88bfc97ca4..0db1456d40bf 100644 --- a/src/tools/clippy/clippy_lints/src/asm_syntax.rs +++ b/src/tools/clippy/clippy_lints/src/asm_syntax.rs @@ -65,9 +65,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for usage of Intel x86 assembly syntax. /// - /// ### Why is this bad? - /// The lint has been enabled to indicate a preference - /// for AT&T x86 assembly syntax. + /// ### Why restrict this? + /// To enforce consistent use of AT&T x86 assembly syntax. /// /// ### Example /// @@ -114,9 +113,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for usage of AT&T x86 assembly syntax. /// - /// ### Why is this bad? - /// The lint has been enabled to indicate a preference - /// for Intel x86 assembly syntax. + /// ### Why restrict this? + /// To enforce consistent use of Intel x86 assembly syntax. /// /// ### Example /// diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs index aec22965b1b0..7217686dcca5 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs @@ -16,23 +16,33 @@ declare_clippy_lint! { /// ### What it does /// Checks for `assert!(r.is_ok())` or `assert!(r.is_err())` calls. /// - /// ### Why is this bad? - /// An assertion failure cannot output an useful message of the error. + /// ### Why restrict this? + /// This form of assertion does not show any of the information present in the `Result` + /// other than which variant it isn’t. /// /// ### Known problems /// The suggested replacement decreases the readability of code and log output. /// /// ### Example - /// ```rust,ignore + /// ```rust,no_run /// # let r = Ok::<_, ()>(()); /// assert!(r.is_ok()); - /// # let r = Err::<_, ()>(()); + /// # let r = Err::<(), _>(()); /// assert!(r.is_err()); /// ``` + /// + /// Use instead: + /// + /// ```rust,no_run + /// # let r = Ok::<_, ()>(()); + /// r.unwrap(); + /// # let r = Err::<(), _>(()); + /// r.unwrap_err(); + /// ``` #[clippy::version = "1.64.0"] pub ASSERTIONS_ON_RESULT_STATES, restriction, - "`assert!(r.is_ok())`/`assert!(r.is_err())` gives worse error message than directly calling `r.unwrap()`/`r.unwrap_err()`" + "`assert!(r.is_ok())` or `assert!(r.is_err())` gives worse panic messages than directly calling `r.unwrap()` or `r.unwrap_err()`" } declare_lint_pass!(AssertionsOnResultStates => [ASSERTIONS_ON_RESULT_STATES]); diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index f0dafb1ae0d5..e94a6f3e3fc5 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -2,15 +2,15 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::HirNode; use clippy_utils::sugg::Sugg; -use clippy_utils::{is_trait_method, path_to_local}; +use clippy_utils::{is_trait_method, local_is_initialized, path_to_local}; use rustc_errors::Applicability; -use rustc_hir::{self as hir, Expr, ExprKind, Node}; +use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Instance, Mutability}; use rustc_session::impl_lint_pass; use rustc_span::def_id::DefId; use rustc_span::symbol::sym; -use rustc_span::ExpnKind; +use rustc_span::{ExpnKind, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -36,6 +36,7 @@ declare_clippy_lint! { /// Use instead: /// ```rust /// struct Thing; + /// /// impl Clone for Thing { /// fn clone(&self) -> Self { todo!() } /// fn clone_from(&mut self, other: &Self) { todo!() } @@ -47,7 +48,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.78.0"] pub ASSIGNING_CLONES, - perf, + pedantic, "assigning the result of cloning may be inefficient" } @@ -67,7 +68,8 @@ impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); impl<'tcx> LateLintPass<'tcx> for AssigningClones { fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx Expr<'_>) { // Do not fire the lint in macros - let expn_data = assign_expr.span().ctxt().outer_expn_data(); + let ctxt = assign_expr.span().ctxt(); + let expn_data = ctxt.outer_expn_data(); match expn_data.kind { ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) | ExpnKind::Macro(..) => return, ExpnKind::Root => {}, @@ -82,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { }; if is_ok_to_suggest(cx, lhs, &call, &self.msrv) { - suggest(cx, assign_expr, lhs, &call); + suggest(cx, ctxt, assign_expr, lhs, &call); } } @@ -163,9 +165,7 @@ fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallC // TODO: This check currently bails if the local variable has no initializer. // That is overly conservative - the lint should fire even if there was no initializer, // but the variable has been initialized before `lhs` was evaluated. - if let Some(Node::LetStmt(local)) = cx.tcx.hir().parent_id_iter(local).next().map(|p| cx.tcx.hir_node(p)) - && local.init.is_none() - { + if !local_is_initialized(cx, local) { return false; } } @@ -222,14 +222,20 @@ fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallC implemented_fns.contains_key(&provided_fn.def_id) } -fn suggest<'tcx>(cx: &LateContext<'tcx>, assign_expr: &Expr<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>) { +fn suggest<'tcx>( + cx: &LateContext<'tcx>, + ctxt: SyntaxContext, + assign_expr: &Expr<'tcx>, + lhs: &Expr<'tcx>, + call: &CallCandidate<'tcx>, +) { span_lint_and_then(cx, ASSIGNING_CLONES, assign_expr.span, call.message(), |diag| { let mut applicability = Applicability::Unspecified; diag.span_suggestion( assign_expr.span, call.suggestion_msg(), - call.suggested_replacement(cx, lhs, &mut applicability), + call.suggested_replacement(cx, ctxt, lhs, &mut applicability), applicability, ); }); @@ -275,6 +281,7 @@ impl<'tcx> CallCandidate<'tcx> { fn suggested_replacement( &self, cx: &LateContext<'tcx>, + ctxt: SyntaxContext, lhs: &Expr<'tcx>, applicability: &mut Applicability, ) -> String { @@ -294,7 +301,7 @@ impl<'tcx> CallCandidate<'tcx> { // Determine whether we need to reference the argument to clone_from(). let clone_receiver_type = cx.typeck_results().expr_ty(receiver); let clone_receiver_adj_type = cx.typeck_results().expr_ty_adjusted(receiver); - let mut arg_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability); + let mut arg_sugg = Sugg::hir_with_context(cx, receiver, ctxt, "_", applicability); if clone_receiver_type != clone_receiver_adj_type { // The receiver may have been a value type, so we need to add an `&` to // be sure the argument to clone_from will be a reference. @@ -312,7 +319,7 @@ impl<'tcx> CallCandidate<'tcx> { Sugg::hir_with_applicability(cx, lhs, "_", applicability).mut_addr() }; // The RHS had to be exactly correct before the call, there is no auto-deref for function calls. - let rhs_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability); + let rhs_sugg = Sugg::hir_with_context(cx, self_arg, ctxt, "_", applicability); format!("Clone::clone_from({self_sugg}, {rhs_sugg})") }, @@ -341,11 +348,11 @@ impl<'tcx> CallCandidate<'tcx> { match self.kind { CallKind::MethodCall { receiver } => { - let receiver_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability); + let receiver_sugg = Sugg::hir_with_context(cx, receiver, ctxt, "_", applicability); format!("{receiver_sugg}.clone_into({rhs_sugg})") }, CallKind::FunctionCall { self_arg, .. } => { - let self_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability); + let self_sugg = Sugg::hir_with_context(cx, self_arg, ctxt, "_", applicability); format!("ToOwned::clone_into({self_sugg}, {rhs_sugg})") }, } diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs index 736ee48641d8..40a1c4e28842 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs @@ -36,9 +36,10 @@ fn check_duplicated_attr( } let Some(ident) = attr.ident() else { return }; let name = ident.name; - if name == sym::doc || name == sym::cfg_attr { + if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented { // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg // conditions are the same. + // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected. return; } if let Some(direct_parent) = parent.last() diff --git a/src/tools/clippy/clippy_lints/src/attrs/maybe_misused_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/maybe_misused_cfg.rs deleted file mode 100644 index e6b2e835be86..000000000000 --- a/src/tools/clippy/clippy_lints/src/attrs/maybe_misused_cfg.rs +++ /dev/null @@ -1,51 +0,0 @@ -use super::{Attribute, MAYBE_MISUSED_CFG}; -use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::{MetaItemKind, NestedMetaItem}; -use rustc_errors::Applicability; -use rustc_lint::EarlyContext; -use rustc_span::sym; - -pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) { - if attr.has_name(sym::cfg) - && let Some(items) = attr.meta_item_list() - { - check_nested_misused_cfg(cx, &items); - } -} - -fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) { - for item in items { - if let NestedMetaItem::MetaItem(meta) = item { - if let Some(ident) = meta.ident() - && ident.name.as_str() == "features" - && let Some(val) = meta.value_str() - { - span_lint_and_sugg( - cx, - MAYBE_MISUSED_CFG, - meta.span, - "'feature' may be misspelled as 'features'", - "did you mean", - format!("feature = \"{val}\""), - Applicability::MaybeIncorrect, - ); - } - if let MetaItemKind::List(list) = &meta.kind { - check_nested_misused_cfg(cx, list); - // If this is not a list, then we check for `cfg(test)`. - } else if let Some(ident) = meta.ident() - && matches!(ident.name.as_str(), "tests" | "Test") - { - span_lint_and_sugg( - cx, - MAYBE_MISUSED_CFG, - meta.span, - format!("'test' may be misspelled as '{}'", ident.name.as_str()), - "did you mean", - "test".to_string(), - Applicability::MaybeIncorrect, - ); - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/attrs/mismatched_target_os.rs b/src/tools/clippy/clippy_lints/src/attrs/mismatched_target_os.rs deleted file mode 100644 index b1cc0a763c5e..000000000000 --- a/src/tools/clippy/clippy_lints/src/attrs/mismatched_target_os.rs +++ /dev/null @@ -1,90 +0,0 @@ -use super::{Attribute, MISMATCHED_TARGET_OS}; -use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::{MetaItemKind, NestedMetaItem}; -use rustc_errors::Applicability; -use rustc_lint::EarlyContext; -use rustc_span::{sym, Span}; - -static UNIX_SYSTEMS: &[&str] = &[ - "android", - "dragonfly", - "emscripten", - "freebsd", - "fuchsia", - "haiku", - "illumos", - "ios", - "l4re", - "linux", - "macos", - "netbsd", - "openbsd", - "redox", - "solaris", - "vxworks", -]; - -// NOTE: windows is excluded from the list because it's also a valid target family. -static NON_UNIX_SYSTEMS: &[&str] = &["hermit", "none", "wasi"]; - -pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) { - fn find_os(name: &str) -> Option<&'static str> { - UNIX_SYSTEMS - .iter() - .chain(NON_UNIX_SYSTEMS.iter()) - .find(|&&os| os == name) - .copied() - } - - fn is_unix(name: &str) -> bool { - UNIX_SYSTEMS.iter().any(|&os| os == name) - } - - fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> { - let mut mismatched = Vec::new(); - - for item in items { - if let NestedMetaItem::MetaItem(meta) = item { - match &meta.kind { - MetaItemKind::List(list) => { - mismatched.extend(find_mismatched_target_os(list)); - }, - MetaItemKind::Word => { - if let Some(ident) = meta.ident() - && let Some(os) = find_os(ident.name.as_str()) - { - mismatched.push((os, ident.span)); - } - }, - MetaItemKind::NameValue(..) => {}, - } - } - } - - mismatched - } - - if attr.has_name(sym::cfg) - && let Some(list) = attr.meta_item_list() - && let mismatched = find_mismatched_target_os(&list) - && !mismatched.is_empty() - { - let mess = "operating system used in target family position"; - - span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| { - // Avoid showing the unix suggestion multiple times in case - // we have more than one mismatch for unix-like systems - let mut unix_suggested = false; - - for (os, span) in mismatched { - let sugg = format!("target_os = \"{os}\""); - diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); - - if !unix_suggested && is_unix(os) { - diag.help("did you mean `unix`?"); - unix_suggested = true; - } - } - }); - } -} diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index 8f47bc7653b7..e4c98a32fd67 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -7,8 +7,6 @@ mod deprecated_semver; mod duplicated_attributes; mod empty_line_after; mod inline_always; -mod maybe_misused_cfg; -mod mismatched_target_os; mod mixed_attributes_style; mod non_minimal_cfg; mod should_panic_without_expect; @@ -61,11 +59,21 @@ declare_clippy_lint! { /// /// This lint permits lint attributes for lints emitted on the items themself. /// For `use` items these lints are: + /// * ambiguous_glob_reexports + /// * dead_code /// * deprecated + /// * hidden_glob_reexports /// * unreachable_pub - /// * unused_imports + /// * unused + /// * unused_braces + /// * unused_import_braces + /// * clippy::disallowed_types /// * clippy::enum_glob_use /// * clippy::macro_use_imports + /// * clippy::module_name_repetitions + /// * clippy::redundant_pub_crate + /// * clippy::single_component_path_imports + /// * clippy::unsafe_removed_from_name /// * clippy::wildcard_imports /// /// For `extern crate` items these lints are: @@ -260,48 +268,15 @@ declare_clippy_lint! { "usage of `cfg_attr(rustfmt)` instead of tool attributes" } -declare_clippy_lint! { - /// ### What it does - /// Checks for cfg attributes having operating systems used in target family position. - /// - /// ### Why is this bad? - /// The configuration option will not be recognised and the related item will not be included - /// by the conditional compilation engine. - /// - /// ### Example - /// ```no_run - /// #[cfg(linux)] - /// fn conditional() { } - /// ``` - /// - /// Use instead: - /// ```no_run - /// # mod hidden { - /// #[cfg(target_os = "linux")] - /// fn conditional() { } - /// # } - /// - /// // or - /// - /// #[cfg(unix)] - /// fn conditional() { } - /// ``` - /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details. - #[clippy::version = "1.45.0"] - pub MISMATCHED_TARGET_OS, - correctness, - "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`" -} - declare_clippy_lint! { /// ### What it does /// Checks for attributes that allow lints without a reason. /// /// (This requires the `lint_reasons` feature) /// - /// ### Why is this bad? - /// Allowing a lint should always have a reason. This reason should be documented to - /// ensure that others understand the reasoning + /// ### Why restrict this? + /// Justifying each `allow` helps readers understand the reasoning, + /// and may allow removing `allow` attributes if their purpose is obsolete. /// /// ### Example /// ```no_run @@ -381,38 +356,6 @@ declare_clippy_lint! { "ensure that all `cfg(any())` and `cfg(all())` have more than one condition" } -declare_clippy_lint! { - /// ### What it does - /// Checks for `#[cfg(features = "...")]` and suggests to replace it with - /// `#[cfg(feature = "...")]`. - /// - /// It also checks if `cfg(test)` was misspelled. - /// - /// ### Why is this bad? - /// Misspelling `feature` as `features` or `test` as `tests` can be sometimes hard to spot. It - /// may cause conditional compilation not work quietly. - /// - /// ### Example - /// ```no_run - /// #[cfg(features = "some-feature")] - /// fn conditional() { } - /// #[cfg(tests)] - /// mod tests { } - /// ``` - /// - /// Use instead: - /// ```no_run - /// #[cfg(feature = "some-feature")] - /// fn conditional() { } - /// #[cfg(test)] - /// mod tests { } - /// ``` - #[clippy::version = "1.69.0"] - pub MAYBE_MISUSED_CFG, - suspicious, - "prevent from misusing the wrong attr name" -} - declare_clippy_lint! { /// ### What it does /// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for @@ -520,7 +463,7 @@ declare_clippy_lint! { /// #[allow(dead_code)] /// fn foo() {} /// ``` - #[clippy::version = "1.78.0"] + #[clippy::version = "1.79.0"] pub DUPLICATED_ATTRIBUTES, suspicious, "duplicated attribute" @@ -602,11 +545,9 @@ pub struct EarlyAttributes { impl_lint_pass!(EarlyAttributes => [ DEPRECATED_CFG_ATTR, - MISMATCHED_TARGET_OS, EMPTY_LINE_AFTER_OUTER_ATTR, EMPTY_LINE_AFTER_DOC_COMMENTS, NON_MINIMAL_CFG, - MAYBE_MISUSED_CFG, DEPRECATED_CLIPPY_CFG_ATTR, UNNECESSARY_CLIPPY_CFG, ]); @@ -619,9 +560,7 @@ impl EarlyLintPass for EarlyAttributes { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { deprecated_cfg_attr::check(cx, attr, &self.msrv); deprecated_cfg_attr::check_clippy(cx, attr); - mismatched_target_os::check(cx, attr); non_minimal_cfg::check(cx, attr); - maybe_misused_cfg::check(cx, attr); } extract_msrv_attr!(EarlyContext); diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs index 7575f502a7c2..f0868231d01a 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs @@ -2,6 +2,7 @@ use super::utils::{extract_clippy_lint, is_lint_level, is_word}; use super::{Attribute, USELESS_ATTRIBUTE}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{first_line_of_span, snippet_opt}; +use rustc_ast::NestedMetaItem; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LintContext}; @@ -20,26 +21,40 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) for lint in lint_list { match item.kind { ItemKind::Use(..) => { - if is_word(lint, sym::unused_imports) - || is_word(lint, sym::deprecated) - || is_word(lint, sym!(unreachable_pub)) - || is_word(lint, sym!(unused)) - || is_word(lint, sym!(unused_import_braces)) - || extract_clippy_lint(lint).map_or(false, |s| { - matches!( - s.as_str(), - "wildcard_imports" - | "enum_glob_use" - | "redundant_pub_crate" - | "macro_use_imports" - | "unsafe_removed_from_name" - | "module_name_repetitions" - | "single_component_path_imports" - ) - }) + if let NestedMetaItem::MetaItem(meta_item) = lint + && meta_item.is_word() + && let Some(ident) = meta_item.ident() + && matches!( + ident.name.as_str(), + "ambiguous_glob_reexports" + | "dead_code" + | "deprecated" + | "hidden_glob_reexports" + | "unreachable_pub" + | "unused" + | "unused_braces" + | "unused_import_braces" + | "unused_imports" + ) { return; } + + if extract_clippy_lint(lint).is_some_and(|symbol| { + matches!( + symbol.as_str(), + "wildcard_imports" + | "enum_glob_use" + | "redundant_pub_crate" + | "macro_use_imports" + | "unsafe_removed_from_name" + | "module_name_repetitions" + | "single_component_path_imports" + | "disallowed_types" + ) + }) { + return; + } }, ItemKind::ExternCrate(..) => { if is_word(lint, sym::unused_imports) && skip_unused_imports { diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs index 171f30318601..eb05dc96cdeb 100644 --- a/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs +++ b/src/tools/clippy/clippy_lints/src/blocks_in_conditions.rs @@ -1,15 +1,11 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_block_with_applicability; -use clippy_utils::ty::implements_trait; -use clippy_utils::visitors::{for_each_expr, Descend}; -use clippy_utils::{get_parent_expr, higher, is_from_proc_macro}; -use core::ops::ControlFlow; +use clippy_utils::{higher, is_from_proc_macro}; use rustc_errors::Applicability; use rustc_hir::{BlockCheckMode, Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -124,30 +120,6 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions { ); } } - } else { - let _: Option = for_each_expr(cond, |e| { - if let ExprKind::Closure(closure) = e.kind { - // do not lint if the closure is called using an iterator (see #1141) - if let Some(parent) = get_parent_expr(cx, e) - && let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind - && let caller = cx.typeck_results().expr_ty(self_arg) - && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator) - && implements_trait(cx, caller, iter_id, &[]) - { - return ControlFlow::Continue(Descend::No); - } - - let body = cx.tcx.hir().body(closure.body); - let ex = &body.value; - if let ExprKind::Block(block, _) = ex.kind { - if !body.value.span.from_expansion() && !block.stmts.is_empty() { - span_lint(cx, BLOCKS_IN_CONDITIONS, ex.span, complex_block_message.clone()); - return ControlFlow::Continue(Descend::No); - } - } - } - ControlFlow::Continue(Descend::Yes) - }); } } } diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index b6341b3fe8e7..a1c6c0b608f7 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -174,6 +174,25 @@ fn check_inverted_bool_in_condition( ); } +fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind + && !expr.span.from_expansion() + && !inner.span.from_expansion() + && let Some(suggestion) = simplify_not(cx, inner) + && cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow + { + span_lint_and_sugg( + cx, + NONMINIMAL_BOOL, + expr.span, + "this boolean expression can be simplified", + "try", + suggestion, + Applicability::MachineApplicable, + ); + } +} + struct NonminimalBoolVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, } @@ -232,6 +251,11 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { _ => (), } } + + if self.cx.typeck_results().expr_ty(e).is_never() { + return Err("contains never type".to_owned()); + } + for (n, expr) in self.terminals.iter().enumerate() { if eq_expr_value(self.cx, e, expr) { #[expect(clippy::cast_possible_truncation)] @@ -542,8 +566,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { } }; if improvements.is_empty() { - let mut visitor = NotSimplificationVisitor { cx: self.cx }; - visitor.visit_expr(e); + check_simplify_not(self.cx, e); } else { nonminimal_bool_lint( improvements @@ -586,30 +609,3 @@ fn implements_ord(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { .get_diagnostic_item(sym::Ord) .map_or(false, |id| implements_trait(cx, ty, id, &[])) } - -struct NotSimplificationVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind - && !expr.span.from_expansion() - && !inner.span.from_expansion() - && let Some(suggestion) = simplify_not(self.cx, inner) - && self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow - { - span_lint_and_sugg( - self.cx, - NONMINIMAL_BOOL, - expr.span, - "this boolean expression can be simplified", - "try", - suggestion, - Applicability::MachineApplicable, - ); - } - - walk_expr(self, expr); - } -} diff --git a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs index a3291c9da109..e924542fea2a 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs @@ -49,7 +49,7 @@ impl LintConfig { type LintTable = BTreeMap, Spanned>; -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Default)] struct Lints { #[serde(default)] rust: LintTable, @@ -57,15 +57,18 @@ struct Lints { clippy: LintTable, } -#[derive(Deserialize, Debug)] -struct CargoToml { +#[derive(Deserialize, Debug, Default)] +struct Workspace { + #[serde(default)] lints: Lints, } -#[derive(Default, Debug)] -struct LintsAndGroups { - lints: Vec>, - groups: Vec<(Spanned, Spanned)>, +#[derive(Deserialize, Debug)] +struct CargoToml { + #[serde(default)] + lints: Lints, + #[serde(default)] + workspace: Workspace, } fn toml_span(range: Range, file: &SourceFile) -> Span { @@ -77,27 +80,28 @@ fn toml_span(range: Range, file: &SourceFile) -> Span { ) } -fn check_table(cx: &LateContext<'_>, table: LintTable, groups: &FxHashSet<&str>, file: &SourceFile) { - let mut by_priority = BTreeMap::<_, LintsAndGroups>::new(); +fn check_table(cx: &LateContext<'_>, table: LintTable, known_groups: &FxHashSet<&str>, file: &SourceFile) { + let mut lints = Vec::new(); + let mut groups = Vec::new(); for (name, config) in table { - let lints_and_groups = by_priority.entry(config.as_ref().priority()).or_default(); - if groups.contains(name.get_ref().as_str()) { - lints_and_groups.groups.push((name, config)); + if name.get_ref() == "warnings" { + continue; + } + + if known_groups.contains(name.get_ref().as_str()) { + groups.push((name, config)); } else { - lints_and_groups.lints.push(name); + lints.push((name, config.into_inner())); } } - let low_priority = by_priority - .iter() - .find(|(_, lints_and_groups)| !lints_and_groups.lints.is_empty()) - .map_or(-1, |(&lowest_lint_priority, _)| lowest_lint_priority - 1); - for (priority, LintsAndGroups { lints, groups }) in by_priority { - let Some(last_lint_alphabetically) = lints.last() else { - continue; - }; - - for (group, config) in groups { + for (group, group_config) in groups { + let priority = group_config.get_ref().priority(); + let level = group_config.get_ref().level(); + if let Some((conflict, _)) = lints + .iter() + .rfind(|(_, lint_config)| lint_config.priority() == priority && lint_config.level() != level) + { span_lint_and_then( cx, LINT_GROUPS_PRIORITY, @@ -107,22 +111,23 @@ fn check_table(cx: &LateContext<'_>, table: LintTable, groups: &FxHashSet<&str>, group.as_ref() ), |diag| { - let config_span = toml_span(config.span(), file); - if config.as_ref().is_implicit() { + let config_span = toml_span(group_config.span(), file); + + if group_config.as_ref().is_implicit() { diag.span_label(config_span, "has an implicit priority of 0"); } - // add the label to next lint after this group that has the same priority - let lint = lints - .iter() - .filter(|lint| lint.span().start > group.span().start) - .min_by_key(|lint| lint.span().start) - .unwrap_or(last_lint_alphabetically); - diag.span_label(toml_span(lint.span(), file), "has the same priority as this lint"); + diag.span_label(toml_span(conflict.span(), file), "has the same priority as this lint"); diag.note("the order of the lints in the table is ignored by Cargo"); + let mut suggestion = String::new(); + let low_priority = lints + .iter() + .map(|(_, config)| config.priority().saturating_sub(1)) + .min() + .unwrap_or(-1); Serialize::serialize( &LintConfigTable { - level: config.as_ref().level().into(), + level: level.into(), priority: Some(low_priority), }, toml::ser::ValueSerializer::new(&mut suggestion), @@ -164,5 +169,7 @@ pub fn check(cx: &LateContext<'_>) { check_table(cx, cargo_toml.lints.rust, &rustc_groups, &file); check_table(cx, cargo_toml.lints.clippy, &clippy_groups, &file); + check_table(cx, cargo_toml.workspace.lints.rust, &rustc_groups, &file); + check_table(cx, cargo_toml.workspace.lints.clippy, &clippy_groups, &file); } } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs index 2b6e17dc1030..8bbd41b0db1e 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::visitors::{for_each_expr_without_closures, Descend}; use clippy_utils::{method_chain_args, sext}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -255,8 +255,10 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign { /// Peels binary operators such as [`BinOpKind::Mul`], [`BinOpKind::Div`] or [`BinOpKind::Rem`], /// where the result depends on: +/// /// - the number of negative values in the entire expression, or /// - the number of negative values on the left hand side of the expression. +/// /// Ignores overflow. /// /// @@ -264,7 +266,7 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign { fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> { let mut res = vec![]; - for_each_expr(expr, |sub_expr| -> ControlFlow { + for_each_expr_without_closures(expr, |sub_expr| -> ControlFlow { // We don't check for mul/div/rem methods here, but we could. if let ExprKind::Binary(op, lhs, _rhs) = sub_expr.kind { if matches!(op.node, BinOpKind::Mul | BinOpKind::Div) { @@ -303,15 +305,17 @@ fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> { } /// Peels binary operators such as [`BinOpKind::Add`], where the result depends on: +/// /// - all the expressions being positive, or /// - all the expressions being negative. +/// /// Ignores overflow. /// /// Expressions using other operators are preserved, so we can try to evaluate them later. fn exprs_with_add_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> { let mut res = vec![]; - for_each_expr(expr, |sub_expr| -> ControlFlow { + for_each_expr_without_closures(expr, |sub_expr| -> ControlFlow { // We don't check for add methods here, but we could. if let ExprKind::Binary(op, _lhs, _rhs) = sub_expr.kind { if matches!(op.node, BinOpKind::Add) { diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index bd2c96f01f6f..e60c36ced75d 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -303,7 +303,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for casts of a function pointer to any integer type. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Casting a function pointer to an integer can have surprising results and can occur /// accidentally if parentheses are omitted from a function call. If you aren't doing anything /// low-level with function pointers then you can opt-out of casting functions to integers in @@ -535,8 +535,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for the usage of `as _` conversion using inferred type. /// - /// ### Why is this bad? - /// The conversion might include lossy conversion and dangerous cast that might go + /// ### Why restrict this? + /// The conversion might include lossy conversion or a dangerous cast that might go /// undetected due to the type being inferred. /// /// The lint is allowed by default as using `_` is less wordy than always specifying the type. diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index a7f7bf7854e6..fb0b0cba6a66 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::source::snippet_opt; -use clippy_utils::visitors::{for_each_expr, Visitable}; +use clippy_utils::visitors::{for_each_expr_without_closures, Visitable}; use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local}; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; @@ -245,7 +245,7 @@ fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 { /// TODO: Maybe we should move this to `clippy_utils` so others won't need to go down this dark, /// dark path reimplementing this (or something similar). fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx>, cast_from: Ty<'tcx>) -> bool { - for_each_expr(expr, |expr| { + for_each_expr_without_closures(expr, |expr| { // Calls are a `Path`, and usage of locals are a `Path`. So, this checks // - call() as i32 // - local as i32 diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index ee1bb63b50d3..e41abf422349 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack}; use core::ops::ControlFlow; use rustc_ast::ast::Attribute; @@ -65,7 +65,7 @@ impl CognitiveComplexity { let mut cc = 1u64; let mut returns = 0u64; - let _: Option = for_each_expr(expr, |e| { + let _: Option = for_each_expr_without_closures(expr, |e| { match e.kind { ExprKind::If(_, _, _) => { cc += 1; diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs index 70856b808810..28d9f68d504c 100644 --- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs +++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::visitors::{for_each_expr_with_closures, Visitable}; +use clippy_utils::visitors::{for_each_expr, Visitable}; use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind}; @@ -82,7 +82,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI let mut has_read_access = false; // Inspect all expressions and sub-expressions in the block. - for_each_expr_with_closures(cx, block, |expr| { + for_each_expr(cx, block, |expr| { // Ignore expressions that are not simply `id`. if !path_to_local_id(expr, id) { return ControlFlow::Continue(()); diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index ccf1d9d6f8c0..480df675d754 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt}; use clippy_utils::ty::{needs_ordered_drop, InteriorMut}; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{ capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq, @@ -362,7 +362,7 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool { /// Checks if the statement modifies or moves any of the given locals. fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool { - for_each_expr(s, |e| { + for_each_expr_without_closures(s, |e| { if let Some(id) = path_to_local(e) && locals.contains(&id) && !capture_local_usage(cx, e).is_imm_ref() @@ -413,7 +413,7 @@ fn scan_block_for_eq<'tcx>( let mut cond_locals = HirIdSet::default(); for &cond in conds { - let _: Option = for_each_expr(cond, |e| { + let _: Option = for_each_expr_without_closures(cond, |e| { if let Some(id) = path_to_local(e) { cond_locals.insert(id); } diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs index 7a3d5a070912..27c00948a8f2 100644 --- a/src/tools/clippy/clippy_lints/src/create_dir.rs +++ b/src/tools/clippy/clippy_lints/src/create_dir.rs @@ -10,8 +10,10 @@ declare_clippy_lint! { /// ### What it does /// Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead. /// - /// ### Why is this bad? - /// Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`. + /// ### Why restrict this? + /// Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`, + /// resulting in failure when more than one directory needs to be created or when the directory already exists. + /// Crates which never need to specifically create a single directory may wish to prevent this mistake. /// /// ### Example /// ```rust,ignore diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index db5937266047..b0590b0a71cb 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -14,7 +14,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for usage of the [`dbg!`](https://doc.rust-lang.org/std/macro.dbg.html) macro. /// - /// ### Why is this bad? + /// ### Why restrict this? /// The `dbg!` macro is intended as a debugging tool. It should not be present in released /// software or committed to a version control system. /// diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 5ff7d8e51343..7e43a99e9f24 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -58,8 +58,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, crate::attrs::INLINE_ALWAYS_INFO, - crate::attrs::MAYBE_MISUSED_CFG_INFO, - crate::attrs::MISMATCHED_TARGET_OS_INFO, crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO, crate::attrs::NON_MINIMAL_CFG_INFO, crate::attrs::SHOULD_PANIC_WITHOUT_EXPECT_INFO, @@ -140,6 +138,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::disallowed_names::DISALLOWED_NAMES_INFO, crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO, crate::disallowed_types::DISALLOWED_TYPES_INFO, + crate::doc::DOC_LAZY_CONTINUATION_INFO, crate::doc::DOC_LINK_WITH_QUOTES_INFO, crate::doc::DOC_MARKDOWN_INFO, crate::doc::EMPTY_DOCS_INFO, @@ -205,6 +204,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::functions::MUST_USE_CANDIDATE_INFO, crate::functions::MUST_USE_UNIT_INFO, crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO, + crate::functions::RENAMED_FUNCTION_PARAMS_INFO, crate::functions::RESULT_LARGE_ERR_INFO, crate::functions::RESULT_UNIT_ERR_INFO, crate::functions::TOO_MANY_ARGUMENTS_INFO, @@ -291,9 +291,11 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::loops::SAME_ITEM_PUSH_INFO, crate::loops::SINGLE_ELEMENT_LOOP_INFO, crate::loops::UNUSED_ENUMERATE_INDEX_INFO, + crate::loops::WHILE_FLOAT_INFO, crate::loops::WHILE_IMMUTABLE_CONDITION_INFO, crate::loops::WHILE_LET_LOOP_INFO, crate::loops::WHILE_LET_ON_ITERATOR_INFO, + crate::macro_metavars_in_unsafe::MACRO_METAVARS_IN_UNSAFE_INFO, crate::macro_use::MACRO_USE_IMPORTS_INFO, crate::main_recursion::MAIN_RECURSION_INFO, crate::manual_assert::MANUAL_ASSERT_INFO, @@ -415,6 +417,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::MAP_UNWRAP_OR_INFO, crate::methods::MUT_MUTEX_LOCK_INFO, crate::methods::NAIVE_BYTECOUNT_INFO, + crate::methods::NEEDLESS_CHARACTER_ITERATION_INFO, crate::methods::NEEDLESS_COLLECT_INFO, crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO, crate::methods::NEEDLESS_OPTION_TAKE_INFO, @@ -445,7 +448,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO, crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO, crate::methods::SINGLE_CHAR_ADD_STR_INFO, - crate::methods::SINGLE_CHAR_PATTERN_INFO, crate::methods::SKIP_WHILE_NEXT_INFO, crate::methods::STABLE_SORT_PRIMITIVE_INFO, crate::methods::STRING_EXTEND_CHARS_INFO, @@ -527,6 +529,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, crate::needless_if::NEEDLESS_IF_INFO, crate::needless_late_init::NEEDLESS_LATE_INIT_INFO, + crate::needless_maybe_sized::NEEDLESS_MAYBE_SIZED_INFO, crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO, crate::needless_pass_by_ref_mut::NEEDLESS_PASS_BY_REF_MUT_INFO, crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO, @@ -652,6 +655,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO, crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO, crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO, + crate::string_patterns::MANUAL_PATTERN_CHAR_COMPARISON_INFO, + crate::string_patterns::SINGLE_CHAR_PATTERN_INFO, crate::strings::STRING_ADD_INFO, crate::strings::STRING_ADD_ASSIGN_INFO, crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index 1d6c4ce72e18..ff631909bcb5 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -22,9 +22,8 @@ declare_clippy_lint! { /// /// See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback. /// - /// ### Why is this bad? - /// For those who are very careful about types, default numeric fallback - /// can be a pitfall that cause unexpected runtime behavior. + /// ### Why restrict this? + /// To ensure that every numeric type is chosen explicitly rather than implicitly. /// /// ### Known problems /// This lint can only be allowed at the function level or above. @@ -49,7 +48,7 @@ declare_clippy_lint! { declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { - fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) { let hir = cx.tcx.hir(); let is_parent_const = matches!( hir.body_const_context(hir.body_owner_def_id(body.id())), diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs index b4290b6437f2..3fa9bad0d03d 100644 --- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs +++ b/src/tools/clippy/clippy_lints/src/default_union_representation.rs @@ -10,7 +10,7 @@ declare_clippy_lint! { /// ### What it does /// Displays a warning when a union is declared with the default representation (without a `#[repr(C)]` attribute). /// - /// ### Why is this bad? + /// ### Why restrict this? /// Unions in Rust have unspecified layout by default, despite many people thinking that they /// lay out each field at the start of the union (like C does). That is, there are no guarantees /// about the offset of the fields for unions with multiple non-ZST fields without an explicitly diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs index 9aa5af3190fb..a0900f46f6aa 100644 --- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -215,3 +215,29 @@ declare_deprecated_lint! { pub WRONG_PUB_SELF_CONVENTION, "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items" } + +declare_deprecated_lint! { + /// ### What it does + /// Nothing. This lint has been deprecated. + /// + /// ### Deprecation reason + /// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect the `#[cfg(features)]` and `#[cfg(tests)]` typos. + /// + /// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs + #[clippy::version = "1.80.0"] + pub MAYBE_MISUSED_CFG, + "this lint has been replaced by `unexpected_cfgs`" +} + +declare_deprecated_lint! { + /// ### What it does + /// Nothing. This lint has been deprecated. + /// + /// ### Deprecation reason + /// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect invalid `#[cfg(linux)]` attributes. + /// + /// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs + #[clippy::version = "1.80.0"] + pub MISMATCHED_TARGET_OS, + "this lint has been replaced by `unexpected_cfgs`" +} diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index c6aef9ac2d60..d60320d82825 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -2,7 +2,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{implements_trait, is_manually_drop, peel_mid_ty_refs}; -use clippy_utils::{expr_use_ctxt, get_parent_expr, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode}; +use clippy_utils::{ + expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode, +}; use core::mem; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; @@ -641,7 +643,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } } - fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &Body<'_>) { if Some(body.id()) == self.current_body { for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) { let replacements = pat.replacements; @@ -1038,14 +1040,8 @@ fn report<'tcx>( ); }, State::ExplicitDeref { mutability } => { - if matches!( - expr.kind, - ExprKind::Block(..) - | ExprKind::ConstBlock(_) - | ExprKind::If(..) - | ExprKind::Loop(..) - | ExprKind::Match(..) - ) && let ty::Ref(_, ty, _) = data.adjusted_ty.kind() + if is_block_like(expr) + && let ty::Ref(_, ty, _) = data.adjusted_ty.kind() && ty.is_sized(cx.tcx, cx.param_env) { // Rustc bug: auto deref doesn't work on block expression when targeting sized types. diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index 2a644922fb1c..636698e96f6d 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -1,17 +1,17 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; use rustc_hir::{ - self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Safety, Impl, Item, ItemKind, UnsafeSource, + self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, Safety, UnsafeSource, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::traits::Reveal; use rustc_middle::ty::{ - self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, Upcast, TraitPredicate, Ty, TyCtxt, + self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast, }; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; @@ -390,13 +390,17 @@ fn check_unsafe_derive_deserialize<'tcx>( .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local())) .any(|imp| has_unsafe(cx, imp)) { - span_lint_and_help( + span_lint_hir_and_then( cx, UNSAFE_DERIVE_DESERIALIZE, + adt_hir_id, item.span, "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`", - None, - "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html", + |diag| { + diag.help( + "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html", + ); + }, ); } } @@ -452,20 +456,27 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r && !has_non_exhaustive_attr(cx.tcx, *adt) && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) && let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) + && let Some(local_def_id) = adt.did().as_local() // If all of our fields implement `Eq`, we can implement `Eq` too && adt .all_fields() .map(|f| f.ty(cx.tcx, args)) .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, None, &[])) { - span_lint_and_sugg( + span_lint_hir_and_then( cx, DERIVE_PARTIAL_EQ_WITHOUT_EQ, + cx.tcx.local_def_id_to_hir_id(local_def_id), span.ctxt().outer_expn_data().call_site, "you are deriving `PartialEq` and can implement `Eq`", - "consider deriving `Eq` as well", - "PartialEq, Eq".to_string(), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion( + span.ctxt().outer_expn_data().call_site, + "consider deriving `Eq` as well", + "PartialEq, Eq", + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs index def4b5932b4b..a995f06fb73d 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs @@ -20,11 +20,11 @@ declare_clippy_lint! { /// [aliases]: http://www.unicode.org/reports/tr24/tr24-31.html#Script_Value_Aliases /// [supported_scripts]: https://www.unicode.org/iso15924/iso15924-codes.html /// - /// ### Why is this bad? + /// ### Why restrict this? /// It may be not desired to have many different scripts for /// identifiers in the codebase. /// - /// Note that if you only want to allow plain English, you might want to use + /// Note that if you only want to allow typical English, you might want to use /// built-in [`non_ascii_idents`] lint instead. /// /// [`non_ascii_idents`]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#non-ascii-idents diff --git a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs new file mode 100644 index 000000000000..38bc58a55019 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs @@ -0,0 +1,95 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use itertools::Itertools; +use rustc_errors::{Applicability, SuggestionStyle}; +use rustc_lint::LateContext; +use rustc_span::{BytePos, Span}; +use std::ops::Range; + +use super::DOC_LAZY_CONTINUATION; + +fn map_container_to_text(c: &super::Container) -> &'static str { + match c { + super::Container::Blockquote => "> ", + // numbered list can have up to nine digits, plus the dot, plus four spaces on either side + super::Container::List(indent) => &" "[0..*indent], + } +} + +// TODO: Adjust the parameters as necessary +pub(super) fn check( + cx: &LateContext<'_>, + doc: &str, + range: Range, + mut span: Span, + containers: &[super::Container], +) { + if doc[range.clone()].contains('\t') { + // We don't do tab stops correctly. + return; + } + + let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count(); + let blockquote_level = containers + .iter() + .filter(|c| matches!(c, super::Container::Blockquote)) + .count(); + let lcount = doc[range.clone()].chars().filter(|c| *c == ' ').count(); + let list_indentation = containers + .iter() + .map(|c| { + if let super::Container::List(indent) = c { + *indent + } else { + 0 + } + }) + .sum(); + if ccount < blockquote_level || lcount < list_indentation { + let msg = if ccount < blockquote_level { + "doc quote missing `>` marker" + } else { + "doc list item missing indentation" + }; + span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| { + if ccount == 0 && blockquote_level == 0 { + // simpler suggestion style for indentation + let indent = list_indentation - lcount; + diag.span_suggestion_with_style( + span.shrink_to_hi(), + "indent this line", + std::iter::repeat(" ").take(indent).join(""), + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); + diag.help("if this is supposed to be its own paragraph, add a blank line"); + return; + } + let mut doc_start_range = &doc[range]; + let mut suggested = String::new(); + for c in containers { + let text = map_container_to_text(c); + if doc_start_range.starts_with(text) { + doc_start_range = &doc_start_range[text.len()..]; + span = span + .with_lo(span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger"))); + } else if matches!(c, super::Container::Blockquote) + && let Some(i) = doc_start_range.find('>') + { + doc_start_range = &doc_start_range[i + 1..]; + span = + span.with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1)); + } else { + suggested.push_str(text); + } + } + diag.span_suggestion_with_style( + span, + "add markers to start of line", + suggested, + Applicability::MachineApplicable, + SuggestionStyle::ShowAlways, + ); + diag.help("if this not intended to be a quote at all, escape it with `\\>`"); + }); + } +} diff --git a/src/tools/clippy/clippy_lints/src/doc/markdown.rs b/src/tools/clippy/clippy_lints/src/doc/markdown.rs index 1add02af3101..41c0bcd55adc 100644 --- a/src/tools/clippy/clippy_lints/src/doc/markdown.rs +++ b/src/tools/clippy/clippy_lints/src/doc/markdown.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, SuggestionStyle}; @@ -30,6 +30,7 @@ pub fn check( word = tmp_word; } + let original_len = word.len(); word = word.trim_start_matches(trim_pattern); // Remove leading or trailing single `:` which may be part of a sentence. @@ -44,6 +45,25 @@ pub fn check( continue; } + // Ensure that all reachable matching closing parens are included as well. + let size_diff = original_len - word.len(); + let mut open_parens = 0; + let mut close_parens = 0; + for c in word.chars() { + if c == '(' { + open_parens += 1; + } else if c == ')' { + close_parens += 1; + } + } + while close_parens < open_parens + && let Some(tmp_word) = orig_word.get(size_diff..=(word.len() + size_diff)) + && tmp_word.ends_with(')') + { + word = tmp_word; + close_parens += 1; + } + // Adjust for the current word let offset = word.as_ptr() as usize - text.as_ptr() as usize; let span = Span::new( @@ -92,13 +112,15 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b if let Ok(url) = Url::parse(word) { // try to get around the fact that `foo::bar` parses as a valid URL if !url.cannot_be_a_base() { - span_lint( + span_lint_and_sugg( cx, DOC_MARKDOWN, span, "you should put bare URLs between `<`/`>` or make a proper Markdown link", + "try", + format!("<{word}>"), + Applicability::MachineApplicable, ); - return; } } diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs index 36ba19698c72..e3d748407261 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -1,3 +1,4 @@ +use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{is_doc_hidden, return_ty}; @@ -6,15 +7,13 @@ use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::{sym, Span}; -use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; - pub fn check( cx: &LateContext<'_>, owner_id: OwnerId, sig: FnSig<'_>, headers: DocHeaders, body_id: Option, - panic_span: Option, + panic_info: Option<(Span, bool)>, check_private_items: bool, ) { if !check_private_items && !cx.effective_visibilities.is_exported(owner_id.def_id) { @@ -38,7 +37,7 @@ pub fn check( cx, MISSING_SAFETY_DOC, span, - "unsafe function's docs miss `# Safety` section", + "unsafe function's docs are missing a `# Safety` section", ), (true, Safety::Safe) => span_lint( cx, @@ -48,13 +47,13 @@ pub fn check( ), _ => (), } - if !headers.panics && panic_span.is_some() { + if !headers.panics && panic_info.map_or(false, |el| !el.1) { span_lint_and_note( cx, MISSING_PANICS_DOC, span, "docs for function which may panic missing `# Panics` section", - panic_span, + panic_info.map(|el| el.0), "first possible panic found here", ); } diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 7fdb582e6403..3d875e7ac2d3 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -1,13 +1,14 @@ +mod lazy_continuation; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::Visitable; -use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args}; +use clippy_utils::{in_constant, is_entrypoint_fn, is_trait_impl_item, method_chain_args}; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; -use pulldown_cmark::Tag::{BlockQuote, CodeBlock, Heading, Item, Link, Paragraph}; +use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, Item, Link, Paragraph}; use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options}; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; @@ -260,7 +261,7 @@ declare_clippy_lint! { /// Checks for the doc comments of publicly visible /// safe functions and traits and warns if there is a `# Safety` section. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Safe functions and traits are safe to implement and therefore do not /// need to describe safety preconditions that users are required to uphold. /// @@ -362,6 +363,63 @@ declare_clippy_lint! { "docstrings exist but documentation is empty" } +declare_clippy_lint! { + /// ### What it does + /// + /// In CommonMark Markdown, the language used to write doc comments, a + /// paragraph nested within a list or block quote does not need any line + /// after the first one to be indented or marked. The specification calls + /// this a "lazy paragraph continuation." + /// + /// ### Why is this bad? + /// + /// This is easy to write but hard to read. Lazy continuations makes + /// unintended markers hard to see, and make it harder to deduce the + /// document's intended structure. + /// + /// ### Example + /// + /// This table is probably intended to have two rows, + /// but it does not. It has zero rows, and is followed by + /// a block quote. + /// ```no_run + /// /// Range | Description + /// /// ----- | ----------- + /// /// >= 1 | fully opaque + /// /// < 1 | partially see-through + /// fn set_opacity(opacity: f32) {} + /// ``` + /// + /// Fix it by escaping the marker: + /// ```no_run + /// /// Range | Description + /// /// ----- | ----------- + /// /// \>= 1 | fully opaque + /// /// < 1 | partially see-through + /// fn set_opacity(opacity: f32) {} + /// ``` + /// + /// This example is actually intended to be a list: + /// ```no_run + /// /// * Do nothing. + /// /// * Then do something. Whatever it is needs done, + /// /// it should be done right now. + /// # fn do_stuff() {} + /// ``` + /// + /// Fix it by indenting the list contents: + /// ```no_run + /// /// * Do nothing. + /// /// * Then do something. Whatever it is needs done, + /// /// it should be done right now. + /// # fn do_stuff() {} + /// ``` + #[clippy::version = "1.80.0"] + pub DOC_LAZY_CONTINUATION, + style, + "require every line of a paragraph to be indented and marked" +} + #[derive(Clone)] pub struct Documentation { valid_idents: FxHashSet, @@ -388,6 +446,7 @@ impl_lint_pass!(Documentation => [ UNNECESSARY_SAFETY_DOC, SUSPICIOUS_DOC_COMMENTS, EMPTY_DOCS, + DOC_LAZY_CONTINUATION, ]); impl<'tcx> LateLintPass<'tcx> for Documentation { @@ -402,14 +461,14 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) { let body = cx.tcx.hir().body(body_id); - let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value); + let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value); missing_headers::check( cx, item.owner_id, sig, headers, Some(body_id), - panic_span, + panic_info, self.check_private_items, ); } @@ -551,6 +610,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ cx, valid_idents, parser.into_offset_iter(), + &doc, Fragments { fragments: &fragments, doc: &doc, @@ -560,6 +620,11 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; +enum Container { + Blockquote, + List(usize), +} + /// Checks parsed documentation. /// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`, /// so lints here will generally access that information. @@ -569,6 +634,7 @@ fn check_doc<'a, Events: Iterator, Range, valid_idents: &FxHashSet, events: Events, + doc: &str, fragments: Fragments<'_>, ) -> DocHeaders { // true if a safety header was found @@ -576,6 +642,7 @@ fn check_doc<'a, Events: Iterator, Range, Range { if tag.starts_with(", Range blockquote_level += 1, - End(BlockQuote) => blockquote_level -= 1, + Start(BlockQuote) => { + blockquote_level += 1; + containers.push(Container::Blockquote); + }, + End(BlockQuote) => { + blockquote_level -= 1; + containers.pop(); + }, Start(CodeBlock(ref kind)) => { in_code = true; if let CodeBlockKind::Fenced(lang) = kind { @@ -633,6 +710,13 @@ fn check_doc<'a, Events: Iterator, Range, Range, Range in_footnote_definition = true, + End(FootnoteDefinition(..)) => in_footnote_definition = false, Start(_tag) | End(_tag) => (), // We don't care about other tags - SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (), + SoftBreak | HardBreak => { + if !containers.is_empty() + && let Some((next_event, next_range)) = events.peek() + && let Some(next_span) = fragments.span(cx, next_range.clone()) + && let Some(span) = fragments.span(cx, range.clone()) + && !in_footnote_definition + && !matches!(next_event, End(_)) + { + lazy_continuation::check( + cx, + doc, + range.end..next_range.start, + Span::new(span.hi(), next_span.lo(), span.ctxt(), span.parent()), + &containers[..], + ); + } + }, + TaskListMarker(_) | Code(_) | Rule => (), FootnoteReference(text) | Text(text) => { paragraph_range.end = range.end; ticks_unbalanced |= text.contains('`') && !in_code; @@ -701,6 +807,7 @@ fn check_doc<'a, Events: Iterator, Range { cx: &'a LateContext<'tcx>, + is_const: bool, panic_span: Option, typeck_results: &'tcx ty::TypeckResults<'tcx>, } @@ -710,14 +817,15 @@ impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> { cx: &'a LateContext<'tcx>, typeck_results: &'tcx ty::TypeckResults<'tcx>, body: impl Visitable<'tcx>, - ) -> Option { + ) -> Option<(Span, bool)> { let mut vis = Self { cx, + is_const: false, panic_span: None, typeck_results, }; body.visit(&mut vis); - vis.panic_span + vis.panic_span.map(|el| (el, vis.is_const)) } } @@ -736,6 +844,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { "assert" | "assert_eq" | "assert_ne" ) { + self.is_const = in_constant(self.cx, expr.hir_id); self.panic_span = Some(macro_call.span); } } diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs index 651f2ebaee6f..c3e3c0431e6b 100644 --- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs +++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs @@ -8,7 +8,7 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::HumanEmitter; use rustc_errors::{Diag, DiagCtxt}; use rustc_lint::LateContext; -use rustc_parse::maybe_new_parser_from_source_str; +use rustc_parse::new_parser_from_source_str; use rustc_parse::parser::ForceCollect; use rustc_session::parse::ParseSess; use rustc_span::edition::Edition; @@ -50,7 +50,7 @@ pub fn check( let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let psess = ParseSess::with_dcx(dcx, sm); - let mut parser = match maybe_new_parser_from_source_str(&psess, filename, code) { + let mut parser = match new_parser_from_source_str(&psess, filename, code) { Ok(p) => p, Err(errs) => { errs.into_iter().for_each(Diag::cancel); diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs index 119473c2454b..4a6ffcd9a788 100644 --- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -52,9 +52,10 @@ declare_clippy_lint! { /// Checks for usage of `std::mem::forget(t)` where `t` is /// `Drop` or has a field that implements `Drop`. /// - /// ### Why is this bad? - /// `std::mem::forget(t)` prevents `t` from running its - /// destructor, possibly causing leaks. + /// ### Why restrict this? + /// `std::mem::forget(t)` prevents `t` from running its destructor, possibly causing leaks. + /// It is not possible to detect all means of creating leaks, but it may be desirable to + /// prohibit the simple ones. /// /// ### Example /// ```no_run diff --git a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs index a6ca7fe9e0bb..bb6f9aac2236 100644 --- a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs +++ b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs @@ -11,7 +11,7 @@ declare_clippy_lint! { /// Checks for usage of if expressions with an `else if` branch, /// but without a final `else` branch. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10). /// /// ### Example diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs index 74db250b3ae9..c5fc72b5e2d8 100644 --- a/src/tools/clippy/clippy_lints/src/empty_drop.rs +++ b/src/tools/clippy/clippy_lints/src/empty_drop.rs @@ -9,7 +9,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for empty `Drop` implementations. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Empty `Drop` implementations have no effect when dropping an instance of the type. They are /// most likely useless. However, an empty `Drop` implementation prevents a type from being /// destructured, which might be the intention behind adding the implementation as a marker. diff --git a/src/tools/clippy/clippy_lints/src/empty_enum.rs b/src/tools/clippy/clippy_lints/src/empty_enum.rs index 420888b6ccb3..d16714695cb7 100644 --- a/src/tools/clippy/clippy_lints/src/empty_enum.rs +++ b/src/tools/clippy/clippy_lints/src/empty_enum.rs @@ -7,32 +7,53 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for `enum`s with no variants. + /// Checks for `enum`s with no variants, which therefore are uninhabited types + /// (cannot be instantiated). /// - /// As of this writing, the `never_type` is still a - /// nightly-only experimental API. Therefore, this lint is only triggered - /// if the `never_type` is enabled. + /// As of this writing, the `never_type` is still a nightly-only experimental API. + /// Therefore, this lint is only triggered if `#![feature(never_type)]` is enabled. /// /// ### Why is this bad? - /// If you want to introduce a type which - /// can't be instantiated, you should use `!` (the primitive type "never"), - /// or a wrapper around it, because `!` has more extensive - /// compiler support (type inference, etc...) and wrappers - /// around it are the conventional way to define an uninhabited type. - /// For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html) + /// * If you only want a type which can’t be instantiated, you should use [`!`] + /// (the primitive type "never"), because [`!`] has more extensive compiler support + /// (type inference, etc.) and implementations of common traits. /// + /// * If you need to introduce a distinct type, consider using a [newtype] `struct` + /// containing [`!`] instead (`struct MyType(pub !)`), because it is more idiomatic + /// to use a `struct` rather than an `enum` when an `enum` is unnecessary. + /// + /// If you do this, note that the [visibility] of the [`!`] field determines whether + /// the uninhabitedness is visible in documentation, and whether it can be pattern + /// matched to mark code unreachable. If the field is not visible, then the struct + /// acts like any other struct with private fields. + /// + /// * If the enum has no variants only because all variants happen to be + /// [disabled by conditional compilation][cfg], then it would be appropriate + /// to allow the lint, with `#[allow(empty_enum)]`. + /// + /// For further information, visit + /// [the never type’s documentation][`!`]. /// /// ### Example /// ```no_run - /// enum Test {} + /// enum CannotExist {} /// ``` /// /// Use instead: /// ```no_run /// #![feature(never_type)] /// - /// struct Test(!); + /// /// Use the `!` type directly... + /// type CannotExist = !; + /// + /// /// ...or define a newtype which is distinct. + /// struct CannotExist2(pub !); /// ``` + /// + /// [`!`]: https://doc.rust-lang.org/std/primitive.never.html + /// [cfg]: https://doc.rust-lang.org/reference/conditional-compilation.html + /// [newtype]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction + /// [visibility]: https://doc.rust-lang.org/reference/visibility-and-privacy.html #[clippy::version = "pre 1.29.0"] pub EMPTY_ENUM, pedantic, diff --git a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs index 969df6d85b5a..745599b0e57a 100644 --- a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs +++ b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs @@ -11,16 +11,23 @@ declare_clippy_lint! { /// ### What it does /// Finds structs without fields (a so-called "empty struct") that are declared with brackets. /// - /// ### Why is this bad? - /// Empty brackets after a struct declaration can be omitted. + /// ### Why restrict this? + /// Empty brackets after a struct declaration can be omitted, + /// and it may be desirable to do so consistently for style. + /// + /// However, removing the brackets also introduces a public constant named after the struct, + /// so this is not just a syntactic simplification but an an API change, and adding them back + /// is a *breaking* API change. /// /// ### Example /// ```no_run /// struct Cookie {} + /// struct Biscuit(); /// ``` /// Use instead: /// ```no_run /// struct Cookie; + /// struct Biscuit; /// ``` #[clippy::version = "1.62.0"] pub EMPTY_STRUCTS_WITH_BRACKETS, @@ -32,14 +39,20 @@ declare_clippy_lint! { /// ### What it does /// Finds enum variants without fields that are declared with empty brackets. /// - /// ### Why is this bad? - /// Empty brackets while defining enum variants are redundant and can be omitted. + /// ### Why restrict this? + /// Empty brackets after a enum variant declaration are redundant and can be omitted, + /// and it may be desirable to do so consistently for style. + /// + /// However, removing the brackets also introduces a public constant named after the variant, + /// so this is not just a syntactic simplification but an an API change, and adding them back + /// is a *breaking* API change. /// /// ### Example /// ```no_run /// enum MyEnum { /// HasData(u8), - /// HasNoData(), // redundant parentheses + /// HasNoData(), // redundant parentheses + /// NoneHereEither {}, // redundant braces /// } /// ``` /// @@ -48,6 +61,7 @@ declare_clippy_lint! { /// enum MyEnum { /// HasData(u8), /// HasNoData, + /// NoneHereEither, /// } /// ``` #[clippy::version = "1.77.0"] diff --git a/src/tools/clippy/clippy_lints/src/endian_bytes.rs b/src/tools/clippy/clippy_lints/src/endian_bytes.rs index dd03df797de3..bb766e963387 100644 --- a/src/tools/clippy/clippy_lints/src/endian_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/endian_bytes.rs @@ -13,8 +13,9 @@ declare_clippy_lint! { /// ### What it does /// Checks for the usage of the `to_ne_bytes` method and/or the function `from_ne_bytes`. /// - /// ### Why is this bad? - /// It's not, but some may prefer to specify the target endianness explicitly. + /// ### Why restrict this? + /// To ensure use of explicitly chosen endianness rather than the target’s endianness, + /// such as when implementing network protocols or file formats rather than FFI. /// /// ### Example /// ```rust,ignore @@ -31,9 +32,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for the usage of the `to_le_bytes` method and/or the function `from_le_bytes`. /// - /// ### Why is this bad? - /// It's not, but some may wish to lint usage of this method, either to suggest using the host - /// endianness or big endian. + /// ### Why restrict this? + /// To ensure use of big endian or the target’s endianness rather than little endian. /// /// ### Example /// ```rust,ignore @@ -50,9 +50,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for the usage of the `to_be_bytes` method and/or the function `from_be_bytes`. /// - /// ### Why is this bad? - /// It's not, but some may wish to lint usage of this method, either to suggest using the host - /// endianness or little endian. + /// ### Why restrict this? + /// To ensure use of little endian or the target’s endianness rather than big endian. /// /// ### Example /// ```rust,ignore diff --git a/src/tools/clippy/clippy_lints/src/error_impl_error.rs b/src/tools/clippy/clippy_lints/src/error_impl_error.rs index 8dbb47fadc5d..8e49138cd26b 100644 --- a/src/tools/clippy/clippy_lints/src/error_impl_error.rs +++ b/src/tools/clippy/clippy_lints/src/error_impl_error.rs @@ -12,7 +12,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for types named `Error` that implement `Error`. /// - /// ### Why is this bad? + /// ### Why restrict this? /// It can become confusing when a codebase has 20 types all named `Error`, requiring either /// aliasing them in the `use` statement or qualifying them like `my_module::Error`. This /// hinders comprehension, as it requires you to memorize every variation of importing `Error` diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs index 6715de52649d..8d6e27700d8d 100644 --- a/src/tools/clippy/clippy_lints/src/escape.rs +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -104,7 +104,9 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { too_large_for_stack: self.too_large_for_stack, }; - ExprUseVisitor::for_clippy(cx, fn_def_id, &mut v).consume_body(body).into_ok(); + ExprUseVisitor::for_clippy(cx, fn_def_id, &mut v) + .consume_body(body) + .into_ok(); for node in v.set { span_lint_hir( diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index b58018ca0353..42ec2c00823c 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -9,7 +9,7 @@ use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Saf use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ - self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty, + self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty, TyCtxt, TypeVisitableExt, TypeckResults, }; use rustc_session::declare_lint_pass; @@ -123,7 +123,8 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..)) ) => { - let callee_ty = typeck.expr_ty(callee).peel_refs(); + let callee_ty_raw = typeck.expr_ty(callee); + let callee_ty = callee_ty_raw.peel_refs(); if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc)) || !check_inputs(typeck, body.params, None, args) { @@ -170,15 +171,25 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if let Ok((ClosureKind::FnMut, _)) = cx.tcx.infer_ctxt().build().type_implements_fn_trait( - cx.param_env, - Binder::bind_with_vars(callee_ty_adjusted, List::empty()), - ty::PredicatePolarity::Positive, - ) && path_to_local(callee).map_or(false, |l| { + if path_to_local(callee).map_or(false, |l| { + // FIXME: Do we really need this `local_used_in` check? + // Isn't it checking something like... `callee(callee)`? + // If somehow this check is needed, add some test for it, + // 'cuz currently nothing changes after deleting this check. local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) }) { - // Mutable closure is used after current expr; we cannot consume it. - snippet = format!("&mut {snippet}"); + match cx.tcx.infer_ctxt().build().type_implements_fn_trait( + cx.param_env, + Binder::bind_with_vars(callee_ty_adjusted, List::empty()), + ty::PredicatePolarity::Positive, + ) { + // Mutable closure is used after current expr; we cannot consume it. + Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"), + Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => { + snippet = format!("&{snippet}"); + }, + _ => (), + } } diag.span_suggestion( expr.span, @@ -240,7 +251,7 @@ fn check_inputs( }) } -fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs<'tcx>, call_sig: FnSig<'_>) -> bool { +fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs>, call_sig: FnSig<'_>) -> bool { call_sig.safety == Safety::Safe && !has_late_bound_to_non_late_bound_regions( cx.tcx.signature_unclosure(closure.sig(), Safety::Safe).skip_binder(), diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs index 9ffda6457424..436dc8611bde 100644 --- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs +++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs @@ -10,10 +10,10 @@ declare_clippy_lint! { /// ### What it does /// Warns on any exported `enum`s that are not tagged `#[non_exhaustive]` /// - /// ### Why is this bad? - /// Exhaustive enums are typically fine, but a project which does - /// not wish to make a stability commitment around exported enums may wish to - /// disable them by default. + /// ### Why restrict this? + /// Making an `enum` exhaustive is a stability commitment: adding a variant is a breaking change. + /// A project may wish to ensure that there are no exhaustive enums or that every exhaustive + /// `enum` is explicitly `#[allow]`ed. /// /// ### Example /// ```no_run @@ -40,10 +40,10 @@ declare_clippy_lint! { /// ### What it does /// Warns on any exported `struct`s that are not tagged `#[non_exhaustive]` /// - /// ### Why is this bad? - /// Exhaustive structs are typically fine, but a project which does - /// not wish to make a stability commitment around exported structs may wish to - /// disable them by default. + /// ### Why restrict this? + /// Making a `struct` exhaustive is a stability commitment: adding a field is a breaking change. + /// A project may wish to ensure that there are no exhaustive structs or that every exhaustive + /// `struct` is explicitly `#[allow]`ed. /// /// ### Example /// ```no_run diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs index 106844dd4348..91c94d66458d 100644 --- a/src/tools/clippy/clippy_lints/src/exit.rs +++ b/src/tools/clippy/clippy_lints/src/exit.rs @@ -9,11 +9,13 @@ declare_clippy_lint! { /// ### What it does /// Detects calls to the `exit()` function which terminates the program. /// - /// ### Why is this bad? - /// Exit terminates the program at the location it is called. For unrecoverable - /// errors `panics` should be used to provide a stacktrace and potentially other - /// information. A normal termination or one with an error code should happen in - /// the main function. + /// ### Why restrict this? + /// `exit()` immediately terminates the program with no information other than an exit code. + /// This provides no means to troubleshoot a problem, and may be an unexpected side effect. + /// + /// Codebases may use this lint to require that all exits are performed either by panicking + /// (which produces a message, a code location, and optionally a backtrace) + /// or by returning from `main()` (which is a single place to look). /// /// ### Example /// ```no_run diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index 724e1843359b..3c4a043a732b 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::{find_format_args, format_args_inputs_span}; +use clippy_utils::macros::{format_args_inputs_span, FormatArgsStorage}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_expn_of, path_def_id}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::{sym, ExpnId}; declare_clippy_lint! { @@ -38,7 +38,17 @@ declare_clippy_lint! { "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work" } -declare_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]); +pub struct ExplicitWrite { + format_args: FormatArgsStorage, +} + +impl ExplicitWrite { + pub fn new(format_args: FormatArgsStorage) -> Self { + Self { format_args } + } +} + +impl_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]); impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -57,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { Some(sym::io_stderr) => ("stderr", "e"), _ => return, }; - let Some(format_args) = find_format_args(cx, write_arg, ExpnId::root()) else { + let Some(format_args) = self.format_args.get(cx, write_arg, ExpnId::root()) else { return; }; @@ -83,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { }; let mut applicability = Applicability::MachineApplicable; let inputs_snippet = - snippet_with_applicability(cx, format_args_inputs_span(&format_args), "..", &mut applicability); + snippet_with_applicability(cx, format_args_inputs_span(format_args), "..", &mut applicability); span_lint_and_sugg( cx, EXPLICIT_WRITE, diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs index 2cd4e9e99a56..4d301daabe4c 100644 --- a/src/tools/clippy/clippy_lints/src/float_literal.rs +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -38,9 +38,9 @@ declare_clippy_lint! { /// Checks for whole number float literals that /// cannot be represented as the underlying type without loss. /// - /// ### Why is this bad? - /// Rust will silently lose precision during - /// conversion to a float. + /// ### Why restrict this? + /// If the value was intended to be exact, it will not be. + /// This may be especially surprising when the lost precision is to the left of the decimal point. /// /// ### Example /// ```no_run @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { return; } - if is_whole && !sym_str.contains(|c| c == 'e' || c == 'E') { + if is_whole && !sym_str.contains(['e', 'E']) { // Normalize the literal by stripping the fractional portion if sym_str.split('.').next().unwrap() != float_str { // If the type suffix is missing the suggestion would be diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs index 46d47e217b04..68bdf88d0a7e 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -2,7 +2,8 @@ use clippy_utils::consts::Constant::{Int, F32, F64}; use clippy_utils::consts::{constant, constant_simple, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ - eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate, numeric_literal, peel_blocks, sugg, + eq_expr_value, get_parent_expr, higher, in_constant, is_inherent_method_call, is_no_std_crate, numeric_literal, + peel_blocks, sugg, }; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; @@ -759,7 +760,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { if let ExprKind::MethodCall(path, receiver, args, _) = &expr.kind { let recv_ty = cx.typeck_results().expr_ty(receiver); - if recv_ty.is_floating_point() && !is_no_std_crate(cx) { + if recv_ty.is_floating_point() && !is_no_std_crate(cx) && is_inherent_method_call(cx, expr) { match path.ident.name.as_str() { "ln" => check_ln1p(cx, expr, receiver), "log" => check_log_base(cx, expr, receiver, args), diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 8a0cd155d211..0b248f784b76 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::{find_format_arg_expr, find_format_args, root_macro_call_first_node}; +use clippy_utils::macros::{find_format_arg_expr, root_macro_call_first_node, FormatArgsStorage}; use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::sugg::Sugg; use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait}; @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::{sym, Span}; declare_clippy_lint! { @@ -39,13 +39,24 @@ declare_clippy_lint! { "useless use of `format!`" } -declare_lint_pass!(UselessFormat => [USELESS_FORMAT]); +#[allow(clippy::module_name_repetitions)] +pub struct UselessFormat { + format_args: FormatArgsStorage, +} + +impl UselessFormat { + pub fn new(format_args: FormatArgsStorage) -> Self { + Self { format_args } + } +} + +impl_lint_pass!(UselessFormat => [USELESS_FORMAT]); impl<'tcx> LateLintPass<'tcx> for UselessFormat { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(macro_call) = root_macro_call_first_node(cx, expr) && cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) - && let Some(format_args) = find_format_args(cx, expr, macro_call.expn) + && let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) { let mut applicability = Applicability::MachineApplicable; let call_site = macro_call.span; diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 003a9995c15f..99def199af0c 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -3,8 +3,8 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_diag_trait_item; use clippy_utils::macros::{ - find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, - is_format_macro, is_panic, matching_root_macro_call, root_macro_call_first_node, FormatParamUsage, MacroCall, + find_format_arg_expr, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, is_format_macro, + is_panic, matching_root_macro_call, root_macro_call_first_node, FormatArgsStorage, FormatParamUsage, MacroCall, }; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_lang_item}; @@ -167,15 +167,18 @@ impl_lint_pass!(FormatArgs => [ UNUSED_FORMAT_SPECS, ]); +#[allow(clippy::struct_field_names)] pub struct FormatArgs { + format_args: FormatArgsStorage, msrv: Msrv, ignore_mixed: bool, } impl FormatArgs { #[must_use] - pub fn new(msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self { + pub fn new(format_args: FormatArgsStorage, msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self { Self { + format_args, msrv, ignore_mixed: allow_mixed_uninlined_format_args, } @@ -186,13 +189,13 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some(macro_call) = root_macro_call_first_node(cx, expr) && is_format_macro(cx, macro_call.def_id) - && let Some(format_args) = find_format_args(cx, expr, macro_call.expn) + && let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) { let linter = FormatArgsExpr { cx, expr, macro_call: ¯o_call, - format_args: &format_args, + format_args, ignore_mixed: self.ignore_mixed, }; @@ -421,14 +424,14 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> { count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter()) && implements_trait(cx, target, display_trait_id, &[]) && let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait() - && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) + && let Some(receiver_snippet) = snippet_opt(cx, receiver.span.source_callsite()) { let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]); if n_needed_derefs == 0 && !needs_ref { span_lint_and_sugg( cx, TO_STRING_IN_FORMAT_ARGS, - to_string_span.with_lo(receiver.span.hi()), + to_string_span.with_lo(receiver.span.source_callsite().hi()), format!("`to_string` applied to a type that implements `Display` in `{name}!` args"), "remove this", String::new(), diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs index 0a52347940ab..09be7237b5ca 100644 --- a/src/tools/clippy/clippy_lints/src/format_impl.rs +++ b/src/tools/clippy/clippy_lints/src/format_impl.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::macros::{find_format_arg_expr, find_format_args, is_format_macro, root_macro_call_first_node}; +use clippy_utils::macros::{find_format_arg_expr, is_format_macro, root_macro_call_first_node, FormatArgsStorage}; use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators}; use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_errors::Applicability; @@ -99,13 +99,15 @@ struct FormatTraitNames { #[derive(Default)] pub struct FormatImpl { + format_args: FormatArgsStorage, // Whether we are inside Display or Debug trait impl - None for neither format_trait_impl: Option, } impl FormatImpl { - pub fn new() -> Self { + pub fn new(format_args: FormatArgsStorage) -> Self { Self { + format_args, format_trait_impl: None, } } @@ -129,6 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl { if let Some(format_trait_impl) = self.format_trait_impl { let linter = FormatImplExpr { cx, + format_args: &self.format_args, expr, format_trait_impl, }; @@ -141,6 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl { struct FormatImplExpr<'a, 'tcx> { cx: &'a LateContext<'tcx>, + format_args: &'a FormatArgsStorage, expr: &'tcx Expr<'tcx>, format_trait_impl: FormatTraitNames, } @@ -175,7 +179,7 @@ impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> { if let Some(outer_macro) = root_macro_call_first_node(self.cx, self.expr) && let macro_def_id = outer_macro.def_id && is_format_macro(self.cx, macro_def_id) - && let Some(format_args) = find_format_args(self.cx, self.expr, outer_macro.expn) + && let Some(format_args) = self.format_args.get(self.cx, self.expr, outer_macro.expn) { for piece in &format_args.template { if let FormatArgsPiece::Placeholder(placeholder) = piece diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs index 2b08437d8271..a75538dd329b 100644 --- a/src/tools/clippy/clippy_lints/src/format_push_string.rs +++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs @@ -11,7 +11,7 @@ declare_clippy_lint! { /// Detects cases where the result of a `format!` call is /// appended to an existing `String`. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Introduces an extra, avoidable heap allocation. /// /// ### Known problems diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs index 633ed96d6a6d..82ce501bac59 100644 --- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs +++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_integer_literal; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::{in_constant, is_integer_literal}; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -47,6 +47,9 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind && let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind + // do not lint in constant context, because the suggestion won't work. + // NB: keep this check until a new `const_trait_impl` is available and stablized. + && !in_constant(cx, exp.hir_id) // check if the first part of the path is some integer primitive && let TyKind::Path(ty_qpath) = &ty.kind diff --git a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs index 7729c556e1fc..2cbc4b072341 100644 --- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs +++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, Safety, ImplicitSelfKind}; +use rustc_hir::{Body, ExprKind, FnDecl, ImplicitSelfKind, Safety}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index 9cc51fa8cd5d..26534492dddd 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -2,15 +2,17 @@ mod impl_trait_in_params; mod misnamed_getters; mod must_use; mod not_unsafe_ptr_arg_deref; +mod renamed_function_params; mod result; mod too_many_arguments; mod too_many_lines; +use clippy_utils::def_path_def_ids; use rustc_hir as hir; use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::def_id::LocalDefId; +use rustc_span::def_id::{DefIdSet, LocalDefId}; use rustc_span::Span; declare_clippy_lint! { @@ -336,8 +338,10 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Lints when `impl Trait` is being used in a function's parameters. - /// ### Why is this bad? - /// Turbofish syntax (`::<>`) cannot be used when `impl Trait` is being used, making `impl Trait` less powerful. Readability may also be a factor. + /// + /// ### Why restrict this? + /// Turbofish syntax (`::<>`) cannot be used to specify the type of an `impl Trait` parameter, + /// making `impl Trait` less powerful. Readability may also be a factor. /// /// ### Example /// ```no_run @@ -359,13 +363,50 @@ declare_clippy_lint! { "`impl Trait` is used in the function's parameters" } -#[derive(Copy, Clone)] -#[allow(clippy::struct_field_names)] +declare_clippy_lint! { + /// ### What it does + /// Lints when the name of function parameters from trait impl is + /// different than its default implementation. + /// + /// ### Why restrict this? + /// Using the default name for parameters of a trait method is more consistent. + /// + /// ### Example + /// ```rust + /// struct A(u32); + /// + /// impl PartialEq for A { + /// fn eq(&self, b: &Self) -> bool { + /// self.0 == b.0 + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct A(u32); + /// + /// impl PartialEq for A { + /// fn eq(&self, other: &Self) -> bool { + /// self.0 == other.0 + /// } + /// } + /// ``` + #[clippy::version = "1.74.0"] + pub RENAMED_FUNCTION_PARAMS, + restriction, + "renamed function parameters in trait implementation" +} + +#[derive(Clone)] pub struct Functions { too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64, avoid_breaking_exported_api: bool, + allow_renamed_params_for: Vec, + /// A set of resolved `def_id` of traits that are configured to allow + /// function params renaming. + trait_ids: DefIdSet, } impl Functions { @@ -374,12 +415,15 @@ impl Functions { too_many_lines_threshold: u64, large_error_threshold: u64, avoid_breaking_exported_api: bool, + allow_renamed_params_for: Vec, ) -> Self { Self { too_many_arguments_threshold, too_many_lines_threshold, large_error_threshold, avoid_breaking_exported_api, + allow_renamed_params_for, + trait_ids: DefIdSet::default(), } } } @@ -395,6 +439,7 @@ impl_lint_pass!(Functions => [ RESULT_LARGE_ERR, MISNAMED_GETTERS, IMPL_TRAIT_IN_PARAMS, + RENAMED_FUNCTION_PARAMS, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -424,6 +469,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { must_use::check_impl_item(cx, item); result::check_impl_item(cx, item, self.large_error_threshold); impl_trait_in_params::check_impl_item(cx, item); + renamed_function_params::check_impl_item(cx, item, &self.trait_ids); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { @@ -433,4 +479,12 @@ impl<'tcx> LateLintPass<'tcx> for Functions { result::check_trait_item(cx, item, self.large_error_threshold); impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api); } + + fn check_crate(&mut self, cx: &LateContext<'tcx>) { + for path in &self.allow_renamed_params_for { + let path_segments: Vec<&str> = path.split("::").collect(); + let ids = def_path_def_ids(cx, &path_segments); + self.trait_ids.extend(ids); + } + } } diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index e7ec2b3151e6..cce8617821e2 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -14,7 +14,7 @@ use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_must_use_ty; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{return_ty, trait_ref_of_method}; use core::ops::ControlFlow; @@ -226,7 +226,7 @@ fn is_mutated_static(e: &hir::Expr<'_>) -> bool { } fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool { - for_each_expr(body.value, |e| { + for_each_expr_without_closures(body.value, |e| { use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; match e.kind { diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index b44a5f20ef68..1aeefe73cf68 100644 --- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -5,7 +5,7 @@ use rustc_span::def_id::LocalDefId; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::type_is_unsafe_function; -use clippy_utils::visitors::for_each_expr_with_closures; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{iter_input_pats, path_to_local}; use core::ops::ControlFlow; @@ -49,7 +49,7 @@ fn check_raw_ptr<'tcx>( if !raw_ptrs.is_empty() { let typeck = cx.tcx.typeck_body(body.id()); - let _: Option = for_each_expr_with_closures(cx, body.value, |e| { + let _: Option = for_each_expr(cx, body.value, |e| { match e.kind { hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => { for arg in args { diff --git a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs new file mode 100644 index 000000000000..c7de0385c021 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs @@ -0,0 +1,110 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_errors::{Applicability, MultiSpan}; +use rustc_hir::def_id::{DefId, DefIdSet}; +use rustc_hir::hir_id::OwnerId; +use rustc_hir::{Impl, ImplItem, ImplItemKind, ImplItemRef, ItemKind, Node, TraitRef}; +use rustc_lint::LateContext; +use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_span::Span; + +use super::RENAMED_FUNCTION_PARAMS; + +pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored_traits: &DefIdSet) { + if !item.span.from_expansion() + && let ImplItemKind::Fn(_, body_id) = item.kind + && let parent_node = cx.tcx.parent_hir_node(item.hir_id()) + && let Node::Item(parent_item) = parent_node + && let ItemKind::Impl(Impl { + items, + of_trait: Some(trait_ref), + .. + }) = &parent_item.kind + && let Some(did) = trait_item_def_id_of_impl(items, item.owner_id) + && !is_from_ignored_trait(trait_ref, ignored_traits) + { + let mut param_idents_iter = cx.tcx.hir().body_param_names(body_id); + let mut default_param_idents_iter = cx.tcx.fn_arg_names(did).iter().copied(); + + let renames = RenamedFnArgs::new(&mut default_param_idents_iter, &mut param_idents_iter); + if !renames.0.is_empty() { + let multi_span = renames.multi_span(); + let plural = if renames.0.len() == 1 { "" } else { "s" }; + span_lint_and_then( + cx, + RENAMED_FUNCTION_PARAMS, + multi_span, + format!("renamed function parameter{plural} of trait impl"), + |diag| { + diag.multipart_suggestion( + format!("consider using the default name{plural}"), + renames.0, + Applicability::Unspecified, + ); + }, + ); + } + } +} + +struct RenamedFnArgs(Vec<(Span, String)>); + +impl RenamedFnArgs { + /// Comparing between an iterator of default names and one with current names, + /// then collect the ones that got renamed. + fn new(default_names: &mut I, current_names: &mut T) -> Self + where + I: Iterator, + T: Iterator, + { + let mut renamed: Vec<(Span, String)> = vec![]; + + debug_assert!(default_names.size_hint() == current_names.size_hint()); + while let (Some(def_name), Some(cur_name)) = (default_names.next(), current_names.next()) { + let current_name = cur_name.name; + let default_name = def_name.name; + if is_unused_or_empty_symbol(current_name) || is_unused_or_empty_symbol(default_name) { + continue; + } + if current_name != default_name { + renamed.push((cur_name.span, default_name.to_string())); + } + } + + Self(renamed) + } + + fn multi_span(&self) -> MultiSpan { + self.0 + .iter() + .map(|(span, _)| span) + .copied() + .collect::>() + .into() + } +} + +fn is_unused_or_empty_symbol(symbol: Symbol) -> bool { + // FIXME: `body_param_names` currently returning empty symbols for `wild` as well, + // so we need to check if the symbol is empty first. + // Therefore the check of whether it's equal to [`kw::Underscore`] has no use for now, + // but it would be nice to keep it here just to be future-proof. + symbol.is_empty() || symbol == kw::Underscore || symbol.as_str().starts_with('_') +} + +/// Get the [`trait_item_def_id`](ImplItemRef::trait_item_def_id) of a relevant impl item. +fn trait_item_def_id_of_impl(items: &[ImplItemRef], target: OwnerId) -> Option { + items.iter().find_map(|item| { + if item.id.owner_id == target { + item.trait_item_def_id + } else { + None + } + }) +} + +fn is_from_ignored_trait(of_trait: &TraitRef<'_>, ignored_traits: &DefIdSet) -> bool { + let Some(trait_did) = of_trait.trait_def_id() else { + return false; + }; + ignored_traits.contains(&trait_did) +} diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs index 2c2daac02349..cb1d0de1edff 100644 --- a/src/tools/clippy/clippy_lints/src/future_not_send.rs +++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs @@ -4,8 +4,8 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind}; use rustc_middle::ty::print::PrintTraitRefExt; +use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{sym, Span}; @@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap(); let span = decl.output.span(); let infcx = cx.tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(&infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let cause = traits::ObligationCause::misc(span, fn_def_id); ocx.register_bound(cause, cx.param_env, ret_ty, send_trait); let send_errors = ocx.select_all_or_error(); diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs index f5ba62ae432e..0b2008152198 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -15,7 +15,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for if-else that could be written using either `bool::then` or `bool::then_some`. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity. /// For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated /// in comparison to `bool::then`. diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index a46aae36d5c5..ca830af3b2f3 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -328,7 +328,7 @@ impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; - fn visit_body(&mut self, body: &'tcx Body<'_>) { + fn visit_body(&mut self, body: &Body<'tcx>) { let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body.id())); walk_body(self, body); self.maybe_typeck_results = old_maybe_typeck_results; diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs index 5288efd8df8c..2f543781c44c 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_return.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{get_async_fn_body, is_async_fn}; use core::ops::ControlFlow; use rustc_errors::Applicability; @@ -16,12 +16,13 @@ declare_clippy_lint! { /// ### What it does /// Checks for missing return statements at the end of a block. /// - /// ### Why is this bad? - /// Actually omitting the return keyword is idiomatic Rust code. Programmers - /// coming from other languages might prefer the expressiveness of `return`. It's possible to miss - /// the last returning statement because the only difference is a missing `;`. Especially in bigger - /// code with multiple return paths having a `return` keyword makes it easier to find the - /// corresponding statements. + /// ### Why restrict this? + /// Omitting the return keyword whenever possible is idiomatic Rust code, but: + /// + /// * Programmers coming from other languages might prefer the expressiveness of `return`. + /// * It's possible to miss the last returning statement because the only difference is a missing `;`. + /// * Especially in bigger code with multiple return paths, having a `return` keyword makes it easier to find the + /// corresponding statements. /// /// ### Example /// ```no_run @@ -152,7 +153,7 @@ fn lint_implicit_returns( ExprKind::Loop(block, ..) => { let mut add_return = false; - let _: Option = for_each_expr(block, |e| { + let _: Option = for_each_expr_without_closures(block, |e| { if let ExprKind::Break(dest, sub_expr) = e.kind { if dest.target_id.ok() == Some(expr.hir_id) { if call_site_span.is_none() && e.span.ctxt() == ctxt { diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index dc935ed3d7fe..170ecf896b4e 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -3,8 +3,8 @@ use clippy_utils::source::snippet; use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::def_id::DefId; use rustc_hir::{ - GenericArg, GenericBound, GenericBounds, ItemKind, PredicateOrigin, TraitBoundModifier, TyKind, TypeBinding, - WherePredicate, + AssocItemConstraint, GenericArg, GenericBound, GenericBounds, ItemKind, PredicateOrigin, TraitBoundModifier, + TyKind, WherePredicate, }; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -54,9 +54,9 @@ fn emit_lint( poly_trait: &rustc_hir::PolyTraitRef<'_>, bounds: GenericBounds<'_>, index: usize, - // The bindings that were implied, used for suggestion purposes since removing a bound with associated types - // means we might need to then move it to a different bound - implied_bindings: &[TypeBinding<'_>], + // The constraints that were implied, used for suggestion purposes since removing a bound with + // associated types means we might need to then move it to a different bound. + implied_constraints: &[AssocItemConstraint<'_>], bound: &ImplTraitBound<'_>, ) { let implied_by = snippet(cx, bound.span, ".."); @@ -83,29 +83,29 @@ fn emit_lint( let mut sugg = vec![(implied_span_extended, String::new())]; - // We also might need to include associated type binding that were specified in the implied bound, - // but omitted in the implied-by bound: + // We also might need to include associated item constraints that were specified in the implied + // bound, but omitted in the implied-by bound: // `fn f() -> impl Deref + DerefMut` // If we're going to suggest removing `Deref<..>`, we'll need to put `` on `DerefMut` - let omitted_assoc_tys: Vec<_> = implied_bindings + let omitted_constraints: Vec<_> = implied_constraints .iter() - .filter(|binding| !bound.bindings.iter().any(|b| b.ident == binding.ident)) + .filter(|constraint| !bound.constraints.iter().any(|c| c.ident == constraint.ident)) .collect(); - if !omitted_assoc_tys.is_empty() { - // `<>` needs to be added if there aren't yet any generic arguments or bindings - let needs_angle_brackets = bound.args.is_empty() && bound.bindings.is_empty(); - let insert_span = match (bound.args, bound.bindings) { - ([.., arg], [.., binding]) => arg.span().max(binding.span).shrink_to_hi(), + if !omitted_constraints.is_empty() { + // `<>` needs to be added if there aren't yet any generic arguments or constraints + let needs_angle_brackets = bound.args.is_empty() && bound.constraints.is_empty(); + let insert_span = match (bound.args, bound.constraints) { + ([.., arg], [.., constraint]) => arg.span().max(constraint.span).shrink_to_hi(), ([.., arg], []) => arg.span().shrink_to_hi(), - ([], [.., binding]) => binding.span.shrink_to_hi(), + ([], [.., constraint]) => constraint.span.shrink_to_hi(), ([], []) => bound.span.shrink_to_hi(), }; - let mut associated_tys_sugg = if needs_angle_brackets { + let mut constraints_sugg = if needs_angle_brackets { "<".to_owned() } else { - // If angle brackets aren't needed (i.e., there are already generic arguments or bindings), + // If angle brackets aren't needed (i.e., there are already generic arguments or constraints), // we need to add a comma: // `impl A` // ^ if we insert `Assoc=i32` without a comma here, that'd be invalid syntax: @@ -113,16 +113,16 @@ fn emit_lint( ", ".to_owned() }; - for (index, binding) in omitted_assoc_tys.into_iter().enumerate() { + for (index, constraint) in omitted_constraints.into_iter().enumerate() { if index > 0 { - associated_tys_sugg += ", "; + constraints_sugg += ", "; } - associated_tys_sugg += &snippet(cx, binding.span, ".."); + constraints_sugg += &snippet(cx, constraint.span, ".."); } if needs_angle_brackets { - associated_tys_sugg += ">"; + constraints_sugg += ">"; } - sugg.push((insert_span, associated_tys_sugg)); + sugg.push((insert_span, constraints_sugg)); } diag.multipart_suggestion_with_style( @@ -229,8 +229,8 @@ struct ImplTraitBound<'tcx> { trait_def_id: DefId, /// The generic arguments on the `impl Trait` bound args: &'tcx [GenericArg<'tcx>], - /// The associated types on this bound - bindings: &'tcx [TypeBinding<'tcx>], + /// The associated item constraints of this bound + constraints: &'tcx [AssocItemConstraint<'tcx>], } /// Given an `impl Trait` type, gets all the supertraits from each bound ("implied bounds"). @@ -253,7 +253,7 @@ fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds Some(ImplTraitBound { predicates, args: path.args.map_or([].as_slice(), |p| p.args), - bindings: path.args.map_or([].as_slice(), |p| p.bindings), + constraints: path.args.map_or([].as_slice(), |p| p.constraints), trait_def_id, span: bound.span(), }) @@ -310,20 +310,20 @@ fn check<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds<'tcx>) { if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound && let [.., path] = poly_trait.trait_ref.path.segments && let implied_args = path.args.map_or([].as_slice(), |a| a.args) - && let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings) + && let implied_constraints = path.args.map_or([].as_slice(), |a| a.constraints) && let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id() && let Some(bound) = find_bound_in_supertraits(cx, def_id, implied_args, &supertraits) // If the implied bound has a type binding that also exists in the implied-by trait, // then we shouldn't lint. See #11880 for an example. && let assocs = cx.tcx.associated_items(bound.trait_def_id) - && !implied_bindings.iter().any(|binding| { + && !implied_constraints.iter().any(|constraint| { assocs - .filter_by_name_unhygienic(binding.ident.name) + .filter_by_name_unhygienic(constraint.ident.name) .next() .is_some_and(|assoc| assoc.kind == ty::AssocKind::Type) }) { - emit_lint(cx, poly_trait, bounds, index, implied_bindings, bound); + emit_lint(cx, poly_trait, bounds, index, implied_constraints, bound); } } } diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index 35fcd8cdd354..d54f2af65cde 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -2,12 +2,14 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::higher; +use clippy_utils::ty::{deref_chain, get_adt_inherent_method}; +use clippy_utils::{higher, is_from_proc_macro}; use rustc_ast::ast::RangeLimits; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -45,9 +47,10 @@ declare_clippy_lint! { /// does report on arrays if we can tell that slicing operations are in bounds and does not /// lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint. /// - /// ### Why is this bad? - /// Indexing and slicing can panic at runtime and there are - /// safe alternatives. + /// ### Why restrict this? + /// To avoid implicit panics from indexing and slicing. + /// There are “checked” alternatives which do not panic, and can be used with `unwrap()` to make + /// an explicit panic when it is desired. /// /// ### Example /// ```rust,no_run @@ -99,11 +102,21 @@ impl IndexingSlicing { impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id) { + if (self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id)) + || is_from_proc_macro(cx, expr) + { return; } - if let ExprKind::Index(array, index, _) = &expr.kind { + if let ExprKind::Index(array, index, _) = &expr.kind + && let expr_ty = cx.typeck_results().expr_ty(array) + && let mut deref = deref_chain(cx, expr_ty) + && deref.any(|l| { + l.peel_refs().is_slice() + || l.peel_refs().is_array() + || ty_has_applicable_get_function(cx, l.peel_refs(), expr_ty, expr) + }) + { let note = "the suggestion might not be applicable in constant blocks"; let ty = cx.typeck_results().expr_ty(array).peel_refs(); if let Some(range) = higher::Range::hir(index) { @@ -230,3 +243,33 @@ fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u1 (start, end) } + +/// Checks if the output Ty of the `get` method on this Ty (if any) matches the Ty returned by the +/// indexing operation (if any). +fn ty_has_applicable_get_function<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + array_ty: Ty<'tcx>, + index_expr: &Expr<'_>, +) -> bool { + if let ty::Adt(_, _) = array_ty.kind() + && let Some(get_output_ty) = get_adt_inherent_method(cx, ty, sym!(get)).map(|m| { + cx.tcx + .fn_sig(m.def_id) + .skip_binder() + .output() + .skip_binder() + }) + && let ty::Adt(def, args) = get_output_ty.kind() + && cx.tcx.is_diagnostic_item(sym::Option, def.0.did) + && let Some(option_generic_param) = args.first() + && let generic_ty = option_generic_param.expect_ty().peel_refs() + // FIXME: ideally this would handle type params and projections properly, for now just assume it's the same type + && (cx.typeck_results().expr_ty(index_expr).peel_refs() == generic_ty.peel_refs() + || matches!(generic_ty.peel_refs().kind(), ty::Param(_) | ty::Alias(_, _))) + { + true + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs index 1127f00abde0..95ae591884bc 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -14,7 +14,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for multiple inherent implementations of a struct /// - /// ### Why is this bad? + /// ### Why restrict this? /// Splitting the implementation of a type makes the code harder to navigate. /// /// ### Example diff --git a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs index e486563808a5..1c8fd0a27f98 100644 --- a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs +++ b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields { snippet_with_applicability(cx, path.span(), "..", &mut appl), expr_spans .into_iter_sorted() - .map(|(_, span)| snippet_with_applicability(cx, span, "..", &mut appl)) + .map(|(_, span)| snippet_with_context(cx, span, path.span().ctxt(), "..", &mut appl).0) .intersperse(Cow::Borrowed(", ")) .collect::() ); diff --git a/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs b/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs index a3577b765c03..a1215491b48c 100644 --- a/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs +++ b/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs @@ -7,10 +7,10 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for the usage of division (/) and remainder (%) operations - /// when performed on any integer types using the default Div and Rem trait implementations. + /// Checks for the usage of division (`/`) and remainder (`%`) operations + /// when performed on any integer types using the default `Div` and `Rem` trait implementations. /// - /// ### Why is this bad? + /// ### Why restrict this? /// In cryptographic contexts, division can result in timing sidechannel vulnerabilities, /// and needs to be replaced with constant-time code instead (e.g. Barrett reduction). /// @@ -22,7 +22,7 @@ declare_clippy_lint! { /// ```no_run /// let my_div = 10 >> 1; /// ``` - #[clippy::version = "1.78.0"] + #[clippy::version = "1.79.0"] pub INTEGER_DIVISION_REMAINDER_USED, restriction, "use of disallowed default division and remainder operations" diff --git a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs index 6c6eff9ba48b..fb29d9824179 100644 --- a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs +++ b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs @@ -14,8 +14,8 @@ declare_clippy_lint! { /// ### What it does /// This is a restriction lint which prevents the use of hash types (i.e., `HashSet` and `HashMap`) in for loops. /// - /// ### Why is this bad? - /// Because hash types are unordered, when iterated through such as in a for loop, the values are returned in + /// ### Why restrict this? + /// Because hash types are unordered, when iterated through such as in a `for` loop, the values are returned in /// an undefined order. As a result, on redundant systems this may cause inconsistencies and anomalies. /// In addition, the unknown order of the elements may reduce readability or introduce other undesired /// side effects. diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs index a75dfaf286fd..6b03f2597b08 100644 --- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs +++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_parent_as_impl; use clippy_utils::source::snippet; -use clippy_utils::ty::{implements_trait, make_normalized_projection}; +use clippy_utils::ty::{deref_chain, get_adt_inherent_method, implements_trait, make_normalized_projection}; use rustc_ast::Mutability; use rustc_errors::Applicability; use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind}; @@ -9,8 +9,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Symbol}; -use std::iter; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -124,33 +123,6 @@ fn is_ty_exported(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { .is_some_and(|did| cx.effective_visibilities.is_exported(did)) } -/// Returns the deref chain of a type, starting with the type itself. -fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl Iterator> + 'cx { - iter::successors(Some(ty), |&ty| { - if let Some(deref_did) = cx.tcx.lang_items().deref_trait() - && implements_trait(cx, ty, deref_did, &[]) - { - make_normalized_projection(cx.tcx, cx.param_env, deref_did, sym::Target, [ty]) - } else { - None - } - }) -} - -fn adt_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool { - if let Some(ty_did) = ty.ty_adt_def().map(ty::AdtDef::did) { - cx.tcx.inherent_impls(ty_did).into_iter().flatten().any(|&did| { - cx.tcx - .associated_items(did) - .filter_by_name_unhygienic(method_name) - .next() - .is_some_and(|item| item.kind == ty::AssocKind::Fn) - }) - } else { - false - } -} - impl LateLintPass<'_> for IterWithoutIntoIter { fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) { if !in_external_macro(cx.sess(), item.span) @@ -167,7 +139,7 @@ impl LateLintPass<'_> for IterWithoutIntoIter { } && !deref_chain(cx, ty).any(|ty| { // We can't check inherent impls for slices, but we know that they have an `iter(_mut)` method - ty.peel_refs().is_slice() || adt_has_inherent_method(cx, ty, expected_method_name) + ty.peel_refs().is_slice() || get_adt_inherent_method(cx, ty, expected_method_name).is_some() }) && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| { if item.ident.name == sym!(IntoIter) { @@ -225,7 +197,7 @@ impl {self_ty_without_ref} {{ && let ImplItemKind::Fn(sig, _) = item.kind && let FnRetTy::Return(ret) = sig.decl.output && is_nameable_in_impl_trait(ret) - && cx.tcx.generics_of(item_did).own_params.is_empty() + && cx.tcx.generics_of(item_did).is_own_empty() && sig.decl.implicit_self == expected_implicit_self && sig.decl.inputs.len() == 1 && let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()) diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs index b561054b5824..7f8197c0cc01 100644 --- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs @@ -54,8 +54,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { && generics.params.is_empty() && !generics.has_where_clause_predicates && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() && let ty::Array(element_type, cst) = ty.kind() - && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind() - && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx) + && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind() + && let element_count = element_count.to_target_usize(cx.tcx) && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) { diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs index 790bed580fd1..07efee159aab 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -10,10 +10,12 @@ use rustc_span::sym; declare_clippy_lint! { /// ### What it does /// Checks for the inclusion of large files via `include_bytes!()` - /// and `include_str!()` + /// or `include_str!()`. /// - /// ### Why is this bad? - /// Including large files can increase the size of the binary + /// ### Why restrict this? + /// Including large files can undesirably increase the size of the binary produced by the compiler. + /// This lint may be used to catch mistakes where an unexpectedly large file is included, or + /// temporarily to obtain a list of all large files. /// /// ### Example /// ```rust,ignore diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs index 208d1bb6e68a..c9bfc9c85d95 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs @@ -64,8 +64,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind && !self.is_from_vec_macro(cx, expr.span) && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind() - && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind() - && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx) + && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind() + && let element_count = element_count.to_target_usize(cx.tcx) && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) && !cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, node)| { matches!( diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs index 00124dcdd91d..eadfeb6e3418 100644 --- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs +++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs @@ -28,7 +28,7 @@ declare_clippy_lint! { /// ```rust /// let eps = f32::EPSILON; /// ``` - #[clippy::version = "1.72.0"] + #[clippy::version = "1.79.0"] pub LEGACY_NUMERIC_CONSTANTS, style, "checks for usage of legacy std numeric constants and methods" diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 97a245b76d44..57e0a7aa2c7e 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -9,7 +9,7 @@ use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatKind, PathSegment, PrimTy, QPath, - TraitItemRef, TyKind, TypeBindingKind, + TraitItemRef, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; @@ -253,7 +253,7 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items // fill the set with current and super traits fn fill_trait_set(traitt: DefId, set: &mut DefIdSet, cx: &LateContext<'_>) { if set.insert(traitt) { - for supertrait in rustc_trait_selection::traits::supertrait_def_ids(cx.tcx, traitt) { + for supertrait in cx.tcx.supertrait_def_ids(traitt) { fill_trait_set(supertrait, set, cx); } } @@ -307,17 +307,12 @@ fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<& && let [GenericBound::Trait(trait_ref, _)] = &opaque.bounds && let Some(segment) = trait_ref.trait_ref.path.segments.last() && let Some(generic_args) = segment.args - && generic_args.bindings.len() == 1 - && let TypeBindingKind::Equality { - term: - rustc_hir::Term::Ty(rustc_hir::Ty { - kind: TyKind::Path(QPath::Resolved(_, path)), - .. - }), - } = &generic_args.bindings[0].kind - && path.segments.len() == 1 + && let [constraint] = generic_args.constraints + && let Some(ty) = constraint.ty() + && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind + && let [segment] = path.segments { - return Some(&path.segments[0]); + return Some(segment); } None diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index 619e933b4fff..9fd4f509aa47 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -12,9 +12,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for `let _ = ` where expr is `#[must_use]` /// - /// ### Why is this bad? - /// It's better to explicitly handle the value of a `#[must_use]` - /// expr + /// ### Why restrict this? + /// To ensure that all `#[must_use]` types are used rather than ignored. /// /// ### Example /// ```no_run @@ -96,8 +95,8 @@ declare_clippy_lint! { /// Checks for `let _ = ` without a type annotation, and suggests to either provide one, /// or remove the `let` keyword altogether. /// - /// ### Why is this bad? - /// The `let _ = ` expression ignores the value of `` but will remain doing so even + /// ### Why restrict this? + /// The `let _ = ` expression ignores the value of ``, but will continue to do so even /// if the type were to change, thus potentially introducing subtle bugs. By supplying a type /// annotation, one will be forced to re-visit the decision to ignore the value in such cases. /// diff --git a/src/tools/clippy/clippy_lints/src/lib.deprecated.rs b/src/tools/clippy/clippy_lints/src/lib.deprecated.rs index 80bde1b11384..0d21261822dd 100644 --- a/src/tools/clippy/clippy_lints/src/lib.deprecated.rs +++ b/src/tools/clippy/clippy_lints/src/lib.deprecated.rs @@ -67,4 +67,12 @@ "clippy::wrong_pub_self_convention", "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items", ); + store.register_removed( + "clippy::maybe_misused_cfg", + "this lint has been replaced by `unexpected_cfgs`", + ); + store.register_removed( + "clippy::mismatched_target_os", + "this lint has been replaced by `unexpected_cfgs`", + ); } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index a8bfbbdd9eca..c65581d5203e 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -61,11 +61,6 @@ extern crate clippy_utils; #[macro_use] extern crate declare_clippy_lint; -use std::collections::BTreeMap; - -use rustc_data_structures::fx::FxHashSet; -use rustc_lint::{Lint, LintId}; - #[cfg(feature = "internal")] pub mod deprecated_lints; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] @@ -199,6 +194,7 @@ mod lifetimes; mod lines_filter_map_ok; mod literal_representation; mod loops; +mod macro_metavars_in_unsafe; mod macro_use; mod main_recursion; mod manual_assert; @@ -255,6 +251,7 @@ mod needless_else; mod needless_for_each; mod needless_if; mod needless_late_init; +mod needless_maybe_sized; mod needless_parens_on_range_literals; mod needless_pass_by_ref_mut; mod needless_pass_by_value; @@ -329,6 +326,7 @@ mod size_of_in_element_count; mod size_of_ref; mod slow_vector_initialization; mod std_instead_of_core; +mod string_patterns; mod strings; mod strlen_on_c_strings; mod suspicious_operation_groupings; @@ -385,6 +383,10 @@ mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` use clippy_config::{get_configuration_metadata, Conf}; +use clippy_utils::macros::FormatArgsStorage; +use rustc_data_structures::fx::FxHashSet; +use rustc_lint::{Lint, LintId}; +use std::collections::BTreeMap; /// Register all pre expansion lints /// @@ -533,6 +535,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { allow_expect_in_tests, allow_mixed_uninlined_format_args, allow_one_hash_in_raw_strings, + allow_panic_in_tests, allow_print_in_tests, allow_private_module_inception, allow_unwrap_in_tests, @@ -597,9 +600,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { ref allowed_duplicate_crates, allow_comparison_to_zero, ref allowed_prefixes, + ref allow_renamed_params_for, blacklisted_names: _, cyclomatic_complexity_threshold: _, + warn_unsafe_macro_metavars_in_private_macros, } = *conf; let msrv = || msrv.clone(); @@ -616,6 +621,14 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { } } + let format_args_storage = FormatArgsStorage::default(); + let format_args = format_args_storage.clone(); + store.register_early_pass(move || { + Box::new(utils::format_args_collector::FormatArgsCollector::new( + format_args.clone(), + )) + }); + // all the internal lints #[cfg(feature = "internal")] { @@ -656,7 +669,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { .collect(), )) }); - store.register_early_pass(|| Box::::default()); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|_| Box::new(utils::author::Author)); store.register_late_pass(move |_| { @@ -698,6 +710,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); + let format_args = format_args_storage.clone(); store.register_late_pass(move |_| { Box::new(methods::Methods::new( avoid_breaking_exported_api, @@ -705,6 +718,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { allow_expect_in_tests, allow_unwrap_in_tests, allowed_dotfiles.clone(), + format_args.clone(), )) }); store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv()))); @@ -759,7 +773,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { allow_in_test: allow_useless_vec_in_tests, }) }); - store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented)); + store.register_late_pass(move |_| Box::new(panic_unimplemented::PanicUnimplemented { allow_panic_in_tests })); store.register_late_pass(|_| Box::new(strings::StringLitAsBytes)); store.register_late_pass(|_| Box::new(derive::Derive)); store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(msrv()))); @@ -769,7 +783,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone()))); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); - store.register_late_pass(|_| Box::new(format::UselessFormat)); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone()))); store.register_late_pass(|_| Box::new(swap::Swap)); store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional)); store.register_late_pass(|_| Box::::default()); @@ -780,6 +795,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { too_many_lines_threshold, large_error_threshold, avoid_breaking_exported_api, + allow_renamed_params_for.clone(), )) }); store.register_late_pass(move |_| Box::new(doc::Documentation::new(doc_valid_idents, check_private_items))); @@ -793,7 +809,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)); store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount)); store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold))); - store.register_late_pass(|_| Box::new(explicit_write::ExplicitWrite)); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| Box::new(explicit_write::ExplicitWrite::new(format_args.clone()))); store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue)); store.register_late_pass(move |tcx| { Box::new(pass_by_ref_or_value::PassByRefOrValue::new( @@ -835,7 +852,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone()))); store.register_early_pass(|| Box::new(reference::DerefAddrOf)); store.register_early_pass(|| Box::new(double_parens::DoubleParens)); - store.register_late_pass(|_| Box::new(format_impl::FormatImpl::new())); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone()))); store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval)); store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse)); store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne)); @@ -961,8 +979,14 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { accept_comment_above_attributes, )) }); - store - .register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined_format_args))); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| { + Box::new(format_args::FormatArgs::new( + format_args.clone(), + msrv(), + allow_mixed_uninlined_format_args, + )) + }); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); @@ -973,7 +997,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); - store.register_late_pass(move |_| Box::new(write::Write::new(allow_print_in_tests))); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| Box::new(write::Write::new(format_args.clone(), allow_print_in_tests))); store.register_late_pass(move |_| { Box::new(cargo::Cargo { ignore_publish: cargo_ignore_publish, @@ -1035,6 +1060,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi)); store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead)); store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage)); + store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized)); store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock)); store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute)); @@ -1136,6 +1162,13 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); store.register_late_pass(|_| Box::new(manual_unwrap_or_default::ManualUnwrapOrDefault)); store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed)); + store.register_late_pass(move |_| { + Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe { + warn_unsafe_macro_metavars_in_private_macros, + ..Default::default() + }) + }); + store.register_late_pass(|_| Box::new(string_patterns::StringPatterns)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index 2348dd18220f..d2a140a36a83 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -132,8 +132,8 @@ declare_clippy_lint! { /// ### What it does /// Warns if there is a better representation for a numeric literal. /// - /// ### Why is this bad? - /// Especially for big powers of 2 a hexadecimal representation is more + /// ### Why restrict this? + /// Especially for big powers of 2, a hexadecimal representation is usually more /// readable than a decimal representation. /// /// ### Example diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index b5e39b33c6a1..086829421530 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -17,6 +17,7 @@ mod same_item_push; mod single_element_loop; mod unused_enumerate_index; mod utils; +mod while_float; mod while_immutable_condition; mod while_let_loop; mod while_let_on_iterator; @@ -416,6 +417,39 @@ declare_clippy_lint! { "variables used within while expression are not mutated in the body" } +declare_clippy_lint! { + /// ### What it does + /// Checks for while loops comparing floating point values. + /// + /// ### Why is this bad? + /// If you increment floating point values, errors can compound, + /// so, use integers instead if possible. + /// + /// ### Known problems + /// The lint will catch all while loops comparing floating point + /// values without regarding the increment. + /// + /// ### Example + /// ```no_run + /// let mut x = 0.0; + /// while x < 42.0 { + /// x += 1.0; + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// let mut x = 0; + /// while x < 42 { + /// x += 1; + /// } + /// ``` + #[clippy::version = "1.80.0"] + pub WHILE_FLOAT, + nursery, + "while loops comaparing floating point values" +} + declare_clippy_lint! { /// ### What it does /// Checks whether a for loop is being used to push a constant @@ -641,9 +675,9 @@ declare_clippy_lint! { /// Checks for infinite loops in a function where the return type is not `!` /// and lint accordingly. /// - /// ### Why is this bad? - /// A loop should be gently exited somewhere, or at least mark its parent function as - /// never return (`!`). + /// ### Why restrict this? + /// Making the return type `!` serves as documentation that the function does not return. + /// If the function is not intended to loop infinitely, then this lint may detect a bug. /// /// ### Example /// ```no_run,ignore @@ -706,6 +740,7 @@ impl_lint_pass!(Loops => [ NEVER_LOOP, MUT_RANGE_BOUND, WHILE_IMMUTABLE_CONDITION, + WHILE_FLOAT, SAME_ITEM_PUSH, SINGLE_ELEMENT_LOOP, MISSING_SPIN_LOOP, @@ -762,6 +797,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { if let Some(higher::While { condition, body, span }) = higher::While::hir(expr) { while_immutable_condition::check(cx, condition, body); + while_float::check(cx, condition); missing_spin_loop::check(cx, condition, body); manual_while_let_some::check(cx, condition, body, span); } diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs index 6b9ecf5f1413..6c6a9a1a2e00 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs @@ -60,12 +60,9 @@ fn check_for_mutation( span_low: None, span_high: None, }; - ExprUseVisitor::for_clippy( - cx, - body.hir_id.owner.def_id, - &mut delegate, - ) - .walk_expr(body).into_ok(); + ExprUseVisitor::for_clippy(cx, body.hir_id.owner.def_id, &mut delegate) + .walk_expr(body) + .into_ok(); delegate.mutation_span() } diff --git a/src/tools/clippy/clippy_lints/src/loops/while_float.rs b/src/tools/clippy/clippy_lints/src/loops/while_float.rs new file mode 100644 index 000000000000..cf62ce29f0c7 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/while_float.rs @@ -0,0 +1,20 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_hir::ExprKind; + +pub(super) fn check(cx: &rustc_lint::LateContext<'_>, condition: &rustc_hir::Expr<'_>) { + if let ExprKind::Binary(_op, left, right) = condition.kind + && is_float_type(cx, left) + && is_float_type(cx, right) + { + span_lint( + cx, + super::WHILE_FLOAT, + condition.span, + "while condition comparing floats", + ); + } +} + +fn is_float_type(cx: &rustc_lint::LateContext<'_>, expr: &rustc_hir::Expr<'_>) -> bool { + cx.typeck_results().expr_ty(expr).is_floating_point() +} diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs new file mode 100644 index 000000000000..d1ae243877d7 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -0,0 +1,256 @@ +use std::collections::btree_map::Entry; +use std::collections::BTreeMap; + +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::is_lint_allowed; +use itertools::Itertools; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor}; +use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; +use rustc_span::{sym, Span, SyntaxContext}; + +declare_clippy_lint! { + /// ### What it does + /// Looks for macros that expand metavariables in an unsafe block. + /// + /// ### Why is this bad? + /// This hides an unsafe block and allows the user of the macro to write unsafe code without an explicit + /// unsafe block at callsite, making it possible to perform unsafe operations in seemingly safe code. + /// + /// The macro should be restructured so that these metavariables are referenced outside of unsafe blocks + /// and that the usual unsafety checks apply to the macro argument. + /// + /// This is usually done by binding it to a variable outside of the unsafe block + /// and then using that variable inside of the block as shown in the example, or by referencing it a second time + /// in a safe context, e.g. `if false { $expr }`. + /// + /// ### Known limitations + /// Due to how macros are represented in the compiler at the time Clippy runs its lints, + /// it's not possible to look for metavariables in macro definitions directly. + /// + /// Instead, this lint looks at expansions of macros. + /// This leads to false negatives for macros that are never actually invoked. + /// + /// By default, this lint is rather conservative and will only emit warnings on publicly-exported + /// macros from the same crate, because oftentimes private internal macros are one-off macros where + /// this lint would just be noise (e.g. macros that generate `impl` blocks). + /// The default behavior should help with preventing a high number of such false positives, + /// however it can be configured to also emit warnings in private macros if desired. + /// + /// ### Example + /// ```no_run + /// /// Gets the first element of a slice + /// macro_rules! first { + /// ($slice:expr) => { + /// unsafe { + /// let slice = $slice; // ⚠️ expansion inside of `unsafe {}` + /// + /// assert!(!slice.is_empty()); + /// // SAFETY: slice is checked to have at least one element + /// slice.first().unwrap_unchecked() + /// } + /// } + /// } + /// + /// assert_eq!(*first!(&[1i32]), 1); + /// + /// // This will compile as a consequence (note the lack of `unsafe {}`) + /// assert_eq!(*first!(std::hint::unreachable_unchecked() as &[i32]), 1); + /// ``` + /// Use instead: + /// ```compile_fail + /// macro_rules! first { + /// ($slice:expr) => {{ + /// let slice = $slice; // ✅ outside of `unsafe {}` + /// unsafe { + /// assert!(!slice.is_empty()); + /// // SAFETY: slice is checked to have at least one element + /// slice.first().unwrap_unchecked() + /// } + /// }} + /// } + /// + /// assert_eq!(*first!(&[1]), 1); + /// + /// // This won't compile: + /// assert_eq!(*first!(std::hint::unreachable_unchecked() as &[i32]), 1); + /// ``` + #[clippy::version = "1.80.0"] + pub MACRO_METAVARS_IN_UNSAFE, + suspicious, + "expanding macro metavariables in an unsafe block" +} +impl_lint_pass!(ExprMetavarsInUnsafe => [MACRO_METAVARS_IN_UNSAFE]); + +#[derive(Clone, Debug)] +pub enum MetavarState { + ReferencedInUnsafe { unsafe_blocks: Vec }, + ReferencedInSafe, +} + +#[derive(Default)] +pub struct ExprMetavarsInUnsafe { + pub warn_unsafe_macro_metavars_in_private_macros: bool, + /// A metavariable can be expanded more than once, potentially across multiple bodies, so it + /// requires some state kept across HIR nodes to make it possible to delay a warning + /// and later undo: + /// + /// ```ignore + /// macro_rules! x { + /// ($v:expr) => { + /// unsafe { $v; } // unsafe context, it might be possible to emit a warning here, so add it to the map + /// + /// $v; // `$v` expanded another time but in a safe context, set to ReferencedInSafe to suppress + /// } + /// } + /// ``` + pub metavar_expns: BTreeMap, +} + +struct BodyVisitor<'a, 'tcx> { + /// Stack of unsafe blocks -- the top item always represents the last seen unsafe block from + /// within a relevant macro. + macro_unsafe_blocks: Vec, + /// When this is >0, it means that the node currently being visited is "within" a + /// macro definition. This is not necessary for correctness, it merely helps reduce the number + /// of spans we need to insert into the map, since only spans from macros are relevant. + expn_depth: u32, + cx: &'a LateContext<'tcx>, + lint: &'a mut ExprMetavarsInUnsafe, +} + +fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { + (cx.effective_visibilities.is_exported(def_id) || cx.tcx.has_attr(def_id, sym::macro_export)) + && !cx.tcx.is_doc_hidden(def_id) +} + +impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> { + fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) { + let from_expn = s.span.from_expansion(); + if from_expn { + self.expn_depth += 1; + } + walk_stmt(self, s); + if from_expn { + self.expn_depth -= 1; + } + } + + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { + let ctxt = e.span.ctxt(); + + if let ExprKind::Block(block, _) = e.kind + && let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules + && !ctxt.is_root() + && let Some(macro_def_id) = ctxt.outer_expn_data().macro_def_id + && let Some(macro_def_id) = macro_def_id.as_local() + && (self.lint.warn_unsafe_macro_metavars_in_private_macros || is_public_macro(self.cx, macro_def_id)) + { + self.macro_unsafe_blocks.push(block.hir_id); + walk_block(self, block); + self.macro_unsafe_blocks.pop(); + } else if ctxt.is_root() && self.expn_depth > 0 { + let unsafe_block = self.macro_unsafe_blocks.last().copied(); + + match (self.lint.metavar_expns.entry(e.span), unsafe_block) { + (Entry::Vacant(e), None) => { + e.insert(MetavarState::ReferencedInSafe); + }, + (Entry::Vacant(e), Some(unsafe_block)) => { + e.insert(MetavarState::ReferencedInUnsafe { + unsafe_blocks: vec![unsafe_block], + }); + }, + (Entry::Occupied(mut e), None) => { + if let MetavarState::ReferencedInUnsafe { .. } = *e.get() { + e.insert(MetavarState::ReferencedInSafe); + } + }, + (Entry::Occupied(mut e), Some(unsafe_block)) => { + if let MetavarState::ReferencedInUnsafe { unsafe_blocks } = e.get_mut() + && !unsafe_blocks.contains(&unsafe_block) + { + unsafe_blocks.push(unsafe_block); + } + }, + } + + // NB: No need to visit descendant nodes. They're guaranteed to represent the same + // metavariable + } else { + walk_expr(self, e); + } + } +} + +impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &rustc_hir::Body<'tcx>) { + if is_lint_allowed(cx, MACRO_METAVARS_IN_UNSAFE, body.value.hir_id) { + return; + } + + // This BodyVisitor is separate and not part of the lint pass because there is no + // `check_stmt_post` on `(Late)LintPass`, which we'd need to detect when we're leaving a macro span + + let mut vis = BodyVisitor { + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning + expn_depth: if body.value.span.from_expansion() { 1 } else { 0 }, + macro_unsafe_blocks: Vec::new(), + lint: self, + cx + }; + vis.visit_body(body); + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + // Aggregate all unsafe blocks from all spans: + // ``` + // macro_rules! x { + // ($w:expr, $x:expr, $y:expr) => { $w; unsafe { $w; $x; }; unsafe { $x; $y; }; } + // } + // $w: [] (unsafe#0 is never added because it was referenced in a safe context) + // $x: [unsafe#0, unsafe#1] + // $y: [unsafe#1] + // ``` + // We want to lint unsafe blocks #0 and #1 + let bad_unsafe_blocks = self + .metavar_expns + .iter() + .filter_map(|(_, state)| match state { + MetavarState::ReferencedInUnsafe { unsafe_blocks } => Some(unsafe_blocks.as_slice()), + MetavarState::ReferencedInSafe => None, + }) + .flatten() + .copied() + .map(|id| { + // Remove the syntax context to hide "in this macro invocation" in the diagnostic. + // The invocation doesn't matter. Also we want to dedupe by the unsafe block and not by anything + // related to the callsite. + let span = cx.tcx.hir().span(id); + + (id, Span::new(span.lo(), span.hi(), SyntaxContext::root(), None)) + }) + .dedup_by(|&(_, a), &(_, b)| a == b); + + for (id, span) in bad_unsafe_blocks { + span_lint_hir_and_then( + cx, + MACRO_METAVARS_IN_UNSAFE, + id, + span, + "this macro expands metavariables in an unsafe block", + |diag| { + diag.note("this allows the user of the macro to write unsafe code outside of an unsafe block"); + diag.help( + "consider expanding any metavariables outside of this block, e.g. by storing them in a variable", + ); + diag.help( + "... or also expand referenced metavariables in a safe context to require an unsafe block at callsite", + ); + }, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index 4cd5f3b81e52..25c7e5d38b31 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -4,8 +4,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, Body, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, FnDecl, - FnRetTy, GenericArg, GenericBound, ImplItem, Item, ItemKind, LifetimeName, Node, Term, TraitRef, Ty, TyKind, - TypeBindingKind, + FnRetTy, GenericArg, GenericBound, ImplItem, Item, ItemKind, LifetimeName, Node, TraitRef, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -138,10 +137,9 @@ fn future_trait_ref<'tcx>( fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'tcx>> { if let Some(segment) = trait_ref.path.segments.last() && let Some(args) = segment.args - && args.bindings.len() == 1 - && let binding = &args.bindings[0] - && binding.ident.name == sym::Output - && let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind + && let [constraint] = args.constraints + && constraint.ident.name == sym::Output + && let Some(output) = constraint.ty() { return Some(output); } diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs index 1eadc200bedc..e2ab44155182 100644 --- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs @@ -611,15 +611,22 @@ impl<'tcx> BinaryOp<'tcx> { /// The clamp meta pattern is a pattern shared between many (but not all) patterns. /// In summary, this pattern consists of two if statements that meet many criteria, +/// /// - binary operators that are one of [`>`, `<`, `>=`, `<=`]. +/// /// - Both binary statements must have a shared argument +/// /// - Which can appear on the left or right side of either statement +/// /// - The binary operators must define a finite range for the shared argument. To put this in /// the terms of Rust `std` library, the following ranges are acceptable +/// /// - `Range` /// - `RangeInclusive` +/// /// And all other range types are not accepted. For the purposes of `clamp` it's irrelevant /// whether the range is inclusive or not, the output is the same. +/// /// - The result of each if statement must be equal to the argument unique to that if statement. The /// result can not be the shared argument in either case. fn is_clamp_meta_pattern<'tcx>( diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs index 84fb183e3f79..17399fb2cc21 100644 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs +++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { /// let x: Option> = Some(Vec::new()); /// let y: Vec = x.unwrap_or_default(); /// ``` - #[clippy::version = "1.78.0"] + #[clippy::version = "1.79.0"] pub MANUAL_UNWRAP_OR_DEFAULT, suspicious, "check if a `match` or `if let` can be simplified with `unwrap_or_default`" @@ -57,7 +57,8 @@ fn get_some<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option { // Since it comes from a pattern binding, we need to get the parent to actually match // against it. && let Some(def_id) = cx.tcx.opt_parent(def_id) - && cx.tcx.lang_items().get(LangItem::OptionSome) == Some(def_id) + && (cx.tcx.lang_items().get(LangItem::OptionSome) == Some(def_id) + || cx.tcx.lang_items().get(LangItem::ResultOk) == Some(def_id)) { let mut bindings = Vec::new(); pat.each_binding(|_, id, _, _| bindings.push(id)); @@ -80,6 +81,14 @@ fn get_none<'tcx>(cx: &LateContext<'tcx>, arm: &Arm<'tcx>) -> Option<&'tcx Expr< && cx.tcx.lang_items().get(LangItem::OptionNone) == Some(def_id) { Some(arm.body) + } else if let PatKind::TupleStruct(QPath::Resolved(_, path), _, _)= arm.pat.kind + && let Some(def_id) = path.res.opt_def_id() + // Since it comes from a pattern binding, we need to get the parent to actually match + // against it. + && let Some(def_id) = cx.tcx.opt_parent(def_id) + && cx.tcx.lang_items().get(LangItem::ResultErr) == Some(def_id) + { + Some(arm.body) } else if let PatKind::Wild = arm.pat.kind { // We consider that the `Some` check will filter it out if it's not right. Some(arm.body) diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index cd61e733694b..da8c918a62bb 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_lint_allowed, path_to_local, search_same, SpanlessEq, SpanlessHash}; use core::cmp::Ordering; use core::{iter, slice}; @@ -9,9 +9,9 @@ use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd}; use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; -use rustc_lint::LateContext; +use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; -use rustc_span::{ErrorGuaranteed, Symbol}; +use rustc_span::{ErrorGuaranteed, Span, Symbol}; use super::MATCH_SAME_ARMS; @@ -110,20 +110,22 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { && check_same_body() }; + let mut appl = Applicability::MaybeIncorrect; let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) { if matches!(arm2.pat.kind, PatKind::Wild) { if !cx.tcx.features().non_exhaustive_omitted_patterns_lint || is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, arm2.hir_id) { + let arm_span = adjusted_arm_span(cx, arm1.span); span_lint_hir_and_then( cx, MATCH_SAME_ARMS, arm1.hir_id, - arm1.span, + arm_span, "this match arm has an identical body to the `_` wildcard arm", |diag| { - diag.span_suggestion(arm1.span, "try removing the arm", "", Applicability::MaybeIncorrect) + diag.span_suggestion(arm_span, "try removing the arm", "", appl) .help("or try changing either arm body") .span_note(arm2.span, "`_` wildcard arm here"); }, @@ -144,23 +146,36 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { keep_arm.span, "this match arm has an identical body to another arm", |diag| { - let move_pat_snip = snippet(cx, move_arm.pat.span, ""); - let keep_pat_snip = snippet(cx, keep_arm.pat.span, ""); + let move_pat_snip = snippet_with_applicability(cx, move_arm.pat.span, "", &mut appl); + let keep_pat_snip = snippet_with_applicability(cx, keep_arm.pat.span, "", &mut appl); diag.span_suggestion( keep_arm.pat.span, - "try merging the arm patterns", + "or try merging the arm patterns", format!("{keep_pat_snip} | {move_pat_snip}"), - Applicability::MaybeIncorrect, + appl, ) - .help("or try changing either arm body") - .span_note(move_arm.span, "other arm here"); + .span_suggestion( + adjusted_arm_span(cx, move_arm.span), + "and remove this obsolete arm", + "", + appl, + ) + .help("try changing either arm body"); }, ); } } } +/// Extend arm's span to include the comma and whitespaces after it. +fn adjusted_arm_span(cx: &LateContext<'_>, span: Span) -> Span { + let source_map = cx.sess().source_map(); + source_map + .span_extend_while(span, |c| c == ',' || c.is_ascii_whitespace()) + .unwrap_or(span) +} + #[derive(Clone, Copy)] enum NormalizedPat<'a> { Wild, diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index ee9f48d71ad8..691ecd57535a 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -260,7 +260,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for wildcard enum matches using `_`. /// - /// ### Why is this bad? + /// ### Why restrict this? /// New enum variants added by library updates can be missed. /// /// ### Known problems @@ -435,7 +435,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Correctness and readability. It's like having a wildcard pattern after /// matching all enum variants explicitly. /// @@ -861,7 +861,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for usage of `Err(x)?`. /// - /// ### Why is this bad? + /// ### Why restrict this? /// The `?` operator is designed to allow calls that /// can fail to be easily chained. For example, `foo()?.bar()` or /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will diff --git a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs index 8199366d175f..45b375dbe3d7 100644 --- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs @@ -37,14 +37,14 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) Some(lhs) => constant(cx, cx.typeck_results(), lhs)?, None => { let min_val_const = ty.numeric_min_val(cx.tcx)?; - mir_to_const(cx, mir::Const::from_ty_const(min_val_const, cx.tcx))? + mir_to_const(cx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? }, }; let rhs_const = match rhs { Some(rhs) => constant(cx, cx.typeck_results(), rhs)?, None => { let max_val_const = ty.numeric_max_val(cx.tcx)?; - mir_to_const(cx, mir::Const::from_ty_const(max_val_const, cx.tcx))? + mir_to_const(cx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))? }, }; let lhs_val = lhs_const.int_value(cx, ty)?; diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs index a75cf37945f7..c2c0fbf439d5 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -2,7 +2,7 @@ use clippy_config::msrvs::Msrv; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::source::snippet; -use clippy_utils::visitors::{for_each_expr, is_local_used}; +use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used}; use clippy_utils::{in_constant, path_to_local}; use rustc_ast::{BorrowKind, LitKind}; use rustc_errors::Applicability; @@ -249,7 +249,7 @@ fn emit_redundant_guards<'tcx>( /// an error in the future, and rustc already actively warns against this (see rust#41620), /// so we don't consider those as usable within patterns for linting purposes. fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - for_each_expr(expr, |expr| { + for_each_expr_without_closures(expr, |expr| { if match expr.kind { ExprKind::ConstBlock(..) => cx.tcx.features().inline_const_pat, ExprKind::Call(c, ..) if let ExprKind::Path(qpath) = c.kind => { diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index 78973984fb0b..0e4b2d9d34a0 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, walk_span_to_context}; use clippy_utils::sugg::{make_unop, Sugg}; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; -use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr}; +use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; use clippy_utils::{higher, is_expn_of, is_trait_method}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -283,7 +283,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op // to see that there aren't any let chains anywhere in the guard, as that would break // if we suggest `t.is_none() && (let X = y && z)` for: // `match t { None if let X = y && z => true, _ => false }` - let has_nested_let_chain = for_each_expr(guard, |expr| { + let has_nested_let_chain = for_each_expr_without_closures(guard, |expr| { if matches!(expr.kind, ExprKind::Let(..)) { ControlFlow::Break(()) } else { diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index f775ea072e1f..2f72e59834fa 100644 --- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -1,12 +1,17 @@ +use std::ops::ControlFlow; + use crate::FxHashSet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{indent_of, snippet}; +use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; use clippy_utils::{get_attr, is_lint_allowed}; +use itertools::Itertools; +use rustc_ast::Mutability; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{Arm, Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LintContext}; -use rustc_middle::ty::{GenericArgKind, Ty}; +use rustc_middle::ty::{GenericArgKind, Region, RegionKind, Ty, TyCtxt, TypeVisitable, TypeVisitor}; use rustc_span::Span; use super::SIGNIFICANT_DROP_IN_SCRUTINEE; @@ -22,43 +27,34 @@ pub(super) fn check<'tcx>( return; } - if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, scrutinee, source) { - for found in suggestions { - span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| { - set_diagnostic(diag, cx, expr, found); - let s = Span::new(expr.span.hi(), expr.span.hi(), expr.span.ctxt(), None); - diag.span_label(s, "temporary lives until here"); - for span in has_significant_drop_in_arms(cx, arms) { - diag.span_label(span, "another value with significant `Drop` created here"); - } - diag.note("this might lead to deadlocks or other unexpected behavior"); - }); - } + let (suggestions, message) = has_significant_drop_in_scrutinee(cx, scrutinee, source); + for found in suggestions { + span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| { + set_diagnostic(diag, cx, expr, found); + let s = Span::new(expr.span.hi(), expr.span.hi(), expr.span.ctxt(), None); + diag.span_label(s, "temporary lives until here"); + for span in has_significant_drop_in_arms(cx, arms) { + diag.span_label(span, "another value with significant `Drop` created here"); + } + diag.note("this might lead to deadlocks or other unexpected behavior"); + }); } } fn set_diagnostic<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) { - if found.lint_suggestion == LintSuggestion::MoveAndClone { - // If our suggestion is to move and clone, then we want to leave it to the user to - // decide how to address this lint, since it may be that cloning is inappropriate. - // Therefore, we won't to emit a suggestion. - return; - } - let original = snippet(cx, found.found_span, ".."); let trailing_indent = " ".repeat(indent_of(cx, found.found_span).unwrap_or(0)); - let replacement = if found.lint_suggestion == LintSuggestion::MoveAndDerefToCopy { - format!("let value = *{original};\n{trailing_indent}") - } else if found.is_unit_return_val { - // If the return value of the expression to be moved is unit, then we don't need to - // capture the result in a temporary -- we can just replace it completely with `()`. - format!("{original};\n{trailing_indent}") - } else { - format!("let value = {original};\n{trailing_indent}") + let replacement = { + let (def_part, deref_part) = if found.is_unit_return_val { + ("", String::new()) + } else { + ("let value = ", "*".repeat(found.peel_ref_times)) + }; + format!("{def_part}{deref_part}{original};\n{trailing_indent}") }; - let suggestion_message = if found.lint_suggestion == LintSuggestion::MoveOnly { + let suggestion_message = if found.peel_ref_times == 0 { "try moving the temporary above the match" } else { "try moving the temporary above the match and create a copy" @@ -66,8 +62,11 @@ fn set_diagnostic<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: & let scrutinee_replacement = if found.is_unit_return_val { "()".to_owned() - } else { + } else if found.peel_ref_times == 0 { "value".to_owned() + } else { + let ref_part = "&".repeat(found.peel_ref_times); + format!("({ref_part}value)") }; diag.multipart_suggestion( @@ -86,20 +85,18 @@ fn has_significant_drop_in_scrutinee<'tcx>( cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>, source: MatchSource, -) -> Option<(Vec, &'static str)> { +) -> (Vec, &'static str) { let mut helper = SigDropHelper::new(cx); let scrutinee = match (source, &scrutinee.kind) { (MatchSource::ForLoopDesugar, ExprKind::Call(_, [e])) => e, _ => scrutinee, }; - helper.find_sig_drop(scrutinee).map(|drops| { - let message = if source == MatchSource::Normal { - "temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression" - } else { - "temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression" - }; - (drops, message) - }) + let message = if source == MatchSource::Normal { + "temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression" + } else { + "temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression" + }; + (helper.find_sig_drop(scrutinee), message) } struct SigDropChecker<'a, 'tcx> { @@ -115,251 +112,305 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { } } - fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> { - self.cx.typeck_results().expr_ty(ex) + fn is_sig_drop_expr(&mut self, ex: &'tcx Expr<'_>) -> bool { + !ex.is_syntactic_place_expr() && self.has_sig_drop_attr(self.cx.typeck_results().expr_ty(ex)) } - fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool { - !self.seen_types.insert(ty) + fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool { + self.seen_types.clear(); + self.has_sig_drop_attr_impl(ty) } - fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + fn has_sig_drop_attr_impl(&mut self, ty: Ty<'tcx>) -> bool { if let Some(adt) = ty.ty_adt_def() { - if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 { + if get_attr( + self.cx.sess(), + self.cx.tcx.get_attrs_unchecked(adt.did()), + "has_significant_drop", + ) + .count() + > 0 + { return true; } } - match ty.kind() { - rustc_middle::ty::Adt(a, b) => { - for f in a.all_fields() { - let ty = f.ty(cx.tcx, b); - if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) { - return true; - } - } - - for generic_arg in *b { - if let GenericArgKind::Type(ty) = generic_arg.unpack() { - if self.has_sig_drop_attr(cx, ty) { - return true; - } - } - } - false - }, - rustc_middle::ty::Array(ty, _) - | rustc_middle::ty::RawPtr(ty, _) - | rustc_middle::ty::Ref(_, ty, _) - | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty), - _ => false, + if !self.seen_types.insert(ty) { + return false; } + + let result = match ty.kind() { + rustc_middle::ty::Adt(adt, args) => { + // if some field has significant drop, + adt.all_fields() + .map(|field| field.ty(self.cx.tcx, args)) + .any(|ty| self.has_sig_drop_attr_impl(ty)) + // or if there is no generic lifetime and.. + // (to avoid false positive on `Ref<'a, MutexGuard>`) + || (args + .iter() + .all(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_))) + // some generic parameter has significant drop + // (to avoid false negative on `Box>`) + && args + .iter() + .filter_map(|arg| match arg.unpack() { + GenericArgKind::Type(ty) => Some(ty), + _ => None, + }) + .any(|ty| self.has_sig_drop_attr_impl(ty))) + }, + rustc_middle::ty::Tuple(tys) => tys.iter().any(|ty| self.has_sig_drop_attr_impl(ty)), + rustc_middle::ty::Array(ty, _) | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr_impl(*ty), + _ => false, + }; + + result + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +enum SigDropHolder { + /// No values with significant drop present in this expression. + /// + /// Expressions that we've emited lints do not count. + None, + /// Some field in this expression references to values with significant drop. + /// + /// Example: `(1, &data.lock().field)`. + PackedRef, + /// The value of this expression references to values with significant drop. + /// + /// Example: `data.lock().field`. + DirectRef, + /// This expression should be moved out to avoid significant drop in scrutinee. + Moved, +} + +impl Default for SigDropHolder { + fn default() -> Self { + Self::None } } struct SigDropHelper<'a, 'tcx> { cx: &'a LateContext<'tcx>, - is_chain_end: bool, - has_significant_drop: bool, - current_sig_drop: Option, - sig_drop_spans: Option>, - special_handling_for_binary_op: bool, + parent_expr: Option<&'tcx Expr<'tcx>>, + sig_drop_holder: SigDropHolder, + sig_drop_spans: Vec, sig_drop_checker: SigDropChecker<'a, 'tcx>, } -#[expect(clippy::enum_variant_names)] -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -enum LintSuggestion { - MoveOnly, - MoveAndDerefToCopy, - MoveAndClone, -} - -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] struct FoundSigDrop { found_span: Span, is_unit_return_val: bool, - lint_suggestion: LintSuggestion, + peel_ref_times: usize, } impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> SigDropHelper<'a, 'tcx> { SigDropHelper { cx, - is_chain_end: true, - has_significant_drop: false, - current_sig_drop: None, - sig_drop_spans: None, - special_handling_for_binary_op: false, + parent_expr: None, + sig_drop_holder: SigDropHolder::None, + sig_drop_spans: Vec::new(), sig_drop_checker: SigDropChecker::new(cx), } } - fn find_sig_drop(&mut self, match_expr: &'tcx Expr<'_>) -> Option> { + fn find_sig_drop(&mut self, match_expr: &'tcx Expr<'_>) -> Vec { self.visit_expr(match_expr); - // If sig drop spans is empty but we found a significant drop, it means that we didn't find - // a type that was trivially copyable as we moved up the chain after finding a significant - // drop, so move the entire scrutinee. - if self.has_significant_drop && self.sig_drop_spans.is_none() { - self.try_setting_current_suggestion(match_expr, true); - self.move_current_suggestion(); - } - - self.sig_drop_spans.take() + core::mem::take(&mut self.sig_drop_spans) } - fn replace_current_sig_drop( - &mut self, - found_span: Span, - is_unit_return_val: bool, - lint_suggestion: LintSuggestion, - ) { - self.current_sig_drop.replace(FoundSigDrop { + fn replace_current_sig_drop(&mut self, found_span: Span, is_unit_return_val: bool, peel_ref_times: usize) { + self.sig_drop_spans.clear(); + self.sig_drop_spans.push(FoundSigDrop { found_span, is_unit_return_val, - lint_suggestion, + peel_ref_times, }); } - /// This will try to set the current suggestion (so it can be moved into the suggestions vec - /// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us - /// an opportunity to look for another type in the chain that will be trivially copyable. - /// However, if we are at the end of the chain, we want to accept whatever is there. (The - /// suggestion won't actually be output, but the diagnostic message will be output, so the user - /// can determine the best way to handle the lint.) - fn try_setting_current_suggestion(&mut self, expr: &'tcx Expr<'_>, allow_move_and_clone: bool) { - if self.current_sig_drop.is_some() { - return; + fn try_move_sig_drop(&mut self, expr: &'tcx Expr<'_>, parent_expr: &'tcx Expr<'_>) { + if self.sig_drop_holder == SigDropHolder::Moved { + self.sig_drop_holder = SigDropHolder::None; } - let ty = self.sig_drop_checker.get_type(expr); - if ty.is_ref() { - // We checked that the type was ref, so builtin_deref will return Some, - // but let's avoid any chance of an ICE. - if let Some(ty) = ty.builtin_deref(true) { - if ty.is_trivially_pure_clone_copy() { - self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndDerefToCopy); - } else if allow_move_and_clone { - self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone); - } + + if self.sig_drop_holder == SigDropHolder::DirectRef { + self.sig_drop_holder = SigDropHolder::PackedRef; + self.try_move_sig_drop_direct_ref(expr, parent_expr); + } else if self.sig_drop_checker.is_sig_drop_expr(expr) { + // The values with significant drop can be moved to some other functions. For example, consider + // `drop(data.lock())`. We use `SigDropHolder::None` here to avoid emitting lints in such scenarios. + self.sig_drop_holder = SigDropHolder::None; + self.try_move_sig_drop_direct_ref(expr, parent_expr); + } + + if self.sig_drop_holder != SigDropHolder::None { + let parent_ty = self.cx.typeck_results().expr_ty(parent_expr); + if !ty_has_erased_regions(parent_ty) && !parent_expr.is_syntactic_place_expr() { + self.replace_current_sig_drop(parent_expr.span, parent_ty.is_unit(), 0); + self.sig_drop_holder = SigDropHolder::Moved; + } + + let (peel_ref_ty, peel_ref_times) = ty_peel_refs(parent_ty); + if !ty_has_erased_regions(peel_ref_ty) && is_copy(self.cx, peel_ref_ty) { + self.replace_current_sig_drop(parent_expr.span, peel_ref_ty.is_unit(), peel_ref_times); + self.sig_drop_holder = SigDropHolder::Moved; } - } else if ty.is_trivially_pure_clone_copy() { - self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveOnly); - } else if allow_move_and_clone { - self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone); } } - fn move_current_suggestion(&mut self) { - if let Some(current) = self.current_sig_drop.take() { - self.sig_drop_spans.get_or_insert_with(Vec::new).push(current); + fn try_move_sig_drop_direct_ref(&mut self, expr: &'tcx Expr<'_>, parent_expr: &'tcx Expr<'_>) { + let arg_idx = match parent_expr.kind { + ExprKind::MethodCall(_, receiver, exprs, _) => std::iter::once(receiver) + .chain(exprs.iter()) + .find_position(|ex| ex.hir_id == expr.hir_id) + .map(|(idx, _)| idx), + ExprKind::Call(_, exprs) => exprs + .iter() + .find_position(|ex| ex.hir_id == expr.hir_id) + .map(|(idx, _)| idx), + ExprKind::Binary(_, lhs, rhs) | ExprKind::AssignOp(_, lhs, rhs) => [lhs, rhs] + .iter() + .find_position(|ex| ex.hir_id == expr.hir_id) + .map(|(idx, _)| idx), + ExprKind::Unary(_, ex) => (ex.hir_id == expr.hir_id).then_some(0), + _ => { + // Here we assume that all other expressions create or propagate the reference to the value with + // significant drop. + self.sig_drop_holder = SigDropHolder::DirectRef; + return; + }, + }; + let Some(arg_idx) = arg_idx else { + return; + }; + + let fn_sig = if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) { + self.cx.tcx.fn_sig(def_id).instantiate_identity() + } else { + return; + }; + + let input_re = if let Some(input_ty) = fn_sig.skip_binder().inputs().get(arg_idx) + && let rustc_middle::ty::Ref(input_re, _, _) = input_ty.kind() + { + input_re + } else { + return; + }; + + // Late bound lifetime parameters are not related to any constraints, so we can track them in a very + // simple manner. For other lifetime parameters, we give up and update the state to `PackedRef`. + let RegionKind::ReBound(_, input_re_bound) = input_re.kind() else { + self.sig_drop_holder = SigDropHolder::PackedRef; + return; + }; + let contains_input_re = |re_bound| { + if re_bound == input_re_bound { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }; + + let output_ty = fn_sig.skip_binder().output(); + if let rustc_middle::ty::Ref(output_re, peel_ref_ty, _) = output_ty.kind() + && input_re == output_re + && for_each_top_level_late_bound_region(*peel_ref_ty, contains_input_re).is_continue() + { + // We're lucky! The output type is still a direct reference to the value with significant drop. + self.sig_drop_holder = SigDropHolder::DirectRef; + } else if for_each_top_level_late_bound_region(output_ty, contains_input_re).is_continue() { + // The lifetime to the value with significant drop goes away. So we can emit a lint that suggests to + // move the expression out. + self.replace_current_sig_drop(parent_expr.span, output_ty.is_unit(), 0); + self.sig_drop_holder = SigDropHolder::Moved; + } else { + // TODO: The lifetime is still there but it's for a inner type. For instance, consider + // `Some(&mutex.lock().field)`, which has a type of `Option<&u32>`. How to address this scenario? + self.sig_drop_holder = SigDropHolder::PackedRef; + } + } +} + +fn ty_peel_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) { + let mut n = 0; + while let rustc_middle::ty::Ref(_, new_ty, Mutability::Not) = ty.kind() { + ty = *new_ty; + n += 1; + } + (ty, n) +} + +fn ty_has_erased_regions(ty: Ty<'_>) -> bool { + struct V; + + impl<'tcx> TypeVisitor> for V { + type Result = ControlFlow<()>; + + fn visit_region(&mut self, region: Region<'tcx>) -> Self::Result { + if region.is_erased() { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } } } - fn visit_exprs_for_binary_ops( - &mut self, - left: &'tcx Expr<'_>, - right: &'tcx Expr<'_>, - is_unit_return_val: bool, - span: Span, - ) { - self.special_handling_for_binary_op = true; - self.visit_expr(left); - self.visit_expr(right); - - // If either side had a significant drop, suggest moving the entire scrutinee to avoid - // unnecessary copies and to simplify cases where both sides have significant drops. - if self.has_significant_drop { - self.replace_current_sig_drop(span, is_unit_return_val, LintSuggestion::MoveOnly); - } - - self.special_handling_for_binary_op = false; - } + ty.visit_with(&mut V).is_break() } impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { - if !self.is_chain_end - && self - .sig_drop_checker - .has_sig_drop_attr(self.cx, self.sig_drop_checker.get_type(ex)) - { - self.has_significant_drop = true; + // We've emited a lint on some neighborhood expression. That lint will suggest to move out the + // _parent_ expression (not the expression itself). Since we decide to move out the parent + // expression, it is pointless to continue to process the current expression. + if self.sig_drop_holder == SigDropHolder::Moved { return; } - self.is_chain_end = false; + + // These states are of neighborhood expressions. We save and clear them here, and we'll later merge + // the states of the current expression with them at the end of the method. + let sig_drop_holder_before = core::mem::take(&mut self.sig_drop_holder); + let sig_drop_spans_before = core::mem::take(&mut self.sig_drop_spans); + let parent_expr_before = self.parent_expr.replace(ex); match ex.kind { - ExprKind::MethodCall(_, expr, ..) => { - self.visit_expr(expr); - } - ExprKind::Binary(_, left, right) => { - self.visit_exprs_for_binary_ops(left, right, false, ex.span); - } - ExprKind::Assign(left, right, _) | ExprKind::AssignOp(_, left, right) => { - self.visit_exprs_for_binary_ops(left, right, true, ex.span); - } - ExprKind::Tup(exprs) => { - for expr in exprs { - self.visit_expr(expr); - if self.has_significant_drop { - // We may have not have set current_sig_drop if all the suggestions were - // MoveAndClone, so add this tuple item's full expression in that case. - if self.current_sig_drop.is_none() { - self.try_setting_current_suggestion(expr, true); - } + // Skip blocks because values in blocks will be dropped as usual. + ExprKind::Block(..) => (), + _ => walk_expr(self, ex), + } - // Now we are guaranteed to have something, so add it to the final vec. - self.move_current_suggestion(); - } - // Reset `has_significant_drop` after each tuple expression so we can look for - // additional cases. - self.has_significant_drop = false; - } - if self.sig_drop_spans.is_some() { - self.has_significant_drop = true; - } - } - ExprKind::Array(..) | - ExprKind::Call(..) | - ExprKind::Unary(..) | - ExprKind::If(..) | - ExprKind::Match(..) | - ExprKind::Field(..) | - ExprKind::Index(..) | - ExprKind::Ret(..) | - ExprKind::Become(..) | - ExprKind::Repeat(..) | - ExprKind::Yield(..) => walk_expr(self, ex), - ExprKind::AddrOf(_, _, _) | - ExprKind::Block(_, _) | - ExprKind::Break(_, _) | - ExprKind::Cast(_, _) | - // Don't want to check the closure itself, only invocation, which is covered by MethodCall - ExprKind::Closure { .. } | - ExprKind::ConstBlock(_) | - ExprKind::Continue(_) | - ExprKind::DropTemps(_) | - ExprKind::Err(_) | - ExprKind::InlineAsm(_) | - ExprKind::OffsetOf(_, _) | - ExprKind::Let(_) | - ExprKind::Lit(_) | - ExprKind::Loop(_, _, _, _) | - ExprKind::Path(_) | - ExprKind::Struct(_, _, _) | - ExprKind::Type(_, _) => { - return; + if let Some(parent_ex) = parent_expr_before { + match parent_ex.kind { + ExprKind::Assign(lhs, _, _) | ExprKind::AssignOp(_, lhs, _) + if lhs.hir_id == ex.hir_id && self.sig_drop_holder == SigDropHolder::Moved => + { + // Never move out only the assignee. Instead, we should always move out the whole assigment. + self.replace_current_sig_drop(parent_ex.span, true, 0); + }, + _ => { + self.try_move_sig_drop(ex, parent_ex); + }, } } - // Once a significant temporary has been found, we need to go back up at least 1 level to - // find the span to extract for replacement, so the temporary gets dropped. However, for - // binary ops, we want to move the whole scrutinee so we avoid unnecessary copies and to - // simplify cases where both sides have significant drops. - if self.has_significant_drop && !self.special_handling_for_binary_op { - self.try_setting_current_suggestion(ex, false); + self.sig_drop_holder = std::cmp::max(self.sig_drop_holder, sig_drop_holder_before); + + // We do not need those old spans in neighborhood expressions if we emit a lint that suggests to + // move out the _parent_ expression (i.e., `self.sig_drop_holder == SigDropHolder::Moved`). + if self.sig_drop_holder != SigDropHolder::Moved { + let mut sig_drop_spans = sig_drop_spans_before; + sig_drop_spans.append(&mut self.sig_drop_spans); + self.sig_drop_spans = sig_drop_spans; } + + self.parent_expr = parent_expr_before; } } @@ -387,10 +438,7 @@ fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<' impl<'a, 'tcx> Visitor<'tcx> for ArmSigDropHelper<'a, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if self - .sig_drop_checker - .has_sig_drop_attr(self.sig_drop_checker.cx, self.sig_drop_checker.get_type(ex)) - { + if self.sig_drop_checker.is_sig_drop_expr(ex) { self.found_sig_drop_spans.insert(ex.span); return; } diff --git a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs index 5409ede6008b..1fab6c0e499d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs +++ b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{eq_expr_value, get_parent_expr}; use core::ops::ControlFlow; use rustc_errors::Applicability; @@ -46,7 +46,7 @@ fn collect_replace_calls<'tcx>( let mut methods = VecDeque::new(); let mut from_args = VecDeque::new(); - let _: Option<()> = for_each_expr(expr, |e| { + let _: Option<()> = for_each_expr_without_closures(expr, |e| { if let Some(("replace", _, [from, to], _, _)) = method_call(e) { if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() { methods.push_front(e); diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index fba76852344f..c9f56e1d9809 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::{find_format_args, format_args_inputs_span, root_macro_call_first_node}; +use clippy_utils::macros::{format_args_inputs_span, root_macro_call_first_node, FormatArgsStorage}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; @@ -16,6 +16,7 @@ use super::EXPECT_FUN_CALL; #[allow(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, + format_args_storage: &FormatArgsStorage, expr: &hir::Expr<'_>, method_span: Span, name: &str, @@ -134,9 +135,9 @@ pub(super) fn check<'tcx>( // Special handling for `format!` as arg_root if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) { if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) - && let Some(format_args) = find_format_args(cx, arg_root, macro_call.expn) + && let Some(format_args) = format_args_storage.get(cx, arg_root, macro_call.expn) { - let span = format_args_inputs_span(&format_args); + let span = format_args_inputs_span(format_args); let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs index 12647ea1ffcb..b93d51eac647 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs @@ -126,15 +126,15 @@ enum FilterType { /// /// How this is done: /// 1. we know that this is invoked in a method call with `filter` as the method name via `mod.rs` -/// 2. we check that we are in a trait method. Therefore we are in an -/// `(x as Iterator).filter({filter_arg})` method call. +/// 2. we check that we are in a trait method. Therefore we are in an `(x as +/// Iterator).filter({filter_arg})` method call. /// 3. we check that the parent expression is not a map. This is because we don't want to lint /// twice, and we already have a specialized lint for that. /// 4. we check that the span of the filter does not contain a comment. /// 5. we get the type of the `Item` in the `Iterator`, and compare against the type of Option and -/// Result. +/// Result. /// 6. we finally check the contents of the filter argument to see if it is a call to `is_some` or -/// `is_ok`. +/// `is_ok`. /// 7. if all of the above are true, then we return the `FilterType` fn expression_type( cx: &LateContext<'_>, diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs index 7c852a3768d1..05e77386128f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs @@ -12,8 +12,10 @@ use rustc_middle::ty; use rustc_span::{sym, Span}; /// lint use of: +/// /// - `hashmap.iter().map(|(_, v)| v)` /// - `hashmap.into_iter().map(|(_, v)| v)` +/// /// on `HashMaps` and `BTreeMaps` in std pub(super) fn check<'tcx>( diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 6c9bdcff8262..7f6b666e434e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -1,8 +1,12 @@ +use std::iter::once; + use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core}; use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; +use rustc_hir::hir_id::HirId; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; @@ -25,7 +29,29 @@ impl IterType { } } -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) { +fn is_arg_ty_unified_in_fn<'tcx>( + cx: &LateContext<'tcx>, + fn_id: DefId, + arg_id: HirId, + args: impl IntoIterator>, +) -> bool { + let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity(); + let arg_id_in_args = args.into_iter().position(|e| e.hir_id == arg_id).unwrap(); + let arg_ty_in_args = fn_sig.input(arg_id_in_args).skip_binder(); + + cx.tcx.predicates_of(fn_id).predicates.iter().any(|(clause, _)| { + clause + .as_projection_clause() + .and_then(|p| p.map_bound(|p| p.term.as_type()).transpose()) + .is_some_and(|ty| ty.skip_binder() == arg_ty_in_args) + }) || fn_sig + .inputs() + .iter() + .enumerate() + .any(|(i, ty)| i != arg_id_in_args && ty.skip_binder().walk().any(|arg| arg.as_type() == Some(arg_ty_in_args))) +} + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: &str, recv: &'tcx Expr<'tcx>) { let item = match recv.kind { ExprKind::Array([]) => None, ExprKind::Array([e]) => Some(e), @@ -43,6 +69,25 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, re let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) { Some((Node::Expr(parent), child_id)) => match parent.kind { ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false, + ExprKind::Call( + Expr { + kind: ExprKind::Path(path), + hir_id, + .. + }, + args, + ) => cx + .typeck_results() + .qpath_res(path, *hir_id) + .opt_def_id() + .filter(|fn_id| cx.tcx.def_kind(fn_id).is_fn_like()) + .is_some_and(|fn_id| is_arg_ty_unified_in_fn(cx, fn_id, child_id, args)), + ExprKind::MethodCall(_name, recv, args, _span) => is_arg_ty_unified_in_fn( + cx, + cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(), + child_id, + once(recv).chain(args.iter()), + ), ExprKind::If(_, _, _) | ExprKind::Match(_, _, _) | ExprKind::Closure(_) diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs index a52d38790a2b..5ccb5243e903 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -69,12 +69,9 @@ pub(super) fn check<'tcx>( used_move: HirIdSet::default(), }; - ExprUseVisitor::for_clippy( - cx, - closure.def_id, - &mut delegate, - ) - .consume_body(body).into_ok(); + ExprUseVisitor::for_clippy(cx, closure.def_id, &mut delegate) + .consume_body(body) + .into_ok(); let mut to_be_discarded = false; diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 2b92bff016db..6200716afbe9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -66,6 +66,7 @@ mod map_flatten; mod map_identity; mod map_unwrap_or; mod mut_mutex_lock; +mod needless_character_iteration; mod needless_collect; mod needless_option_as_deref; mod needless_option_take; @@ -93,7 +94,6 @@ mod seek_from_current; mod seek_to_start_instead_of_rewind; mod single_char_add_str; mod single_char_insert_string; -mod single_char_pattern; mod single_char_push_string; mod skip_while_next; mod stable_sort_primitive; @@ -133,6 +133,7 @@ use bind_instead_of_map::BindInsteadOfMap; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::macros::FormatArgsStorage; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty}; pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; @@ -256,7 +257,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s. /// - /// ### Why is this bad? + /// ### Why restrict this? /// It is better to handle the `None` or `Err` case, /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is @@ -332,7 +333,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Usually it is better to handle the `None` or `Err` case. /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why /// this lint is `Allow` by default. @@ -1028,8 +1029,8 @@ declare_clippy_lint! { /// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified /// function syntax instead (e.g., `Rc::clone(foo)`). /// - /// ### Why is this bad? - /// Calling '.clone()' on an Rc, Arc, or Weak + /// ### Why restrict this? + /// Calling `.clone()` on an `Rc`, `Arc`, or `Weak` /// can obscure the fact that only the pointer is being cloned, not the underlying /// data. /// @@ -1050,7 +1051,7 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub CLONE_ON_REF_PTR, restriction, - "using 'clone' on a ref-counted pointer" + "using `clone` on a ref-counted pointer" } declare_clippy_lint! { @@ -1139,38 +1140,6 @@ declare_clippy_lint! { "not returning type containing `Self` in a `new` method" } -declare_clippy_lint! { - /// ### What it does - /// Checks for string methods that receive a single-character - /// `str` as an argument, e.g., `_.split("x")`. - /// - /// ### Why is this bad? - /// While this can make a perf difference on some systems, - /// benchmarks have proven inconclusive. But at least using a - /// char literal makes it clear that we are looking at a single - /// character. - /// - /// ### Known problems - /// Does not catch multi-byte unicode characters. This is by - /// design, on many machines, splitting by a non-ascii char is - /// actually slower. Please do your own measurements instead of - /// relying solely on the results of this lint. - /// - /// ### Example - /// ```rust,ignore - /// _.split("x"); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// _.split('x'); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub SINGLE_CHAR_PATTERN, - pedantic, - "using a single-character str where a char could be used, e.g., `_.split(\"x\")`" -} - declare_clippy_lint! { /// ### What it does /// Checks for calling `.step_by(0)` on iterators which panics. @@ -1358,7 +1327,7 @@ declare_clippy_lint! { /// Checks for usage of `.get().unwrap()` (or /// `.get_mut().unwrap`) on a standard library type which implements `Index` /// - /// ### Why is this bad? + /// ### Why restrict this? /// Using the Index trait (`[]`) is more clear and more /// concise. /// @@ -1742,7 +1711,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for `FileType::is_file()`. /// - /// ### Why is this bad? + /// ### Why restrict this? /// When people testing a file type with `FileType::is_file` /// they are testing whether a path is something they can get bytes from. But /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover @@ -2687,8 +2656,9 @@ declare_clippy_lint! { /// ### What it does /// Checks for instances of `map_err(|_| Some::Enum)` /// - /// ### Why is this bad? - /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error + /// ### Why restrict this? + /// This `map_err` throws away the original error rather than allowing the enum to + /// contain and report the cause of the error. /// /// ### Example /// Before: @@ -3144,7 +3114,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for usage of File::read_to_end and File::read_to_string. /// - /// ### Why is this bad? + /// ### Why restrict this? /// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values. /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html) /// @@ -4082,17 +4052,40 @@ declare_clippy_lint! { /// ```no_run /// println!("the string is empty"); /// ``` - #[clippy::version = "1.78.0"] + #[clippy::version = "1.79.0"] pub CONST_IS_EMPTY, suspicious, "is_empty() called on strings known at compile time" } + +declare_clippy_lint! { + /// ### What it does + /// Checks if an iterator is used to check if a string is ascii. + /// + /// ### Why is this bad? + /// The `str` type already implements the `is_ascii` method. + /// + /// ### Example + /// ```no_run + /// "foo".chars().all(|c| c.is_ascii()); + /// ``` + /// Use instead: + /// ```no_run + /// "foo".is_ascii(); + /// ``` + #[clippy::version = "1.80.0"] + pub NEEDLESS_CHARACTER_ITERATION, + suspicious, + "is_ascii() called on a char iterator" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, allowed_dotfiles: FxHashSet, + format_args: FormatArgsStorage, } impl Methods { @@ -4103,6 +4096,7 @@ impl Methods { allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, mut allowed_dotfiles: FxHashSet, + format_args: FormatArgsStorage, ) -> Self { allowed_dotfiles.extend(DEFAULT_ALLOWED_DOTFILES.iter().map(ToString::to_string)); @@ -4112,6 +4106,7 @@ impl Methods { allow_expect_in_tests, allow_unwrap_in_tests, allowed_dotfiles, + format_args, } } } @@ -4141,7 +4136,6 @@ impl_lint_pass!(Methods => [ FLAT_MAP_OPTION, INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, - SINGLE_CHAR_PATTERN, SINGLE_CHAR_ADD_STR, SEARCH_IS_SOME, FILTER_NEXT, @@ -4249,6 +4243,7 @@ impl_lint_pass!(Methods => [ UNNECESSARY_RESULT_MAP_OR_ELSE, MANUAL_C_STR_LITERALS, UNNECESSARY_GET_THEN_CHECK, + NEEDLESS_CHARACTER_ITERATION, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4281,13 +4276,20 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args); - expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args); + expect_fun_call::check( + cx, + &self.format_args, + expr, + method_span, + method_call.ident.as_str(), + receiver, + args, + ); clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args); clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args); inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args); single_char_add_str::check(cx, expr, receiver, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver); - single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args); unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv); }, ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { @@ -4448,6 +4450,7 @@ impl Methods { }, ("all", [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); + needless_character_iteration::check(cx, expr, recv, arg, true); if let Some(("cloned", recv2, [], _, _)) = method_call(recv) { iter_overeager_cloned::check( cx, @@ -4468,6 +4471,7 @@ impl Methods { }, ("any", [arg]) => { unused_enumerate_index::check(cx, expr, recv, arg); + needless_character_iteration::check(cx, expr, recv, arg, false); match method_call(recv) { Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs new file mode 100644 index 000000000000..e3d782077154 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs @@ -0,0 +1,124 @@ +use rustc_errors::Applicability; +use rustc_hir::{Closure, Expr, ExprKind, HirId, StmtKind, UnOp}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::Span; + +use super::utils::get_last_chain_binding_hir_id; +use super::NEEDLESS_CHARACTER_ITERATION; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use clippy_utils::{match_def_path, path_to_local_id, peel_blocks}; + +fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { + while let ExprKind::AddrOf(_, _, e) = expr.kind { + expr = e; + } + expr +} + +fn handle_expr( + cx: &LateContext<'_>, + expr: &Expr<'_>, + first_param: HirId, + span: Span, + before_chars: Span, + revert: bool, + is_all: bool, +) { + match expr.kind { + ExprKind::MethodCall(method, receiver, [], _) => { + // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is + // `is_ascii`, then only `.all()` should warn. + if revert != is_all + && method.ident.name.as_str() == "is_ascii" + && path_to_local_id(receiver, first_param) + && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() + && *char_arg_ty.kind() == ty::Char + && let Some(snippet) = snippet_opt(cx, before_chars) + { + span_lint_and_sugg( + cx, + NEEDLESS_CHARACTER_ITERATION, + span, + "checking if a string is ascii using iterators", + "try", + format!("{}{snippet}.is_ascii()", if revert { "!" } else { "" }), + Applicability::MachineApplicable, + ); + } + }, + ExprKind::Block(block, _) => { + if block.stmts.iter().any(|stmt| !matches!(stmt.kind, StmtKind::Let(_))) { + // If there is something else than let bindings, then better not emit the lint. + return; + } + if let Some(block_expr) = block.expr + // First we ensure that this is a "binding chain" (each statement is a binding + // of the previous one) and that it is a binding of the closure argument. + && let Some(last_chain_binding_id) = + get_last_chain_binding_hir_id(first_param, block.stmts) + { + handle_expr( + cx, + block_expr, + last_chain_binding_id, + span, + before_chars, + revert, + is_all, + ); + } + }, + ExprKind::Unary(UnOp::Not, expr) => handle_expr(cx, expr, first_param, span, before_chars, !revert, is_all), + ExprKind::Call(fn_path, [arg]) => { + // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is + // `is_ascii`, then only `.all()` should warn. + if revert != is_all + && let ExprKind::Path(path) = fn_path.kind + && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id() + && match_def_path(cx, fn_def_id, &["core", "char", "methods", "", "is_ascii"]) + && path_to_local_id(peels_expr_ref(arg), first_param) + && let Some(snippet) = snippet_opt(cx, before_chars) + { + span_lint_and_sugg( + cx, + NEEDLESS_CHARACTER_ITERATION, + span, + "checking if a string is ascii using iterators", + "try", + format!("{}{snippet}.is_ascii()", if revert { "!" } else { "" }), + Applicability::MachineApplicable, + ); + } + }, + _ => {}, + } +} + +pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>, is_all: bool) { + if let ExprKind::Closure(&Closure { body, .. }) = closure_arg.kind + && let body = cx.tcx.hir().body(body) + && let Some(first_param) = body.params.first() + && let ExprKind::MethodCall(method, mut recv, [], _) = recv.kind + && method.ident.name.as_str() == "chars" + && let str_ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs() + && *str_ty.kind() == ty::Str + { + let expr_start = recv.span; + while let ExprKind::MethodCall(_, new_recv, _, _) = recv.kind { + recv = new_recv; + } + let body_expr = peel_blocks(body.value); + + handle_expr( + cx, + body_expr, + first_param.pat.hir_id, + recv.span.with_hi(call_expr.span.hi()), + recv.span.with_hi(expr_start.hi()), + false, + is_all, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index 1c695655536c..f26f164fa54a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -127,7 +127,7 @@ pub(super) fn check<'tcx>( } } -/// checks for for collecting into a (generic) method or function argument +/// checks for collecting into a (generic) method or function argument /// taking an `IntoIterator` fn check_collect_into_intoiterator<'tcx>( cx: &LateContext<'tcx>, diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs index efec9dd716dc..3d326bc99f95 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -59,7 +59,7 @@ pub(super) fn check<'tcx>( }; let map = cx.tcx.hir(); - let body = map.body(map.body_owned_by(map.enclosing_body_owner(expr.hir_id))); + let body = map.body_owned_by(map.enclosing_body_owner(expr.hir_id)); reference_visitor.visit_body(body); if reference_visitor.found_reference { diff --git a/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs b/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs index 04a27cc98f3c..2d3007e50b81 100644 --- a/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs +++ b/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs @@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t lit.span, "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", "try", - format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')), + format!("\"{}\"", pushed_path_lit.trim_start_matches(['/', '\\'])), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs index f5f1e94bbf45..3b2dd285b8c9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; use clippy_utils::ty::is_type_lang_item; -use clippy_utils::{get_parent_expr, is_trait_method, strip_pat_refs}; +use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs}; use hir::ExprKind; use rustc_errors::Applicability; use rustc_hir as hir; @@ -156,13 +156,3 @@ pub(super) fn check<'tcx>( } } } - -fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - if let Some(parent_expr) = get_parent_expr(cx, expr) - && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind - && receiver.hir_id == expr.hir_id - { - return true; - } - false -} diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs index 20ec2b74d81e..e2f76ac114c6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs +++ b/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs @@ -1,8 +1,8 @@ -use super::utils::get_hint_if_single_char_arg; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; +use rustc_ast::BorrowKind; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, ExprKind}; use rustc_lint::LateContext; use super::SINGLE_CHAR_ADD_STR; @@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR; /// lint for length-1 `str`s as argument for `insert_str` pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability, false) { + if let Some(extension_string) = str_literal_to_char_literal(cx, &args[1], &mut applicability, false) { let base_string_snippet = snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); @@ -25,4 +25,43 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir:: applicability, ); } + + if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[1].kind + && let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind + && path_segment.ident.name == rustc_span::sym::to_string + && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) + { + let base_string_snippet = + snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); + let extension_string = + snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability); + let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); + let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" }; + + let sugg = format!("{base_string_snippet}.insert({pos_arg}, {deref_string}{extension_string})"); + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + "calling `insert_str()` using a single-character converted to string", + "consider using `insert` without `to_string()`", + sugg, + applicability, + ); + } +} + +fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + if cx.typeck_results().expr_ty(expr).is_ref() + && let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind() + && ty.is_char() + { + return true; + } + + false +} + +fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + cx.typeck_results().expr_ty(expr).is_char() } diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs deleted file mode 100644 index 982a7901c453..000000000000 --- a/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs +++ /dev/null @@ -1,64 +0,0 @@ -use super::utils::get_hint_if_single_char_arg; -use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_middle::ty; -use rustc_span::symbol::Symbol; - -use super::SINGLE_CHAR_PATTERN; - -const PATTERN_METHODS: [(&str, usize); 22] = [ - ("contains", 0), - ("starts_with", 0), - ("ends_with", 0), - ("find", 0), - ("rfind", 0), - ("split", 0), - ("split_inclusive", 0), - ("rsplit", 0), - ("split_terminator", 0), - ("rsplit_terminator", 0), - ("splitn", 1), - ("rsplitn", 1), - ("split_once", 0), - ("rsplit_once", 0), - ("matches", 0), - ("rmatches", 0), - ("match_indices", 0), - ("rmatch_indices", 0), - ("trim_start_matches", 0), - ("trim_end_matches", 0), - ("replace", 0), - ("replacen", 0), -]; - -/// lint for length-1 `str`s for methods in `PATTERN_METHODS` -pub(super) fn check( - cx: &LateContext<'_>, - _expr: &hir::Expr<'_>, - method_name: Symbol, - receiver: &hir::Expr<'_>, - args: &[hir::Expr<'_>], -) { - for &(method, pos) in &PATTERN_METHODS { - if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() - && ty.is_str() - && method_name.as_str() == method - && args.len() > pos - && let arg = &args[pos] - && let mut applicability = Applicability::MachineApplicable - && let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability, true) - { - span_lint_and_sugg( - cx, - SINGLE_CHAR_PATTERN, - arg.span, - "single-character string constant used as pattern", - "consider using a `char`", - hint, - applicability, - ); - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs index 97c13825bc10..4ae8634305da 100644 --- a/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs +++ b/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs @@ -1,8 +1,8 @@ -use super::utils::get_hint_if_single_char_arg; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal}; +use rustc_ast::BorrowKind; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, ExprKind}; use rustc_lint::LateContext; use super::SINGLE_CHAR_ADD_STR; @@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR; /// lint for length-1 `str`s as argument for `push_str` pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; - if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability, false) { + if let Some(extension_string) = str_literal_to_char_literal(cx, &args[0], &mut applicability, false) { let base_string_snippet = snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); let sugg = format!("{base_string_snippet}.push({extension_string})"); @@ -24,4 +24,42 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir:: applicability, ); } + + if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[0].kind + && let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind + && path_segment.ident.name == rustc_span::sym::to_string + && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) + { + let base_string_snippet = + snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); + let extension_string = + snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability); + let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" }; + + let sugg = format!("{base_string_snippet}.push({deref_string}{extension_string})"); + span_lint_and_sugg( + cx, + SINGLE_CHAR_ADD_STR, + expr.span, + "calling `push_str()` using a single-character converted to string", + "consider using `push` without `to_string()`", + sugg, + applicability, + ); + } +} + +fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + if cx.typeck_results().expr_ty(expr).is_ref() + && let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind() + && ty.is_char() + { + return true; + } + + false +} + +fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + cx.typeck_results().expr_ty(expr).is_char() } diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index e8c12bbeea0e..4f42fb73547a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -3,7 +3,7 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; +use clippy_utils::visitors::{for_each_expr, Descend}; use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths}; use core::ops::ControlFlow; use rustc_errors::Applicability; @@ -209,7 +209,7 @@ fn indirect_usage<'tcx>( }) = stmt.kind { let mut path_to_binding = None; - let _: Option = for_each_expr_with_closures(cx, init_expr, |e| { + let _: Option = for_each_expr(cx, init_expr, |e| { if path_to_local_id(e, binding) { path_to_binding = Some(e); } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index daf99d986142..c9b9d98dbe60 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -2,7 +2,7 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; -use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::visitors::{for_each_expr_without_closures, Descend}; use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; use core::ops::ControlFlow; use rustc_hir as hir; @@ -26,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value); - let _: Option = for_each_expr(body.value, |e| { + let _: Option = for_each_expr_without_closures(body.value, |e| { if let hir::ExprKind::Ret(Some(e)) = &e.kind { let (found_mapping_res, found_filtering_res) = check_expression(cx, arg_id, e); found_mapping |= found_mapping_res; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 520dcb2d52dc..70885e46e955 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -3,10 +3,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; -use clippy_utils::{fn_def_id, get_parent_expr}; +use clippy_utils::visitors::for_each_expr_without_closures; +use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local}; +use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind}; use rustc_lint::LateContext; use rustc_span::{sym, Symbol}; @@ -36,10 +38,57 @@ pub fn check_for_loop_iter( ) -> bool { if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent)) && let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent) - && let (clone_or_copy_needed, addr_of_exprs) = clone_or_copy_needed(cx, pat, body) + && let (clone_or_copy_needed, references_to_binding) = clone_or_copy_needed(cx, pat, body) && !clone_or_copy_needed && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) { + // Issue 12098 + // https://github.com/rust-lang/rust-clippy/issues/12098 + // if the assignee have `mut borrow` conflict with the iteratee + // the lint should not execute, former didn't consider the mut case + + // check whether `expr` is mutable + fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(hir_id) = path_to_local(expr) + && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) + { + matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) + } else { + true + } + } + + fn is_caller_or_fields_change(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Expr<'_>) -> bool { + let mut change = false; + if let ExprKind::Block(block, ..) = body.kind { + for_each_expr_without_closures(block, |e| { + match e.kind { + ExprKind::Assign(assignee, _, _) | ExprKind::AssignOp(_, assignee, _) => { + change |= !can_mut_borrow_both(cx, caller, assignee); + }, + _ => {}, + } + // the return value has no effect but the function need one return value + ControlFlow::<()>::Continue(()) + }); + } + change + } + + if let ExprKind::Call(_, [child, ..]) = expr.kind { + // filter first layer of iterator + let mut child = child; + // get inner real caller requests for clone + while let ExprKind::MethodCall(_, caller, _, _) = child.kind { + child = caller; + } + if is_mutable(cx, child) && is_caller_or_fields_change(cx, body, child) { + // skip lint + return true; + } + }; + + // the lint should not be executed if no violation happens let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind && maybe_iter_method_name.ident.name == sym::iter && let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) @@ -74,14 +123,12 @@ pub fn check_for_loop_iter( Applicability::MachineApplicable }; diag.span_suggestion(expr.span, "use", snippet, applicability); - for addr_of_expr in addr_of_exprs { - match addr_of_expr.kind { - ExprKind::AddrOf(_, _, referent) => { - let span = addr_of_expr.span.with_hi(referent.span.lo()); - diag.span_suggestion(span, "remove this `&`", "", applicability); - }, - _ => unreachable!(), - } + if !references_to_binding.is_empty() { + diag.multipart_suggestion( + "remove any references to the binding", + references_to_binding, + applicability, + ); } }, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs index cdfaa690d5f4..c14a87c15341 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs @@ -4,10 +4,11 @@ use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath, Stmt, StmtKind}; +use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath}; use rustc_lint::LateContext; use rustc_span::symbol::sym; +use super::utils::get_last_chain_binding_hir_id; use super::UNNECESSARY_RESULT_MAP_OR_ELSE; fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) { @@ -25,22 +26,6 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &E ); } -fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option { - for stmt in statements { - if let StmtKind::Let(local) = stmt.kind - && let Some(init) = local.init - && let ExprKind::Path(QPath::Resolved(_, path)) = init.kind - && let hir::def::Res::Local(local_hir_id) = path.res - && local_hir_id == hir_id - { - hir_id = local.pat.hir_id; - } else { - return None; - } - } - Some(hir_id) -} - fn handle_qpath( cx: &LateContext<'_>, expr: &Expr<'_>, diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs index c50f24f824ab..0d2b0a313176 100644 --- a/src/tools/clippy/clippy_lints/src/methods/utils.rs +++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs @@ -1,14 +1,12 @@ -use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, path_to_local_id, usage}; -use rustc_ast::ast; -use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat}; +use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, QPath, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::sym; +use rustc_span::Span; pub(super) fn derefs_to_slice<'tcx>( cx: &LateContext<'tcx>, @@ -48,63 +46,21 @@ pub(super) fn derefs_to_slice<'tcx>( } } -pub(super) fn get_hint_if_single_char_arg( - cx: &LateContext<'_>, - arg: &Expr<'_>, - applicability: &mut Applicability, - ascii_only: bool, -) -> Option { - if let ExprKind::Lit(lit) = &arg.kind - && let ast::LitKind::Str(r, style) = lit.node - && let string = r.as_str() - && let len = if ascii_only { - string.len() - } else { - string.chars().count() - } - && len == 1 - { - let snip = snippet_with_applicability(cx, arg.span, string, applicability); - let ch = if let ast::StrStyle::Raw(nhash) = style { - let nhash = nhash as usize; - // for raw string: r##"a"## - &snip[(nhash + 2)..(snip.len() - 1 - nhash)] - } else { - // for regular string: "a" - &snip[1..(snip.len() - 1)] - }; - - let hint = format!( - "'{}'", - match ch { - "'" => "\\'", - r"\" => "\\\\", - "\\\"" => "\"", // no need to escape `"` in `'"'` - _ => ch, - } - ); - - Some(hint) - } else { - None - } -} - /// The core logic of `check_for_loop_iter` in `unnecessary_iter_cloned.rs`, this function wraps a /// use of `CloneOrCopyVisitor`. pub(super) fn clone_or_copy_needed<'tcx>( cx: &LateContext<'tcx>, pat: &Pat<'tcx>, body: &'tcx Expr<'tcx>, -) -> (bool, Vec<&'tcx Expr<'tcx>>) { +) -> (bool, Vec<(Span, String)>) { let mut visitor = CloneOrCopyVisitor { cx, binding_hir_ids: pat_bindings(pat), clone_or_copy_needed: false, - addr_of_exprs: Vec::new(), + references_to_binding: Vec::new(), }; visitor.visit_expr(body); - (visitor.clone_or_copy_needed, visitor.addr_of_exprs) + (visitor.clone_or_copy_needed, visitor.references_to_binding) } /// Returns a vector of all `HirId`s bound by the pattern. @@ -120,13 +76,14 @@ fn pat_bindings(pat: &Pat<'_>) -> Vec { /// operations performed on `binding_hir_ids` are: /// * to take non-mutable references to them /// * to use them as non-mutable `&self` in method calls +/// /// If any of `binding_hir_ids` is used in any other way, then `clone_or_copy_needed` will be true /// when `CloneOrCopyVisitor` is done visiting. struct CloneOrCopyVisitor<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, binding_hir_ids: Vec, clone_or_copy_needed: bool, - addr_of_exprs: Vec<&'tcx Expr<'tcx>>, + references_to_binding: Vec<(Span, String)>, } impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> { @@ -141,8 +98,11 @@ impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> { if self.is_binding(expr) { if let Some(parent) = get_parent_expr(self.cx, expr) { match parent.kind { - ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) => { - self.addr_of_exprs.push(parent); + ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, referent) => { + if !parent.span.from_expansion() { + self.references_to_binding + .push((parent.span.until(referent.span), String::new())); + } return; }, ExprKind::MethodCall(.., args, _) => { @@ -170,3 +130,19 @@ impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> { .any(|hir_id| path_to_local_id(expr, *hir_id)) } } + +pub(super) fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option { + for stmt in statements { + if let StmtKind::Let(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Path(QPath::Resolved(_, path)) = init.kind + && let rustc_hir::def::Res::Local(local_hir_id) = path.res + && local_hir_id == hir_id + { + hir_id = local.pat.hir_id; + } else { + return None; + } + } + Some(hir_id) +} diff --git a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs index c6b7f5b0ce27..e43b712021ae 100644 --- a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs +++ b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs @@ -12,14 +12,13 @@ use std::borrow::Cow; declare_clippy_lint! { /// ### What it does - /// Checks for idents which comprise of a single letter. + /// Checks for identifiers which consist of a single character (or fewer than the configured threshold). /// /// Note: This lint can be very noisy when enabled; it may be desirable to only enable it /// temporarily. /// - /// ### Why is this bad? - /// In many cases it's not, but at times it can severely hinder readability. Some codebases may - /// wish to disallow this to improve readability. + /// ### Why restrict this? + /// To improve readability by requiring that every variable has a name more specific than a single letter can be. /// /// ### Example /// ```rust,ignore diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs index 2f5499d7656f..fedcfd11fdcc 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for structure field patterns bound to wildcards. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Using `..` instead is shorter and leaves the focus on /// the fields that are actually bound. /// @@ -138,7 +138,7 @@ declare_clippy_lint! { /// To enforce unseparated literal suffix style, /// see the `separated_literal_suffix` lint. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Suffix style should be consistent. /// /// ### Example @@ -166,7 +166,7 @@ declare_clippy_lint! { /// To enforce separated literal suffix style, /// see the `unseparated_literal_suffix` lint. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Suffix style should be consistent. /// /// ### Example diff --git a/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs index 4f9578d1b257..61f4684c9e37 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs @@ -6,7 +6,7 @@ use rustc_span::Span; use super::ZERO_PREFIXED_LITERAL; pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) { - let trimmed_lit_snip = lit_snip.trim_start_matches(|c| c == '_' || c == '0'); + let trimmed_lit_snip = lit_snip.trim_start_matches(['_', '0']); span_lint_and_then( cx, ZERO_PREFIXED_LITERAL, @@ -20,7 +20,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) { Applicability::MaybeIncorrect, ); // do not advise to use octal form if the literal cannot be expressed in base 8. - if !lit_snip.contains(|c| c == '8' || c == '9') { + if !lit_snip.contains(['8', '9']) { diag.span_suggestion( lit_span, "if you mean to use an octal constant, use `0o`", diff --git a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs index 04df7b7a7e5a..dd98352da860 100644 --- a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs +++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs @@ -10,7 +10,7 @@ declare_clippy_lint! { /// ### What it does /// Checks assertions without a custom panic message. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Without a good custom message, it'd be hard to understand what went wrong when the assertion fails. /// A good custom message should be more about why the failure of the assertion is problematic /// and not what is failed because the assertion already conveys that. diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index c29e46b941c6..94c91b095171 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -4,7 +4,7 @@ use std::ops::ControlFlow; use clippy_utils::comparisons::{normalize_comparison, Rel}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{eq_expr_value, hash_expr, higher}; use rustc_ast::{LitKind, RangeLimits}; use rustc_data_structures::packed::Pu128; @@ -21,7 +21,7 @@ declare_clippy_lint! { /// Checks for repeated slice indexing without asserting beforehand that the length /// is greater than the largest index used to index into the slice. /// - /// ### Why is this bad? + /// ### Why restrict this? /// In the general case where the compiler does not have a lot of information /// about the length of a slice, indexing it repeatedly will generate a bounds check /// for every single index. @@ -405,7 +405,7 @@ impl LateLintPass<'_> for MissingAssertsForIndexing { fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) { let mut map = UnhashMap::default(); - for_each_expr(body.value, |expr| { + for_each_expr_without_closures(body.value, |expr| { check_index(cx, expr, &mut map); check_assert(cx, expr, &mut map); ControlFlow::::Continue(()) diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index 9ba19e0a8658..4592324809fa 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -1,7 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint; use clippy_utils::qualify_min_const_fn::is_min_const_fn; -use clippy_utils::ty::has_drop; use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method}; use rustc_hir as hir; use rustc_hir::def_id::CRATE_DEF_ID; @@ -121,10 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { } }, FnKind::Method(_, sig, ..) => { - if trait_ref_of_method(cx, def_id).is_some() - || already_const(sig.header) - || method_accepts_droppable(cx, def_id) - { + if trait_ref_of_method(cx, def_id).is_some() || already_const(sig.header) { return; } }, @@ -151,26 +147,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); - if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) { - if cx.tcx.is_const_fn_raw(def_id.to_def_id()) { - cx.tcx.dcx().span_err(span, err); - } - } else { + if let Ok(()) = is_min_const_fn(cx.tcx, mir, &self.msrv) { span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`"); } } extract_msrv_attr!(LateContext); } -/// Returns true if any of the method parameters is a type that implements `Drop`. The method -/// can't be made const then, because `drop` can't be const-evaluated. -fn method_accepts_droppable(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { - let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); - - // If any of the params are droppable, return true - sig.inputs().iter().any(|&ty| has_drop(cx, ty)) -} - // We don't have to lint on something that's already `const` #[must_use] fn already_const(header: hir::FnHeader) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 2fb784dae1cd..ca344dc5c810 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -20,9 +20,9 @@ use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does - /// Warns if there is missing doc for any private documentable item + /// Warns if there is missing documentation for any private documentable item. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Doc is good. *rustc* has a `MISSING_DOCS` /// allowed-by-default lint for /// public members, but has no way to enforce documentation of private items. diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index a64faa124f08..77595b121aad 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -110,7 +110,7 @@ fn should_lint<'tcx>( // Is there a call to `DebugStruct::debug_struct`? Do lint if there is. let mut has_debug_struct = false; - for_each_expr(block, |expr| { + for_each_expr(cx, block, |expr| { if let ExprKind::MethodCall(path, recv, ..) = &expr.kind { let recv_ty = typeck_results.expr_ty(recv).peel_refs(); @@ -165,7 +165,7 @@ fn check_struct<'tcx>( let mut has_direct_field_access = false; let mut field_accesses = FxHashSet::default(); - for_each_expr(block, |expr| { + for_each_expr(cx, block, |expr| { if let ExprKind::Field(target, ident) = expr.kind && let target_ty = typeck_results.expr_ty_adjusted(target).peel_refs() && target_ty == self_ty diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index c6a76478806a..33a14d8b7fed 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -10,13 +10,16 @@ declare_clippy_lint! { /// It lints if an exported function, method, trait method with default impl, /// or trait method impl is not `#[inline]`. /// - /// ### Why is this bad? - /// In general, it is not. Functions can be inlined across - /// crates when that's profitable as long as any form of LTO is used. When LTO is disabled, - /// functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates - /// might intend for most of the methods in their public API to be able to be inlined across - /// crates even when LTO is disabled. For these types of crates, enabling this lint might make - /// sense. It allows the crate to require all exported methods to be `#[inline]` by default, and + /// ### Why restrict this? + /// When a function is not marked `#[inline]`, it is not + /// [a “small” candidate for automatic inlining][small], and LTO is not in use, then it is not + /// possible for the function to be inlined into the code of any crate other than the one in + /// which it is defined. Depending on the role of the function and the relationship of the crates, + /// this could significantly reduce performance. + /// + /// Certain types of crates might intend for most of the methods in their public API to be able + /// to be inlined across crates even when LTO is disabled. + /// This lint allows those crates to require all exported methods to be `#[inline]` by default, and /// then opt out for specific methods where this might not make sense. /// /// ### Example @@ -51,6 +54,8 @@ declare_clippy_lint! { /// fn def_bar() {} // missing #[inline] /// } /// ``` + /// + /// [small]: https://github.com/rust-lang/rust/pull/116505 #[clippy::version = "pre 1.29.0"] pub MISSING_INLINE_IN_PUBLIC_ITEMS, restriction, diff --git a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs index 6f844bc646a2..85029a5e6a0d 100644 --- a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs +++ b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs @@ -10,16 +10,16 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does /// Checks if a provided method is used implicitly by a trait - /// implementation. A usage example would be a wrapper where every method - /// should perform some operation before delegating to the inner type's /// implementation. /// + /// ### Why restrict this? + /// To ensure that a certain implementation implements every method; for example, + /// a wrapper type where every method should delegate to the corresponding method of + /// the inner type's implementation. + /// /// This lint should typically be enabled on a specific trait `impl` item /// rather than globally. /// - /// ### Why is this bad? - /// Indicates that a method is missing. - /// /// ### Example /// ```no_run /// trait Trait { diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index 181351910db6..9c5a8a0cfcdf 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -12,9 +12,10 @@ declare_clippy_lint! { /// whether the read occurs before or after the write depends on the evaluation /// order of sub-expressions. /// - /// ### Why is this bad? - /// It is often confusing to read. As described [here](https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands), - /// the operands of these expressions are evaluated before applying the effects of the expression. + /// ### Why restrict this? + /// While [the evaluation order of sub-expressions] is fully specified in Rust, + /// it still may be confusing to read an expression where the evaluation order + /// affects its behavior. /// /// ### Known problems /// Code which intentionally depends on the evaluation @@ -40,6 +41,8 @@ declare_clippy_lint! { /// }; /// let a = tmp + x; /// ``` + /// + /// [order]: (https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands) #[clippy::version = "pre 1.29.0"] pub MIXED_READ_WRITE_IN_EXPRESSION, restriction, diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index 6c031c081750..305499f9da43 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -10,9 +10,9 @@ use std::path::{Component, Path}; declare_clippy_lint! { /// ### What it does - /// Checks that module layout uses only self named module files, bans `mod.rs` files. + /// Checks that module layout uses only self named module files; bans `mod.rs` files. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Having multiple module layout styles in a project can be confusing. /// /// ### Example @@ -41,7 +41,7 @@ declare_clippy_lint! { /// ### What it does /// Checks that module layout uses only `mod.rs` files. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Having multiple module layout styles in a project can be confusing. /// /// ### Example diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 5306205aed7e..da74a7c7145a 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::visitors::{for_each_expr_with_closures, Descend, Visitable}; +use clippy_utils::visitors::{for_each_expr, Descend, Visitable}; use core::ops::ControlFlow::Continue; use hir::def::{DefKind, Res}; -use hir::{BlockCheckMode, ExprKind, Safety, QPath, UnOp}; +use hir::{BlockCheckMode, ExprKind, QPath, Safety, UnOp}; use rustc_ast::Mutability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -15,7 +15,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for `unsafe` blocks that contain more than one unsafe operation. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Combined with `undocumented_unsafe_blocks`, /// this lint ensures that each unsafe operation must be independently justified. /// Combined with `unused_unsafe`, this lint also ensures @@ -96,7 +96,7 @@ fn collect_unsafe_exprs<'tcx>( node: impl Visitable<'tcx>, unsafe_ops: &mut Vec<(&'static str, Span)>, ) { - for_each_expr_with_closures(cx, node, |expr| { + for_each_expr(cx, node, |expr| { match expr.kind { ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)), diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index 7ecc86176942..853e476a006c 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -14,7 +14,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for usage of `Mutex` where an atomic will do. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Using a mutex just to make access to a plain bool or /// reference sequential is shooting flies with cannons. /// `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index f9ee4a3dc93a..9cb4fa41c73f 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -6,8 +6,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{ - higher, is_else_clause, is_expn_of, is_parent_stmt, peel_blocks, peel_blocks_with_stmt, span_extract_comment, - SpanlessEq, + get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, is_receiver_of_method_call, + peel_blocks, peel_blocks_with_stmt, span_extract_comment, SpanlessEq, }; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -121,14 +121,7 @@ fn condition_needs_parentheses(e: &Expr<'_>) -> bool { | ExprKind::Type(i, _) | ExprKind::Index(i, _, _) = inner.kind { - if matches!( - i.kind, - ExprKind::Block(..) - | ExprKind::ConstBlock(..) - | ExprKind::If(..) - | ExprKind::Loop(..) - | ExprKind::Match(..) - ) { + if is_block_like(i) { return true; } inner = i; @@ -161,7 +154,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { snip = snip.blockify(); } - if condition_needs_parentheses(cond) && is_parent_stmt(cx, e.hir_id) { + if (condition_needs_parentheses(cond) && is_parent_stmt(cx, e.hir_id)) + || is_receiver_of_method_call(cx, e) + || is_as_argument(cx, e) + { snip = snip.maybe_par(); } @@ -449,3 +445,7 @@ fn fetch_assign<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, bool) None } } + +fn is_as_argument(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + matches!(get_parent_expr(cx, e).map(|e| e.kind), Some(ExprKind::Cast(_, _))) +} diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs index ae6cf992ef7b..4f99eaa40c29 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -1,6 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; +use clippy_utils::mir::PossibleBorrowerMap; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode}; @@ -11,7 +11,6 @@ use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath}; use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::{ self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, Ty, }; @@ -106,7 +105,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> { } && let count = needless_borrow_count( cx, - &mut self.possible_borrowers, fn_id, cx.typeck_results().node_args(hir_id), i, @@ -131,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> { } } - fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &Body<'_>) { if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| { local_def_id == cx.tcx.hir().body_owner_def_id(body.id()) }) { @@ -155,11 +153,9 @@ fn path_has_args(p: &QPath<'_>) -> bool { /// The following constraints will be checked: /// * The borrowed expression meets all the generic type's constraints. /// * The generic type appears only once in the functions signature. -/// * The borrowed value will not be moved if it is used later in the function. -#[expect(clippy::too_many_arguments)] +/// * The borrowed value is Copy itself OR not a variable (created by a function call) fn needless_borrow_count<'tcx>( cx: &LateContext<'tcx>, - possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, fn_id: DefId, callee_args: ty::GenericArgsRef<'tcx>, arg_index: usize, @@ -234,9 +230,9 @@ fn needless_borrow_count<'tcx>( let referent_ty = cx.typeck_results().expr_ty(referent); - if !is_copy(cx, referent_ty) - && (referent_ty.has_significant_drop(cx.tcx, cx.param_env) - || !referent_used_exactly_once(cx, possible_borrowers, reference)) + if (!is_copy(cx, referent_ty) && !referent_ty.is_ref()) + && let ExprKind::AddrOf(_, _, inner) = reference.kind + && !matches!(inner.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) { return false; } @@ -315,7 +311,7 @@ fn is_mixed_projection_predicate<'tcx>( ) -> bool { let generics = cx.tcx.generics_of(callee_def_id); // The predicate requires the projected type to equal a type parameter from the parent context. - if let Some(term_ty) = projection_predicate.term.ty() + if let Some(term_ty) = projection_predicate.term.as_type() && let ty::Param(term_param_ty) = term_ty.kind() && (term_param_ty.index as usize) < generics.parent_count { @@ -339,37 +335,6 @@ fn is_mixed_projection_predicate<'tcx>( } } -fn referent_used_exactly_once<'tcx>( - cx: &LateContext<'tcx>, - possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, - reference: &Expr<'tcx>, -) -> bool { - if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id) - && let Some(local) = expr_local(cx.tcx, reference) - && let [location] = *local_assignments(mir, local).as_slice() - && let block_data = &mir.basic_blocks[location.block] - && let Some(statement) = block_data.statements.get(location.statement_index) - && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind - && !place.is_indirect_first_projection() - { - let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); - if possible_borrowers - .last() - .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id) - { - possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); - } - let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; - // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is - // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of - // itself. See the comment in that method for an explanation as to why. - possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) - && used_exactly_once(mir, place.local).unwrap_or(false) - } else { - false - } -} - // Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting // projected type that is a type parameter. Returns `false` if replacing the types would have an // effect on the function signature beyond substituting `new_ty` for `param_ty`. @@ -405,10 +370,14 @@ fn replace_types<'tcx>( if replaced.insert(param_ty.index) { for projection_predicate in projection_predicates { if projection_predicate.projection_term.self_ty() == param_ty.to_ty(cx.tcx) - && let Some(term_ty) = projection_predicate.term.ty() + && let Some(term_ty) = projection_predicate.term.as_type() && let ty::Param(term_param_ty) = term_ty.kind() { - let projection = projection_predicate.projection_term.with_self_ty(cx.tcx, new_ty).expect_ty(cx.tcx).to_ty(cx.tcx); + let projection = projection_predicate + .projection_term + .with_self_ty(cx.tcx, new_ty) + .expect_ty(cx.tcx) + .to_ty(cx.tcx); if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) && args[term_param_ty.index as usize] != GenericArg::from(projected_ty) diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs index 8b4a12bb7664..b97cb4579ca7 100644 --- a/src/tools/clippy/clippy_lints/src/needless_continue.rs +++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs @@ -178,8 +178,7 @@ impl EarlyLintPass for NeedlessContinue { /// Given an expression, returns true if either of the following is true /// /// - The expression is a `continue` node. -/// - The expression node is a block with the first statement being a -/// `continue`. +/// - The expression node is a block with the first statement being a `continue`. fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool { match else_expr.kind { ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index 6605d1fa51a3..4bfc30fa5cf8 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; use clippy_utils::source::snippet_opt; use clippy_utils::ty::needs_ordered_drop; -use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_local_used}; +use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used}; use core::ops::ControlFlow; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{ @@ -63,7 +63,7 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]); fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool { - for_each_expr_with_closures(cx, stmt, |e| { + for_each_expr(cx, stmt, |e| { if matches!(e.kind, ExprKind::Assign(..)) { ControlFlow::Break(()) } else { @@ -74,7 +74,7 @@ fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> } fn contains_let(cond: &Expr<'_>) -> bool { - for_each_expr(cond, |e| { + for_each_expr_without_closures(cond, |e| { if matches!(e.kind, ExprKind::Let(_)) { ControlFlow::Break(()) } else { @@ -273,24 +273,16 @@ fn check<'tcx>( msg_span, "unneeded late initialization", |diag| { - diag.tool_only_span_suggestion( - local_stmt.span, - "remove the local", - "", - Applicability::MachineApplicable, - ); - - diag.span_suggestion( - assign.lhs_span, - format!("declare `{binding_name}` here"), - let_snippet, + diag.multipart_suggestion( + format!("move the declaration `{binding_name}` here"), + vec![(local_stmt.span, String::new()), (assign.lhs_span, let_snippet)], Applicability::MachineApplicable, ); }, ); }, ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => { - let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?; + let (applicability, mut suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?; span_lint_and_then( cx, @@ -298,30 +290,26 @@ fn check<'tcx>( local_stmt.span, "unneeded late initialization", |diag| { - diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability); - - diag.span_suggestion_verbose( - usage.stmt.span.shrink_to_lo(), - format!("declare `{binding_name}` here"), - format!("{let_snippet} = "), - applicability, - ); - - diag.multipart_suggestion("remove the assignments from the branches", suggestions, applicability); + suggestions.push((local_stmt.span, String::new())); + suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = "))); if usage.needs_semi { - diag.span_suggestion( - usage.stmt.span.shrink_to_hi(), - "add a semicolon after the `if` expression", - ";", - applicability, - ); + suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned())); } + + diag.multipart_suggestion( + format!( + "move the declaration `{binding_name}` here and remove the assignments from the branches" + ), + suggestions, + applicability, + ); }, ); }, ExprKind::Match(_, arms, MatchSource::Normal) => { - let (applicability, suggestions) = assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?; + let (applicability, mut suggestions) = + assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?; span_lint_and_then( cx, @@ -329,29 +317,18 @@ fn check<'tcx>( local_stmt.span, "unneeded late initialization", |diag| { - diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability); + suggestions.push((local_stmt.span, String::new())); + suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = "))); - diag.span_suggestion_verbose( - usage.stmt.span.shrink_to_lo(), - format!("declare `{binding_name}` here"), - format!("{let_snippet} = "), - applicability, - ); + if usage.needs_semi { + suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned())); + } diag.multipart_suggestion( - "remove the assignments from the `match` arms", + format!("move the declaration `{binding_name}` here and remove the assignments from the `match` arms"), suggestions, applicability, ); - - if usage.needs_semi { - diag.span_suggestion( - usage.stmt.span.shrink_to_hi(), - "add a semicolon after the `match` expression", - ";", - applicability, - ); - } }, ); }, diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs new file mode 100644 index 000000000000..4922c87b206c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs @@ -0,0 +1,164 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_errors::Applicability; +use rustc_hir::def_id::{DefId, DefIdMap}; +use rustc_hir::{GenericBound, Generics, PolyTraitRef, TraitBoundModifier, WherePredicate}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{ClauseKind, PredicatePolarity}; +use rustc_session::declare_lint_pass; +use rustc_span::symbol::Ident; + +declare_clippy_lint! { + /// ### What it does + /// Lints `?Sized` bounds applied to type parameters that cannot be unsized + /// + /// ### Why is this bad? + /// The `?Sized` bound is misleading because it cannot be satisfied by an + /// unsized type + /// + /// ### Example + /// ```rust + /// // `T` cannot be unsized because `Clone` requires it to be `Sized` + /// fn f(t: &T) {} + /// ``` + /// Use instead: + /// ```rust + /// fn f(t: &T) {} + /// + /// // or choose alternative bounds for `T` so that it can be unsized + /// ``` + #[clippy::version = "1.79.0"] + pub NEEDLESS_MAYBE_SIZED, + suspicious, + "a `?Sized` bound that is unusable due to a `Sized` requirement" +} +declare_lint_pass!(NeedlessMaybeSized => [NEEDLESS_MAYBE_SIZED]); + +#[allow(clippy::struct_field_names)] +struct Bound<'tcx> { + /// The [`DefId`] of the type parameter the bound refers to + param: DefId, + ident: Ident, + + trait_bound: &'tcx PolyTraitRef<'tcx>, + modifier: TraitBoundModifier, + + predicate_pos: usize, + bound_pos: usize, +} + +/// Finds all of the [`Bound`]s that refer to a type parameter and are not from a macro expansion +fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator> { + generics + .predicates + .iter() + .enumerate() + .filter_map(|(predicate_pos, predicate)| { + let WherePredicate::BoundPredicate(bound_predicate) = predicate else { + return None; + }; + + let (param, ident) = bound_predicate.bounded_ty.as_generic_param()?; + + Some( + bound_predicate + .bounds + .iter() + .enumerate() + .filter_map(move |(bound_pos, bound)| match bound { + &GenericBound::Trait(ref trait_bound, modifier) => Some(Bound { + param, + ident, + trait_bound, + modifier, + predicate_pos, + bound_pos, + }), + GenericBound::Outlives(_) | GenericBound::Use(..) => None, + }) + .filter(|bound| !bound.trait_bound.span.from_expansion()), + ) + }) + .flatten() +} + +/// Searches the supertraits of the trait referred to by `trait_bound` recursively, returning the +/// path taken to find a `Sized` bound if one is found +fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) -> Option> { + fn search(cx: &LateContext<'_>, path: &mut Vec) -> bool { + let trait_def_id = *path.last().unwrap(); + + if Some(trait_def_id) == cx.tcx.lang_items().sized_trait() { + return true; + } + + for &(predicate, _) in cx.tcx.super_predicates_of(trait_def_id).predicates { + if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && trait_predicate.polarity == PredicatePolarity::Positive + && !path.contains(&trait_predicate.def_id()) + { + path.push(trait_predicate.def_id()); + if search(cx, path) { + return true; + } + path.pop(); + } + } + + false + } + + let mut path = vec![trait_bound.trait_ref.trait_def_id()?]; + search(cx, &mut path).then_some(path) +} + +impl LateLintPass<'_> for NeedlessMaybeSized { + fn check_generics(&mut self, cx: &LateContext<'_>, generics: &Generics<'_>) { + let Some(sized_trait) = cx.tcx.lang_items().sized_trait() else { + return; + }; + + let maybe_sized_params: DefIdMap<_> = type_param_bounds(generics) + .filter(|bound| { + bound.trait_bound.trait_ref.trait_def_id() == Some(sized_trait) + && bound.modifier == TraitBoundModifier::Maybe + }) + .map(|bound| (bound.param, bound)) + .collect(); + + for bound in type_param_bounds(generics) { + if bound.modifier == TraitBoundModifier::None + && let Some(sized_bound) = maybe_sized_params.get(&bound.param) + && let Some(path) = path_to_sized_bound(cx, bound.trait_bound) + { + span_lint_and_then( + cx, + NEEDLESS_MAYBE_SIZED, + sized_bound.trait_bound.span, + "`?Sized` bound is ignored because of a `Sized` requirement", + |diag| { + let ty_param = sized_bound.ident; + diag.span_note( + bound.trait_bound.span, + format!("`{ty_param}` cannot be unsized because of the bound"), + ); + + for &[current_id, next_id] in path.array_windows() { + let current = cx.tcx.item_name(current_id); + let next = cx.tcx.item_name(next_id); + diag.note(format!("...because `{current}` has the bound `{next}`")); + } + + diag.span_suggestion_verbose( + generics.span_for_bound_removal(sized_bound.predicate_pos, sized_bound.bound_pos), + "change the bounds that require `Sized`, or remove the `?Sized` bound", + "", + Applicability::MaybeIncorrect, + ); + }, + ); + + return; + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index 9b852f52ea1e..57ba0da53319 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -1,7 +1,7 @@ use super::needless_pass_by_value::requires_exact_signature; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; -use clippy_utils::visitors::for_each_expr_with_closures; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; @@ -117,7 +117,9 @@ fn check_closures<'tcx>( .associated_body() .map(|(_, body_id)| hir.body(body_id)) { - euv::ExprUseVisitor::for_clippy(cx, closure, &mut *ctx).consume_body(body).into_ok(); + euv::ExprUseVisitor::for_clippy(cx, closure, &mut *ctx) + .consume_body(body) + .into_ok(); } } } @@ -194,14 +196,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { async_closures: FxHashSet::default(), tcx: cx.tcx, }; - euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx).consume_body(body).into_ok(); + euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx) + .consume_body(body) + .into_ok(); let mut checked_closures = FxHashSet::default(); // We retrieve all the closures declared in the function because they will not be found // by `euv::Delegate`. let mut closures: FxHashSet = FxHashSet::default(); - for_each_expr_with_closures(cx, body, |expr| { + for_each_expr(cx, body, |expr| { if let ExprKind::Closure(closure) = expr.kind { closures.insert(closure.def_id); } diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 0986571d0f28..f2e00cef7e9f 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -133,7 +133,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // function body. let MovedVariablesCtxt { moved_vars } = { let mut ctx = MovedVariablesCtxt::default(); - euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx).consume_body(body).into_ok(); + euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx) + .consume_body(body) + .into_ok(); ctx }; diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index f915145e794b..87f886b1128d 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -94,7 +94,6 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx rustc_hir::Block<'tcx>) { for hir_id in self.local_bindings.pop().unwrap() { - // FIXME(rust/#120456) - is `swap_remove` correct? if let Some(span) = self.underscore_bindings.swap_remove(&hir_id) { span_lint_hir( cx, @@ -109,7 +108,6 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some(def_id) = path_to_local(expr) { - // FIXME(rust/#120456) - is `swap_remove` correct? self.underscore_bindings.swap_remove(&def_id); } } @@ -118,7 +116,11 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { impl NoEffect { fn check_no_effect(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { if let StmtKind::Semi(expr) = stmt.kind { - // move `expr.span.from_expansion()` ahead + // Covered by rustc `path_statements` lint + if matches!(expr.kind, ExprKind::Path(_)) { + return true; + } + if expr.span.from_expansion() { return false; } diff --git a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs index 932d6fe54d66..de6a1a36f3e9 100644 --- a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs +++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_res_lang_ctor, last_path_segment, path_res, std_or_core}; +use clippy_utils::{is_from_proc_macro, is_res_lang_ctor, last_path_segment, path_res, std_or_core}; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty::EarlyBinder; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -111,7 +112,7 @@ declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL impl LateLintPass<'_> for NonCanonicalImpls { #[expect(clippy::too_many_lines)] - fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { + fn check_impl_item<'tcx>(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'tcx>) { let Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) else { return; }; @@ -128,6 +129,9 @@ impl LateLintPass<'_> for NonCanonicalImpls { let ExprKind::Block(block, ..) = body.value.kind else { return; }; + if in_external_macro(cx.sess(), block.span) || is_from_proc_macro(cx, impl_item) { + return; + } if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id) && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 76d9cee18aa7..5cb8e7bfab2a 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -7,7 +7,7 @@ use std::ptr; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::in_constant; use clippy_utils::macros::macro_backtrace; -use clippy_utils::ty::InteriorMut; +use clippy_utils::ty::{implements_trait, InteriorMut}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{ @@ -18,7 +18,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId}; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, InnerSpan, Span, DUMMY_SP}; +use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::abi::VariantIdx; // FIXME: this is a correctness problem but there's no suitable @@ -127,19 +127,19 @@ declare_clippy_lint! { } #[derive(Copy, Clone)] -enum Source { - Item { item: Span }, +enum Source<'tcx> { + Item { item: Span, ty: Ty<'tcx> }, Assoc { item: Span }, Expr { expr: Span }, } -impl Source { +impl Source<'_> { #[must_use] fn lint(&self) -> (&'static Lint, &'static str, Span) { match self { - Self::Item { item } | Self::Assoc { item, .. } => ( + Self::Item { item, .. } | Self::Assoc { item, .. } => ( DECLARE_INTERIOR_MUTABLE_CONST, - "a `const` item should never be interior mutable", + "a `const` item should not be interior mutable", *item, ), Self::Expr { expr } => ( @@ -151,16 +151,24 @@ impl Source { } } -fn lint(cx: &LateContext<'_>, source: Source) { +fn lint<'tcx>(cx: &LateContext<'tcx>, source: Source<'tcx>) { let (lint, msg, span) = source.lint(); span_lint_and_then(cx, lint, span, msg, |diag| { if span.from_expansion() { return; // Don't give suggestions into macros. } match source { - Source::Item { .. } => { - let const_kw_span = span.from_inner(InnerSpan::new(0, 5)); - diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)"); + Source::Item { ty, .. } => { + let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else { + return; + }; + if implements_trait(cx, ty, sync_trait, &[]) { + diag.help("consider making this a static item"); + } else { + diag.help( + "consider making this `Sync` so that it can go in a static item or using a `thread_local`", + ); + } }, Source::Assoc { .. } => (), Source::Expr { .. } => { @@ -199,7 +207,7 @@ impl<'tcx> NonCopyConst<'tcx> { .any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), ty::Adt(def, args) if def.is_enum() => { let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap(); - let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap()); + let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().to_u32()); fields .iter() .copied() @@ -311,7 +319,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { && self.interior_mut.is_interior_mut_ty(cx, ty) && Self::is_value_unfrozen_poly(cx, body_id, ty) { - lint(cx, Source::Item { item: it.span }); + lint(cx, Source::Item { item: it.span, ty }); } } } diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs index 7b26235291a5..eacfe9ff328d 100644 --- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -98,6 +98,10 @@ struct SimilarNamesLocalVisitor<'a, 'tcx> { impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> { fn check_single_char_names(&self) { + if self.single_char_names.last().map(Vec::len) == Some(0) { + return; + } + let num_single_char_names = self.single_char_names.iter().flatten().count(); let threshold = self.lint.single_char_binding_names_threshold; if num_single_char_names as u64 > threshold { diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs index b3ff5ecae862..8b8aabe7accc 100644 --- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs @@ -221,7 +221,7 @@ pub struct OnlyUsedInRecursion { } impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { - fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) { + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) { if body.value.span.from_expansion() { return; } @@ -350,7 +350,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { } } - fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) { + fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) { if self.entered_body == Some(body.value.hir_id) { self.entered_body = None; self.params.flag_for_linting(); diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs index 910e584a7a0f..641d881d974b 100644 --- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{binop_traits, eq_expr_value, trait_ref_of_method}; use core::ops::ControlFlow; use rustc_errors::Applicability; @@ -62,7 +62,7 @@ pub(super) fn check<'tcx>( }; let mut found = false; - let found_multiple = for_each_expr(e, |e| { + let found_multiple = for_each_expr_without_closures(e, |e| { if eq_expr_value(cx, assignee, e) { if found { return ControlFlow::Break(()); diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index e002429e3a47..48a442705b29 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -70,7 +70,7 @@ declare_clippy_lint! { /// Known safe built-in types like `Wrapping` or `Saturating`, floats, operations in constant /// environments, allowed types and non-constant operations that won't overflow are ignored. /// - /// ### Why is this bad? + /// ### Why restrict this? /// For integers, overflow will trigger a panic in debug builds or wrap the result in /// release mode; division by zero will cause a panic in either mode. As a result, it is /// desirable to explicitly call checked, wrapping or saturating arithmetic methods. @@ -100,7 +100,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for float arithmetic. /// - /// ### Why is this bad? + /// ### Why restrict this? /// For some embedded systems or kernel development, it /// can be useful to rule out floating-point numbers. /// @@ -502,7 +502,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for division of integers /// - /// ### Why is this bad? + /// ### Why restrict this? /// When outside of some very specific algorithms, /// integer division is very often a mistake because it discards the /// remainder. @@ -596,7 +596,7 @@ declare_clippy_lint! { /// value and constant, except in functions called `*eq*` (which probably /// implement equality for a type involving floats). /// - /// ### Why is this bad? + /// ### Why restrict this? /// Floating point calculations are usually imprecise, so /// asking if two values are *exactly* equal is asking for trouble. For a good /// guide on what to do, see [the floating point @@ -653,8 +653,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for modulo arithmetic. /// - /// ### Why is this bad? - /// The results of modulo (%) operation might differ + /// ### Why restrict this? + /// The results of modulo (`%`) operation might differ /// depending on the language, when negative numbers are involved. /// If you interop with different languages it might be beneficial /// to double check all places that use modulo arithmetic. @@ -868,11 +868,11 @@ impl<'tcx> LateLintPass<'tcx> for Operators { self.arithmetic_context.expr_post(e.hir_id); } - fn check_body(&mut self, cx: &LateContext<'tcx>, b: &'tcx Body<'_>) { + fn check_body(&mut self, cx: &LateContext<'tcx>, b: &Body<'_>) { self.arithmetic_context.enter_body(cx, b); } - fn check_body_post(&mut self, cx: &LateContext<'tcx>, b: &'tcx Body<'_>) { + fn check_body_post(&mut self, cx: &LateContext<'tcx>, b: &Body<'_>) { self.arithmetic_context.body_post(cx, b); } } diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs index f821a4efee7b..381975199d28 100644 --- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs +++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs @@ -13,9 +13,9 @@ use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does - /// Checks for usage of `panic!` or assertions in a function of type result. + /// Checks for usage of `panic!` or assertions in a function whose return type is `Result`. /// - /// ### Why is this bad? + /// ### Why restrict this? /// For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided. /// /// ### Known problems @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { let mut panics = Vec::new(); - let _: Option = for_each_expr(body.value, |e| { + let _: Option = for_each_expr(cx, body.value, |e| { let Some(macro_call) = root_macro_call_first_node(cx, e) else { return ControlFlow::Continue(Descend::Yes); }; diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs index ef51a9a9a1c8..80ef761906e0 100644 --- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs +++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs @@ -1,15 +1,21 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_in_test; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; + +#[derive(Clone)] +pub struct PanicUnimplemented { + pub allow_panic_in_tests: bool, +} declare_clippy_lint! { /// ### What it does /// Checks for usage of `panic!`. /// - /// ### Why is this bad? - /// `panic!` will stop the execution of the executable. + /// ### Why restrict this? + /// This macro, or panics in general, may be unwanted in production code. /// /// ### Example /// ```no_run @@ -25,8 +31,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for usage of `unimplemented!`. /// - /// ### Why is this bad? - /// This macro should not be present in production code. + /// ### Why restrict this? + /// This macro, or panics in general, may be unwanted in production code. /// /// ### Example /// ```no_run @@ -42,9 +48,9 @@ declare_clippy_lint! { /// ### What it does /// Checks for usage of `todo!`. /// - /// ### Why is this bad? - /// The `todo!` macro is often used for unfinished code, and it causes - /// code to panic. It should not be present in production code. + /// ### Why restrict this? + /// The `todo!` macro indicates the presence of unfinished code, + /// so it should not be present in production code. /// /// ### Example /// ```no_run @@ -64,8 +70,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for usage of `unreachable!`. /// - /// ### Why is this bad? - /// This macro can cause code to panic. + /// ### Why restrict this? + /// This macro, or panics in general, may be unwanted in production code. /// /// ### Example /// ```no_run @@ -77,7 +83,7 @@ declare_clippy_lint! { "usage of the `unreachable!` macro" } -declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]); +impl_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]); impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -85,7 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { return; }; if is_panic(cx, macro_call.def_id) { - if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + if cx.tcx.hir().is_inside_const_context(expr.hir_id) + || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id) + { return; } diff --git a/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs b/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs index ffa403e27ca3..2d20cbea698f 100644 --- a/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs +++ b/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs @@ -5,15 +5,16 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks whether partial fields of a struct are public. + /// Checks whether some but not all fields of a `struct` are public. /// /// Either make all fields of a type public, or make none of them public /// - /// ### Why is this bad? + /// ### Why restrict this? /// Most types should either be: /// * Abstract data types: complex objects with opaque implementation which guard - /// interior invariants and expose intentionally limited API to the outside world. - /// * Data: relatively simple objects which group a bunch of related attributes together. + /// interior invariants and expose intentionally limited API to the outside world. + /// * Data: relatively simple objects which group a bunch of related attributes together, + /// but have no invariants. /// /// ### Example /// ```no_run diff --git a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs index 44db061b8bee..9661a57b8b95 100644 --- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs @@ -30,9 +30,8 @@ declare_clippy_lint! { /// this lint can still be used to highlight areas of interest and ensure a good understanding /// of ownership semantics. /// - /// ### Why is this bad? - /// It isn't bad in general. But in some contexts it can be desirable - /// because it increases ownership hints in the code, and will guard against some changes + /// ### Why restrict this? + /// It increases ownership hints in the code, and will guard against some changes /// in ownership. /// /// ### Example diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 65929cd5fea9..02c05e0aaf9c 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -188,7 +188,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { } } - fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) { let hir = cx.tcx.hir(); let mut parents = hir.parent_iter(body.value.hir_id); let (item_id, sig, is_trait_item) = match parents.next() { @@ -460,13 +460,19 @@ fn check_fn_args<'cx, 'tcx: 'cx>( } None }) { - if !lifetime.is_anonymous() + if let LifetimeName::Param(param_def_id) = lifetime.res + && !lifetime.is_anonymous() && fn_sig .output() .walk() .filter_map(|arg| { arg.as_region().and_then(|lifetime| match lifetime.kind() { - ty::ReEarlyParam(r) => Some(r.def_id), + ty::ReEarlyParam(r) => Some( + cx.tcx + .generics_of(cx.tcx.parent(param_def_id.to_def_id())) + .region_param(r, cx.tcx) + .def_id, + ), ty::ReBound(_, r) => r.kind.get_id(), ty::ReLateParam(r) => r.bound_region.get_id(), ty::ReStatic @@ -476,14 +482,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( | ty::ReError(_) => None, }) }) - .any(|def_id| { - matches!( - lifetime.res, - LifetimeName::Param(param_def_id) if def_id - .as_local() - .is_some_and(|def_id| def_id == param_def_id), - ) - }) + .any(|def_id| def_id.as_local().is_some_and(|def_id| def_id == param_def_id)) { // `&Cow<'a, T>` when the return type uses 'a is okay return None; @@ -526,7 +525,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( }) } -fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) { +fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&Body<'tcx>>) { if let FnRetTy::Return(ty) = sig.decl.output && let Some((out, Mutability::Mut, _)) = get_ref_lm(ty) { @@ -560,7 +559,7 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio } #[expect(clippy::too_many_lines)] -fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: &[PtrArg<'tcx>]) -> Vec { +fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[PtrArg<'tcx>]) -> Vec { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, /// Map from a local id to which argument it came from (index into `Self::args` and diff --git a/src/tools/clippy/clippy_lints/src/pub_use.rs b/src/tools/clippy/clippy_lints/src/pub_use.rs index c0e999e76ef2..ab8f8a1689dc 100644 --- a/src/tools/clippy/clippy_lints/src/pub_use.rs +++ b/src/tools/clippy/clippy_lints/src/pub_use.rs @@ -5,13 +5,11 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// /// Restricts the usage of `pub use ...` /// - /// ### Why is this bad? - /// - /// `pub use` is usually fine, but a project may wish to limit `pub use` instances to prevent - /// unintentional exports or to encourage placing exported items directly in public modules + /// ### Why restrict this? + /// A project may wish to limit `pub use` instances to prevent + /// unintentional exports, or to encourage placing exported items directly in public modules. /// /// ### Example /// ```no_run diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 1f1ce147ca24..7cf98ad9e09a 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -370,11 +370,11 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { } } - fn check_body(&mut self, _: &LateContext<'tcx>, _: &'tcx Body<'tcx>) { + fn check_body(&mut self, _: &LateContext<'tcx>, _: &Body<'tcx>) { self.try_block_depth_stack.push(0); } - fn check_body_post(&mut self, _: &LateContext<'tcx>, _: &'tcx Body<'tcx>) { + fn check_body_post(&mut self, _: &LateContext<'tcx>, _: &Body<'tcx>) { self.try_block_depth_stack.pop(); } diff --git a/src/tools/clippy/clippy_lints/src/question_mark_used.rs b/src/tools/clippy/clippy_lints/src/question_mark_used.rs index ddfc53083c46..f5e6cb804da1 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark_used.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark_used.rs @@ -9,7 +9,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for expressions that use the question mark operator and rejects them. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Sometimes code wants to avoid the question mark operator because for instance a local /// block requires a macro to re-throw errors to attach additional information to the /// error. diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs index 7e71f48c6d9a..3a0042454592 100644 --- a/src/tools/clippy/clippy_lints/src/raw_strings.rs +++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs @@ -15,8 +15,10 @@ declare_clippy_lint! { /// ### What it does /// Checks for raw string literals where a string literal can be used instead. /// - /// ### Why is this bad? - /// It's just unnecessary, but there are many cases where using a raw string literal is more + /// ### Why restrict this? + /// For consistent style by using simpler string literals whenever possible. + /// + /// However, there are many cases where using a raw string literal is more /// idiomatic than a string literal, so it's opt-in. /// /// ### Example diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs index 0ed957f1f2fb..313e4083256b 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::peel_blocks; use clippy_utils::source::{snippet, walk_span_to_context}; use clippy_utils::ty::implements_trait; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use rustc_errors::Applicability; use rustc_hir::{ Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, MatchSource, @@ -107,7 +107,7 @@ fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind && let ExprKind::Call(_, [into_future_arg]) = match_value.kind && let ctxt = expr.span.ctxt() - && for_each_expr(into_future_arg, |e| { + && for_each_expr_without_closures(into_future_arg, |e| { walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(())) }) .is_none() diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index c99b657c23a2..7f87d18e5023 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -46,7 +46,7 @@ declare_clippy_lint! { /// Checks for slicing expressions which are equivalent to dereferencing the /// value. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Some people may prefer to dereference rather than slice. /// /// ### Example diff --git a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs index 11b95ee3a54c..81556f396141 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs @@ -11,7 +11,7 @@ declare_clippy_lint! { /// ### What it does /// Warns about needless / redundant type annotations. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Code without type annotations is shorter and in most cases /// more idiomatic and easier to modify. /// diff --git a/src/tools/clippy/clippy_lints/src/ref_patterns.rs b/src/tools/clippy/clippy_lints/src/ref_patterns.rs index 607a0740b843..467038523b49 100644 --- a/src/tools/clippy/clippy_lints/src/ref_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/ref_patterns.rs @@ -6,9 +6,11 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does /// Checks for usages of the `ref` keyword. - /// ### Why is this bad? + /// + /// ### Why restrict this? /// The `ref` keyword can be confusing for people unfamiliar with it, and often /// it is more concise to use `&` instead. + /// /// ### Example /// ```no_run /// let opt = Some(5); diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs index 16086ba6637c..8f32cf5f2a1d 100644 --- a/src/tools/clippy/clippy_lints/src/reference.rs +++ b/src/tools/clippy/clippy_lints/src/reference.rs @@ -48,6 +48,9 @@ impl EarlyLintPass for DerefAddrOf { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind && let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind + // NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section. + // See #12854 for details. + && !matches!(addrof_target.kind, ExprKind::Array(_)) && deref_target.span.eq_ctxt(e.span) && !addrof_target.span.from_expansion() { diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index e8f9d4381047..c11da3147ef4 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; +use clippy_utils::visitors::{for_each_expr, Descend}; use clippy_utils::{ - fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res, path_to_local_id, span_contains_cfg, - span_find_starting_semi, + binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res, + path_to_local_id, span_contains_cfg, span_find_starting_semi, }; use core::ops::ControlFlow; use rustc_errors::Applicability; @@ -129,7 +129,7 @@ enum RetReplacement<'tcx> { Empty, Block, Unit, - IfSequence(Cow<'tcx, str>, Applicability), + NeedsPar(Cow<'tcx, str>, Applicability), Expr(Cow<'tcx, str>, Applicability), } @@ -139,13 +139,13 @@ impl<'tcx> RetReplacement<'tcx> { Self::Empty | Self::Expr(..) => "remove `return`", Self::Block => "replace `return` with an empty block", Self::Unit => "replace `return` with a unit value", - Self::IfSequence(..) => "remove `return` and wrap the sequence with parentheses", + Self::NeedsPar(..) => "remove `return` and wrap the sequence with parentheses", } } fn applicability(&self) -> Applicability { match self { - Self::Expr(_, ap) | Self::IfSequence(_, ap) => *ap, + Self::Expr(_, ap) | Self::NeedsPar(_, ap) => *ap, _ => Applicability::MachineApplicable, } } @@ -157,7 +157,7 @@ impl<'tcx> Display for RetReplacement<'tcx> { Self::Empty => write!(f, ""), Self::Block => write!(f, "{{}}"), Self::Unit => write!(f, "()"), - Self::IfSequence(inner, _) => write!(f, "({inner})"), + Self::NeedsPar(inner, _) => write!(f, "({inner})"), Self::Expr(inner, _) => write!(f, "{inner}"), } } @@ -244,7 +244,11 @@ impl<'tcx> LateLintPass<'tcx> for Return { err.span_label(local.span, "unnecessary `let` binding"); if let Some(mut snippet) = snippet_opt(cx, initexpr.span) { - if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { + if binary_expr_needs_parentheses(initexpr) { + if !has_enclosing_paren(&snippet) { + snippet = format!("({snippet})"); + } + } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { if !has_enclosing_paren(&snippet) { snippet = format!("({snippet})"); } @@ -349,8 +353,8 @@ fn check_final_expr<'tcx>( let mut applicability = Applicability::MachineApplicable; let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability); - if expr_contains_conjunctive_ifs(inner_expr) { - RetReplacement::IfSequence(snippet, applicability) + if binary_expr_needs_parentheses(inner_expr) { + RetReplacement::NeedsPar(snippet, applicability) } else { RetReplacement::Expr(snippet, applicability) } @@ -404,18 +408,6 @@ fn check_final_expr<'tcx>( } } -fn expr_contains_conjunctive_ifs<'tcx>(expr: &'tcx Expr<'tcx>) -> bool { - fn contains_if(expr: &Expr<'_>, on_if: bool) -> bool { - match expr.kind { - ExprKind::If(..) => on_if, - ExprKind::Binary(_, left, right) => contains_if(left, true) || contains_if(right, true), - _ => false, - } - } - - contains_if(expr, false) -} - fn emit_return_lint( cx: &LateContext<'_>, ret_span: Span, @@ -444,7 +436,7 @@ fn emit_return_lint( } fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - for_each_expr_with_closures(cx, expr, |e| { + for_each_expr(cx, expr, |e| { if let Some(def_id) = fn_def_id(cx, e) && cx .tcx diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs index 7e27f70bcf94..8fdd19c549f5 100644 --- a/src/tools/clippy/clippy_lints/src/same_name_method.rs +++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs @@ -14,7 +14,7 @@ declare_clippy_lint! { /// It lints if a struct has two methods with the same name: /// one from a trait, another not from trait. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Confusing. /// /// ### Example diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs index 0b3adfb7a4b8..0e77acdfd77f 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs @@ -11,8 +11,7 @@ declare_clippy_lint! { /// Suggests moving the semicolon after a block to the inside of the block, after its last /// expression. /// - /// ### Why is this bad? - /// + /// ### Why restrict this? /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine /// and this lint suggests inside the block. /// Take a look at `semicolon_outside_block` for the other alternative. @@ -40,8 +39,7 @@ declare_clippy_lint! { /// /// Suggests moving the semicolon from a block's final expression outside of the block. /// - /// ### Why is this bad? - /// + /// ### Why restrict this? /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine /// and this lint suggests outside the block. /// Take a look at `semicolon_inside_block` for the other alternative. diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index 9db08acb03b2..80f5fd0b4944 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -15,10 +15,10 @@ declare_clippy_lint! { /// Checks for bindings that shadow other bindings already in /// scope, while just changing reference level or mutability. /// - /// ### Why is this bad? - /// Not much, in fact it's a very common pattern in Rust - /// code. Still, some may opt to avoid it in their code base, they can set this - /// lint to `Warn`. + /// ### Why restrict this? + /// To require that what are formally distinct variables be given distinct names. + /// + /// See also `shadow_reuse` and `shadow_unrelated` for other restrictions on shadowing. /// /// ### Example /// ```no_run @@ -42,12 +42,13 @@ declare_clippy_lint! { /// Checks for bindings that shadow other bindings already in /// scope, while reusing the original value. /// - /// ### Why is this bad? - /// Not too much, in fact it's a common pattern in Rust - /// code. Still, some argue that name shadowing like this hurts readability, + /// ### Why restrict this? + /// Some argue that name shadowing like this hurts readability, /// because a value may be bound to different things depending on position in /// the code. /// + /// See also `shadow_same` and `shadow_unrelated` for other restrictions on shadowing. + /// /// ### Example /// ```no_run /// let x = 2; @@ -70,11 +71,14 @@ declare_clippy_lint! { /// scope, either without an initialization or with one that does not even use /// the original value. /// - /// ### Why is this bad? - /// Name shadowing can hurt readability, especially in + /// ### Why restrict this? + /// Shadowing a binding with a closely related one is part of idiomatic Rust, + /// but shadowing a binding by accident with an unrelated one may indicate a mistake. + /// + /// Additionally, name shadowing in general can hurt readability, especially in /// large code bases, because it is easy to lose track of the active binding at - /// any place in the code. This can be alleviated by either giving more specific - /// names to bindings or introducing more scopes to contain the bindings. + /// any place in the code. If linting against all shadowing is desired, you may wish + /// to use the `shadow_same` and `shadow_reuse` lints as well. /// /// ### Example /// ```no_run diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index 038eb92d652b..979d6dc77aed 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{indent_of, snippet}; use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_expr, Visitor}; @@ -12,6 +12,7 @@ use rustc_session::impl_lint_pass; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, DUMMY_SP}; use std::borrow::Cow; +use std::collections::hash_map::Entry; declare_clippy_lint! { /// ### What it does @@ -57,7 +58,6 @@ impl_lint_pass!(SignificantDropTightening<'_> => [SIGNIFICANT_DROP_TIGHTENING]); pub struct SignificantDropTightening<'tcx> { apas: FxIndexMap, /// Auxiliary structure used to avoid having to verify the same type multiple times. - seen_types: FxHashSet>, type_cache: FxHashMap, bool>, } @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { self.apas.clear(); let initial_dummy_stmt = dummy_stmt_expr(body.value); let mut ap = AuxParams::new(&mut self.apas, &initial_dummy_stmt); - StmtsChecker::new(&mut ap, cx, &mut self.seen_types, &mut self.type_cache).visit_body(body); + StmtsChecker::new(&mut ap, cx, &mut self.type_cache).visit_body(body); for apa in ap.apas.values() { if apa.counter <= 1 || !apa.has_expensive_expr_after_last_attr { continue; @@ -142,28 +142,25 @@ impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { /// Checks the existence of the `#[has_significant_drop]` attribute. struct AttrChecker<'cx, 'others, 'tcx> { cx: &'cx LateContext<'tcx>, - seen_types: &'others mut FxHashSet>, type_cache: &'others mut FxHashMap, bool>, } impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { - pub(crate) fn new( - cx: &'cx LateContext<'tcx>, - seen_types: &'others mut FxHashSet>, - type_cache: &'others mut FxHashMap, bool>, - ) -> Self { - seen_types.clear(); - Self { - cx, - seen_types, - type_cache, - } + pub(crate) fn new(cx: &'cx LateContext<'tcx>, type_cache: &'others mut FxHashMap, bool>) -> Self { + Self { cx, type_cache } } fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool { - // The borrow checker prevents us from using something fancier like or_insert_with. - if let Some(ty) = self.type_cache.get(&ty) { - return *ty; + let ty = self + .cx + .tcx + .try_normalize_erasing_regions(self.cx.param_env, ty) + .unwrap_or(ty); + match self.type_cache.entry(ty) { + Entry::Occupied(e) => return *e.get(), + Entry::Vacant(e) => { + e.insert(false); + }, } let value = self.has_sig_drop_attr_uncached(ty); self.type_cache.insert(ty, value); @@ -185,7 +182,7 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { rustc_middle::ty::Adt(a, b) => { for f in a.all_fields() { let ty = f.ty(self.cx.tcx, b); - if !self.has_seen_ty(ty) && self.has_sig_drop_attr(ty) { + if self.has_sig_drop_attr(ty) { return true; } } @@ -205,16 +202,11 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { _ => false, } } - - fn has_seen_ty(&mut self, ty: Ty<'tcx>) -> bool { - !self.seen_types.insert(ty) - } } struct StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx> { ap: &'ap mut AuxParams<'others, 'stmt, 'tcx>, cx: &'lc LateContext<'tcx>, - seen_types: &'others mut FxHashSet>, type_cache: &'others mut FxHashMap, bool>, } @@ -222,15 +214,9 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx fn new( ap: &'ap mut AuxParams<'others, 'stmt, 'tcx>, cx: &'lc LateContext<'tcx>, - seen_types: &'others mut FxHashSet>, type_cache: &'others mut FxHashMap, bool>, ) -> Self { - Self { - ap, - cx, - seen_types, - type_cache, - } + Self { ap, cx, type_cache } } fn manage_has_expensive_expr_after_last_attr(&mut self) { @@ -288,7 +274,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'o apa.counter = apa.counter.wrapping_add(1); apa.has_expensive_expr_after_last_attr = false; }; - let mut ac = AttrChecker::new(self.cx, self.seen_types, self.type_cache); + let mut ac = AttrChecker::new(self.cx, self.type_cache); if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr)) { if let hir::StmtKind::Let(local) = self.ap.curr_stmt.kind && let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind diff --git a/src/tools/clippy/clippy_lints/src/single_call_fn.rs b/src/tools/clippy/clippy_lints/src/single_call_fn.rs index 2ce7e714c642..23bbeae0e31d 100644 --- a/src/tools/clippy/clippy_lints/src/single_call_fn.rs +++ b/src/tools/clippy/clippy_lints/src/single_call_fn.rs @@ -13,13 +13,20 @@ declare_clippy_lint! { /// ### What it does /// Checks for functions that are only used once. Does not lint tests. /// - /// ### Why is this bad? - /// It's usually not, splitting a function into multiple parts often improves readability and in - /// the case of generics, can prevent the compiler from duplicating the function dozens of - /// time; instead, only duplicating a thunk. But this can prevent segmentation across a - /// codebase, where many small functions are used only once. + /// ### Why restrict this? + /// If a function is only used once (perhaps because it used to be used more widely), + /// then the code could be simplified by moving that function's code into its caller. /// - /// Note: If this lint is used, prepare to allow this a lot. + /// However, there are reasons not to do this everywhere: + /// + /// * Splitting a large function into multiple parts often improves readability + /// by giving names to its parts. + /// * A function’s signature might serve a necessary purpose, such as constraining + /// the type of a closure passed to it. + /// * Generic functions might call non-generic functions to reduce duplication + /// in the produced machine code. + /// + /// If this lint is used, prepare to `#[allow]` it a lot. /// /// ### Example /// ```no_run @@ -79,7 +86,6 @@ impl SingleCallFn { .tcx .hir() .maybe_body_owned_by(fn_def_id) - .map(|body| cx.tcx.hir().body(body)) .map_or(true, |body| is_in_test_function(cx.tcx, body.value.hir_id)) || match cx.tcx.hir_node(fn_hir_id) { Node::Item(item) => is_from_proc_macro(cx, item), diff --git a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs index 42f1564db353..72feb977c310 100644 --- a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs +++ b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs @@ -9,11 +9,10 @@ declare_clippy_lint! { /// Checks for lifetimes with names which are one character /// long. /// - /// ### Why is this bad? + /// ### Why restrict this? /// A single character is likely not enough to express the /// purpose of a lifetime. Using a longer name can make code - /// easier to understand, especially for those who are new to - /// Rust. + /// easier to understand. /// /// ### Known problems /// Rust programmers and learning resources tend to use single diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index 926c56332cc1..12b70075a3d5 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -12,11 +12,9 @@ use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does - /// /// Finds items imported through `std` when available through `core`. /// - /// ### Why is this bad? - /// + /// ### Why restrict this? /// Crates which have `no_std` compatibility may wish to ensure types are imported from core to ensure /// disabling `std` does not cause the crate to fail to compile. This lint is also useful for crates /// migrating to become `no_std` compatible. @@ -37,11 +35,9 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// /// Finds items imported through `std` when available through `alloc`. /// - /// ### Why is this bad? - /// + /// ### Why restrict this? /// Crates which have `no_std` compatibility and require alloc may wish to ensure types are imported from /// alloc to ensure disabling `std` does not cause the crate to fail to compile. This lint is also useful /// for crates migrating to become `no_std` compatible. @@ -63,11 +59,9 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// /// Finds items imported through `alloc` when available through `core`. /// - /// ### Why is this bad? - /// + /// ### Why restrict this? /// Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are /// imported from core to ensure disabling `alloc` does not cause the crate to fail to compile. This lint /// is also useful for crates migrating to become `no_std` compatible. diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs new file mode 100644 index 000000000000..64b5b8f9f27b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs @@ -0,0 +1,227 @@ +use std::ops::ControlFlow; + +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::eager_or_lazy::switch_to_eager_eval; +use clippy_utils::macros::matching_root_macro_call; +use clippy_utils::path_to_local_id; +use clippy_utils::source::{snippet, str_literal_to_char_literal}; +use clippy_utils::visitors::{for_each_expr, Descend}; +use itertools::Itertools; +use rustc_ast::{BinOpKind, LitKind}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::declare_lint_pass; +use rustc_span::{sym, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for manual `char` comparison in string patterns + /// + /// ### Why is this bad? + /// This can be written more concisely using a `char` or an array of `char`. + /// This is more readable and more optimized when comparing to only one `char`. + /// + /// ### Example + /// ```no_run + /// "Hello World!".trim_end_matches(|c| c == '.' || c == ',' || c == '!' || c == '?'); + /// ``` + /// Use instead: + /// ```no_run + /// "Hello World!".trim_end_matches(['.', ',', '!', '?']); + /// ``` + #[clippy::version = "1.80.0"] + pub MANUAL_PATTERN_CHAR_COMPARISON, + style, + "manual char comparison in string patterns" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for string methods that receive a single-character + /// `str` as an argument, e.g., `_.split("x")`. + /// + /// ### Why is this bad? + /// While this can make a perf difference on some systems, + /// benchmarks have proven inconclusive. But at least using a + /// char literal makes it clear that we are looking at a single + /// character. + /// + /// ### Known problems + /// Does not catch multi-byte unicode characters. This is by + /// design, on many machines, splitting by a non-ascii char is + /// actually slower. Please do your own measurements instead of + /// relying solely on the results of this lint. + /// + /// ### Example + /// ```rust,ignore + /// _.split("x"); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// _.split('x'); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub SINGLE_CHAR_PATTERN, + pedantic, + "using a single-character str where a char could be used, e.g., `_.split(\"x\")`" +} + +declare_lint_pass!(StringPatterns => [MANUAL_PATTERN_CHAR_COMPARISON, SINGLE_CHAR_PATTERN]); + +const PATTERN_METHODS: [(&str, usize); 22] = [ + ("contains", 0), + ("starts_with", 0), + ("ends_with", 0), + ("find", 0), + ("rfind", 0), + ("split", 0), + ("split_inclusive", 0), + ("rsplit", 0), + ("split_terminator", 0), + ("rsplit_terminator", 0), + ("splitn", 1), + ("rsplitn", 1), + ("split_once", 0), + ("rsplit_once", 0), + ("matches", 0), + ("rmatches", 0), + ("match_indices", 0), + ("rmatch_indices", 0), + ("trim_start_matches", 0), + ("trim_end_matches", 0), + ("replace", 0), + ("replacen", 0), +]; + +fn check_single_char_pattern_lint(cx: &LateContext<'_>, arg: &Expr<'_>) { + let mut applicability = Applicability::MachineApplicable; + if let Some(hint) = str_literal_to_char_literal(cx, arg, &mut applicability, true) { + span_lint_and_sugg( + cx, + SINGLE_CHAR_PATTERN, + arg.span, + "single-character string constant used as pattern", + "consider using a `char`", + hint, + applicability, + ); + } +} + +fn get_char_span<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { + if cx.typeck_results().expr_ty_adjusted(expr).is_char() + && !expr.span.from_expansion() + && switch_to_eager_eval(cx, expr) + { + Some(expr.span) + } else { + None + } +} + +fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr<'_>) { + if let ExprKind::Closure(closure) = method_arg.kind + && let body = cx.tcx.hir().body(closure.body) + && let Some(PatKind::Binding(_, binding, ..)) = body.params.first().map(|p| p.pat.kind) + { + let mut set_char_spans: Vec = Vec::new(); + + // We want to retrieve all the comparisons done. + // They are ordered in a nested way and so we need to traverse the AST to collect them all. + if for_each_expr(cx, body.value, |sub_expr| -> ControlFlow<(), Descend> { + match sub_expr.kind { + ExprKind::Binary(op, left, right) if op.node == BinOpKind::Eq => { + if path_to_local_id(left, binding) + && let Some(span) = get_char_span(cx, right) + { + set_char_spans.push(span); + ControlFlow::Continue(Descend::No) + } else if path_to_local_id(right, binding) + && let Some(span) = get_char_span(cx, left) + { + set_char_spans.push(span); + ControlFlow::Continue(Descend::No) + } else { + ControlFlow::Break(()) + } + }, + ExprKind::Binary(op, _, _) if op.node == BinOpKind::Or => ControlFlow::Continue(Descend::Yes), + ExprKind::Match(match_value, [arm, _], _) => { + if matching_root_macro_call(cx, sub_expr.span, sym::matches_macro).is_none() + || arm.guard.is_some() + || !path_to_local_id(match_value, binding) + { + return ControlFlow::Break(()); + } + if arm.pat.walk_short(|pat| match pat.kind { + PatKind::Lit(expr) if let ExprKind::Lit(lit) = expr.kind => { + if let LitKind::Char(_) = lit.node { + set_char_spans.push(lit.span); + } + true + }, + PatKind::Or(_) => true, + _ => false, + }) { + ControlFlow::Continue(Descend::No) + } else { + ControlFlow::Break(()) + } + }, + _ => ControlFlow::Break(()), + } + }) + .is_some() + { + return; + } + span_lint_and_then( + cx, + MANUAL_PATTERN_CHAR_COMPARISON, + method_arg.span, + "this manual char comparison can be written more succinctly", + |diag| { + if let [set_char_span] = set_char_spans[..] { + diag.span_suggestion( + method_arg.span, + "consider using a `char`", + snippet(cx, set_char_span, "c"), + Applicability::MachineApplicable, + ); + } else { + diag.span_suggestion( + method_arg.span, + "consider using an array of `char`", + format!( + "[{}]", + set_char_spans.into_iter().map(|span| snippet(cx, span, "c")).join(", ") + ), + Applicability::MachineApplicable, + ); + } + }, + ); + } +} + +impl<'tcx> LateLintPass<'tcx> for StringPatterns { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !expr.span.from_expansion() + && let ExprKind::MethodCall(method, receiver, args, _) = expr.kind + && let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() + && ty.is_str() + && let method_name = method.ident.name.as_str() + && let Some(&(_, pos)) = PATTERN_METHODS + .iter() + .find(|(array_method_name, _)| *array_method_name == method_name) + && let Some(arg) = args.get(pos) + { + check_single_char_pattern_lint(cx, arg); + + check_manual_pattern_char_comparison(cx, arg); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 87a3c3874d77..7da661485abf 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -45,10 +45,10 @@ declare_clippy_lint! { /// `String`, but only if [`string_add_assign`](#string_add_assign) does *not* /// match. /// - /// ### Why is this bad? - /// It's not bad in and of itself. However, this particular + /// ### Why restrict this? + /// This particular /// `Add` implementation is asymmetric (the other operand need not be `String`, - /// but `x` does), while addition as mathematically defined is symmetric, also + /// but `x` does), while addition as mathematically defined is symmetric, and /// the `String::push_str(_)` function is a perfectly good replacement. /// Therefore, some dislike it and wish not to have it in their code. /// @@ -123,7 +123,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for slice operations on strings /// - /// ### Why is this bad? + /// ### Why restrict this? /// UTF-8 characters span multiple bytes, and it is easy to inadvertently confuse character /// counts and string indices. This may lead to panics, and should warrant some test cases /// containing wide UTF-8 characters. This lint is most useful in code that should avoid @@ -364,10 +364,10 @@ declare_clippy_lint! { /// ### What it does /// This lint checks for `.to_string()` method calls on values of type `&str`. /// - /// ### Why is this bad? + /// ### Why restrict this? /// The `to_string` method is also used on other types to convert them to a string. - /// When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better - /// expressed with `.to_owned()`. + /// When called on a `&str` it turns the `&str` into the owned variant `String`, which can be + /// more specifically expressed with `.to_owned()`. /// /// ### Example /// ```no_run @@ -389,19 +389,27 @@ declare_lint_pass!(StrToString => [STR_TO_STRING]); impl<'tcx> LateLintPass<'tcx> for StrToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if expr.span.from_expansion() { + return; + } + if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind && path.ident.name == sym::to_string && let ty = cx.typeck_results().expr_ty(self_arg) && let ty::Ref(_, ty, ..) = ty.kind() && ty.is_str() { - span_lint_and_help( + let mut applicability = Applicability::MachineApplicable; + let snippet = snippet_with_applicability(cx, self_arg.span, "..", &mut applicability); + + span_lint_and_sugg( cx, STR_TO_STRING, expr.span, "`to_string()` called on a `&str`", - None, - "consider using `.to_owned()`", + "try", + format!("{snippet}.to_owned()"), + applicability, ); } } @@ -411,9 +419,10 @@ declare_clippy_lint! { /// ### What it does /// This lint checks for `.to_string()` method calls on values of type `String`. /// - /// ### Why is this bad? + /// ### Why restrict this? /// The `to_string` method is also used on other types to convert them to a string. - /// When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`. + /// When called on a `String` it only clones the `String`, which can be more specifically + /// expressed with `.clone()`. /// /// ### Example /// ```no_run @@ -437,6 +446,10 @@ declare_lint_pass!(StringToString => [STRING_TO_STRING]); impl<'tcx> LateLintPass<'tcx> for StringToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if expr.span.from_expansion() { + return; + } + if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind && path.ident.name == sym::to_string && let ty = cx.typeck_results().expr_ty(self_arg) diff --git a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs index 3f030b803318..744d6392e065 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS}; use core::ops::ControlFlow; use rustc_hir as hir; @@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { fn count_binops(expr: &hir::Expr<'_>) -> u32 { let mut count = 0u32; - let _: Option = for_each_expr(expr, |e| { + let _: Option = for_each_expr_without_closures(expr, |e| { if matches!( e.kind, hir::ExprKind::Binary(..) diff --git a/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs b/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs index 1cc27670fa8b..d150a5f858aa 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs @@ -11,8 +11,10 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does /// Warns for a Bitwise XOR (`^`) operator being probably confused as a powering. It will not trigger if any of the numbers are not in decimal. - /// ### Why is this bad? + /// + /// ### Why restrict this? /// It's most probably a typo and may lead to unexpected behaviours. + /// /// ### Example /// ```no_run /// let x = 3_i32 ^ 4_i32; diff --git a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs index da5575826475..58e42892c41d 100644 --- a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs +++ b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs @@ -11,9 +11,11 @@ declare_clippy_lint! { /// ### What it does /// Triggers when a testing function (marked with the `#[test]` attribute) isn't inside a testing module /// (marked with `#[cfg(test)]`). - /// ### Why is this bad? + /// + /// ### Why restrict this? /// The idiomatic (and more performant) way of writing tests is inside a testing module (flagged with `#[cfg(test)]`), /// having test functions outside of this module is confusing and may lead to them being "hidden". + /// /// ### Example /// ```no_run /// #[test] diff --git a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs index cc6ff1cf3b42..b2892d136fa3 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs @@ -30,8 +30,7 @@ fn get_parent_local_binding_ty<'tcx>(cx: &LateContext<'tcx>, expr_hir_id: HirId) fn is_function_block(cx: &LateContext<'_>, expr_hir_id: HirId) -> bool { let def_id = cx.tcx.hir().enclosing_body_owner(expr_hir_id); - if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(def_id) { - let body = cx.tcx.hir().body(body_id); + if let Some(body) = cx.tcx.hir().maybe_body_owned_by(def_id) { return body.value.peel_blocks().hir_id == expr_hir_id; } false diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index 598032ccdebe..aa329ec33668 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -546,7 +546,7 @@ declare_clippy_lint! { /// let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); /// # } /// ``` - #[clippy::version = "1.77.0"] + #[clippy::version = "1.79.0"] pub MISSING_TRANSMUTE_ANNOTATIONS, suspicious, "warns if a transmute call doesn't have all generics specified" diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs index 7d824ef21390..3729dfd3e86f 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -28,7 +28,7 @@ pub(super) fn check<'tcx>( let int_ty = substs.type_at(0); if from_ty != int_ty { - return false; + return false; } span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs index 9c8dd37d53dc..3b32e4396b9f 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -278,7 +278,7 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> ty = sized_ty; continue; } - if def.repr().inhibit_struct_field_reordering_opt() { + if def.repr().inhibit_struct_field_reordering() { ReducedTy::OrderedFields(Some(sized_ty)) } else { ReducedTy::UnorderedFields(ty) diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 5e45ab211efd..62ef65ca122f 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -217,7 +217,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for `Rc` and `Arc` when `T` is a mutable buffer type such as `String` or `Vec`. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Expressions such as `Rc` usually have no advantage over `Rc`, since /// it is larger and involves an extra level of indirection, and doesn't implement `Borrow`. /// @@ -274,7 +274,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for `Rc>`. /// - /// ### Why is this bad? + /// ### Why restrict this? /// `Rc` is used in single thread and `Mutex` is used in multi thread. /// Consider using `Rc>` in single thread or `Arc>` in multi thread. /// diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index 0c4e2c91aec5..5e41b3f4914f 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -336,7 +336,7 @@ impl UnconditionalRecursion { // We need to use typeck here to infer the actual function being called. && let body_def_id = cx.tcx.hir().enclosing_body_owner(call_expr.hir_id) && let Some(body_owner) = cx.tcx.hir().maybe_body_owned_by(body_def_id) - && let typeck = cx.tcx.typeck_body(body_owner) + && let typeck = cx.tcx.typeck_body(body_owner.id()) && let Some(call_def_id) = typeck.type_dependent_def_id(call_expr.hir_id) { self.default_impl_for_type.insert(self_def_id, call_def_id); diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 4120bb1331bd..93a1089a9707 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; -use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; +use clippy_utils::visitors::{for_each_expr, Descend}; use hir::HirId; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; @@ -38,10 +38,9 @@ declare_clippy_lint! { /// ); /// ``` /// - /// ### Why is this bad? - /// Undocumented unsafe blocks and impls can make it difficult to - /// read and maintain code, as well as uncover unsoundness - /// and bugs. + /// ### Why restrict this? + /// Undocumented unsafe blocks and impls can make it difficult to read and maintain code. + /// Writing out the safety justification may help in discovering unsoundness or bugs. /// /// ### Example /// ```no_run @@ -67,7 +66,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for `// SAFETY: ` comments on safe code. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Safe code has no safety requirements, so there is no need to /// describe safety invariants. /// @@ -297,7 +296,7 @@ fn expr_has_unnecessary_safety_comment<'tcx>( } // this should roughly be the reverse of `block_parents_have_safety_comment` - if for_each_expr_with_closures(cx, expr, |expr| match expr.kind { + if for_each_expr(cx, expr, |expr| match expr.kind { hir::ExprKind::Block( Block { rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), diff --git a/src/tools/clippy/clippy_lints/src/unicode.rs b/src/tools/clippy/clippy_lints/src/unicode.rs index 3d319b9fe767..d42697b31d1f 100644 --- a/src/tools/clippy/clippy_lints/src/unicode.rs +++ b/src/tools/clippy/clippy_lints/src/unicode.rs @@ -31,7 +31,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for non-ASCII characters in string and char literals. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Yeah, we know, the 90's called and wanted their charset /// back. Even so, there still are editors and other programs out there that /// don't work well with Unicode. So if the code is meant to be used diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs index f0d1458a59b2..a8cc2f979633 100644 --- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs +++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs @@ -100,12 +100,12 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve { if ord_preds .iter() - .any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) + .any(|ord| Some(ord.self_ty()) == return_ty_pred.term.as_type()) { args_to_check.push((i, "Ord".to_string())); } else if partial_ord_preds .iter() - .any(|pord| pord.self_ty() == return_ty_pred.term.ty().unwrap()) + .any(|pord| pord.self_ty() == return_ty_pred.term.expect_type()) { args_to_check.push((i, "PartialOrd".to_string())); } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs index 528a1dfcfc10..93dff1b85796 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs @@ -9,7 +9,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for imports ending in `::{self}`. /// - /// ### Why is this bad? + /// ### Why restrict this? /// In most cases, this can be written much more cleanly by omitting `::{self}`. /// /// ### Known problems diff --git a/src/tools/clippy/clippy_lints/src/unused_self.rs b/src/tools/clippy/clippy_lints/src/unused_self.rs index a67f53f00aee..3e6102f5982f 100644 --- a/src/tools/clippy/clippy_lints/src/unused_self.rs +++ b/src/tools/clippy/clippy_lints/src/unused_self.rs @@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { let parent_item = cx.tcx.hir().expect_item(parent); let assoc_item = cx.tcx.associated_item(impl_item.owner_id); let contains_todo = |cx, body: &'_ Body<'_>| -> bool { - clippy_utils::visitors::for_each_expr(body.value, |e| { + clippy_utils::visitors::for_each_expr_without_closures(body.value, |e| { if let Some(macro_call) = root_macro_call_first_node(cx, e) { if cx.tcx.item_name(macro_call.def_id).as_str() == "todo" { ControlFlow::Break(()) diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index 5b2841dcd833..c0d9bcdd259c 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -251,11 +251,7 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { local_id: unwrap_info.local_id, }; - let vis = ExprUseVisitor::for_clippy( - self.cx, - cond.hir_id.owner.def_id, - &mut delegate, - ); + let vis = ExprUseVisitor::for_clippy(self.cx, cond.hir_id.owner.def_id, &mut delegate); vis.walk_expr(cond).into_ok(); vis.walk_expr(branch).into_ok(); diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs index aca500590cef..f77badd97b7a 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs @@ -13,8 +13,10 @@ declare_clippy_lint! { /// ### What it does /// Checks for functions of type `Result` that contain `expect()` or `unwrap()` /// - /// ### Why is this bad? - /// These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics. + /// ### Why restrict this? + /// These functions promote recoverable errors to non-recoverable errors, + /// which may be undesirable in code bases which wish to avoid panics, + /// or be a bug in the specific function. /// /// ### Known problems /// This can cause false positives in functions that handle both recoverable and non recoverable errors. @@ -75,7 +77,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc let body = cx.tcx.hir().body(body_id); let typeck = cx.tcx.typeck(impl_item.owner_id.def_id); let mut result = Vec::new(); - let _: Option = for_each_expr(body.value, |e| { + let _: Option = for_each_expr(cx, body.value, |e| { // check for `expect` if let Some(arglists) = method_chain_args(e, &["expect"]) { let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 4448c9ae3df7..e9d69407df8b 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -137,9 +137,9 @@ impl<'tcx> LateLintPass<'tcx> for Author { fn check_item(cx: &LateContext<'_>, hir_id: HirId) { let hir = cx.tcx.hir(); - if let Some(body_id) = hir.maybe_body_owned_by(hir_id.expect_owner().def_id) { + if let Some(body) = hir.maybe_body_owned_by(hir_id.expect_owner().def_id) { check_node(cx, hir_id, |v| { - v.expr(&v.bind("expr", hir.body(body_id).value)); + v.expr(&v.bind("expr", body.value)); }); } } @@ -733,7 +733,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { match stmt.value.kind { StmtKind::Let(local) => { bind!(self, local); - kind!("Local({local})"); + kind!("Let({local})"); self.option(field!(local.init), "init", |init| { self.expr(init); }); diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs index 58e66c9f9b95..5acfd35fd6ae 100644 --- a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs @@ -1,4 +1,4 @@ -use clippy_utils::macros::AST_FORMAT_ARGS; +use clippy_utils::macros::FormatArgsStorage; use clippy_utils::source::snippet_opt; use itertools::Itertools; use rustc_ast::{Crate, Expr, ExprKind, FormatArgs}; @@ -9,13 +9,20 @@ use rustc_session::impl_lint_pass; use rustc_span::{hygiene, Span}; use std::iter::once; use std::mem; -use std::rc::Rc; -/// Collects [`rustc_ast::FormatArgs`] so that future late passes can call -/// [`clippy_utils::macros::find_format_args`] -#[derive(Default)] +/// Populates [`FormatArgsStorage`] with AST [`FormatArgs`] nodes pub struct FormatArgsCollector { - format_args: FxHashMap>, + format_args: FxHashMap, + storage: FormatArgsStorage, +} + +impl FormatArgsCollector { + pub fn new(storage: FormatArgsStorage) -> Self { + Self { + format_args: FxHashMap::default(), + storage, + } + } } impl_lint_pass!(FormatArgsCollector => []); @@ -27,16 +34,12 @@ impl EarlyLintPass for FormatArgsCollector { return; } - self.format_args - .insert(expr.span.with_parent(None), Rc::new((**args).clone())); + self.format_args.insert(expr.span.with_parent(None), (**args).clone()); } } fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) { - AST_FORMAT_ARGS.with(|ast_format_args| { - let result = ast_format_args.set(mem::take(&mut self.format_args)); - debug_assert!(result.is_ok()); - }); + self.storage.set(mem::take(&mut self.format_args)); } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 9be225759df9..84f84781e712 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -213,7 +213,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { output: &mut self.registered_lints, cx, }; - let body_id = cx.tcx.hir().body_owned_by( + let body = cx.tcx.hir().body_owned_by( impl_item_refs .iter() .find(|iiref| iiref.ident.as_str() == "get_lints") @@ -222,7 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { .owner_id .def_id, ); - collector.visit_expr(cx.tcx.hir().body(body_id).value); + collector.visit_expr(body.value); } } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 5c1ebb922f12..1c149f204562 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -719,7 +719,7 @@ fn get_lint_group_and_level_or_lint( Some(sym::clippy), &std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(), ); - if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { + if let CheckLintNameResult::Tool(lint_lst, None) = result { if let Some(group) = get_lint_group(cx, lint_lst[0]) { if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) { return None; diff --git a/src/tools/clippy/clippy_lints/src/visibility.rs b/src/tools/clippy/clippy_lints/src/visibility.rs index 9818b98dd5b4..11dcceca7abb 100644 --- a/src/tools/clippy/clippy_lints/src/visibility.rs +++ b/src/tools/clippy/clippy_lints/src/visibility.rs @@ -32,7 +32,7 @@ declare_clippy_lint! { /// ### What it does /// Checks for usage of `pub()` with `in`. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Consistency. Use it or don't, just be consistent about it. /// /// Also see the `pub_without_shorthand` lint for an alternative. @@ -57,7 +57,7 @@ declare_clippy_lint! { /// Note: As you cannot write a module's path in `pub()`, this will only trigger on /// `pub(super)` and the like. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Consistency. Use it or don't, just be consistent about it. /// /// Also see the `pub_with_shorthand` lint for an alternative. diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 26c6859233d5..652ce88bd951 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro_call_first_node, MacroCall}; +use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall}; use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_ast::token::LitKind; @@ -66,7 +66,7 @@ declare_clippy_lint! { /// Checks for printing on *stdout*. The purpose of this lint /// is to catch debugging remnants. /// - /// ### Why is this bad? + /// ### Why restrict this? /// People often print on *stdout* while debugging an /// application and might forget to remove those prints afterward. /// @@ -88,7 +88,7 @@ declare_clippy_lint! { /// Checks for printing on *stderr*. The purpose of this lint /// is to catch debugging remnants. /// - /// ### Why is this bad? + /// ### Why restrict this? /// People often print on *stderr* while debugging an /// application and might forget to remove those prints afterward. /// @@ -110,15 +110,18 @@ declare_clippy_lint! { /// Checks for usage of `Debug` formatting. The purpose of this /// lint is to catch debugging remnants. /// - /// ### Why is this bad? - /// The purpose of the `Debug` trait is to facilitate - /// debugging Rust code. It should not be used in user-facing output. + /// ### Why restrict this? + /// The purpose of the `Debug` trait is to facilitate debugging Rust code, + /// and [no guarantees are made about its output][stability]. + /// It should not be used in user-facing output. /// /// ### Example /// ```no_run /// # let foo = "bar"; /// println!("{:?}", foo); /// ``` + /// + /// [stability]: https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html#stability #[clippy::version = "pre 1.29.0"] pub USE_DEBUG, restriction, @@ -236,13 +239,15 @@ declare_clippy_lint! { #[derive(Default)] pub struct Write { + format_args: FormatArgsStorage, in_debug_impl: bool, allow_print_in_tests: bool, } impl Write { - pub fn new(allow_print_in_tests: bool) -> Self { + pub fn new(format_args: FormatArgsStorage, allow_print_in_tests: bool) -> Self { Self { + format_args, allow_print_in_tests, ..Default::default() } @@ -307,7 +312,7 @@ impl<'tcx> LateLintPass<'tcx> for Write { _ => return, } - if let Some(format_args) = find_format_args(cx, expr, macro_call.expn) { + if let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) { // ignore `writeln!(w)` and `write!(v, some_macro!())` if format_args.span.from_expansion() { return; @@ -315,15 +320,15 @@ impl<'tcx> LateLintPass<'tcx> for Write { match diag_name { sym::print_macro | sym::eprint_macro | sym::write_macro => { - check_newline(cx, &format_args, ¯o_call, name); + check_newline(cx, format_args, ¯o_call, name); }, sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { - check_empty_string(cx, &format_args, ¯o_call, name); + check_empty_string(cx, format_args, ¯o_call, name); }, _ => {}, } - check_literal(cx, &format_args, name); + check_literal(cx, format_args, name); if !self.in_debug_impl { for piece in &format_args.template { diff --git a/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs index 143fecdd237d..ad041e55bdaf 100644 --- a/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::for_each_expr_without_closures; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{ExprKind, Node}; @@ -36,7 +36,7 @@ declare_clippy_lint! { /// side_effect(); /// let a: [i32; 0] = []; /// ``` - #[clippy::version = "1.75.0"] + #[clippy::version = "1.79.0"] pub ZERO_REPEAT_SIDE_EFFECTS, suspicious, "usage of zero-sized initializations of arrays or vecs causing side effects" @@ -55,9 +55,8 @@ impl LateLintPass<'_> for ZeroRepeatSideEffects { inner_check(cx, expr, inner_expr, true); } else if let ExprKind::Repeat(inner_expr, _) = expr.kind && let ty::Array(_, cst) = cx.typeck_results().expr_ty(expr).kind() - && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind() - && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx) - && element_count == 0 + && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind() + && element_count.to_target_usize(cx.tcx) == 0 { inner_check(cx, expr, inner_expr, false); } @@ -66,7 +65,7 @@ impl LateLintPass<'_> for ZeroRepeatSideEffects { fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: &'_ rustc_hir::Expr<'_>, is_vec: bool) { // check if expr is a call or has a call inside it - if for_each_expr(inner_expr, |x| { + if for_each_expr_without_closures(inner_expr, |x| { if let ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _, _) = x.kind { std::ops::ControlFlow::Break(()) } else { diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index ab883c25338b..3a3aeb882164 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.80" +version = "0.1.81" edition = "2021" publish = false diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index d4a5f547211a..fb43f7d80aff 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -108,7 +108,7 @@ pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool { pub fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool { match (l, r) { (AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r), - (AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_constraint(l, r), + (AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_item_constraint(l, r), _ => false, } } @@ -308,13 +308,15 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { ty: lt, mutability: lm, expr: le, + safety: ls, }), Static(box StaticItem { ty: rt, mutability: rm, expr: re, + safety: rs, }), - ) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re), + ) => lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le, re), ( Const(box ConstItem { defaultness: ld, @@ -451,13 +453,15 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { ty: lt, mutability: lm, expr: le, + safety: ls, }), Static(box StaticForeignItem { ty: rt, mutability: rm, expr: re, + safety: rs, }), - ) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re), + ) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re) && ls == rs, ( Fn(box ast::Fn { defaultness: ld, @@ -720,11 +724,8 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool { (Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)), (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp), (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, eq_generic_bound), - (ImplTrait(_, lg, lc), ImplTrait(_, rg, rc)) => { + (ImplTrait(_, lg), ImplTrait(_, rg)) => { over(lg, rg, eq_generic_bound) - && both(lc, rc, |lc, rc| { - over(lc.0.as_slice(), rc.0.as_slice(), eq_precise_capture) - }) }, (Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), @@ -802,8 +803,8 @@ fn eq_term(l: &Term, r: &Term) -> bool { } } -pub fn eq_assoc_constraint(l: &AssocConstraint, r: &AssocConstraint) -> bool { - use AssocConstraintKind::*; +pub fn eq_assoc_item_constraint(l: &AssocItemConstraint, r: &AssocItemConstraint) -> bool { + use AssocItemConstraintKind::*; eq_id(l.ident, r.ident) && match (&l.kind, &r.kind) { (Equality { term: l }, Equality { term: r }) => eq_term(l, r), diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 253ae3aca689..cfd142fe1ff6 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -812,14 +812,10 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> (ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), - ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))), - ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits( - int.try_into().expect("invalid f32 bit representation"), - ))), - ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits( - int.try_into().expect("invalid f64 bit representation"), - ))), - ty::RawPtr(_, _) => Some(Constant::RawPtr(int.assert_bits(int.size()))), + ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))), + ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(int.into()))), + ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(int.into()))), + ty::RawPtr(_, _) => Some(Constant::RawPtr(int.to_bits(int.size()))), _ => None, }, (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => { diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index dc0a139e3c78..0641d37cd9a6 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -61,7 +61,8 @@ fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) { /// ``` pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into, msg: impl Into) { #[expect(clippy::disallowed_methods)] - cx.span_lint(lint, sp, msg.into(), |diag| { + cx.span_lint(lint, sp, |diag| { + diag.primary_message(msg); docs_link(diag, lint); }); } @@ -109,7 +110,8 @@ pub fn span_lint_and_help( help: impl Into, ) { #[expect(clippy::disallowed_methods)] - cx.span_lint(lint, span, msg.into(), |diag| { + cx.span_lint(lint, span, |diag| { + diag.primary_message(msg); if let Some(help_span) = help_span { diag.span_help(help_span, help.into()); } else { @@ -165,7 +167,8 @@ pub fn span_lint_and_note( note: impl Into, ) { #[expect(clippy::disallowed_methods)] - cx.span_lint(lint, span, msg.into(), |diag| { + cx.span_lint(lint, span, |diag| { + diag.primary_message(msg); if let Some(note_span) = note_span { diag.span_note(note_span, note.into()); } else { @@ -201,7 +204,8 @@ where F: FnOnce(&mut Diag<'_, ()>), { #[expect(clippy::disallowed_methods)] - cx.span_lint(lint, sp, msg, |diag| { + cx.span_lint(lint, sp, |diag| { + diag.primary_message(msg); f(diag); docs_link(diag, lint); }); @@ -233,7 +237,8 @@ where /// the `#[allow]` will work. pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: impl Into) { #[expect(clippy::disallowed_methods)] - cx.tcx.node_span_lint(lint, hir_id, sp, msg.into(), |diag| { + cx.tcx.node_span_lint(lint, hir_id, sp, |diag| { + diag.primary_message(msg); docs_link(diag, lint); }); } @@ -271,7 +276,8 @@ pub fn span_lint_hir_and_then( f: impl FnOnce(&mut Diag<'_, ()>), ) { #[expect(clippy::disallowed_methods)] - cx.tcx.node_span_lint(lint, hir_id, sp, msg.into(), |diag| { + cx.tcx.node_span_lint(lint, hir_id, sp, |diag| { + diag.primary_message(msg); f(diag); docs_link(diag, lint); }); diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 9f285621e0c9..50dd8430ac06 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -7,9 +7,9 @@ use rustc_data_structures::fx::FxHasher; use rustc_hir::def::Res; use rustc_hir::MatchSource::TryDesugar; use rustc_hir::{ - ArrayLen, BinOpKind, BindingMode, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg, - GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, Pat, PatField, PatKind, Path, - PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, + ArrayLen, AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, + GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, Pat, PatField, + PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::LateContext; @@ -486,7 +486,7 @@ impl HirEqInterExpr<'_, '_, '_> { fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool { if left.parenthesized == right.parenthesized { over(left.args, right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work - && over(left.bindings, right.bindings, |l, r| self.eq_type_binding(l, r)) + && over(left.constraints, right.constraints, |l, r| self.eq_assoc_type_binding(l, r)) } else { false } @@ -518,8 +518,12 @@ impl HirEqInterExpr<'_, '_, '_> { } } - fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool { - left.ident.name == right.ident.name && self.eq_ty(left.ty(), right.ty()) + fn eq_assoc_type_binding(&mut self, left: &AssocItemConstraint<'_>, right: &AssocItemConstraint<'_>) -> bool { + left.ident.name == right.ident.name + && self.eq_ty( + left.ty().expect("expected assoc type binding"), + right.ty().expect("expected assoc type binding"), + ) } fn check_ctxt(&mut self, left: SyntaxContext, right: SyntaxContext) -> bool { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 99d7aba2f7a1..7dc341ec8d71 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -126,7 +126,7 @@ use visitors::Visitable; use crate::consts::{constant, mir_to_const, Constant}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; -use crate::visitors::for_each_expr; +use crate::visitors::for_each_expr_without_closures; use rustc_middle::hir::nested_filter; @@ -193,6 +193,21 @@ pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option< None } +/// Checks if the given local has an initializer or is from something other than a `let` statement +/// +/// e.g. returns true for `x` in `fn f(x: usize) { .. }` and `let x = 1;` but false for `let x;` +pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool { + for (_, node) in cx.tcx.hir().parent_iter(local) { + match node { + Node::Pat(..) | Node::PatField(..) => {}, + Node::LetStmt(let_stmt) => return let_stmt.init.is_some(), + _ => return true, + } + } + + false +} + /// Returns `true` if the given `NodeId` is inside a constant context /// /// # Example @@ -306,6 +321,15 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) .map_or(false, |trt_id| match_def_path(cx, trt_id, path)) } +/// Checks if the given method call expression calls an inherent method. +pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + cx.tcx.trait_of_item(method_id).is_none() + } else { + false + } +} + /// Checks if a method is defined in an impl of a diagnostic item pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { @@ -1298,7 +1322,7 @@ pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext< /// Returns `true` if `expr` contains a return expression pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool { - for_each_expr(expr, |e| { + for_each_expr_without_closures(expr, |e| { if matches!(e.kind, ExprKind::Ret(..)) { ControlFlow::Break(()) } else { @@ -1499,15 +1523,18 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { } /// Checks whether the given `Expr` is a range equivalent to a `RangeFull`. +/// /// For the lower bound, this means that: /// - either there is none /// - or it is the smallest value that can be represented by the range's integer type +/// /// For the upper bound, this means that: /// - either there is none /// - or it is the largest value that can be represented by the range's integer type and is /// inclusive /// - or it is a call to some container's `len` method and is exclusive, and the range is passed to /// a method call on that same container (e.g. `v.drain(..v.len())`) +/// /// If the given `Expr` is not some kind of range, the function returns `false`. pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool { let ty = cx.typeck_results().expr_ty(expr); @@ -1516,7 +1543,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx) - && let Some(min_const) = mir_to_const(cx, Const::from_ty_const(min_val, cx.tcx)) + && let Some(min_const) = mir_to_const(cx, Const::from_ty_const(min_val, bnd_ty, cx.tcx)) && let Some(start_const) = constant(cx, cx.typeck_results(), start) { start_const == min_const @@ -1529,7 +1556,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx) - && let Some(max_const) = mir_to_const(cx, Const::from_ty_const(max_val, cx.tcx)) + && let Some(max_const) = mir_to_const(cx, Const::from_ty_const(max_val, bnd_ty, cx.tcx)) && let Some(end_const) = constant(cx, cx.typeck_results(), end) { end_const == max_const @@ -3352,3 +3379,36 @@ pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool { Node::Stmt(..) | Node::Block(Block { stmts: &[], .. }) ) } + +/// Returns true if the given `expr` is a block or resembled as a block, +/// such as `if`, `loop`, `match` expressions etc. +pub fn is_block_like(expr: &Expr<'_>) -> bool { + matches!( + expr.kind, + ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..) + ) +} + +/// Returns true if the given `expr` is binary expression that needs to be wrapped in parentheses. +pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool { + fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool { + match expr.kind { + ExprKind::Binary(_, lhs, _) => contains_block(lhs, true), + _ if is_block_like(expr) => is_operand, + _ => false, + } + } + + contains_block(expr, false) +} + +/// Returns true if the specified expression is in a receiver position. +pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(parent_expr) = get_parent_expr(cx, expr) + && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind + && receiver.hir_id == expr.hir_id + { + return true; + } + false +} diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index 257dd76ab15c..455239cc37f3 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -1,19 +1,17 @@ #![allow(clippy::similar_names)] // `expr` and `expn` -use crate::visitors::{for_each_expr, Descend}; +use crate::visitors::{for_each_expr_without_closures, Descend}; use arrayvec::ArrayVec; use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder}; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::{Lrc, OnceLock}; use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath}; use rustc_lint::LateContext; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol}; -use std::cell::OnceCell; use std::ops::ControlFlow; -use std::rc::Rc; -use std::sync::atomic::{AtomicBool, Ordering}; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ sym::assert_eq_macro, @@ -325,7 +323,7 @@ fn find_assert_args_inner<'a, const N: usize>( Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?, }; let mut args = ArrayVec::new(); - let panic_expn = for_each_expr(expr, |e| { + let panic_expn = for_each_expr_without_closures(expr, |e| { if args.is_full() { match PanicExpn::parse(e) { Some(expn) => ControlFlow::Break(expn), @@ -351,7 +349,7 @@ fn find_assert_within_debug_assert<'a>( expn: ExpnId, assert_name: Symbol, ) -> Option<(&'a Expr<'a>, ExpnId)> { - for_each_expr(expr, |e| { + for_each_expr_without_closures(expr, |e| { if !e.span.from_expansion() { return ControlFlow::Continue(Descend::No); } @@ -388,50 +386,44 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> } } -thread_local! { - /// We preserve the [`FormatArgs`] structs from the early pass for use in the late pass to be - /// able to access the many features of a [`LateContext`]. +/// Stores AST [`FormatArgs`] nodes for use in late lint passes, as they are in a desugared form in +/// the HIR +#[derive(Default, Clone)] +pub struct FormatArgsStorage(Lrc>>); + +impl FormatArgsStorage { + /// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of + /// `expn_id` /// - /// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an - /// assumption that the early pass that populates the map and the later late passes will all be - /// running on the same thread. - #[doc(hidden)] - pub static AST_FORMAT_ARGS: OnceCell>> = { - static CALLED: AtomicBool = AtomicBool::new(false); - debug_assert!( - !CALLED.swap(true, Ordering::SeqCst), - "incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread", - ); - - OnceCell::new() - }; -} - -/// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of -/// `expn_id` -pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option> { - let format_args_expr = for_each_expr(start, |expr| { - let ctxt = expr.span.ctxt(); - if ctxt.outer_expn().is_descendant_of(expn_id) { - if macro_backtrace(expr.span) - .map(|macro_call| cx.tcx.item_name(macro_call.def_id)) - .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl)) - { - ControlFlow::Break(expr) + /// See also [`find_format_arg_expr`] + pub fn get(&self, cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option<&FormatArgs> { + let format_args_expr = for_each_expr_without_closures(start, |expr| { + let ctxt = expr.span.ctxt(); + if ctxt.outer_expn().is_descendant_of(expn_id) { + if macro_backtrace(expr.span) + .map(|macro_call| cx.tcx.item_name(macro_call.def_id)) + .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl)) + { + ControlFlow::Break(expr) + } else { + ControlFlow::Continue(Descend::Yes) + } } else { - ControlFlow::Continue(Descend::Yes) + ControlFlow::Continue(Descend::No) } - } else { - ControlFlow::Continue(Descend::No) - } - })?; + })?; - AST_FORMAT_ARGS.with(|ast_format_args| { - ast_format_args - .get()? - .get(&format_args_expr.span.with_parent(None)) - .cloned() - }) + debug_assert!(self.0.get().is_some(), "`FormatArgsStorage` not yet populated"); + + self.0.get()?.get(&format_args_expr.span.with_parent(None)) + } + + /// Should only be called by `FormatArgsCollector` + pub fn set(&self, format_args: FxHashMap) { + self.0 + .set(format_args) + .expect("`FormatArgsStorage::set` should only be called once"); + } } /// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if @@ -447,7 +439,7 @@ pub fn find_format_arg_expr<'hir, 'ast>( parent: _, } = target.expr.span.data(); - for_each_expr(start, |expr| { + for_each_expr_without_closures(start, |expr| { // When incremental compilation is enabled spans gain a parent during AST to HIR lowering, // since we're comparing an AST span to a HIR one we need to ignore the parent field let data = expr.span.data(); diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs index 06229ac938f9..7b4fd8a210ed 100644 --- a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs +++ b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs @@ -149,7 +149,7 @@ impl TypeVisitor> for ContainsRegion { } fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { - use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use}; + use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, Repeat, UnaryOp, Use}; let mut visit_op = |op: &mir::Operand<'_>| match op { mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local), @@ -159,7 +159,7 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { match rvalue { Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op), Aggregate(_, ops) => ops.iter().for_each(visit_op), - BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => { + BinaryOp(_, box (lhs, rhs)) => { visit_op(lhs); visit_op(rhs); }, diff --git a/src/tools/clippy/clippy_utils/src/ptr.rs b/src/tools/clippy/clippy_utils/src/ptr.rs index 88837d8a143e..991ea428dc33 100644 --- a/src/tools/clippy/clippy_utils/src/ptr.rs +++ b/src/tools/clippy/clippy_utils/src/ptr.rs @@ -1,5 +1,5 @@ use crate::source::snippet; -use crate::visitors::{for_each_expr, Descend}; +use crate::visitors::{for_each_expr_without_closures, Descend}; use crate::{path_to_local_id, strip_pat_refs}; use core::ops::ControlFlow; use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind}; @@ -31,7 +31,7 @@ fn extract_clone_suggestions<'tcx>( body: &'tcx Body<'_>, ) -> Option)>> { let mut spans = Vec::new(); - for_each_expr(body, |e| { + for_each_expr_without_closures(body, |e| { if let ExprKind::MethodCall(seg, recv, [], _) = e.kind && path_to_local_id(recv, id) { diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 95851a2eed81..42b10f69c0cd 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -3,10 +3,10 @@ // of terminologies might not be relevant in the context of Clippy. Note that its behavior might // differ from the time of `rustc` even if the name stays the same. -use clippy_config::msrvs::Msrv; +use clippy_config::msrvs::{self, Msrv}; use hir::LangItem; use rustc_attr::StableSince; -use rustc_const_eval::transform::check_consts::ConstCx; +use rustc_const_eval::check_consts::ConstCx; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; @@ -40,9 +40,13 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) )?; for bb in &*body.basic_blocks { - check_terminator(tcx, body, bb.terminator(), msrv)?; - for stmt in &bb.statements { - check_statement(tcx, body, def_id, stmt)?; + // Cleanup blocks are ignored entirely by const eval, so we can too: + // https://github.com/rust-lang/rust/blob/1dea922ea6e74f99a0e97de5cdb8174e4dea0444/compiler/rustc_const_eval/src/transform/check_consts/check.rs#L382 + if !bb.is_cleanup { + check_terminator(tcx, body, bb.terminator(), msrv)?; + for stmt in &bb.statements { + check_statement(tcx, body, def_id, stmt, msrv)?; + } } } Ok(()) @@ -102,13 +106,14 @@ fn check_rvalue<'tcx>( def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span, + msrv: &Msrv, ) -> McfResult { match rvalue { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { - check_place(tcx, *place, span, body) + check_place(tcx, *place, span, body, msrv) }, - Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body), + Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body, msrv), Rvalue::Repeat(operand, _) | Rvalue::Use(operand) | Rvalue::Cast( @@ -122,7 +127,7 @@ fn check_rvalue<'tcx>( | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer), operand, _, - ) => check_operand(tcx, operand, span, body), + ) => check_operand(tcx, operand, span, body, msrv), Rvalue::Cast( CastKind::PointerCoercion( PointerCoercion::UnsafeFnPointer @@ -133,15 +138,13 @@ fn check_rvalue<'tcx>( _, ) => Err((span, "function pointer casts are not allowed in const fn".into())), Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), op, cast_ty) => { - let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) { - deref_ty - } else { + let Some(pointee_ty) = cast_ty.builtin_deref(true) else { // We cannot allow this for now. return Err((span, "unsizing casts are only allowed for references right now".into())); }; let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id)); if let ty::Slice(_) | ty::Str = unsized_ty.kind() { - check_operand(tcx, op, span, body)?; + check_operand(tcx, op, span, body, msrv)?; // Casting/coercing things to slices is fine. Ok(()) } else { @@ -161,9 +164,9 @@ fn check_rvalue<'tcx>( "transmute can attempt to turn pointers into integers, so is unstable in const fn".into(), )), // binops are fine on integers - Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => { - check_operand(tcx, lhs, span, body)?; - check_operand(tcx, rhs, span, body)?; + Rvalue::BinaryOp(_, box (lhs, rhs)) => { + check_operand(tcx, lhs, span, body, msrv)?; + check_operand(tcx, rhs, span, body, msrv)?; let ty = lhs.ty(body, tcx); if ty.is_integral() || ty.is_bool() || ty.is_char() { Ok(()) @@ -179,14 +182,14 @@ fn check_rvalue<'tcx>( Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); if ty.is_integral() || ty.is_bool() { - check_operand(tcx, operand, span, body) + check_operand(tcx, operand, span, body, msrv) } else { Err((span, "only int and `bool` operations are stable in const fn".into())) } }, Rvalue::Aggregate(_, operands) => { for operand in operands { - check_operand(tcx, operand, span, body)?; + check_operand(tcx, operand, span, body, msrv)?; } Ok(()) }, @@ -198,28 +201,29 @@ fn check_statement<'tcx>( body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>, + msrv: &Msrv, ) -> McfResult { let span = statement.source_info.span; match &statement.kind { StatementKind::Assign(box (place, rval)) => { - check_place(tcx, *place, span, body)?; - check_rvalue(tcx, body, def_id, rval, span) + check_place(tcx, *place, span, body, msrv)?; + check_rvalue(tcx, body, def_id, rval, span, msrv) }, - StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body), + StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body, msrv), // just an assignment StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { - check_place(tcx, **place, span, body) + check_place(tcx, **place, span, body, msrv) }, - StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(tcx, op, span, body), + StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(tcx, op, span, body, msrv), StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping( rustc_middle::mir::CopyNonOverlapping { dst, src, count }, )) => { - check_operand(tcx, dst, span, body)?; - check_operand(tcx, src, span, body)?; - check_operand(tcx, count, span, body) + check_operand(tcx, dst, span, body, msrv)?; + check_operand(tcx, src, span, body, msrv)?; + check_operand(tcx, count, span, body, msrv) }, // These are all NOPs StatementKind::StorageLive(_) @@ -233,7 +237,13 @@ fn check_statement<'tcx>( } } -fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { +fn check_operand<'tcx>( + tcx: TyCtxt<'tcx>, + operand: &Operand<'tcx>, + span: Span, + body: &Body<'tcx>, + msrv: &Msrv, +) -> McfResult { match operand { Operand::Move(place) => { if !place.projection.as_ref().is_empty() @@ -245,9 +255,9 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b )); } - check_place(tcx, *place, span, body) + check_place(tcx, *place, span, body, msrv) }, - Operand::Copy(place) => check_place(tcx, *place, span, body), + Operand::Copy(place) => check_place(tcx, *place, span, body, msrv), Operand::Constant(c) => match c.check_static_ptr(tcx) { Some(_) => Err((span, "cannot access `static` items in const fn".into())), None => Ok(()), @@ -255,23 +265,27 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b } } -fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { +fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>, msrv: &Msrv) -> McfResult { for (base, elem) in place.as_ref().iter_projections() { match elem { ProjectionElem::Field(..) => { - let base_ty = base.ty(body, tcx).ty; - if let Some(def) = base_ty.ty_adt_def() { - // No union field accesses in `const fn` - if def.is_union() { - return Err((span, "accessing union fields is unstable".into())); - } + if base.ty(body, tcx).ty.is_union() && !msrv.meets(msrvs::CONST_FN_UNION) { + return Err((span, "accessing union fields is unstable".into())); } }, + ProjectionElem::Deref => match base.ty(body, tcx).ty.kind() { + ty::RawPtr(_, hir::Mutability::Mut) => { + return Err((span, "dereferencing raw mut pointer in const fn is unstable".into())); + }, + ty::RawPtr(_, hir::Mutability::Not) if !msrv.meets(msrvs::CONST_RAW_PTR_DEREF) => { + return Err((span, "dereferencing raw const pointer in const fn is unstable".into())); + }, + _ => (), + }, ProjectionElem::ConstantIndex { .. } | ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } - | ProjectionElem::Deref | ProjectionElem::Subtype(_) | ProjectionElem::Index(_) => {}, } @@ -304,7 +318,7 @@ fn check_terminator<'tcx>( } Ok(()) }, - TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body), + TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body, msrv), TerminatorKind::CoroutineDrop | TerminatorKind::Yield { .. } => { Err((span, "const fn coroutines are unstable".into())) }, @@ -341,10 +355,10 @@ fn check_terminator<'tcx>( )); } - check_operand(tcx, func, span, body)?; + check_operand(tcx, func, span, body, msrv)?; for arg in args { - check_operand(tcx, &arg.node, span, body)?; + check_operand(tcx, &arg.node, span, body, msrv)?; } Ok(()) } else { @@ -357,7 +371,7 @@ fn check_terminator<'tcx>( msg: _, target: _, unwind: _, - } => check_operand(tcx, cond, span, body), + } => check_operand(tcx, cond, span, body, msrv), TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), } } diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index e72467edeeb0..e2e5cde947d8 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -2,6 +2,7 @@ #![allow(clippy::module_name_repetitions)] +use rustc_ast::{LitKind, StrStyle}; use rustc_data_structures::sync::Lrc; use rustc_errors::Applicability; use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource}; @@ -250,7 +251,7 @@ pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow< /// - Applicability level `Unspecified` will never be changed. /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. /// - If the default value is used and the applicability level is `MachineApplicable`, change it to -/// `HasPlaceholders` +/// `HasPlaceholders` pub fn snippet_with_applicability<'a, T: LintContext>( cx: &T, span: Span, @@ -500,6 +501,50 @@ pub fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span { extended.with_lo(extended.lo() - BytePos(1)) } +/// Converts `expr` to a `char` literal if it's a `str` literal containing a single +/// character (or a single byte with `ascii_only`) +pub fn str_literal_to_char_literal( + cx: &LateContext<'_>, + expr: &Expr<'_>, + applicability: &mut Applicability, + ascii_only: bool, +) -> Option { + if let ExprKind::Lit(lit) = &expr.kind + && let LitKind::Str(r, style) = lit.node + && let string = r.as_str() + && let len = if ascii_only { + string.len() + } else { + string.chars().count() + } + && len == 1 + { + let snip = snippet_with_applicability(cx, expr.span, string, applicability); + let ch = if let StrStyle::Raw(nhash) = style { + let nhash = nhash as usize; + // for raw string: r##"a"## + &snip[(nhash + 2)..(snip.len() - 1 - nhash)] + } else { + // for regular string: "a" + &snip[1..(snip.len() - 1)] + }; + + let hint = format!( + "'{}'", + match ch { + "'" => "\\'", + r"\" => "\\\\", + "\\\"" => "\"", // no need to escape `"` in `'"'` + _ => ch, + } + ); + + Some(hint) + } else { + None + } +} + #[cfg(test)] mod test { use super::{reindent_multiline, without_block_comments}; diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index bf03c6c16015..6319c7bfa6b8 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -67,8 +67,7 @@ impl<'a> Sugg<'a> { /// - Applicability level `Unspecified` will never be changed. /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`. /// - If the default value is used and the applicability level is `MachineApplicable`, change it - /// to - /// `HasPlaceholders` + /// to `HasPlaceholders` pub fn hir_with_applicability( cx: &LateContext<'_>, expr: &hir::Expr<'_>, diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index c29e3feac9ad..7d4332a3d9de 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -9,7 +9,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, FnDecl, Safety, LangItem, TyKind}; +use rustc_hir::{Expr, FnDecl, LangItem, Safety, TyKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::Scalar; @@ -17,13 +17,13 @@ use rustc_middle::mir::ConstValue; use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ - self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, - GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, Upcast, TraitRef, Ty, TyCtxt, - TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr, + self, AdtDef, AliasTy, AssocItem, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, + GenericArgsRef, GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; -use rustc_target::abi::{Size, VariantIdx}; +use rustc_target::abi::VariantIdx; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::{Obligation, ObligationCause}; @@ -273,11 +273,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>( let infcx = tcx.infer_ctxt().build(); let args = args .into_iter() - .map(|arg| { - arg.into().unwrap_or_else(|| { - infcx.next_ty_var(DUMMY_SP).into() - }) - }) + .map(|arg| arg.into().unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into())) .collect::>(); // If an effect arg was not specified, we need to specify it. @@ -754,7 +750,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option None, @@ -795,13 +791,14 @@ fn sig_from_bounds<'tcx>( inputs = Some(i); }, ty::ClauseKind::Projection(p) - if Some(p.projection_term.def_id) == lang_items.fn_once_output() && p.projection_term.self_ty() == ty => + if Some(p.projection_term.def_id) == lang_items.fn_once_output() + && p.projection_term.self_ty() == ty => { if output.is_some() { // Multiple different fn trait impls. Is this even allowed? return None; } - output = Some(pred.kind().rebind(p.term.ty().unwrap())); + output = Some(pred.kind().rebind(p.term.expect_type())); }, _ => (), } @@ -839,7 +836,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option // Multiple different fn trait impls. Is this even allowed? return None; } - output = pred.kind().rebind(p.term.ty()).transpose(); + output = pred.kind().rebind(p.term.as_type()).transpose(); }, _ => (), } @@ -864,26 +861,11 @@ impl core::ops::Add for EnumValue { } /// Attempts to read the given constant as though it were an enum value. -#[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option { if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) { match tcx.type_of(id).instantiate_identity().kind() { - ty::Int(_) => Some(EnumValue::Signed(match value.size().bytes() { - 1 => i128::from(value.assert_bits(Size::from_bytes(1)) as u8 as i8), - 2 => i128::from(value.assert_bits(Size::from_bytes(2)) as u16 as i16), - 4 => i128::from(value.assert_bits(Size::from_bytes(4)) as u32 as i32), - 8 => i128::from(value.assert_bits(Size::from_bytes(8)) as u64 as i64), - 16 => value.assert_bits(Size::from_bytes(16)) as i128, - _ => return None, - })), - ty::Uint(_) => Some(EnumValue::Unsigned(match value.size().bytes() { - 1 => value.assert_bits(Size::from_bytes(1)), - 2 => value.assert_bits(Size::from_bytes(2)), - 4 => value.assert_bits(Size::from_bytes(4)), - 8 => value.assert_bits(Size::from_bytes(8)), - 16 => value.assert_bits(Size::from_bytes(16)), - _ => return None, - })), + ty::Int(_) => Some(EnumValue::Signed(value.to_int(value.size()))), + ty::Uint(_) => Some(EnumValue::Unsigned(value.to_uint(value.size()))), _ => None, } } else { @@ -956,11 +938,7 @@ pub struct AdtVariantInfo { impl AdtVariantInfo { /// Returns ADT variants ordered by size - pub fn new<'tcx>( - cx: &LateContext<'tcx>, - adt: AdtDef<'tcx>, - subst: GenericArgsRef<'tcx> - ) -> Vec { + pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: GenericArgsRef<'tcx>) -> Vec { let mut variants_size = adt .variants() .iter() @@ -1335,3 +1313,39 @@ pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx> pub fn is_manually_drop(ty: Ty<'_>) -> bool { ty.ty_adt_def().map_or(false, AdtDef::is_manually_drop) } + +/// Returns the deref chain of a type, starting with the type itself. +pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl Iterator> + 'cx { + iter::successors(Some(ty), |&ty| { + if let Some(deref_did) = cx.tcx.lang_items().deref_trait() + && implements_trait(cx, ty, deref_did, &[]) + { + make_normalized_projection(cx.tcx, cx.param_env, deref_did, sym::Target, [ty]) + } else { + None + } + }) +} + +/// Checks if a Ty<'_> has some inherent method Symbol. +/// This does not look for impls in the type's `Deref::Target` type. +/// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`. +pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<&'a AssocItem> { + if let Some(ty_did) = ty.ty_adt_def().map(AdtDef::did) { + cx.tcx + .inherent_impls(ty_did) + .into_iter() + .flatten() + .map(|&did| { + cx.tcx + .associated_items(did) + .filter_by_name_unhygienic(method_name) + .next() + .filter(|item| item.kind == AssocKind::Fn) + }) + .next() + .flatten() + } else { + None + } +} diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index c2ff19931d5c..cba61c841efc 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -176,7 +176,7 @@ fn qpath_certainty(cx: &LateContext<'_>, qpath: &QPath<'_>, resolves_to_type: bo .get(*lang_item) .map_or(Certainty::Uncertain, |def_id| { let generics = cx.tcx.generics_of(def_id); - if generics.parent_count == 0 && generics.own_params.is_empty() { + if generics.is_empty() { Certainty::Certain(if resolves_to_type { Some(def_id) } else { None }) } else { Certainty::Uncertain @@ -206,8 +206,18 @@ fn path_segment_certainty( // Checking `res_generics_def_id(..)` before calling `generics_of` avoids an ICE. if cx.tcx.res_generics_def_id(path_segment.res).is_some() { let generics = cx.tcx.generics_of(def_id); - let count = generics.own_params.len() - usize::from(generics.host_effect_index.is_some()); - let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && count == 0 { + + let own_count = generics.own_params.len() + - usize::from(generics.host_effect_index.is_some_and(|index| { + // Check that the host index actually belongs to this resolution. + // E.g. for `Add::add`, host_effect_index is `Some(2)`, but it's part of the parent `Add` + // trait's generics. + // Add params: [Self#0, Rhs#1, host#2] parent_count=0, count=3 + // Add::add params: [] parent_count=3, count=3 + // (3..3).contains(&host_effect_index) => false + (generics.parent_count..generics.count()).contains(&index) + })); + let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && own_count == 0 { Certainty::Certain(None) } else { Certainty::Uncertain diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index 9abb4ef9b8d3..fbf3d95365ac 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -1,4 +1,4 @@ -use crate::visitors::{for_each_expr, for_each_expr_with_closures, Descend, Visitable}; +use crate::visitors::{for_each_expr, for_each_expr_without_closures, Descend, Visitable}; use crate::{self as utils, get_enclosing_loop_or_multi_call_closure}; use core::ops::ControlFlow; use hir::def::Res; @@ -16,13 +16,9 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> used_mutably: HirIdSet::default(), skip: false, }; - ExprUseVisitor::for_clippy( - cx, - expr.hir_id.owner.def_id, - &mut delegate, - ) - .walk_expr(expr) - .into_ok(); + ExprUseVisitor::for_clippy(cx, expr.hir_id.owner.def_id, &mut delegate) + .walk_expr(expr) + .into_ok(); if delegate.skip { return None; @@ -149,7 +145,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { } pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { - for_each_expr(expression, |e| { + for_each_expr_without_closures(expression, |e| { match e.kind { ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => ControlFlow::Break(()), // Something special could be done here to handle while or for loop @@ -163,7 +159,7 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { } pub fn local_used_in<'tcx>(cx: &LateContext<'tcx>, local_id: HirId, v: impl Visitable<'tcx>) -> bool { - for_each_expr_with_closures(cx, v, |e| { + for_each_expr(cx, v, |e| { if utils::path_to_local_id(e, local_id) { ControlFlow::Break(()) } else { @@ -188,7 +184,7 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr let loop_start = get_enclosing_loop_or_multi_call_closure(cx, after).map(|e| e.hir_id); let mut past_expr = false; - for_each_expr_with_closures(cx, block, |e| { + for_each_expr(cx, block, |e| { if past_expr { if utils::path_to_local_id(e, local_id) { ControlFlow::Break(()) diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 90b56297bb55..3a39e178515d 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -100,7 +100,7 @@ visitable_ref!(Stmt, visit_stmt); /// Calls the given function once for each expression contained. This does not enter any bodies or /// nested items. -pub fn for_each_expr<'tcx, B, C: Continue>( +pub fn for_each_expr_without_closures<'tcx, B, C: Continue>( node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, ) -> Option { @@ -134,7 +134,7 @@ pub fn for_each_expr<'tcx, B, C: Continue>( /// Calls the given function once for each expression contained. This will enter bodies, but not /// nested items. -pub fn for_each_expr_with_closures<'tcx, B, C: Continue>( +pub fn for_each_expr<'tcx, B, C: Continue>( cx: &LateContext<'tcx>, node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, @@ -181,7 +181,7 @@ pub fn for_each_expr_with_closures<'tcx, B, C: Continue>( /// returns `true` if expr contains match expr desugared from try fn contains_try(expr: &Expr<'_>) -> bool { - for_each_expr(expr, |e| { + for_each_expr_without_closures(expr, |e| { if matches!(e.kind, ExprKind::Match(_, _, hir::MatchSource::TryDesugar(_))) { ControlFlow::Break(()) } else { @@ -286,7 +286,7 @@ where /// Checks if the given resolved path is used in the given body. pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { - for_each_expr_with_closures(cx, cx.tcx.hir().body(body).value, |e| { + for_each_expr(cx, cx.tcx.hir().body(body).value, |e| { if let ExprKind::Path(p) = &e.kind { if cx.qpath_res(p, e.hir_id) == res { return ControlFlow::Break(()); @@ -299,7 +299,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { /// Checks if the given local is used. pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { - for_each_expr_with_closures(cx, visitable, |e| { + for_each_expr(cx, visitable, |e| { if path_to_local_id(e, id) { ControlFlow::Break(()) } else { @@ -757,7 +757,7 @@ pub fn for_each_local_assignment<'tcx, B>( } pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool { - for_each_expr(expr, |e| { + for_each_expr_without_closures(expr, |e| { if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) { ControlFlow::Break(()) } else { @@ -776,7 +776,7 @@ pub fn local_used_once<'tcx>( ) -> Option<&'tcx Expr<'tcx>> { let mut expr = None; - let cf = for_each_expr_with_closures(cx, visitable, |e| { + let cf = for_each_expr(cx, visitable, |e| { if path_to_local_id(e, id) && expr.replace(e).is_some() { ControlFlow::Break(()) } else { diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml index c8c734c3a7c9..86d945c14a58 100644 --- a/src/tools/clippy/declare_clippy_lint/Cargo.toml +++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.80" +version = "0.1.81" edition = "2021" publish = false diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml index a828d1237046..8c5a409e25b1 100644 --- a/src/tools/clippy/lintcheck/Cargo.toml +++ b/src/tools/clippy/lintcheck/Cargo.toml @@ -13,7 +13,7 @@ default-run = "lintcheck" [dependencies] anyhow = "1.0.69" cargo_metadata = "0.15.3" -clap = { version = "4.1.8", features = ["derive", "env"] } +clap = { version = "4.4", features = ["derive", "env"] } crates_io_api = "0.8.1" crossbeam-channel = "0.5.6" flate2 = "1.0" diff --git a/src/tools/clippy/lintcheck/README.md b/src/tools/clippy/lintcheck/README.md index 37cc04538094..61b581ba0fae 100644 --- a/src/tools/clippy/lintcheck/README.md +++ b/src/tools/clippy/lintcheck/README.md @@ -1,6 +1,6 @@ ## `cargo lintcheck` -Runs clippy on a fixed set of crates read from +Runs Clippy on a fixed set of crates read from `lintcheck/lintcheck_crates.toml` and saves logs of the lint warnings into the repo. We can then check the diff and spot new or disappearing warnings. @@ -84,7 +84,7 @@ This lets us spot bad suggestions or false positives automatically in some cases > Note: Fix mode implies `--all-targets`, so it can fix as much code as it can. -Please note that the target dir should be cleaned afterwards since clippy will modify +Please note that the target dir should be cleaned afterwards since Clippy will modify the downloaded sources which can lead to unexpected results when running lintcheck again afterwards. ### Recursive mode diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 055f305eb8e1..842c2f3de0d1 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-05-02" +channel = "nightly-2024-06-13" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 9e42abbc9aa9..6117e76897f2 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -2,7 +2,6 @@ #![allow(rustc::untranslatable_diagnostic)] #![feature(rustc_private)] #![feature(let_chains)] -#![feature(lazy_cell)] #![feature(lint_reasons)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap @@ -181,12 +180,12 @@ pub fn main() { rustc_driver::init_rustc_env_logger(&early_dcx); - let using_internal_features = rustc_driver::install_ice_hook(BUG_REPORT_URL, |handler| { + let using_internal_features = rustc_driver::install_ice_hook(BUG_REPORT_URL, |dcx| { // FIXME: this macro calls unwrap internally but is called in a panicking context! It's not // as simple as moving the call from the hook to main, because `install_ice_hook` doesn't // accept a generic closure. let version_info = rustc_tools_util::get_version_info!(); - handler.note(format!("Clippy version: {version_info}")); + dcx.handle().note(format!("Clippy version: {version_info}")); }); exit(rustc_driver::catch_with_exit_code(move || { diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index b06a11702ec8..333a2ab58575 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -1,4 +1,3 @@ -#![feature(lazy_cell)] #![feature(is_sorted)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs index 3f16c180ea78..36a7a651c4d2 100644 --- a/src/tools/clippy/tests/dogfood.rs +++ b/src/tools/clippy/tests/dogfood.rs @@ -3,7 +3,6 @@ //! //! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context -#![feature(lazy_cell)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] diff --git a/src/tools/clippy/tests/lint_message_convention.rs b/src/tools/clippy/tests/lint_message_convention.rs index 98019c755276..6ce7e44474d8 100644 --- a/src/tools/clippy/tests/lint_message_convention.rs +++ b/src/tools/clippy/tests/lint_message_convention.rs @@ -1,4 +1,3 @@ -#![feature(lazy_cell)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] diff --git a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr index 103e60d84844..4fe7f6f7a9ed 100644 --- a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr @@ -1,17 +1,18 @@ error: lint group `rust_2018_idioms` has the same priority (0) as a lint - --> Cargo.toml:7:1 - | -7 | rust_2018_idioms = "warn" - | ^^^^^^^^^^^^^^^^ ------ has an implicit priority of 0 -8 | bare_trait_objects = "allow" - | ------------------ has the same priority as this lint - | - = note: the order of the lints in the table is ignored by Cargo - = note: `#[deny(clippy::lint_groups_priority)]` on by default + --> Cargo.toml:7:1 + | +7 | rust_2018_idioms = "warn" + | ^^^^^^^^^^^^^^^^ ------ has an implicit priority of 0 +... +12 | unused_attributes = { level = "allow" } + | ----------------- has the same priority as this lint + | + = note: the order of the lints in the table is ignored by Cargo + = note: `#[deny(clippy::lint_groups_priority)]` on by default help: to have lints override the group set `rust_2018_idioms` to a lower priority - | -7 | rust_2018_idioms = { level = "warn", priority = -1 } - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | +7 | rust_2018_idioms = { level = "warn", priority = -1 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: lint group `unused` has the same priority (0) as a lint --> Cargo.toml:10:1 @@ -29,17 +30,45 @@ help: to have lints override the group set `unused` to a lower priority | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: lint group `pedantic` has the same priority (-1) as a lint - --> Cargo.toml:19:1 + --> Cargo.toml:15:1 | -19 | pedantic = { level = "warn", priority = -1 } +15 | pedantic = { level = "warn", priority = -1 } | ^^^^^^^^ -20 | similar_names = { level = "allow", priority = -1 } +16 | similar_names = { level = "allow", priority = -1 } | ------------- has the same priority as this lint | = note: the order of the lints in the table is ignored by Cargo help: to have lints override the group set `pedantic` to a lower priority | -19 | pedantic = { level = "warn", priority = -2 } +15 | pedantic = { level = "warn", priority = -2 } | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -error: could not compile `fail` (lib) due to 3 previous errors +error: lint group `rust_2018_idioms` has the same priority (0) as a lint + --> Cargo.toml:19:1 + | +19 | rust_2018_idioms = "warn" + | ^^^^^^^^^^^^^^^^ ------ has an implicit priority of 0 +20 | bare_trait_objects = "allow" + | ------------------ has the same priority as this lint + | + = note: the order of the lints in the table is ignored by Cargo +help: to have lints override the group set `rust_2018_idioms` to a lower priority + | +19 | rust_2018_idioms = { level = "warn", priority = -1 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: lint group `pedantic` has the same priority (0) as a lint + --> Cargo.toml:23:1 + | +23 | pedantic = "warn" + | ^^^^^^^^ ------ has an implicit priority of 0 +24 | similar_names = "allow" + | ------------- has the same priority as this lint + | + = note: the order of the lints in the table is ignored by Cargo +help: to have lints override the group set `pedantic` to a lower priority + | +23 | pedantic = { level = "warn", priority = -1 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: could not compile `fail` (lib) due to 5 previous errors diff --git a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml index 4ce41f781711..c87662f822e1 100644 --- a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml +++ b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml @@ -11,10 +11,14 @@ unused = { level = "deny" } unused_braces = { level = "allow", priority = 1 } unused_attributes = { level = "allow" } -# `warnings` is not a group so the order it is passed does not matter -warnings = "deny" -deprecated = "allow" - [lints.clippy] pedantic = { level = "warn", priority = -1 } similar_names = { level = "allow", priority = -1 } + +[workspace.lints.rust] +rust_2018_idioms = "warn" +bare_trait_objects = "allow" + +[workspace.lints.clippy] +pedantic = "warn" +similar_names = "allow" diff --git a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/Cargo.toml b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/Cargo.toml index e9fcf803d936..979c915cf0c1 100644 --- a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/Cargo.toml +++ b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/Cargo.toml @@ -3,6 +3,13 @@ name = "pass" version = "0.1.0" publish = false +[lints.rust] +# Warnings does not conflict with any group or lint +warnings = "deny" +# Groups & lints at the same level do not conflict +rust_2018_idioms = "warn" +unsafe_code = "warn" + [lints.clippy] pedantic = { level = "warn", priority = -1 } style = { level = "warn", priority = 1 } diff --git a/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs b/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs index 5a2a868ed3ec..ca71dddcc24f 100644 --- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs +++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs @@ -11,11 +11,15 @@ use rustc_lint::{Lint, LintContext}; use rustc_middle::ty::TyCtxt; pub fn a(cx: impl LintContext, lint: &'static Lint, span: impl Into, msg: impl Into) { - cx.span_lint(lint, span, msg, |_| {}); + cx.span_lint(lint, span, |lint| { + lint.primary_message(msg); + }); } pub fn b(tcx: TyCtxt<'_>, lint: &'static Lint, hir_id: HirId, span: impl Into, msg: impl Into) { - tcx.node_span_lint(lint, hir_id, span, msg, |_| {}); + tcx.node_span_lint(lint, hir_id, span, |lint| { + lint.primary_message(msg); + }); } fn main() {} diff --git a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr index cfc590bed369..1be4b665bcba 100644 --- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr +++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr @@ -1,18 +1,22 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` --> tests/ui-internal/disallow_span_lint.rs:14:5 | -LL | cx.span_lint(lint, span, msg, |_| {}); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | / cx.span_lint(lint, span, |lint| { +LL | | lint.primary_message(msg); +LL | | }); + | |______^ | = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead (from clippy.toml) = note: `-D clippy::disallowed-methods` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint` - --> tests/ui-internal/disallow_span_lint.rs:18:5 + --> tests/ui-internal/disallow_span_lint.rs:20:5 | -LL | tcx.node_span_lint(lint, hir_id, span, msg, |_| {}); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | / tcx.node_span_lint(lint, hir_id, span, |lint| { +LL | | lint.primary_message(msg); +LL | | }); + | |______^ | = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead (from clippy.toml) diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs new file mode 100644 index 000000000000..f5e01b431ad9 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs @@ -0,0 +1,260 @@ +//! Tests macro_metavars_in_unsafe with default configuration +#![feature(decl_macro, lint_reasons)] +#![warn(clippy::macro_metavars_in_unsafe)] +#![allow(clippy::no_effect)] + +#[macro_export] +macro_rules! allow_works { + ($v:expr) => { + #[expect(clippy::macro_metavars_in_unsafe)] + unsafe { + $v; + }; + }; +} + +#[macro_export] +macro_rules! simple { + ($v:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + dbg!($v); + } + }; +} + +#[macro_export] +#[rustfmt::skip] // for some reason rustfmt rewrites $r#unsafe to r#u$nsafe, bug? +macro_rules! raw_symbol { + ($r#mod:expr, $r#unsafe:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $r#mod; + } + $r#unsafe; + }; +} + +#[macro_export] +macro_rules! multilevel_unsafe { + ($v:expr) => { + unsafe { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } + } + }; +} + +#[macro_export] +macro_rules! in_function { + ($v:expr) => { + unsafe { + fn f() { + // function introduces a new body, so don't lint. + $v; + } + } + }; +} + +#[macro_export] +macro_rules! in_function_with_unsafe { + ($v:expr) => { + unsafe { + fn f() { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } + } + } + }; +} + +#[macro_export] +macro_rules! const_static { + ($c:expr, $s:expr) => { + unsafe { + // const and static introduces new body, don't lint + const _X: i32 = $c; + static _Y: i32 = $s; + } + }; +} + +#[macro_export] +macro_rules! const_generic_in_struct { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + struct Ty< + const L: i32 = 1, + const M: i32 = { + 1; + unsafe { $inside_unsafe } + //~^ ERROR: this macro expands metavariables in an unsafe block + }, + const N: i32 = { $outside_unsafe }, + >; + } + }; +} + +#[macro_export] +macro_rules! fn_with_const_generic { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + fn f() { + $outside_unsafe; + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $inside_unsafe; + } + } + } + }; +} + +#[macro_export] +macro_rules! variables { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $inside_unsafe; + let inside_unsafe = 1; + inside_unsafe; + } + $outside_unsafe; + let outside_unsafe = 1; + outside_unsafe; + }; +} + +#[macro_export] +macro_rules! multiple_matchers { + ($inside_unsafe:expr, $outside_unsafe:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $inside_unsafe; + } + $outside_unsafe; + }; + ($($v:expr, $x:expr),+) => { + $( + $v; + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $x; + } + );+ + }; +} + +#[macro_export] +macro_rules! multiple_unsafe_blocks { + ($w:expr, $x:expr, $y:expr) => { + $w; + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $x; + } + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $x; + $y; + } + }; +} + +pub macro macro2_0($v:expr) { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } +} + +// don't lint private macros with the default configuration +macro_rules! private_mac { + ($v:expr) => { + unsafe { + $v; + } + }; +} + +// don't lint exported macros that are doc(hidden) because they also aren't part of the public API +#[macro_export] +#[doc(hidden)] +macro_rules! exported_but_hidden { + ($v:expr) => { + unsafe { + $v; + } + }; +} + +// don't lint if the same metavariable is expanded in an unsafe block and then outside of one: +// unsafe {} is still needed at callsite so not problematic +#[macro_export] +macro_rules! does_require_unsafe { + ($v:expr) => { + unsafe { + $v; + } + $v; + }; +} + +#[macro_export] +macro_rules! unsafe_from_root_ctxt { + ($v:expr) => { + // Expands to unsafe { 1 }, but the unsafe block is from the root ctxt and not this macro, + // so no warning. + $v; + }; +} + +// invoked from another macro, should still generate a warning +#[macro_export] +macro_rules! nested_macro_helper { + ($v:expr) => {{ + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + $v; + } + }}; +} + +#[macro_export] +macro_rules! nested_macros { + ($v:expr, $v2:expr) => {{ + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + nested_macro_helper!($v); + $v; + } + }}; +} + +fn main() { + allow_works!(1); + simple!(1); + raw_symbol!(1, 1); + multilevel_unsafe!(1); + in_function!(1); + in_function_with_unsafe!(1); + const_static!(1, 1); + const_generic_in_struct!(1, 1); + fn_with_const_generic!(1, 1); + variables!(1, 1); + multiple_matchers!(1, 1); + multiple_matchers!(1, 1, 1, 1); + macro2_0!(1); + private_mac!(1); + exported_but_hidden!(1); + does_require_unsafe!(1); + multiple_unsafe_blocks!(1, 1, 1); + unsafe_from_root_ctxt!(unsafe { 1 }); + nested_macros!(1, 1); +} diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr new file mode 100644 index 000000000000..d6b97f6fde1e --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr @@ -0,0 +1,187 @@ +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:19:9 + | +LL | / unsafe { +LL | | +LL | | dbg!($v); +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]` + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:30:9 + | +LL | / unsafe { +LL | | +LL | | $r#mod; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:42:13 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_____________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:67:17 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_________________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:95:21 + | +LL | unsafe { $inside_unsafe } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:110:17 + | +LL | / unsafe { +LL | | +LL | | $inside_unsafe; +LL | | } + | |_________________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:122:9 + | +LL | / unsafe { +LL | | +LL | | $inside_unsafe; +LL | | let inside_unsafe = 1; +LL | | inside_unsafe; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:137:9 + | +LL | / unsafe { +LL | | +LL | | $inside_unsafe; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:146:13 + | +LL | / unsafe { +LL | | +LL | | $x; +LL | | } + | |_____________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:171:5 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_____^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:158:9 + | +LL | / unsafe { +LL | | +LL | | $x; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:162:9 + | +LL | / unsafe { +LL | | +LL | | $x; +LL | | $y; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:222:9 + | +LL | / unsafe { +LL | | +LL | | $v; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:232:9 + | +LL | / unsafe { +LL | | +LL | | nested_macro_helper!($v); +LL | | $v; +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml new file mode 100644 index 000000000000..d4bbc2a1be89 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml @@ -0,0 +1 @@ +warn-unsafe-macro-metavars-in-private-macros = true diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs new file mode 100644 index 000000000000..2bbe1fa7b7f9 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs @@ -0,0 +1,15 @@ +//! Tests macro_metavars_in_unsafe with private (non-exported) macros +#![warn(clippy::macro_metavars_in_unsafe)] + +macro_rules! mac { + ($v:expr) => { + unsafe { + //~^ ERROR: this macro expands metavariables in an unsafe block + dbg!($v); + } + }; +} + +fn main() { + mac!(1); +} diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr new file mode 100644 index 000000000000..f9c418b22188 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr @@ -0,0 +1,17 @@ +error: this macro expands metavariables in an unsafe block + --> tests/ui-toml/macro_metavars_in_unsafe/private/test.rs:6:9 + | +LL | / unsafe { +LL | | +LL | | dbg!($v); +LL | | } + | |_________^ + | + = note: this allows the user of the macro to write unsafe code outside of an unsafe block + = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable + = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite + = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/panic/clippy.toml b/src/tools/clippy/tests/ui-toml/panic/clippy.toml new file mode 100644 index 000000000000..5d6230d092c0 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/panic/clippy.toml @@ -0,0 +1 @@ +allow-panic-in-tests = true diff --git a/src/tools/clippy/tests/ui-toml/panic/panic.rs b/src/tools/clippy/tests/ui-toml/panic/panic.rs new file mode 100644 index 000000000000..618a37ddfc55 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/panic/panic.rs @@ -0,0 +1,54 @@ +//@compile-flags: --test +#![warn(clippy::panic)] + +fn main() { + enum Enam { + A, + } + let a = Enam::A; + match a { + Enam::A => {}, + _ => panic!(""), + } +} + +#[test] +fn lonely_test() { + enum Enam { + A, + } + let a = Enam::A; + match a { + Enam::A => {}, + _ => panic!(""), + } +} + +#[cfg(test)] +mod tests { + // should not lint in `#[cfg(test)]` modules + #[test] + fn test_fn() { + enum Enam { + A, + } + let a = Enam::A; + match a { + Enam::A => {}, + _ => panic!(""), + } + + bar(); + } + + fn bar() { + enum Enam { + A, + } + let a = Enam::A; + match a { + Enam::A => {}, + _ => panic!(""), + } + } +} diff --git a/src/tools/clippy/tests/ui-toml/panic/panic.stderr b/src/tools/clippy/tests/ui-toml/panic/panic.stderr new file mode 100644 index 000000000000..bf7503e086c9 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/panic/panic.stderr @@ -0,0 +1,11 @@ +error: `panic` should not be present in production code + --> tests/ui-toml/panic/panic.rs:11:14 + | +LL | _ => panic!(""), + | ^^^^^^^^^^ + | + = note: `-D clippy::panic` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::panic)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs b/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs index ae4c3f84c296..79c8751468de 100644 --- a/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs +++ b/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs @@ -47,7 +47,7 @@ pub mod __macro { pub struct T; impl T { pub unsafe fn f() {} - //~^ ERROR: unsafe function's docs miss `# Safety` section + //~^ ERROR: unsafe function's docs are missing a `# Safety` section } } diff --git a/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.stderr b/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.stderr index 65ec1a7bebbf..a8ee09b9df78 100644 --- a/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.stderr +++ b/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.stderr @@ -51,7 +51,7 @@ note: the lint level is defined here LL | clippy::missing_panics_doc | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: unsafe function's docs miss `# Safety` section +error: unsafe function's docs are missing a `# Safety` section --> tests/ui-toml/private-doc-errors/doc_lints.rs:49:9 | LL | pub unsafe fn f() {} diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/default/clippy.toml b/src/tools/clippy/tests/ui-toml/renamed_function_params/default/clippy.toml new file mode 100644 index 000000000000..5381e70a9391 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/default/clippy.toml @@ -0,0 +1,2 @@ +# Ignore `From`, `TryFrom`, `FromStr` by default +# allow-renamed-params-for = [] diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/extend/clippy.toml b/src/tools/clippy/tests/ui-toml/renamed_function_params/extend/clippy.toml new file mode 100644 index 000000000000..9b3853e76961 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/extend/clippy.toml @@ -0,0 +1,2 @@ +# Ignore `From`, `TryFrom`, `FromStr` by default +allow-renamed-params-for = [ "..", "std::ops::Add", "renamed_function_params::MyTrait" ] diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr new file mode 100644 index 000000000000..2d700f607592 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr @@ -0,0 +1,46 @@ +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:30:18 + | +LL | fn eq(&self, rhs: &Self) -> bool { + | ^^^ help: consider using the default name: `other` + | + = note: `-D clippy::renamed-function-params` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::renamed_function_params)]` + +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:34:18 + | +LL | fn ne(&self, rhs: &Self) -> bool { + | ^^^ help: consider using the default name: `other` + +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:48:19 + | +LL | fn foo(&self, i_dont_wanna_use_your_name: u8) {} // only lint in `extend` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the default name: `val` + +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:55:31 + | +LL | fn hash(&self, states: &mut H) { + | ^^^^^^ help: consider using the default name: `state` + +error: renamed function parameters of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:59:30 + | +LL | fn hash_slice(date: &[Self], states: &mut H) { + | ^^^^ ^^^^^^ + | +help: consider using the default names + | +LL | fn hash_slice(data: &[Self], state: &mut H) { + | ~~~~ ~~~~~ + +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:80:18 + | +LL | fn add(self, b: B) -> C { + | ^ help: consider using the default name: `rhs` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr new file mode 100644 index 000000000000..e57554fa613a --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr @@ -0,0 +1,34 @@ +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:30:18 + | +LL | fn eq(&self, rhs: &Self) -> bool { + | ^^^ help: consider using the default name: `other` + | + = note: `-D clippy::renamed-function-params` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::renamed_function_params)]` + +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:34:18 + | +LL | fn ne(&self, rhs: &Self) -> bool { + | ^^^ help: consider using the default name: `other` + +error: renamed function parameter of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:55:31 + | +LL | fn hash(&self, states: &mut H) { + | ^^^^^^ help: consider using the default name: `state` + +error: renamed function parameters of trait impl + --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:59:30 + | +LL | fn hash_slice(date: &[Self], states: &mut H) { + | ^^^^ ^^^^^^ + | +help: consider using the default names + | +LL | fn hash_slice(data: &[Self], state: &mut H) { + | ~~~~ ~~~~~ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.rs b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.rs new file mode 100644 index 000000000000..f3eb910abbd6 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.rs @@ -0,0 +1,110 @@ +//@no-rustfix +//@revisions: default extend +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/renamed_function_params/default +//@[extend] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/renamed_function_params/extend +#![warn(clippy::renamed_function_params)] +#![allow(clippy::partialeq_ne_impl, clippy::to_string_trait_impl)] +#![allow(unused)] + +use std::hash::{Hash, Hasher}; + +struct A; +impl From for String { + fn from(_value: A) -> Self { + String::new() + } +} +impl ToString for A { + fn to_string(&self) -> String { + String::new() + } +} + +struct B(u32); +impl std::convert::From for String { + fn from(b: B) -> Self { + b.0.to_string() + } +} +impl PartialEq for B { + fn eq(&self, rhs: &Self) -> bool { + //~^ ERROR: renamed function parameter of trait impl + self.0 == rhs.0 + } + fn ne(&self, rhs: &Self) -> bool { + //~^ ERROR: renamed function parameter of trait impl + self.0 != rhs.0 + } +} + +trait MyTrait { + fn foo(&self, val: u8); + fn bar(a: u8, b: u8); + fn baz(self, _val: u8); + fn quz(&self, _: u8); +} + +impl MyTrait for B { + fn foo(&self, i_dont_wanna_use_your_name: u8) {} // only lint in `extend` + fn bar(_a: u8, _: u8) {} + fn baz(self, val: u8) {} + fn quz(&self, val: u8) {} +} + +impl Hash for B { + fn hash(&self, states: &mut H) { + //~^ ERROR: renamed function parameter of trait impl + self.0.hash(states); + } + fn hash_slice(date: &[Self], states: &mut H) { + //~^ ERROR: renamed function parameters of trait impl + for d in date { + d.hash(states); + } + } +} + +impl B { + fn totally_irrelevant(&self, right: bool) {} + fn some_fn(&self, other: impl MyTrait) {} +} + +#[derive(Copy, Clone)] +enum C { + A, + B(u32), +} + +impl std::ops::Add for C { + type Output = C; + fn add(self, b: B) -> C { + // only lint in `extend` + C::B(b.0) + } +} + +impl From for C { + fn from(_: A) -> C { + C::A + } +} + +trait CustomTraitA { + fn foo(&self, other: u32); +} +trait CustomTraitB { + fn bar(&self, value: u8); +} + +macro_rules! impl_trait { + ($impl_for:ident, $tr:ty, $fn_name:ident, $t:ty) => { + impl $tr for $impl_for { + fn $fn_name(&self, v: $t) {} + } + }; +} + +impl_trait!(C, CustomTraitA, foo, u32); +impl_trait!(C, CustomTraitB, bar, u8); + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs index 7f28efd676f2..f02bd07cfe7b 100644 --- a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs +++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs @@ -40,3 +40,9 @@ fn main() { let _ = HashMap; let _: usize = 64_usize; } + +mod useless_attribute { + // Regression test for https://github.com/rust-lang/rust-clippy/issues/12753 + #[allow(clippy::disallowed_types)] + use std::collections::HashMap; +} diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 722e9b3bc8d4..5cf9c0fb2710 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -8,8 +8,10 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-expect-in-tests allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings + allow-panic-in-tests allow-print-in-tests allow-private-module-inception + allow-renamed-params-for allow-unwrap-in-tests allow-useless-vec-in-tests allowed-dotfiles @@ -74,6 +76,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect vec-box-size-threshold verbose-bit-mask-threshold warn-on-all-wildcard-imports + warn-unsafe-macro-metavars-in-private-macros --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:2:1 | LL | foobar = 42 @@ -89,8 +92,10 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-expect-in-tests allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings + allow-panic-in-tests allow-print-in-tests allow-private-module-inception + allow-renamed-params-for allow-unwrap-in-tests allow-useless-vec-in-tests allowed-dotfiles @@ -155,6 +160,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect vec-box-size-threshold verbose-bit-mask-threshold warn-on-all-wildcard-imports + warn-unsafe-macro-metavars-in-private-macros --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:4:1 | LL | barfoo = 53 @@ -170,8 +176,10 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni allow-expect-in-tests allow-mixed-uninlined-format-args allow-one-hash-in-raw-strings + allow-panic-in-tests allow-print-in-tests allow-private-module-inception + allow-renamed-params-for allow-unwrap-in-tests allow-useless-vec-in-tests allowed-dotfiles @@ -236,6 +244,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni vec-box-size-threshold verbose-bit-mask-threshold warn-on-all-wildcard-imports + warn-unsafe-macro-metavars-in-private-macros --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:7:1 | LL | allow_mixed_uninlined_format_args = true diff --git a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs index 2454c10382df..7f93d2071c9d 100644 --- a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs +++ b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.rs @@ -1,3 +1,4 @@ +#![allow(clippy::needless_maybe_sized)] #![warn(clippy::type_repetition_in_bounds)] fn f() diff --git a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr index 6005f76b94be..c5102c39d1cf 100644 --- a/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr +++ b/src/tools/clippy/tests/ui-toml/type_repetition_in_bounds/main.stderr @@ -1,5 +1,5 @@ error: this type has already been used as a bound predicate - --> tests/ui-toml/type_repetition_in_bounds/main.rs:13:5 + --> tests/ui-toml/type_repetition_in_bounds/main.rs:14:5 | LL | T: Unpin + PartialEq, | ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/assigning_clones.fixed b/src/tools/clippy/tests/ui/assigning_clones.fixed index 8387c7d6156b..70ab43b49b3a 100644 --- a/src/tools/clippy/tests/ui/assigning_clones.fixed +++ b/src/tools/clippy/tests/ui/assigning_clones.fixed @@ -62,6 +62,16 @@ fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFr mut_thing.clone_from(ref_thing + ref_thing); } +fn clone_method_macro() { + let mut s = String::from(""); + s.clone_from(&format!("{} {}", "hello", "world")); +} + +fn clone_function_macro() { + let mut s = String::from(""); + Clone::clone_from(&mut s, &format!("{} {}", "hello", "world")); +} + fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom { let mut a = HasCloneFrom; for _ in 1..10 { @@ -86,6 +96,12 @@ fn assign_to_uninit_mut_var(b: HasCloneFrom) { a = b.clone(); } +fn late_init_let_tuple() { + let (p, q): (String, String); + p = "ghi".to_string(); + q = p.clone(); +} + #[derive(Clone)] pub struct HasDeriveClone; @@ -208,6 +224,16 @@ fn owned_function_val(mut mut_thing: String, ref_str: &str) { ToOwned::clone_into(ref_str, &mut mut_thing); } +fn owned_method_macro() { + let mut s = String::from(""); + format!("{} {}", "hello", "world").clone_into(&mut s); +} + +fn owned_function_macro() { + let mut s = String::from(""); + ToOwned::clone_into(&format!("{} {}", "hello", "world"), &mut s); +} + struct FakeToOwned; impl FakeToOwned { /// This looks just like `ToOwned::to_owned` diff --git a/src/tools/clippy/tests/ui/assigning_clones.rs b/src/tools/clippy/tests/ui/assigning_clones.rs index 6f4da9f652c9..9699fed100c8 100644 --- a/src/tools/clippy/tests/ui/assigning_clones.rs +++ b/src/tools/clippy/tests/ui/assigning_clones.rs @@ -62,6 +62,16 @@ fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFr *mut_thing = (ref_thing + ref_thing).clone(); } +fn clone_method_macro() { + let mut s = String::from(""); + s = format!("{} {}", "hello", "world").clone(); +} + +fn clone_function_macro() { + let mut s = String::from(""); + s = Clone::clone(&format!("{} {}", "hello", "world")); +} + fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom { let mut a = HasCloneFrom; for _ in 1..10 { @@ -86,6 +96,12 @@ fn assign_to_uninit_mut_var(b: HasCloneFrom) { a = b.clone(); } +fn late_init_let_tuple() { + let (p, q): (String, String); + p = "ghi".to_string(); + q = p.clone(); +} + #[derive(Clone)] pub struct HasDeriveClone; @@ -208,6 +224,16 @@ fn owned_function_val(mut mut_thing: String, ref_str: &str) { mut_thing = ToOwned::to_owned(ref_str); } +fn owned_method_macro() { + let mut s = String::from(""); + s = format!("{} {}", "hello", "world").to_owned(); +} + +fn owned_function_macro() { + let mut s = String::from(""); + s = ToOwned::to_owned(&format!("{} {}", "hello", "world")); +} + struct FakeToOwned; impl FakeToOwned { /// This looks just like `ToOwned::to_owned` diff --git a/src/tools/clippy/tests/ui/assigning_clones.stderr b/src/tools/clippy/tests/ui/assigning_clones.stderr index 793927bd1cb1..a68516376abb 100644 --- a/src/tools/clippy/tests/ui/assigning_clones.stderr +++ b/src/tools/clippy/tests/ui/assigning_clones.stderr @@ -62,64 +62,88 @@ LL | *mut_thing = (ref_thing + ref_thing).clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing + ref_thing)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:68:9 + --> tests/ui/assigning_clones.rs:67:5 + | +LL | s = format!("{} {}", "hello", "world").clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `s.clone_from(&format!("{} {}", "hello", "world"))` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:72:5 + | +LL | s = Clone::clone(&format!("{} {}", "hello", "world")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut s, &format!("{} {}", "hello", "world"))` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:78:9 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:133:5 + --> tests/ui/assigning_clones.rs:149:5 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `Clone::clone()` may be inefficient - --> tests/ui/assigning_clones.rs:140:5 + --> tests/ui/assigning_clones.rs:156:5 | LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:141:5 + --> tests/ui/assigning_clones.rs:157:5 | LL | a = c.to_owned(); | ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `c.clone_into(&mut a)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:171:5 + --> tests/ui/assigning_clones.rs:187:5 | LL | *mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:175:5 + --> tests/ui/assigning_clones.rs:191:5 | LL | mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:196:5 + --> tests/ui/assigning_clones.rs:212:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:200:5 + --> tests/ui/assigning_clones.rs:216:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:204:5 + --> tests/ui/assigning_clones.rs:220:5 | LL | *mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:208:5 + --> tests/ui/assigning_clones.rs:224:5 | LL | mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)` -error: aborting due to 20 previous errors +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:229:5 + | +LL | s = format!("{} {}", "hello", "world").to_owned(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `format!("{} {}", "hello", "world").clone_into(&mut s)` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:234:5 + | +LL | s = ToOwned::to_owned(&format!("{} {}", "hello", "world")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(&format!("{} {}", "hello", "world"), &mut s)` + +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/author.stdout b/src/tools/clippy/tests/ui/author.stdout index d448db097a7e..eed704e82fe1 100644 --- a/src/tools/clippy/tests/ui/author.stdout +++ b/src/tools/clippy/tests/ui/author.stdout @@ -1,4 +1,4 @@ -if let StmtKind::Local(local) = stmt.kind +if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Cast(expr, cast_ty) = init.kind && let TyKind::Path(ref qpath) = cast_ty.kind diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout index 80b928dd6cb5..6bf48d5ba4ef 100644 --- a/src/tools/clippy/tests/ui/author/blocks.stdout +++ b/src/tools/clippy/tests/ui/author/blocks.stdout @@ -1,12 +1,12 @@ if let ExprKind::Block(block, None) = expr.kind && block.stmts.len() == 3 - && let StmtKind::Local(local) = block.stmts[0].kind + && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init && let ExprKind::Lit(ref lit) = init.kind && let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind && name.as_str() == "x" - && let StmtKind::Local(local1) = block.stmts[1].kind + && let StmtKind::Let(local1) = block.stmts[1].kind && let Some(init1) = local1.init && let ExprKind::Lit(ref lit1) = init1.kind && let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node @@ -22,7 +22,7 @@ if let ExprKind::Block(block, None) = expr.kind } if let ExprKind::Block(block, None) = expr.kind && block.stmts.len() == 1 - && let StmtKind::Local(local) = block.stmts[0].kind + && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind && let ExprKind::Path(ref qpath) = func.kind diff --git a/src/tools/clippy/tests/ui/author/call.stdout b/src/tools/clippy/tests/ui/author/call.stdout index f040f6330a64..59d4da490fe5 100644 --- a/src/tools/clippy/tests/ui/author/call.stdout +++ b/src/tools/clippy/tests/ui/author/call.stdout @@ -1,4 +1,4 @@ -if let StmtKind::Local(local) = stmt.kind +if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind && let ExprKind::Path(ref qpath) = func.kind diff --git a/src/tools/clippy/tests/ui/author/if.stdout b/src/tools/clippy/tests/ui/author/if.stdout index 5d79618820d8..a85dcddd3315 100644 --- a/src/tools/clippy/tests/ui/author/if.stdout +++ b/src/tools/clippy/tests/ui/author/if.stdout @@ -1,4 +1,4 @@ -if let StmtKind::Local(local) = stmt.kind +if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::If(cond, then, Some(else_expr)) = init.kind && let ExprKind::DropTemps(expr) = cond.kind diff --git a/src/tools/clippy/tests/ui/author/issue_3849.stdout b/src/tools/clippy/tests/ui/author/issue_3849.stdout index 32a3127b85a3..a5a8c0304ee4 100644 --- a/src/tools/clippy/tests/ui/author/issue_3849.stdout +++ b/src/tools/clippy/tests/ui/author/issue_3849.stdout @@ -1,4 +1,4 @@ -if let StmtKind::Local(local) = stmt.kind +if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Call(func, args) = init.kind && let ExprKind::Path(ref qpath) = func.kind diff --git a/src/tools/clippy/tests/ui/author/loop.stdout b/src/tools/clippy/tests/ui/author/loop.stdout index 631105a2238d..609d24910610 100644 --- a/src/tools/clippy/tests/ui/author/loop.stdout +++ b/src/tools/clippy/tests/ui/author/loop.stdout @@ -12,7 +12,7 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node && let ExprKind::Block(block, None) = body.kind && block.stmts.len() == 1 - && let StmtKind::Local(local) = block.stmts[0].kind + && let StmtKind::Let(local) = block.stmts[0].kind && let Some(init) = local.init && let ExprKind::Path(ref qpath1) = init.kind && match_qpath(qpath1, &["y"]) diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout index b90c830e0307..66caf382d897 100644 --- a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout +++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout @@ -1,4 +1,4 @@ -if let StmtKind::Local(local) = stmt.kind +if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Closure { capture_clause: CaptureBy::Ref, fn_decl: fn_decl, body: body_id, closure_kind: ClosureKind::Closure, .. } = init.kind && let FnRetTy::DefaultReturn(_) = fn_decl.output diff --git a/src/tools/clippy/tests/ui/author/matches.stdout b/src/tools/clippy/tests/ui/author/matches.stdout index 30e4a9b2560a..91b3b6f6877e 100644 --- a/src/tools/clippy/tests/ui/author/matches.stdout +++ b/src/tools/clippy/tests/ui/author/matches.stdout @@ -1,4 +1,4 @@ -if let StmtKind::Local(local) = stmt.kind +if let StmtKind::Let(local) = stmt.kind && let Some(init) = local.init && let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind && let ExprKind::Lit(ref lit) = scrutinee.kind @@ -16,7 +16,7 @@ if let StmtKind::Local(local) = stmt.kind && arms[1].guard.is_none() && let ExprKind::Block(block, None) = arms[1].body.kind && block.stmts.len() == 1 - && let StmtKind::Local(local1) = block.stmts[0].kind + && let StmtKind::Let(local1) = block.stmts[0].kind && let Some(init1) = local1.init && let ExprKind::Lit(ref lit4) = init1.kind && let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs index 79a95d775b11..4c3df4722690 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs @@ -169,3 +169,16 @@ pub fn derive_ignored_unit_pattern(_: TokenStream) -> TokenStream { } } } + +#[proc_macro_derive(NonCanonicalClone)] +pub fn non_canonical_clone_derive(_: TokenStream) -> TokenStream { + quote! { + struct NonCanonicalClone; + impl Clone for NonCanonicalClone { + fn clone(&self) -> Self { + todo!() + } + } + impl Copy for NonCanonicalClone {} + } +} diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs index 6e6919cd295c..ed7412f7c407 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs @@ -58,7 +58,7 @@ fn group_with_span(delimiter: Delimiter, stream: TokenStream, span: Span) -> Gro const ESCAPE_CHAR: char = '$'; /// Takes a single token followed by a sequence of tokens. Returns the sequence of tokens with their -/// span set to that of the first token. Tokens may be escaped with either `#ident` or `#(tokens)`. +/// span set to that of the first token. Tokens may be escaped with either `$ident` or `$(tokens)`. #[proc_macro] pub fn with_span(input: TokenStream) -> TokenStream { let mut iter = input.into_iter(); @@ -72,7 +72,7 @@ pub fn with_span(input: TokenStream) -> TokenStream { } /// Takes a sequence of tokens and return the tokens with the span set such that they appear to be -/// from an external macro. Tokens may be escaped with either `#ident` or `#(tokens)`. +/// from an external macro. Tokens may be escaped with either `$ident` or `$(tokens)`. #[proc_macro] pub fn external(input: TokenStream) -> TokenStream { let mut res = TokenStream::new(); @@ -84,7 +84,7 @@ pub fn external(input: TokenStream) -> TokenStream { } /// Copies all the tokens, replacing all their spans with the given span. Tokens can be escaped -/// either by `#ident` or `#(tokens)`. +/// either by `$ident` or `$(tokens)`. fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Result<()> { while let Some(tt) = input.next() { match tt { diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed index a2da5f9c5fb9..af8e65270d09 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed @@ -117,4 +117,17 @@ mod issue_12016 { } } +fn in_closure() { + let v = vec![1, 2, 3]; + if v.into_iter() + .filter(|x| { + let y = x + 1; + y > 3 + }) + .any(|x| x == 5) + { + println!("contains 4!"); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.rs b/src/tools/clippy/tests/ui/blocks_in_conditions.rs index 608ca4cf267f..6adae951a290 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.rs +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.rs @@ -117,4 +117,17 @@ mod issue_12016 { } } +fn in_closure() { + let v = vec![1, 2, 3]; + if v.into_iter() + .filter(|x| { + let y = x + 1; + y > 3 + }) + .any(|x| x == 5) + { + println!("contains 4!"); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions_closure.rs b/src/tools/clippy/tests/ui/blocks_in_conditions_closure.rs deleted file mode 100644 index db31e4ae1a9a..000000000000 --- a/src/tools/clippy/tests/ui/blocks_in_conditions_closure.rs +++ /dev/null @@ -1,89 +0,0 @@ -#![warn(clippy::blocks_in_conditions)] -#![allow( - unused, - clippy::let_and_return, - clippy::needless_if, - clippy::unnecessary_literal_unwrap -)] - -fn predicate bool, T>(pfn: F, val: T) -> bool { - pfn(val) -} - -fn pred_test() { - let v = 3; - let sky = "blue"; - // This is a sneaky case, where the block isn't directly in the condition, - // but is actually inside a closure that the condition is using. - // The same principle applies -- add some extra expressions to make sure - // linter isn't confused by them. - if v == 3 - && sky == "blue" - && predicate( - |x| { - //~^ ERROR: in an `if` condition, avoid complex blocks or closures with blocks - //~| NOTE: `-D clippy::blocks-in-conditions` implied by `-D warnings` - let target = 3; - x == target - }, - v, - ) - {} - - if predicate( - |x| { - //~^ ERROR: in an `if` condition, avoid complex blocks or closures with blocks; in - let target = 3; - x == target - }, - v, - ) {} -} - -fn closure_without_block() { - if predicate(|x| x == 3, 6) {} -} - -fn macro_in_closure() { - let option = Some(true); - - if option.unwrap_or_else(|| unimplemented!()) { - unimplemented!() - } -} - -fn closure(_: impl FnMut()) -> bool { - true -} - -fn function_with_empty_closure() { - if closure(|| {}) {} -} - -// issue #11814 -fn match_with_pred() { - let v = 3; - match Some(predicate( - |x| { - //~^ ERROR: in a `match` scrutinee, avoid complex blocks or closures with blocks - let target = 3; - x == target - }, - v, - )) { - Some(true) => 1, - Some(false) => 2, - None => 3, - }; -} - -#[rustfmt::skip] -fn main() { - let mut range = 0..10; - range.all(|i| {i < 10} ); - - let v = vec![1, 2, 3]; - if v.into_iter().any(|x| {x == 4}) { - println!("contains 4!"); - } -} diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions_closure.stderr b/src/tools/clippy/tests/ui/blocks_in_conditions_closure.stderr deleted file mode 100644 index 2faae680ec02..000000000000 --- a/src/tools/clippy/tests/ui/blocks_in_conditions_closure.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> tests/ui/blocks_in_conditions_closure.rs:23:17 - | -LL | |x| { - | _________________^ -LL | | -LL | | -LL | | let target = 3; -LL | | x == target -LL | | }, - | |_____________^ - | - = note: `-D clippy::blocks-in-conditions` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::blocks_in_conditions)]` - -error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> tests/ui/blocks_in_conditions_closure.rs:34:13 - | -LL | |x| { - | _____________^ -LL | | -LL | | let target = 3; -LL | | x == target -LL | | }, - | |_________^ - -error: in a `match` scrutinee, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> tests/ui/blocks_in_conditions_closure.rs:67:13 - | -LL | |x| { - | _____________^ -LL | | -LL | | let target = 3; -LL | | x == target -LL | | }, - | |_________^ - -error: aborting due to 3 previous errors - diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed index 84dac431169a..05d5b3d10eaf 100644 --- a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed +++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed @@ -16,7 +16,7 @@ fn foo( fn skip_on_statements() { #[rustfmt::skip] - { 5+3; } + 5+3; } #[rustfmt::skip] @@ -33,11 +33,11 @@ mod foo { #[clippy::msrv = "1.29"] fn msrv_1_29() { #[cfg_attr(rustfmt, rustfmt::skip)] - { 1+29; } + 1+29; } #[clippy::msrv = "1.30"] fn msrv_1_30() { #[rustfmt::skip] - { 1+30; } + 1+30; } diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs index 4ab5c70e13b5..bc29e20210e8 100644 --- a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs +++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs @@ -16,7 +16,7 @@ fn foo( fn skip_on_statements() { #[cfg_attr(rustfmt, rustfmt::skip)] - { 5+3; } + 5+3; } #[cfg_attr(rustfmt, rustfmt_skip)] @@ -33,11 +33,11 @@ mod foo { #[clippy::msrv = "1.29"] fn msrv_1_29() { #[cfg_attr(rustfmt, rustfmt::skip)] - { 1+29; } + 1+29; } #[clippy::msrv = "1.30"] fn msrv_1_30() { #[cfg_attr(rustfmt, rustfmt::skip)] - { 1+30; } + 1+30; } diff --git a/src/tools/clippy/tests/ui/cfg_features.fixed b/src/tools/clippy/tests/ui/cfg_features.fixed deleted file mode 100644 index 0fe38f169f9c..000000000000 --- a/src/tools/clippy/tests/ui/cfg_features.fixed +++ /dev/null @@ -1,29 +0,0 @@ -#![warn(clippy::maybe_misused_cfg)] - -fn main() { - #[cfg(feature = "not-really-a-feature")] - //~^ ERROR: 'feature' may be misspelled as 'features' - //~| NOTE: `-D clippy::maybe-misused-cfg` implied by `-D warnings` - let _ = 1 + 2; - - #[cfg(all(feature = "right", feature = "wrong"))] - //~^ ERROR: 'feature' may be misspelled as 'features' - let _ = 1 + 2; - - #[cfg(all(feature = "wrong1", any(feature = "right", feature = "wrong2", feature, features)))] - //~^ ERROR: 'feature' may be misspelled as 'features' - //~| ERROR: 'feature' may be misspelled as 'features' - let _ = 1 + 2; - - #[cfg(test)] - //~^ ERROR: 'test' may be misspelled as 'tests' - let _ = 2; - #[cfg(test)] - //~^ ERROR: 'test' may be misspelled as 'Test' - let _ = 2; - - #[cfg(all(test, test))] - //~^ ERROR: 'test' may be misspelled as 'tests' - //~| ERROR: 'test' may be misspelled as 'Test' - let _ = 2; -} diff --git a/src/tools/clippy/tests/ui/cfg_features.rs b/src/tools/clippy/tests/ui/cfg_features.rs deleted file mode 100644 index 9c0db035eac4..000000000000 --- a/src/tools/clippy/tests/ui/cfg_features.rs +++ /dev/null @@ -1,29 +0,0 @@ -#![warn(clippy::maybe_misused_cfg)] - -fn main() { - #[cfg(features = "not-really-a-feature")] - //~^ ERROR: 'feature' may be misspelled as 'features' - //~| NOTE: `-D clippy::maybe-misused-cfg` implied by `-D warnings` - let _ = 1 + 2; - - #[cfg(all(feature = "right", features = "wrong"))] - //~^ ERROR: 'feature' may be misspelled as 'features' - let _ = 1 + 2; - - #[cfg(all(features = "wrong1", any(feature = "right", features = "wrong2", feature, features)))] - //~^ ERROR: 'feature' may be misspelled as 'features' - //~| ERROR: 'feature' may be misspelled as 'features' - let _ = 1 + 2; - - #[cfg(tests)] - //~^ ERROR: 'test' may be misspelled as 'tests' - let _ = 2; - #[cfg(Test)] - //~^ ERROR: 'test' may be misspelled as 'Test' - let _ = 2; - - #[cfg(all(tests, Test))] - //~^ ERROR: 'test' may be misspelled as 'tests' - //~| ERROR: 'test' may be misspelled as 'Test' - let _ = 2; -} diff --git a/src/tools/clippy/tests/ui/cfg_features.stderr b/src/tools/clippy/tests/ui/cfg_features.stderr deleted file mode 100644 index d576271f1a29..000000000000 --- a/src/tools/clippy/tests/ui/cfg_features.stderr +++ /dev/null @@ -1,53 +0,0 @@ -error: 'feature' may be misspelled as 'features' - --> tests/ui/cfg_features.rs:4:11 - | -LL | #[cfg(features = "not-really-a-feature")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `feature = "not-really-a-feature"` - | - = note: `-D clippy::maybe-misused-cfg` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::maybe_misused_cfg)]` - -error: 'feature' may be misspelled as 'features' - --> tests/ui/cfg_features.rs:9:34 - | -LL | #[cfg(all(feature = "right", features = "wrong"))] - | ^^^^^^^^^^^^^^^^^^ help: did you mean: `feature = "wrong"` - -error: 'feature' may be misspelled as 'features' - --> tests/ui/cfg_features.rs:13:15 - | -LL | #[cfg(all(features = "wrong1", any(feature = "right", features = "wrong2", feature, features)))] - | ^^^^^^^^^^^^^^^^^^^ help: did you mean: `feature = "wrong1"` - -error: 'feature' may be misspelled as 'features' - --> tests/ui/cfg_features.rs:13:59 - | -LL | #[cfg(all(features = "wrong1", any(feature = "right", features = "wrong2", feature, features)))] - | ^^^^^^^^^^^^^^^^^^^ help: did you mean: `feature = "wrong2"` - -error: 'test' may be misspelled as 'tests' - --> tests/ui/cfg_features.rs:18:11 - | -LL | #[cfg(tests)] - | ^^^^^ help: did you mean: `test` - -error: 'test' may be misspelled as 'Test' - --> tests/ui/cfg_features.rs:21:11 - | -LL | #[cfg(Test)] - | ^^^^ help: did you mean: `test` - -error: 'test' may be misspelled as 'tests' - --> tests/ui/cfg_features.rs:25:15 - | -LL | #[cfg(all(tests, Test))] - | ^^^^^ help: did you mean: `test` - -error: 'test' may be misspelled as 'Test' - --> tests/ui/cfg_features.rs:25:22 - | -LL | #[cfg(all(tests, Test))] - | ^^^^ help: did you mean: `test` - -error: aborting due to 8 previous errors - diff --git a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr index ce3b9495eb1e..cd2031af1c6d 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr @@ -4,9 +4,7 @@ error[E0412]: cannot find type `PhantomData` in this scope LL | _n: PhantomData, | ^^^^^^^^^^^ not found in this scope | -help: consider importing one of these items - | -LL + use core::marker::PhantomData; +help: consider importing this struct | LL + use std::marker::PhantomData; | diff --git a/src/tools/clippy/tests/ui/crashes/ice-9445.rs b/src/tools/clippy/tests/ui/crashes/ice-9445.rs index b6afbd33c79f..c67b22f6f8c4 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-9445.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-9445.rs @@ -1,5 +1,3 @@ const UNINIT: core::mem::MaybeUninit> = core::mem::MaybeUninit::uninit(); -//~^ ERROR: a `const` item should never be interior mutable -//~| NOTE: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-9445.stderr b/src/tools/clippy/tests/ui/crashes/ice-9445.stderr index d6957e9549d6..76689cd6f5c2 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-9445.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-9445.stderr @@ -1,11 +1,10 @@ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/crashes/ice-9445.rs:1:1 | LL | const UNINIT: core::mem::MaybeUninit> = core::mem::MaybeUninit::uninit(); - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` diff --git a/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs deleted file mode 100644 index 92821b6ecbba..000000000000 --- a/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs +++ /dev/null @@ -1,33 +0,0 @@ -#![deny(clippy::mut_mut, clippy::zero_ptr)] -#![allow(dead_code)] - -// FIXME: compiletest + extern crates doesn't work together. To make this test work, it would need -// the following three lines and the lazy_static crate. -// -// #[macro_use] -// extern crate lazy_static; -// use std::collections::HashMap; - -/// ensure that we don't suggest `is_null` inside constants -/// FIXME: once const fn is stable, suggest these functions again in constants - -const BAA: *const i32 = 0 as *const i32; -static mut BAR: *const i32 = BAA; -static mut FOO: *const i32 = 0 as *const i32; - -#[allow(unused_variables, unused_mut)] -fn main() { - /* - lazy_static! { - static ref MUT_MAP : HashMap = { - let mut m = HashMap::new(); - m.insert(0, "zero"); - m - }; - static ref MUT_COUNT : usize = MUT_MAP.len(); - } - assert_eq!(*MUT_COUNT, 1); - */ - // FIXME: don't lint in array length, requires `check_body` - //let _ = [""; (42.0 < f32::NAN) as usize]; -} diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr index 6c0dce6b5eaf..22329172c3af 100644 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/enums.stderr @@ -1,87 +1,84 @@ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:12:1 | LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:23:1 | LL | const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:45:1 | -LL | const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { - | ^---- - | | - | _make this a static item (maybe with lazy_static) - | | +LL | / const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { LL | | LL | | outer: NestedOuter::NestedInner(NestedInner { LL | | inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), LL | | }), LL | | }; | |__^ + | + = help: consider making this a static item -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:60:5 | LL | const TO_BE_UNFROZEN_VARIANT: OptionalCell; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:61:5 | LL | const TO_BE_FROZEN_VARIANT: OptionalCell; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:64:5 | LL | const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:90:5 | LL | const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:102:5 | LL | const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:105:5 | LL | const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:111:5 | LL | const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:118:5 | LL | / const UNFROZEN_VARIANT: BothOfCellAndGeneric = LL | | BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); | |____________________________________________________________________^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/enums.rs:120:5 | LL | const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr index 9dba0c952214..1f2b9561ce50 100644 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.stderr @@ -1,31 +1,30 @@ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/others.rs:9:1 | LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: consider making this a static item = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/others.rs:10:1 | LL | const CELL: Cell = Cell::new(6); - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/others.rs:11:1 | LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider making this a static item -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/others.rs:16:9 | LL | const $name: $ty = $e; @@ -36,7 +35,7 @@ LL | declare_const!(_ONCE: Once = Once::new()); | = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/others.rs:44:13 | LL | const _BAZ: Cell = Cell::new(0); diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr index 1d1e9e2002fa..4a793d985e5e 100644 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/traits.stderr @@ -1,4 +1,4 @@ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:16:5 | LL | const ATOMIC: AtomicUsize; @@ -7,7 +7,7 @@ LL | const ATOMIC: AtomicUsize; = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]` -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:9:9 | LL | const $name: $ty = $e; @@ -18,67 +18,67 @@ LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); | = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info) -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:44:5 | LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:69:5 | LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:70:5 | LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:89:5 | LL | const BOUNDED: T::ToBeBounded; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:117:5 | LL | const SELF: Self = AtomicUsize::new(17); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:118:5 | LL | const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:124:5 | LL | const DIRECT: Cell; | ^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:125:5 | LL | const INDIRECT: Cell<*const T>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:129:5 | LL | const DIRECT: Cell = Cell::new(T::DEFAULT); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:141:5 | LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable +error: a `const` item should not be interior mutable --> tests/ui/declare_interior_mutable_const/traits.rs:147:5 | LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); diff --git a/src/tools/clippy/tests/ui/deprecated.rs b/src/tools/clippy/tests/ui/deprecated.rs index 07270bd76362..d3c34fb37167 100644 --- a/src/tools/clippy/tests/ui/deprecated.rs +++ b/src/tools/clippy/tests/ui/deprecated.rs @@ -18,5 +18,7 @@ #![warn(clippy::filter_map)] #![warn(clippy::pub_enum_variant_names)] #![warn(clippy::wrong_pub_self_convention)] +#![warn(clippy::maybe_misused_cfg)] +#![warn(clippy::mismatched_target_os)] fn main() {} diff --git a/src/tools/clippy/tests/ui/deprecated.stderr b/src/tools/clippy/tests/ui/deprecated.stderr index a9cf04bea525..49b90c70c06e 100644 --- a/src/tools/clippy/tests/ui/deprecated.stderr +++ b/src/tools/clippy/tests/ui/deprecated.stderr @@ -97,5 +97,17 @@ error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid LL | #![warn(clippy::wrong_pub_self_convention)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors +error: lint `clippy::maybe_misused_cfg` has been removed: this lint has been replaced by `unexpected_cfgs` + --> tests/ui/deprecated.rs:21:9 + | +LL | #![warn(clippy::maybe_misused_cfg)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::mismatched_target_os` has been removed: this lint has been replaced by `unexpected_cfgs` + --> tests/ui/deprecated.rs:22:9 + | +LL | #![warn(clippy::mismatched_target_os)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/deref_addrof.fixed b/src/tools/clippy/tests/ui/deref_addrof.fixed index aa1cf19b76f9..b6278c6ca8a5 100644 --- a/src/tools/clippy/tests/ui/deref_addrof.fixed +++ b/src/tools/clippy/tests/ui/deref_addrof.fixed @@ -43,6 +43,10 @@ fn main() { let b = *aref; let _ = unsafe { *core::ptr::addr_of!(a) }; + + let _repeat = [0; 64]; + // do NOT lint for array as sematic differences with/out `*&`. + let _arr = *&[0, 1, 2, 3, 4]; } #[derive(Copy, Clone)] diff --git a/src/tools/clippy/tests/ui/deref_addrof.rs b/src/tools/clippy/tests/ui/deref_addrof.rs index 38796aef390e..572b0fdb1027 100644 --- a/src/tools/clippy/tests/ui/deref_addrof.rs +++ b/src/tools/clippy/tests/ui/deref_addrof.rs @@ -43,6 +43,10 @@ fn main() { let b = **&aref; let _ = unsafe { *core::ptr::addr_of!(a) }; + + let _repeat = *&[0; 64]; + // do NOT lint for array as sematic differences with/out `*&`. + let _arr = *&[0, 1, 2, 3, 4]; } #[derive(Copy, Clone)] diff --git a/src/tools/clippy/tests/ui/deref_addrof.stderr b/src/tools/clippy/tests/ui/deref_addrof.stderr index 5e3cb417aa0e..20069f746c81 100644 --- a/src/tools/clippy/tests/ui/deref_addrof.stderr +++ b/src/tools/clippy/tests/ui/deref_addrof.stderr @@ -50,7 +50,13 @@ LL | let b = **&aref; | ^^^^^^ help: try: `aref` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:53:17 + --> tests/ui/deref_addrof.rs:47:19 + | +LL | let _repeat = *&[0; 64]; + | ^^^^^^^^^ help: try: `[0; 64]` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:57:17 | LL | inline!(*& $(@expr self)) | ^^^^^^^^^^^^^^^^ help: try: `$(@expr self)` @@ -58,12 +64,12 @@ LL | inline!(*& $(@expr self)) = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:57:17 + --> tests/ui/deref_addrof.rs:61:17 | LL | inline!(*&mut $(@expr self)) | ^^^^^^^^^^^^^^^^^^^ help: try: `$(@expr self)` | = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed index 6f42487bbf49..eb93eb8e8ed4 100644 --- a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed +++ b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed @@ -1,3 +1,4 @@ +#![feature(lint_reasons)] #![allow(unused)] #![warn(clippy::derive_partial_eq_without_eq)] @@ -14,6 +15,22 @@ pub struct MissingEq { bar: String, } +// Check that we honor the `allow` attribute +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, PartialEq)] +pub struct AllowedMissingEq { + foo: u32, + bar: String, +} + +// Check that we honor the `expect` attribute +#[expect(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, PartialEq)] +pub struct ExpectedMissingEq { + foo: u32, + bar: String, +} + // Eq is derived #[derive(PartialEq, Eq)] pub struct NotMissingEq { diff --git a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs index 24f687c6c9db..42dc435bdd52 100644 --- a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs +++ b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs @@ -1,3 +1,4 @@ +#![feature(lint_reasons)] #![allow(unused)] #![warn(clippy::derive_partial_eq_without_eq)] @@ -14,6 +15,22 @@ pub struct MissingEq { bar: String, } +// Check that we honor the `allow` attribute +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, PartialEq)] +pub struct AllowedMissingEq { + foo: u32, + bar: String, +} + +// Check that we honor the `expect` attribute +#[expect(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, PartialEq)] +pub struct ExpectedMissingEq { + foo: u32, + bar: String, +} + // Eq is derived #[derive(PartialEq, Eq)] pub struct NotMissingEq { diff --git a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.stderr b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.stderr index 3d92112dc36d..29cd7da6b77d 100644 --- a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.stderr +++ b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.stderr @@ -1,5 +1,5 @@ error: you are deriving `PartialEq` and can implement `Eq` - --> tests/ui/derive_partial_eq_without_eq.rs:11:17 + --> tests/ui/derive_partial_eq_without_eq.rs:12:17 | LL | #[derive(Debug, PartialEq)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` @@ -8,73 +8,73 @@ LL | #[derive(Debug, PartialEq)] = help: to override `-D warnings` add `#[allow(clippy::derive_partial_eq_without_eq)]` error: you are deriving `PartialEq` and can implement `Eq` - --> tests/ui/derive_partial_eq_without_eq.rs:53:10 + --> tests/ui/derive_partial_eq_without_eq.rs:70:10 | LL | #[derive(PartialEq)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` error: you are deriving `PartialEq` and can implement `Eq` - --> tests/ui/derive_partial_eq_without_eq.rs:59:10 + --> tests/ui/derive_partial_eq_without_eq.rs:76:10 | LL | #[derive(PartialEq)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` error: you are deriving `PartialEq` and can implement `Eq` - --> tests/ui/derive_partial_eq_without_eq.rs:65:10 + --> tests/ui/derive_partial_eq_without_eq.rs:82:10 | LL | #[derive(PartialEq)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` error: you are deriving `PartialEq` and can implement `Eq` - --> tests/ui/derive_partial_eq_without_eq.rs:68:10 + --> tests/ui/derive_partial_eq_without_eq.rs:85:10 | LL | #[derive(PartialEq)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` error: you are deriving `PartialEq` and can implement `Eq` - --> tests/ui/derive_partial_eq_without_eq.rs:74:10 + --> tests/ui/derive_partial_eq_without_eq.rs:91:10 | LL | #[derive(PartialEq)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` error: you are deriving `PartialEq` and can implement `Eq` - --> tests/ui/derive_partial_eq_without_eq.rs:80:10 + --> tests/ui/derive_partial_eq_without_eq.rs:97:10 | LL | #[derive(PartialEq)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` error: you are deriving `PartialEq` and can implement `Eq` - --> tests/ui/derive_partial_eq_without_eq.rs:93:17 + --> tests/ui/derive_partial_eq_without_eq.rs:110:17 | LL | #[derive(Debug, PartialEq, Clone)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` error: you are deriving `PartialEq` and can implement `Eq` - --> tests/ui/derive_partial_eq_without_eq.rs:96:10 + --> tests/ui/derive_partial_eq_without_eq.rs:113:10 | LL | #[derive(PartialEq)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` error: you are deriving `PartialEq` and can implement `Eq` - --> tests/ui/derive_partial_eq_without_eq.rs:103:14 + --> tests/ui/derive_partial_eq_without_eq.rs:120:14 | LL | #[derive(PartialEq)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` error: you are deriving `PartialEq` and can implement `Eq` - --> tests/ui/derive_partial_eq_without_eq.rs:106:14 + --> tests/ui/derive_partial_eq_without_eq.rs:123:14 | LL | #[derive(PartialEq)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` error: you are deriving `PartialEq` and can implement `Eq` - --> tests/ui/derive_partial_eq_without_eq.rs:166:14 + --> tests/ui/derive_partial_eq_without_eq.rs:183:14 | LL | #[derive(PartialEq)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` error: you are deriving `PartialEq` and can implement `Eq` - --> tests/ui/derive_partial_eq_without_eq.rs:174:14 + --> tests/ui/derive_partial_eq_without_eq.rs:191:14 | LL | #[derive(PartialEq)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed index 7e22c847b1bc..84673f1f43fd 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed @@ -240,3 +240,6 @@ extern { /// `foo()` fn in_extern(); } + +/// +fn check_autofix_for_base_urls() {} diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs index 3e2cb0df54b0..4d017a99e0fb 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs @@ -240,3 +240,6 @@ extern { /// foo() fn in_extern(); } + +/// https://github.com/rust-lang/rust-clippy/pull/12836 +fn check_autofix_for_base_urls() {} diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr index cd2228c47e35..a9263f62d38d 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr @@ -363,5 +363,11 @@ help: try LL | /// `foo()` | ~~~~~~~ -error: aborting due to 33 previous errors +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> tests/ui/doc/doc-fixable.rs:244:5 + | +LL | /// https://github.com/rust-lang/rust-clippy/pull/12836 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `` + +error: aborting due to 34 previous errors diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.fixed b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.fixed new file mode 100644 index 000000000000..9877991f183a --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.fixed @@ -0,0 +1,47 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// > blockquote with +/// > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn first() {} + +/// > blockquote with no +/// > lazy continuation +fn first_nowarn() {} + +/// > blockquote with no +/// +/// lazy continuation +fn two_nowarn() {} + +/// > nest here +/// > +/// > > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn two() {} + +/// > nest here +/// > +/// > > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn three() {} + +/// > * > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four() {} + +/// > * > nest here +/// > > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four_point_1() {} + +/// * > nest here lazy continuation +fn five() {} + +/// 1. > nest here +/// > lazy continuation (this results in strange indentation, but still works) +//~^ ERROR: doc quote missing `>` marker +fn six() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.rs b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.rs new file mode 100644 index 000000000000..587b2fdd533c --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.rs @@ -0,0 +1,47 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// > blockquote with +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn first() {} + +/// > blockquote with no +/// > lazy continuation +fn first_nowarn() {} + +/// > blockquote with no +/// +/// lazy continuation +fn two_nowarn() {} + +/// > nest here +/// > +/// > > nest here +/// > lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn two() {} + +/// > nest here +/// > +/// > > nest here +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn three() {} + +/// > * > nest here +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four() {} + +/// > * > nest here +/// lazy continuation +//~^ ERROR: doc quote missing `>` marker +fn four_point_1() {} + +/// * > nest here lazy continuation +fn five() {} + +/// 1. > nest here +/// lazy continuation (this results in strange indentation, but still works) +//~^ ERROR: doc quote missing `>` marker +fn six() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.stderr b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.stderr new file mode 100644 index 000000000000..975184a01c3f --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.stderr @@ -0,0 +1,76 @@ +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:4:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` + = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]` +help: add markers to start of line + | +LL | /// > lazy continuation + | + + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:20:5 + | +LL | /// > lazy continuation + | ^^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | + + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:27:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | +++ + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:32:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | +++++++ + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:37:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > > lazy continuation + | +++++ + +error: doc quote missing `>` marker + --> tests/ui/doc/doc_lazy_blockquote.rs:45:5 + | +LL | /// lazy continuation (this results in strange indentation, but still works) + | ^ + | + = help: if this not intended to be a quote at all, escape it with `\>` +help: add markers to start of line + | +LL | /// > lazy continuation (this results in strange indentation, but still works) + | + + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed new file mode 100644 index 000000000000..409e6b0bc227 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed @@ -0,0 +1,77 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// 1. nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn one() {} + +/// 1. first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn two() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn three() {} + +/// - first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn four() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn five() {} + +/// - - first line +/// this will warn on the lazy continuation +//~^ ERROR: doc list item missing indentation +/// and so should this +//~^ ERROR: doc list item missing indentation +fn six() {} + +/// - - first line +/// +/// this is not a lazy continuation +fn seven() {} + +#[rustfmt::skip] +// https://github.com/rust-lang/rust-clippy/pull/12770#issuecomment-2118601768 +/// Returns a list of ProtocolDescriptors from a Serde JSON input. +/// +/// Defined Protocol Identifiers for the Protocol Descriptor +/// We intentionally omit deprecated profile identifiers. +/// From Bluetooth Assigned Numbers: +/// https://www.bluetooth.com/specifications/assigned-numbers/service-discovery +/// +/// # Arguments +/// * `protocol_descriptors`: A Json Representation of the ProtocolDescriptors +/// to set up. Example: +/// 'protocol_descriptors': [ +//~^ ERROR: doc list item missing indentation +/// { +/// 'protocol': 25, # u64 Representation of ProtocolIdentifier::AVDTP +/// 'params': [ +/// { +/// 'data': 0x0103 # to indicate 1.3 +/// }, +/// { +/// 'data': 0x0105 # to indicate 1.5 +/// } +/// ] +/// }, +/// { +/// 'protocol': 1, # u64 Representation of ProtocolIdentifier::SDP +/// 'params': [{ +/// 'data': 0x0019 +/// }] +/// } +/// ] +//~^ ERROR: doc list item missing indentation +fn eight() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs b/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs new file mode 100644 index 000000000000..30ab448a1130 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs @@ -0,0 +1,77 @@ +#![warn(clippy::doc_lazy_continuation)] + +/// 1. nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn one() {} + +/// 1. first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn two() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn three() {} + +/// - first line +/// lazy list continuations don't make warnings with this lint +//~^ ERROR: doc list item missing indentation +/// because they don't have the +//~^ ERROR: doc list item missing indentation +fn four() {} + +/// - nest here +/// lazy continuation +//~^ ERROR: doc list item missing indentation +fn five() {} + +/// - - first line +/// this will warn on the lazy continuation +//~^ ERROR: doc list item missing indentation +/// and so should this +//~^ ERROR: doc list item missing indentation +fn six() {} + +/// - - first line +/// +/// this is not a lazy continuation +fn seven() {} + +#[rustfmt::skip] +// https://github.com/rust-lang/rust-clippy/pull/12770#issuecomment-2118601768 +/// Returns a list of ProtocolDescriptors from a Serde JSON input. +/// +/// Defined Protocol Identifiers for the Protocol Descriptor +/// We intentionally omit deprecated profile identifiers. +/// From Bluetooth Assigned Numbers: +/// https://www.bluetooth.com/specifications/assigned-numbers/service-discovery +/// +/// # Arguments +/// * `protocol_descriptors`: A Json Representation of the ProtocolDescriptors +/// to set up. Example: +/// 'protocol_descriptors': [ +//~^ ERROR: doc list item missing indentation +/// { +/// 'protocol': 25, # u64 Representation of ProtocolIdentifier::AVDTP +/// 'params': [ +/// { +/// 'data': 0x0103 # to indicate 1.3 +/// }, +/// { +/// 'data': 0x0105 # to indicate 1.5 +/// } +/// ] +/// }, +/// { +/// 'protocol': 1, # u64 Representation of ProtocolIdentifier::SDP +/// 'params': [{ +/// 'data': 0x0019 +/// }] +/// } +/// ] +//~^ ERROR: doc list item missing indentation +fn eight() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr new file mode 100644 index 000000000000..ddfdc49340c4 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr @@ -0,0 +1,136 @@ +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:4:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line + = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]` +help: indent this line + | +LL | /// lazy continuation + | +++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:9:5 + | +LL | /// lazy list continuations don't make warnings with this lint + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy list continuations don't make warnings with this lint + | +++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:11:5 + | +LL | /// because they don't have the + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// because they don't have the + | +++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:16:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy continuation + | ++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:21:5 + | +LL | /// lazy list continuations don't make warnings with this lint + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy list continuations don't make warnings with this lint + | ++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:23:5 + | +LL | /// because they don't have the + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// because they don't have the + | ++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:28:5 + | +LL | /// lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// lazy continuation + | ++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:33:5 + | +LL | /// this will warn on the lazy continuation + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// this will warn on the lazy continuation + | ++++++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:35:5 + | +LL | /// and so should this + | ^^^^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// and so should this + | ++ + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:56:5 + | +LL | /// 'protocol_descriptors': [ + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// 'protocol_descriptors': [ + | + + +error: doc list item missing indentation + --> tests/ui/doc/doc_lazy_list.rs:75:5 + | +LL | /// ] + | ^ + | + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line + | +LL | /// ] + | + + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/doc/issue_12795.fixed b/src/tools/clippy/tests/ui/doc/issue_12795.fixed new file mode 100644 index 000000000000..ade23bf975c2 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/issue_12795.fixed @@ -0,0 +1,9 @@ +#![warn(clippy::doc_markdown)] + +//! A comment with `a_b(x)` and `a_c` in it and (`a_b((c))` ) too and (maybe `a_b((c))`) +//~^ ERROR: item in documentation is missing backticks +//~| ERROR: item in documentation is missing backticks +//~| ERROR: item in documentation is missing backticks +//~| ERROR: item in documentation is missing backticks + +pub fn main() {} diff --git a/src/tools/clippy/tests/ui/doc/issue_12795.rs b/src/tools/clippy/tests/ui/doc/issue_12795.rs new file mode 100644 index 000000000000..6d94a07e3036 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/issue_12795.rs @@ -0,0 +1,9 @@ +#![warn(clippy::doc_markdown)] + +//! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b((c))) +//~^ ERROR: item in documentation is missing backticks +//~| ERROR: item in documentation is missing backticks +//~| ERROR: item in documentation is missing backticks +//~| ERROR: item in documentation is missing backticks + +pub fn main() {} diff --git a/src/tools/clippy/tests/ui/doc/issue_12795.stderr b/src/tools/clippy/tests/ui/doc/issue_12795.stderr new file mode 100644 index 000000000000..5700145ec8f7 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/issue_12795.stderr @@ -0,0 +1,48 @@ +error: item in documentation is missing backticks + --> tests/ui/doc/issue_12795.rs:3:20 + | +LL | //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b((c))) + | ^^^^^^ + | + = note: `-D clippy::doc-markdown` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]` +help: try + | +LL | //! A comment with `a_b(x)` and a_c in it and (a_b((c)) ) too and (maybe a_b((c))) + | ~~~~~~~~ + +error: item in documentation is missing backticks + --> tests/ui/doc/issue_12795.rs:3:31 + | +LL | //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b((c))) + | ^^^ + | +help: try + | +LL | //! A comment with a_b(x) and `a_c` in it and (a_b((c)) ) too and (maybe a_b((c))) + | ~~~~~ + +error: item in documentation is missing backticks + --> tests/ui/doc/issue_12795.rs:3:46 + | +LL | //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b((c))) + | ^^^^^^^^ + | +help: try + | +LL | //! A comment with a_b(x) and a_c in it and (`a_b((c))` ) too and (maybe a_b((c))) + | ~~~~~~~~~~ + +error: item in documentation is missing backticks + --> tests/ui/doc/issue_12795.rs:3:72 + | +LL | //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b((c))) + | ^^^^^^^^ + | +help: try + | +LL | //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe `a_b((c))`) + | ~~~~~~~~~~ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/doc_unsafe.stderr b/src/tools/clippy/tests/ui/doc_unsafe.stderr index 4fcbe716951f..929afbceb879 100644 --- a/src/tools/clippy/tests/ui/doc_unsafe.stderr +++ b/src/tools/clippy/tests/ui/doc_unsafe.stderr @@ -1,4 +1,4 @@ -error: unsafe function's docs miss `# Safety` section +error: unsafe function's docs are missing a `# Safety` section --> tests/ui/doc_unsafe.rs:9:1 | LL | pub unsafe fn destroy_the_planet() { @@ -7,13 +7,13 @@ LL | pub unsafe fn destroy_the_planet() { = note: `-D clippy::missing-safety-doc` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::missing_safety_doc)]` -error: unsafe function's docs miss `# Safety` section +error: unsafe function's docs are missing a `# Safety` section --> tests/ui/doc_unsafe.rs:32:5 | LL | pub unsafe fn republished() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: unsafe function's docs miss `# Safety` section +error: unsafe function's docs are missing a `# Safety` section --> tests/ui/doc_unsafe.rs:40:5 | LL | unsafe fn woefully_underdocumented(self); @@ -25,13 +25,13 @@ error: docs for unsafe trait missing `# Safety` section LL | pub unsafe trait UnsafeTrait { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: unsafe function's docs miss `# Safety` section +error: unsafe function's docs are missing a `# Safety` section --> tests/ui/doc_unsafe.rs:76:5 | LL | pub unsafe fn more_undocumented_unsafe() -> Self { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: unsafe function's docs miss `# Safety` section +error: unsafe function's docs are missing a `# Safety` section --> tests/ui/doc_unsafe.rs:92:9 | LL | pub unsafe fn whee() { diff --git a/src/tools/clippy/tests/ui/double_neg.rs b/src/tools/clippy/tests/ui/double_neg.rs index da82890443eb..3be8c6288738 100644 --- a/src/tools/clippy/tests/ui/double_neg.rs +++ b/src/tools/clippy/tests/ui/double_neg.rs @@ -5,6 +5,6 @@ fn main() { -x; -(-x); --x; - //~^ ERROR: `--x` could be misinterpreted as pre-decrement by C programmers, is usuall + //~^ ERROR: `--x` could be misinterpreted as pre-decrement by C programmers, is usually //~| NOTE: `-D clippy::double-neg` implied by `-D warnings` } diff --git a/src/tools/clippy/tests/ui/duplicated_attributes.rs b/src/tools/clippy/tests/ui/duplicated_attributes.rs index d51e7e37beb6..97cf4a69682d 100644 --- a/src/tools/clippy/tests/ui/duplicated_attributes.rs +++ b/src/tools/clippy/tests/ui/duplicated_attributes.rs @@ -1,5 +1,5 @@ //@aux-build:proc_macro_attr.rs - +#![feature(rustc_attrs)] #![warn(clippy::duplicated_attributes)] #![cfg(any(unix, windows))] #![allow(dead_code)] @@ -20,6 +20,10 @@ fn foo() {} #[cfg(unix)] // cfgs are not handled fn bar() {} +// No warning: +#[rustc_on_unimplemented(on(_Self = "&str", label = "`a"), on(_Self = "alloc::string::String", label = "a"))] +trait Abc {} + #[proc_macro_attr::duplicated_attr()] // Should not warn! fn babar() {} diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed index da28ec2e653a..7126f2799455 100644 --- a/src/tools/clippy/tests/ui/eta.fixed +++ b/src/tools/clippy/tests/ui/eta.fixed @@ -471,3 +471,14 @@ mod issue_10854 { } } } + +mod issue_12853 { + fn f_by_value(f: F) { + let x = Box::new(|| None.map(&f)); + x(); + } + fn f_by_ref(f: &F) { + let x = Box::new(|| None.map(f)); + x(); + } +} diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs index f924100f8f41..0787abf5f3e3 100644 --- a/src/tools/clippy/tests/ui/eta.rs +++ b/src/tools/clippy/tests/ui/eta.rs @@ -471,3 +471,14 @@ mod issue_10854 { } } } + +mod issue_12853 { + fn f_by_value(f: F) { + let x = Box::new(|| None.map(|x| f(x))); + x(); + } + fn f_by_ref(f: &F) { + let x = Box::new(|| None.map(|x| f(x))); + x(); + } +} diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr index d9a8768a6821..c757601042f1 100644 --- a/src/tools/clippy/tests/ui/eta.stderr +++ b/src/tools/clippy/tests/ui/eta.stderr @@ -190,5 +190,17 @@ error: redundant closure LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `crate::issue_10854::d::Test::method` -error: aborting due to 31 previous errors +error: redundant closure + --> tests/ui/eta.rs:477:38 + | +LL | let x = Box::new(|| None.map(|x| f(x))); + | ^^^^^^^^ help: replace the closure with the function itself: `&f` + +error: redundant closure + --> tests/ui/eta.rs:481:38 + | +LL | let x = Box::new(|| None.map(|x| f(x))); + | ^^^^^^^^ help: replace the closure with the function itself: `f` + +error: aborting due to 33 previous errors diff --git a/src/tools/clippy/tests/ui/floating_point_log.fixed b/src/tools/clippy/tests/ui/floating_point_log.fixed index 01f0fc5c671a..15cc47eef0dd 100644 --- a/src/tools/clippy/tests/ui/floating_point_log.fixed +++ b/src/tools/clippy/tests/ui/floating_point_log.fixed @@ -55,4 +55,19 @@ fn check_ln1p() { let _ = (1.0 + x - 2.0).ln(); } +fn issue12881() { + pub trait MyLog { + fn log(&self) -> Self; + } + + impl MyLog for f32 { + fn log(&self) -> Self { + 4. + } + } + + let x = 2.0; + x.log(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/floating_point_log.rs b/src/tools/clippy/tests/ui/floating_point_log.rs index 197e3e1ff909..1241af828593 100644 --- a/src/tools/clippy/tests/ui/floating_point_log.rs +++ b/src/tools/clippy/tests/ui/floating_point_log.rs @@ -55,4 +55,19 @@ fn check_ln1p() { let _ = (1.0 + x - 2.0).ln(); } +fn issue12881() { + pub trait MyLog { + fn log(&self) -> Self; + } + + impl MyLog for f32 { + fn log(&self) -> Self { + 4. + } + } + + let x = 2.0; + x.log(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/format_args.fixed b/src/tools/clippy/tests/ui/format_args.fixed index cab20b11e073..4d812f6bf1dc 100644 --- a/src/tools/clippy/tests/ui/format_args.fixed +++ b/src/tools/clippy/tests/ui/format_args.fixed @@ -104,6 +104,7 @@ fn main() { println!("{foo}{bar}", foo = "foo", bar = "bar"); println!("{foo}{bar}", bar = "bar", foo = "foo"); println!("{foo}{bar}", bar = "bar", foo = "foo"); + println!("{}", my_other_macro!()); // negative tests println!("error: something failed at {}", Somewhere.to_string()); diff --git a/src/tools/clippy/tests/ui/format_args.rs b/src/tools/clippy/tests/ui/format_args.rs index bc3645cb2c2b..d242623feb62 100644 --- a/src/tools/clippy/tests/ui/format_args.rs +++ b/src/tools/clippy/tests/ui/format_args.rs @@ -104,6 +104,7 @@ fn main() { println!("{foo}{bar}", foo = "foo", bar = "bar".to_string()); println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo"); println!("{foo}{bar}", bar = "bar", foo = "foo".to_string()); + println!("{}", my_other_macro!().to_string()); // negative tests println!("error: something failed at {}", Somewhere.to_string()); diff --git a/src/tools/clippy/tests/ui/format_args.stderr b/src/tools/clippy/tests/ui/format_args.stderr index f20cf9eca2f9..91a45e270081 100644 --- a/src/tools/clippy/tests/ui/format_args.stderr +++ b/src/tools/clippy/tests/ui/format_args.stderr @@ -127,29 +127,35 @@ error: `to_string` applied to a type that implements `Display` in `println!` arg LL | println!("{foo}{bar}", bar = "bar", foo = "foo".to_string()); | ^^^^^^^^^^^^ help: remove this +error: `to_string` applied to a type that implements `Display` in `println!` args + --> tests/ui/format_args.rs:107:37 + | +LL | println!("{}", my_other_macro!().to_string()); + | ^^^^^^^^^^^^ help: remove this + error: `to_string` applied to a type that implements `Display` in `print!` args - --> tests/ui/format_args.rs:118:37 + --> tests/ui/format_args.rs:119:37 | LL | print!("{}", (Location::caller().to_string())); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `print!` args - --> tests/ui/format_args.rs:119:39 + --> tests/ui/format_args.rs:120:39 | LL | print!("{}", ((Location::caller()).to_string())); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `format!` args - --> tests/ui/format_args.rs:147:38 + --> tests/ui/format_args.rs:148:38 | LL | let x = format!("{} {}", a, b.to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> tests/ui/format_args.rs:161:24 + --> tests/ui/format_args.rs:162:24 | LL | println!("{}", original[..10].to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this: `&original[..10]` -error: aborting due to 25 previous errors +error: aborting due to 26 previous errors diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.fixed b/src/tools/clippy/tests/ui/from_str_radix_10.fixed index 8c253bfd99a5..f9ce1defda17 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.fixed +++ b/src/tools/clippy/tests/ui/from_str_radix_10.fixed @@ -1,3 +1,4 @@ +#![feature(const_int_from_str)] #![warn(clippy::from_str_radix_10)] mod some_mod { @@ -59,3 +60,13 @@ fn main() -> Result<(), Box> { Ok(()) } + +fn issue_12732() { + const A: Result = u32::from_str_radix("123", 10); + const B: () = { + let _ = u32::from_str_radix("123", 10); + }; + const fn foo() { + let _ = u32::from_str_radix("123", 10); + } +} diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.rs b/src/tools/clippy/tests/ui/from_str_radix_10.rs index e9d02215710c..2d5b351f8da3 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.rs +++ b/src/tools/clippy/tests/ui/from_str_radix_10.rs @@ -1,3 +1,4 @@ +#![feature(const_int_from_str)] #![warn(clippy::from_str_radix_10)] mod some_mod { @@ -59,3 +60,13 @@ fn main() -> Result<(), Box> { Ok(()) } + +fn issue_12732() { + const A: Result = u32::from_str_radix("123", 10); + const B: () = { + let _ = u32::from_str_radix("123", 10); + }; + const fn foo() { + let _ = u32::from_str_radix("123", 10); + } +} diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.stderr b/src/tools/clippy/tests/ui/from_str_radix_10.stderr index 4aa84eca2612..01a1bf8940a1 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.stderr +++ b/src/tools/clippy/tests/ui/from_str_radix_10.stderr @@ -1,5 +1,5 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:28:5 + --> tests/ui/from_str_radix_10.rs:29:5 | LL | u32::from_str_radix("30", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"30".parse::()` @@ -8,43 +8,43 @@ LL | u32::from_str_radix("30", 10)?; = help: to override `-D warnings` add `#[allow(clippy::from_str_radix_10)]` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:31:5 + --> tests/ui/from_str_radix_10.rs:32:5 | LL | i64::from_str_radix("24", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"24".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:33:5 + --> tests/ui/from_str_radix_10.rs:34:5 | LL | isize::from_str_radix("100", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"100".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:35:5 + --> tests/ui/from_str_radix_10.rs:36:5 | LL | u8::from_str_radix("7", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"7".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:37:5 + --> tests/ui/from_str_radix_10.rs:38:5 | LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("10".to_owned() + "5").parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:39:5 + --> tests/ui/from_str_radix_10.rs:40:5 | LL | i128::from_str_radix(Test + Test, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(Test + Test).parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:43:5 + --> tests/ui/from_str_radix_10.rs:44:5 | LL | i32::from_str_radix(string, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:47:5 + --> tests/ui/from_str_radix_10.rs:48:5 | LL | i32::from_str_radix(&stringier, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::()` diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.rs b/src/tools/clippy/tests/ui/indexing_slicing_index.rs index 2e726141649e..2af5fcc82a9b 100644 --- a/src/tools/clippy/tests/ui/indexing_slicing_index.rs +++ b/src/tools/clippy/tests/ui/indexing_slicing_index.rs @@ -1,4 +1,5 @@ //@compile-flags: -Zdeduplicate-diagnostics=yes +//@aux-build: proc_macros.rs #![warn(clippy::indexing_slicing)] // We also check the out_of_bounds_indexing lint here, because it lints similar things and @@ -11,6 +12,9 @@ clippy::useless_vec )] +extern crate proc_macros; +use proc_macros::with_span; + const ARR: [i32; 2] = [1, 2]; const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false. //~^ ERROR: indexing may panic @@ -22,6 +26,22 @@ const fn idx4() -> usize { 4 } +with_span!( + span + + fn dont_lint_proc_macro_array() { + let x = [1, 2, 3, 4]; + let index: usize = 1; + x[index]; + x[10]; + + let x = vec![0; 5]; + let index: usize = 1; + x[index]; + x[10]; + } +); + fn main() { let x = [1, 2, 3, 4]; let index: usize = 1; diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr index 386f91becf14..71677584d25b 100644 --- a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr +++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr @@ -1,5 +1,5 @@ error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:15:20 + --> tests/ui/indexing_slicing_index.rs:19:20 | LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false. | ^^^^^^^^^^ @@ -10,19 +10,19 @@ LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-re = help: to override `-D warnings` add `#[allow(clippy::indexing_slicing)]` error[E0080]: evaluation of `main::{constant#3}` failed - --> tests/ui/indexing_slicing_index.rs:47:14 + --> tests/ui/indexing_slicing_index.rs:67:14 | LL | const { &ARR[idx4()] }; | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 note: erroneous constant encountered - --> tests/ui/indexing_slicing_index.rs:47:5 + --> tests/ui/indexing_slicing_index.rs:67:5 | LL | const { &ARR[idx4()] }; | ^^^^^^^^^^^^^^^^^^^^^^ error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:28:5 + --> tests/ui/indexing_slicing_index.rs:48:5 | LL | x[index]; | ^^^^^^^^ @@ -30,7 +30,7 @@ LL | x[index]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: index is out of bounds - --> tests/ui/indexing_slicing_index.rs:31:5 + --> tests/ui/indexing_slicing_index.rs:51:5 | LL | x[4]; | ^^^^ @@ -39,13 +39,13 @@ LL | x[4]; = help: to override `-D warnings` add `#[allow(clippy::out_of_bounds_indexing)]` error: index is out of bounds - --> tests/ui/indexing_slicing_index.rs:33:5 + --> tests/ui/indexing_slicing_index.rs:53:5 | LL | x[1 << 3]; | ^^^^^^^^^ error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:44:14 + --> tests/ui/indexing_slicing_index.rs:64:14 | LL | const { &ARR[idx()] }; | ^^^^^^^^^^ @@ -54,7 +54,7 @@ LL | const { &ARR[idx()] }; = note: the suggestion might not be applicable in constant blocks error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:47:14 + --> tests/ui/indexing_slicing_index.rs:67:14 | LL | const { &ARR[idx4()] }; | ^^^^^^^^^^^ @@ -63,13 +63,13 @@ LL | const { &ARR[idx4()] }; = note: the suggestion might not be applicable in constant blocks error: index is out of bounds - --> tests/ui/indexing_slicing_index.rs:54:5 + --> tests/ui/indexing_slicing_index.rs:74:5 | LL | y[4]; | ^^^^ error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:57:5 + --> tests/ui/indexing_slicing_index.rs:77:5 | LL | v[0]; | ^^^^ @@ -77,7 +77,7 @@ LL | v[0]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:59:5 + --> tests/ui/indexing_slicing_index.rs:79:5 | LL | v[10]; | ^^^^^ @@ -85,7 +85,7 @@ LL | v[10]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:61:5 + --> tests/ui/indexing_slicing_index.rs:81:5 | LL | v[1 << 3]; | ^^^^^^^^^ @@ -93,13 +93,13 @@ LL | v[1 << 3]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: index is out of bounds - --> tests/ui/indexing_slicing_index.rs:69:5 + --> tests/ui/indexing_slicing_index.rs:89:5 | LL | x[N]; | ^^^^ error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:72:5 + --> tests/ui/indexing_slicing_index.rs:92:5 | LL | v[N]; | ^^^^ @@ -107,7 +107,7 @@ LL | v[N]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui/indexing_slicing_index.rs:74:5 + --> tests/ui/indexing_slicing_index.rs:94:5 | LL | v[M]; | ^^^^ @@ -115,7 +115,7 @@ LL | v[M]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: index is out of bounds - --> tests/ui/indexing_slicing_index.rs:78:13 + --> tests/ui/indexing_slicing_index.rs:98:13 | LL | let _ = x[4]; | ^^^^ diff --git a/src/tools/clippy/tests/ui/indexing_slicing_slice.rs b/src/tools/clippy/tests/ui/indexing_slicing_slice.rs index fc591021ed6b..f37bcc4aa0ca 100644 --- a/src/tools/clippy/tests/ui/indexing_slicing_slice.rs +++ b/src/tools/clippy/tests/ui/indexing_slicing_slice.rs @@ -1,8 +1,111 @@ +//@aux-build: proc_macros.rs + #![warn(clippy::indexing_slicing)] // We also check the out_of_bounds_indexing lint here, because it lints similar things and // we want to avoid false positives. #![warn(clippy::out_of_bounds_indexing)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec)] +#![allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::useless_vec, + unused_must_use, + unused +)] +#![warn(clippy::indexing_slicing)] + +extern crate proc_macros; +use proc_macros::with_span; + +use std::ops::Index; + +struct BoolMap { + false_value: T, + true_value: T, +} + +impl Index for BoolMap { + type Output = T; + fn index(&self, index: bool) -> &T { + if index { &self.true_value } else { &self.false_value } + } +} + +struct BoolMapWithGet { + false_value: T, + true_value: T, +} + +impl Index for BoolMapWithGet { + type Output = T; + fn index(&self, index: bool) -> &Self::Output { + if index { &self.true_value } else { &self.false_value } + } +} + +impl BoolMapWithGet { + fn get(&self, index: bool) -> Option<&T> { + if index { + Some(&self.true_value) + } else { + Some(&self.false_value) + } + } +} + +struct S(T); +impl S { + fn get() -> Option { + unimplemented!() + } +} +impl Index for S { + type Output = T; + fn index(&self, _index: i32) -> &Self::Output { + &self.0 + } +} + +struct Y(T); +impl Y { + fn get() -> Option { + unimplemented!() + } +} +impl Index for Y { + type Output = T; + fn index(&self, _index: i32) -> &Self::Output { + &self.0 + } +} + +struct Z(T); +impl Z { + fn get() -> T2 { + unimplemented!() + } +} +impl Index for Z { + type Output = T; + fn index(&self, _index: i32) -> &Self::Output { + &self.0 + } +} + +with_span!( + span + + fn dont_lint_proc_macro() { + let x = [1, 2, 3, 4]; + let index: usize = 1; + &x[index..]; + &x[..10]; + + let x = vec![0; 5]; + let index: usize = 1; + &x[index..]; + &x[..10]; + } +); fn main() { let x = [1, 2, 3, 4]; @@ -51,4 +154,28 @@ fn main() { //~^ ERROR: slicing may panic &v[..]; // Ok, should not produce stderr. + + let map = BoolMap { + false_value: 2, + true_value: 4, + }; + + map[true]; // Ok, because `get` does not exist (custom indexing) + + let map_with_get = BoolMapWithGet { + false_value: 2, + true_value: 4, + }; + + // Lint on this, because `get` does exist with same signature + map_with_get[true]; + + let s = S::(1); + s[0]; + + let y = Y::(1); + y[0]; + + let z = Z::(1); + z[0]; } diff --git a/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr b/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr index 790d4a41f5b1..1e72506746ec 100644 --- a/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr +++ b/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr @@ -1,5 +1,5 @@ error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:12:6 + --> tests/ui/indexing_slicing_slice.rs:115:6 | LL | &x[index..]; | ^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | &x[index..]; = help: to override `-D warnings` add `#[allow(clippy::indexing_slicing)]` error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:14:6 + --> tests/ui/indexing_slicing_slice.rs:117:6 | LL | &x[..index]; | ^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | &x[..index]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:16:6 + --> tests/ui/indexing_slicing_slice.rs:119:6 | LL | &x[index_from..index_to]; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | &x[index_from..index_to]; = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:18:6 + --> tests/ui/indexing_slicing_slice.rs:121:6 | LL | &x[index_from..][..index_to]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | &x[index_from..][..index_to]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:18:6 + --> tests/ui/indexing_slicing_slice.rs:121:6 | LL | &x[index_from..][..index_to]; | ^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | &x[index_from..][..index_to]; = help: consider using `.get(n..)` or .get_mut(n..)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:21:6 + --> tests/ui/indexing_slicing_slice.rs:124:6 | LL | &x[5..][..10]; | ^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | &x[5..][..10]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:21:8 + --> tests/ui/indexing_slicing_slice.rs:124:8 | LL | &x[5..][..10]; | ^ @@ -58,7 +58,7 @@ LL | &x[5..][..10]; = help: to override `-D warnings` add `#[allow(clippy::out_of_bounds_indexing)]` error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:25:6 + --> tests/ui/indexing_slicing_slice.rs:128:6 | LL | &x[0..][..3]; | ^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | &x[0..][..3]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:27:6 + --> tests/ui/indexing_slicing_slice.rs:130:6 | LL | &x[1..][..5]; | ^^^^^^^^^^^ @@ -74,19 +74,19 @@ LL | &x[1..][..5]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:35:12 + --> tests/ui/indexing_slicing_slice.rs:138:12 | LL | &y[0..=4]; | ^ error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:37:11 + --> tests/ui/indexing_slicing_slice.rs:140:11 | LL | &y[..=4]; | ^ error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:43:6 + --> tests/ui/indexing_slicing_slice.rs:146:6 | LL | &v[10..100]; | ^^^^^^^^^^ @@ -94,7 +94,7 @@ LL | &v[10..100]; = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:45:6 + --> tests/ui/indexing_slicing_slice.rs:148:6 | LL | &x[10..][..100]; | ^^^^^^^^^^^^^^ @@ -102,13 +102,13 @@ LL | &x[10..][..100]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:45:8 + --> tests/ui/indexing_slicing_slice.rs:148:8 | LL | &x[10..][..100]; | ^^ error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:48:6 + --> tests/ui/indexing_slicing_slice.rs:151:6 | LL | &v[10..]; | ^^^^^^^ @@ -116,12 +116,36 @@ LL | &v[10..]; = help: consider using `.get(n..)` or .get_mut(n..)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:50:6 + --> tests/ui/indexing_slicing_slice.rs:153:6 | LL | &v[..100]; | ^^^^^^^^ | = help: consider using `.get(..n)`or `.get_mut(..n)` instead -error: aborting due to 16 previous errors +error: indexing may panic + --> tests/ui/indexing_slicing_slice.rs:171:5 + | +LL | map_with_get[true]; + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> tests/ui/indexing_slicing_slice.rs:174:5 + | +LL | s[0]; + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> tests/ui/indexing_slicing_slice.rs:177:5 + | +LL | y[0]; + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/infinite_loops.rs b/src/tools/clippy/tests/ui/infinite_loops.rs index 52fbbaa8e31e..b2d522fa011b 100644 --- a/src/tools/clippy/tests/ui/infinite_loops.rs +++ b/src/tools/clippy/tests/ui/infinite_loops.rs @@ -137,7 +137,7 @@ fn can_break_both_inner_and_outer(cond: bool) { } fn break_wrong_loop(cond: bool) { - // 'inner has statement to break 'outer loop, but it was breaked early by a labeled child loop + // 'inner has statement to break 'outer loop, but it was broken out of early by a labeled child loop 'outer: loop { loop { //~^ ERROR: infinite loop detected diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.fixed b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed index c03d91c797c8..72b39c982bf7 100644 --- a/src/tools/clippy/tests/ui/into_iter_on_ref.fixed +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed @@ -11,7 +11,6 @@ fn main() { let _ = vec![1, 2, 3].into_iter(); let _ = (&vec![1, 2, 3]).iter(); //~ ERROR: equivalent to `.iter() - let _ = vec![1, 2, 3].into_boxed_slice().iter(); //~ ERROR: equivalent to `.iter() let _ = std::rc::Rc::from(&[X][..]).iter(); //~ ERROR: equivalent to `.iter() let _ = std::sync::Arc::from(&[X][..]).iter(); //~ ERROR: equivalent to `.iter() diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.rs b/src/tools/clippy/tests/ui/into_iter_on_ref.rs index 93c732fd6ccf..5ba224720d34 100644 --- a/src/tools/clippy/tests/ui/into_iter_on_ref.rs +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.rs @@ -11,7 +11,6 @@ fn main() { let _ = vec![1, 2, 3].into_iter(); let _ = (&vec![1, 2, 3]).into_iter(); //~ ERROR: equivalent to `.iter() - let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ ERROR: equivalent to `.iter() let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ ERROR: equivalent to `.iter() let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ ERROR: equivalent to `.iter() diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.stderr b/src/tools/clippy/tests/ui/into_iter_on_ref.stderr index 0e9d485f1a9d..64d814074da4 100644 --- a/src/tools/clippy/tests/ui/into_iter_on_ref.stderr +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.stderr @@ -8,160 +8,154 @@ LL | let _ = (&vec![1, 2, 3]).into_iter(); = help: to override `-D warnings` add `#[allow(clippy::into_iter_on_ref)]` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice` - --> tests/ui/into_iter_on_ref.rs:14:46 - | -LL | let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); - | ^^^^^^^^^ help: call directly: `iter` - -error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice` - --> tests/ui/into_iter_on_ref.rs:15:41 + --> tests/ui/into_iter_on_ref.rs:14:41 | LL | let _ = std::rc::Rc::from(&[X][..]).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice` - --> tests/ui/into_iter_on_ref.rs:16:44 + --> tests/ui/into_iter_on_ref.rs:15:44 | LL | let _ = std::sync::Arc::from(&[X][..]).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array` - --> tests/ui/into_iter_on_ref.rs:18:32 + --> tests/ui/into_iter_on_ref.rs:17:32 | LL | let _ = (&&&&&&&[1, 2, 3]).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array` - --> tests/ui/into_iter_on_ref.rs:19:36 + --> tests/ui/into_iter_on_ref.rs:18:36 | LL | let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `array` - --> tests/ui/into_iter_on_ref.rs:20:40 + --> tests/ui/into_iter_on_ref.rs:19:40 | LL | let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Option` - --> tests/ui/into_iter_on_ref.rs:22:24 + --> tests/ui/into_iter_on_ref.rs:21:24 | LL | let _ = (&Some(4)).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Option` - --> tests/ui/into_iter_on_ref.rs:23:28 + --> tests/ui/into_iter_on_ref.rs:22:28 | LL | let _ = (&mut Some(5)).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Result` - --> tests/ui/into_iter_on_ref.rs:24:32 + --> tests/ui/into_iter_on_ref.rs:23:32 | LL | let _ = (&Ok::<_, i32>(6)).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Result` - --> tests/ui/into_iter_on_ref.rs:25:37 + --> tests/ui/into_iter_on_ref.rs:24:37 | LL | let _ = (&mut Err::(7)).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec` - --> tests/ui/into_iter_on_ref.rs:26:34 + --> tests/ui/into_iter_on_ref.rs:25:34 | LL | let _ = (&Vec::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Vec` - --> tests/ui/into_iter_on_ref.rs:27:38 + --> tests/ui/into_iter_on_ref.rs:26:38 | LL | let _ = (&mut Vec::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeMap` - --> tests/ui/into_iter_on_ref.rs:28:44 + --> tests/ui/into_iter_on_ref.rs:27:44 | LL | let _ = (&BTreeMap::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `BTreeMap` - --> tests/ui/into_iter_on_ref.rs:29:48 + --> tests/ui/into_iter_on_ref.rs:28:48 | LL | let _ = (&mut BTreeMap::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `VecDeque` - --> tests/ui/into_iter_on_ref.rs:30:39 + --> tests/ui/into_iter_on_ref.rs:29:39 | LL | let _ = (&VecDeque::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `VecDeque` - --> tests/ui/into_iter_on_ref.rs:31:43 + --> tests/ui/into_iter_on_ref.rs:30:43 | LL | let _ = (&mut VecDeque::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `LinkedList` - --> tests/ui/into_iter_on_ref.rs:32:41 + --> tests/ui/into_iter_on_ref.rs:31:41 | LL | let _ = (&LinkedList::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `LinkedList` - --> tests/ui/into_iter_on_ref.rs:33:45 + --> tests/ui/into_iter_on_ref.rs:32:45 | LL | let _ = (&mut LinkedList::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashMap` - --> tests/ui/into_iter_on_ref.rs:34:43 + --> tests/ui/into_iter_on_ref.rs:33:43 | LL | let _ = (&HashMap::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `HashMap` - --> tests/ui/into_iter_on_ref.rs:35:47 + --> tests/ui/into_iter_on_ref.rs:34:47 | LL | let _ = (&mut HashMap::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter_mut` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeSet` - --> tests/ui/into_iter_on_ref.rs:37:39 + --> tests/ui/into_iter_on_ref.rs:36:39 | LL | let _ = (&BTreeSet::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BinaryHeap` - --> tests/ui/into_iter_on_ref.rs:38:41 + --> tests/ui/into_iter_on_ref.rs:37:41 | LL | let _ = (&BinaryHeap::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashSet` - --> tests/ui/into_iter_on_ref.rs:39:38 + --> tests/ui/into_iter_on_ref.rs:38:38 | LL | let _ = (&HashSet::::new()).into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Path` - --> tests/ui/into_iter_on_ref.rs:40:43 + --> tests/ui/into_iter_on_ref.rs:39:43 | LL | let _ = std::path::Path::new("12/34").into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `PathBuf` - --> tests/ui/into_iter_on_ref.rs:41:47 + --> tests/ui/into_iter_on_ref.rs:40:47 | LL | let _ = std::path::PathBuf::from("12/34").into_iter(); | ^^^^^^^^^ help: call directly: `iter` error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array` - --> tests/ui/into_iter_on_ref.rs:43:26 + --> tests/ui/into_iter_on_ref.rs:42:26 | LL | let _ = (&[1, 2, 3]).into_iter().next(); | ^^^^^^^^^ help: call directly: `iter` -error: aborting due to 27 previous errors +error: aborting due to 26 previous errors diff --git a/src/tools/clippy/tests/ui/iter_on_empty_collections.fixed b/src/tools/clippy/tests/ui/iter_on_empty_collections.fixed index 794629f240eb..0f28b48d9ab8 100644 --- a/src/tools/clippy/tests/ui/iter_on_empty_collections.fixed +++ b/src/tools/clippy/tests/ui/iter_on_empty_collections.fixed @@ -20,6 +20,28 @@ fn array() { }; let _ = if false { ["test"].iter() } else { [].iter() }; + + let smth = Some(vec![1, 2, 3]); + + // Don't trigger when the empty collection iter is relied upon for its concrete type + // But do trigger if it is just an iterator, despite being an argument to a method + for i in smth.as_ref().map_or([].iter(), |s| s.iter()).chain(std::iter::empty()) { + println!("{i}"); + } + + // Same as above, but for empty collection iters with extra layers + for i in smth.as_ref().map_or({ [].iter() }, |s| s.iter()) { + println!("{y}", y = i + 1); + } + + // Same as above, but for regular function calls + for i in Option::map_or(smth.as_ref(), [].iter(), |s| s.iter()) { + println!("{i}"); + } + + // Same as above, but when there are no predicates that mention the collection iter type. + let mut iter = [34, 228, 35].iter(); + let _ = std::mem::replace(&mut iter, [].iter()); } macro_rules! in_macros { diff --git a/src/tools/clippy/tests/ui/iter_on_empty_collections.rs b/src/tools/clippy/tests/ui/iter_on_empty_collections.rs index a6461a702eb2..702da514df7d 100644 --- a/src/tools/clippy/tests/ui/iter_on_empty_collections.rs +++ b/src/tools/clippy/tests/ui/iter_on_empty_collections.rs @@ -20,6 +20,28 @@ fn array() { }; let _ = if false { ["test"].iter() } else { [].iter() }; + + let smth = Some(vec![1, 2, 3]); + + // Don't trigger when the empty collection iter is relied upon for its concrete type + // But do trigger if it is just an iterator, despite being an argument to a method + for i in smth.as_ref().map_or([].iter(), |s| s.iter()).chain([].iter()) { + println!("{i}"); + } + + // Same as above, but for empty collection iters with extra layers + for i in smth.as_ref().map_or({ [].iter() }, |s| s.iter()) { + println!("{y}", y = i + 1); + } + + // Same as above, but for regular function calls + for i in Option::map_or(smth.as_ref(), [].iter(), |s| s.iter()) { + println!("{i}"); + } + + // Same as above, but when there are no predicates that mention the collection iter type. + let mut iter = [34, 228, 35].iter(); + let _ = std::mem::replace(&mut iter, [].iter()); } macro_rules! in_macros { diff --git a/src/tools/clippy/tests/ui/iter_on_empty_collections.stderr b/src/tools/clippy/tests/ui/iter_on_empty_collections.stderr index ade20ff26a01..da9caa6925bd 100644 --- a/src/tools/clippy/tests/ui/iter_on_empty_collections.stderr +++ b/src/tools/clippy/tests/ui/iter_on_empty_collections.stderr @@ -37,5 +37,11 @@ error: `iter` call on an empty collection LL | assert_eq!(None.iter().next(), Option::<&i32>::None); | ^^^^^^^^^^^ help: try: `std::iter::empty()` -error: aborting due to 6 previous errors +error: `iter` call on an empty collection + --> tests/ui/iter_on_empty_collections.rs:28:66 + | +LL | for i in smth.as_ref().map_or([].iter(), |s| s.iter()).chain([].iter()) { + | ^^^^^^^^^ help: try: `std::iter::empty()` + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/iter_over_hash_type.rs b/src/tools/clippy/tests/ui/iter_over_hash_type.rs index 7000c8bf96f8..65bae1df4218 100644 --- a/src/tools/clippy/tests/ui/iter_over_hash_type.rs +++ b/src/tools/clippy/tests/ui/iter_over_hash_type.rs @@ -59,7 +59,7 @@ fn main() { let _ = x; } - // shouldnt fire + // shouldn't fire for x in &vec { let _ = x; } diff --git a/src/tools/clippy/tests/ui/let_and_return.fixed b/src/tools/clippy/tests/ui/let_and_return.fixed index 4187019e5894..b68b41cdca23 100644 --- a/src/tools/clippy/tests/ui/let_and_return.fixed +++ b/src/tools/clippy/tests/ui/let_and_return.fixed @@ -210,4 +210,38 @@ fn issue9150() -> usize { x } +fn issue12801() { + fn left_is_if() -> String { + + (if true { "a".to_string() } else { "b".to_string() } + "c") + //~^ ERROR: returning the result of a `let` binding from a block + } + + fn no_par_needed() -> String { + + "c".to_string() + if true { "a" } else { "b" } + //~^ ERROR: returning the result of a `let` binding from a block + } + + fn conjunctive_blocks() -> String { + + ({ "a".to_string() } + "b" + { "c" } + "d") + //~^ ERROR: returning the result of a `let` binding from a block + } + + #[allow(clippy::overly_complex_bool_expr)] + fn other_ops() { + let _ = || { + + (if true { 2 } else { 3 } << 4) + //~^ ERROR: returning the result of a `let` binding from a block + }; + let _ = || { + + ({ true } || { false } && { 2 <= 3 }) + //~^ ERROR: returning the result of a `let` binding from a block + }; + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/let_and_return.rs b/src/tools/clippy/tests/ui/let_and_return.rs index 54444957b7d5..6b9035f94288 100644 --- a/src/tools/clippy/tests/ui/let_and_return.rs +++ b/src/tools/clippy/tests/ui/let_and_return.rs @@ -210,4 +210,38 @@ fn issue9150() -> usize { x } +fn issue12801() { + fn left_is_if() -> String { + let s = if true { "a".to_string() } else { "b".to_string() } + "c"; + s + //~^ ERROR: returning the result of a `let` binding from a block + } + + fn no_par_needed() -> String { + let s = "c".to_string() + if true { "a" } else { "b" }; + s + //~^ ERROR: returning the result of a `let` binding from a block + } + + fn conjunctive_blocks() -> String { + let s = { "a".to_string() } + "b" + { "c" } + "d"; + s + //~^ ERROR: returning the result of a `let` binding from a block + } + + #[allow(clippy::overly_complex_bool_expr)] + fn other_ops() { + let _ = || { + let s = if true { 2 } else { 3 } << 4; + s + //~^ ERROR: returning the result of a `let` binding from a block + }; + let _ = || { + let s = { true } || { false } && { 2 <= 3 }; + s + //~^ ERROR: returning the result of a `let` binding from a block + }; + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/let_and_return.stderr b/src/tools/clippy/tests/ui/let_and_return.stderr index ff5962ec196e..75efa05d770a 100644 --- a/src/tools/clippy/tests/ui/let_and_return.stderr +++ b/src/tools/clippy/tests/ui/let_and_return.stderr @@ -78,5 +78,75 @@ LL + E::B(x) => x, LL + }) as _ | -error: aborting due to 5 previous errors +error: returning the result of a `let` binding from a block + --> tests/ui/let_and_return.rs:216:9 + | +LL | let s = if true { "a".to_string() } else { "b".to_string() } + "c"; + | ------------------------------------------------------------------- unnecessary `let` binding +LL | s + | ^ + | +help: return the expression directly + | +LL ~ +LL ~ (if true { "a".to_string() } else { "b".to_string() } + "c") + | + +error: returning the result of a `let` binding from a block + --> tests/ui/let_and_return.rs:222:9 + | +LL | let s = "c".to_string() + if true { "a" } else { "b" }; + | ------------------------------------------------------- unnecessary `let` binding +LL | s + | ^ + | +help: return the expression directly + | +LL ~ +LL ~ "c".to_string() + if true { "a" } else { "b" } + | + +error: returning the result of a `let` binding from a block + --> tests/ui/let_and_return.rs:228:9 + | +LL | let s = { "a".to_string() } + "b" + { "c" } + "d"; + | -------------------------------------------------- unnecessary `let` binding +LL | s + | ^ + | +help: return the expression directly + | +LL ~ +LL ~ ({ "a".to_string() } + "b" + { "c" } + "d") + | + +error: returning the result of a `let` binding from a block + --> tests/ui/let_and_return.rs:236:13 + | +LL | let s = if true { 2 } else { 3 } << 4; + | -------------------------------------- unnecessary `let` binding +LL | s + | ^ + | +help: return the expression directly + | +LL ~ +LL ~ (if true { 2 } else { 3 } << 4) + | + +error: returning the result of a `let` binding from a block + --> tests/ui/let_and_return.rs:241:13 + | +LL | let s = { true } || { false } && { 2 <= 3 }; + | -------------------------------------------- unnecessary `let` binding +LL | s + | ^ + | +help: return the expression directly + | +LL ~ +LL ~ ({ true } || { false } && { 2 <= 3 }) + | + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/manual_pattern_char_comparison.fixed b/src/tools/clippy/tests/ui/manual_pattern_char_comparison.fixed new file mode 100644 index 000000000000..588226b87e87 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_pattern_char_comparison.fixed @@ -0,0 +1,49 @@ +#![warn(clippy::manual_pattern_char_comparison)] + +struct NotStr; + +impl NotStr { + fn find(&self, _: impl FnMut(char) -> bool) {} +} + +fn main() { + let sentence = "Hello, world!"; + sentence.trim_end_matches(['.', ',', '!', '?']); + sentence.split(['\n', 'X']); + sentence.split(['\n', 'X']); + sentence.splitn(3, 'X'); + sentence.splitn(3, |c: char| c.is_whitespace() || c == 'X'); + let char_compare = 'X'; + sentence.splitn(3, char_compare); + sentence.split(['\n', 'X', 'Y']); + sentence.splitn(3, 'X'); + sentence.splitn(3, ['X', 'W']); + sentence.find('🎈'); + + let not_str = NotStr; + not_str.find(|c: char| c == 'X'); + + "".find(|c| c == 'a' || c > 'z'); + + let x = true; + "".find(|c| c == 'a' || x || c == 'b'); + + let d = 'd'; + "".find(|c| c == 'a' || d == 'b'); + + "".find(|c| match c { + 'a' | 'b' => true, + _ => c.is_ascii(), + }); + + "".find(|c| matches!(c, 'a' | 'b' if false)); + + "".find(|c| matches!(c, 'a' | '1'..'4')); + "".find(|c| c == 'a' || matches!(c, '1'..'4')); + macro_rules! m { + ($e:expr) => { + $e == '?' + }; + } + "".find(|c| m!(c)); +} diff --git a/src/tools/clippy/tests/ui/manual_pattern_char_comparison.rs b/src/tools/clippy/tests/ui/manual_pattern_char_comparison.rs new file mode 100644 index 000000000000..5078f3ee27f3 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_pattern_char_comparison.rs @@ -0,0 +1,49 @@ +#![warn(clippy::manual_pattern_char_comparison)] + +struct NotStr; + +impl NotStr { + fn find(&self, _: impl FnMut(char) -> bool) {} +} + +fn main() { + let sentence = "Hello, world!"; + sentence.trim_end_matches(|c: char| c == '.' || c == ',' || c == '!' || c == '?'); + sentence.split(|c: char| c == '\n' || c == 'X'); + sentence.split(|c| c == '\n' || c == 'X'); + sentence.splitn(3, |c: char| c == 'X'); + sentence.splitn(3, |c: char| c.is_whitespace() || c == 'X'); + let char_compare = 'X'; + sentence.splitn(3, |c: char| c == char_compare); + sentence.split(|c: char| matches!(c, '\n' | 'X' | 'Y')); + sentence.splitn(3, |c: char| matches!(c, 'X')); + sentence.splitn(3, |c: char| matches!(c, 'X' | 'W')); + sentence.find(|c| c == '🎈'); + + let not_str = NotStr; + not_str.find(|c: char| c == 'X'); + + "".find(|c| c == 'a' || c > 'z'); + + let x = true; + "".find(|c| c == 'a' || x || c == 'b'); + + let d = 'd'; + "".find(|c| c == 'a' || d == 'b'); + + "".find(|c| match c { + 'a' | 'b' => true, + _ => c.is_ascii(), + }); + + "".find(|c| matches!(c, 'a' | 'b' if false)); + + "".find(|c| matches!(c, 'a' | '1'..'4')); + "".find(|c| c == 'a' || matches!(c, '1'..'4')); + macro_rules! m { + ($e:expr) => { + $e == '?' + }; + } + "".find(|c| m!(c)); +} diff --git a/src/tools/clippy/tests/ui/manual_pattern_char_comparison.stderr b/src/tools/clippy/tests/ui/manual_pattern_char_comparison.stderr new file mode 100644 index 000000000000..b6b51794a11f --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_pattern_char_comparison.stderr @@ -0,0 +1,59 @@ +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:11:31 + | +LL | sentence.trim_end_matches(|c: char| c == '.' || c == ',' || c == '!' || c == '?'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using an array of `char`: `['.', ',', '!', '?']` + | + = note: `-D clippy::manual-pattern-char-comparison` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_pattern_char_comparison)]` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:12:20 + | +LL | sentence.split(|c: char| c == '\n' || c == 'X'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using an array of `char`: `['\n', 'X']` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:13:20 + | +LL | sentence.split(|c| c == '\n' || c == 'X'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using an array of `char`: `['\n', 'X']` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:14:24 + | +LL | sentence.splitn(3, |c: char| c == 'X'); + | ^^^^^^^^^^^^^^^^^^ help: consider using a `char`: `'X'` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:17:24 + | +LL | sentence.splitn(3, |c: char| c == char_compare); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a `char`: `char_compare` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:18:20 + | +LL | sentence.split(|c: char| matches!(c, '\n' | 'X' | 'Y')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using an array of `char`: `['\n', 'X', 'Y']` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:19:24 + | +LL | sentence.splitn(3, |c: char| matches!(c, 'X')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a `char`: `'X'` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:20:24 + | +LL | sentence.splitn(3, |c: char| matches!(c, 'X' | 'W')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using an array of `char`: `['X', 'W']` + +error: this manual char comparison can be written more succinctly + --> tests/ui/manual_pattern_char_comparison.rs:21:19 + | +LL | sentence.find(|c| c == '🎈'); + | ^^^^^^^^^^^^^ help: consider using a `char`: `'🎈'` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed index d6e736ba9cc2..663de1a5f067 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed @@ -26,6 +26,12 @@ fn main() { Some(x) => x, None => &[], }; + + let x: Result = Ok(String::new()); + x.unwrap_or_default(); + + let x: Result = Ok(String::new()); + x.unwrap_or_default(); } // Issue #12531 diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs index 462d5d90ee77..75ffe09be9d4 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs @@ -47,6 +47,21 @@ fn main() { Some(x) => x, None => &[], }; + + let x: Result = Ok(String::new()); + match x { + //~^ ERROR: match can be simplified with `.unwrap_or_default()` + Ok(v) => v, + Err(_) => String::new(), + }; + + let x: Result = Ok(String::new()); + if let Ok(v) = x { + //~^ ERROR: if let can be simplified with `.unwrap_or_default()` + v + } else { + String::new() + }; } // Issue #12531 diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr index 3f1da444301f..9e3b1be5cb9b 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr @@ -53,7 +53,28 @@ LL | | }; | |_____^ help: replace it with: `x.unwrap_or_default()` error: match can be simplified with `.unwrap_or_default()` - --> tests/ui/manual_unwrap_or_default.rs:56:20 + --> tests/ui/manual_unwrap_or_default.rs:52:5 + | +LL | / match x { +LL | | +LL | | Ok(v) => v, +LL | | Err(_) => String::new(), +LL | | }; + | |_____^ help: replace it with: `x.unwrap_or_default()` + +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:59:5 + | +LL | / if let Ok(v) = x { +LL | | +LL | | v +LL | | } else { +LL | | String::new() +LL | | }; + | |_____^ help: replace it with: `x.unwrap_or_default()` + +error: match can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:71:20 | LL | Some(_) => match *b { | ____________________^ @@ -62,5 +83,5 @@ LL | | _ => 0, LL | | }, | |_________^ help: replace it with: `(*b).unwrap_or_default()` -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/many_single_char_names.rs b/src/tools/clippy/tests/ui/many_single_char_names.rs index 2af45eaab8af..68578340d90e 100644 --- a/src/tools/clippy/tests/ui/many_single_char_names.rs +++ b/src/tools/clippy/tests/ui/many_single_char_names.rs @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![allow(clippy::too_many_arguments, clippy::diverging_sub_expression)] #![warn(clippy::many_single_char_names)] diff --git a/src/tools/clippy/tests/ui/many_single_char_names.stderr b/src/tools/clippy/tests/ui/many_single_char_names.stderr index 3b2460b5c077..131836ef7c88 100644 --- a/src/tools/clippy/tests/ui/many_single_char_names.stderr +++ b/src/tools/clippy/tests/ui/many_single_char_names.stderr @@ -1,5 +1,5 @@ error: 5 bindings with single-character names in scope - --> tests/ui/many_single_char_names.rs:7:9 + --> tests/ui/many_single_char_names.rs:5:9 | LL | let a: i32; | ^ @@ -14,7 +14,7 @@ LL | let e: i32; = help: to override `-D warnings` add `#[allow(clippy::many_single_char_names)]` error: 6 bindings with single-character names in scope - --> tests/ui/many_single_char_names.rs:7:9 + --> tests/ui/many_single_char_names.rs:5:9 | LL | let a: i32; | ^ @@ -28,7 +28,7 @@ LL | let f: i32; | ^ error: 5 bindings with single-character names in scope - --> tests/ui/many_single_char_names.rs:7:9 + --> tests/ui/many_single_char_names.rs:5:9 | LL | let a: i32; | ^ @@ -40,13 +40,13 @@ LL | e => panic!(), | ^ error: 8 bindings with single-character names in scope - --> tests/ui/many_single_char_names.rs:36:13 + --> tests/ui/many_single_char_names.rs:34:13 | LL | fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {} | ^ ^ ^ ^ ^ ^ ^ ^ error: 8 bindings with single-character names in scope - --> tests/ui/many_single_char_names.rs:40:10 + --> tests/ui/many_single_char_names.rs:38:10 | LL | let (a, b, c, d, e, f, g, h): (bool, bool, bool, bool, bool, bool, bool, bool) = unimplemented!(); | ^ ^ ^ ^ ^ ^ ^ ^ diff --git a/src/tools/clippy/tests/ui/match_same_arms.stderr b/src/tools/clippy/tests/ui/match_same_arms.stderr index a926570b60ad..3c0382767c3f 100644 --- a/src/tools/clippy/tests/ui/match_same_arms.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms.stderr @@ -2,7 +2,7 @@ error: this match arm has an identical body to the `_` wildcard arm --> tests/ui/match_same_arms.rs:12:9 | LL | Abc::A => 0, - | ^^^^^^^^^^^ help: try removing the arm + | ^^^^^^^^^^^^^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here @@ -17,106 +17,114 @@ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:18:9 | LL | (1, .., 3) => 42, - | ----------^^^^^^ - | | - | help: try merging the arm patterns: `(1, .., 3) | (.., 3)` + | ^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:19:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | (1, .., 3) | (.., 3) => 42, + | ~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - (.., 3) => 42, | -LL | (.., 3) => 42, - | ^^^^^^^^^^^^^ error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:25:9 | LL | 51 => 1, - | --^^^^^ - | | - | help: try merging the arm patterns: `51 | 42` - | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:24:9 - | -LL | 42 => 1, | ^^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 51 | 42 => 1, + | ~~~~~~~ +help: and remove this obsolete arm + | +LL - 42 => 1, + | error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:26:9 | LL | 41 => 2, - | --^^^^^ - | | - | help: try merging the arm patterns: `41 | 52` - | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:27:9 - | -LL | 52 => 2, | ^^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 41 | 52 => 2, + | ~~~~~~~ +help: and remove this obsolete arm + | +LL - 52 => 2, + | error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:33:9 | LL | 2 => 2, - | -^^^^^ - | | - | help: try merging the arm patterns: `2 | 1` - | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:32:9 - | -LL | 1 => 2, | ^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 2 | 1 => 2, + | ~~~~~ +help: and remove this obsolete arm + | +LL - 1 => 2, + | error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:35:9 | LL | 3 => 2, - | -^^^^^ - | | - | help: try merging the arm patterns: `3 | 1` - | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:32:9 - | -LL | 1 => 2, | ^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 3 | 1 => 2, + | ~~~~~ +help: and remove this obsolete arm + | +LL - 1 => 2, + | error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:33:9 | LL | 2 => 2, - | -^^^^^ - | | - | help: try merging the arm patterns: `2 | 3` - | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:35:9 - | -LL | 3 => 2, | ^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 2 | 3 => 2, + | ~~~~~ +help: and remove this obsolete arm + | +LL - 3 => 2, +LL + + | error: this match arm has an identical body to another arm --> tests/ui/match_same_arms.rs:52:17 | LL | CommandInfo::External { name, .. } => name.to_string(), - | ----------------------------------^^^^^^^^^^^^^^^^^^^^ - | | - | help: try merging the arm patterns: `CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms.rs:51:17 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. } => name.to_string(), + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - CommandInfo::BuiltIn { name, .. } => name.to_string(), | -LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms2.fixed b/src/tools/clippy/tests/ui/match_same_arms2.fixed new file mode 100644 index 000000000000..09e960ddd6a9 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms2.fixed @@ -0,0 +1,258 @@ +#![warn(clippy::match_same_arms)] +#![allow( + clippy::disallowed_names, + clippy::diverging_sub_expression, + clippy::uninlined_format_args, + clippy::match_single_binding, + clippy::match_like_matches_macro +)] +fn bar(_: T) {} +fn foo() -> bool { + unimplemented!() +} + +fn match_same_arms() { + let _ = match 42 { + _ => { + foo(); + let mut a = 42 + [23].len() as i32; + if true { + a += 7; + } + a = -31 - a; + a + }, + }; + //~^^^^^^^^^^^^^^^^^^^ ERROR: this match arm has an identical body to the `_` wildcard arm + + let _ = match 42 { + 51 | 42 => foo(), //~ ERROR: this match arm has an identical body to another arm + _ => true, + }; + + let _ = match Some(42) { + None | Some(_) => 24, //~ ERROR: this match arm has an identical body to another arm + }; + + let _ = match Some(42) { + Some(foo) => 24, + None => 24, + }; + + let _ = match Some(42) { + Some(42) => 24, + Some(a) => 24, // bindings are different + None => 0, + }; + + let _ = match Some(42) { + Some(a) if a > 0 => 24, + Some(a) => 24, // one arm has a guard + None => 0, + }; + + match (Some(42), Some(42)) { + (None, Some(a)) | (Some(a), None) => bar(a), //~ ERROR: this match arm has an identical body to another arm + _ => (), + } + + // No warning because guards are different + let _ = match Some(42) { + Some(a) if a == 42 => a, + Some(a) if a == 24 => a, + Some(_) => 24, + None => 0, + }; + + let _ = match (Some(42), Some(42)) { + (None, Some(a)) | (Some(a), None) if a == 42 => a, //~ ERROR: this match arm has an identical body to another arm + _ => 0, + }; + + match (Some(42), Some(42)) { + (Some(a), ..) | (.., Some(a)) => bar(a), //~ ERROR: this match arm has an identical body to another arm + _ => (), + } + + let _ = match Some(()) { + Some(()) => 0.0, + None => -0.0, + }; + + match (Some(42), Some("")) { + (Some(a), None) => bar(a), + (None, Some(a)) => bar(a), // bindings have different types + _ => (), + } + + let x: Result = Ok(3); + + // No warning because of the guard. + match x { + Ok(x) if x * x == 64 => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => println!("err"), + } + + // This used to be a false positive; see issue #1996. + match x { + Ok(3) => println!("ok"), + Ok(x) if x * x == 64 => println!("ok 64"), + Ok(_) => println!("ok"), + Err(_) => println!("err"), + } + + match (x, Some(1i32)) { + (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), //~ ERROR: this match arm has an identical body to another arm + _ => println!("err"), + } + + // No warning; different types for `x`. + match (x, Some(1.0f64)) { + (Ok(x), Some(_)) => println!("ok {}", x), + (Ok(_), Some(x)) => println!("ok {}", x), + _ => println!("err"), + } + + // False negative #2251. + match x { + Ok(_tmp) => println!("ok"), + Ok(_) | Ok(3) => println!("ok"), //~ ERROR: this match arm has an identical body to another arm + Err(_) => { + unreachable!(); + }, + } + + // False positive #1390 + macro_rules! empty { + ($e:expr) => {}; + } + match 0 { + 0 => { + empty!(0); + }, + 1 => { + empty!(1); + }, + x => { + empty!(x); + }, + }; + + // still lint if the tokens are the same + match 0 { + 1 | 0 => { + empty!(0); + }, + x => { + empty!(x); + }, + } + //~^^^^^^^ ERROR: this match arm has an identical body to another arm + + match_expr_like_matches_macro_priority(); +} + +fn match_expr_like_matches_macro_priority() { + enum E { + A, + B, + C, + } + let x = E::A; + let _ans = match x { + E::A => false, + E::B => false, + _ => true, + }; +} + +fn main() { + let _ = match Some(0) { + Some(0) => 0, + Some(1) => 1, + #[cfg(feature = "foo")] + Some(2) => 2, + _ => 1, + }; + + enum Foo { + X(u32), + Y(u32), + Z(u32), + } + + // Don't lint. `Foo::X(0)` and `Foo::Z(_)` overlap with the arm in between. + let _ = match Foo::X(0) { + Foo::X(0) => 1, + Foo::X(_) | Foo::Y(_) | Foo::Z(0) => 2, + Foo::Z(_) => 1, + _ => 0, + }; + + // Suggest moving `Foo::Z(_)` up. + let _ = match Foo::X(0) { + Foo::X(0) | Foo::Z(_) => 1, //~ ERROR: this match arm has an identical body to another arm + Foo::X(_) | Foo::Y(_) => 2, + _ => 0, + }; + + // Suggest moving `Foo::X(0)` down. + let _ = match Foo::X(0) { + Foo::Y(_) | Foo::Z(0) => 2, + Foo::Z(_) | Foo::X(0) => 1, //~ ERROR: this match arm has an identical body to another arm + _ => 0, + }; + + // Don't lint. + let _ = match 0 { + -2 => 1, + -5..=50 => 2, + -150..=88 => 1, + _ => 3, + }; + + struct Bar { + x: u32, + y: u32, + z: u32, + } + + // Lint. + let _ = match None { + Some(Bar { y: 10, z: 0, .. }) => 2, + None => 50, + Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, //~ ERROR: this match arm has an identical body to another arm + _ => 200, + }; + + let _ = match 0 { + 0 => todo!(), + 1 => todo!(), + 2 => core::convert::identity::(todo!()), + 3 => core::convert::identity::(todo!()), + _ => 5, + }; + + let _ = match 0 { + 1 | 0 => cfg!(not_enable), + _ => false, + }; +} + +// issue #8919, fixed on https://github.com/rust-lang/rust/pull/97312 +mod with_lifetime { + enum MaybeStaticStr<'a> { + Static(&'static str), + Borrowed(&'a str), + } + + impl<'a> MaybeStaticStr<'a> { + fn get(&self) -> &'a str { + match *self { + MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, + //~^ ERROR: this match arm has an identical body to another arm + } + } + } +} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs index 85ad0962eb4b..cc7425135cc4 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.rs +++ b/src/tools/clippy/tests/ui/match_same_arms2.rs @@ -2,9 +2,10 @@ #![allow( clippy::disallowed_names, clippy::diverging_sub_expression, - clippy::uninlined_format_args + clippy::uninlined_format_args, + clippy::match_single_binding, + clippy::match_like_matches_macro )] -//@no-rustfix fn bar(_: T) {} fn foo() -> bool { unimplemented!() @@ -261,3 +262,21 @@ fn main() { _ => false, }; } + +// issue #8919, fixed on https://github.com/rust-lang/rust/pull/97312 +mod with_lifetime { + enum MaybeStaticStr<'a> { + Static(&'static str), + Borrowed(&'a str), + } + + impl<'a> MaybeStaticStr<'a> { + fn get(&self) -> &'a str { + match *self { + MaybeStaticStr::Static(s) => s, + MaybeStaticStr::Borrowed(s) => s, + //~^ ERROR: this match arm has an identical body to another arm + } + } + } +} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr index f4c38c1af897..a5d137c658b7 100644 --- a/src/tools/clippy/tests/ui/match_same_arms2.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr @@ -1,18 +1,18 @@ error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms2.rs:15:9 + --> tests/ui/match_same_arms2.rs:16:9 | LL | / 42 => { LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; LL | | if true { ... | -LL | | a LL | | }, - | |_________^ help: try removing the arm +LL | | _ => { + | |________^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here - --> tests/ui/match_same_arms2.rs:24:9 + --> tests/ui/match_same_arms2.rs:25:9 | LL | / _ => { LL | | foo(); @@ -26,203 +26,216 @@ LL | | }, = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:38:9 + --> tests/ui/match_same_arms2.rs:39:9 | LL | 51 => foo(), - | --^^^^^^^^^ - | | - | help: try merging the arm patterns: `51 | 42` - | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:37:9 - | -LL | 42 => foo(), | ^^^^^^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 51 | 42 => foo(), + | ~~~~~~~ +help: and remove this obsolete arm + | +LL - 42 => foo(), + | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:44:9 + --> tests/ui/match_same_arms2.rs:45:9 | LL | None => 24, - | ----^^^^^^ - | | - | help: try merging the arm patterns: `None | Some(_)` + | ^^^^^^^^^^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:43:9 + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | None | Some(_) => 24, + | ~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - Some(_) => 24, | -LL | Some(_) => 24, - | ^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:66:9 + --> tests/ui/match_same_arms2.rs:67:9 | LL | (None, Some(a)) => bar(a), - | ---------------^^^^^^^^^^ - | | - | help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)` - | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:65:9 - | -LL | (Some(a), None) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | (None, Some(a)) | (Some(a), None) => bar(a), + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - (Some(a), None) => bar(a), + | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:80:9 + --> tests/ui/match_same_arms2.rs:81:9 | LL | (None, Some(a)) if a == 42 => a, - | ---------------^^^^^^^^^^^^^^^^ - | | - | help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)` - | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:79:9 - | -LL | (Some(a), None) if a == 42 => a, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | (None, Some(a)) | (Some(a), None) if a == 42 => a, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - (Some(a), None) if a == 42 => a, + | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:85:9 - | -LL | (Some(a), ..) => bar(a), - | -------------^^^^^^^^^^ - | | - | help: try merging the arm patterns: `(Some(a), ..) | (.., Some(a))` - | - = help: or try changing either arm body -note: other arm here --> tests/ui/match_same_arms2.rs:86:9 | -LL | (.., Some(a)) => bar(a), +LL | (Some(a), ..) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | (Some(a), ..) | (.., Some(a)) => bar(a), + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - (.., Some(a)) => bar(a), + | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:119:9 - | -LL | (Ok(x), Some(_)) => println!("ok {}", x), - | ----------------^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | help: try merging the arm patterns: `(Ok(x), Some(_)) | (Ok(_), Some(x))` - | - = help: or try changing either arm body -note: other arm here --> tests/ui/match_same_arms2.rs:120:9 | -LL | (Ok(_), Some(x)) => println!("ok {}", x), +LL | (Ok(x), Some(_)) => println!("ok {}", x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - (Ok(_), Some(x)) => println!("ok {}", x), + | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:135:9 + --> tests/ui/match_same_arms2.rs:136:9 | LL | Ok(_) => println!("ok"), - | -----^^^^^^^^^^^^^^^^^^ - | | - | help: try merging the arm patterns: `Ok(_) | Ok(3)` - | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:134:9 - | -LL | Ok(3) => println!("ok"), | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | Ok(_) | Ok(3) => println!("ok"), + | ~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - Ok(3) => println!("ok"), + | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:162:9 + --> tests/ui/match_same_arms2.rs:163:9 | -LL | 1 => { - | ^ help: try merging the arm patterns: `1 | 0` - | _________| - | | +LL | / 1 => { LL | | empty!(0); LL | | }, | |_________^ | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:159:9 + = help: try changing either arm body +help: or try merging the arm patterns | -LL | / 0 => { -LL | | empty!(0); -LL | | }, - | |_________^ - -error: match expression looks like `matches!` macro - --> tests/ui/match_same_arms2.rs:181:16 +LL | 1 | 0 => { + | ~~~~~ +help: and remove this obsolete arm | -LL | let _ans = match x { - | ________________^ -LL | | E::A => false, -LL | | E::B => false, -LL | | _ => true, -LL | | }; - | |_____^ help: try: `!matches!(x, E::A | E::B)` +LL - 0 => { +LL - empty!(0); +LL - }, | - = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::match_like_matches_macro)]` error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:213:9 - | -LL | Foo::X(0) => 1, - | ---------^^^^^ - | | - | help: try merging the arm patterns: `Foo::X(0) | Foo::Z(_)` - | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:215:9 - | -LL | Foo::Z(_) => 1, - | ^^^^^^^^^^^^^^ - -error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:223:9 - | -LL | Foo::Z(_) => 1, - | ---------^^^^^ - | | - | help: try merging the arm patterns: `Foo::Z(_) | Foo::X(0)` - | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:221:9 + --> tests/ui/match_same_arms2.rs:214:9 | LL | Foo::X(0) => 1, | ^^^^^^^^^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | Foo::X(0) | Foo::Z(_) => 1, + | ~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - Foo::Z(_) => 1, + | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:246:9 + --> tests/ui/match_same_arms2.rs:224:9 + | +LL | Foo::Z(_) => 1, + | ^^^^^^^^^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | Foo::Z(_) | Foo::X(0) => 1, + | ~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - Foo::X(0) => 1, + | + +error: this match arm has an identical body to another arm + --> tests/ui/match_same_arms2.rs:247:9 | LL | Some(Bar { y: 0, x: 5, .. }) => 1, - | ----------------------------^^^^^ - | | - | help: try merging the arm patterns: `Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. })` - | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:243:9 - | -LL | Some(Bar { x: 0, y: 5, .. }) => 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - Some(Bar { x: 0, y: 5, .. }) => 1, + | error: this match arm has an identical body to another arm - --> tests/ui/match_same_arms2.rs:260:9 + --> tests/ui/match_same_arms2.rs:261:9 | LL | 1 => cfg!(not_enable), - | -^^^^^^^^^^^^^^^^^^^^ - | | - | help: try merging the arm patterns: `1 | 0` - | - = help: or try changing either arm body -note: other arm here - --> tests/ui/match_same_arms2.rs:259:9 - | -LL | 0 => cfg!(not_enable), | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | 1 | 0 => cfg!(not_enable), + | ~~~~~ +help: and remove this obsolete arm + | +LL - 0 => cfg!(not_enable), + | + +error: this match arm has an identical body to another arm + --> tests/ui/match_same_arms2.rs:277:17 + | +LL | MaybeStaticStr::Borrowed(s) => s, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try changing either arm body +help: or try merging the arm patterns + | +LL | MaybeStaticStr::Borrowed(s) | MaybeStaticStr::Static(s) => s, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: and remove this obsolete arm + | +LL - MaybeStaticStr::Static(s) => s, + | error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed new file mode 100644 index 000000000000..804c0a869a9f --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed @@ -0,0 +1,61 @@ +#![feature(non_exhaustive_omitted_patterns_lint)] +#![warn(clippy::match_same_arms)] +#![no_main] +use std::sync::atomic::Ordering; // #[non_exhaustive] enum + +fn repeat() -> ! { + panic!() +} + +pub fn f(x: Ordering) { + #[deny(non_exhaustive_omitted_patterns)] + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + Ordering::AcqRel | Ordering::SeqCst => repeat(), + _ => repeat(), + } +} + +mod f { + #![deny(non_exhaustive_omitted_patterns)] + + use super::*; + + pub fn f(x: Ordering) { + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + Ordering::AcqRel | Ordering::SeqCst => repeat(), + _ => repeat(), + } + } +} + +// Below should still lint + +pub fn g(x: Ordering) { + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + //~^ ERROR: this match arm has an identical body to the `_` wildcard arm + _ => repeat(), + } +} + +mod g { + use super::*; + + pub fn g(x: Ordering) { + match x { + Ordering::Relaxed => println!("relaxed"), + Ordering::Release => println!("release"), + Ordering::Acquire => println!("acquire"), + //~^ ERROR: this match arm has an identical body to the `_` wildcard arm + _ => repeat(), + } + } +} diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs index 5c277f925a8f..e50663932a1a 100644 --- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs @@ -1,7 +1,6 @@ #![feature(non_exhaustive_omitted_patterns_lint)] #![warn(clippy::match_same_arms)] #![no_main] -//@no-rustfix use std::sync::atomic::Ordering; // #[non_exhaustive] enum fn repeat() -> ! { diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr index cf2a75354e15..aa7f8c95dce8 100644 --- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr +++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr @@ -1,12 +1,13 @@ error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms_non_exhaustive.rs:45:9 + --> tests/ui/match_same_arms_non_exhaustive.rs:44:9 | -LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm +LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(), +LL | | + | |________^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here - --> tests/ui/match_same_arms_non_exhaustive.rs:47:9 + --> tests/ui/match_same_arms_non_exhaustive.rs:46:9 | LL | _ => repeat(), | ^^^^^^^^^^^^^ @@ -14,14 +15,15 @@ LL | _ => repeat(), = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]` error: this match arm has an identical body to the `_` wildcard arm - --> tests/ui/match_same_arms_non_exhaustive.rs:59:13 + --> tests/ui/match_same_arms_non_exhaustive.rs:58:13 | -LL | Ordering::AcqRel | Ordering::SeqCst => repeat(), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm +LL | / Ordering::AcqRel | Ordering::SeqCst => repeat(), +LL | | + | |____________^ help: try removing the arm | = help: or try changing either arm body note: `_` wildcard arm here - --> tests/ui/match_same_arms_non_exhaustive.rs:61:13 + --> tests/ui/match_same_arms_non_exhaustive.rs:60:13 | LL | _ => repeat(), | ^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed deleted file mode 100644 index de02b2bee31f..000000000000 --- a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::mismatched_target_os)] -#![allow(unused)] - -#[cfg(target_os = "hermit")] -fn hermit() {} - -#[cfg(target_os = "wasi")] -fn wasi() {} - -#[cfg(target_os = "none")] -fn none() {} - -// list with conditions -#[cfg(all(not(windows), target_os = "wasi"))] -fn list() {} - -// windows is a valid target family, should be ignored -#[cfg(windows)] -fn windows() {} - -// correct use, should be ignored -#[cfg(target_os = "hermit")] -fn correct() {} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs deleted file mode 100644 index a960518751bf..000000000000 --- a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::mismatched_target_os)] -#![allow(unused)] - -#[cfg(hermit)] -fn hermit() {} - -#[cfg(wasi)] -fn wasi() {} - -#[cfg(none)] -fn none() {} - -// list with conditions -#[cfg(all(not(windows), wasi))] -fn list() {} - -// windows is a valid target family, should be ignored -#[cfg(windows)] -fn windows() {} - -// correct use, should be ignored -#[cfg(target_os = "hermit")] -fn correct() {} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr deleted file mode 100644 index 7f7a4e9d6f62..000000000000 --- a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr +++ /dev/null @@ -1,37 +0,0 @@ -error: operating system used in target family position - --> tests/ui/mismatched_target_os_non_unix.rs:4:1 - | -LL | #[cfg(hermit)] - | ^^^^^^------^^ - | | - | help: try: `target_os = "hermit"` - | - = note: `-D clippy::mismatched-target-os` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::mismatched_target_os)]` - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_non_unix.rs:7:1 - | -LL | #[cfg(wasi)] - | ^^^^^^----^^ - | | - | help: try: `target_os = "wasi"` - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_non_unix.rs:10:1 - | -LL | #[cfg(none)] - | ^^^^^^----^^ - | | - | help: try: `target_os = "none"` - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_non_unix.rs:14:1 - | -LL | #[cfg(all(not(windows), wasi))] - | ^^^^^^^^^^^^^^^^^^^^^^^^----^^^ - | | - | help: try: `target_os = "wasi"` - -error: aborting due to 4 previous errors - diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed b/src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed deleted file mode 100644 index b945c4d9619d..000000000000 --- a/src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed +++ /dev/null @@ -1,60 +0,0 @@ -#![warn(clippy::mismatched_target_os)] -#![allow(unused)] - -#[cfg(target_os = "linux")] -fn linux() {} - -#[cfg(target_os = "freebsd")] -fn freebsd() {} - -#[cfg(target_os = "dragonfly")] -fn dragonfly() {} - -#[cfg(target_os = "openbsd")] -fn openbsd() {} - -#[cfg(target_os = "netbsd")] -fn netbsd() {} - -#[cfg(target_os = "macos")] -fn macos() {} - -#[cfg(target_os = "ios")] -fn ios() {} - -#[cfg(target_os = "android")] -fn android() {} - -#[cfg(target_os = "emscripten")] -fn emscripten() {} - -#[cfg(target_os = "fuchsia")] -fn fuchsia() {} - -#[cfg(target_os = "haiku")] -fn haiku() {} - -#[cfg(target_os = "illumos")] -fn illumos() {} - -#[cfg(target_os = "l4re")] -fn l4re() {} - -#[cfg(target_os = "redox")] -fn redox() {} - -#[cfg(target_os = "solaris")] -fn solaris() {} - -#[cfg(target_os = "vxworks")] -fn vxworks() {} - -// list with conditions -#[cfg(all(not(any(target_os = "solaris", target_os = "linux")), target_os = "freebsd"))] -fn list() {} - -// correct use, should be ignored -#[cfg(target_os = "freebsd")] -fn correct() {} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.rs b/src/tools/clippy/tests/ui/mismatched_target_os_unix.rs deleted file mode 100644 index 34307facd654..000000000000 --- a/src/tools/clippy/tests/ui/mismatched_target_os_unix.rs +++ /dev/null @@ -1,60 +0,0 @@ -#![warn(clippy::mismatched_target_os)] -#![allow(unused)] - -#[cfg(linux)] -fn linux() {} - -#[cfg(freebsd)] -fn freebsd() {} - -#[cfg(dragonfly)] -fn dragonfly() {} - -#[cfg(openbsd)] -fn openbsd() {} - -#[cfg(netbsd)] -fn netbsd() {} - -#[cfg(macos)] -fn macos() {} - -#[cfg(ios)] -fn ios() {} - -#[cfg(android)] -fn android() {} - -#[cfg(emscripten)] -fn emscripten() {} - -#[cfg(fuchsia)] -fn fuchsia() {} - -#[cfg(haiku)] -fn haiku() {} - -#[cfg(illumos)] -fn illumos() {} - -#[cfg(l4re)] -fn l4re() {} - -#[cfg(redox)] -fn redox() {} - -#[cfg(solaris)] -fn solaris() {} - -#[cfg(vxworks)] -fn vxworks() {} - -// list with conditions -#[cfg(all(not(any(solaris, linux)), freebsd))] -fn list() {} - -// correct use, should be ignored -#[cfg(target_os = "freebsd")] -fn correct() {} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr b/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr deleted file mode 100644 index 3071bad1324b..000000000000 --- a/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr +++ /dev/null @@ -1,184 +0,0 @@ -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:4:1 - | -LL | #[cfg(linux)] - | ^^^^^^-----^^ - | | - | help: try: `target_os = "linux"` - | - = help: did you mean `unix`? - = note: `-D clippy::mismatched-target-os` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::mismatched_target_os)]` - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:7:1 - | -LL | #[cfg(freebsd)] - | ^^^^^^-------^^ - | | - | help: try: `target_os = "freebsd"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:10:1 - | -LL | #[cfg(dragonfly)] - | ^^^^^^---------^^ - | | - | help: try: `target_os = "dragonfly"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:13:1 - | -LL | #[cfg(openbsd)] - | ^^^^^^-------^^ - | | - | help: try: `target_os = "openbsd"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:16:1 - | -LL | #[cfg(netbsd)] - | ^^^^^^------^^ - | | - | help: try: `target_os = "netbsd"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:19:1 - | -LL | #[cfg(macos)] - | ^^^^^^-----^^ - | | - | help: try: `target_os = "macos"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:22:1 - | -LL | #[cfg(ios)] - | ^^^^^^---^^ - | | - | help: try: `target_os = "ios"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:25:1 - | -LL | #[cfg(android)] - | ^^^^^^-------^^ - | | - | help: try: `target_os = "android"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:28:1 - | -LL | #[cfg(emscripten)] - | ^^^^^^----------^^ - | | - | help: try: `target_os = "emscripten"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:31:1 - | -LL | #[cfg(fuchsia)] - | ^^^^^^-------^^ - | | - | help: try: `target_os = "fuchsia"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:34:1 - | -LL | #[cfg(haiku)] - | ^^^^^^-----^^ - | | - | help: try: `target_os = "haiku"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:37:1 - | -LL | #[cfg(illumos)] - | ^^^^^^-------^^ - | | - | help: try: `target_os = "illumos"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:40:1 - | -LL | #[cfg(l4re)] - | ^^^^^^----^^ - | | - | help: try: `target_os = "l4re"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:43:1 - | -LL | #[cfg(redox)] - | ^^^^^^-----^^ - | | - | help: try: `target_os = "redox"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:46:1 - | -LL | #[cfg(solaris)] - | ^^^^^^-------^^ - | | - | help: try: `target_os = "solaris"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:49:1 - | -LL | #[cfg(vxworks)] - | ^^^^^^-------^^ - | | - | help: try: `target_os = "vxworks"` - | - = help: did you mean `unix`? - -error: operating system used in target family position - --> tests/ui/mismatched_target_os_unix.rs:53:1 - | -LL | #[cfg(all(not(any(solaris, linux)), freebsd))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: did you mean `unix`? -help: try - | -LL | #[cfg(all(not(any(target_os = "solaris", linux)), freebsd))] - | ~~~~~~~~~~~~~~~~~~~~~ -help: try - | -LL | #[cfg(all(not(any(solaris, target_os = "linux")), freebsd))] - | ~~~~~~~~~~~~~~~~~~~ -help: try - | -LL | #[cfg(all(not(any(solaris, linux)), target_os = "freebsd"))] - | ~~~~~~~~~~~~~~~~~~~~~ - -error: aborting due to 17 previous errors - diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs index d026e009684a..2750e0cdf3f7 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -161,7 +161,23 @@ union U { f: u32, } -// Do not lint because accessing union fields from const functions is unstable +// Do not lint because accessing union fields from const functions is unstable in 1.55 +#[clippy::msrv = "1.55"] fn h(u: U) -> u32 { unsafe { u.f } } + +mod msrv { + struct Foo(*const u8, *mut u8); + + impl Foo { + #[clippy::msrv = "1.57"] + fn deref_ptr_cannot_be_const(self) -> usize { + unsafe { *self.0 as usize } + } + #[clippy::msrv = "1.58"] + fn deref_mut_ptr_cannot_be_const(self) -> usize { + unsafe { *self.1 as usize } + } + } +} diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs index 12a8320c8f32..58e639cc7fd1 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -113,3 +113,61 @@ impl const Drop for D { // Lint this, since it can be dropped in const contexts // FIXME(effects) fn d(this: D) {} + +mod msrv { + struct Foo(*const u8, &'static u8); + + impl Foo { + #[clippy::msrv = "1.58"] + fn deref_ptr_can_be_const(self) -> usize { + //~^ ERROR: this could be a `const fn` + unsafe { *self.0 as usize } + } + + fn deref_copied_val(self) -> usize { + //~^ ERROR: this could be a `const fn` + *self.1 as usize + } + } + + union Bar { + val: u8, + } + + #[clippy::msrv = "1.56"] + fn union_access_can_be_const() { + //~^ ERROR: this could be a `const fn` + let bar = Bar { val: 1 }; + let _ = unsafe { bar.val }; + } +} + +mod issue12677 { + pub struct Wrapper { + pub strings: Vec, + } + + impl Wrapper { + #[must_use] + pub fn new(strings: Vec) -> Self { + Self { strings } + } + + #[must_use] + pub fn empty() -> Self { + Self { strings: Vec::new() } + } + } + + pub struct Other { + pub text: String, + pub vec: Vec, + } + + impl Other { + pub fn new(text: String) -> Self { + let vec = Vec::new(); + Self { text, vec } + } + } +} diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr index 082459fd8212..1c61c3e87132 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -102,5 +102,58 @@ LL | | 46 LL | | } | |_^ -error: aborting due to 11 previous errors +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:122:9 + | +LL | / fn deref_ptr_can_be_const(self) -> usize { +LL | | +LL | | unsafe { *self.0 as usize } +LL | | } + | |_________^ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:127:9 + | +LL | / fn deref_copied_val(self) -> usize { +LL | | +LL | | *self.1 as usize +LL | | } + | |_________^ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:138:5 + | +LL | / fn union_access_can_be_const() { +LL | | +LL | | let bar = Bar { val: 1 }; +LL | | let _ = unsafe { bar.val }; +LL | | } + | |_____^ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:152:9 + | +LL | / pub fn new(strings: Vec) -> Self { +LL | | Self { strings } +LL | | } + | |_________^ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:157:9 + | +LL | / pub fn empty() -> Self { +LL | | Self { strings: Vec::new() } +LL | | } + | |_________^ + +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:168:9 + | +LL | / pub fn new(text: String) -> Self { +LL | | let vec = Vec::new(); +LL | | Self { text, vec } +LL | | } + | |_________^ + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/missing_fields_in_debug.rs b/src/tools/clippy/tests/ui/missing_fields_in_debug.rs index e91e8ab7f475..68867ec819d1 100644 --- a/src/tools/clippy/tests/ui/missing_fields_in_debug.rs +++ b/src/tools/clippy/tests/ui/missing_fields_in_debug.rs @@ -4,6 +4,7 @@ use std::fmt; use std::marker::PhantomData; use std::ops::Deref; +use std::thread::LocalKey; struct NamedStruct1Ignored { data: u8, @@ -191,4 +192,21 @@ impl fmt::Debug for WithPD { } } +struct InClosure { + a: u8, + b: String, +} + +impl fmt::Debug for InClosure { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_struct("InClosure"); + d.field("a", &self.a); + let mut c = || { + d.field("b", &self.b); + }; + c(); + d.finish() + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr b/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr index 6f8a9abe9228..8c1810909dd8 100644 --- a/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr +++ b/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr @@ -1,5 +1,5 @@ error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:13:1 + --> tests/ui/missing_fields_in_debug.rs:14:1 | LL | / impl fmt::Debug for NamedStruct1Ignored { LL | | @@ -11,7 +11,7 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:10:5 + --> tests/ui/missing_fields_in_debug.rs:11:5 | LL | hidden: u32, | ^^^^^^^^^^^ @@ -21,7 +21,7 @@ LL | hidden: u32, = help: to override `-D warnings` add `#[allow(clippy::missing_fields_in_debug)]` error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:32:1 + --> tests/ui/missing_fields_in_debug.rs:33:1 | LL | / impl fmt::Debug for NamedStructMultipleIgnored { LL | | @@ -33,17 +33,17 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:26:5 + --> tests/ui/missing_fields_in_debug.rs:27:5 | LL | hidden: u32, | ^^^^^^^^^^^ note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:27:5 + --> tests/ui/missing_fields_in_debug.rs:28:5 | LL | hidden2: String, | ^^^^^^^^^^^^^^^ note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:29:5 + --> tests/ui/missing_fields_in_debug.rs:30:5 | LL | hidden4: ((((u8), u16), u32), u64), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -51,7 +51,7 @@ LL | hidden4: ((((u8), u16), u32), u64), = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:94:1 + --> tests/ui/missing_fields_in_debug.rs:95:1 | LL | / impl fmt::Debug for MultiExprDebugImpl { LL | | @@ -63,7 +63,7 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:90:5 + --> tests/ui/missing_fields_in_debug.rs:91:5 | LL | b: String, | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.rs b/src/tools/clippy/tests/ui/missing_panics_doc.rs index 0e1533fc1ab1..b0fa8e988598 100644 --- a/src/tools/clippy/tests/ui/missing_panics_doc.rs +++ b/src/tools/clippy/tests/ui/missing_panics_doc.rs @@ -191,3 +191,11 @@ fn from_declared_macro_should_lint_at_macrosite() { // Not here. some_macro_that_panics!() } + +pub fn issue_12760() { + const { + if N == 0 { + panic!(); + } + } +} diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed index 3059de8f89c4..ec63c4fd6a26 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed @@ -131,3 +131,15 @@ fn needless_bool_condition() -> bool { foo() } + +fn issue12846() { + let a = true; + let b = false; + + // parentheses are needed here + let _x = (a && b).then(|| todo!()); + let _x = (a && b) as u8; + + // parentheses are not needed here + let _x = a.then(|| todo!()); +} diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.rs b/src/tools/clippy/tests/ui/needless_bool/fixable.rs index b2cbe86e2235..8694aa715908 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.rs +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.rs @@ -191,3 +191,15 @@ fn needless_bool_condition() -> bool { foo() } + +fn issue12846() { + let a = true; + let b = false; + + // parentheses are needed here + let _x = if a && b { true } else { false }.then(|| todo!()); + let _x = if a && b { true } else { false } as u8; + + // parentheses are not needed here + let _x = if a { true } else { false }.then(|| todo!()); +} diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr index 9746e931f50f..99b5b9983448 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr @@ -191,5 +191,23 @@ error: this if-then-else expression returns a bool literal LL | if unsafe { no(4) } & 1 != 0 { true } else { false } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` -error: aborting due to 21 previous errors +error: this if-then-else expression returns a bool literal + --> tests/ui/needless_bool/fixable.rs:200:14 + | +LL | let _x = if a && b { true } else { false }.then(|| todo!()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(a && b)` + +error: this if-then-else expression returns a bool literal + --> tests/ui/needless_bool/fixable.rs:201:14 + | +LL | let _x = if a && b { true } else { false } as u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(a && b)` + +error: this if-then-else expression returns a bool literal + --> tests/ui/needless_bool/fixable.rs:204:14 + | +LL | let _x = if a { true } else { false }.then(|| todo!()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `a` + +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed index bd7a9a0b9840..5478372cbe00 100644 --- a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed +++ b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed @@ -141,8 +141,8 @@ fn main() { let f = |arg| { let loc = "loc".to_owned(); let _ = std::fs::write("x", &env); // Don't lint. In environment - let _ = std::fs::write("x", arg); - let _ = std::fs::write("x", loc); + let _ = std::fs::write("x", &arg); + let _ = std::fs::write("x", &loc); }; let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` f(arg); @@ -158,13 +158,13 @@ fn main() { fn f(_: impl Debug) {} let x = X; - f(&x); // Don't lint. Has significant drop + f(&x); // Don't lint, not copy, passed by a reference to a variable } { fn f(_: impl AsRef) {} let x = String::new(); - f(x); + f(&x); } { fn f(_: impl AsRef) {} @@ -299,4 +299,38 @@ fn main() { check_str(&owner.0); // Don't lint. `owner` can't be partially moved because it impl Drop } } + { + #[derive(Debug)] + struct X(Vec); + + fn f(_: impl Debug) {} + + let x = X(vec![]); + f(&x); // Don't lint, makes x unavailable later + } + { + #[derive(Debug)] + struct X; + + impl Drop for X { + fn drop(&mut self) {} + } + + fn f(_: impl Debug) {} + + #[derive(Debug)] + struct Y(X); + + let y = Y(X); + f(&y); // Don't lint. Not copy, passed by a reference to value + } + { + fn f(_: impl AsRef) {} + let x = String::new(); + f(&x); // Don't lint, not a copy, makes it unavailable later + f(String::new()); // Lint, makes no difference + let y = "".to_owned(); + f(&y); // Don't lint + f("".to_owned()); // Lint + } } diff --git a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs index 5cfd4ce30cc5..2643815d939b 100644 --- a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs @@ -158,7 +158,7 @@ fn main() { fn f(_: impl Debug) {} let x = X; - f(&x); // Don't lint. Has significant drop + f(&x); // Don't lint, not copy, passed by a reference to a variable } { fn f(_: impl AsRef) {} @@ -299,4 +299,38 @@ fn main() { check_str(&owner.0); // Don't lint. `owner` can't be partially moved because it impl Drop } } + { + #[derive(Debug)] + struct X(Vec); + + fn f(_: impl Debug) {} + + let x = X(vec![]); + f(&x); // Don't lint, makes x unavailable later + } + { + #[derive(Debug)] + struct X; + + impl Drop for X { + fn drop(&mut self) {} + } + + fn f(_: impl Debug) {} + + #[derive(Debug)] + struct Y(X); + + let y = Y(X); + f(&y); // Don't lint. Not copy, passed by a reference to value + } + { + fn f(_: impl AsRef) {} + let x = String::new(); + f(&x); // Don't lint, not a copy, makes it unavailable later + f(&String::new()); // Lint, makes no difference + let y = "".to_owned(); + f(&y); // Don't lint + f(&"".to_owned()); // Lint + } } diff --git a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.stderr b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.stderr index 83c076f8d863..fba0755d14b5 100644 --- a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.stderr +++ b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.stderr @@ -49,29 +49,23 @@ error: the borrowed expression implements the required traits LL | let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` -error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:144:41 - | -LL | let _ = std::fs::write("x", &arg); - | ^^^^ help: change this to: `arg` - -error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:145:41 - | -LL | let _ = std::fs::write("x", &loc); - | ^^^^ help: change this to: `loc` - -error: the borrowed expression implements the required traits - --> tests/ui/needless_borrows_for_generic_args.rs:167:11 - | -LL | f(&x); - | ^^ help: change this to: `x` - error: the borrowed expression implements the required traits --> tests/ui/needless_borrows_for_generic_args.rs:247:13 | LL | foo(&a); | ^^ help: change this to: `a` -error: aborting due to 12 previous errors +error: the borrowed expression implements the required traits + --> tests/ui/needless_borrows_for_generic_args.rs:331:11 + | +LL | f(&String::new()); // Lint, makes no difference + | ^^^^^^^^^^^^^^ help: change this to: `String::new()` + +error: the borrowed expression implements the required traits + --> tests/ui/needless_borrows_for_generic_args.rs:334:11 + | +LL | f(&"".to_owned()); // Lint + | ^^^^^^^^^^^^^^ help: change this to: `"".to_owned()` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/needless_character_iteration.fixed b/src/tools/clippy/tests/ui/needless_character_iteration.fixed new file mode 100644 index 000000000000..f0bf84a41d7e --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_character_iteration.fixed @@ -0,0 +1,57 @@ +#![warn(clippy::needless_character_iteration)] +#![allow(clippy::map_identity, clippy::unnecessary_operation)] + +#[derive(Default)] +struct S { + field: &'static str, +} + +impl S { + fn field(&self) -> &str { + self.field + } +} + +fn magic(_: char) {} + +fn main() { + "foo".is_ascii(); + //~^ ERROR: checking if a string is ascii using iterators + !"foo".is_ascii(); + //~^ ERROR: checking if a string is ascii using iterators + "foo".is_ascii(); + //~^ ERROR: checking if a string is ascii using iterators + !"foo".is_ascii(); + //~^ ERROR: checking if a string is ascii using iterators + + let s = String::new(); + s.is_ascii(); + //~^ ERROR: checking if a string is ascii using iterators + !"foo".to_string().is_ascii(); + //~^ ERROR: checking if a string is ascii using iterators + + "foo".is_ascii(); + !"foo".is_ascii(); + + S::default().field().is_ascii(); + //~^ ERROR: checking if a string is ascii using iterators + + // Should not lint! + "foo".chars().all(|c| { + let x = c; + magic(x); + x.is_ascii() + }); + + // Should not lint! + "foo".chars().all(|c| c.is_ascii() && c.is_alphabetic()); + + // Should not lint! + "foo".chars().map(|c| c).all(|c| !char::is_ascii(&c)); + + // Should not lint! + "foo".chars().all(|c| !c.is_ascii()); + + // Should not lint! + "foo".chars().any(|c| c.is_ascii()); +} diff --git a/src/tools/clippy/tests/ui/needless_character_iteration.rs b/src/tools/clippy/tests/ui/needless_character_iteration.rs new file mode 100644 index 000000000000..2805d2438b4a --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_character_iteration.rs @@ -0,0 +1,65 @@ +#![warn(clippy::needless_character_iteration)] +#![allow(clippy::map_identity, clippy::unnecessary_operation)] + +#[derive(Default)] +struct S { + field: &'static str, +} + +impl S { + fn field(&self) -> &str { + self.field + } +} + +fn magic(_: char) {} + +fn main() { + "foo".chars().all(|c| c.is_ascii()); + //~^ ERROR: checking if a string is ascii using iterators + "foo".chars().any(|c| !c.is_ascii()); + //~^ ERROR: checking if a string is ascii using iterators + "foo".chars().all(|c| char::is_ascii(&c)); + //~^ ERROR: checking if a string is ascii using iterators + "foo".chars().any(|c| !char::is_ascii(&c)); + //~^ ERROR: checking if a string is ascii using iterators + + let s = String::new(); + s.chars().all(|c| c.is_ascii()); + //~^ ERROR: checking if a string is ascii using iterators + "foo".to_string().chars().any(|c| !c.is_ascii()); + //~^ ERROR: checking if a string is ascii using iterators + + "foo".chars().all(|c| { + //~^ ERROR: checking if a string is ascii using iterators + let x = c; + x.is_ascii() + }); + "foo".chars().any(|c| { + //~^ ERROR: checking if a string is ascii using iterators + let x = c; + !x.is_ascii() + }); + + S::default().field().chars().all(|x| x.is_ascii()); + //~^ ERROR: checking if a string is ascii using iterators + + // Should not lint! + "foo".chars().all(|c| { + let x = c; + magic(x); + x.is_ascii() + }); + + // Should not lint! + "foo".chars().all(|c| c.is_ascii() && c.is_alphabetic()); + + // Should not lint! + "foo".chars().map(|c| c).all(|c| !char::is_ascii(&c)); + + // Should not lint! + "foo".chars().all(|c| !c.is_ascii()); + + // Should not lint! + "foo".chars().any(|c| c.is_ascii()); +} diff --git a/src/tools/clippy/tests/ui/needless_character_iteration.stderr b/src/tools/clippy/tests/ui/needless_character_iteration.stderr new file mode 100644 index 000000000000..7966033555f5 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_character_iteration.stderr @@ -0,0 +1,67 @@ +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:18:5 + | +LL | "foo".chars().all(|c| c.is_ascii()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"foo".is_ascii()` + | + = note: `-D clippy::needless-character-iteration` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_character_iteration)]` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:20:5 + | +LL | "foo".chars().any(|c| !c.is_ascii()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!"foo".is_ascii()` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:22:5 + | +LL | "foo".chars().all(|c| char::is_ascii(&c)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"foo".is_ascii()` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:24:5 + | +LL | "foo".chars().any(|c| !char::is_ascii(&c)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!"foo".is_ascii()` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:28:5 + | +LL | s.chars().all(|c| c.is_ascii()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.is_ascii()` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:30:5 + | +LL | "foo".to_string().chars().any(|c| !c.is_ascii()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!"foo".to_string().is_ascii()` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:33:5 + | +LL | / "foo".chars().all(|c| { +LL | | +LL | | let x = c; +LL | | x.is_ascii() +LL | | }); + | |______^ help: try: `"foo".is_ascii()` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:38:5 + | +LL | / "foo".chars().any(|c| { +LL | | +LL | | let x = c; +LL | | !x.is_ascii() +LL | | }); + | |______^ help: try: `!"foo".is_ascii()` + +error: checking if a string is ascii using iterators + --> tests/ui/needless_character_iteration.rs:44:5 + | +LL | S::default().field().chars().all(|x| x.is_ascii()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `S::default().field().is_ascii()` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_late_init.stderr b/src/tools/clippy/tests/ui/needless_late_init.stderr index 1695784030d0..ce64861fa40a 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.stderr +++ b/src/tools/clippy/tests/ui/needless_late_init.stderr @@ -8,10 +8,11 @@ LL | a = "zero"; | = note: `-D clippy::needless-late-init` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_late_init)]` -help: declare `a` here +help: move the declaration `a` here + | +LL ~ +LL ~ let a = "zero"; | -LL | let a = "zero"; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:30:5 @@ -22,10 +23,12 @@ LL | let c; LL | b = 1; | ^^^^^ initialised here | -help: declare `b` here +help: move the declaration `b` here + | +LL ~ +LL | let c; +LL ~ let b = 1; | -LL | let b = 1; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:31:5 @@ -36,10 +39,12 @@ LL | b = 1; LL | c = 2; | ^^^^^ initialised here | -help: declare `c` here +help: move the declaration `c` here + | +LL ~ +LL | b = 1; +LL ~ let c = 2; | -LL | let c = 2; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:35:5 @@ -49,10 +54,11 @@ LL | let d: usize; LL | d = 1; | ^^^^^ initialised here | -help: declare `d` here +help: move the declaration `d` here + | +LL ~ +LL ~ let d: usize = 1; | -LL | let d: usize = 1; - | ~~~~~~~~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:38:5 @@ -62,10 +68,11 @@ LL | let e; LL | e = format!("{}", d); | ^^^^^^^^^^^^^^^^^^^^ initialised here | -help: declare `e` here +help: move the declaration `e` here + | +LL ~ +LL ~ let e = format!("{}", d); | -LL | let e = format!("{}", d); - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:43:5 @@ -73,20 +80,17 @@ error: unneeded late initialization LL | let a; | ^^^^^^ | -help: declare `a` here - | -LL | let a = match n { - | +++++++ -help: remove the assignments from the `match` arms +help: move the declaration `a` here and remove the assignments from the `match` arms | +LL ~ +LL | let n = 1; +LL ~ let a = match n { LL ~ 1 => "one", LL | _ => { LL ~ "two" +LL | }, +LL ~ }; | -help: add a semicolon after the `match` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:52:5 @@ -94,20 +98,15 @@ error: unneeded late initialization LL | let b; | ^^^^^^ | -help: declare `b` here - | -LL | let b = if n == 3 { - | +++++++ -help: remove the assignments from the branches +help: move the declaration `b` here and remove the assignments from the branches | +LL ~ +LL ~ let b = if n == 3 { LL ~ "four" LL | } else { LL ~ "five" +LL ~ }; | -help: add a semicolon after the `if` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:59:5 @@ -115,20 +114,16 @@ error: unneeded late initialization LL | let d; | ^^^^^^ | -help: declare `d` here - | -LL | let d = if true { - | +++++++ -help: remove the assignments from the branches +help: move the declaration `d` here and remove the assignments from the branches | +LL ~ +LL ~ let d = if true { +LL | let temp = 5; LL ~ temp LL | } else { LL ~ 15 +LL ~ }; | -help: add a semicolon after the `if` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:67:5 @@ -136,20 +131,15 @@ error: unneeded late initialization LL | let e; | ^^^^^^ | -help: declare `e` here - | -LL | let e = if true { - | +++++++ -help: remove the assignments from the branches +help: move the declaration `e` here and remove the assignments from the branches | +LL ~ +LL ~ let e = if true { LL ~ format!("{} {}", a, b) LL | } else { LL ~ format!("{}", n) +LL ~ }; | -help: add a semicolon after the `if` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:74:5 @@ -157,14 +147,11 @@ error: unneeded late initialization LL | let f; | ^^^^^^ | -help: declare `f` here +help: move the declaration `f` here and remove the assignments from the `match` arms | -LL | let f = match 1 { - | +++++++ -help: remove the assignments from the `match` arms - | -LL - 1 => f = "three", -LL + 1 => "three", +LL ~ +LL ~ let f = match 1 { +LL ~ 1 => "three", | error: unneeded late initialization @@ -173,19 +160,15 @@ error: unneeded late initialization LL | let g: usize; | ^^^^^^^^^^^^^ | -help: declare `g` here +help: move the declaration `g` here and remove the assignments from the branches | -LL | let g: usize = if true { - | ++++++++++++++ -help: remove the assignments from the branches +LL ~ +LL ~ let g: usize = if true { +LL ~ 5 +LL | } else { +LL | panic!(); +LL ~ }; | -LL - g = 5; -LL + 5 - | -help: add a semicolon after the `if` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:88:5 @@ -196,10 +179,12 @@ LL | let y = SignificantDrop; LL | x = 1; | ^^^^^ initialised here | -help: declare `x` here +help: move the declaration `x` here + | +LL ~ +LL | let y = SignificantDrop; +LL ~ let x = 1; | -LL | let x = 1; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:92:5 @@ -210,10 +195,12 @@ LL | let y = 1; LL | x = SignificantDrop; | ^^^^^^^^^^^^^^^^^^^ initialised here | -help: declare `x` here +help: move the declaration `x` here + | +LL ~ +LL | let y = 1; +LL ~ let x = SignificantDrop; | -LL | let x = SignificantDrop; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:96:5 @@ -224,10 +211,14 @@ LL | let x; LL | x = SignificantDrop; | ^^^^^^^^^^^^^^^^^^^ initialised here | -help: declare `x` here +help: move the declaration `x` here + | +LL ~ +LL | // types that should be considered insignificant + ... +LL | let y = Box::new(4); +LL ~ let x = SignificantDrop; | -LL | let x = SignificantDrop; - | ~~~~~ error: unneeded late initialization --> tests/ui/needless_late_init.rs:115:5 @@ -235,20 +226,17 @@ error: unneeded late initialization LL | let a; | ^^^^^^ | -help: declare `a` here - | -LL | let a = match n { - | +++++++ -help: remove the assignments from the `match` arms +help: move the declaration `a` here and remove the assignments from the `match` arms | +LL ~ +LL | let n = 1; +LL ~ let a = match n { LL ~ 1 => f().await, LL | _ => { LL ~ "two" +LL | }, +LL ~ }; | -help: add a semicolon after the `match` expression - | -LL | }; - | + error: unneeded late initialization --> tests/ui/needless_late_init.rs:132:5 @@ -256,20 +244,17 @@ error: unneeded late initialization LL | let a; | ^^^^^^ | -help: declare `a` here - | -LL | let a = match n { - | +++++++ -help: remove the assignments from the `match` arms +help: move the declaration `a` here and remove the assignments from the `match` arms | +LL ~ +LL | let n = 1; +LL ~ let a = match n { LL ~ 1 => f(), LL | _ => { LL ~ "two" +LL | }, +LL ~ }; | -help: add a semicolon after the `match` expression - | -LL | }; - | + error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/needless_maybe_sized.fixed b/src/tools/clippy/tests/ui/needless_maybe_sized.fixed new file mode 100644 index 000000000000..4d24a7cee619 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_maybe_sized.fixed @@ -0,0 +1,116 @@ +//@aux-build:proc_macros.rs + +#![allow(unused, clippy::multiple_bound_locations)] +#![warn(clippy::needless_maybe_sized)] + +extern crate proc_macros; +use proc_macros::external; + +fn directly(t: &T) {} + +trait A: Sized {} +trait B: A {} + +fn depth_1(t: &T) {} +fn depth_2(t: &T) {} + +// We only need to show one +fn multiple_paths(t: &T) {} + +fn in_where(t: &T) +where + T: Sized, +{ +} + +fn mixed_1(t: &T) +{ +} + +fn mixed_2(t: &T) +where + T: Sized, +{ +} + +fn mixed_3(t: &T) +where + T: Sized, +{ +} + +struct Struct(T); + +impl Struct { + fn method(&self) {} +} + +enum Enum { + Variant(&'static T), +} + +union Union<'a, T: Sized> { + a: &'a T, +} + +trait Trait { + fn trait_method() {} + + type GAT; + + type Assoc: Sized + ?Sized; // False negative +} + +trait SecondInTrait: Send + Sized {} +fn second_in_trait() {} + +fn impl_trait(_: &(impl Sized)) {} + +trait GenericTrait: Sized {} +fn in_generic_trait, U>() {} + +mod larger_graph { + // C1 C2 Sized + // \ /\ / + // B1 B2 + // \ / + // A1 + + trait C1 {} + trait C2 {} + trait B1: C1 + C2 {} + trait B2: C2 + Sized {} + trait A1: B1 + B2 {} + + fn larger_graph() {} +} + +// Should not lint + +fn sized() {} +fn maybe_sized() {} + +struct SeparateBounds(T); +impl SeparateBounds {} + +trait P {} +trait Q: P {} + +fn ok_depth_1() {} +fn ok_depth_2() {} + +external! { + fn in_macro(t: &T) {} + + fn with_local_clone(t: &T) {} +} + +#[derive(Clone)] +struct InDerive { + t: T, +} + +struct Refined(T); +impl Refined {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_maybe_sized.rs b/src/tools/clippy/tests/ui/needless_maybe_sized.rs new file mode 100644 index 000000000000..ef66f9a4f2ae --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_maybe_sized.rs @@ -0,0 +1,119 @@ +//@aux-build:proc_macros.rs + +#![allow(unused, clippy::multiple_bound_locations)] +#![warn(clippy::needless_maybe_sized)] + +extern crate proc_macros; +use proc_macros::external; + +fn directly(t: &T) {} + +trait A: Sized {} +trait B: A {} + +fn depth_1(t: &T) {} +fn depth_2(t: &T) {} + +// We only need to show one +fn multiple_paths(t: &T) {} + +fn in_where(t: &T) +where + T: Sized + ?Sized, +{ +} + +fn mixed_1(t: &T) +where + T: ?Sized, +{ +} + +fn mixed_2(t: &T) +where + T: Sized, +{ +} + +fn mixed_3(t: &T) +where + T: Sized, + T: ?Sized, +{ +} + +struct Struct(T); + +impl Struct { + fn method(&self) {} +} + +enum Enum { + Variant(&'static T), +} + +union Union<'a, T: Sized + ?Sized> { + a: &'a T, +} + +trait Trait { + fn trait_method() {} + + type GAT; + + type Assoc: Sized + ?Sized; // False negative +} + +trait SecondInTrait: Send + Sized {} +fn second_in_trait() {} + +fn impl_trait(_: &(impl Sized + ?Sized)) {} + +trait GenericTrait: Sized {} +fn in_generic_trait + ?Sized, U>() {} + +mod larger_graph { + // C1 C2 Sized + // \ /\ / + // B1 B2 + // \ / + // A1 + + trait C1 {} + trait C2 {} + trait B1: C1 + C2 {} + trait B2: C2 + Sized {} + trait A1: B1 + B2 {} + + fn larger_graph() {} +} + +// Should not lint + +fn sized() {} +fn maybe_sized() {} + +struct SeparateBounds(T); +impl SeparateBounds {} + +trait P {} +trait Q: P {} + +fn ok_depth_1() {} +fn ok_depth_2() {} + +external! { + fn in_macro(t: &T) {} + + fn with_local_clone(t: &T) {} +} + +#[derive(Clone)] +struct InDerive { + t: T, +} + +struct Refined(T); +impl Refined {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_maybe_sized.stderr b/src/tools/clippy/tests/ui/needless_maybe_sized.stderr new file mode 100644 index 000000000000..3b1d2b49b062 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_maybe_sized.stderr @@ -0,0 +1,353 @@ +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:9:24 + | +LL | fn directly(t: &T) {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:9:16 + | +LL | fn directly(t: &T) {} + | ^^^^^ + = note: `-D clippy::needless-maybe-sized` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_maybe_sized)]` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn directly(t: &T) {} +LL + fn directly(t: &T) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:14:19 + | +LL | fn depth_1(t: &T) {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:14:15 + | +LL | fn depth_1(t: &T) {} + | ^ + = note: ...because `A` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn depth_1(t: &T) {} +LL + fn depth_1(t: &T) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:15:19 + | +LL | fn depth_2(t: &T) {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:15:15 + | +LL | fn depth_2(t: &T) {} + | ^ + = note: ...because `B` has the bound `A` + = note: ...because `A` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn depth_2(t: &T) {} +LL + fn depth_2(t: &T) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:18:30 + | +LL | fn multiple_paths(t: &T) {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:18:22 + | +LL | fn multiple_paths(t: &T) {} + | ^ + = note: ...because `A` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn multiple_paths(t: &T) {} +LL + fn multiple_paths(t: &T) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:22:16 + | +LL | T: Sized + ?Sized, + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:22:8 + | +LL | T: Sized + ?Sized, + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - T: Sized + ?Sized, +LL + T: Sized, + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:28:8 + | +LL | T: ?Sized, + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:26:15 + | +LL | fn mixed_1(t: &T) + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - where +LL - T: ?Sized, + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:32:15 + | +LL | fn mixed_2(t: &T) + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:34:8 + | +LL | T: Sized, + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn mixed_2(t: &T) +LL + fn mixed_2(t: &T) + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:41:8 + | +LL | T: ?Sized, + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:40:8 + | +LL | T: Sized, + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - T: Sized, +LL - T: ?Sized, +LL + T: Sized, + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:45:26 + | +LL | struct Struct(T); + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:45:18 + | +LL | struct Struct(T); + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - struct Struct(T); +LL + struct Struct(T); + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:47:17 + | +LL | impl Struct { + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:47:9 + | +LL | impl Struct { + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - impl Struct { +LL + impl Struct { + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:48:26 + | +LL | fn method(&self) {} + | ^^^^^^ + | +note: `U` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:48:18 + | +LL | fn method(&self) {} + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn method(&self) {} +LL + fn method(&self) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:51:22 + | +LL | enum Enum { + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:51:14 + | +LL | enum Enum { + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - enum Enum { +LL + enum Enum { + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:55:28 + | +LL | union Union<'a, T: Sized + ?Sized> { + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:55:20 + | +LL | union Union<'a, T: Sized + ?Sized> { + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - union Union<'a, T: Sized + ?Sized> { +LL + union Union<'a, T: Sized> { + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:59:24 + | +LL | trait Trait { + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:59:16 + | +LL | trait Trait { + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - trait Trait { +LL + trait Trait { + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:60:32 + | +LL | fn trait_method() {} + | ^^^^^^ + | +note: `U` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:60:24 + | +LL | fn trait_method() {} + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn trait_method() {} +LL + fn trait_method() {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:62:25 + | +LL | type GAT; + | ^^^^^^ + | +note: `U` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:62:17 + | +LL | type GAT; + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - type GAT; +LL + type GAT; + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:68:23 + | +LL | fn second_in_trait() {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:68:32 + | +LL | fn second_in_trait() {} + | ^^^^^^^^^^^^^ + = note: ...because `SecondInTrait` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn second_in_trait() {} +LL + fn second_in_trait() {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:70:33 + | +LL | fn impl_trait(_: &(impl Sized + ?Sized)) {} + | ^^^^^^ + | +note: `impl Sized + ?Sized` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:70:25 + | +LL | fn impl_trait(_: &(impl Sized + ?Sized)) {} + | ^^^^^ +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn impl_trait(_: &(impl Sized + ?Sized)) {} +LL + fn impl_trait(_: &(impl Sized)) {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:73:42 + | +LL | fn in_generic_trait + ?Sized, U>() {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:73:24 + | +LL | fn in_generic_trait + ?Sized, U>() {} + | ^^^^^^^^^^^^^^^ + = note: ...because `GenericTrait` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn in_generic_trait + ?Sized, U>() {} +LL + fn in_generic_trait, U>() {} + | + +error: `?Sized` bound is ignored because of a `Sized` requirement + --> tests/ui/needless_maybe_sized.rs:88:29 + | +LL | fn larger_graph() {} + | ^^^^^^ + | +note: `T` cannot be unsized because of the bound + --> tests/ui/needless_maybe_sized.rs:88:24 + | +LL | fn larger_graph() {} + | ^^ + = note: ...because `A1` has the bound `B2` + = note: ...because `B2` has the bound `Sized` +help: change the bounds that require `Sized`, or remove the `?Sized` bound + | +LL - fn larger_graph() {} +LL + fn larger_graph() {} + | + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed index 2575f2449e18..a9271cb399d8 100644 --- a/src/tools/clippy/tests/ui/needless_return.fixed +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -323,4 +323,8 @@ fn allow_works() -> i32 { } } +fn conjunctive_blocks() -> String { + ({ "a".to_string() } + "b" + { "c" }) +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs index 04f21834d885..dc888bf667f1 100644 --- a/src/tools/clippy/tests/ui/needless_return.rs +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -333,4 +333,8 @@ fn allow_works() -> i32 { } } +fn conjunctive_blocks() -> String { + return { "a".to_string() } + "b" + { "c" }; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr index 758ff6d985cd..bf5a89d8b75d 100644 --- a/src/tools/clippy/tests/ui/needless_return.stderr +++ b/src/tools/clippy/tests/ui/needless_return.stderr @@ -653,5 +653,17 @@ LL - return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 LL + (if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }) | -error: aborting due to 52 previous errors +error: unneeded `return` statement + --> tests/ui/needless_return.rs:337:5 + | +LL | return { "a".to_string() } + "b" + { "c" }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove `return` and wrap the sequence with parentheses + | +LL - return { "a".to_string() } + "b" + { "c" }; +LL + ({ "a".to_string() } + "b" + { "c" }) + | + +error: aborting due to 53 previous errors diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.rs b/src/tools/clippy/tests/ui/new_ret_no_self.rs index b944f531ef66..175b14d815a2 100644 --- a/src/tools/clippy/tests/ui/new_ret_no_self.rs +++ b/src/tools/clippy/tests/ui/new_ret_no_self.rs @@ -390,9 +390,7 @@ mod issue7344 { impl RetImplTraitSelf2 { // should not trigger lint - fn new(t: T) -> impl Trait2<(), Self> { - unimplemented!() - } + fn new(t: T) -> impl Trait2<(), Self> {} } struct RetImplTraitNoSelf2(T); @@ -401,7 +399,6 @@ mod issue7344 { // should trigger lint fn new(t: T) -> impl Trait2<(), i32> { //~^ ERROR: methods called `new` usually return `Self` - unimplemented!() } } diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.stderr b/src/tools/clippy/tests/ui/new_ret_no_self.stderr index d440a9f45fcd..3597ad65838f 100644 --- a/src/tools/clippy/tests/ui/new_ret_no_self.stderr +++ b/src/tools/clippy/tests/ui/new_ret_no_self.stderr @@ -96,11 +96,10 @@ LL | | } | |_________^ error: methods called `new` usually return `Self` - --> tests/ui/new_ret_no_self.rs:402:9 + --> tests/ui/new_ret_no_self.rs:400:9 | LL | / fn new(t: T) -> impl Trait2<(), i32> { LL | | -LL | | unimplemented!() LL | | } | |_________^ diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs index dabeda72f0c8..0ea911c34348 100644 --- a/src/tools/clippy/tests/ui/no_effect.rs +++ b/src/tools/clippy/tests/ui/no_effect.rs @@ -1,6 +1,5 @@ #![feature(fn_traits, unboxed_closures)] #![warn(clippy::no_effect_underscore_binding)] -#![allow(dead_code, path_statements)] #![allow( clippy::deref_addrof, clippy::redundant_field_names, @@ -33,7 +32,6 @@ impl Neg for Cout { } } -struct Unit; struct Tuple(i32); struct Struct { field: i32, @@ -42,10 +40,6 @@ enum Enum { Tuple(i32), Struct { field: i32 }, } -struct DropUnit; -impl Drop for DropUnit { - fn drop(&mut self) {} -} struct DropStruct { field: i32, } @@ -117,15 +111,9 @@ impl FnOnce<(&str,)> for GreetStruct3 { fn main() { let s = get_struct(); - let s2 = get_struct(); 0; //~^ ERROR: statement with no effect - //~| NOTE: `-D clippy::no-effect` implied by `-D warnings` - s2; - //~^ ERROR: statement with no effect - Unit; - //~^ ERROR: statement with no effect Tuple(0); //~^ ERROR: statement with no effect Struct { field: 0 }; @@ -192,7 +180,6 @@ fn main() { unsafe { unsafe_fn() }; let _used = get_struct(); let _x = vec![1]; - DropUnit; DropStruct { field: 0 }; DropTuple(0); DropEnum::Tuple(0); diff --git a/src/tools/clippy/tests/ui/no_effect.stderr b/src/tools/clippy/tests/ui/no_effect.stderr index c7c8eecd054b..48ec997d938c 100644 --- a/src/tools/clippy/tests/ui/no_effect.stderr +++ b/src/tools/clippy/tests/ui/no_effect.stderr @@ -1,5 +1,5 @@ error: statement with no effect - --> tests/ui/no_effect.rs:122:5 + --> tests/ui/no_effect.rs:115:5 | LL | 0; | ^^ @@ -8,151 +8,139 @@ LL | 0; = help: to override `-D warnings` add `#[allow(clippy::no_effect)]` error: statement with no effect - --> tests/ui/no_effect.rs:125:5 - | -LL | s2; - | ^^^ - -error: statement with no effect - --> tests/ui/no_effect.rs:127:5 - | -LL | Unit; - | ^^^^^ - -error: statement with no effect - --> tests/ui/no_effect.rs:129:5 + --> tests/ui/no_effect.rs:117:5 | LL | Tuple(0); | ^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:131:5 + --> tests/ui/no_effect.rs:119:5 | LL | Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:133:5 + --> tests/ui/no_effect.rs:121:5 | LL | Struct { ..s }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:135:5 + --> tests/ui/no_effect.rs:123:5 | LL | Union { a: 0 }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:137:5 + --> tests/ui/no_effect.rs:125:5 | LL | Enum::Tuple(0); | ^^^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:139:5 + --> tests/ui/no_effect.rs:127:5 | LL | Enum::Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:141:5 + --> tests/ui/no_effect.rs:129:5 | LL | 5 + 6; | ^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:143:5 + --> tests/ui/no_effect.rs:131:5 | LL | *&42; | ^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:145:5 + --> tests/ui/no_effect.rs:133:5 | LL | &6; | ^^^ error: statement with no effect - --> tests/ui/no_effect.rs:147:5 + --> tests/ui/no_effect.rs:135:5 | LL | (5, 6, 7); | ^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:149:5 + --> tests/ui/no_effect.rs:137:5 | LL | ..; | ^^^ error: statement with no effect - --> tests/ui/no_effect.rs:151:5 + --> tests/ui/no_effect.rs:139:5 | LL | 5..; | ^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:153:5 + --> tests/ui/no_effect.rs:141:5 | LL | ..5; | ^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:155:5 + --> tests/ui/no_effect.rs:143:5 | LL | 5..6; | ^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:157:5 + --> tests/ui/no_effect.rs:145:5 | LL | 5..=6; | ^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:159:5 + --> tests/ui/no_effect.rs:147:5 | LL | [42, 55]; | ^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:161:5 + --> tests/ui/no_effect.rs:149:5 | LL | [42, 55][1]; | ^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:163:5 + --> tests/ui/no_effect.rs:151:5 | LL | (42, 55).1; | ^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:165:5 + --> tests/ui/no_effect.rs:153:5 | LL | [42; 55]; | ^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:167:5 + --> tests/ui/no_effect.rs:155:5 | LL | [42; 55][13]; | ^^^^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:170:5 + --> tests/ui/no_effect.rs:158:5 | LL | || x += 5; | ^^^^^^^^^^ error: statement with no effect - --> tests/ui/no_effect.rs:173:5 + --> tests/ui/no_effect.rs:161:5 | LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> tests/ui/no_effect.rs:175:9 + --> tests/ui/no_effect.rs:163:9 | LL | let _unused = 1; | ^^^^^^^ @@ -161,22 +149,22 @@ LL | let _unused = 1; = help: to override `-D warnings` add `#[allow(clippy::no_effect_underscore_binding)]` error: binding to `_` prefixed variable with no side-effect - --> tests/ui/no_effect.rs:178:9 + --> tests/ui/no_effect.rs:166:9 | LL | let _penguin = || println!("Some helpful closure"); | ^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> tests/ui/no_effect.rs:180:9 + --> tests/ui/no_effect.rs:168:9 | LL | let _duck = Struct { field: 0 }; | ^^^^^ error: binding to `_` prefixed variable with no side-effect - --> tests/ui/no_effect.rs:182:9 + --> tests/ui/no_effect.rs:170:9 | LL | let _cat = [2, 4, 6, 8][2]; | ^^^^ -error: aborting due to 29 previous errors +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed b/src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed index 7d1be412e54f..11616f28825b 100644 --- a/src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed +++ b/src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed @@ -1,7 +1,11 @@ +//@aux-build:proc_macro_derive.rs #![allow(clippy::clone_on_copy, unused)] #![allow(clippy::assigning_clones)] #![no_main] +extern crate proc_macros; +use proc_macros::with_span; + // lint struct A(u32); @@ -95,3 +99,19 @@ impl Clone for Uwu { } impl Copy for Uwu {} + +// should skip proc macros, see https://github.com/rust-lang/rust-clippy/issues/12788 +#[derive(proc_macro_derive::NonCanonicalClone)] +pub struct G; + +with_span!( + span + + #[derive(Copy)] + struct H; + impl Clone for H { + fn clone(&self) -> Self { + todo!() + } + } +); diff --git a/src/tools/clippy/tests/ui/non_canonical_clone_impl.rs b/src/tools/clippy/tests/ui/non_canonical_clone_impl.rs index beae05efb2fb..a36c7ed44c24 100644 --- a/src/tools/clippy/tests/ui/non_canonical_clone_impl.rs +++ b/src/tools/clippy/tests/ui/non_canonical_clone_impl.rs @@ -1,7 +1,11 @@ +//@aux-build:proc_macro_derive.rs #![allow(clippy::clone_on_copy, unused)] #![allow(clippy::assigning_clones)] #![no_main] +extern crate proc_macros; +use proc_macros::with_span; + // lint struct A(u32); @@ -105,3 +109,19 @@ impl Clone for Uwu { } impl Copy for Uwu {} + +// should skip proc macros, see https://github.com/rust-lang/rust-clippy/issues/12788 +#[derive(proc_macro_derive::NonCanonicalClone)] +pub struct G; + +with_span!( + span + + #[derive(Copy)] + struct H; + impl Clone for H { + fn clone(&self) -> Self { + todo!() + } + } +); diff --git a/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr b/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr index 6bfc99d988bb..f7cad58150f7 100644 --- a/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr +++ b/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr @@ -1,5 +1,5 @@ error: non-canonical implementation of `clone` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:10:29 + --> tests/ui/non_canonical_clone_impl.rs:14:29 | LL | fn clone(&self) -> Self { | _____________________________^ @@ -11,7 +11,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::non_canonical_clone_impl)]` error: unnecessary implementation of `clone_from` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:14:5 + --> tests/ui/non_canonical_clone_impl.rs:18:5 | LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone(); @@ -20,7 +20,7 @@ LL | | } | |_____^ help: remove it error: non-canonical implementation of `clone` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:81:29 + --> tests/ui/non_canonical_clone_impl.rs:85:29 | LL | fn clone(&self) -> Self { | _____________________________^ @@ -29,7 +29,7 @@ LL | | } | |_____^ help: change this to: `{ *self }` error: unnecessary implementation of `clone_from` on a `Copy` type - --> tests/ui/non_canonical_clone_impl.rs:85:5 + --> tests/ui/non_canonical_clone_impl.rs:89:5 | LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone(); diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed index aba599678e39..cc91ba6ec66e 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![allow(unused, clippy::diverging_sub_expression, clippy::needless_if)] #![warn(clippy::nonminimal_bool)] diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs index 35f22db1d36a..c812f6f0ca4f 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![allow(unused, clippy::diverging_sub_expression, clippy::needless_if)] #![warn(clippy::nonminimal_bool)] diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr index 18da4e0d380b..d7adc0638b37 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr @@ -1,5 +1,5 @@ error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:10:13 + --> tests/ui/nonminimal_bool_methods.rs:8:13 | LL | let _ = !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` @@ -8,91 +8,91 @@ LL | let _ = !a.is_some(); = help: to override `-D warnings` add `#[allow(clippy::nonminimal_bool)]` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:12:13 + --> tests/ui/nonminimal_bool_methods.rs:10:13 | LL | let _ = !a.is_none(); | ^^^^^^^^^^^^ help: try: `a.is_some()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:14:13 + --> tests/ui/nonminimal_bool_methods.rs:12:13 | LL | let _ = !b.is_err(); | ^^^^^^^^^^^ help: try: `b.is_ok()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:16:13 + --> tests/ui/nonminimal_bool_methods.rs:14:13 | LL | let _ = !b.is_ok(); | ^^^^^^^^^^ help: try: `b.is_err()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:18:13 + --> tests/ui/nonminimal_bool_methods.rs:16:13 | LL | let _ = !(a.is_some() && !c); | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() || c` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:19:13 + --> tests/ui/nonminimal_bool_methods.rs:17:13 | LL | let _ = !(a.is_some() || !c); | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() && c` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:20:26 + --> tests/ui/nonminimal_bool_methods.rs:18:26 | LL | let _ = !(!c ^ c) || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:21:25 + --> tests/ui/nonminimal_bool_methods.rs:19:25 | LL | let _ = (!c ^ c) || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:22:23 + --> tests/ui/nonminimal_bool_methods.rs:20:23 | LL | let _ = !c ^ c || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:94:8 + --> tests/ui/nonminimal_bool_methods.rs:92:8 | LL | if !res.is_ok() {} | ^^^^^^^^^^^^ help: try: `res.is_err()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:95:8 + --> tests/ui/nonminimal_bool_methods.rs:93:8 | LL | if !res.is_err() {} | ^^^^^^^^^^^^^ help: try: `res.is_ok()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:98:8 + --> tests/ui/nonminimal_bool_methods.rs:96:8 | LL | if !res.is_some() {} | ^^^^^^^^^^^^^^ help: try: `res.is_none()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:99:8 + --> tests/ui/nonminimal_bool_methods.rs:97:8 | LL | if !res.is_none() {} | ^^^^^^^^^^^^^^ help: try: `res.is_some()` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:115:8 + --> tests/ui/nonminimal_bool_methods.rs:113:8 | LL | if !(a as u64 >= b) {} | ^^^^^^^^^^^^^^^^ help: try: `(a as u64) < b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:116:8 + --> tests/ui/nonminimal_bool_methods.rs:114:8 | LL | if !((a as u64) >= b) {} | ^^^^^^^^^^^^^^^^^^ help: try: `(a as u64) < b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool_methods.rs:117:8 + --> tests/ui/nonminimal_bool_methods.rs:115:8 | LL | if !(a as u64 <= b) {} | ^^^^^^^^^^^^^^^^ help: try: `a as u64 > b` diff --git a/src/tools/clippy/tests/ui/numbered_fields.fixed b/src/tools/clippy/tests/ui/numbered_fields.fixed index dc88081ba0a7..108520eed38d 100644 --- a/src/tools/clippy/tests/ui/numbered_fields.fixed +++ b/src/tools/clippy/tests/ui/numbered_fields.fixed @@ -34,4 +34,9 @@ fn main() { // Aliases can't be tuple constructed #8638 let _ = Alias { 0: 0, 1: 1, 2: 2 }; + + // Issue #12367 + struct TupleStructVec(Vec); + + let _ = TupleStructVec(vec![0, 1, 2, 3]); } diff --git a/src/tools/clippy/tests/ui/numbered_fields.rs b/src/tools/clippy/tests/ui/numbered_fields.rs index e8fa652e3c1d..c718661a6826 100644 --- a/src/tools/clippy/tests/ui/numbered_fields.rs +++ b/src/tools/clippy/tests/ui/numbered_fields.rs @@ -42,4 +42,9 @@ fn main() { // Aliases can't be tuple constructed #8638 let _ = Alias { 0: 0, 1: 1, 2: 2 }; + + // Issue #12367 + struct TupleStructVec(Vec); + + let _ = TupleStructVec { 0: vec![0, 1, 2, 3] }; } diff --git a/src/tools/clippy/tests/ui/numbered_fields.stderr b/src/tools/clippy/tests/ui/numbered_fields.stderr index 96426cab1e62..9d3f59cd3769 100644 --- a/src/tools/clippy/tests/ui/numbered_fields.stderr +++ b/src/tools/clippy/tests/ui/numbered_fields.stderr @@ -23,5 +23,11 @@ LL | | 1: 3u32, LL | | }; | |_____^ help: try: `TupleStruct(1u32, 3u32, 2u8)` -error: aborting due to 2 previous errors +error: used a field initializer for a tuple struct + --> tests/ui/numbered_fields.rs:49:13 + | +LL | let _ = TupleStructVec { 0: vec![0, 1, 2, 3] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `TupleStructVec(vec![0, 1, 2, 3])` + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed index e7ba54864ab0..7657ef470c58 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -311,4 +311,11 @@ mod lazy { } } +fn host_effect() { + // #12877 - make sure we don't ICE in type_certainty + use std::ops::Add; + + Add::::add(1, 1).add(i32::MIN); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index 196632133d52..97cf496d3ac7 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -311,4 +311,11 @@ mod lazy { } } +fn host_effect() { + // #12877 - make sure we don't ICE in type_certainty + use std::ops::Add; + + Add::::add(1, 1).add(i32::MIN); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/overly_complex_bool_expr.fixed b/src/tools/clippy/tests/ui/overly_complex_bool_expr.fixed index e44f6063156a..439b1145431c 100644 --- a/src/tools/clippy/tests/ui/overly_complex_bool_expr.fixed +++ b/src/tools/clippy/tests/ui/overly_complex_bool_expr.fixed @@ -37,3 +37,13 @@ fn check_expect() { #[expect(clippy::overly_complex_bool_expr)] let _ = a < b && a >= b; } + +#[allow(clippy::never_loop)] +fn check_never_type() { + loop { + _ = (break) || true; + } + loop { + _ = (return) || true; + } +} diff --git a/src/tools/clippy/tests/ui/overly_complex_bool_expr.rs b/src/tools/clippy/tests/ui/overly_complex_bool_expr.rs index f010a8537e7f..b96fd1adf118 100644 --- a/src/tools/clippy/tests/ui/overly_complex_bool_expr.rs +++ b/src/tools/clippy/tests/ui/overly_complex_bool_expr.rs @@ -37,3 +37,13 @@ fn check_expect() { #[expect(clippy::overly_complex_bool_expr)] let _ = a < b && a >= b; } + +#[allow(clippy::never_loop)] +fn check_never_type() { + loop { + _ = (break) || true; + } + loop { + _ = (return) || true; + } +} diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn.rs b/src/tools/clippy/tests/ui/panic_in_result_fn.rs index 41e2f5226899..aaaf9a109acb 100644 --- a/src/tools/clippy/tests/ui/panic_in_result_fn.rs +++ b/src/tools/clippy/tests/ui/panic_in_result_fn.rs @@ -56,6 +56,11 @@ fn function_result_with_panic() -> Result // should emit lint panic!("error"); } +fn in_closure() -> Result { + let c = || panic!(); + c() +} + fn todo() { println!("something"); } diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn.stderr index cd2234bdfb13..2d49b5ab1b8f 100644 --- a/src/tools/clippy/tests/ui/panic_in_result_fn.stderr +++ b/src/tools/clippy/tests/ui/panic_in_result_fn.stderr @@ -34,5 +34,21 @@ note: return Err() instead of panicking LL | panic!("error"); | ^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: used `panic!()` or assertion in a function that returns `Result` + --> tests/ui/panic_in_result_fn.rs:59:1 + | +LL | / fn in_closure() -> Result { +LL | | let c = || panic!(); +LL | | c() +LL | | } + | |_^ + | + = help: `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> tests/ui/panic_in_result_fn.rs:60:16 + | +LL | let c = || panic!(); + | ^^^^^^^^ + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs index fcd716f41444..5d6e488972cd 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.rs +++ b/src/tools/clippy/tests/ui/ptr_arg.rs @@ -282,7 +282,7 @@ mod issue_9218 { todo!() } - // These two's return types don't use use 'a so it's not okay + // These two's return types don't use 'a so it's not okay fn cow_bad_ret_ty_1<'a>(input: &'a Cow<'a, str>) -> &'static str { //~^ ERROR: using a reference to `Cow` is not recommended todo!() diff --git a/src/tools/clippy/tests/ui/search_is_some.rs b/src/tools/clippy/tests/ui/search_is_some.rs index e8a0920b645d..9a9aaba56adc 100644 --- a/src/tools/clippy/tests/ui/search_is_some.rs +++ b/src/tools/clippy/tests/ui/search_is_some.rs @@ -1,5 +1,6 @@ //@aux-build:option_helpers.rs #![warn(clippy::search_is_some)] +#![allow(clippy::manual_pattern_char_comparison)] #![allow(clippy::useless_vec)] #![allow(dead_code)] extern crate option_helpers; diff --git a/src/tools/clippy/tests/ui/search_is_some.stderr b/src/tools/clippy/tests/ui/search_is_some.stderr index b5f84d23284a..b5ef55341770 100644 --- a/src/tools/clippy/tests/ui/search_is_some.stderr +++ b/src/tools/clippy/tests/ui/search_is_some.stderr @@ -1,5 +1,5 @@ error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:15:13 + --> tests/ui/search_is_some.rs:16:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -13,7 +13,7 @@ LL | | ).is_some(); = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` error: called `is_some()` after searching an `Iterator` with `position` - --> tests/ui/search_is_some.rs:21:13 + --> tests/ui/search_is_some.rs:22:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -25,7 +25,7 @@ LL | | ).is_some(); = help: this is more succinctly expressed by calling `any()` error: called `is_some()` after searching an `Iterator` with `rposition` - --> tests/ui/search_is_some.rs:27:13 + --> tests/ui/search_is_some.rs:28:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -37,13 +37,13 @@ LL | | ).is_some(); = help: this is more succinctly expressed by calling `any()` error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:42:20 + --> tests/ui/search_is_some.rs:43:20 | LL | let _ = (0..1).find(some_closure).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(some_closure)` error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:52:13 + --> tests/ui/search_is_some.rs:53:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -55,7 +55,7 @@ LL | | ).is_none(); = help: this is more succinctly expressed by calling `any()` with negation error: called `is_none()` after searching an `Iterator` with `position` - --> tests/ui/search_is_some.rs:58:13 + --> tests/ui/search_is_some.rs:59:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -67,7 +67,7 @@ LL | | ).is_none(); = help: this is more succinctly expressed by calling `any()` with negation error: called `is_none()` after searching an `Iterator` with `rposition` - --> tests/ui/search_is_some.rs:64:13 + --> tests/ui/search_is_some.rs:65:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -79,7 +79,7 @@ LL | | ).is_none(); = help: this is more succinctly expressed by calling `any()` with negation error: called `is_none()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some.rs:79:13 + --> tests/ui/search_is_some.rs:80:13 | LL | let _ = (0..1).find(some_closure).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(0..1).any(some_closure)` diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs index 0305d895fc58..8ee15440ccf0 100644 --- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs @@ -274,11 +274,20 @@ fn should_trigger_lint_for_tuple_in_scrutinee() { }, (_, _, _) => {}, }; + } +} +// Should not trigger lint since `String::as_str` returns a reference (i.e., `&str`) +// to the locked data (i.e., the `String`) and it is not surprising that matching such +// a reference needs to keep the data locked until the end of the match block. +fn should_not_trigger_lint_for_string_as_str() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + + { + let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() }); let mutex3 = Mutex::new(StateWithField { s: "three".to_owned() }); + match mutex3.lock().unwrap().s.as_str() { - //~^ ERROR: temporary with significant `Drop` in `match` scrutinee will live until - //~| NOTE: this might lead to deadlocks or other unexpected behavior "three" => { println!("started"); mutex1.lock().unwrap().s.len(); @@ -289,8 +298,6 @@ fn should_trigger_lint_for_tuple_in_scrutinee() { }; match (true, mutex3.lock().unwrap().s.as_str()) { - //~^ ERROR: temporary with significant `Drop` in `match` scrutinee will live until - //~| NOTE: this might lead to deadlocks or other unexpected behavior (_, "three") => { println!("started"); mutex1.lock().unwrap().s.len(); @@ -514,16 +521,15 @@ impl StateStringWithBoxedMutexGuard { } } -fn should_trigger_lint_for_boxed_mutex_guard_holding_string() { +fn should_not_trigger_lint_for_string_ref() { let s = StateStringWithBoxedMutexGuard::new(); let matcher = String::from("A String"); - // Should trigger lint because a temporary Box holding a type with a significant drop in a match - // scrutinee may have a potentially surprising lifetime. + // Should not trigger lint because the second `deref` returns a string reference (`&String`). + // So it is not surprising that matching the reference implies that the lock needs to be held + // until the end of the block. match s.lock().deref().deref() { - //~^ ERROR: temporary with significant `Drop` in `match` scrutinee will live until the - //~| NOTE: this might lead to deadlocks or other unexpected behavior matcher => println!("Value is {}", s.lock().deref()), _ => println!("Value was not a match"), }; @@ -639,13 +645,12 @@ fn should_trigger_lint_for_non_ref_move_and_clone_suggestion() { }; } -fn should_trigger_lint_for_read_write_lock_for_loop() { - // For-in loops desugar to match expressions and are prone to the type of deadlock this lint is - // designed to look for. +fn should_not_trigger_lint_for_read_write_lock_for_loop() { let rwlock = RwLock::>::new(vec!["1".to_string()]); + // Should not trigger lint. Since we're iterating over the data, it's obvious that the lock + // has to be held until the iteration finishes. + // https://github.com/rust-lang/rust-clippy/issues/8987 for s in rwlock.read().unwrap().iter() { - //~^ ERROR: temporary with significant `Drop` in `for` loop condition will live until - //~| NOTE: this might lead to deadlocks or other unexpected behavior println!("{}", s); } } @@ -675,4 +680,125 @@ fn should_not_trigger_on_significant_iterator_drop() { } } +// https://github.com/rust-lang/rust-clippy/issues/9072 +fn should_not_trigger_lint_if_place_expr_has_significant_drop() { + let x = Mutex::new(vec![1, 2, 3]); + let x_guard = x.lock().unwrap(); + + match x_guard[0] { + 1 => println!("1!"), + x => println!("{x}"), + } + + match x_guard.len() { + 1 => println!("1!"), + x => println!("{x}"), + } +} + +struct Guard<'a, T>(MutexGuard<'a, T>); + +struct Ref<'a, T>(&'a T); + +impl<'a, T> Guard<'a, T> { + fn guard(&self) -> &MutexGuard { + &self.0 + } + + fn guard_ref(&self) -> Ref> { + Ref(&self.0) + } + + fn take(self) -> Box> { + Box::new(self.0) + } +} + +fn should_not_trigger_for_significant_drop_ref() { + let mutex = Mutex::new(vec![1, 2]); + let guard = Guard(mutex.lock().unwrap()); + + match guard.guard().len() { + 0 => println!("empty"), + _ => println!("not empty"), + } + + match guard.guard_ref().0.len() { + 0 => println!("empty"), + _ => println!("not empty"), + } + + match guard.take().len() { + //~^ ERROR: temporary with significant `Drop` in `match` scrutinee will live until the + //~| NOTE: this might lead to deadlocks or other unexpected behavior + 0 => println!("empty"), + _ => println!("not empty"), + }; +} + +struct Foo<'a>(&'a Vec); + +impl<'a> Foo<'a> { + fn copy_old_lifetime(&self) -> &'a Vec { + self.0 + } + + fn reborrow_new_lifetime(&self) -> &Vec { + self.0 + } +} + +fn should_trigger_lint_if_and_only_if_lifetime_is_irrelevant() { + let vec = Vec::new(); + let mutex = Mutex::new(Foo(&vec)); + + // Should trigger lint even if `copy_old_lifetime()` has a lifetime, as the lifetime of + // `&vec` is unrelated to the temporary with significant drop (i.e., the `MutexGuard`). + for val in mutex.lock().unwrap().copy_old_lifetime() { + //~^ ERROR: temporary with significant `Drop` in `for` loop condition will live until the + //~| NOTE: this might lead to deadlocks or other unexpected behavior + println!("{}", val); + } + + // Should not trigger lint because `reborrow_new_lifetime()` has a lifetime and the + // lifetime is related to the temporary with significant drop (i.e., the `MutexGuard`). + for val in mutex.lock().unwrap().reborrow_new_lifetime() { + println!("{}", val); + } +} + +fn should_not_trigger_lint_for_complex_lifetime() { + let mutex = Mutex::new(vec!["hello".to_owned()]); + let string = "world".to_owned(); + + // Should not trigger lint due to the relevant lifetime. + for c in mutex.lock().unwrap().first().unwrap().chars() { + println!("{}", c); + } + + // Should trigger lint due to the irrelevant lifetime. + // + // FIXME: The lifetime is too complex to analyze. In order to avoid false positives, we do not + // trigger lint. + for c in mutex.lock().unwrap().first().map(|_| &string).unwrap().chars() { + println!("{}", c); + } +} + +fn should_not_trigger_lint_with_explicit_drop() { + let mutex = Mutex::new(vec![1]); + + // Should not trigger lint since the drop explicitly happens. + for val in [drop(mutex.lock().unwrap()), ()] { + println!("{:?}", val); + } + + // Should trigger lint if there is no explicit drop. + for val in [mutex.lock().unwrap()[0], 2] { + //~^ ERROR: temporary with significant `Drop` in `for` loop condition will live until the + //~| NOTE: this might lead to deadlocks or other unexpected behavior + println!("{:?}", val); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr index 7d5b1acc7f00..4a483e79d8ad 100644 --- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr +++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr @@ -28,6 +28,9 @@ LL | match s.lock_m().get_the_value() { LL | println!("{}", s.lock_m().get_the_value()); | ---------- another value with significant `Drop` created here ... +LL | println!("{}", s.lock_m().get_the_value()); + | ---------- another value with significant `Drop` created here +... LL | } | - temporary lives until here | @@ -47,6 +50,9 @@ LL | match s.lock_m_m().get_the_value() { LL | println!("{}", s.lock_m().get_the_value()); | ---------- another value with significant `Drop` created here ... +LL | println!("{}", s.lock_m().get_the_value()); + | ---------- another value with significant `Drop` created here +... LL | } | - temporary lives until here | @@ -154,42 +160,10 @@ LL ~ match (mutex1.lock().unwrap().s.len(), true, value) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:279:15 - | -LL | match mutex3.lock().unwrap().s.as_str() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | mutex1.lock().unwrap().s.len(); - | ---------------------- another value with significant `Drop` created here -LL | mutex2.lock().unwrap().s.len(); - | ---------------------- another value with significant `Drop` created here -... -LL | }; - | - temporary lives until here - | - = note: this might lead to deadlocks or other unexpected behavior - -error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:291:22 - | -LL | match (true, mutex3.lock().unwrap().s.as_str()) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | mutex1.lock().unwrap().s.len(); - | ---------------------- another value with significant `Drop` created here -LL | mutex2.lock().unwrap().s.len(); - | ---------------------- another value with significant `Drop` created here -... -LL | }; - | - temporary lives until here - | - = note: this might lead to deadlocks or other unexpected behavior - -error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:312:11 + --> tests/ui/significant_drop_in_scrutinee.rs:319:11 | LL | match mutex.lock().unwrap().s.len() > 1 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | mutex.lock().unwrap().s.len(); | --------------------- another value with significant `Drop` created here @@ -200,15 +174,15 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior help: try moving the temporary above the match | -LL ~ let value = mutex.lock().unwrap().s.len() > 1; -LL ~ match value { +LL ~ let value = mutex.lock().unwrap().s.len(); +LL ~ match value > 1 { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:321:11 + --> tests/ui/significant_drop_in_scrutinee.rs:328:15 | LL | match 1 < mutex.lock().unwrap().s.len() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | mutex.lock().unwrap().s.len(); | --------------------- another value with significant `Drop` created here @@ -219,15 +193,15 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior help: try moving the temporary above the match | -LL ~ let value = 1 < mutex.lock().unwrap().s.len(); -LL ~ match value { +LL ~ let value = mutex.lock().unwrap().s.len(); +LL ~ match 1 < value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:341:11 + --> tests/ui/significant_drop_in_scrutinee.rs:348:11 | LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | mutex1.lock().unwrap().s.len(), | ---------------------- another value with significant `Drop` created here @@ -240,15 +214,36 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior help: try moving the temporary above the match | -LL ~ let value = mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len(); -LL ~ match value { +LL ~ let value = mutex1.lock().unwrap().s.len(); +LL ~ match value < mutex2.lock().unwrap().s.len() { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:354:11 + --> tests/ui/significant_drop_in_scrutinee.rs:348:44 + | +LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | mutex1.lock().unwrap().s.len(), + | ---------------------- another value with significant `Drop` created here +LL | mutex2.lock().unwrap().s.len() + | ---------------------- another value with significant `Drop` created here +... +LL | }; + | - temporary lives until here + | + = note: this might lead to deadlocks or other unexpected behavior +help: try moving the temporary above the match + | +LL ~ let value = mutex2.lock().unwrap().s.len(); +LL ~ match mutex1.lock().unwrap().s.len() < value { + | + +error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression + --> tests/ui/significant_drop_in_scrutinee.rs:361:11 | LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | mutex1.lock().unwrap().s.len(), | ---------------------- another value with significant `Drop` created here @@ -261,15 +256,36 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior help: try moving the temporary above the match | -LL ~ let value = mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len(); -LL ~ match value { +LL ~ let value = mutex1.lock().unwrap().s.len(); +LL ~ match value >= mutex2.lock().unwrap().s.len() { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:391:11 + --> tests/ui/significant_drop_in_scrutinee.rs:361:45 + | +LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | mutex1.lock().unwrap().s.len(), + | ---------------------- another value with significant `Drop` created here +LL | mutex2.lock().unwrap().s.len() + | ---------------------- another value with significant `Drop` created here +... +LL | }; + | - temporary lives until here + | + = note: this might lead to deadlocks or other unexpected behavior +help: try moving the temporary above the match + | +LL ~ let value = mutex2.lock().unwrap().s.len(); +LL ~ match mutex1.lock().unwrap().s.len() >= value { + | + +error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression + --> tests/ui/significant_drop_in_scrutinee.rs:398:11 | LL | match get_mutex_guard().s.len() > 1 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | mutex1.lock().unwrap().s.len(); | ---------------------- another value with significant `Drop` created here @@ -280,12 +296,12 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior help: try moving the temporary above the match | -LL ~ let value = get_mutex_guard().s.len() > 1; -LL ~ match value { +LL ~ let value = get_mutex_guard().s.len(); +LL ~ match value > 1 { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:410:11 + --> tests/ui/significant_drop_in_scrutinee.rs:417:11 | LL | match match i { | ___________^ @@ -293,9 +309,9 @@ LL | | LL | | LL | | 100 => mutex1.lock().unwrap(), ... | +LL | | .s LL | | .len() -LL | | > 1 - | |___________^ + | |__________^ ... LL | mutex1.lock().unwrap().s.len(); | ---------------------- another value with significant `Drop` created here @@ -313,13 +329,12 @@ LL + 100 => mutex1.lock().unwrap(), LL + _ => mutex2.lock().unwrap(), LL + } LL + .s -LL + .len() -LL + > 1; +LL + .len(); LL ~ match value | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:438:11 + --> tests/ui/significant_drop_in_scrutinee.rs:445:11 | LL | match if i > 1 { | ___________^ @@ -327,9 +342,9 @@ LL | | LL | | LL | | mutex1.lock().unwrap() ... | +LL | | .s LL | | .len() -LL | | > 1 - | |___________^ + | |__________^ ... LL | mutex1.lock().unwrap().s.len(); | ---------------------- another value with significant `Drop` created here @@ -348,19 +363,18 @@ LL + } else { LL + mutex2.lock().unwrap() LL + } LL + .s -LL + .len() -LL + > 1; +LL + .len(); LL ~ match value | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:494:11 + --> tests/ui/significant_drop_in_scrutinee.rs:501:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | _ => println!("Value is {}", s.lock().deref()), - | ---------------- another value with significant `Drop` created here + | -------- another value with significant `Drop` created here LL | }; | - temporary lives until here | @@ -368,25 +382,11 @@ LL | }; help: try moving the temporary above the match and create a copy | LL ~ let value = *s.lock().deref().deref(); -LL ~ match value { +LL ~ match (&value) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:524:11 - | -LL | match s.lock().deref().deref() { - | ^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | matcher => println!("Value is {}", s.lock().deref()), - | ---------------- another value with significant `Drop` created here -LL | _ => println!("Value was not a match"), -LL | }; - | - temporary lives until here - | - = note: this might lead to deadlocks or other unexpected behavior - -error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:545:11 + --> tests/ui/significant_drop_in_scrutinee.rs:551:11 | LL | match mutex.lock().unwrap().i = i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -405,10 +405,10 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:553:11 + --> tests/ui/significant_drop_in_scrutinee.rs:559:15 | LL | match i = mutex.lock().unwrap().i { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ ... LL | println!("{}", mutex.lock().unwrap().i); | --------------------- another value with significant `Drop` created here @@ -419,12 +419,12 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior help: try moving the temporary above the match | -LL ~ i = mutex.lock().unwrap().i; -LL ~ match () { +LL ~ let value = mutex.lock().unwrap().i; +LL ~ match i = value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:561:11 + --> tests/ui/significant_drop_in_scrutinee.rs:567:11 | LL | match mutex.lock().unwrap().i += 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -443,10 +443,10 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:569:11 + --> tests/ui/significant_drop_in_scrutinee.rs:575:16 | LL | match i += mutex.lock().unwrap().i { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ ... LL | println!("{}", mutex.lock().unwrap().i); | --------------------- another value with significant `Drop` created here @@ -457,12 +457,12 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior help: try moving the temporary above the match | -LL ~ i += mutex.lock().unwrap().i; -LL ~ match () { +LL ~ let value = mutex.lock().unwrap().i; +LL ~ match i += value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:634:11 + --> tests/ui/significant_drop_in_scrutinee.rs:640:11 | LL | match rwlock.read().unwrap().to_number() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -471,20 +471,14 @@ LL | }; | - temporary lives until here | = note: this might lead to deadlocks or other unexpected behavior - -error: temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression - --> tests/ui/significant_drop_in_scrutinee.rs:646:14 +help: try moving the temporary above the match | -LL | for s in rwlock.read().unwrap().iter() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | } - | - temporary lives until here +LL ~ let value = rwlock.read().unwrap().to_number(); +LL ~ match value { | - = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> tests/ui/significant_drop_in_scrutinee.rs:663:11 + --> tests/ui/significant_drop_in_scrutinee.rs:668:11 | LL | match mutex.lock().unwrap().foo() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -499,5 +493,53 @@ LL ~ let value = mutex.lock().unwrap().foo(); LL ~ match value { | -error: aborting due to 26 previous errors +error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression + --> tests/ui/significant_drop_in_scrutinee.rs:731:11 + | +LL | match guard.take().len() { + | ^^^^^^^^^^^^^^^^^^ +... +LL | }; + | - temporary lives until here + | + = note: this might lead to deadlocks or other unexpected behavior +help: try moving the temporary above the match + | +LL ~ let value = guard.take().len(); +LL ~ match value { + | + +error: temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression + --> tests/ui/significant_drop_in_scrutinee.rs:757:16 + | +LL | for val in mutex.lock().unwrap().copy_old_lifetime() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | } + | - temporary lives until here + | + = note: this might lead to deadlocks or other unexpected behavior +help: try moving the temporary above the match + | +LL ~ let value = mutex.lock().unwrap().copy_old_lifetime(); +LL ~ for val in value { + | + +error: temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression + --> tests/ui/significant_drop_in_scrutinee.rs:797:17 + | +LL | for val in [mutex.lock().unwrap()[0], 2] { + | ^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | } + | - temporary lives until here + | + = note: this might lead to deadlocks or other unexpected behavior +help: try moving the temporary above the match + | +LL ~ let value = mutex.lock().unwrap()[0]; +LL ~ for val in [value, 2] { + | + +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/single_char_add_str.fixed b/src/tools/clippy/tests/ui/single_char_add_str.fixed index eafd17f53874..aef15252b1bc 100644 --- a/src/tools/clippy/tests/ui/single_char_add_str.fixed +++ b/src/tools/clippy/tests/ui/single_char_add_str.fixed @@ -21,6 +21,12 @@ fn main() { string.push('\u{0052}'); string.push('a'); + let c_ref = &'a'; + string.push(*c_ref); + let c = 'a'; + string.push(c); + string.push('a'); + get_string!().push('ö'); // `insert_str` tests @@ -41,5 +47,9 @@ fn main() { string.insert(Y, '"'); string.insert(Y, '\''); + string.insert(0, *c_ref); + string.insert(0, c); + string.insert(0, 'a'); + get_string!().insert(1, '?'); } diff --git a/src/tools/clippy/tests/ui/single_char_add_str.rs b/src/tools/clippy/tests/ui/single_char_add_str.rs index 5326c7cf24c6..7f97250dacd4 100644 --- a/src/tools/clippy/tests/ui/single_char_add_str.rs +++ b/src/tools/clippy/tests/ui/single_char_add_str.rs @@ -21,6 +21,12 @@ fn main() { string.push_str("\u{0052}"); string.push_str(r##"a"##); + let c_ref = &'a'; + string.push_str(&c_ref.to_string()); + let c = 'a'; + string.push_str(&c.to_string()); + string.push_str(&'a'.to_string()); + get_string!().push_str("ö"); // `insert_str` tests @@ -41,5 +47,9 @@ fn main() { string.insert_str(Y, r##"""##); string.insert_str(Y, r##"'"##); + string.insert_str(0, &c_ref.to_string()); + string.insert_str(0, &c.to_string()); + string.insert_str(0, &'a'.to_string()); + get_string!().insert_str(1, "?"); } diff --git a/src/tools/clippy/tests/ui/single_char_add_str.stderr b/src/tools/clippy/tests/ui/single_char_add_str.stderr index 89d75f20f55a..7791c67578aa 100644 --- a/src/tools/clippy/tests/ui/single_char_add_str.stderr +++ b/src/tools/clippy/tests/ui/single_char_add_str.stderr @@ -31,65 +31,101 @@ error: calling `push_str()` using a single-character string literal LL | string.push_str(r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` +error: calling `push_str()` using a single-character converted to string + --> tests/ui/single_char_add_str.rs:25:5 + | +LL | string.push_str(&c_ref.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` without `to_string()`: `string.push(*c_ref)` + +error: calling `push_str()` using a single-character converted to string + --> tests/ui/single_char_add_str.rs:27:5 + | +LL | string.push_str(&c.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` without `to_string()`: `string.push(c)` + +error: calling `push_str()` using a single-character converted to string + --> tests/ui/single_char_add_str.rs:28:5 + | +LL | string.push_str(&'a'.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` without `to_string()`: `string.push('a')` + error: calling `push_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:24:5 + --> tests/ui/single_char_add_str.rs:30:5 | LL | get_string!().push_str("ö"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `get_string!().push('ö')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:29:5 + --> tests/ui/single_char_add_str.rs:35:5 | LL | string.insert_str(0, "R"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:30:5 + --> tests/ui/single_char_add_str.rs:36:5 | LL | string.insert_str(1, "'"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '\'')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:35:5 + --> tests/ui/single_char_add_str.rs:41:5 | LL | string.insert_str(0, "\x52"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '\x52')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:36:5 + --> tests/ui/single_char_add_str.rs:42:5 | LL | string.insert_str(0, "\u{0052}"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '\u{0052}')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:38:5 + --> tests/ui/single_char_add_str.rs:44:5 | LL | string.insert_str(x, r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:40:5 + --> tests/ui/single_char_add_str.rs:46:5 | LL | string.insert_str(Y, r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:41:5 + --> tests/ui/single_char_add_str.rs:47:5 | LL | string.insert_str(Y, r##"""##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '"')` error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:42:5 + --> tests/ui/single_char_add_str.rs:48:5 | LL | string.insert_str(Y, r##"'"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '\'')` +error: calling `insert_str()` using a single-character converted to string + --> tests/ui/single_char_add_str.rs:50:5 + | +LL | string.insert_str(0, &c_ref.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` without `to_string()`: `string.insert(0, *c_ref)` + +error: calling `insert_str()` using a single-character converted to string + --> tests/ui/single_char_add_str.rs:51:5 + | +LL | string.insert_str(0, &c.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` without `to_string()`: `string.insert(0, c)` + +error: calling `insert_str()` using a single-character converted to string + --> tests/ui/single_char_add_str.rs:52:5 + | +LL | string.insert_str(0, &'a'.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` without `to_string()`: `string.insert(0, 'a')` + error: calling `insert_str()` using a single-character string literal - --> tests/ui/single_char_add_str.rs:44:5 + --> tests/ui/single_char_add_str.rs:54:5 | LL | get_string!().insert_str(1, "?"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')` -error: aborting due to 15 previous errors +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed index ec4ae2ea13c5..6ede7bfcd9f6 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed +++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed @@ -45,8 +45,8 @@ fn std_instead_of_core() { let _ = std::env!("PATH"); - // do not lint until `error_in_core` is stable - use std::error::Error; + use core::error::Error; + //~^ ERROR: used import from `std` instead of `core` // lint items re-exported from private modules, `core::iter::traits::iterator::Iterator` use core::iter::Iterator; diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs index c12c459c7eb4..e22b4f61f3ec 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.rs +++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs @@ -45,8 +45,8 @@ fn std_instead_of_core() { let _ = std::env!("PATH"); - // do not lint until `error_in_core` is stable use std::error::Error; + //~^ ERROR: used import from `std` instead of `core` // lint items re-exported from private modules, `core::iter::traits::iterator::Iterator` use std::iter::Iterator; diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.stderr b/src/tools/clippy/tests/ui/std_instead_of_core.stderr index 8f920511cc5d..22cb9db7050b 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.stderr +++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr @@ -49,6 +49,12 @@ error: used import from `std` instead of `core` LL | let cell_absolute = ::std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:48:9 + | +LL | use std::error::Error; + | ^^^ help: consider importing the item from `core`: `core` + error: used import from `std` instead of `core` --> tests/ui/std_instead_of_core.rs:52:9 | @@ -79,5 +85,5 @@ LL | use alloc::slice::from_ref; = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]` -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/str_to_string.fixed b/src/tools/clippy/tests/ui/str_to_string.fixed new file mode 100644 index 000000000000..52e40b45a8bd --- /dev/null +++ b/src/tools/clippy/tests/ui/str_to_string.fixed @@ -0,0 +1,9 @@ +#![warn(clippy::str_to_string)] + +fn main() { + let hello = "hello world".to_owned(); + //~^ ERROR: `to_string()` called on a `&str` + let msg = &hello[..]; + msg.to_owned(); + //~^ ERROR: `to_string()` called on a `&str` +} diff --git a/src/tools/clippy/tests/ui/str_to_string.stderr b/src/tools/clippy/tests/ui/str_to_string.stderr index 13b73622d698..a761d96cd6b1 100644 --- a/src/tools/clippy/tests/ui/str_to_string.stderr +++ b/src/tools/clippy/tests/ui/str_to_string.stderr @@ -2,9 +2,8 @@ error: `to_string()` called on a `&str` --> tests/ui/str_to_string.rs:4:17 | LL | let hello = "hello world".to_string(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"hello world".to_owned()` | - = help: consider using `.to_owned()` = note: `-D clippy::str-to-string` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::str_to_string)]` @@ -12,9 +11,7 @@ error: `to_string()` called on a `&str` --> tests/ui/str_to_string.rs:7:5 | LL | msg.to_string(); - | ^^^^^^^^^^^^^^^ - | - = help: consider using `.to_owned()` + | ^^^^^^^^^^^^^^^ help: try: `msg.to_owned()` error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr b/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr index aef6c3914526..23d5dcd3a8da 100644 --- a/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr +++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr @@ -1,35 +1,11 @@ -error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:6:5 - | -LL | /// - first one - | ^^^^ help: consider using four spaces per tab - | - = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::tabs_in_doc_comments)]` - -error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:6:13 - | -LL | /// - first one - | ^^^^^^^^ help: consider using four spaces per tab - -error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:7:5 - | -LL | /// - second one - | ^^^^ help: consider using four spaces per tab - -error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:7:14 - | -LL | /// - second one - | ^^^^ help: consider using four spaces per tab - error: using tabs in doc comments is not recommended --> tests/ui/tabs_in_doc_comments.rs:10:9 | LL | /// - First String: | ^^^^ help: consider using four spaces per tab + | + = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::tabs_in_doc_comments)]` error: using tabs in doc comments is not recommended --> tests/ui/tabs_in_doc_comments.rs:11:9 @@ -49,5 +25,29 @@ error: using tabs in doc comments is not recommended LL | /// - needs to be inside here | ^^^^^^^^ help: consider using four spaces per tab +error: using tabs in doc comments is not recommended + --> tests/ui/tabs_in_doc_comments.rs:6:5 + | +LL | /// - first one + | ^^^^ help: consider using four spaces per tab + +error: using tabs in doc comments is not recommended + --> tests/ui/tabs_in_doc_comments.rs:6:13 + | +LL | /// - first one + | ^^^^^^^^ help: consider using four spaces per tab + +error: using tabs in doc comments is not recommended + --> tests/ui/tabs_in_doc_comments.rs:7:5 + | +LL | /// - second one + | ^^^^ help: consider using four spaces per tab + +error: using tabs in doc comments is not recommended + --> tests/ui/tabs_in_doc_comments.rs:7:14 + | +LL | /// - second one + | ^^^^ help: consider using four spaces per tab + error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs index 0039c805b7df..d325887bfba3 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs @@ -1,5 +1,9 @@ #![deny(clippy::type_repetition_in_bounds)] -#![allow(clippy::extra_unused_type_parameters, clippy::multiple_bound_locations)] +#![allow( + clippy::extra_unused_type_parameters, + clippy::multiple_bound_locations, + clippy::needless_maybe_sized +)] use serde::Deserialize; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr index e9c6b41aaa8e..77944c950457 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr @@ -1,5 +1,5 @@ error: this type has already been used as a bound predicate - --> tests/ui/type_repetition_in_bounds.rs:10:5 + --> tests/ui/type_repetition_in_bounds.rs:14:5 | LL | T: Clone, | ^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::type_repetition_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this type has already been used as a bound predicate - --> tests/ui/type_repetition_in_bounds.rs:28:5 + --> tests/ui/type_repetition_in_bounds.rs:32:5 | LL | Self: Copy + Default + Ord, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | Self: Copy + Default + Ord, = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` error: this type has already been used as a bound predicate - --> tests/ui/type_repetition_in_bounds.rs:103:5 + --> tests/ui/type_repetition_in_bounds.rs:107:5 | LL | T: Clone, | ^^^^^^^^ @@ -28,7 +28,7 @@ LL | T: Clone, = help: consider combining the bounds: `T: ?Sized + Clone` error: this type has already been used as a bound predicate - --> tests/ui/type_repetition_in_bounds.rs:109:5 + --> tests/ui/type_repetition_in_bounds.rs:113:5 | LL | T: ?Sized, | ^^^^^^^^^ @@ -36,7 +36,7 @@ LL | T: ?Sized, = help: consider combining the bounds: `T: Clone + ?Sized` error: this type has already been used as a bound predicate - --> tests/ui/type_repetition_in_bounds.rs:135:9 + --> tests/ui/type_repetition_in_bounds.rs:139:9 | LL | T: Trait, Box<[String]>, bool> + 'static, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unicode.stderr b/src/tools/clippy/tests/ui/unicode.stderr index 9c365e1097db..b004493300ee 100644 --- a/src/tools/clippy/tests/ui/unicode.stderr +++ b/src/tools/clippy/tests/ui/unicode.stderr @@ -11,7 +11,7 @@ error: invisible character detected --> tests/ui/unicode.rs:7:12 | LL | print!("Here >­< is a SHY, and ­another"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >\u{AD}< is a SHY, and \u{AD}another"` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >\u{AD}< is a SHY, and \u{AD}another"` error: invisible character detected --> tests/ui/unicode.rs:9:12 diff --git a/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.rs b/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.rs index ff960520f5e7..9915f8b843ef 100644 --- a/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.rs +++ b/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.rs @@ -5,18 +5,18 @@ //~^ ERROR: no need to put clippy lints behind a `clippy` cfg #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] //~^ ERROR: no need to put clippy lints behind a `clippy` cfg -#![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +#![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] //~^ ERROR: no need to put clippy lints behind a `clippy` cfg -#![cfg_attr(clippy, deny(clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +#![cfg_attr(clippy, deny(clippy::non_minimal_cfg))] //~^ ERROR: no need to put clippy lints behind a `clippy` cfg #[cfg_attr(clippy, deny(clippy::non_minimal_cfg))] //~^ ERROR: no need to put clippy lints behind a `clippy` cfg #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] //~^ ERROR: no need to put clippy lints behind a `clippy` cfg -#[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +#[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] //~^ ERROR: no need to put clippy lints behind a `clippy` cfg -#[cfg_attr(clippy, deny(clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +#[cfg_attr(clippy, deny(clippy::non_minimal_cfg))] //~^ ERROR: no need to put clippy lints behind a `clippy` cfg pub struct Bar; diff --git a/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr b/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr index 3d58c9eb5dae..16a861652956 100644 --- a/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_clippy_cfg.stderr @@ -18,16 +18,16 @@ LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] error: no need to put clippy lints behind a `clippy` cfg --> tests/ui/unnecessary_clippy_cfg.rs:17:36 | -LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] - | ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: write instead: `#[deny(clippy::non_minimal_cfg,clippy::maybe_misused_cfg)]` + = note: write instead: `#[deny(clippy::non_minimal_cfg)]` error: no need to put clippy lints behind a `clippy` cfg --> tests/ui/unnecessary_clippy_cfg.rs:19:1 | -LL | #[cfg_attr(clippy, deny(clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#[deny(clippy::non_minimal_cfg, clippy::maybe_misused_cfg)]` +LL | #[cfg_attr(clippy, deny(clippy::non_minimal_cfg))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#[deny(clippy::non_minimal_cfg)]` error: no need to put clippy lints behind a `clippy` cfg --> tests/ui/unnecessary_clippy_cfg.rs:4:1 @@ -46,21 +46,21 @@ LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] error: no need to put clippy lints behind a `clippy` cfg --> tests/ui/unnecessary_clippy_cfg.rs:8:37 | -LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] - | ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: write instead: `#![deny(clippy::non_minimal_cfg,clippy::maybe_misused_cfg)]` + = note: write instead: `#![deny(clippy::non_minimal_cfg)]` error: no need to put clippy lints behind a `clippy` cfg --> tests/ui/unnecessary_clippy_cfg.rs:10:1 | -LL | #![cfg_attr(clippy, deny(clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#![deny(clippy::non_minimal_cfg, clippy::maybe_misused_cfg)]` +LL | #![cfg_attr(clippy, deny(clippy::non_minimal_cfg))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#![deny(clippy::non_minimal_cfg)]` error: duplicated attribute --> tests/ui/unnecessary_clippy_cfg.rs:8:26 | -LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] | ^^^^^^^^^ | note: first defined here @@ -71,7 +71,7 @@ LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] help: remove this attribute --> tests/ui/unnecessary_clippy_cfg.rs:8:26 | -LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] | ^^^^^^^^^ = note: `-D clippy::duplicated-attributes` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::duplicated_attributes)]` @@ -79,7 +79,7 @@ LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_ error: duplicated attribute --> tests/ui/unnecessary_clippy_cfg.rs:17:25 | -LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] | ^^^^^^^^^ | note: first defined here @@ -90,7 +90,7 @@ LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] help: remove this attribute --> tests/ui/unnecessary_clippy_cfg.rs:17:25 | -LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg, clippy::maybe_misused_cfg))] +LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] | ^^^^^^^^^ error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed index ad0e5fab029e..dc5e163ff04e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed @@ -21,6 +21,8 @@ fn main() { let _ = check_files_ref_mut(&[(FileType::Account, path)]); let _ = check_files_self_and_arg(&[(FileType::Account, path)]); let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]); + + check_mut_iteratee_and_modify_inner_variable(); } // `check_files` and its variants are based on: @@ -138,3 +140,62 @@ fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool { fn get_file_path(_file_type: &FileType) -> Result { Ok(std::path::PathBuf::new()) } + +// Issue 12098 +// https://github.com/rust-lang/rust-clippy/issues/12098 +// no message emits +fn check_mut_iteratee_and_modify_inner_variable() { + struct Test { + list: Vec, + mut_this: bool, + } + + impl Test { + fn list(&self) -> &[String] { + &self.list + } + } + + let mut test = Test { + list: vec![String::from("foo"), String::from("bar")], + mut_this: false, + }; + + for _item in test.list().to_vec() { + println!("{}", _item); + + test.mut_this = true; + { + test.mut_this = true; + } + } +} + +mod issue_12821 { + fn foo() { + let v: Vec<_> = "hello".chars().collect(); + for c in v.iter() { + //~^ ERROR: unnecessary use of `cloned` + println!("{c}"); // should not suggest to remove `&` + } + } + + fn bar() { + let v: Vec<_> = "hello".chars().collect(); + for c in v.iter() { + //~^ ERROR: unnecessary use of `cloned` + let ref_c = c; //~ HELP: remove any references to the binding + println!("{ref_c}"); + } + } + + fn baz() { + let v: Vec<_> = "hello".chars().enumerate().collect(); + for (i, c) in v.iter() { + //~^ ERROR: unnecessary use of `cloned` + let ref_c = c; //~ HELP: remove any references to the binding + let ref_i = i; + println!("{i} {ref_c}"); // should not suggest to remove `&` from `i` + } + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs index d3d59c4c70f5..8f797ac717fb 100644 --- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs @@ -21,6 +21,8 @@ fn main() { let _ = check_files_ref_mut(&[(FileType::Account, path)]); let _ = check_files_self_and_arg(&[(FileType::Account, path)]); let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]); + + check_mut_iteratee_and_modify_inner_variable(); } // `check_files` and its variants are based on: @@ -138,3 +140,62 @@ fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool { fn get_file_path(_file_type: &FileType) -> Result { Ok(std::path::PathBuf::new()) } + +// Issue 12098 +// https://github.com/rust-lang/rust-clippy/issues/12098 +// no message emits +fn check_mut_iteratee_and_modify_inner_variable() { + struct Test { + list: Vec, + mut_this: bool, + } + + impl Test { + fn list(&self) -> &[String] { + &self.list + } + } + + let mut test = Test { + list: vec![String::from("foo"), String::from("bar")], + mut_this: false, + }; + + for _item in test.list().to_vec() { + println!("{}", _item); + + test.mut_this = true; + { + test.mut_this = true; + } + } +} + +mod issue_12821 { + fn foo() { + let v: Vec<_> = "hello".chars().collect(); + for c in v.iter().cloned() { + //~^ ERROR: unnecessary use of `cloned` + println!("{c}"); // should not suggest to remove `&` + } + } + + fn bar() { + let v: Vec<_> = "hello".chars().collect(); + for c in v.iter().cloned() { + //~^ ERROR: unnecessary use of `cloned` + let ref_c = &c; //~ HELP: remove any references to the binding + println!("{ref_c}"); + } + } + + fn baz() { + let v: Vec<_> = "hello".chars().enumerate().collect(); + for (i, c) in v.iter().cloned() { + //~^ ERROR: unnecessary use of `cloned` + let ref_c = &c; //~ HELP: remove any references to the binding + let ref_i = &i; + println!("{i} {ref_c}"); // should not suggest to remove `&` from `i` + } + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr index 9d3591e0dbfc..0bdb37a521fc 100644 --- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr @@ -1,5 +1,5 @@ error: unnecessary use of `copied` - --> tests/ui/unnecessary_iter_cloned.rs:29:22 + --> tests/ui/unnecessary_iter_cloned.rs:31:22 | LL | for (t, path) in files.iter().copied() { | ^^^^^^^^^^^^^^^^^^^^^ @@ -10,14 +10,14 @@ help: use | LL | for (t, path) in files { | ~~~~~ -help: remove this `&` +help: remove any references to the binding | LL - let other = match get_file_path(&t) { LL + let other = match get_file_path(t) { | error: unnecessary use of `copied` - --> tests/ui/unnecessary_iter_cloned.rs:44:22 + --> tests/ui/unnecessary_iter_cloned.rs:46:22 | LL | for (t, path) in files.iter().copied() { | ^^^^^^^^^^^^^^^^^^^^^ @@ -26,11 +26,49 @@ help: use | LL | for (t, path) in files.iter() { | ~~~~~~~~~~~~ -help: remove this `&` +help: remove any references to the binding | LL - let other = match get_file_path(&t) { LL + let other = match get_file_path(t) { | -error: aborting due to 2 previous errors +error: unnecessary use of `cloned` + --> tests/ui/unnecessary_iter_cloned.rs:177:18 + | +LL | for c in v.iter().cloned() { + | ^^^^^^^^^^^^^^^^^ help: use: `v.iter()` + +error: unnecessary use of `cloned` + --> tests/ui/unnecessary_iter_cloned.rs:185:18 + | +LL | for c in v.iter().cloned() { + | ^^^^^^^^^^^^^^^^^ + | +help: use + | +LL | for c in v.iter() { + | ~~~~~~~~ +help: remove any references to the binding + | +LL - let ref_c = &c; +LL + let ref_c = c; + | + +error: unnecessary use of `cloned` + --> tests/ui/unnecessary_iter_cloned.rs:194:23 + | +LL | for (i, c) in v.iter().cloned() { + | ^^^^^^^^^^^^^^^^^ + | +help: use + | +LL | for (i, c) in v.iter() { + | ~~~~~~~~ +help: remove any references to the binding + | +LL ~ let ref_c = c; +LL ~ let ref_i = i; + | + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr index 5475df9c7b93..2829f3cd6e98 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr @@ -487,7 +487,7 @@ help: use | LL | for t in file_types { | ~~~~~~~~~~ -help: remove this `&` +help: remove any references to the binding | LL - let path = match get_file_path(&t) { LL + let path = match get_file_path(t) { diff --git a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs index 70dcaa3afa45..5187e0790423 100644 --- a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs +++ b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs @@ -1,3 +1,4 @@ +#![feature(lint_reasons)] #![warn(clippy::unsafe_derive_deserialize)] #![allow(unused, clippy::missing_safety_doc)] @@ -71,4 +72,14 @@ impl G { } } +// Check that we honor the `expect` attribute on the ADT +#[expect(clippy::unsafe_derive_deserialize)] +#[derive(Deserialize)] +pub struct H; +impl H { + pub fn unsafe_block(&self) { + unsafe {} + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr index f2d4429f707a..06719f23d57f 100644 --- a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr +++ b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr @@ -1,5 +1,5 @@ error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe` - --> tests/ui/unsafe_derive_deserialize.rs:8:10 + --> tests/ui/unsafe_derive_deserialize.rs:9:10 | LL | #[derive(Deserialize)] | ^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | #[derive(Deserialize)] = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe` - --> tests/ui/unsafe_derive_deserialize.rs:17:10 + --> tests/ui/unsafe_derive_deserialize.rs:18:10 | LL | #[derive(Deserialize)] | ^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | #[derive(Deserialize)] = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe` - --> tests/ui/unsafe_derive_deserialize.rs:24:10 + --> tests/ui/unsafe_derive_deserialize.rs:25:10 | LL | #[derive(Deserialize)] | ^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | #[derive(Deserialize)] = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe` - --> tests/ui/unsafe_derive_deserialize.rs:33:10 + --> tests/ui/unsafe_derive_deserialize.rs:34:10 | LL | #[derive(Deserialize)] | ^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unwrap_in_result.rs b/src/tools/clippy/tests/ui/unwrap_in_result.rs index a19db46c667d..62c6d959c84b 100644 --- a/src/tools/clippy/tests/ui/unwrap_in_result.rs +++ b/src/tools/clippy/tests/ui/unwrap_in_result.rs @@ -38,6 +38,11 @@ impl A { } None } + + fn in_closure(a: Option) -> Option { + let c = || a.unwrap(); + Some(c()) + } } fn main() { diff --git a/src/tools/clippy/tests/ui/unwrap_in_result.stderr b/src/tools/clippy/tests/ui/unwrap_in_result.stderr index 29c7a9373aa7..752177aaca57 100644 --- a/src/tools/clippy/tests/ui/unwrap_in_result.stderr +++ b/src/tools/clippy/tests/ui/unwrap_in_result.stderr @@ -38,5 +38,21 @@ note: potential non-recoverable error(s) LL | let i = i_str.parse::().expect("not a number"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: used unwrap or expect in a function that returns result or option + --> tests/ui/unwrap_in_result.rs:42:5 + | +LL | / fn in_closure(a: Option) -> Option { +LL | | let c = || a.unwrap(); +LL | | Some(c()) +LL | | } + | |_____^ + | + = help: unwrap and expect should not be used in a function that returns result or option +note: potential non-recoverable error(s) + --> tests/ui/unwrap_in_result.rs:43:20 + | +LL | let c = || a.unwrap(); + | ^^^^^^^^^^ + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/useless_attribute.fixed b/src/tools/clippy/tests/ui/useless_attribute.fixed index 81759086f79e..231fc0a892ad 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.fixed +++ b/src/tools/clippy/tests/ui/useless_attribute.fixed @@ -86,8 +86,51 @@ mod module { #[rustfmt::skip] #[allow(unused_import_braces)] +#[allow(unused_braces)] use module::{Struct}; fn main() { test_indented_attr(); } + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/4467 +#[allow(dead_code)] +use std::collections as puppy_doggy; + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/11595 +pub mod hidden_glob_reexports { + #![allow(unreachable_pub)] + + mod my_prelude { + pub struct MyCoolTypeInternal; + pub use MyCoolTypeInternal as MyCoolType; + } + + mod my_uncool_type { + pub(crate) struct MyUncoolType; + } + + // This exports `MyCoolType`. + pub use my_prelude::*; + + // This hides `my_prelude::MyCoolType`. + #[allow(hidden_glob_reexports)] + use my_uncool_type::MyUncoolType as MyCoolType; +} + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/10878 +pub mod ambiguous_glob_exports { + #![allow(unreachable_pub)] + + mod my_prelude { + pub struct MyType; + } + + mod my_type { + pub struct MyType; + } + + #[allow(ambiguous_glob_reexports)] + pub use my_prelude::*; + pub use my_type::*; +} diff --git a/src/tools/clippy/tests/ui/useless_attribute.rs b/src/tools/clippy/tests/ui/useless_attribute.rs index 59a9dcf093bc..8dfcd2110a4b 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.rs +++ b/src/tools/clippy/tests/ui/useless_attribute.rs @@ -86,8 +86,51 @@ mod module { #[rustfmt::skip] #[allow(unused_import_braces)] +#[allow(unused_braces)] use module::{Struct}; fn main() { test_indented_attr(); } + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/4467 +#[allow(dead_code)] +use std::collections as puppy_doggy; + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/11595 +pub mod hidden_glob_reexports { + #![allow(unreachable_pub)] + + mod my_prelude { + pub struct MyCoolTypeInternal; + pub use MyCoolTypeInternal as MyCoolType; + } + + mod my_uncool_type { + pub(crate) struct MyUncoolType; + } + + // This exports `MyCoolType`. + pub use my_prelude::*; + + // This hides `my_prelude::MyCoolType`. + #[allow(hidden_glob_reexports)] + use my_uncool_type::MyUncoolType as MyCoolType; +} + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/10878 +pub mod ambiguous_glob_exports { + #![allow(unreachable_pub)] + + mod my_prelude { + pub struct MyType; + } + + mod my_type { + pub struct MyType; + } + + #[allow(ambiguous_glob_reexports)] + pub use my_prelude::*; + pub use my_type::*; +} diff --git a/src/tools/clippy/tests/ui/useless_conversion_try.rs b/src/tools/clippy/tests/ui/useless_conversion_try.rs index 803d3b39f375..23edeae12b83 100644 --- a/src/tools/clippy/tests/ui/useless_conversion_try.rs +++ b/src/tools/clippy/tests/ui/useless_conversion_try.rs @@ -1,5 +1,9 @@ #![deny(clippy::useless_conversion)] -#![allow(clippy::needless_if, clippy::unnecessary_fallible_conversions)] +#![allow( + clippy::needless_if, + clippy::unnecessary_fallible_conversions, + clippy::manual_unwrap_or_default +)] fn test_generic(val: T) -> T { let _ = T::try_from(val).unwrap(); diff --git a/src/tools/clippy/tests/ui/useless_conversion_try.stderr b/src/tools/clippy/tests/ui/useless_conversion_try.stderr index 11fb8f38ea5c..30a43629dbd6 100644 --- a/src/tools/clippy/tests/ui/useless_conversion_try.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion_try.stderr @@ -1,5 +1,5 @@ error: useless conversion to the same type: `T` - --> tests/ui/useless_conversion_try.rs:5:13 + --> tests/ui/useless_conversion_try.rs:9:13 | LL | let _ = T::try_from(val).unwrap(); | ^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: useless conversion to the same type: `T` - --> tests/ui/useless_conversion_try.rs:7:5 + --> tests/ui/useless_conversion_try.rs:11:5 | LL | val.try_into().unwrap() | ^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | val.try_into().unwrap() = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion_try.rs:30:21 + --> tests/ui/useless_conversion_try.rs:34:21 | LL | let _: String = "foo".to_string().try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | let _: String = "foo".to_string().try_into().unwrap(); = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion_try.rs:32:21 + --> tests/ui/useless_conversion_try.rs:36:21 | LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); = help: consider removing `TryFrom::try_from()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion_try.rs:34:13 + --> tests/ui/useless_conversion_try.rs:38:13 | LL | let _ = String::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | let _ = String::try_from("foo".to_string()).unwrap(); = help: consider removing `String::try_from()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion_try.rs:36:13 + --> tests/ui/useless_conversion_try.rs:40:13 | LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); = help: consider removing `String::try_from()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion_try.rs:38:21 + --> tests/ui/useless_conversion_try.rs:42:21 | LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion_try.rs:40:21 + --> tests/ui/useless_conversion_try.rs:44:21 | LL | let _: String = String::new().try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | let _: String = String::new().try_into().unwrap(); = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion_try.rs:42:27 + --> tests/ui/useless_conversion_try.rs:46:27 | LL | let _: String = match String::from("_").try_into() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/while_float.rs b/src/tools/clippy/tests/ui/while_float.rs new file mode 100644 index 000000000000..a3b0618948e6 --- /dev/null +++ b/src/tools/clippy/tests/ui/while_float.rs @@ -0,0 +1,14 @@ +#[deny(clippy::while_float)] +fn main() { + let mut x = 0.0_f32; + while x < 42.0_f32 { + x += 0.5; + } + while x < 42.0 { + x += 1.0; + } + let mut x = 0; + while x < 42 { + x += 1; + } +} diff --git a/src/tools/clippy/tests/ui/while_float.stderr b/src/tools/clippy/tests/ui/while_float.stderr new file mode 100644 index 000000000000..b8e934b97c6c --- /dev/null +++ b/src/tools/clippy/tests/ui/while_float.stderr @@ -0,0 +1,20 @@ +error: while condition comparing floats + --> tests/ui/while_float.rs:4:11 + | +LL | while x < 42.0_f32 { + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui/while_float.rs:1:8 + | +LL | #[deny(clippy::while_float)] + | ^^^^^^^^^^^^^^^^^^^ + +error: while condition comparing floats + --> tests/ui/while_float.rs:7:11 + | +LL | while x < 42.0 { + | ^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/workspace.rs b/src/tools/clippy/tests/workspace.rs index 699ab2be199a..19ccc7ae9607 100644 --- a/src/tools/clippy/tests/workspace.rs +++ b/src/tools/clippy/tests/workspace.rs @@ -1,5 +1,3 @@ -#![feature(lazy_cell)] - use std::path::PathBuf; use std::process::Command; use test_utils::{CARGO_CLIPPY_PATH, IS_RUSTC_TEST_SUITE}; diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js index c63edd5bf709..7cca298df8e4 100644 --- a/src/tools/clippy/util/gh-pages/script.js +++ b/src/tools/clippy/util/gh-pages/script.js @@ -397,7 +397,7 @@ $scope.bySearch = function (lint, index, array) { let searchStr = $scope.search; // It can be `null` I haven't missed this value - if (searchStr == null || searchStr.length < 3) { + if (searchStr == null) { return true; } searchStr = searchStr.toLowerCase(); @@ -406,7 +406,7 @@ } // Search by id - if (lint.id.indexOf(searchStr.replace("-", "_")) !== -1) { + if (lint.id.indexOf(searchStr.replaceAll("-", "_")) !== -1) { return true; } diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index afbcc3e92bcb..da7f03441e72 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -187,9 +187,6 @@ pub struct Config { /// The rustdoc executable. pub rustdoc_path: Option, - /// The rust-demangler executable. - pub rust_demangler_path: Option, - /// The coverage-dump executable. pub coverage_dump_path: Option, @@ -250,7 +247,7 @@ pub struct Config { /// Only run tests that match these filters pub filters: Vec, - /// Skip tests tests matching these substrings. Corresponds to + /// Skip tests matching these substrings. Corresponds to /// `test::TestOpts::skip`. `filter_exact` does not apply to these flags. pub skip: Vec, @@ -381,7 +378,7 @@ pub struct Config { /// Whether to rerun tests even if the inputs are unchanged. pub force_rerun: bool, - /// Only rerun the tests that result has been modified accoring to Git status + /// Only rerun the tests that result has been modified according to Git status pub only_modified: bool, pub target_cfgs: OnceLock, @@ -585,7 +582,7 @@ impl TargetCfgs { name, Some( value - .strip_suffix("\"") + .strip_suffix('\"') .expect("key-value pair should be properly quoted"), ), ) diff --git a/src/tools/compiletest/src/errors.rs b/src/tools/compiletest/src/errors.rs index c11d3da13a8b..7a5abc51d043 100644 --- a/src/tools/compiletest/src/errors.rs +++ b/src/tools/compiletest/src/errors.rs @@ -57,6 +57,18 @@ pub struct Error { pub msg: String, } +impl Error { + pub fn render_for_expected(&self) -> String { + use colored::Colorize; + format!( + "{: <10}line {: >3}: {}", + self.kind.map(|kind| kind.to_string()).unwrap_or_default().to_uppercase(), + self.line_num, + self.msg.cyan(), + ) + } +} + #[derive(PartialEq, Debug)] enum WhichLine { ThisLine, diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index c7c807d3e684..5469b9f1a0a2 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -14,6 +14,7 @@ use crate::common::{Config, Debugger, FailMode, Mode, PassMode}; use crate::header::cfg::parse_cfg_name_directive; use crate::header::cfg::MatchOutcome; use crate::header::needs::CachedNeedsConditions; +use crate::util::static_regex; use crate::{extract_cdb_version, extract_gdb_version}; mod cfg; @@ -81,7 +82,7 @@ impl EarlyProps { panic!("errors encountered during EarlyProps parsing"); } - return props; + props } } @@ -94,7 +95,7 @@ pub struct TestProps { // Extra flags to pass to the compiler pub compile_flags: Vec, // Extra flags to pass when the compiled code is run (such as --bench) - pub run_flags: Option, + pub run_flags: Vec, // If present, the name of a file that this test should match when // pretty-printed pub pp_exact: Option, @@ -107,6 +108,9 @@ pub struct TestProps { // Similar to `aux_builds`, but a list of NAME=somelib.rs of dependencies // to build and pass with the `--extern` flag. pub aux_crates: Vec<(String, String)>, + /// Similar to `aux_builds`, but also passes the resulting dylib path to + /// `-Zcodegen-backend`. + pub aux_codegen_backend: Option, // Environment settings to use for compiling pub rustc_env: Vec<(String, String)>, // Environment variables to unset prior to compiling. @@ -231,6 +235,7 @@ mod directives { pub const AUX_BIN: &'static str = "aux-bin"; pub const AUX_BUILD: &'static str = "aux-build"; pub const AUX_CRATE: &'static str = "aux-crate"; + pub const AUX_CODEGEN_BACKEND: &'static str = "aux-codegen-backend"; pub const EXEC_ENV: &'static str = "exec-env"; pub const RUSTC_ENV: &'static str = "rustc-env"; pub const UNSET_EXEC_ENV: &'static str = "unset-exec-env"; @@ -262,11 +267,12 @@ impl TestProps { error_patterns: vec![], regex_error_patterns: vec![], compile_flags: vec![], - run_flags: None, + run_flags: vec![], pp_exact: None, aux_builds: vec![], aux_bins: vec![], aux_crates: vec![], + aux_codegen_backend: None, revisions: vec![], rustc_env: vec![ ("RUSTC_ICE".to_string(), "0".to_string()), @@ -376,7 +382,7 @@ impl TestProps { // Individual flags can be single-quoted to preserve spaces; see // . flags - .split("'") + .split('\'') .enumerate() .flat_map(|(i, f)| { if i % 2 == 1 { vec![f] } else { f.split_whitespace().collect() } @@ -399,7 +405,9 @@ impl TestProps { config.parse_and_update_revisions(ln, &mut self.revisions); - config.set_name_value_directive(ln, RUN_FLAGS, &mut self.run_flags, |r| r); + if let Some(flags) = config.parse_name_value_directive(ln, RUN_FLAGS) { + self.run_flags.extend(split_flags(&flags)); + } if self.pp_exact.is_none() { self.pp_exact = config.parse_pp_exact(ln, testfile); @@ -444,6 +452,9 @@ impl TestProps { &mut self.aux_crates, Config::parse_aux_crate, ); + if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND) { + self.aux_codegen_backend = Some(r.trim().to_owned()); + } config.push_name_value_directive( ln, EXEC_ENV, @@ -602,7 +613,7 @@ impl TestProps { for key in &["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] { if let Ok(val) = env::var(key) { - if self.exec_env.iter().find(|&&(ref x, _)| x == key).is_none() { + if !self.exec_env.iter().any(|&(ref x, _)| x == key) { self.exec_env.push(((*key).to_owned(), val)) } } @@ -720,6 +731,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "assembly-output", "aux-bin", "aux-build", + "aux-codegen-backend", "aux-crate", "build-aux-docs", "build-fail", @@ -747,6 +759,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-aarch64", "ignore-aarch64-unknown-linux-gnu", "ignore-android", + "ignore-apple", "ignore-arm", "ignore-avr", "ignore-beta", @@ -798,6 +811,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-none", "ignore-nto", "ignore-nvptx64", + "ignore-nvptx64-nvidia-cuda", "ignore-openbsd", "ignore-pass", "ignore-remote", @@ -829,7 +843,6 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-x32", "ignore-x86", "ignore-x86_64", - "ignore-x86_64-apple-darwin", "ignore-x86_64-unknown-linux-gnu", "incremental", "known-bug", @@ -842,9 +855,9 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "needs-asm-support", "needs-dlltool", "needs-dynamic-linking", + "needs-force-clang-based-tests", "needs-git-hash", "needs-llvm-components", - "needs-matching-clang", "needs-profiler-support", "needs-relocation-model-pic", "needs-run-enabled", @@ -876,6 +889,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-32bit", "only-64bit", "only-aarch64", + "only-apple", "only-arm", "only-avr", "only-beta", @@ -977,7 +991,7 @@ pub(crate) fn check_directive(directive_ln: &str) -> CheckDirectiveResult<'_> { let trailing = post.trim().split_once(' ').map(|(pre, _)| pre).unwrap_or(post); let trailing_directive = { // 1. is the directive name followed by a space? (to exclude `:`) - matches!(directive_ln.get(directive_name.len()..), Some(s) if s.starts_with(" ")) + matches!(directive_ln.get(directive_name.len()..), Some(s) if s.starts_with(' ')) // 2. is what is after that directive also a directive (ex: "only-x86 only-arm") && KNOWN_DIRECTIVE_NAMES.contains(&trailing) } @@ -1009,9 +1023,6 @@ fn iter_header( if mode == Mode::CoverageRun { let extra_directives: &[&str] = &[ "needs-profiler-support", - // FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works - // properly. Since we only have GCC on the CI ignore the test for now. - "ignore-windows-gnu", // FIXME(pietroalbini): this test currently does not work on cross-compiled // targets because remote-test is not capable of sending back the *.profraw // files generated by the LLVM instrumentation. @@ -1173,11 +1184,11 @@ impl Config { } } - fn parse_custom_normalization(&self, mut line: &str, prefix: &str) -> Option<(String, String)> { + fn parse_custom_normalization(&self, line: &str, prefix: &str) -> Option<(String, String)> { if parse_cfg_name_directive(self, line, prefix).outcome == MatchOutcome::Match { - let from = parse_normalization_string(&mut line)?; - let to = parse_normalization_string(&mut line)?; - Some((from, to)) + let (regex, replacement) = parse_normalize_rule(line) + .unwrap_or_else(|| panic!("couldn't parse custom normalization rule: `{line}`")); + Some((regex, replacement)) } else { None } @@ -1266,6 +1277,9 @@ fn expand_variables(mut value: String, config: &Config) -> String { const CWD: &str = "{{cwd}}"; const SRC_BASE: &str = "{{src-base}}"; const BUILD_BASE: &str = "{{build-base}}"; + const SYSROOT_BASE: &str = "{{sysroot-base}}"; + const TARGET_LINKER: &str = "{{target-linker}}"; + const TARGET: &str = "{{target}}"; if value.contains(CWD) { let cwd = env::current_dir().unwrap(); @@ -1280,27 +1294,44 @@ fn expand_variables(mut value: String, config: &Config) -> String { value = value.replace(BUILD_BASE, &config.build_base.to_string_lossy()); } + if value.contains(SYSROOT_BASE) { + value = value.replace(SYSROOT_BASE, &config.sysroot_base.to_string_lossy()); + } + + if value.contains(TARGET_LINKER) { + value = value.replace(TARGET_LINKER, config.target_linker.as_deref().unwrap_or("")); + } + + if value.contains(TARGET) { + value = value.replace(TARGET, &config.target); + } + value } -/// Finds the next quoted string `"..."` in `line`, and extract the content from it. Move the `line` -/// variable after the end of the quoted string. -/// -/// # Examples -/// +/// Parses the regex and replacement values of a `//@ normalize-*` header, +/// in the format: +/// ```text +/// normalize-*: "REGEX" -> "REPLACEMENT" /// ``` -/// let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\"."; -/// let first = parse_normalization_string(&mut s); -/// assert_eq!(first, Some("something (32 bits)".to_owned())); -/// assert_eq!(s, " -> \"something ($WORD bits)\"."); -/// ``` -fn parse_normalization_string(line: &mut &str) -> Option { - // FIXME support escapes in strings. - let begin = line.find('"')? + 1; - let end = line[begin..].find('"')? + begin; - let result = line[begin..end].to_owned(); - *line = &line[end + 1..]; - Some(result) +fn parse_normalize_rule(header: &str) -> Option<(String, String)> { + // FIXME(#126370): A colon after the header name should be mandatory, but + // currently is not, and there are many tests that lack the colon. + // FIXME: Support escaped double-quotes in strings. + let captures = static_regex!( + r#"(?x) # (verbose mode regex) + ^ + [^:\s]+:?\s* # (header name followed by optional colon) + "(?[^"]*)" # "REGEX" + \s+->\s+ # -> + "(?[^"]*)" # "REPLACEMENT" + $ + "# + ) + .captures(header)?; + let regex = captures["regex"].to_owned(); + let replacement = captures["replacement"].to_owned(); + Some((regex, replacement)) } pub fn extract_llvm_version(version: &str) -> Option { @@ -1329,7 +1360,7 @@ pub fn extract_llvm_version_from_binary(binary_path: &str) -> Option { } let version = String::from_utf8(output.stdout).ok()?; for line in version.lines() { - if let Some(version) = line.split("LLVM version ").skip(1).next() { + if let Some(version) = line.split("LLVM version ").nth(1) { return extract_llvm_version(version); } } @@ -1360,7 +1391,7 @@ where let min = parse(min)?; let max = match max { - Some(max) if max.is_empty() => return None, + Some("") => return None, Some(max) => parse(max)?, _ => min, }; @@ -1432,12 +1463,12 @@ pub fn make_test_description( decision!(ignore_gdb(config, ln)); decision!(ignore_lldb(config, ln)); - if config.target == "wasm32-unknown-unknown" { - if config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS) { - decision!(IgnoreDecision::Ignore { - reason: "ignored when checking the run results on WASM".into(), - }); - } + if config.target == "wasm32-unknown-unknown" + && config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS) + { + decision!(IgnoreDecision::Ignore { + reason: "ignored on WASM as the run results cannot be checked there".into(), + }); } should_fail |= config.parse_name_directive(ln, "should-fail"); @@ -1576,8 +1607,11 @@ fn ignore_llvm(config: &Config, line: &str) -> IgnoreDecision { .split_whitespace() .find(|needed_component| !components.contains(needed_component)) { - if env::var_os("COMPILETEST_NEEDS_ALL_LLVM_COMPONENTS").is_some() { - panic!("missing LLVM component: {}", missing_component); + if env::var_os("COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS").is_some() { + panic!( + "missing LLVM component {}, and COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS is set", + missing_component + ); } return IgnoreDecision::Ignore { reason: format!("ignored when the {missing_component} LLVM component is missing"), diff --git a/src/tools/compiletest/src/header/cfg.rs b/src/tools/compiletest/src/header/cfg.rs index df8c80470502..522c52b1de2a 100644 --- a/src/tools/compiletest/src/header/cfg.rs +++ b/src/tools/compiletest/src/header/cfg.rs @@ -58,7 +58,7 @@ pub(super) fn parse_cfg_name_directive<'a>( // Some of the matchers might be "" depending on what the target information is. To avoid // problems we outright reject empty directives. - if name == "" { + if name.is_empty() { return ParsedNameDirective::not_a_directive(); } @@ -159,6 +159,12 @@ pub(super) fn parse_cfg_name_directive<'a>( message: "when the architecture is part of the Thumb family" } + condition! { + name: "apple", + condition: config.target.contains("apple"), + message: "when the target vendor is Apple" + } + // Technically the locally built compiler uses the "dev" channel rather than the "nightly" // channel, even though most people don't know or won't care about it. To avoid confusion, we // treat the "dev" channel as the "nightly" channel when processing the directive. diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs index 7bc7e4b86803..b96832db67be 100644 --- a/src/tools/compiletest/src/header/needs.rs +++ b/src/tools/compiletest/src/header/needs.rs @@ -100,9 +100,9 @@ pub(super) fn handle_needs( ignore_reason: "ignored when profiler support is disabled", }, Need { - name: "needs-matching-clang", + name: "needs-force-clang-based-tests", condition: config.run_clang_based_tests_with.is_some(), - ignore_reason: "ignored when the used clang does not match the built LLVM", + ignore_reason: "ignored when RUSTBUILD_FORCE_CLANG_BASED_TESTS is not set", }, Need { name: "needs-xray", diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 8a37a4d6d313..61a85b84ad64 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -3,7 +3,7 @@ use std::path::Path; use std::str::FromStr; use crate::common::{Config, Debugger, Mode}; -use crate::header::{parse_normalization_string, EarlyProps, HeadersCache}; +use crate::header::{parse_normalize_rule, EarlyProps, HeadersCache}; use super::iter_header; @@ -32,35 +32,41 @@ fn make_test_description( } #[test] -fn test_parse_normalization_string() { - let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\"."; - let first = parse_normalization_string(&mut s); - assert_eq!(first, Some("something (32 bits)".to_owned())); - assert_eq!(s, " -> \"something ($WORD bits)\"."); +fn test_parse_normalize_rule() { + let good_data = &[ + ( + r#"normalize-stderr-32bit: "something (32 bits)" -> "something ($WORD bits)""#, + "something (32 bits)", + "something ($WORD bits)", + ), + // FIXME(#126370): A colon after the header name should be mandatory, + // but currently is not, and there are many tests that lack the colon. + ( + r#"normalize-stderr-32bit "something (32 bits)" -> "something ($WORD bits)""#, + "something (32 bits)", + "something ($WORD bits)", + ), + ]; - // Nothing to normalize (No quotes) - let mut s = "normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)."; - let first = parse_normalization_string(&mut s); - assert_eq!(first, None); - assert_eq!(s, r#"normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)."#); + for &(input, expected_regex, expected_replacement) in good_data { + let parsed = parse_normalize_rule(input); + let parsed = + parsed.as_ref().map(|(regex, replacement)| (regex.as_str(), replacement.as_str())); + assert_eq!(parsed, Some((expected_regex, expected_replacement))); + } - // Nothing to normalize (Only a single quote) - let mut s = "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits)."; - let first = parse_normalization_string(&mut s); - assert_eq!(first, None); - assert_eq!(s, "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits)."); + let bad_data = &[ + r#"normalize-stderr-16bit: something (16 bits) -> something ($WORD bits)"#, + r#"normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)"#, + r#"normalize-stderr-32bit: "something (32 bits) -> something ($WORD bits)"#, + r#"normalize-stderr-32bit: "something (32 bits)" -> "something ($WORD bits)"#, + r#"normalize-stderr-32bit: "something (32 bits)" -> "something ($WORD bits)"."#, + ]; - // Nothing to normalize (Three quotes) - let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)."; - let first = parse_normalization_string(&mut s); - assert_eq!(first, Some("something (32 bits)".to_owned())); - assert_eq!(s, " -> \"something ($WORD bits)."); - - // Nothing to normalize (No quotes, 16-bit) - let mut s = "normalize-stderr-16bit: something (16 bits) -> something ($WORD bits)."; - let first = parse_normalization_string(&mut s); - assert_eq!(first, None); - assert_eq!(s, r#"normalize-stderr-16bit: something (16 bits) -> something ($WORD bits)."#); + for &input in bad_data { + let parsed = parse_normalize_rule(input); + assert_eq!(parsed, None); + } } #[derive(Default)] diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs index 10726b984208..29e8809e5bd6 100644 --- a/src/tools/compiletest/src/json.rs +++ b/src/tools/compiletest/src/json.rs @@ -282,7 +282,7 @@ fn push_expected_errors( // Add notes for the backtrace for span in primary_spans { - for frame in &span.expansion { + if let Some(frame) = &span.expansion { push_backtrace(expected_errors, frame, file_name); } } @@ -315,7 +315,7 @@ fn push_backtrace( }); } - for previous_expansion in &expansion.span.expansion { + if let Some(previous_expansion) = &expansion.span.expansion { push_backtrace(expected_errors, previous_expansion, file_name); } } diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 2e45caec46cd..0cf05b32e968 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -46,7 +46,6 @@ pub fn parse_config(args: Vec) -> Config { .reqopt("", "run-lib-path", "path to target shared libraries", "PATH") .reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH") .optopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH") - .optopt("", "rust-demangler-path", "path to rust-demangler to use in tests", "PATH") .optopt("", "coverage-dump-path", "path to coverage-dump to use in tests", "PATH") .reqopt("", "python", "path to python to use for doc tests", "PATH") .optopt("", "jsondocck-path", "path to jsondocck to use for doc tests", "PATH") @@ -232,7 +231,6 @@ pub fn parse_config(args: Vec) -> Config { run_lib_path: make_absolute(opt_path(matches, "run-lib-path")), rustc_path: opt_path(matches, "rustc-path"), rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from), - rust_demangler_path: matches.opt_str("rust-demangler-path").map(PathBuf::from), coverage_dump_path: matches.opt_str("coverage-dump-path").map(PathBuf::from), python: matches.opt_str("python").unwrap(), jsondocck_path: matches.opt_str("jsondocck-path"), @@ -337,7 +335,6 @@ pub fn log_config(config: &Config) { logv(c, format!("run_lib_path: {:?}", config.run_lib_path)); logv(c, format!("rustc_path: {:?}", config.rustc_path.display())); logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path)); - logv(c, format!("rust_demangler_path: {:?}", config.rust_demangler_path)); logv(c, format!("src_base: {:?}", config.src_base.display())); logv(c, format!("build_base: {:?}", config.build_base.display())); logv(c, format!("stage_id: {}", config.stage_id)); @@ -950,7 +947,7 @@ fn is_android_gdb_target(target: &str) -> bool { ) } -/// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing. +/// Returns `true` if the given target is a MSVC target for the purposes of CDB testing. fn is_pc_windows_msvc_target(target: &str) -> bool { target.ends_with("-pc-windows-msvc") } @@ -1150,7 +1147,7 @@ fn extract_lldb_version(full_version_line: &str) -> Option<(u32, bool)> { } fn not_a_digit(c: char) -> bool { - !c.is_digit(10) + !c.is_ascii_digit() } fn check_overlapping_tests(found_paths: &HashSet) { diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 42c751bb6bed..1ec3f0a0552e 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -1,10 +1,18 @@ -use std::{env, sync::Arc}; +use std::{env, io::IsTerminal, sync::Arc}; use compiletest::{common::Mode, log_config, parse_config, run_tests}; fn main() { tracing_subscriber::fmt::init(); + // colored checks stdout by default, but for some reason only stderr is a terminal. + // compiletest *does* print many things to stdout, but it doesn't really matter. + if std::io::stderr().is_terminal() + && matches!(std::env::var("NO_COLOR").as_deref(), Err(_) | Ok("0")) + { + colored::control::set_override(true); + } + let config = Arc::new(parse_config(env::args().collect())); if config.valgrind_path.is_none() && config.force_valgrind { diff --git a/src/tools/compiletest/src/read2.rs b/src/tools/compiletest/src/read2.rs index 3f1921cb6bd5..8c06339f3c36 100644 --- a/src/tools/compiletest/src/read2.rs +++ b/src/tools/compiletest/src/read2.rs @@ -6,7 +6,6 @@ mod tests; pub use self::imp::read2; use std::io::{self, Write}; -use std::mem::replace; use std::process::{Child, Output}; #[derive(Copy, Clone, Debug)] @@ -101,10 +100,10 @@ impl ProcOutput { return; } - let mut head = replace(bytes, Vec::new()); + let mut head = std::mem::take(bytes); // Don't truncate if this as a whole line. // That should make it less likely that we cut a JSON line in half. - if head.last() != Some(&('\n' as u8)) { + if head.last() != Some(&b'\n') { head.truncate(MAX_OUT_LEN); } let skipped = new_len - head.len(); diff --git a/src/tools/compiletest/src/read2/tests.rs b/src/tools/compiletest/src/read2/tests.rs index 5ad2db3cb830..9e052ff069b9 100644 --- a/src/tools/compiletest/src/read2/tests.rs +++ b/src/tools/compiletest/src/read2/tests.rs @@ -64,9 +64,9 @@ fn test_abbreviate_filterss_are_detected() { #[test] fn test_abbreviate_filters_avoid_abbreviations() { let mut out = ProcOutput::new(); - let filters = &[std::iter::repeat('a').take(64).collect::()]; + let filters = &["a".repeat(64)]; - let mut expected = vec![b'.'; MAX_OUT_LEN - FILTERED_PATHS_PLACEHOLDER_LEN as usize]; + let mut expected = vec![b'.'; MAX_OUT_LEN - FILTERED_PATHS_PLACEHOLDER_LEN]; expected.extend_from_slice(filters[0].as_bytes()); out.extend(&expected, filters); @@ -81,7 +81,7 @@ fn test_abbreviate_filters_avoid_abbreviations() { #[test] fn test_abbreviate_filters_can_still_cause_abbreviations() { let mut out = ProcOutput::new(); - let filters = &[std::iter::repeat('a').take(64).collect::()]; + let filters = &["a".repeat(64)]; let mut input = vec![b'.'; MAX_OUT_LEN]; input.extend_from_slice(filters[0].as_bytes()); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 02c9d384ab7e..72b57d91c234 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -9,18 +9,18 @@ use crate::common::{Codegen, CodegenUnits, DebugInfo, Debugger, Rustdoc}; use crate::common::{CompareMode, FailMode, PassMode}; use crate::common::{Config, TestPaths}; use crate::common::{CoverageMap, CoverageRun, Pretty, RunPassValgrind}; -use crate::common::{UI_COVERAGE, UI_COVERAGE_MAP, UI_RUN_STDERR, UI_RUN_STDOUT}; +use crate::common::{UI_RUN_STDERR, UI_RUN_STDOUT}; use crate::compute_diff::{write_diff, write_filtered_diff}; use crate::errors::{self, Error, ErrorKind}; use crate::header::TestProps; use crate::json; use crate::read2::{read2_abbreviated, Truncated}; -use crate::util::{add_dylib_path, dylib_env_var, logv, PathBufExt}; +use crate::util::{add_dylib_path, copy_dir_all, dylib_env_var, logv, static_regex, PathBufExt}; use crate::ColorConfig; +use colored::Colorize; use miropt_test_tools::{files_for_miropt_test, MiroptTest, MiroptTestFile}; use regex::{Captures, Regex}; use rustfix::{apply_suggestions, get_suggestions_from_json, Filter}; - use std::collections::{HashMap, HashSet}; use std::env; use std::ffi::{OsStr, OsString}; @@ -41,19 +41,13 @@ use tracing::*; use crate::extract_gdb_version; use crate::is_android_gdb_target; +mod coverage; mod debugger; use debugger::DebuggerCommands; #[cfg(test)] mod tests; -macro_rules! static_regex { - ($re:literal) => {{ - static RE: ::std::sync::OnceLock<::regex::Regex> = ::std::sync::OnceLock::new(); - RE.get_or_init(|| ::regex::Regex::new($re).unwrap()) - }}; -} - const FAKE_SRC_BASE: &str = "fake-test-src-base"; #[cfg(windows)] @@ -98,7 +92,7 @@ fn get_lib_name(lib: &str, aux_type: AuxType) -> Option { AuxType::Lib => Some(format!("lib{}.rlib", lib)), AuxType::Dylib => Some(if cfg!(windows) { format!("{}.dll", lib) - } else if cfg!(target_os = "macos") { + } else if cfg!(target_vendor = "apple") { format!("lib{}.dylib", lib) } else { format!("lib{}.so", lib) @@ -267,8 +261,8 @@ impl<'test> TestCx<'test> { MirOpt => self.run_mir_opt_test(), Assembly => self.run_assembly_test(), JsDocTest => self.run_js_doc_test(), - CoverageMap => self.run_coverage_map_test(), - CoverageRun => self.run_coverage_run_test(), + CoverageMap => self.run_coverage_map_test(), // see self::coverage + CoverageRun => self.run_coverage_run_test(), // see self::coverage Crashes => self.run_crash_test(), } } @@ -380,11 +374,11 @@ impl<'test> TestCx<'test> { // if a test does not crash, consider it an error if proc_res.status.success() || matches!(proc_res.status.code(), Some(1 | 0)) { - self.fatal(&format!( + self.fatal( "test no longer crashes/triggers ICE! Please give it a mearningful name, \ add a doc-comment to the start of the test explaining why it exists and \ - move it to tests/ui or wherever you see fit." - )); + move it to tests/ui or wherever you see fit.", + ); } } @@ -504,226 +498,20 @@ impl<'test> TestCx<'test> { } } - fn run_coverage_map_test(&self) { - let Some(coverage_dump_path) = &self.config.coverage_dump_path else { - self.fatal("missing --coverage-dump"); - }; - - let (proc_res, llvm_ir_path) = self.compile_test_and_save_ir(); - if !proc_res.status.success() { - self.fatal_proc_rec("compilation failed!", &proc_res); - } - drop(proc_res); - - let mut dump_command = Command::new(coverage_dump_path); - dump_command.arg(llvm_ir_path); - let proc_res = self.run_command_to_procres(&mut dump_command); - if !proc_res.status.success() { - self.fatal_proc_rec("coverage-dump failed!", &proc_res); - } - - let kind = UI_COVERAGE_MAP; - - let expected_coverage_dump = self.load_expected_output(kind); - let actual_coverage_dump = self.normalize_output(&proc_res.stdout, &[]); - - let coverage_dump_errors = - self.compare_output(kind, &actual_coverage_dump, &expected_coverage_dump); - - if coverage_dump_errors > 0 { - self.fatal_proc_rec( - &format!("{coverage_dump_errors} errors occurred comparing coverage output."), - &proc_res, - ); - } - } - - fn run_coverage_run_test(&self) { - let should_run = self.run_if_enabled(); - let proc_res = self.compile_test(should_run, Emit::None); - - if !proc_res.status.success() { - self.fatal_proc_rec("compilation failed!", &proc_res); - } - drop(proc_res); - - if let WillExecute::Disabled = should_run { - return; - } - - let profraw_path = self.output_base_dir().join("default.profraw"); - let profdata_path = self.output_base_dir().join("default.profdata"); - - // Delete any existing profraw/profdata files to rule out unintended - // interference between repeated test runs. - if profraw_path.exists() { - std::fs::remove_file(&profraw_path).unwrap(); - } - if profdata_path.exists() { - std::fs::remove_file(&profdata_path).unwrap(); - } - - let proc_res = self.exec_compiled_test_general( - &[("LLVM_PROFILE_FILE", &profraw_path.to_str().unwrap())], - false, - ); - if self.props.failure_status.is_some() { - self.check_correct_failure_status(&proc_res); - } else if !proc_res.status.success() { - self.fatal_proc_rec("test run failed!", &proc_res); - } - drop(proc_res); - - let mut profraw_paths = vec![profraw_path]; - let mut bin_paths = vec![self.make_exe_name()]; - - if self.config.suite == "coverage-run-rustdoc" { - self.run_doctests_for_coverage(&mut profraw_paths, &mut bin_paths); - } - - // Run `llvm-profdata merge` to index the raw coverage output. - let proc_res = self.run_llvm_tool("llvm-profdata", |cmd| { - cmd.args(["merge", "--sparse", "--output"]); - cmd.arg(&profdata_path); - cmd.args(&profraw_paths); - }); - if !proc_res.status.success() { - self.fatal_proc_rec("llvm-profdata merge failed!", &proc_res); - } - drop(proc_res); - - // Run `llvm-cov show` to produce a coverage report in text format. - let proc_res = self.run_llvm_tool("llvm-cov", |cmd| { - cmd.args(["show", "--format=text", "--show-line-counts-or-regions"]); - - cmd.arg("--Xdemangler"); - cmd.arg(self.config.rust_demangler_path.as_ref().unwrap()); - - cmd.arg("--instr-profile"); - cmd.arg(&profdata_path); - - for bin in &bin_paths { - cmd.arg("--object"); - cmd.arg(bin); - } - - cmd.args(&self.props.llvm_cov_flags); - }); - if !proc_res.status.success() { - self.fatal_proc_rec("llvm-cov show failed!", &proc_res); - } - - let kind = UI_COVERAGE; - - let expected_coverage = self.load_expected_output(kind); - let normalized_actual_coverage = - self.normalize_coverage_output(&proc_res.stdout).unwrap_or_else(|err| { - self.fatal_proc_rec(&err, &proc_res); - }); - - let coverage_errors = - self.compare_output(kind, &normalized_actual_coverage, &expected_coverage); - - if coverage_errors > 0 { - self.fatal_proc_rec( - &format!("{} errors occurred comparing coverage output.", coverage_errors), - &proc_res, - ); - } - } - - /// Run any doctests embedded in this test file, and add any resulting - /// `.profraw` files and doctest executables to the given vectors. - fn run_doctests_for_coverage( - &self, - profraw_paths: &mut Vec, - bin_paths: &mut Vec, - ) { - // Put .profraw files and doctest executables in dedicated directories, - // to make it easier to glob them all later. - let profraws_dir = self.output_base_dir().join("doc_profraws"); - let bins_dir = self.output_base_dir().join("doc_bins"); - - // Remove existing directories to prevent cross-run interference. - if profraws_dir.try_exists().unwrap() { - std::fs::remove_dir_all(&profraws_dir).unwrap(); - } - if bins_dir.try_exists().unwrap() { - std::fs::remove_dir_all(&bins_dir).unwrap(); - } - - let mut rustdoc_cmd = - Command::new(self.config.rustdoc_path.as_ref().expect("--rustdoc-path not passed")); - - // In general there will be multiple doctest binaries running, so we - // tell the profiler runtime to write their coverage data into separate - // profraw files. - rustdoc_cmd.env("LLVM_PROFILE_FILE", profraws_dir.join("%p-%m.profraw")); - - rustdoc_cmd.args(["--test", "-Cinstrument-coverage"]); - - // Without this, the doctests complain about not being able to find - // their enclosing file's crate for some reason. - rustdoc_cmd.args(["--crate-name", "workaround_for_79771"]); - - // Persist the doctest binaries so that `llvm-cov show` can read their - // embedded coverage mappings later. - rustdoc_cmd.arg("-Zunstable-options"); - rustdoc_cmd.arg("--persist-doctests"); - rustdoc_cmd.arg(&bins_dir); - - rustdoc_cmd.arg("-L"); - rustdoc_cmd.arg(self.aux_output_dir_name()); - - rustdoc_cmd.arg(&self.testpaths.file); - - let proc_res = self.compose_and_run_compiler(rustdoc_cmd, None); - if !proc_res.status.success() { - self.fatal_proc_rec("rustdoc --test failed!", &proc_res) - } - - fn glob_iter(path: impl AsRef) -> impl Iterator { - let path_str = path.as_ref().to_str().unwrap(); - let iter = glob(path_str).unwrap(); - iter.map(Result::unwrap) - } - - // Find all profraw files in the profraw directory. - for p in glob_iter(profraws_dir.join("*.profraw")) { - profraw_paths.push(p); - } - // Find all executables in the `--persist-doctests` directory, while - // avoiding other file types (e.g. `.pdb` on Windows). This doesn't - // need to be perfect, as long as it can handle the files actually - // produced by `rustdoc --test`. - for p in glob_iter(bins_dir.join("**/*")) { - let is_bin = p.is_file() - && match p.extension() { - None => true, - Some(ext) => ext == OsStr::new("exe"), - }; - if is_bin { - bin_paths.push(p); - } - } - } - - fn run_llvm_tool(&self, name: &str, configure_cmd_fn: impl FnOnce(&mut Command)) -> ProcRes { - let tool_path = self - .config - .llvm_bin_dir - .as_ref() - .expect("this test expects the LLVM bin dir to be available") - .join(name); - - let mut cmd = Command::new(tool_path); - configure_cmd_fn(&mut cmd); - - self.run_command_to_procres(&mut cmd) - } - + /// Runs a [`Command`] and waits for it to finish, then converts its exit + /// status and output streams into a [`ProcRes`]. + /// + /// The command might have succeeded or failed; it is the caller's + /// responsibility to check the exit status and take appropriate action. + /// + /// # Panics + /// Panics if the command couldn't be executed at all + /// (e.g. because the executable could not be found). + #[must_use = "caller should check whether the command succeeded"] fn run_command_to_procres(&self, cmd: &mut Command) -> ProcRes { - let output = cmd.output().unwrap_or_else(|e| panic!("failed to exec `{cmd:?}`: {e:?}")); + let output = cmd + .output() + .unwrap_or_else(|e| self.fatal(&format!("failed to exec `{cmd:?}` because: {e}"))); let proc_res = ProcRes { status: output.status, @@ -737,143 +525,6 @@ impl<'test> TestCx<'test> { proc_res } - fn normalize_coverage_output(&self, coverage: &str) -> Result { - let normalized = self.normalize_output(coverage, &[]); - let normalized = Self::anonymize_coverage_line_numbers(&normalized); - - let mut lines = normalized.lines().collect::>(); - - Self::sort_coverage_file_sections(&mut lines)?; - Self::sort_coverage_subviews(&mut lines)?; - - let joined_lines = lines.iter().flat_map(|line| [line, "\n"]).collect::(); - Ok(joined_lines) - } - - /// Replace line numbers in coverage reports with the placeholder `LL`, - /// so that the tests are less sensitive to lines being added/removed. - fn anonymize_coverage_line_numbers(coverage: &str) -> String { - // The coverage reporter prints line numbers at the start of a line. - // They are truncated or left-padded to occupy exactly 5 columns. - // (`LineNumberColumnWidth` in `SourceCoverageViewText.cpp`.) - // A pipe character `|` appears immediately after the final digit. - // - // Line numbers that appear inside expansion/instantiation subviews - // have an additional prefix of ` |` for each nesting level. - // - // Branch views also include the relevant line number, so we want to - // redact those too. (These line numbers don't have padding.) - // - // Note: The pattern `(?m:^)` matches the start of a line. - - // ` 1|` => ` LL|` - // ` 10|` => ` LL|` - // ` 100|` => ` LL|` - // ` | 1000|` => ` | LL|` - // ` | | 1000|` => ` | | LL|` - let coverage = static_regex!(r"(?m:^)(?(?: \|)*) *[0-9]+\|") - .replace_all(&coverage, "${prefix} LL|"); - - // ` | Branch (1:` => ` | Branch (LL:` - // ` | | Branch (10:` => ` | | Branch (LL:` - let coverage = static_regex!(r"(?m:^)(?(?: \|)+ Branch \()[0-9]+:") - .replace_all(&coverage, "${prefix}LL:"); - - // ` |---> MC/DC Decision Region (1:30) to (2:` => ` |---> MC/DC Decision Region (LL:30) to (LL:` - let coverage = - static_regex!(r"(?m:^)(?(?: \|)+---> MC/DC Decision Region \()[0-9]+:(?[0-9]+\) to \()[0-9]+:") - .replace_all(&coverage, "${prefix}LL:${middle}LL:"); - - // ` | Condition C1 --> (1:` => ` | Condition C1 --> (LL:` - let coverage = - static_regex!(r"(?m:^)(?(?: \|)+ Condition C[0-9]+ --> \()[0-9]+:") - .replace_all(&coverage, "${prefix}LL:"); - - coverage.into_owned() - } - - /// Coverage reports can describe multiple source files, separated by - /// blank lines. The order of these files is unpredictable (since it - /// depends on implementation details), so we need to sort the file - /// sections into a consistent order before comparing against a snapshot. - fn sort_coverage_file_sections(coverage_lines: &mut Vec<&str>) -> Result<(), String> { - // Group the lines into file sections, separated by blank lines. - let mut sections = coverage_lines.split(|line| line.is_empty()).collect::>(); - - // The last section should be empty, representing an extra trailing blank line. - if !sections.last().is_some_and(|last| last.is_empty()) { - return Err("coverage report should end with an extra blank line".to_owned()); - } - - // Sort the file sections (not including the final empty "section"). - let except_last = sections.len() - 1; - (&mut sections[..except_last]).sort(); - - // Join the file sections back into a flat list of lines, with - // sections separated by blank lines. - let joined = sections.join(&[""] as &[_]); - assert_eq!(joined.len(), coverage_lines.len()); - *coverage_lines = joined; - - Ok(()) - } - - fn sort_coverage_subviews(coverage_lines: &mut Vec<&str>) -> Result<(), String> { - let mut output_lines = Vec::new(); - - // We accumulate a list of zero or more "subviews", where each - // subview is a list of one or more lines. - let mut subviews: Vec> = Vec::new(); - - fn flush<'a>(subviews: &mut Vec>, output_lines: &mut Vec<&'a str>) { - if subviews.is_empty() { - return; - } - - // Take and clear the list of accumulated subviews. - let mut subviews = std::mem::take(subviews); - - // The last "subview" should be just a boundary line on its own, - // so exclude it when sorting the other subviews. - let except_last = subviews.len() - 1; - (&mut subviews[..except_last]).sort(); - - for view in subviews { - for line in view { - output_lines.push(line); - } - } - } - - for (line, line_num) in coverage_lines.iter().zip(1..) { - if line.starts_with(" ------------------") { - // This is a subview boundary line, so start a new subview. - subviews.push(vec![line]); - } else if line.starts_with(" |") { - // Add this line to the current subview. - subviews - .last_mut() - .ok_or(format!( - "unexpected subview line outside of a subview on line {line_num}" - ))? - .push(line); - } else { - // This line is not part of a subview, so sort and print any - // accumulated subviews, and then print the line as-is. - flush(&mut subviews, &mut output_lines); - output_lines.push(line); - } - } - - flush(&mut subviews, &mut output_lines); - assert!(subviews.is_empty()); - - assert_eq!(output_lines.len(), coverage_lines.len()); - *coverage_lines = output_lines; - - Ok(()) - } - fn run_pretty_test(&self) { if self.props.pp_exact.is_some() { logv(self.config, "testing for exact pretty-printing".to_owned()); @@ -1046,10 +697,10 @@ impl<'test> TestCx<'test> { // since it is extensively used in the testsuite. check_cfg.push_str("cfg(FALSE"); for revision in &self.props.revisions { - check_cfg.push_str(","); - check_cfg.push_str(&normalize_revision(&revision)); + check_cfg.push(','); + check_cfg.push_str(&normalize_revision(revision)); } - check_cfg.push_str(")"); + check_cfg.push(')'); cmd.args(&["--check-cfg", &check_cfg]); } @@ -1167,7 +818,7 @@ impl<'test> TestCx<'test> { // Append the other `cdb-command:`s for line in &dbg_cmds.commands { script_str.push_str(line); - script_str.push_str("\n"); + script_str.push('\n'); } script_str.push_str("qq\n"); // Quit the debugger (including remote debugger, if any) @@ -1549,7 +1200,7 @@ impl<'test> TestCx<'test> { // Append the other commands for line in &dbg_cmds.commands { script_str.push_str(line); - script_str.push_str("\n"); + script_str.push('\n'); } // Finally, quit the debugger @@ -1585,7 +1236,7 @@ impl<'test> TestCx<'test> { } else { self.config.lldb_python_dir.as_ref().unwrap().to_string() }; - self.cmd2procres( + self.run_command_to_procres( Command::new(&self.config.python) .arg(&lldb_script_path) .arg(test_executable) @@ -1595,33 +1246,11 @@ impl<'test> TestCx<'test> { ) } - fn cmd2procres(&self, cmd: &mut Command) -> ProcRes { - let (status, out, err) = match cmd.output() { - Ok(Output { status, stdout, stderr }) => { - (status, String::from_utf8(stdout).unwrap(), String::from_utf8(stderr).unwrap()) - } - Err(e) => self.fatal(&format!( - "Failed to setup Python process for \ - LLDB script: {}", - e - )), - }; - - self.dump_output(&out, &err); - ProcRes { - status, - stdout: out, - stderr: err, - truncated: Truncated::No, - cmdline: format!("{:?}", cmd), - } - } - fn cleanup_debug_info_options(&self, options: &Vec) -> Vec { // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS. let options_to_remove = ["-O".to_owned(), "-g".to_owned(), "--debuginfo".to_owned()]; - options.iter().filter(|x| !options_to_remove.contains(x)).map(|x| x.clone()).collect() + options.iter().filter(|x| !options_to_remove.contains(x)).cloned().collect() } fn maybe_add_external_args(&self, cmd: &mut Command, args: &Vec) { @@ -1856,14 +1485,22 @@ impl<'test> TestCx<'test> { unexpected.len(), not_found.len() )); - println!("status: {}\ncommand: {}", proc_res.status, proc_res.cmdline); + println!("status: {}\ncommand: {}\n", proc_res.status, proc_res.cmdline); if !unexpected.is_empty() { - println!("unexpected errors (from JSON output): {:#?}\n", unexpected); + println!("{}", "--- unexpected errors (from JSON output) ---".green()); + for error in &unexpected { + println!("{}", error.render_for_expected()); + } + println!("{}", "---".green()); } if !not_found.is_empty() { - println!("not found errors (from test file): {:#?}\n", not_found); + println!("{}", "--- not found errors (from test file) ---".red()); + for error in ¬_found { + println!("{}", error.render_for_expected()); + } + println!("{}", "---\n".red()); } - panic!(); + panic!("errors differ from expected"); } } @@ -2188,6 +1825,16 @@ impl<'test> TestCx<'test> { )); } } + + // Build any `//@ aux-codegen-backend`, and pass the resulting library + // to `-Zcodegen-backend` when compiling the test file. + if let Some(aux_file) = &self.props.aux_codegen_backend { + let aux_type = self.build_auxiliary(of, aux_file, aux_dir, false); + if let Some(lib_name) = get_lib_name(aux_file.trim_end_matches(".rs"), aux_type) { + let lib_path = aux_dir.join(&lib_name); + rustc.arg(format!("-Zcodegen-backend={}", lib_path.display())); + } + } } fn compose_and_run_compiler(&self, mut rustc: Command, input: Option) -> ProcRes { @@ -2609,6 +2256,9 @@ impl<'test> TestCx<'test> { } match output_file { + // If the test's compile flags specify an output path with `-o`, + // avoid a compiler warning about `--out-dir` being ignored. + _ if self.props.compile_flags.iter().any(|flag| flag == "-o") => {} TargetLocation::ThisFile(path) => { rustc.arg("-o").arg(path); } @@ -2710,7 +2360,7 @@ impl<'test> TestCx<'test> { args.push(exe_file.into_os_string()); // Add the arguments in the run_flags directive - args.extend(self.split_maybe_args(&self.props.run_flags)); + args.extend(self.props.run_flags.iter().map(OsString::from)); let prog = args.remove(0); ProcArgs { prog, args } @@ -2824,6 +2474,7 @@ impl<'test> TestCx<'test> { } } + #[track_caller] fn fatal(&self, err: &str) -> ! { self.error(err); error!("fatal error, panic: {:?}", err); @@ -2853,8 +2504,8 @@ impl<'test> TestCx<'test> { // This works with both `--emit asm` (as default output name for the assembly) // and `ptx-linker` because the latter can write output at requested location. let output_path = self.output_base_name().with_extension(extension); - let output_file = TargetLocation::ThisFile(output_path.clone()); - output_file + + TargetLocation::ThisFile(output_path.clone()) } } @@ -3036,7 +2687,7 @@ impl<'test> TestCx<'test> { if self.config.bless { cmd.arg("--bless"); } - let res = self.cmd2procres(&mut cmd); + let res = self.run_command_to_procres(&mut cmd); if !res.status.success() { self.fatal_proc_rec_with_ctx("htmldocck failed!", &res, |mut this| { this.compare_to_default_rustdoc(&out_dir) @@ -3101,7 +2752,7 @@ impl<'test> TestCx<'test> { for entry in walkdir::WalkDir::new(dir) { let entry = entry.expect("failed to read file"); if entry.file_type().is_file() - && entry.path().extension().and_then(|p| p.to_str()) == Some("html".into()) + && entry.path().extension().and_then(|p| p.to_str()) == Some("html") { let status = Command::new("tidy").args(&tidy_args).arg(entry.path()).status().unwrap(); @@ -3132,8 +2783,7 @@ impl<'test> TestCx<'test> { &compare_dir, self.config.verbose, |file_type, extension| { - file_type.is_file() - && (extension == Some("html".into()) || extension == Some("js".into())) + file_type.is_file() && (extension == Some("html") || extension == Some("js")) }, ) { return; @@ -3179,11 +2829,11 @@ impl<'test> TestCx<'test> { } match String::from_utf8(line.clone()) { Ok(line) => { - if line.starts_with("+") { + if line.starts_with('+') { write!(&mut out, "{}", line.green()).unwrap(); - } else if line.starts_with("-") { + } else if line.starts_with('-') { write!(&mut out, "{}", line.red()).unwrap(); - } else if line.starts_with("@") { + } else if line.starts_with('@') { write!(&mut out, "{}", line.blue()).unwrap(); } else { out.write_all(line.as_bytes()).unwrap(); @@ -3213,7 +2863,7 @@ impl<'test> TestCx<'test> { let root = self.config.find_rust_src_root().unwrap(); let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap()); json_out.set_extension("json"); - let res = self.cmd2procres( + let res = self.run_command_to_procres( Command::new(self.config.jsondocck_path.as_ref().unwrap()) .arg("--doc-dir") .arg(root.join(&out_dir)) @@ -3231,7 +2881,7 @@ impl<'test> TestCx<'test> { let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap()); json_out.set_extension("json"); - let res = self.cmd2procres( + let res = self.run_command_to_procres( Command::new(self.config.jsondoclint_path.as_ref().unwrap()).arg(&json_out), ); @@ -3256,7 +2906,7 @@ impl<'test> TestCx<'test> { && line.ends_with(';') { if let Some(ref mut other_files) = other_files { - other_files.push(line.rsplit("mod ").next().unwrap().replace(";", "")); + other_files.push(line.rsplit("mod ").next().unwrap().replace(';', "")); } None } else { @@ -3488,7 +3138,7 @@ impl<'test> TestCx<'test> { let mut string = String::new(); for cgu in cgus { string.push_str(&cgu[..]); - string.push_str(" "); + string.push(' '); } string @@ -3521,10 +3171,7 @@ impl<'test> TestCx<'test> { // CGUs joined with "--". This function splits such composite CGU names // and handles each component individually. fn remove_crate_disambiguators_from_set_of_cgu_names(cgus: &str) -> String { - cgus.split("--") - .map(|cgu| remove_crate_disambiguator_from_cgu(cgu)) - .collect::>() - .join("--") + cgus.split("--").map(remove_crate_disambiguator_from_cgu).collect::>().join("--") } } @@ -3706,7 +3353,7 @@ impl<'test> TestCx<'test> { // endif } - if self.config.target.contains("msvc") && self.config.cc != "" { + if self.config.target.contains("msvc") && !self.config.cc.is_empty() { // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe` // and that `lib.exe` lives next to it. let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe"); @@ -3794,11 +3441,38 @@ impl<'test> TestCx<'test> { let build_root = self.config.build_base.parent().unwrap().parent().unwrap(); let build_root = cwd.join(&build_root); - let tmpdir = cwd.join(self.output_base_name()); - if tmpdir.exists() { - self.aggressive_rm_rf(&tmpdir).unwrap(); + // We construct the following directory tree for each rmake.rs test: + // ``` + // base_dir/ + // rmake.exe + // rmake_out/ + // ``` + // having the executable separate from the output artifacts directory allows the recipes to + // `remove_dir_all($TMPDIR)` without running into permission denied issues because + // the executable is not under the `rmake_out/` directory. + // + // This setup intentionally diverges from legacy Makefile run-make tests. + let base_dir = cwd.join(self.output_base_name()); + if base_dir.exists() { + self.aggressive_rm_rf(&base_dir).unwrap(); + } + let rmake_out_dir = base_dir.join("rmake_out"); + create_dir_all(&rmake_out_dir).unwrap(); + + // Copy all input files (apart from rmake.rs) to the temporary directory, + // so that the input directory structure from `tests/run-make/` is mirrored + // to the `rmake_out` directory. + for path in walkdir::WalkDir::new(&self.testpaths.file).min_depth(1) { + let path = path.unwrap().path().to_path_buf(); + if path.file_name().is_some_and(|s| s != "rmake.rs") { + let target = rmake_out_dir.join(path.strip_prefix(&self.testpaths.file).unwrap()); + if path.is_dir() { + copy_dir_all(&path, target).unwrap(); + } else { + fs::copy(&path, target).unwrap(); + } + } } - create_dir_all(&tmpdir).unwrap(); // HACK: assume stageN-target, we only want stageN. let stage = self.config.stage_id.split('-').next().unwrap(); @@ -3815,8 +3489,11 @@ impl<'test> TestCx<'test> { stage_std_path.push("lib"); // Then, we need to build the recipe `rmake.rs` and link in the support library. - let recipe_bin = - tmpdir.join(if self.config.target.contains("windows") { "rmake.exe" } else { "rmake" }); + let recipe_bin = base_dir.join(if self.config.target.contains("windows") { + "rmake.exe" + } else { + "rmake" + }); let mut support_lib_deps = PathBuf::new(); support_lib_deps.push(&build_root); @@ -3854,21 +3531,17 @@ impl<'test> TestCx<'test> { .arg(&self.testpaths.file.join("rmake.rs")) .env("TARGET", &self.config.target) .env("PYTHON", &self.config.python) - .env("S", &src_root) .env("RUST_BUILD_STAGE", &self.config.stage_id) .env("RUSTC", cwd.join(&self.config.rustc_path)) - .env("TMPDIR", &tmpdir) .env("LD_LIB_PATH_ENVVAR", dylib_env_var()) .env(dylib_env_var(), &host_dylib_env_paths) .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path)) .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path)) - .env("LLVM_COMPONENTS", &self.config.llvm_components) - // We for sure don't want these tests to run in parallel, so make - // sure they don't have access to these vars if we run via `make` - // at the top level - .env_remove("MAKEFLAGS") - .env_remove("MFLAGS") - .env_remove("CARGO_MAKEFLAGS"); + .env("LLVM_COMPONENTS", &self.config.llvm_components); + + // In test code we want to be very pedantic about values being silently discarded that are + // annotated with `#[must_use]`. + cmd.arg("-Dunused_must_use"); if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() { let mut stage0_sysroot = build_root.clone(); @@ -3879,7 +3552,7 @@ impl<'test> TestCx<'test> { cmd.arg("--sysroot").arg(&stage0_sysroot); } - let res = self.cmd2procres(&mut cmd); + let res = self.run_command_to_procres(&mut cmd); if !res.status.success() { self.fatal_proc_rec("run-make test failed: could not build `rmake.rs` recipe", &res); } @@ -3893,12 +3566,12 @@ impl<'test> TestCx<'test> { let dylib_env_paths = env::join_paths(dylib_env_paths).unwrap(); let mut target_rpath_env_path = Vec::new(); - target_rpath_env_path.push(&tmpdir); + target_rpath_env_path.push(&rmake_out_dir); target_rpath_env_path.extend(&orig_dylib_env_paths); let target_rpath_env_path = env::join_paths(target_rpath_env_path).unwrap(); let mut cmd = Command::new(&recipe_bin); - cmd.current_dir(&self.testpaths.file) + cmd.current_dir(&rmake_out_dir) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .env("LD_LIB_PATH_ENVVAR", dylib_env_var()) @@ -3906,28 +3579,17 @@ impl<'test> TestCx<'test> { .env(dylib_env_var(), &dylib_env_paths) .env("TARGET", &self.config.target) .env("PYTHON", &self.config.python) - .env("S", &src_root) + .env("SOURCE_ROOT", &src_root) .env("RUST_BUILD_STAGE", &self.config.stage_id) .env("RUSTC", cwd.join(&self.config.rustc_path)) - .env("TMPDIR", &tmpdir) .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path)) .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path)) - .env("LLVM_COMPONENTS", &self.config.llvm_components) - // We for sure don't want these tests to run in parallel, so make - // sure they don't have access to these vars if we run via `make` - // at the top level - .env_remove("MAKEFLAGS") - .env_remove("MFLAGS") - .env_remove("CARGO_MAKEFLAGS"); + .env("LLVM_COMPONENTS", &self.config.llvm_components); if let Some(ref rustdoc) = self.config.rustdoc_path { cmd.env("RUSTDOC", cwd.join(rustdoc)); } - if let Some(ref rust_demangler) = self.config.rust_demangler_path { - cmd.env("RUST_DEMANGLER", cwd.join(rust_demangler)); - } - if let Some(ref node) = self.config.nodejs { cmd.env("NODE", node); } @@ -3973,7 +3635,7 @@ impl<'test> TestCx<'test> { // endif } - if self.config.target.contains("msvc") && self.config.cc != "" { + if self.config.target.contains("msvc") && !self.config.cc.is_empty() { // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe` // and that `lib.exe` lives next to it. let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe"); @@ -4040,7 +3702,7 @@ impl<'test> TestCx<'test> { let root = self.config.find_rust_src_root().unwrap(); let file_stem = self.testpaths.file.file_stem().and_then(|f| f.to_str()).expect("no file stem"); - let res = self.cmd2procres( + let res = self.run_command_to_procres( Command::new(&nodejs) .arg(root.join("src/tools/rustdoc-js/tester.js")) .arg("--doc-folder") @@ -4164,7 +3826,7 @@ impl<'test> TestCx<'test> { && !self.props.dont_check_compiler_stderr { self.fatal_proc_rec( - &format!("compiler output got truncated, cannot compare with reference file"), + "compiler output got truncated, cannot compare with reference file", &proc_res, ); } @@ -4345,8 +4007,8 @@ impl<'test> TestCx<'test> { crate_name.to_str().expect("crate name implies file name must be valid UTF-8"); // replace `a.foo` -> `a__foo` for crate name purposes. // replace `revision-name-with-dashes` -> `revision_name_with_underscore` - let crate_name = crate_name.replace(".", "__"); - let crate_name = crate_name.replace("-", "_"); + let crate_name = crate_name.replace('.', "__"); + let crate_name = crate_name.replace('-', "_"); rustc.arg("--crate-name"); rustc.arg(crate_name); } @@ -4394,7 +4056,7 @@ impl<'test> TestCx<'test> { fn check_mir_dump(&self, test_info: MiroptTest) { let test_dir = self.testpaths.file.parent().unwrap(); let test_crate = - self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace("-", "_"); + self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace('-', "_"); let MiroptTest { run_filecheck, suffix, files, passes: _ } = test_info; @@ -4517,10 +4179,12 @@ impl<'test> TestCx<'test> { } fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> String { - let rflags = self.props.run_flags.as_ref(); + // Crude heuristic to detect when the output should have JSON-specific + // normalization steps applied. + let rflags = self.props.run_flags.join(" "); let cflags = self.props.compile_flags.join(" "); - let json = rflags - .map_or(false, |s| s.contains("--format json") || s.contains("--format=json")) + let json = rflags.contains("--format json") + || rflags.contains("--format=json") || cflags.contains("--error-format json") || cflags.contains("--error-format pretty-json") || cflags.contains("--error-format=json") diff --git a/src/tools/compiletest/src/runtest/coverage.rs b/src/tools/compiletest/src/runtest/coverage.rs new file mode 100644 index 000000000000..6ee147da5a96 --- /dev/null +++ b/src/tools/compiletest/src/runtest/coverage.rs @@ -0,0 +1,375 @@ +//! Code specific to the coverage test suites. + +use std::ffi::OsStr; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use glob::glob; + +use crate::common::{UI_COVERAGE, UI_COVERAGE_MAP}; +use crate::runtest::{Emit, ProcRes, TestCx, WillExecute}; +use crate::util::static_regex; + +impl<'test> TestCx<'test> { + fn coverage_dump_path(&self) -> &Path { + self.config + .coverage_dump_path + .as_deref() + .unwrap_or_else(|| self.fatal("missing --coverage-dump")) + } + + pub(crate) fn run_coverage_map_test(&self) { + let coverage_dump_path = self.coverage_dump_path(); + + let (proc_res, llvm_ir_path) = self.compile_test_and_save_ir(); + if !proc_res.status.success() { + self.fatal_proc_rec("compilation failed!", &proc_res); + } + drop(proc_res); + + let mut dump_command = Command::new(coverage_dump_path); + dump_command.arg(llvm_ir_path); + let proc_res = self.run_command_to_procres(&mut dump_command); + if !proc_res.status.success() { + self.fatal_proc_rec("coverage-dump failed!", &proc_res); + } + + let kind = UI_COVERAGE_MAP; + + let expected_coverage_dump = self.load_expected_output(kind); + let actual_coverage_dump = self.normalize_output(&proc_res.stdout, &[]); + + let coverage_dump_errors = + self.compare_output(kind, &actual_coverage_dump, &expected_coverage_dump); + + if coverage_dump_errors > 0 { + self.fatal_proc_rec( + &format!("{coverage_dump_errors} errors occurred comparing coverage output."), + &proc_res, + ); + } + } + + pub(crate) fn run_coverage_run_test(&self) { + let should_run = self.run_if_enabled(); + let proc_res = self.compile_test(should_run, Emit::None); + + if !proc_res.status.success() { + self.fatal_proc_rec("compilation failed!", &proc_res); + } + drop(proc_res); + + if let WillExecute::Disabled = should_run { + return; + } + + let profraw_path = self.output_base_dir().join("default.profraw"); + let profdata_path = self.output_base_dir().join("default.profdata"); + + // Delete any existing profraw/profdata files to rule out unintended + // interference between repeated test runs. + if profraw_path.exists() { + std::fs::remove_file(&profraw_path).unwrap(); + } + if profdata_path.exists() { + std::fs::remove_file(&profdata_path).unwrap(); + } + + let proc_res = self.exec_compiled_test_general( + &[("LLVM_PROFILE_FILE", &profraw_path.to_str().unwrap())], + false, + ); + if self.props.failure_status.is_some() { + self.check_correct_failure_status(&proc_res); + } else if !proc_res.status.success() { + self.fatal_proc_rec("test run failed!", &proc_res); + } + drop(proc_res); + + let mut profraw_paths = vec![profraw_path]; + let mut bin_paths = vec![self.make_exe_name()]; + + if self.config.suite == "coverage-run-rustdoc" { + self.run_doctests_for_coverage(&mut profraw_paths, &mut bin_paths); + } + + // Run `llvm-profdata merge` to index the raw coverage output. + let proc_res = self.run_llvm_tool("llvm-profdata", |cmd| { + cmd.args(["merge", "--sparse", "--output"]); + cmd.arg(&profdata_path); + cmd.args(&profraw_paths); + }); + if !proc_res.status.success() { + self.fatal_proc_rec("llvm-profdata merge failed!", &proc_res); + } + drop(proc_res); + + // Run `llvm-cov show` to produce a coverage report in text format. + let proc_res = self.run_llvm_tool("llvm-cov", |cmd| { + cmd.args(["show", "--format=text", "--show-line-counts-or-regions"]); + + // Specify the demangler binary and its arguments. + let coverage_dump_path = self.coverage_dump_path(); + cmd.arg("--Xdemangler").arg(coverage_dump_path); + cmd.arg("--Xdemangler").arg("--demangle"); + + cmd.arg("--instr-profile"); + cmd.arg(&profdata_path); + + for bin in &bin_paths { + cmd.arg("--object"); + cmd.arg(bin); + } + + cmd.args(&self.props.llvm_cov_flags); + }); + if !proc_res.status.success() { + self.fatal_proc_rec("llvm-cov show failed!", &proc_res); + } + + let kind = UI_COVERAGE; + + let expected_coverage = self.load_expected_output(kind); + let normalized_actual_coverage = + self.normalize_coverage_output(&proc_res.stdout).unwrap_or_else(|err| { + self.fatal_proc_rec(&err, &proc_res); + }); + + let coverage_errors = + self.compare_output(kind, &normalized_actual_coverage, &expected_coverage); + + if coverage_errors > 0 { + self.fatal_proc_rec( + &format!("{} errors occurred comparing coverage output.", coverage_errors), + &proc_res, + ); + } + } + + /// Run any doctests embedded in this test file, and add any resulting + /// `.profraw` files and doctest executables to the given vectors. + fn run_doctests_for_coverage( + &self, + profraw_paths: &mut Vec, + bin_paths: &mut Vec, + ) { + // Put .profraw files and doctest executables in dedicated directories, + // to make it easier to glob them all later. + let profraws_dir = self.output_base_dir().join("doc_profraws"); + let bins_dir = self.output_base_dir().join("doc_bins"); + + // Remove existing directories to prevent cross-run interference. + if profraws_dir.try_exists().unwrap() { + std::fs::remove_dir_all(&profraws_dir).unwrap(); + } + if bins_dir.try_exists().unwrap() { + std::fs::remove_dir_all(&bins_dir).unwrap(); + } + + let mut rustdoc_cmd = + Command::new(self.config.rustdoc_path.as_ref().expect("--rustdoc-path not passed")); + + // In general there will be multiple doctest binaries running, so we + // tell the profiler runtime to write their coverage data into separate + // profraw files. + rustdoc_cmd.env("LLVM_PROFILE_FILE", profraws_dir.join("%p-%m.profraw")); + + rustdoc_cmd.args(["--test", "-Cinstrument-coverage"]); + + // Without this, the doctests complain about not being able to find + // their enclosing file's crate for some reason. + rustdoc_cmd.args(["--crate-name", "workaround_for_79771"]); + + // Persist the doctest binaries so that `llvm-cov show` can read their + // embedded coverage mappings later. + rustdoc_cmd.arg("-Zunstable-options"); + rustdoc_cmd.arg("--persist-doctests"); + rustdoc_cmd.arg(&bins_dir); + + rustdoc_cmd.arg("-L"); + rustdoc_cmd.arg(self.aux_output_dir_name()); + + rustdoc_cmd.arg(&self.testpaths.file); + + let proc_res = self.compose_and_run_compiler(rustdoc_cmd, None); + if !proc_res.status.success() { + self.fatal_proc_rec("rustdoc --test failed!", &proc_res) + } + + fn glob_iter(path: impl AsRef) -> impl Iterator { + let path_str = path.as_ref().to_str().unwrap(); + let iter = glob(path_str).unwrap(); + iter.map(Result::unwrap) + } + + // Find all profraw files in the profraw directory. + for p in glob_iter(profraws_dir.join("*.profraw")) { + profraw_paths.push(p); + } + // Find all executables in the `--persist-doctests` directory, while + // avoiding other file types (e.g. `.pdb` on Windows). This doesn't + // need to be perfect, as long as it can handle the files actually + // produced by `rustdoc --test`. + for p in glob_iter(bins_dir.join("**/*")) { + let is_bin = p.is_file() + && match p.extension() { + None => true, + Some(ext) => ext == OsStr::new("exe"), + }; + if is_bin { + bin_paths.push(p); + } + } + } + + fn run_llvm_tool(&self, name: &str, configure_cmd_fn: impl FnOnce(&mut Command)) -> ProcRes { + let tool_path = self + .config + .llvm_bin_dir + .as_ref() + .expect("this test expects the LLVM bin dir to be available") + .join(name); + + let mut cmd = Command::new(tool_path); + configure_cmd_fn(&mut cmd); + + self.run_command_to_procres(&mut cmd) + } + + fn normalize_coverage_output(&self, coverage: &str) -> Result { + let normalized = self.normalize_output(coverage, &[]); + let normalized = Self::anonymize_coverage_line_numbers(&normalized); + + let mut lines = normalized.lines().collect::>(); + + Self::sort_coverage_file_sections(&mut lines)?; + Self::sort_coverage_subviews(&mut lines)?; + + let joined_lines = lines.iter().flat_map(|line| [line, "\n"]).collect::(); + Ok(joined_lines) + } + + /// Replace line numbers in coverage reports with the placeholder `LL`, + /// so that the tests are less sensitive to lines being added/removed. + fn anonymize_coverage_line_numbers(coverage: &str) -> String { + // The coverage reporter prints line numbers at the start of a line. + // They are truncated or left-padded to occupy exactly 5 columns. + // (`LineNumberColumnWidth` in `SourceCoverageViewText.cpp`.) + // A pipe character `|` appears immediately after the final digit. + // + // Line numbers that appear inside expansion/instantiation subviews + // have an additional prefix of ` |` for each nesting level. + // + // Branch views also include the relevant line number, so we want to + // redact those too. (These line numbers don't have padding.) + // + // Note: The pattern `(?m:^)` matches the start of a line. + + // ` 1|` => ` LL|` + // ` 10|` => ` LL|` + // ` 100|` => ` LL|` + // ` | 1000|` => ` | LL|` + // ` | | 1000|` => ` | | LL|` + let coverage = static_regex!(r"(?m:^)(?(?: \|)*) *[0-9]+\|") + .replace_all(&coverage, "${prefix} LL|"); + + // ` | Branch (1:` => ` | Branch (LL:` + // ` | | Branch (10:` => ` | | Branch (LL:` + let coverage = static_regex!(r"(?m:^)(?(?: \|)+ Branch \()[0-9]+:") + .replace_all(&coverage, "${prefix}LL:"); + + // ` |---> MC/DC Decision Region (1:30) to (2:` => ` |---> MC/DC Decision Region (LL:30) to (LL:` + let coverage = + static_regex!(r"(?m:^)(?(?: \|)+---> MC/DC Decision Region \()[0-9]+:(?[0-9]+\) to \()[0-9]+:") + .replace_all(&coverage, "${prefix}LL:${middle}LL:"); + + // ` | Condition C1 --> (1:` => ` | Condition C1 --> (LL:` + let coverage = + static_regex!(r"(?m:^)(?(?: \|)+ Condition C[0-9]+ --> \()[0-9]+:") + .replace_all(&coverage, "${prefix}LL:"); + + coverage.into_owned() + } + + /// Coverage reports can describe multiple source files, separated by + /// blank lines. The order of these files is unpredictable (since it + /// depends on implementation details), so we need to sort the file + /// sections into a consistent order before comparing against a snapshot. + fn sort_coverage_file_sections(coverage_lines: &mut Vec<&str>) -> Result<(), String> { + // Group the lines into file sections, separated by blank lines. + let mut sections = coverage_lines.split(|line| line.is_empty()).collect::>(); + + // The last section should be empty, representing an extra trailing blank line. + if !sections.last().is_some_and(|last| last.is_empty()) { + return Err("coverage report should end with an extra blank line".to_owned()); + } + + // Sort the file sections (not including the final empty "section"). + let except_last = sections.len() - 1; + (&mut sections[..except_last]).sort(); + + // Join the file sections back into a flat list of lines, with + // sections separated by blank lines. + let joined = sections.join(&[""] as &[_]); + assert_eq!(joined.len(), coverage_lines.len()); + *coverage_lines = joined; + + Ok(()) + } + + fn sort_coverage_subviews(coverage_lines: &mut Vec<&str>) -> Result<(), String> { + let mut output_lines = Vec::new(); + + // We accumulate a list of zero or more "subviews", where each + // subview is a list of one or more lines. + let mut subviews: Vec> = Vec::new(); + + fn flush<'a>(subviews: &mut Vec>, output_lines: &mut Vec<&'a str>) { + if subviews.is_empty() { + return; + } + + // Take and clear the list of accumulated subviews. + let mut subviews = std::mem::take(subviews); + + // The last "subview" should be just a boundary line on its own, + // so exclude it when sorting the other subviews. + let except_last = subviews.len() - 1; + (&mut subviews[..except_last]).sort(); + + for view in subviews { + for line in view { + output_lines.push(line); + } + } + } + + for (line, line_num) in coverage_lines.iter().zip(1..) { + if line.starts_with(" ------------------") { + // This is a subview boundary line, so start a new subview. + subviews.push(vec![line]); + } else if line.starts_with(" |") { + // Add this line to the current subview. + subviews + .last_mut() + .ok_or(format!( + "unexpected subview line outside of a subview on line {line_num}" + ))? + .push(line); + } else { + // This line is not part of a subview, so sort and print any + // accumulated subviews, and then print the line as-is. + flush(&mut subviews, &mut output_lines); + output_lines.push(line); + } + } + + flush(&mut subviews, &mut output_lines); + assert!(subviews.is_empty()); + + assert_eq!(output_lines.len(), coverage_lines.len()); + *coverage_lines = output_lines; + + Ok(()) + } +} diff --git a/src/tools/compiletest/src/runtest/debugger.rs b/src/tools/compiletest/src/runtest/debugger.rs index eebe5f3580b3..ed6cc97a8abb 100644 --- a/src/tools/compiletest/src/runtest/debugger.rs +++ b/src/tools/compiletest/src/runtest/debugger.rs @@ -148,5 +148,5 @@ fn check_single_line(line: &str, check_line: &str) -> bool { rest = &rest[pos + current_fragment.len()..]; } - if !can_end_anywhere && !rest.is_empty() { false } else { true } + can_end_anywhere || rest.is_empty() } diff --git a/src/tools/compiletest/src/runtest/tests.rs b/src/tools/compiletest/src/runtest/tests.rs index 817b56109a50..fb3dd326a4c8 100644 --- a/src/tools/compiletest/src/runtest/tests.rs +++ b/src/tools/compiletest/src/runtest/tests.rs @@ -48,71 +48,3 @@ fn normalize_platform_differences() { r#"println!("test\ntest")"#, ); } - -/// Test for anonymizing line numbers in coverage reports, especially for -/// MC/DC regions. -/// -/// FIXME(#123409): This test can be removed when we have examples of MC/DC -/// coverage in the actual coverage test suite. -#[test] -fn anonymize_coverage_line_numbers() { - let anon = |coverage| TestCx::anonymize_coverage_line_numbers(coverage); - - let input = r#" - 7| 2|fn mcdc_check_neither(a: bool, b: bool) { - 8| 2| if a && b { - ^0 - ------------------ - |---> MC/DC Decision Region (8:8) to (8:14) - | - | Number of Conditions: 2 - | Condition C1 --> (8:8) - | Condition C2 --> (8:13) - | - | Executed MC/DC Test Vectors: - | - | C1, C2 Result - | 1 { F, - = F } - | - | C1-Pair: not covered - | C2-Pair: not covered - | MC/DC Coverage for Decision: 0.00% - | - ------------------ - 9| 0| say("a and b"); - 10| 2| } else { - 11| 2| say("not both"); - 12| 2| } - 13| 2|} -"#; - - let expected = r#" - LL| 2|fn mcdc_check_neither(a: bool, b: bool) { - LL| 2| if a && b { - ^0 - ------------------ - |---> MC/DC Decision Region (LL:8) to (LL:14) - | - | Number of Conditions: 2 - | Condition C1 --> (LL:8) - | Condition C2 --> (LL:13) - | - | Executed MC/DC Test Vectors: - | - | C1, C2 Result - | 1 { F, - = F } - | - | C1-Pair: not covered - | C2-Pair: not covered - | MC/DC Coverage for Decision: 0.00% - | - ------------------ - LL| 0| say("a and b"); - LL| 2| } else { - LL| 2| say("not both"); - LL| 2| } - LL| 2|} -"#; - - assert_eq!(anon(input), expected); -} diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs index e6725dba2605..4292f234adc7 100644 --- a/src/tools/compiletest/src/tests.rs +++ b/src/tools/compiletest/src/tests.rs @@ -58,11 +58,11 @@ fn test_extract_lldb_version() { #[test] fn is_test_test() { - assert_eq!(true, is_test(&OsString::from("a_test.rs"))); - assert_eq!(false, is_test(&OsString::from(".a_test.rs"))); - assert_eq!(false, is_test(&OsString::from("a_cat.gif"))); - assert_eq!(false, is_test(&OsString::from("#a_dog_gif"))); - assert_eq!(false, is_test(&OsString::from("~a_temp_file"))); + assert!(is_test(&OsString::from("a_test.rs"))); + assert!(!is_test(&OsString::from(".a_test.rs"))); + assert!(!is_test(&OsString::from("a_cat.gif"))); + assert!(!is_test(&OsString::from("#a_dog_gif"))); + assert!(!is_test(&OsString::from("~a_temp_file"))); } #[test] diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 8f9425eb0716..cdec49a51d75 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -1,7 +1,7 @@ use crate::common::Config; use std::env; use std::ffi::OsStr; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; use tracing::*; @@ -57,7 +57,7 @@ impl PathBufExt for PathBuf { pub fn dylib_env_var() -> &'static str { if cfg!(windows) { "PATH" - } else if cfg!(target_os = "macos") { + } else if cfg!(target_vendor = "apple") { "DYLD_LIBRARY_PATH" } else if cfg!(target_os = "haiku") { "LIBRARY_PATH" @@ -76,3 +76,25 @@ pub fn add_dylib_path(cmd: &mut Command, paths: impl Iterator, dst: impl AsRef) -> std::io::Result<()> { + std::fs::create_dir_all(&dst)?; + for entry in std::fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; + } else { + std::fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; + } + } + Ok(()) +} + +macro_rules! static_regex { + ($re:literal) => {{ + static RE: ::std::sync::OnceLock<::regex::Regex> = ::std::sync::OnceLock::new(); + RE.get_or_init(|| ::regex::Regex::new($re).unwrap()) + }}; +} +pub(crate) use static_regex; diff --git a/src/tools/coverage-dump/README.md b/src/tools/coverage-dump/README.md index e2625d5adf27..49d8e14c7bcc 100644 --- a/src/tools/coverage-dump/README.md +++ b/src/tools/coverage-dump/README.md @@ -6,3 +6,8 @@ The output format is mostly arbitrary, so it's OK to change the output as long as any affected tests are also re-blessed. However, the output should be consistent across different executions on different platforms, so avoid printing any information that is platform-specific or non-deterministic. + +## Demangle mode + +When run as `coverage-dump --demangle`, this tool instead functions as a +command-line demangler that can be invoked by `llvm-cov`. diff --git a/src/tools/coverage-dump/src/main.rs b/src/tools/coverage-dump/src/main.rs index 93fed1799e04..b21e3e292f2b 100644 --- a/src/tools/coverage-dump/src/main.rs +++ b/src/tools/coverage-dump/src/main.rs @@ -7,6 +7,13 @@ fn main() -> anyhow::Result<()> { let args = std::env::args().collect::>(); + // The coverage-dump tool already needs `rustc_demangle` in order to read + // coverage metadata, so it's very easy to also have a separate mode that + // turns it into a command-line demangler for use by coverage-run tests. + if &args[1..] == &["--demangle"] { + return demangle(); + } + let llvm_ir_path = args.get(1).context("LLVM IR file not specified")?; let llvm_ir = std::fs::read_to_string(llvm_ir_path).context("couldn't read LLVM IR file")?; @@ -15,3 +22,15 @@ fn main() -> anyhow::Result<()> { Ok(()) } + +fn demangle() -> anyhow::Result<()> { + use std::fmt::Write as _; + + let stdin = std::io::read_to_string(std::io::stdin())?; + let mut output = String::with_capacity(stdin.len()); + for line in stdin.lines() { + writeln!(output, "{:#}", rustc_demangle::demangle(line))?; + } + print!("{output}"); + Ok(()) +} diff --git a/src/tools/generate-windows-sys/Cargo.toml b/src/tools/generate-windows-sys/Cargo.toml index 8b971d6efe7e..ebf3082fb4f2 100644 --- a/src/tools/generate-windows-sys/Cargo.toml +++ b/src/tools/generate-windows-sys/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies.windows-bindgen] -version = "0.56.0" +version = "0.57.0" diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs index 9bd04e11cb39..525de03bbce3 100644 --- a/src/tools/jsondoclint/src/item_kind.rs +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -150,7 +150,7 @@ impl Kind { ItemEnum::Impl(_) => Impl, ItemEnum::TypeAlias(_) => TypeAlias, ItemEnum::OpaqueTy(_) => OpaqueTy, - ItemEnum::Constant(_) => Constant, + ItemEnum::Constant { .. } => Constant, ItemEnum::Static(_) => Static, ItemEnum::Macro(_) => Macro, ItemEnum::ProcMacro(_) => ProcMacro, diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 9e08f7e5f9be..1713a4d812c4 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -21,7 +21,7 @@ const LOCAL_CRATE_ID: u32 = 0; /// it is well formed. This involves calling `check_*` functions on /// fields of that item, and `add_*` functions on [`Id`]s. /// - `add_*`: These add an [`Id`] to the worklist, after validating it to check if -/// the `Id` is a kind expected in this suituation. +/// the `Id` is a kind expected in this situation. #[derive(Debug)] pub struct Validator<'a> { pub(crate) errs: Vec, @@ -101,7 +101,10 @@ impl<'a> Validator<'a> { ItemEnum::Impl(x) => self.check_impl(x, id), ItemEnum::TypeAlias(x) => self.check_type_alias(x), ItemEnum::OpaqueTy(x) => self.check_opaque_ty(x), - ItemEnum::Constant(x) => self.check_constant(x), + ItemEnum::Constant { type_, const_ } => { + self.check_type(type_); + self.check_constant(const_); + } ItemEnum::Static(x) => self.check_static(x), ItemEnum::ForeignType => {} // nop ItemEnum::Macro(x) => self.check_macro(x), @@ -231,8 +234,8 @@ impl<'a> Validator<'a> { self.check_generics(&x.generics); } - fn check_constant(&mut self, x: &'a Constant) { - self.check_type(&x.type_); + fn check_constant(&mut self, _x: &'a Constant) { + // nop } fn check_static(&mut self, x: &'a Static) { @@ -415,15 +418,13 @@ impl<'a> Validator<'a> { } else { self.fail_expecting(id, expected); } - } else { - if !self.missing_ids.contains(id) { - self.missing_ids.insert(id); + } else if !self.missing_ids.contains(id) { + self.missing_ids.insert(id); - let sels = json_find::find_selector(&self.krate_json, &Value::String(id.0.clone())); - assert_ne!(sels.len(), 0); + let sels = json_find::find_selector(&self.krate_json, &Value::String(id.0.clone())); + assert_ne!(sels.len(), 0); - self.fail(id, ErrorKind::NotFound(sels)) - } + self.fail(id, ErrorKind::NotFound(sels)) } } diff --git a/src/tools/libcxx-version/main.cpp b/src/tools/libcxx-version/main.cpp new file mode 100644 index 000000000000..79df7ef457c4 --- /dev/null +++ b/src/tools/libcxx-version/main.cpp @@ -0,0 +1,26 @@ +// Detecting the standard library version manually using a bunch of shell commands is very +// complicated and fragile across different platforms. This program provides the major version +// of the standard library on any target platform without requiring any messy work. +// +// It's nothing more than specifying the name of the standard library implementation (either libstdc++ or libc++) +// and its major version. + +#include + +int main() { + #ifdef _GLIBCXX_RELEASE + std::cout << "libstdc++ version: " << _GLIBCXX_RELEASE << std::endl; + #elif defined(_LIBCPP_VERSION) + // _LIBCPP_VERSION follows "XXYYZZ" format (e.g., 170001 for 17.0.1). + // ref: https://github.com/llvm/llvm-project/blob/f64732195c1030ee2627ff4e4142038e01df1d26/libcxx/include/__config#L51-L54 + // + // Since we use the major version from _GLIBCXX_RELEASE, we need to extract only the first 2 characters of _LIBCPP_VERSION + // to provide the major version for consistency. + std::cout << "libc++ version: " << std::to_string(_LIBCPP_VERSION).substr(0, 2) << std::endl; + #else + std::cerr << "Coudln't recognize the standard library version." << std::endl; + return 1; + #endif + + return 0; +} diff --git a/src/tools/linkchecker/Cargo.toml b/src/tools/linkchecker/Cargo.toml index 05049aabc7d9..f1be6e9e749d 100644 --- a/src/tools/linkchecker/Cargo.toml +++ b/src/tools/linkchecker/Cargo.toml @@ -9,4 +9,4 @@ path = "main.rs" [dependencies] regex = "1" -html5ever = "0.26.0" +html5ever = "0.27.0" diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 32f935de7302..7321bd1bb52f 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -503,7 +503,7 @@ fn maybe_redirect(source: &str) -> Option { fn parse_html(source: &str, sink: Sink) -> Sink { let tendril: ByteTendril = source.as_bytes().into(); - let mut input = BufferQueue::new(); + let mut input = BufferQueue::default(); input.push_back(tendril.try_reinterpret().unwrap()); let mut tok = Tokenizer::new(sink, TokenizerOpts::default()); diff --git a/src/tools/lint-docs/src/groups.rs b/src/tools/lint-docs/src/groups.rs index 73f5738469ed..f246d71d499b 100644 --- a/src/tools/lint-docs/src/groups.rs +++ b/src/tools/lint-docs/src/groups.rs @@ -121,13 +121,13 @@ impl<'a> LintExtractor<'a> { }; to_link.extend(group_lints); let brackets: Vec<_> = group_lints.iter().map(|l| format!("[{}]", l)).collect(); - write!(result, "| {} | {} | {} |\n", group_name, description, brackets.join(", ")) + writeln!(result, "| {} | {} | {} |", group_name, description, brackets.join(", ")) .unwrap(); } result.push('\n'); result.push_str("[warn-by-default]: listing/warn-by-default.md\n"); for lint_name in to_link { - let lint_def = match lints.iter().find(|l| l.name == lint_name.replace("-", "_")) { + let lint_def = match lints.iter().find(|l| l.name == lint_name.replace('-', "_")) { Some(def) => def, None => { let msg = format!( @@ -144,9 +144,9 @@ impl<'a> LintExtractor<'a> { } } }; - write!( + writeln!( result, - "[{}]: listing/{}#{}\n", + "[{}]: listing/{}#{}", lint_name, lint_def.level.doc_filename(), lint_name diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index 22ab576b0776..72d6a495e7e7 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -84,8 +84,8 @@ impl Lint { for &expected in &["### Example", "### Explanation", "{{produces}}"] { if expected == "{{produces}}" && self.is_ignored() { if self.doc_contains("{{produces}}") { - return Err(format!( - "the lint example has `ignore`, but also contains the {{{{produces}}}} marker\n\ + return Err( + "the lint example has `ignore`, but also contains the {{produces}} marker\n\ \n\ The documentation generator cannot generate the example output when the \ example is ignored.\n\ @@ -111,7 +111,7 @@ impl Lint { Replacing the output with the text of the example you \ compiled manually yourself.\n\ " - ).into()); + .into()); } continue; } @@ -441,10 +441,19 @@ impl<'a> LintExtractor<'a> { fs::write(&tempfile, source) .map_err(|e| format!("failed to write {}: {}", tempfile.display(), e))?; let mut cmd = Command::new(self.rustc_path); - if options.contains(&"edition2015") { - cmd.arg("--edition=2015"); - } else { + if options.contains(&"edition2024") { + cmd.arg("--edition=2024"); + } else if options.contains(&"edition2021") { + cmd.arg("--edition=2021"); + } else if options.contains(&"edition2018") { cmd.arg("--edition=2018"); + } else if options.contains(&"edition2015") { + cmd.arg("--edition=2015"); + } else if options.contains(&"edition") { + panic!("lint-docs: unknown edition"); + } else { + // defaults to latest edition + cmd.arg("--edition=2021"); } cmd.arg("--error-format=json"); cmd.arg("--target").arg(self.rustc_target); @@ -510,11 +519,11 @@ impl<'a> LintExtractor<'a> { let mut these_lints: Vec<_> = lints.iter().filter(|lint| lint.level == level).collect(); these_lints.sort_unstable_by_key(|lint| &lint.name); for lint in &these_lints { - write!(result, "* [`{}`](#{})\n", lint.name, lint.name.replace("_", "-")).unwrap(); + writeln!(result, "* [`{}`](#{})", lint.name, lint.name.replace('_', "-")).unwrap(); } result.push('\n'); for lint in &these_lints { - write!(result, "## {}\n\n", lint.name.replace("_", "-")).unwrap(); + write!(result, "## {}\n\n", lint.name.replace('_', "-")).unwrap(); for line in &lint.doc { result.push_str(line); result.push('\n'); @@ -574,7 +583,7 @@ fn add_rename_redirect(level: Level, output: &mut String) { let filename = level.doc_filename().replace(".md", ".html"); output.push_str(RENAME_START); for (from, to) in *names { - write!(output, " \"#{from}\": \"{filename}#{to}\",\n").unwrap(); + writeln!(output, " \"#{from}\": \"{filename}#{to}\",").unwrap(); } output.push_str(RENAME_END); } diff --git a/src/tools/lld-wrapper/src/main.rs b/src/tools/lld-wrapper/src/main.rs index da94e686f382..79e279a8614b 100644 --- a/src/tools/lld-wrapper/src/main.rs +++ b/src/tools/lld-wrapper/src/main.rs @@ -1,6 +1,6 @@ //! Script to invoke the bundled rust-lld with the correct flavor. //! -//! lld supports multiple command line interfaces. If `-flavor ` are passed as the first +//! `lld` supports multiple command line interfaces. If `-flavor ` are passed as the first //! two arguments the `` command line interface is used to process the remaining arguments. //! If no `-flavor` argument is present the flavor is determined by the executable name. //! diff --git a/src/tools/miri/.gitignore b/src/tools/miri/.gitignore index 97e006e8b1b8..03c5591b7875 100644 --- a/src/tools/miri/.gitignore +++ b/src/tools/miri/.gitignore @@ -5,6 +5,7 @@ tex/*/out *.out *.rs.bk .vscode +.helix *.mm_profdata perf.data perf.data.old diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index 092ad46a7cad..9067cbc60326 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -223,7 +223,7 @@ will eventually sync those changes back into this repository. When working on Miri in the rustc tree, here's how you can run tests: ``` -./x.py test miri --stage 0 +./x.py test miri ``` `--bless` will work, too. @@ -231,7 +231,7 @@ When working on Miri in the rustc tree, here's how you can run tests: You can also directly run Miri on a Rust source file: ``` -./x.py run miri --stage 0 --args src/tools/miri/tests/pass/hello.rs +./x.py run miri --stage 1 --args src/tools/miri/tests/pass/hello.rs ``` ## Advanced topic: Syncing with the rustc repo @@ -287,7 +287,22 @@ https. Add the following to your `.gitconfig`: pushInsteadOf = https://github.com/ ``` -## Internal environment variables +## Further environment variables + +The following environment variables are relevant to `./miri`: + +* `MIRI_AUTO_OPS` indicates whether the automatic execution of rustfmt, clippy and toolchain setup + (as controlled by the `./auto-*` files) should be skipped. If it is set to `no`, they are skipped. + This is used to allow automated IDE actions to avoid the auto ops. +* `MIRI_LOG`, `MIRI_BACKTRACE` control logging and backtrace printing during Miri executions. +* `MIRI_TEST_THREADS` (recognized by `./miri test`) sets the number of threads to use for running + tests. By default, the number of cores is used. +* `MIRI_SKIP_UI_CHECKS` (recognized by `./miri test`) disables checking that the `stderr` or + `stdout` files match the actual output. + +Furthermore, the usual environment variables recognized by `cargo miri` also work for `./miri`, e.g. +`MIRI_LIB_SRC`. Note that `MIRIFLAGS` is ignored by `./miri test` as each test controls the flags it +is run with. The following environment variables are *internal* and must not be used by anyone but Miri itself. They are used to communicate between different Miri diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 208a8b9ee617..4b4f2f83062f 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -151,6 +151,21 @@ platform. For example `cargo miri test --target s390x-unknown-linux-gnu` will run your test suite on a big-endian target, which is useful for testing endian-sensitive code. +### Testing multiple different executions + +Certain parts of the execution are picked randomly by Miri, such as the exact base address +allocations are stored at and the interleaving of concurrently executing threads. Sometimes, it can +be useful to explore multiple different execution, e.g. to make sure that your code does not depend +on incidental "super-alignment" of new allocations and to test different thread interleavings. +This can be done with the `--many-seeds` flag: + +``` +cargo miri test --many-seeds # tries the seeds in 0..64 +cargo miri test --many-seeds=0..16 +``` + +The default of 64 different seeds is quite slow, so you probably want to specify a smaller range. + ### Running Miri on CI When running Miri on CI, use the following snippet to install a nightly toolchain with the Miri @@ -183,23 +198,6 @@ Here is an example job for GitHub Actions: The explicit `cargo miri setup` helps to keep the output of the actual test step clean. -### Testing for alignment issues - -Miri can sometimes miss misaligned accesses since allocations can "happen to be" -aligned just right. You can use `-Zmiri-symbolic-alignment-check` to definitely -catch all such issues, but that flag will also cause false positives when code -does manual pointer arithmetic to account for alignment. Another alternative is -to call Miri with various values for `-Zmiri-seed`; that will alter the -randomness that is used to determine allocation base addresses. The following -snippet calls Miri in a loop with different values for the seed: - -``` -for SEED in $(seq 0 255); do - echo "Trying seed: $SEED" - MIRIFLAGS=-Zmiri-seed=$SEED cargo miri test || { echo "Failing seed: $SEED"; break; }; -done -``` - ### Supported targets Miri does not support all targets supported by Rust. The good news, however, is @@ -448,28 +446,19 @@ Some native rustc `-Z` flags are also very relevant for Miri: * `-Zmir-emit-retag` controls whether `Retag` statements are emitted. Miri enables this per default because it is needed for [Stacked Borrows] and [Tree Borrows]. -Moreover, Miri recognizes some environment variables (unless noted otherwise, these are supported -by all intended entry points, i.e. `cargo miri` and `./miri {test,run}`): +Moreover, Miri recognizes some environment variables: -* `MIRI_AUTO_OPS` indicates whether the automatic execution of rustfmt, clippy and toolchain setup - should be skipped. If it is set to `no`, they are skipped. This is used to allow automated IDE - actions to avoid the auto ops. -* `MIRI_LOG`, `MIRI_BACKTRACE` control logging and backtrace printing during - Miri executions, also [see "Testing the Miri driver" in `CONTRIBUTING.md`][testing-miri]. * `MIRIFLAGS` defines extra flags to be passed to Miri. * `MIRI_LIB_SRC` defines the directory where Miri expects the sources of the standard library that it will build and use for interpretation. This directory must point to the `library` subdirectory of a `rust-lang/rust` repository checkout. -* `MIRI_SYSROOT` indicates the sysroot to use. When using `cargo miri`, this skips the automatic +* `MIRI_SYSROOT` indicates the sysroot to use. When using `cargo miri test`/`cargo miri run`, this skips the automatic setup -- only set this if you do not want to use the automatically created sysroot. When invoking `cargo miri setup`, this indicates where the sysroot will be put. -* `MIRI_TEST_THREADS` (recognized by `./miri test`): set the number of threads to use for running tests. - By default, the number of cores is used. * `MIRI_NO_STD` makes sure that the target's sysroot is built without libstd. This allows testing - and running no_std programs. (Miri has a heuristic to detect no-std targets based on the target - name; this environment variable is only needed when that heuristic fails.) -* `MIRI_SKIP_UI_CHECKS` (recognized by `./miri test`): don't check whether the - `stderr` or `stdout` files match the actual output. + and running no_std programs. This should *not usually be used*; Miri has a heuristic to detect + no-std targets based on the target name. Setting this on a target that does support libstd can + lead to confusing results. [testing-miri]: CONTRIBUTING.md#testing-the-miri-driver diff --git a/src/tools/miri/bench-cargo-miri/big-allocs/Cargo.lock b/src/tools/miri/bench-cargo-miri/big-allocs/Cargo.lock new file mode 100644 index 000000000000..da0db7f47ea4 --- /dev/null +++ b/src/tools/miri/bench-cargo-miri/big-allocs/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "big-allocs" +version = "0.1.0" diff --git a/src/tools/miri/bench-cargo-miri/big-allocs/Cargo.toml b/src/tools/miri/bench-cargo-miri/big-allocs/Cargo.toml new file mode 100644 index 000000000000..7234c9371cff --- /dev/null +++ b/src/tools/miri/bench-cargo-miri/big-allocs/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "big-allocs" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/tools/miri/bench-cargo-miri/big-allocs/src/main.rs b/src/tools/miri/bench-cargo-miri/big-allocs/src/main.rs new file mode 100644 index 000000000000..a1c1708cf3ba --- /dev/null +++ b/src/tools/miri/bench-cargo-miri/big-allocs/src/main.rs @@ -0,0 +1,13 @@ +//! This is a regression test for https://github.com/rust-lang/miri/issues/3637. +//! `Allocation`s are backed by a `Box<[u8]>`, which we create using `alloc_zeroed`, which should +//! make very large allocations cheap. But then we also need to not clone those `Allocation`s, or +//! we end up slow anyway. + +fn main() { + // We can't use too big of an allocation or this code will encounter an allocation failure in + // CI. Since the allocation can't be huge, we need to do a few iterations so that the effect + // we're trying to measure is clearly visible above the interpreter's startup time. + for _ in 0..10 { + drop(Vec::::with_capacity(512 * 1024 * 1024)); + } +} diff --git a/src/tools/miri/cargo-miri/Cargo.lock b/src/tools/miri/cargo-miri/Cargo.lock index b8ead460249f..8bd8f1030532 100644 --- a/src/tools/miri/cargo-miri/Cargo.lock +++ b/src/tools/miri/cargo-miri/Cargo.lock @@ -178,9 +178,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.4.7" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab1dbbd1bdf65fdac44c885f6cca147ba179108ce284b60a08ccc04b1f1dbac0" +checksum = "fa3ca63cc537c1cb69e4c2c0afc5fda2ccd36ac84c97d5a4ae05e69b1c834afb" dependencies = [ "anyhow", "rustc_version", diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml index b16068b6d192..6acdbc46f642 100644 --- a/src/tools/miri/cargo-miri/Cargo.toml +++ b/src/tools/miri/cargo-miri/Cargo.toml @@ -18,7 +18,7 @@ directories = "5" rustc_version = "0.4" serde_json = "1.0.40" cargo_metadata = "0.18.0" -rustc-build-sysroot = "0.4.6" +rustc-build-sysroot = "0.5.2" # Enable some feature flags that dev-dependencies need but dependencies # do not. This makes `./miri install` after `./miri build` faster. diff --git a/src/tools/miri/cargo-miri/src/main.rs b/src/tools/miri/cargo-miri/src/main.rs index 9fdd4c3e4704..7d9f77f3752d 100644 --- a/src/tools/miri/cargo-miri/src/main.rs +++ b/src/tools/miri/cargo-miri/src/main.rs @@ -1,15 +1,14 @@ #![allow(clippy::useless_format, clippy::derive_partial_eq_without_eq, rustc::internal)] -#[macro_use] -mod util; - mod arg; mod phases; mod setup; +mod util; use std::{env, iter}; use crate::phases::*; +use crate::util::show_error; /// Returns `true` if our flags look like they may be for rustdoc, i.e., this is cargo calling us to /// be rustdoc. It's hard to be sure as cargo does not have a RUSTDOC_WRAPPER or an env var that diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index e2fc2a0c2779..3c36f606d845 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -1,10 +1,10 @@ //! Implements the various phases of `cargo miri run/test`. -use std::env; use std::fs::{self, File}; -use std::io::BufReader; +use std::io::{BufReader, Write}; use std::path::{Path, PathBuf}; use std::process::Command; +use std::{env, thread}; use rustc_version::VersionMeta; @@ -34,6 +34,8 @@ Examples: "; +const DEFAULT_MANY_SEEDS: &str = "0..64"; + fn show_help() { println!("{CARGO_MIRI_HELP}"); } @@ -119,7 +121,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { // describes an alternative // approach that uses `cargo check`, making that part easier but target and binary handling // harder. - let cargo_miri_path = std::env::current_exe() + let cargo_miri_path = env::current_exe() .expect("current executable path invalid") .into_os_string() .into_string() @@ -163,14 +165,22 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { let target_dir = get_target_dir(&metadata); cmd.arg("--target-dir").arg(target_dir); + // Store many-seeds argument. + let mut many_seeds = None; // *After* we set all the flags that need setting, forward everything else. Make sure to skip - // `--target-dir` (which would otherwise be set twice). + // `--target-dir` (which would otherwise be set twice) and `--many-seeds` (which is our flag, not cargo's). for arg in ArgSplitFlagValue::from_string_iter(&mut args, "--target-dir").filter_map(Result::err) { - cmd.arg(arg); + if arg == "--many-seeds" { + many_seeds = Some(DEFAULT_MANY_SEEDS.to_owned()); + } else if let Some(val) = arg.strip_prefix("--many-seeds=") { + many_seeds = Some(val.to_owned()); + } else { + cmd.arg(arg); + } } - // Forward all further arguments (not consumed by `ArgSplitFlagValue`) to cargo. + // Forward all further arguments after `--` (not consumed by `ArgSplitFlagValue`) to cargo. cmd.args(args); // Set `RUSTC_WRAPPER` to ourselves. Cargo will prepend that binary to its usual invocation, @@ -222,6 +232,9 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { // Forward some crucial information to our own re-invocations. cmd.env("MIRI_SYSROOT", miri_sysroot); cmd.env("MIRI_LOCAL_CRATES", local_crates(&metadata)); + if let Some(many_seeds) = many_seeds { + cmd.env("MIRI_MANY_SEEDS", many_seeds); + } if verbose > 0 { cmd.env("MIRI_VERBOSE", verbose.to_string()); // This makes the other phases verbose. } @@ -309,7 +322,7 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { } } - let verbose = std::env::var("MIRI_VERBOSE") + let verbose = env::var("MIRI_VERBOSE") .map_or(0, |verbose| verbose.parse().expect("verbosity flag must be an integer")); let target_crate = is_target_crate(); @@ -489,7 +502,7 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { // This is a host crate. // When we're running `cargo-miri` from `x.py` we need to pass the sysroot explicitly // due to bootstrap complications. - if let Some(sysroot) = std::env::var_os("MIRI_HOST_SYSROOT") { + if let Some(sysroot) = env::var_os("MIRI_HOST_SYSROOT") { cmd.arg("--sysroot").arg(sysroot); } @@ -532,7 +545,7 @@ pub enum RunnerPhase { } pub fn phase_runner(mut binary_args: impl Iterator, phase: RunnerPhase) { - let verbose = std::env::var("MIRI_VERBOSE") + let verbose = env::var("MIRI_VERBOSE") .map_or(0, |verbose| verbose.parse().expect("verbosity flag must be an integer")); let binary = binary_args.next().unwrap(); @@ -541,6 +554,7 @@ pub fn phase_runner(mut binary_args: impl Iterator, phase: Runner "file {:?} not found or `cargo-miri` invoked incorrectly; please only invoke this binary through `cargo miri`", binary )); let file = BufReader::new(file); + let binary_args = binary_args.collect::>(); let info = serde_json::from_reader(file).unwrap_or_else(|_| { show_error!("file {:?} contains outdated or invalid JSON; try `cargo clean`", binary) @@ -555,84 +569,114 @@ pub fn phase_runner(mut binary_args: impl Iterator, phase: Runner } }; - let mut cmd = miri(); + let many_seeds = env::var("MIRI_MANY_SEEDS"); + run_many_seeds(many_seeds.ok(), |seed| { + let mut cmd = miri(); - // Set missing env vars. We prefer build-time env vars over run-time ones; see - // for the kind of issue that fixes. - for (name, val) in info.env { - // `CARGO_MAKEFLAGS` contains information about how to reach the jobserver, but by the time - // the program is being run, that jobserver no longer exists (cargo only runs the jobserver - // for the build portion of `cargo run`/`cargo test`). Hence we shouldn't forward this. - // Also see . - if name == "CARGO_MAKEFLAGS" { - continue; - } - if let Some(old_val) = env::var_os(&name) { - if old_val == val { - // This one did not actually change, no need to re-set it. - // (This keeps the `debug_cmd` below more manageable.) + // Set missing env vars. We prefer build-time env vars over run-time ones; see + // for the kind of issue that fixes. + for (name, val) in &info.env { + // `CARGO_MAKEFLAGS` contains information about how to reach the jobserver, but by the time + // the program is being run, that jobserver no longer exists (cargo only runs the jobserver + // for the build portion of `cargo run`/`cargo test`). Hence we shouldn't forward this. + // Also see . + if name == "CARGO_MAKEFLAGS" { continue; - } else if verbose > 0 { - eprintln!( - "[cargo-miri runner] Overwriting run-time env var {name:?}={old_val:?} with build-time value {val:?}" - ); + } + if let Some(old_val) = env::var_os(name) { + if *old_val == *val { + // This one did not actually change, no need to re-set it. + // (This keeps the `debug_cmd` below more manageable.) + continue; + } else if verbose > 0 { + eprintln!( + "[cargo-miri runner] Overwriting run-time env var {name:?}={old_val:?} with build-time value {val:?}" + ); + } + } + cmd.env(name, val); + } + + if phase != RunnerPhase::Rustdoc { + // Set the sysroot. Not necessary in rustdoc, where we already set the sysroot in + // `phase_rustdoc`. rustdoc will forward that flag when invoking rustc (i.e., us), so the + // flag is present in `info.args`. + cmd.arg("--sysroot").arg(env::var_os("MIRI_SYSROOT").unwrap()); + } + // Forward rustc arguments. + // We need to patch "--extern" filenames because we forced a check-only + // build without cargo knowing about that: replace `.rlib` suffix by + // `.rmeta`. + // We also need to remove `--error-format` as cargo specifies that to be JSON, + // but when we run here, cargo does not interpret the JSON any more. `--json` + // then also needs to be dropped. + let mut args = info.args.iter(); + while let Some(arg) = args.next() { + if arg == "--extern" { + forward_patched_extern_arg(&mut (&mut args).cloned(), &mut cmd); + } else if let Some(suffix) = arg.strip_prefix("--error-format") { + assert!(suffix.starts_with('=')); + // Drop this argument. + } else if let Some(suffix) = arg.strip_prefix("--json") { + assert!(suffix.starts_with('=')); + // Drop this argument. + } else { + cmd.arg(arg); } } - cmd.env(name, val); - } - - if phase != RunnerPhase::Rustdoc { - // Set the sysroot. Not necessary in rustdoc, where we already set the sysroot in - // `phase_rustdoc`. rustdoc will forward that flag when invoking rustc (i.e., us), so the - // flag is present in `info.args`. - cmd.arg("--sysroot").arg(env::var_os("MIRI_SYSROOT").unwrap()); - } - // Forward rustc arguments. - // We need to patch "--extern" filenames because we forced a check-only - // build without cargo knowing about that: replace `.rlib` suffix by - // `.rmeta`. - // We also need to remove `--error-format` as cargo specifies that to be JSON, - // but when we run here, cargo does not interpret the JSON any more. `--json` - // then also needs to be dropped. - let mut args = info.args.into_iter(); - while let Some(arg) = args.next() { - if arg == "--extern" { - forward_patched_extern_arg(&mut args, &mut cmd); - } else if let Some(suffix) = arg.strip_prefix("--error-format") { - assert!(suffix.starts_with('=')); - // Drop this argument. - } else if let Some(suffix) = arg.strip_prefix("--json") { - assert!(suffix.starts_with('=')); - // Drop this argument. - } else { - cmd.arg(arg); + // Respect `MIRIFLAGS`. + if let Ok(a) = env::var("MIRIFLAGS") { + let args = flagsplit(&a); + cmd.args(args); + } + // Set the current seed. + if let Some(seed) = seed { + eprintln!("Trying seed: {seed}"); + cmd.arg(format!("-Zmiri-seed={seed}")); } - } - // Respect `MIRIFLAGS`. - if let Ok(a) = env::var("MIRIFLAGS") { - let args = flagsplit(&a); - cmd.args(args); - } - // Then pass binary arguments. - cmd.arg("--"); - cmd.args(binary_args); + // Then pass binary arguments. + cmd.arg("--"); + cmd.args(&binary_args); - // Make sure we use the build-time working directory for interpreting Miri/rustc arguments. - // But then we need to switch to the run-time one, which we instruct Miri to do by setting `MIRI_CWD`. - cmd.current_dir(info.current_dir); - cmd.env("MIRI_CWD", env::current_dir().unwrap()); + // Make sure we use the build-time working directory for interpreting Miri/rustc arguments. + // But then we need to switch to the run-time one, which we instruct Miri to do by setting `MIRI_CWD`. + cmd.current_dir(&info.current_dir); + cmd.env("MIRI_CWD", env::current_dir().unwrap()); - // Run it. - debug_cmd("[cargo-miri runner]", verbose, &cmd); - match phase { - RunnerPhase::Rustdoc => exec_with_pipe(cmd, &info.stdin, format!("{binary}.stdin")), - RunnerPhase::Cargo => exec(cmd), - } + // Run it. + debug_cmd("[cargo-miri runner]", verbose, &cmd); + + match phase { + RunnerPhase::Rustdoc => { + cmd.stdin(std::process::Stdio::piped()); + let mut child = cmd.spawn().expect("failed to spawn process"); + let child_stdin = child.stdin.take().unwrap(); + // Write stdin in a background thread, as it may block. + let exit_status = thread::scope(|s| { + s.spawn(|| { + let mut child_stdin = child_stdin; + // Ignore failure, it is most likely due to the process having terminated. + let _ = child_stdin.write_all(&info.stdin); + }); + child.wait().expect("failed to run command") + }); + if !exit_status.success() { + std::process::exit(exit_status.code().unwrap_or(-1)); + } + } + RunnerPhase::Cargo => { + let exit_status = cmd.status().expect("failed to run command"); + if !exit_status.success() { + std::process::exit(exit_status.code().unwrap_or(-1)); + } + } + } + }); } pub fn phase_rustdoc(mut args: impl Iterator) { - let verbose = std::env::var("MIRI_VERBOSE") + let verbose = env::var("MIRI_VERBOSE") .map_or(0, |verbose| verbose.parse().expect("verbosity flag must be an integer")); // phase_cargo_miri sets the RUSTDOC env var to ourselves, and puts a backup @@ -681,7 +725,7 @@ pub fn phase_rustdoc(mut args: impl Iterator) { cmd.arg("--cfg").arg("miri"); // Make rustdoc call us back. - let cargo_miri_path = std::env::current_exe().expect("current executable path invalid"); + let cargo_miri_path = env::current_exe().expect("current executable path invalid"); cmd.arg("--test-builder").arg(&cargo_miri_path); // invoked by forwarding most arguments cmd.arg("--runtool").arg(&cargo_miri_path); // invoked with just a single path argument diff --git a/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs index 9a58e6fa018d..fe67aad465cd 100644 --- a/src/tools/miri/cargo-miri/src/setup.rs +++ b/src/tools/miri/cargo-miri/src/setup.rs @@ -2,11 +2,10 @@ use std::env; use std::ffi::OsStr; -use std::fmt::Write; use std::path::PathBuf; use std::process::{self, Command}; -use rustc_build_sysroot::{BuildMode, SysrootBuilder, SysrootConfig}; +use rustc_build_sysroot::{BuildMode, SysrootBuilder, SysrootConfig, SysrootStatus}; use rustc_version::VersionMeta; use crate::util::*; @@ -24,6 +23,7 @@ pub fn setup( let only_setup = matches!(subcommand, MiriCommand::Setup); let ask_user = !only_setup; let print_sysroot = only_setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path + let show_setup = only_setup && !print_sysroot; if !only_setup { if let Some(sysroot) = std::env::var_os("MIRI_SYSROOT") { // Skip setup step if MIRI_SYSROOT is explicitly set, *unless* we are `cargo miri setup`. @@ -115,18 +115,16 @@ pub fn setup( // `config.toml`. command.env("RUSTC_WRAPPER", ""); - if only_setup && !print_sysroot { + if show_setup { // Forward output. Even make it verbose, if requested. + command.stdout(process::Stdio::inherit()); + command.stderr(process::Stdio::inherit()); for _ in 0..verbose { command.arg("-v"); } if quiet { command.arg("--quiet"); } - } else { - // Suppress output. - command.stdout(process::Stdio::null()); - command.stderr(process::Stdio::null()); } command @@ -137,49 +135,52 @@ pub fn setup( // not apply `RUSTFLAGS` to the sysroot either. let rustflags = &["-Cdebug-assertions=off", "-Coverflow-checks=on"]; + let mut after_build_output = String::new(); // what should be printed when the build is done. + let notify = || { + if !quiet { + eprint!("Preparing a sysroot for Miri (target: {target})"); + if verbose > 0 { + eprint!(" in {}", sysroot_dir.display()); + } + if show_setup { + // Cargo will print things, so we need to finish this line. + eprintln!("..."); + after_build_output = format!( + "A sysroot for Miri is now available in `{}`.\n", + sysroot_dir.display() + ); + } else { + // Keep all output on a single line. + eprint!("... "); + after_build_output = format!("done\n"); + } + } + }; + // Do the build. - if print_sysroot || quiet { - // Be silent. - } else { - let mut msg = String::new(); - write!(msg, "Preparing a sysroot for Miri (target: {target})").unwrap(); - if verbose > 0 { - write!(msg, " in {}", sysroot_dir.display()).unwrap(); - } - write!(msg, "...").unwrap(); - if only_setup { - // We want to be explicit. - eprintln!("{msg}"); - } else { - // We want to be quiet, but still let the user know that something is happening. - eprint!("{msg} "); - } - } - SysrootBuilder::new(&sysroot_dir, target) + let status = SysrootBuilder::new(&sysroot_dir, target) .build_mode(BuildMode::Check) .rustc_version(rustc_version.clone()) .sysroot_config(sysroot_config) .rustflags(rustflags) .cargo(cargo_cmd) - .build_from_source(&rust_src) - .unwrap_or_else(|err| { - if print_sysroot { - show_error!("failed to build sysroot") - } else if only_setup { - show_error!("failed to build sysroot: {err:?}") - } else { - show_error!( - "failed to build sysroot; run `cargo miri setup` to see the error details" - ) - } - }); - if print_sysroot || quiet { - // Be silent. - } else if only_setup { - eprintln!("A sysroot for Miri is now available in `{}`.", sysroot_dir.display()); - } else { - eprintln!("done"); + .when_build_required(notify) + .build_from_source(&rust_src); + match status { + Ok(SysrootStatus::AlreadyCached) => + if !quiet && show_setup { + eprintln!( + "A sysroot for Miri is already available in `{}`.", + sysroot_dir.display() + ); + }, + Ok(SysrootStatus::SysrootBuilt) => { + // Print what `notify` prepared. + eprint!("{after_build_output}"); + } + Err(err) => show_error!("failed to build sysroot: {err:?}"), } + if print_sysroot { // Print just the sysroot and nothing else to stdout; this way we do not need any escaping. println!("{}", sysroot_dir.display()); diff --git a/src/tools/miri/cargo-miri/src/util.rs b/src/tools/miri/cargo-miri/src/util.rs index d99957d9c220..5f2794e2244f 100644 --- a/src/tools/miri/cargo-miri/src/util.rs +++ b/src/tools/miri/cargo-miri/src/util.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::env; use std::ffi::OsString; use std::fs::File; @@ -11,14 +12,15 @@ use serde::{Deserialize, Serialize}; pub use crate::arg::*; -pub fn show_error(msg: &impl std::fmt::Display) -> ! { +pub fn show_error_(msg: &impl std::fmt::Display) -> ! { eprintln!("fatal error: {msg}"); std::process::exit(1) } macro_rules! show_error { - ($($tt:tt)*) => { crate::util::show_error(&format_args!($($tt)*)) }; + ($($tt:tt)*) => { crate::util::show_error_(&format_args!($($tt)*)) }; } +pub(crate) use show_error; /// The information to run a crate with the given environment. #[derive(Clone, Serialize, Deserialize)] @@ -169,11 +171,16 @@ where drop(path); // We don't need the path, we can pipe the bytes directly cmd.stdin(std::process::Stdio::piped()); let mut child = cmd.spawn().expect("failed to spawn process"); - { - let stdin = child.stdin.as_mut().expect("failed to open stdin"); - stdin.write_all(input).expect("failed to write out test source"); - } - let exit_status = child.wait().expect("failed to run command"); + let child_stdin = child.stdin.take().unwrap(); + // Write stdin in a background thread, as it may block. + let exit_status = std::thread::scope(|s| { + s.spawn(|| { + let mut child_stdin = child_stdin; + // Ignore failure, it is most likely due to the process having terminated. + let _ = child_stdin.write_all(input); + }); + child.wait().expect("failed to run command") + }); std::process::exit(exit_status.code().unwrap_or(-1)) } } @@ -232,21 +239,18 @@ pub fn get_cargo_metadata() -> Metadata { } /// Pulls all the crates in this workspace from the cargo metadata. -/// Workspace members are emitted like "miri 0.1.0 (path+file:///path/to/miri)" /// Additionally, somewhere between cargo metadata and TyCtxt, '-' gets replaced with '_' so we /// make that same transformation here. pub fn local_crates(metadata: &Metadata) -> String { assert!(!metadata.workspace_members.is_empty()); - let mut local_crates = String::new(); - for member in &metadata.workspace_members { - let name = member.repr.split(' ').next().unwrap(); - let name = name.replace('-', "_"); - local_crates.push_str(&name); - local_crates.push(','); - } - local_crates.pop(); // Remove the trailing ',' - - local_crates + let package_name_by_id: HashMap<_, _> = + metadata.packages.iter().map(|package| (&package.id, package.name.as_str())).collect(); + metadata + .workspace_members + .iter() + .map(|id| package_name_by_id[id].replace('-', "_")) + .collect::>() + .join(",") } /// Debug-print a command that is going to be run. @@ -269,7 +273,7 @@ pub fn get_target_dir(meta: &Metadata) -> PathBuf { output } -/// Determines where the sysroot of this exeuction is +/// Determines where the sysroot of this execution is /// /// Either in a user-specified spot by an envar, or in a default cache location. pub fn get_sysroot_dir() -> PathBuf { @@ -318,3 +322,24 @@ pub fn clean_target_dir(meta: &Metadata) { remove_dir_all_idem(&target_dir).unwrap_or_else(|err| show_error!("{}", err)) } + +/// Run `f` according to the many-seeds argument. In single-seed mode, `f` will only +/// be called once, with `None`. +pub fn run_many_seeds(many_seeds: Option, f: impl Fn(Option)) { + let Some(many_seeds) = many_seeds else { + return f(None); + }; + let (from, to) = many_seeds + .split_once("..") + .unwrap_or_else(|| show_error!("invalid format for `--many-seeds`: expected `from..to`")); + let from: u32 = if from.is_empty() { + 0 + } else { + from.parse().unwrap_or_else(|_| show_error!("invalid `from` in `--many-seeds=from..to")) + }; + let to: u32 = + to.parse().unwrap_or_else(|_| show_error!("invalid `to` in `--many-seeds=from..to")); + for seed in from..to { + f(Some(seed)); + } +} diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index f5fbb05d8962..67985f9b7d6e 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -144,16 +144,16 @@ case $HOST_TARGET in TEST_TARGET=arm-unknown-linux-gnueabi run_tests TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice # Partially supported targets (tier 2) - VERY_BASIC="integer vec string btreemap" # common things we test on all of them (if they have std), requires no target-specific shims - BASIC="$VERY_BASIC hello hashmap alloc align" # ensures we have the shims for stdout and basic data structures - TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus - TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus - TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random - TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random - TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic - TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm - TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm - TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std + BASIC="empty_main integer vec string btreemap hello hashmap heap_alloc align" # ensures we have the basics: stdout/stderr, system allocator, randomness (for HashMap initialization) + UNIX="panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there + TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname libc-time fs + TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname libc-time fs + TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX threadname pthread-sync available-parallelism libc-time + TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX threadname pthread-sync available-parallelism libc-time + TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX + TEST_TARGET=wasm32-wasip2 run_tests_minimal empty_main wasm heap_alloc libc-mem + TEST_TARGET=wasm32-unknown-unknown run_tests_minimal empty_main wasm + TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std # Custom target JSON file TEST_TARGET=tests/avr.json MIRI_NO_STD=1 run_tests_minimal no_std ;; diff --git a/src/tools/miri/miri-script/src/args.rs b/src/tools/miri/miri-script/src/args.rs new file mode 100644 index 000000000000..16a21757b353 --- /dev/null +++ b/src/tools/miri/miri-script/src/args.rs @@ -0,0 +1,136 @@ +use std::env; +use std::iter; + +use anyhow::{bail, Result}; + +pub struct Args { + args: iter::Peekable, + /// Set to `true` once we saw a `--`. + terminated: bool, +} + +impl Args { + pub fn new() -> Self { + let mut args = Args { args: env::args().peekable(), terminated: false }; + args.args.next().unwrap(); // skip program name + args + } + + /// Get the next argument without any interpretation. + pub fn next_raw(&mut self) -> Option { + self.args.next() + } + + /// Consume a `-$f` flag if present. + pub fn get_short_flag(&mut self, flag: char) -> Result { + if self.terminated { + return Ok(false); + } + if let Some(next) = self.args.peek() { + if let Some(next) = next.strip_prefix("-") { + if let Some(next) = next.strip_prefix(flag) { + if next.is_empty() { + self.args.next().unwrap(); // consume this argument + return Ok(true); + } else { + bail!("`-{flag}` followed by value"); + } + } + } + } + Ok(false) + } + + /// Consume a `--$name` flag if present. + pub fn get_long_flag(&mut self, name: &str) -> Result { + if self.terminated { + return Ok(false); + } + if let Some(next) = self.args.peek() { + if let Some(next) = next.strip_prefix("--") { + if next == name { + self.args.next().unwrap(); // consume this argument + return Ok(true); + } + } + } + Ok(false) + } + + /// Consume a `--$name val` or `--$name=val` option if present. + pub fn get_long_opt(&mut self, name: &str) -> Result> { + assert!(!name.is_empty()); + if self.terminated { + return Ok(None); + } + let Some(next) = self.args.peek() else { return Ok(None) }; + let Some(next) = next.strip_prefix("--") else { return Ok(None) }; + let Some(next) = next.strip_prefix(name) else { return Ok(None) }; + // Starts with `--flag`. + Ok(if let Some(val) = next.strip_prefix("=") { + // `--flag=val` form + let val = val.into(); + self.args.next().unwrap(); // consume this argument + Some(val) + } else if next.is_empty() { + // `--flag val` form + self.args.next().unwrap(); // consume this argument + let Some(val) = self.args.next() else { bail!("`--{name}` not followed by value") }; + Some(val) + } else { + // Some unrelated flag, like `--flag-more` or so. + None + }) + } + + /// Consume a `--$name=val` or `--$name` option if present; the latter + /// produces a default value. (`--$name val` is *not* accepted for this form + /// of argument, it understands `val` already as the next argument!) + pub fn get_long_opt_with_default( + &mut self, + name: &str, + default: &str, + ) -> Result> { + assert!(!name.is_empty()); + if self.terminated { + return Ok(None); + } + let Some(next) = self.args.peek() else { return Ok(None) }; + let Some(next) = next.strip_prefix("--") else { return Ok(None) }; + let Some(next) = next.strip_prefix(name) else { return Ok(None) }; + // Starts with `--flag`. + Ok(if let Some(val) = next.strip_prefix("=") { + // `--flag=val` form + let val = val.into(); + self.args.next().unwrap(); // consume this argument + Some(val) + } else if next.is_empty() { + // `--flag` form + self.args.next().unwrap(); // consume this argument + Some(default.into()) + } else { + // Some unrelated flag, like `--flag-more` or so. + None + }) + } + + /// Returns the next free argument or uninterpreted flag, or `None` if there are no more + /// arguments left. `--` is returned as well, but it is interpreted in the sense that no more + /// flags will be parsed after this. + pub fn get_other(&mut self) -> Option { + if self.terminated { + return self.args.next(); + } + let next = self.args.next()?; + if next == "--" { + self.terminated = true; // don't parse any more flags + // This is where our parser is special, we do yield the `--`. + } + Some(next) + } + + /// Return the rest of the aguments entirely unparsed. + pub fn remainder(self) -> Vec { + self.args.collect() + } +} diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 8e2b07ad8052..57bdfbad9afb 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -25,7 +25,11 @@ impl MiriEnv { /// Returns the location of the sysroot. /// /// If the target is None the sysroot will be built for the host machine. - fn build_miri_sysroot(&mut self, quiet: bool, target: Option<&OsStr>) -> Result { + fn build_miri_sysroot( + &mut self, + quiet: bool, + target: Option>, + ) -> Result { if let Some(miri_sysroot) = self.sh.var_os("MIRI_SYSROOT") { // Sysroot already set, use that. return Ok(miri_sysroot.into()); @@ -37,33 +41,27 @@ impl MiriEnv { self.build(path!(self.miri_dir / "Cargo.toml"), &[], quiet)?; self.build(&manifest_path, &[], quiet)?; - let target_flag = - if let Some(target) = target { vec![OsStr::new("--target"), target] } else { vec![] }; + let target_flag = if let Some(target) = &target { + vec![OsStr::new("--target"), target.as_ref()] + } else { + vec![] + }; let target_flag = &target_flag; if !quiet { - if let Some(target) = target { - eprintln!("$ (building Miri sysroot for {})", target.to_string_lossy()); - } else { - eprintln!("$ (building Miri sysroot)"); + eprint!("$ cargo miri setup"); + if let Some(target) = &target { + eprint!(" --target {target}", target = target.as_ref().to_string_lossy()); } + eprintln!(); } - let output = cmd!(self.sh, + let mut cmd = cmd!(self.sh, "cargo +{toolchain} --quiet run {cargo_extra_flags...} --manifest-path {manifest_path} -- miri setup --print-sysroot {target_flag...}" - ).read(); - let Ok(output) = output else { - // Run it again (without `--print-sysroot` or `--quiet`) so the user can see the error. - cmd!( - self.sh, - "cargo +{toolchain} run {cargo_extra_flags...} --manifest-path {manifest_path} -- - miri setup {target_flag...}" - ) - .run() - .with_context(|| "`cargo miri setup` failed")?; - panic!("`cargo miri setup` didn't fail again the 2nd time?"); - }; + ); + cmd.set_quiet(quiet); + let output = cmd.read()?; self.sh.set_var("MIRI_SYSROOT", &output); Ok(output.into()) } @@ -166,8 +164,8 @@ impl Command { Command::Build { flags } => Self::build(flags), Command::Check { flags } => Self::check(flags), Command::Test { bless, flags, target } => Self::test(bless, flags, target), - Command::Run { dep, verbose, many_seeds, flags } => - Self::run(dep, verbose, many_seeds, flags), + Command::Run { dep, verbose, many_seeds, target, edition, flags } => + Self::run(dep, verbose, many_seeds, target, edition, flags), Command::Fmt { flags } => Self::fmt(flags), Command::Clippy { flags } => Self::clippy(flags), Command::Cargo { flags } => Self::cargo(flags), @@ -178,7 +176,7 @@ impl Command { } } - fn toolchain(flags: Vec) -> Result<()> { + fn toolchain(flags: Vec) -> Result<()> { // Make sure rustup-toolchain-install-master is installed. which::which("rustup-toolchain-install-master") .context("Please install rustup-toolchain-install-master by running 'cargo install rustup-toolchain-install-master'")?; @@ -255,7 +253,7 @@ impl Command { cmd!(sh, "git fetch http://localhost:{JOSH_PORT}/rust-lang/rust.git@{commit}{JOSH_FILTER}.git") .run() .map_err(|e| { - // Try to un-do the previous `git commit`, to leave the repo in the state we found it it. + // Try to un-do the previous `git commit`, to leave the repo in the state we found it. cmd!(sh, "git reset --hard HEAD^") .run() .expect("FAILED to clean up again after failed `git fetch`, sorry for that"); @@ -373,7 +371,7 @@ impl Command { Ok(()) } - fn bench(target: Option, benches: Vec) -> Result<()> { + fn bench(target: Option, benches: Vec) -> Result<()> { // The hyperfine to use let hyperfine = env::var("HYPERFINE"); let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none"); @@ -387,14 +385,14 @@ impl Command { let sh = Shell::new()?; sh.change_dir(miri_dir()?); let benches_dir = "bench-cargo-miri"; - let benches = if benches.is_empty() { + let benches: Vec = if benches.is_empty() { sh.read_dir(benches_dir)? .into_iter() .filter(|path| path.is_dir()) .map(Into::into) .collect() } else { - benches.to_owned() + benches.into_iter().map(Into::into).collect() }; let target_flag = if let Some(target) = target { let mut flag = OsString::from("--target="); @@ -418,28 +416,28 @@ impl Command { Ok(()) } - fn install(flags: Vec) -> Result<()> { + fn install(flags: Vec) -> Result<()> { let e = MiriEnv::new()?; e.install_to_sysroot(e.miri_dir.clone(), &flags)?; e.install_to_sysroot(path!(e.miri_dir / "cargo-miri"), &flags)?; Ok(()) } - fn build(flags: Vec) -> Result<()> { + fn build(flags: Vec) -> Result<()> { let e = MiriEnv::new()?; e.build(path!(e.miri_dir / "Cargo.toml"), &flags, /* quiet */ false)?; e.build(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags, /* quiet */ false)?; Ok(()) } - fn check(flags: Vec) -> Result<()> { + fn check(flags: Vec) -> Result<()> { let e = MiriEnv::new()?; e.check(path!(e.miri_dir / "Cargo.toml"), &flags)?; e.check(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags)?; Ok(()) } - fn clippy(flags: Vec) -> Result<()> { + fn clippy(flags: Vec) -> Result<()> { let e = MiriEnv::new()?; e.clippy(path!(e.miri_dir / "Cargo.toml"), &flags)?; e.clippy(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags)?; @@ -447,7 +445,7 @@ impl Command { Ok(()) } - fn cargo(flags: Vec) -> Result<()> { + fn cargo(flags: Vec) -> Result<()> { let e = MiriEnv::new()?; let toolchain = &e.toolchain; // We carefully kept the working dir intact, so this will run cargo *on the workspace in the @@ -456,7 +454,7 @@ impl Command { Ok(()) } - fn test(bless: bool, mut flags: Vec, target: Option) -> Result<()> { + fn test(bless: bool, mut flags: Vec, target: Option) -> Result<()> { let mut e = MiriEnv::new()?; // Prepare a sysroot. @@ -484,21 +482,30 @@ impl Command { dep: bool, verbose: bool, many_seeds: Option>, - mut flags: Vec, + target: Option, + edition: Option, + flags: Vec, ) -> Result<()> { let mut e = MiriEnv::new()?; - let target = arg_flag_value(&flags, "--target"); + // More flags that we will pass before `flags` + // (because `flags` may contain `--`). + let mut early_flags = Vec::::new(); - // Scan for "--edition", set one ourselves if that flag is not present. - let have_edition = arg_flag_value(&flags, "--edition").is_some(); - if !have_edition { - flags.push("--edition=2021".into()); // keep in sync with `tests/ui.rs`.` + // Add target, edition to flags. + if let Some(target) = &target { + early_flags.push("--target".into()); + early_flags.push(target.into()); } + if verbose { + early_flags.push("--verbose".into()); + } + early_flags.push("--edition".into()); + early_flags.push(edition.as_deref().unwrap_or("2021").into()); - // Prepare a sysroot, and add it to the flags. + // Prepare a sysroot, add it to the flags. let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref())?; - flags.push("--sysroot".into()); - flags.push(miri_sysroot.into()); + early_flags.push("--sysroot".into()); + early_flags.push(miri_sysroot.into()); // Compute everything needed to run the actual command. Also add MIRIFLAGS. let miri_manifest = path!(e.miri_dir / "Cargo.toml"); @@ -524,7 +531,13 @@ impl Command { }; cmd.set_quiet(!verbose); // Add Miri flags - let cmd = cmd.args(&miri_flags).args(seed_flag).args(&flags); + let mut cmd = cmd.args(&miri_flags).args(&seed_flag).args(&early_flags).args(&flags); + // For `--dep` we also need to set the env var. + if dep { + if let Some(target) = &target { + cmd = cmd.env("MIRI_TEST_TARGET", target); + } + } // And run the thing. Ok(cmd.run()?) }; @@ -543,7 +556,7 @@ impl Command { Ok(()) } - fn fmt(flags: Vec) -> Result<()> { + fn fmt(flags: Vec) -> Result<()> { use itertools::Itertools; let e = MiriEnv::new()?; @@ -565,6 +578,6 @@ impl Command { .filter_ok(|item| item.file_type().is_file()) .map_ok(|item| item.into_path()); - e.format_files(files, &e.toolchain[..], &config_path, &flags[..]) + e.format_files(files, &e.toolchain[..], &config_path, &flags) } } diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs index a8626ceb45d7..c4f0d808d93e 100644 --- a/src/tools/miri/miri-script/src/main.rs +++ b/src/tools/miri/miri-script/src/main.rs @@ -1,10 +1,10 @@ #![allow(clippy::needless_question_mark)] +mod args; mod commands; mod util; -use std::ffi::OsString; -use std::{env, ops::Range}; +use std::ops::Range; use anyhow::{anyhow, bail, Context, Result}; @@ -16,26 +16,26 @@ pub enum Command { /// sysroot, to prevent conflicts with other toolchains. Install { /// Flags that are passed through to `cargo install`. - flags: Vec, + flags: Vec, }, /// Just build miri. Build { /// Flags that are passed through to `cargo build`. - flags: Vec, + flags: Vec, }, /// Just check miri. Check { /// Flags that are passed through to `cargo check`. - flags: Vec, + flags: Vec, }, /// Build miri, set up a sysroot and then run the test suite. Test { bless: bool, /// The cross-interpretation target. /// If none then the host is the target. - target: Option, + target: Option, /// Flags that are passed through to the test harness. - flags: Vec, + flags: Vec, }, /// Build miri, set up a sysroot and then run the driver with the given . /// (Also respects MIRIFLAGS environment variable.) @@ -43,33 +43,35 @@ pub enum Command { dep: bool, verbose: bool, many_seeds: Option>, + target: Option, + edition: Option, /// Flags that are passed through to `miri`. - flags: Vec, + flags: Vec, }, /// Format all sources and tests. Fmt { /// Flags that are passed through to `rustfmt`. - flags: Vec, + flags: Vec, }, /// Runs clippy on all sources. Clippy { /// Flags that are passed through to `cargo clippy`. - flags: Vec, + flags: Vec, }, /// Runs just `cargo ` with the Miri-specific environment variables. /// Mainly meant to be invoked by rust-analyzer. - Cargo { flags: Vec }, + Cargo { flags: Vec }, /// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed. Bench { - target: Option, + target: Option, /// List of benchmarks to run. By default all benchmarks are run. - benches: Vec, + benches: Vec, }, /// Update and activate the rustup toolchain 'miri' to the commit given in the /// `rust-version` file. /// `rustup-toolchain-install-master` must be installed for this to work. Any extra /// flags are passed to `rustup-toolchain-install-master`. - Toolchain { flags: Vec }, + Toolchain { flags: Vec }, /// Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest /// rustc commit. The fetched commit is stored in the `rust-version` file, so the /// next `./miri toolchain` will install the rustc that just got pulled. @@ -96,7 +98,7 @@ Build miri, set up a sysroot and then run the test suite. Build miri, set up a sysroot and then run the driver with the given . (Also respects MIRIFLAGS environment variable.) If `--many-seeds` is present, Miri is run many times in parallel with different seeds. -The range defaults to `0..256`. +The range defaults to `0..64`. ./miri fmt : Format all sources and tests. are passed to `rustfmt`. @@ -145,49 +147,40 @@ Pass extra flags to all cargo invocations. (Ignored by `./miri cargo`.)"#; fn main() -> Result<()> { // We are hand-rolling our own argument parser, since `clap` can't express what we need // (https://github.com/clap-rs/clap/issues/5055). - let mut args = env::args_os().peekable(); - args.next().unwrap(); // skip program name - let command = match args.next().and_then(|s| s.into_string().ok()).as_deref() { - Some("build") => Command::Build { flags: args.collect() }, - Some("check") => Command::Check { flags: args.collect() }, + let mut args = args::Args::new(); + let command = match args.next_raw().as_deref() { + Some("build") => Command::Build { flags: args.remainder() }, + Some("check") => Command::Check { flags: args.remainder() }, Some("test") => { let mut target = None; let mut bless = false; - - while let Some(arg) = args.peek().and_then(|s| s.to_str()) { - match arg { - "--bless" => bless = true, - "--target" => { - // Skip "--target" - args.next().unwrap(); - // Next argument is the target triple. - let val = args.peek().ok_or_else(|| { - anyhow!("`--target` must be followed by target triple") - })?; - target = Some(val.to_owned()); - } - // Only parse the leading flags. - _ => break, + let mut flags = Vec::new(); + loop { + if args.get_long_flag("bless")? { + bless = true; + } else if let Some(val) = args.get_long_opt("target")? { + target = Some(val); + } else if let Some(flag) = args.get_other() { + flags.push(flag); + } else { + break; } - - // Consume the flag, look at the next one. - args.next().unwrap(); } - - Command::Test { bless, flags: args.collect(), target } + Command::Test { bless, flags, target } } Some("run") => { let mut dep = false; let mut verbose = false; let mut many_seeds = None; - while let Some(arg) = args.peek().and_then(|s| s.to_str()) { - if arg == "--dep" { + let mut target = None; + let mut edition = None; + let mut flags = Vec::new(); + loop { + if args.get_long_flag("dep")? { dep = true; - } else if arg == "-v" || arg == "--verbose" { + } else if args.get_long_flag("verbose")? || args.get_short_flag('v')? { verbose = true; - } else if arg == "--many-seeds" { - many_seeds = Some(0..256); - } else if let Some(val) = arg.strip_prefix("--many-seeds=") { + } else if let Some(val) = args.get_long_opt_with_default("many-seeds", "0..64")? { let (from, to) = val.split_once("..").ok_or_else(|| { anyhow!("invalid format for `--many-seeds`: expected `from..to`") })?; @@ -198,60 +191,50 @@ fn main() -> Result<()> { }; let to: u32 = to.parse().context("invalid `to` in `--many-seeds=from..to")?; many_seeds = Some(from..to); + } else if let Some(val) = args.get_long_opt("target")? { + target = Some(val); + } else if let Some(val) = args.get_long_opt("edition")? { + edition = Some(val); + } else if let Some(flag) = args.get_other() { + flags.push(flag); } else { - break; // not for us + break; } - // Consume the flag, look at the next one. - args.next().unwrap(); } - Command::Run { dep, verbose, many_seeds, flags: args.collect() } + Command::Run { dep, verbose, many_seeds, target, edition, flags } } - Some("fmt") => Command::Fmt { flags: args.collect() }, - Some("clippy") => Command::Clippy { flags: args.collect() }, - Some("cargo") => Command::Cargo { flags: args.collect() }, - Some("install") => Command::Install { flags: args.collect() }, + Some("fmt") => Command::Fmt { flags: args.remainder() }, + Some("clippy") => Command::Clippy { flags: args.remainder() }, + Some("cargo") => Command::Cargo { flags: args.remainder() }, + Some("install") => Command::Install { flags: args.remainder() }, Some("bench") => { let mut target = None; - while let Some(arg) = args.peek().and_then(|s| s.to_str()) { - match arg { - "--target" => { - // Skip "--target" - args.next().unwrap(); - // Next argument is the target triple. - let val = args.peek().ok_or_else(|| { - anyhow!("`--target` must be followed by target triple") - })?; - target = Some(val.to_owned()); - } - // Only parse the leading flags. - _ => break, + let mut benches = Vec::new(); + loop { + if let Some(val) = args.get_long_opt("target")? { + target = Some(val); + } else if let Some(flag) = args.get_other() { + benches.push(flag); + } else { + break; } - - // Consume the flag, look at the next one. - args.next().unwrap(); } - - Command::Bench { target, benches: args.collect() } + Command::Bench { target, benches } } - Some("toolchain") => Command::Toolchain { flags: args.collect() }, + Some("toolchain") => Command::Toolchain { flags: args.remainder() }, Some("rustc-pull") => { - let commit = args.next().map(|a| a.to_string_lossy().into_owned()); - if args.next().is_some() { + let commit = args.next_raw(); + if args.next_raw().is_some() { bail!("Too many arguments for `./miri rustc-pull`"); } Command::RustcPull { commit } } Some("rustc-push") => { - let github_user = args - .next() - .ok_or_else(|| { - anyhow!("Missing first argument for `./miri rustc-push GITHUB_USER [BRANCH]`") - })? - .to_string_lossy() - .into_owned(); - let branch = - args.next().unwrap_or_else(|| "miri-sync".into()).to_string_lossy().into_owned(); - if args.next().is_some() { + let github_user = args.next_raw().ok_or_else(|| { + anyhow!("Missing first argument for `./miri rustc-push GITHUB_USER [BRANCH]`") + })?; + let branch = args.next_raw().unwrap_or_else(|| "miri-sync".into()); + if args.next_raw().is_some() { bail!("Too many arguments for `./miri rustc-push GITHUB_USER BRANCH`"); } Command::RustcPush { github_user, branch } diff --git a/src/tools/miri/miri-script/src/util.rs b/src/tools/miri/miri-script/src/util.rs index 2b5791f3ea53..e9095a45fce7 100644 --- a/src/tools/miri/miri-script/src/util.rs +++ b/src/tools/miri/miri-script/src/util.rs @@ -27,30 +27,6 @@ pub fn flagsplit(flags: &str) -> Vec { flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect() } -pub fn arg_flag_value( - args: impl IntoIterator>, - flag: &str, -) -> Option { - let mut args = args.into_iter(); - while let Some(arg) = args.next() { - let arg = arg.as_ref(); - if arg == "--" { - return None; - } - let Some(arg) = arg.to_str() else { - // Skip non-UTF-8 arguments. - continue; - }; - if arg == flag { - // Next one is the value. - return Some(args.next()?.as_ref().to_owned()); - } else if let Some(val) = arg.strip_prefix(flag).and_then(|s| s.strip_prefix("=")) { - return Some(val.to_owned().into()); - } - } - None -} - /// Some extra state we track for building Miri, such as the right RUSTFLAGS. pub struct MiriEnv { /// miri_dir is the root of the miri repository checkout we are working in. @@ -133,7 +109,7 @@ impl MiriEnv { pub fn build( &self, manifest_path: impl AsRef, - args: &[OsString], + args: &[String], quiet: bool, ) -> Result<()> { let MiriEnv { toolchain, cargo_extra_flags, .. } = self; @@ -149,21 +125,21 @@ impl MiriEnv { Ok(()) } - pub fn check(&self, manifest_path: impl AsRef, args: &[OsString]) -> Result<()> { + pub fn check(&self, manifest_path: impl AsRef, args: &[String]) -> Result<()> { let MiriEnv { toolchain, cargo_extra_flags, .. } = self; cmd!(self.sh, "cargo +{toolchain} check {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}") .run()?; Ok(()) } - pub fn clippy(&self, manifest_path: impl AsRef, args: &[OsString]) -> Result<()> { + pub fn clippy(&self, manifest_path: impl AsRef, args: &[String]) -> Result<()> { let MiriEnv { toolchain, cargo_extra_flags, .. } = self; cmd!(self.sh, "cargo +{toolchain} clippy {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}") .run()?; Ok(()) } - pub fn test(&self, manifest_path: impl AsRef, args: &[OsString]) -> Result<()> { + pub fn test(&self, manifest_path: impl AsRef, args: &[String]) -> Result<()> { let MiriEnv { toolchain, cargo_extra_flags, .. } = self; cmd!( self.sh, @@ -181,7 +157,7 @@ impl MiriEnv { files: impl Iterator>, toolchain: &str, config_path: &Path, - flags: &[OsString], + flags: &[String], ) -> anyhow::Result<()> { use itertools::Itertools; diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 3636c856d0b5..e49680ba75a5 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -ef15976387ad9c1cdceaabf469e0cf35f5852f6d +f6b4b71ef10307201b52c17b0f9dcf9557cd90ba diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index 2bbb34c9a4bf..ae95d28d3eb6 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -85,7 +85,7 @@ impl GlobalStateInner { } } - pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_, '_>) { + pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_>) { // `exposed` and `int_to_ptr_map` are cleared immediately when an allocation // is freed, so `base_addr` is the only one we have to clean up based on the GC. self.base_addr.retain(|id, _| allocs.is_live(*id)); @@ -101,8 +101,8 @@ fn align_addr(addr: u64, align: u64) -> u64 { } } -impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} +trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Returns the exposed `AllocId` that corresponds to the specified addr, // or `None` if the addr is out of bounds fn alloc_id_from_addr(&self, addr: u64) -> Option { @@ -169,12 +169,10 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { size, align, memory_kind, - ecx.get_active_thread(), + ecx.active_thread(), ) { - if let Some(clock) = clock - && let Some(data_race) = &ecx.machine.data_race - { - data_race.acquire_clock(&clock, ecx.get_active_thread()); + if let Some(clock) = clock { + ecx.acquire_clock(&clock); } reuse_addr } else { @@ -236,8 +234,8 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn expose_ptr(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> { let ecx = self.eval_context_mut(); let global_state = ecx.machine.alloc_addresses.get_mut(); @@ -259,7 +257,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } - fn ptr_from_addr_cast(&self, addr: u64) -> InterpResult<'tcx, Pointer>> { + fn ptr_from_addr_cast(&self, addr: u64) -> InterpResult<'tcx, Pointer> { trace!("Casting {:#x} to a pointer", addr); let ecx = self.eval_context_ref(); @@ -299,10 +297,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Convert a relative (tcx) pointer to a Miri pointer. fn adjust_alloc_root_pointer( &self, - ptr: Pointer, + ptr: interpret::Pointer, tag: BorTag, kind: MemoryKind, - ) -> InterpResult<'tcx, Pointer> { + ) -> InterpResult<'tcx, interpret::Pointer> { let ecx = self.eval_context_ref(); let (prov, offset) = ptr.into_parts(); // offset is relative (AllocId provenance) @@ -312,12 +310,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Add offset with the right kind of pointer-overflowing arithmetic. let dl = ecx.data_layout(); let absolute_addr = dl.overflowing_offset(base_addr, offset.bytes()).0; - Ok(Pointer::new(Provenance::Concrete { alloc_id, tag }, Size::from_bytes(absolute_addr))) + Ok(interpret::Pointer::new( + Provenance::Concrete { alloc_id, tag }, + Size::from_bytes(absolute_addr), + )) } /// When a pointer is used for a memory access, this computes where in which allocation the /// access is going. - fn ptr_get_alloc(&self, ptr: Pointer) -> Option<(AllocId, Size)> { + fn ptr_get_alloc(&self, ptr: interpret::Pointer) -> Option<(AllocId, Size)> { let ecx = self.eval_context_ref(); let (tag, addr) = ptr.into_parts(); // addr is absolute (Tag provenance) @@ -343,7 +344,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } -impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { +impl<'tcx> MiriMachine<'tcx> { pub fn free_alloc_id(&mut self, dead_id: AllocId, size: Size, align: Align, kind: MemoryKind) { let global_state = self.alloc_addresses.get_mut(); let rng = self.rng.get_mut(); @@ -369,12 +370,10 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { // `alloc_id_from_addr` any more. global_state.exposed.remove(&dead_id); // Also remember this address for future reuse. - let thread = self.threads.get_active_thread_id(); + let thread = self.threads.active_thread(); global_state.reuse.add_addr(rng, addr, size, align, kind, thread, || { if let Some(data_race) = &self.data_race { - data_race - .release_clock(thread, self.threads.active_thread_ref().current_span()) - .clone() + data_race.release_clock(&self.threads).clone() } else { VClock::default() } diff --git a/src/tools/miri/src/alloc_bytes.rs b/src/tools/miri/src/alloc_bytes.rs new file mode 100644 index 000000000000..97841a05cdeb --- /dev/null +++ b/src/tools/miri/src/alloc_bytes.rs @@ -0,0 +1,111 @@ +use std::alloc; +use std::alloc::Layout; +use std::borrow::Cow; +use std::slice; + +use rustc_middle::mir::interpret::AllocBytes; +use rustc_target::abi::{Align, Size}; + +/// Allocation bytes that explicitly handle the layout of the data they're storing. +/// This is necessary to interface with native code that accesses the program store in Miri. +#[derive(Debug)] +pub struct MiriAllocBytes { + /// Stored layout information about the allocation. + layout: alloc::Layout, + /// Pointer to the allocation contents. + /// Invariant: + /// * If `self.layout.size() == 0`, then `self.ptr` was allocated with the equivalent layout with size 1. + /// * Otherwise, `self.ptr` points to memory allocated with `self.layout`. + ptr: *mut u8, +} + +impl Clone for MiriAllocBytes { + fn clone(&self) -> Self { + let bytes: Cow<'_, [u8]> = Cow::Borrowed(self); + let align = Align::from_bytes(self.layout.align().try_into().unwrap()).unwrap(); + MiriAllocBytes::from_bytes(bytes, align) + } +} + +impl Drop for MiriAllocBytes { + fn drop(&mut self) { + // We have to reconstruct the actual layout used for allocation. + // (`Deref` relies on `size` so we can't just always set it to at least 1.) + let alloc_layout = if self.layout.size() == 0 { + Layout::from_size_align(1, self.layout.align()).unwrap() + } else { + self.layout + }; + // SAFETY: Invariant, `self.ptr` points to memory allocated with `self.layout`. + unsafe { alloc::dealloc(self.ptr, alloc_layout) } + } +} + +impl std::ops::Deref for MiriAllocBytes { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + // SAFETY: `ptr` is non-null, properly aligned, and valid for reading out `self.layout.size()`-many bytes. + // Note that due to the invariant this is true even if `self.layout.size() == 0`. + unsafe { slice::from_raw_parts(self.ptr, self.layout.size()) } + } +} + +impl std::ops::DerefMut for MiriAllocBytes { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: `ptr` is non-null, properly aligned, and valid for reading out `self.layout.size()`-many bytes. + // Note that due to the invariant this is true even if `self.layout.size() == 0`. + unsafe { slice::from_raw_parts_mut(self.ptr, self.layout.size()) } + } +} + +impl MiriAllocBytes { + /// This method factors out how a `MiriAllocBytes` object is allocated, given a specific allocation function. + /// If `size == 0` we allocate using a different `alloc_layout` with `size = 1`, to ensure each allocation has a unique address. + /// Returns `Err(alloc_layout)` if the allocation function returns a `ptr` where `ptr.is_null()`. + fn alloc_with( + size: usize, + align: usize, + alloc_fn: impl FnOnce(Layout) -> *mut u8, + ) -> Result { + let layout = Layout::from_size_align(size, align).unwrap(); + // When size is 0 we allocate 1 byte anyway, to ensure each allocation has a unique address. + let alloc_layout = + if size == 0 { Layout::from_size_align(1, align).unwrap() } else { layout }; + let ptr = alloc_fn(alloc_layout); + if ptr.is_null() { + Err(alloc_layout) + } else { + // SAFETY: All `MiriAllocBytes` invariants are fulfilled. + Ok(Self { ptr, layout }) + } + } +} + +impl AllocBytes for MiriAllocBytes { + fn from_bytes<'a>(slice: impl Into>, align: Align) -> Self { + let slice = slice.into(); + let size = slice.len(); + let align = align.bytes_usize(); + // SAFETY: `alloc_fn` will only be used with `size != 0`. + let alloc_fn = |layout| unsafe { alloc::alloc(layout) }; + let alloc_bytes = MiriAllocBytes::alloc_with(size, align, alloc_fn) + .unwrap_or_else(|layout| alloc::handle_alloc_error(layout)); + // SAFETY: `alloc_bytes.ptr` and `slice.as_ptr()` are non-null, properly aligned + // and valid for the `size`-many bytes to be copied. + unsafe { alloc_bytes.ptr.copy_from(slice.as_ptr(), size) }; + alloc_bytes + } + + fn zeroed(size: Size, align: Align) -> Option { + let size = size.bytes_usize(); + let align = align.bytes_usize(); + // SAFETY: `alloc_fn` will only be used with `size != 0`. + let alloc_fn = |layout| unsafe { alloc::alloc_zeroed(layout) }; + MiriAllocBytes::alloc_with(size, align, alloc_fn).ok() + } + + fn as_mut_ptr(&mut self) -> *mut u8 { + self.ptr + } +} diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 305e9cd8d342..829bfa7cd708 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -8,7 +8,6 @@ )] // Some "regular" crates we want to share with rustc -#[macro_use] extern crate tracing; // The rustc crates we need @@ -26,6 +25,8 @@ use std::num::NonZero; use std::path::PathBuf; use std::str::FromStr; +use tracing::debug; + use rustc_data_structures::sync::Lrc; use rustc_driver::Compilation; use rustc_hir::{self as hir, Node}; @@ -405,9 +406,12 @@ fn main() { let mut rustc_args = vec![]; let mut after_dashdash = false; - // If user has explicitly enabled/disabled isolation let mut isolation_enabled: Option = None; + + // Note that we require values to be given with `=`, not with a space. + // This matches how rustc parses `-Z`. + // However, unlike rustc we do not accept a space after `-Z`. for arg in args { if rustc_args.is_empty() { // Very first arg: binary name. diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs index 24e2a9a74bbe..c9e7e300593b 100644 --- a/src/tools/miri/src/borrow_tracker/mod.rs +++ b/src/tools/miri/src/borrow_tracker/mod.rs @@ -192,7 +192,7 @@ impl GlobalStateInner { id } - pub fn new_frame(&mut self, machine: &MiriMachine<'_, '_>) -> FrameState { + pub fn new_frame(&mut self, machine: &MiriMachine<'_>) -> FrameState { let call_id = self.next_call_id; trace!("new_frame: Assigning call ID {}", call_id); if self.tracked_call_ids.contains(&call_id) { @@ -213,7 +213,7 @@ impl GlobalStateInner { } } - pub fn root_ptr_tag(&mut self, id: AllocId, machine: &MiriMachine<'_, '_>) -> BorTag { + pub fn root_ptr_tag(&mut self, id: AllocId, machine: &MiriMachine<'_>) -> BorTag { self.root_ptr_tags.get(&id).copied().unwrap_or_else(|| { let tag = self.new_ptr(); if self.tracked_pointer_tags.contains(&tag) { @@ -229,7 +229,7 @@ impl GlobalStateInner { }) } - pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_, '_>) { + pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_>) { self.root_ptr_tags.retain(|id, _| allocs.is_live(*id)); } } @@ -261,7 +261,7 @@ impl GlobalStateInner { id: AllocId, alloc_size: Size, kind: MemoryKind, - machine: &MiriMachine<'_, '_>, + machine: &MiriMachine<'_>, ) -> AllocState { match self.borrow_tracker_method { BorrowTrackerMethod::StackedBorrows => @@ -276,13 +276,13 @@ impl GlobalStateInner { } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn retag_ptr_value( &mut self, kind: RetagKind, - val: &ImmTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { + val: &ImmTy<'tcx>, + ) -> InterpResult<'tcx, ImmTy<'tcx>> { let this = self.eval_context_mut(); let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method; match method { @@ -294,7 +294,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn retag_place_contents( &mut self, kind: RetagKind, - place: &PlaceTy<'tcx, Provenance>, + place: &PlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method; @@ -304,10 +304,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn protect_place( - &mut self, - place: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { + fn protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> { let this = self.eval_context_mut(); let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method; match method { @@ -327,7 +324,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn give_pointer_debug_name( &mut self, - ptr: Pointer>, + ptr: Pointer, nth_parent: u8, name: &str, ) -> InterpResult<'tcx> { @@ -354,7 +351,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn on_stack_pop( &self, - frame: &Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>, + frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>, ) -> InterpResult<'tcx> { let this = self.eval_context_ref(); let borrow_tracker = this.machine.borrow_tracker.as_ref().unwrap(); @@ -431,7 +428,7 @@ impl AllocState { alloc_id: AllocId, prov_extra: ProvenanceExtra, range: AllocRange, - machine: &MiriMachine<'_, 'tcx>, + machine: &MiriMachine<'tcx>, ) -> InterpResult<'tcx> { match self { AllocState::StackedBorrows(sb) => @@ -452,7 +449,7 @@ impl AllocState { alloc_id: AllocId, prov_extra: ProvenanceExtra, range: AllocRange, - machine: &MiriMachine<'_, 'tcx>, + machine: &MiriMachine<'tcx>, ) -> InterpResult<'tcx> { match self { AllocState::StackedBorrows(sb) => @@ -473,7 +470,7 @@ impl AllocState { alloc_id: AllocId, prov_extra: ProvenanceExtra, size: Size, - machine: &MiriMachine<'_, 'tcx>, + machine: &MiriMachine<'tcx>, ) -> InterpResult<'tcx> { match self { AllocState::StackedBorrows(sb) => @@ -493,7 +490,7 @@ impl AllocState { /// Tree Borrows needs to be told when a tag stops being protected. pub fn release_protector<'tcx>( &self, - machine: &MiriMachine<'_, 'tcx>, + machine: &MiriMachine<'tcx>, global: &GlobalState, tag: BorTag, alloc_id: AllocId, // diagnostics diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs index cb677b865311..87d9057cb89d 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs @@ -115,29 +115,29 @@ pub struct TagHistory { pub protected: Option<(String, SpanData)>, } -pub struct DiagnosticCxBuilder<'ecx, 'mir, 'tcx> { +pub struct DiagnosticCxBuilder<'ecx, 'tcx> { operation: Operation, - machine: &'ecx MiriMachine<'mir, 'tcx>, + machine: &'ecx MiriMachine<'tcx>, } -pub struct DiagnosticCx<'history, 'ecx, 'mir, 'tcx> { +pub struct DiagnosticCx<'history, 'ecx, 'tcx> { operation: Operation, - machine: &'ecx MiriMachine<'mir, 'tcx>, + machine: &'ecx MiriMachine<'tcx>, history: &'history mut AllocHistory, offset: Size, } -impl<'ecx, 'mir, 'tcx> DiagnosticCxBuilder<'ecx, 'mir, 'tcx> { +impl<'ecx, 'tcx> DiagnosticCxBuilder<'ecx, 'tcx> { pub fn build<'history>( self, history: &'history mut AllocHistory, offset: Size, - ) -> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> { + ) -> DiagnosticCx<'history, 'ecx, 'tcx> { DiagnosticCx { operation: self.operation, machine: self.machine, history, offset } } pub fn retag( - machine: &'ecx MiriMachine<'mir, 'tcx>, + machine: &'ecx MiriMachine<'tcx>, info: RetagInfo, new_tag: BorTag, orig_tag: ProvenanceExtra, @@ -149,17 +149,13 @@ impl<'ecx, 'mir, 'tcx> DiagnosticCxBuilder<'ecx, 'mir, 'tcx> { DiagnosticCxBuilder { machine, operation } } - pub fn read( - machine: &'ecx MiriMachine<'mir, 'tcx>, - tag: ProvenanceExtra, - range: AllocRange, - ) -> Self { + pub fn read(machine: &'ecx MiriMachine<'tcx>, tag: ProvenanceExtra, range: AllocRange) -> Self { let operation = Operation::Access(AccessOp { kind: AccessKind::Read, tag, range }); DiagnosticCxBuilder { machine, operation } } pub fn write( - machine: &'ecx MiriMachine<'mir, 'tcx>, + machine: &'ecx MiriMachine<'tcx>, tag: ProvenanceExtra, range: AllocRange, ) -> Self { @@ -167,14 +163,14 @@ impl<'ecx, 'mir, 'tcx> DiagnosticCxBuilder<'ecx, 'mir, 'tcx> { DiagnosticCxBuilder { machine, operation } } - pub fn dealloc(machine: &'ecx MiriMachine<'mir, 'tcx>, tag: ProvenanceExtra) -> Self { + pub fn dealloc(machine: &'ecx MiriMachine<'tcx>, tag: ProvenanceExtra) -> Self { let operation = Operation::Dealloc(DeallocOp { tag }); DiagnosticCxBuilder { machine, operation } } } -impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> { - pub fn unbuild(self) -> DiagnosticCxBuilder<'ecx, 'mir, 'tcx> { +impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { + pub fn unbuild(self) -> DiagnosticCxBuilder<'ecx, 'tcx> { DiagnosticCxBuilder { machine: self.machine, operation: self.operation } } } @@ -222,7 +218,7 @@ struct DeallocOp { } impl AllocHistory { - pub fn new(id: AllocId, item: Item, machine: &MiriMachine<'_, '_>) -> Self { + pub fn new(id: AllocId, item: Item, machine: &MiriMachine<'_>) -> Self { Self { id, root: (item, machine.current_span()), @@ -239,7 +235,7 @@ impl AllocHistory { } } -impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> { +impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { pub fn start_grant(&mut self, perm: Permission) { let Operation::Retag(op) = &mut self.operation else { unreachable!( diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 3da8744626d7..603733f9dc00 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -59,11 +59,7 @@ enum NewPermission { impl NewPermission { /// A key function: determine the permissions to grant at a retag for the given kind of /// reference/pointer. - fn from_ref_ty<'tcx>( - ty: Ty<'tcx>, - kind: RetagKind, - cx: &crate::MiriInterpCx<'_, 'tcx>, - ) -> Self { + fn from_ref_ty<'tcx>(ty: Ty<'tcx>, kind: RetagKind, cx: &crate::MiriInterpCx<'tcx>) -> Self { let protector = (kind == RetagKind::FnEntry).then_some(ProtectorKind::StrongProtector); match ty.kind() { ty::Ref(_, pointee, Mutability::Mut) => { @@ -130,11 +126,7 @@ impl NewPermission { } } - fn from_box_ty<'tcx>( - ty: Ty<'tcx>, - kind: RetagKind, - cx: &crate::MiriInterpCx<'_, 'tcx>, - ) -> Self { + fn from_box_ty<'tcx>(ty: Ty<'tcx>, kind: RetagKind, cx: &crate::MiriInterpCx<'tcx>) -> Self { // `ty` is not the `Box` but the field of the Box with this pointer (due to allocator handling). let pointee = ty.builtin_deref(true).unwrap(); if pointee.is_unpin(*cx.tcx, cx.param_env()) { @@ -230,7 +222,7 @@ impl<'tcx> Stack { fn item_invalidated( item: &Item, global: &GlobalStateInner, - dcx: &DiagnosticCx<'_, '_, '_, 'tcx>, + dcx: &DiagnosticCx<'_, '_, 'tcx>, cause: ItemInvalidationCause, ) -> InterpResult<'tcx> { if !global.tracked_pointer_tags.is_empty() { @@ -275,7 +267,7 @@ impl<'tcx> Stack { access: AccessKind, tag: ProvenanceExtra, global: &GlobalStateInner, - dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>, + dcx: &mut DiagnosticCx<'_, '_, 'tcx>, exposed_tags: &FxHashSet, ) -> InterpResult<'tcx> { // Two main steps: Find granting item, remove incompatible items above. @@ -362,7 +354,7 @@ impl<'tcx> Stack { &mut self, tag: ProvenanceExtra, global: &GlobalStateInner, - dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>, + dcx: &mut DiagnosticCx<'_, '_, 'tcx>, exposed_tags: &FxHashSet, ) -> InterpResult<'tcx> { // Step 1: Make a write access. @@ -387,7 +379,7 @@ impl<'tcx> Stack { new: Item, access: Option, global: &GlobalStateInner, - dcx: &mut DiagnosticCx<'_, '_, '_, 'tcx>, + dcx: &mut DiagnosticCx<'_, '_, 'tcx>, exposed_tags: &FxHashSet, ) -> InterpResult<'tcx> { dcx.start_grant(new.perm()); @@ -471,7 +463,7 @@ impl<'tcx> Stacks { perm: Permission, tag: BorTag, id: AllocId, - machine: &MiriMachine<'_, '_>, + machine: &MiriMachine<'_>, ) -> Self { let item = Item::new(tag, perm, false); let stack = Stack::new(item); @@ -487,10 +479,10 @@ impl<'tcx> Stacks { fn for_each( &mut self, range: AllocRange, - mut dcx_builder: DiagnosticCxBuilder<'_, '_, 'tcx>, + mut dcx_builder: DiagnosticCxBuilder<'_, 'tcx>, mut f: impl FnMut( &mut Stack, - &mut DiagnosticCx<'_, '_, '_, 'tcx>, + &mut DiagnosticCx<'_, '_, 'tcx>, &mut FxHashSet, ) -> InterpResult<'tcx>, ) -> InterpResult<'tcx> { @@ -510,7 +502,7 @@ impl Stacks { size: Size, state: &mut GlobalStateInner, kind: MemoryKind, - machine: &MiriMachine<'_, '_>, + machine: &MiriMachine<'_>, ) -> Self { let (base_tag, perm) = match kind { // New unique borrow. This tag is not accessible by the program, @@ -526,12 +518,12 @@ impl Stacks { } #[inline(always)] - pub fn before_memory_read<'tcx, 'mir, 'ecx>( + pub fn before_memory_read<'ecx, 'tcx>( &mut self, alloc_id: AllocId, tag: ProvenanceExtra, range: AllocRange, - machine: &'ecx MiriMachine<'mir, 'tcx>, + machine: &'ecx MiriMachine<'tcx>, ) -> InterpResult<'tcx> where 'tcx: 'ecx, @@ -539,7 +531,7 @@ impl Stacks { trace!( "read access with tag {:?}: {:?}, size {}", tag, - Pointer::new(alloc_id, range.start), + interpret::Pointer::new(alloc_id, range.start), range.size.bytes() ); let dcx = DiagnosticCxBuilder::read(machine, tag, range); @@ -555,12 +547,12 @@ impl Stacks { alloc_id: AllocId, tag: ProvenanceExtra, range: AllocRange, - machine: &MiriMachine<'_, 'tcx>, + machine: &MiriMachine<'tcx>, ) -> InterpResult<'tcx> { trace!( "write access with tag {:?}: {:?}, size {}", tag, - Pointer::new(alloc_id, range.start), + interpret::Pointer::new(alloc_id, range.start), range.size.bytes() ); let dcx = DiagnosticCxBuilder::write(machine, tag, range); @@ -576,7 +568,7 @@ impl Stacks { alloc_id: AllocId, tag: ProvenanceExtra, size: Size, - machine: &MiriMachine<'_, 'tcx>, + machine: &MiriMachine<'tcx>, ) -> InterpResult<'tcx> { trace!("deallocation with tag {:?}: {:?}, size {}", tag, alloc_id, size.bytes()); let dcx = DiagnosticCxBuilder::dealloc(machine, tag); @@ -590,15 +582,12 @@ impl Stacks { /// Retagging/reborrowing. There is some policy in here, such as which permissions /// to grant for which references, and when to add protectors. -impl<'mir: 'ecx, 'tcx: 'mir, 'ecx> EvalContextPrivExt<'mir, 'tcx, 'ecx> - for crate::MiriInterpCx<'mir, 'tcx> -{ -} -trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx, 'ecx> EvalContextPrivExt<'tcx, 'ecx> for crate::MiriInterpCx<'tcx> {} +trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { /// Returns the provenance that should be used henceforth. fn sb_reborrow( &mut self, - place: &MPlaceTy<'tcx, Provenance>, + place: &MPlaceTy<'tcx>, size: Size, new_perm: NewPermission, new_tag: BorTag, @@ -609,7 +598,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' this.check_ptr_access(place.ptr(), size, CheckInAllocMsg::InboundsTest)?; // It is crucial that this gets called on all code paths, to ensure we track tag creation. - let log_creation = |this: &MiriInterpCx<'mir, 'tcx>, + let log_creation = |this: &MiriInterpCx<'tcx>, loc: Option<(AllocId, Size, ProvenanceExtra)>| // alloc_id, base_offset, orig_tag -> InterpResult<'tcx> { let global = this.machine.borrow_tracker.as_ref().unwrap().borrow(); @@ -703,7 +692,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' new_tag, orig_tag, place.layout.ty, - Pointer::new(alloc_id, base_offset), + interpret::Pointer::new(alloc_id, base_offset), size.bytes() ); @@ -820,10 +809,10 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' fn sb_retag_place( &mut self, - place: &MPlaceTy<'tcx, Provenance>, + place: &MPlaceTy<'tcx>, new_perm: NewPermission, info: RetagInfo, // diagnostics info about this retag - ) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { let this = self.eval_context_mut(); let size = this.size_and_align_of_mplace(place)?.map(|(size, _)| size); // FIXME: If we cannot determine the size (because the unsized tail is an `extern type`), @@ -850,10 +839,10 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' /// `kind` indicates what kind of reference is being created. fn sb_retag_reference( &mut self, - val: &ImmTy<'tcx, Provenance>, + val: &ImmTy<'tcx>, new_perm: NewPermission, info: RetagInfo, // diagnostics info about this retag - ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { + ) -> InterpResult<'tcx, ImmTy<'tcx>> { let this = self.eval_context_mut(); let place = this.ref_to_mplace(val)?; let new_place = this.sb_retag_place(&place, new_perm, info)?; @@ -861,13 +850,13 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn sb_retag_ptr_value( &mut self, kind: RetagKind, - val: &ImmTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { + val: &ImmTy<'tcx>, + ) -> InterpResult<'tcx, ImmTy<'tcx>> { let this = self.eval_context_mut(); let new_perm = NewPermission::from_ref_ty(val.layout.ty, kind, this); let cause = match kind { @@ -881,7 +870,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn sb_retag_place_contents( &mut self, kind: RetagKind, - place: &PlaceTy<'tcx, Provenance>, + place: &PlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let retag_fields = this.machine.borrow_tracker.as_mut().unwrap().get_mut().retag_fields; @@ -895,18 +884,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { return visitor.visit_value(place); // The actual visitor. - struct RetagVisitor<'ecx, 'mir, 'tcx> { - ecx: &'ecx mut MiriInterpCx<'mir, 'tcx>, + struct RetagVisitor<'ecx, 'tcx> { + ecx: &'ecx mut MiriInterpCx<'tcx>, kind: RetagKind, retag_cause: RetagCause, retag_fields: RetagFields, in_field: bool, } - impl<'ecx, 'mir, 'tcx> RetagVisitor<'ecx, 'mir, 'tcx> { + impl<'ecx, 'tcx> RetagVisitor<'ecx, 'tcx> { #[inline(always)] // yes this helps in our benchmarks fn retag_ptr_inplace( &mut self, - place: &PlaceTy<'tcx, Provenance>, + place: &PlaceTy<'tcx>, new_perm: NewPermission, ) -> InterpResult<'tcx> { let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?; @@ -919,21 +908,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } } - impl<'ecx, 'mir, 'tcx> ValueVisitor<'mir, 'tcx, MiriMachine<'mir, 'tcx>> - for RetagVisitor<'ecx, 'mir, 'tcx> - { - type V = PlaceTy<'tcx, Provenance>; + impl<'ecx, 'tcx> ValueVisitor<'tcx, MiriMachine<'tcx>> for RetagVisitor<'ecx, 'tcx> { + type V = PlaceTy<'tcx>; #[inline(always)] - fn ecx(&self) -> &MiriInterpCx<'mir, 'tcx> { + fn ecx(&self) -> &MiriInterpCx<'tcx> { self.ecx } - fn visit_box( - &mut self, - box_ty: Ty<'tcx>, - place: &PlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx> { + fn visit_box(&mut self, box_ty: Ty<'tcx>, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> { // Only boxes for the global allocator get any special treatment. if box_ty.is_box_global(*self.ecx.tcx) { // Boxes get a weak protectors, since they may be deallocated. @@ -943,7 +926,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } - fn visit_value(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { + fn visit_value(&mut self, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> { // If this place is smaller than a pointer, we know that it can't contain any // pointers we need to retag, so we can stop recursion early. // This optimization is crucial for ZSTs, because they can contain way more fields @@ -997,10 +980,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// call. /// /// This is used to ensure soundness of in-place function argument/return passing. - fn sb_protect_place( - &mut self, - place: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { + fn sb_protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> { let this = self.eval_context_mut(); // Retag it. With protection! That is the entire point. diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs index 55ff09c53fed..f65f49a75ddd 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs @@ -2,6 +2,7 @@ use std::ops::Range; use rustc_data_structures::fx::FxHashSet; +use tracing::trace; use crate::borrow_tracker::{ stacked_borrows::{Item, Permission}, @@ -9,7 +10,7 @@ use crate::borrow_tracker::{ }; use crate::ProvenanceExtra; -/// Exactly what cache size we should use is a difficult tradeoff. There will always be some +/// Exactly what cache size we should use is a difficult trade-off. There will always be some /// workload which has a `BorTag` working set which exceeds the size of the cache, and ends up /// falling back to linear searches of the borrow stack very often. /// The cost of making this value too large is that the loop in `Stack::insert` which ensures the diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index b9f0b5bc17a2..8abc8530f7c4 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -390,7 +390,7 @@ struct DisplayFmtWrapper { warning_text: S, } -/// Formating of the permissions on each range. +/// Formatting of the permissions on each range. /// /// Example: /// ```rust,ignore (private type) @@ -422,7 +422,7 @@ struct DisplayFmtPermission { range_sep: S, } -/// Formating of the tree structure. +/// Formatting of the tree structure. /// /// Example: /// ```rust,ignore (private type) @@ -487,7 +487,7 @@ struct DisplayFmtAccess { meh: S, } -/// All parameters to determine how the tree is formated. +/// All parameters to determine how the tree is formatted. struct DisplayFmt { wrapper: DisplayFmtWrapper, perm: DisplayFmtPermission, diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index b5bf16d3d366..77e003ab8a78 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -35,7 +35,7 @@ impl<'tcx> Tree { size: Size, state: &mut GlobalStateInner, _kind: MemoryKind, - machine: &MiriMachine<'_, 'tcx>, + machine: &MiriMachine<'tcx>, ) -> Self { let tag = state.root_ptr_tag(id, machine); // Fresh tag for the root let span = machine.current_span(); @@ -50,13 +50,13 @@ impl<'tcx> Tree { alloc_id: AllocId, prov: ProvenanceExtra, range: AllocRange, - machine: &MiriMachine<'_, 'tcx>, + machine: &MiriMachine<'tcx>, ) -> InterpResult<'tcx> { trace!( "{} with tag {:?}: {:?}, size {}", access_kind, prov, - Pointer::new(alloc_id, range.start), + interpret::Pointer::new(alloc_id, range.start), range.size.bytes(), ); // TODO: for now we bail out on wildcard pointers. Eventually we should @@ -84,7 +84,7 @@ impl<'tcx> Tree { alloc_id: AllocId, prov: ProvenanceExtra, size: Size, - machine: &MiriMachine<'_, 'tcx>, + machine: &MiriMachine<'tcx>, ) -> InterpResult<'tcx> { // TODO: for now we bail out on wildcard pointers. Eventually we should // handle them as much as we can. @@ -109,7 +109,7 @@ impl<'tcx> Tree { /// protector. pub fn release_protector( &mut self, - machine: &MiriMachine<'_, 'tcx>, + machine: &MiriMachine<'tcx>, global: &GlobalState, tag: BorTag, alloc_id: AllocId, // diagnostics @@ -146,7 +146,7 @@ impl<'tcx> NewPermission { pointee: Ty<'tcx>, mutability: Mutability, kind: RetagKind, - cx: &crate::MiriInterpCx<'_, 'tcx>, + cx: &crate::MiriInterpCx<'tcx>, ) -> Option { let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.param_env()); let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.param_env()); @@ -170,7 +170,7 @@ impl<'tcx> NewPermission { fn from_unique_ty( ty: Ty<'tcx>, kind: RetagKind, - cx: &crate::MiriInterpCx<'_, 'tcx>, + cx: &crate::MiriInterpCx<'tcx>, zero_size: bool, ) -> Option { let pointee = ty.builtin_deref(true).unwrap(); @@ -190,15 +190,12 @@ impl<'tcx> NewPermission { /// Retagging/reborrowing. /// Policy on which permission to grant to each pointer should be left to /// the implementation of NewPermission. -impl<'mir: 'ecx, 'tcx: 'mir, 'ecx> EvalContextPrivExt<'mir, 'tcx, 'ecx> - for crate::MiriInterpCx<'mir, 'tcx> -{ -} -trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {} +trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Returns the provenance that should be used henceforth. fn tb_reborrow( &mut self, - place: &MPlaceTy<'tcx, Provenance>, // parent tag extracted from here + place: &MPlaceTy<'tcx>, // parent tag extracted from here ptr_size: Size, new_perm: NewPermission, new_tag: BorTag, @@ -210,7 +207,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' this.check_ptr_access(place.ptr(), ptr_size, CheckInAllocMsg::InboundsTest)?; // It is crucial that this gets called on all code paths, to ensure we track tag creation. - let log_creation = |this: &MiriInterpCx<'mir, 'tcx>, + let log_creation = |this: &MiriInterpCx<'tcx>, loc: Option<(AllocId, Size, ProvenanceExtra)>| // alloc_id, base_offset, orig_tag -> InterpResult<'tcx> { let global = this.machine.borrow_tracker.as_ref().unwrap().borrow(); @@ -261,7 +258,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' new_tag, orig_tag, place.layout.ty, - Pointer::new(alloc_id, base_offset), + interpret::Pointer::new(alloc_id, base_offset), ptr_size.bytes() ); @@ -330,9 +327,9 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' fn tb_retag_place( &mut self, - place: &MPlaceTy<'tcx, Provenance>, + place: &MPlaceTy<'tcx>, new_perm: NewPermission, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { let this = self.eval_context_mut(); // Determine the size of the reborrow. @@ -369,9 +366,9 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' /// Retags an individual pointer, returning the retagged version. fn tb_retag_reference( &mut self, - val: &ImmTy<'tcx, Provenance>, + val: &ImmTy<'tcx>, new_perm: NewPermission, - ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { + ) -> InterpResult<'tcx, ImmTy<'tcx>> { let this = self.eval_context_mut(); let place = this.ref_to_mplace(val)?; let new_place = this.tb_retag_place(&place, new_perm)?; @@ -379,15 +376,15 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Retag a pointer. References are passed to `from_ref_ty` and /// raw pointers are never reborrowed. fn tb_retag_ptr_value( &mut self, kind: RetagKind, - val: &ImmTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { + val: &ImmTy<'tcx>, + ) -> InterpResult<'tcx, ImmTy<'tcx>> { let this = self.eval_context_mut(); let new_perm = match val.layout.ty.kind() { &ty::Ref(_, pointee, mutability) => @@ -405,7 +402,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn tb_retag_place_contents( &mut self, kind: RetagKind, - place: &PlaceTy<'tcx, Provenance>, + place: &PlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let options = this.machine.borrow_tracker.as_mut().unwrap().get_mut(); @@ -416,17 +413,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { return visitor.visit_value(place); // The actual visitor. - struct RetagVisitor<'ecx, 'mir, 'tcx> { - ecx: &'ecx mut MiriInterpCx<'mir, 'tcx>, + struct RetagVisitor<'ecx, 'tcx> { + ecx: &'ecx mut MiriInterpCx<'tcx>, kind: RetagKind, retag_fields: RetagFields, unique_did: Option, } - impl<'ecx, 'mir, 'tcx> RetagVisitor<'ecx, 'mir, 'tcx> { + impl<'ecx, 'tcx> RetagVisitor<'ecx, 'tcx> { #[inline(always)] // yes this helps in our benchmarks fn retag_ptr_inplace( &mut self, - place: &PlaceTy<'tcx, Provenance>, + place: &PlaceTy<'tcx>, new_perm: Option, ) -> InterpResult<'tcx> { if let Some(new_perm) = new_perm { @@ -437,24 +434,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } } - impl<'ecx, 'mir, 'tcx> ValueVisitor<'mir, 'tcx, MiriMachine<'mir, 'tcx>> - for RetagVisitor<'ecx, 'mir, 'tcx> - { - type V = PlaceTy<'tcx, Provenance>; + impl<'ecx, 'tcx> ValueVisitor<'tcx, MiriMachine<'tcx>> for RetagVisitor<'ecx, 'tcx> { + type V = PlaceTy<'tcx>; #[inline(always)] - fn ecx(&self) -> &MiriInterpCx<'mir, 'tcx> { + fn ecx(&self) -> &MiriInterpCx<'tcx> { self.ecx } /// Regardless of how `Unique` is handled, Boxes are always reborrowed. /// When `Unique` is also reborrowed, then it behaves exactly like `Box` /// except for the fact that `Box` has a non-zero-sized reborrow. - fn visit_box( - &mut self, - box_ty: Ty<'tcx>, - place: &PlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx> { + fn visit_box(&mut self, box_ty: Ty<'tcx>, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> { // Only boxes for the global allocator get any special treatment. if box_ty.is_box_global(*self.ecx.tcx) { let new_perm = NewPermission::from_unique_ty( @@ -468,7 +459,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } - fn visit_value(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { + fn visit_value(&mut self, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> { // If this place is smaller than a pointer, we know that it can't contain any // pointers we need to retag, so we can stop recursion early. // This optimization is crucial for ZSTs, because they can contain way more fields @@ -531,10 +522,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// call. /// /// This is used to ensure soundness of in-place function argument/return passing. - fn tb_protect_place( - &mut self, - place: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { + fn tb_protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> { let this = self.eval_context_mut(); // Retag it. With protection! That is the entire point. @@ -586,7 +574,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// of `ptr` (with 0 representing `ptr` itself) fn tb_give_pointer_debug_name( &mut self, - ptr: Pointer>, + ptr: Pointer, nth_parent: u8, name: &str, ) -> InterpResult<'tcx> { @@ -608,9 +596,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// I.e. input is what you get from the visitor upon encountering an `adt` that is `Unique`, /// and output can be used by `retag_ptr_inplace`. fn inner_ptr_of_unique<'tcx>( - ecx: &MiriInterpCx<'_, 'tcx>, - place: &PlaceTy<'tcx, Provenance>, -) -> InterpResult<'tcx, PlaceTy<'tcx, Provenance>> { + ecx: &MiriInterpCx<'tcx>, + place: &PlaceTy<'tcx>, +) -> InterpResult<'tcx, PlaceTy<'tcx>> { // Follows the same layout as `interpret/visitor.rs:walk_value` for `Box` in // `rustc_const_eval`, just with one fewer layer. // Here we have a `Unique(NonNull(*mut), PhantomData)` diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index bec51c7cdf2e..fb3a4c8dad91 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -17,6 +17,7 @@ enum PermissionPriv { /// is relevant /// - `conflicted` is set on foreign reads, /// - `conflicted` must not be set on child writes (there is UB otherwise). + /// /// This is so that the behavior of `Reserved` adheres to the rules of `noalias`: /// - foreign-read then child-write is UB due to `conflicted`, /// - child-write then foreign-read is UB since child-write will activate and then @@ -202,7 +203,7 @@ impl Permission { Self { inner: Frozen } } - /// Default initial permission of the root of a new tre at out-of-bounds positions. + /// Default initial permission of the root of a new tree at out-of-bounds positions. /// Must *only* be used for the root, this is not in general an "initial" permission! pub fn new_disabled() -> Self { Self { inner: Disabled } @@ -339,15 +340,15 @@ pub mod diagnostics { /// This function assumes that its arguments apply to the same location /// and that they were obtained during a normal execution. It will panic otherwise. /// - all transitions involved in `self` and `err` should be increasing - /// (Reserved < Active < Frozen < Disabled); + /// (Reserved < Active < Frozen < Disabled); /// - between `self` and `err` the permission should also be increasing, - /// so all permissions inside `err` should be greater than `self.1`; + /// so all permissions inside `err` should be greater than `self.1`; /// - `Active` and `Reserved(conflicted=false)` cannot cause an error - /// due to insufficient permissions, so `err` cannot be a `ChildAccessForbidden(_)` - /// of either of them; + /// due to insufficient permissions, so `err` cannot be a `ChildAccessForbidden(_)` + /// of either of them; /// - `err` should not be `ProtectedDisabled(Disabled)`, because the protected - /// tag should not have been `Disabled` in the first place (if this occurs it means - /// we have unprotected tags that become protected) + /// tag should not have been `Disabled` in the first place (if this occurs it means + /// we have unprotected tags that become protected) pub(in super::super) fn is_relevant(&self, err: TransitionError) -> bool { // NOTE: `super::super` is the visibility of `TransitionError` assert!(self.is_possible()); diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs index 6777f41ac2d0..73717014e899 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs @@ -230,6 +230,7 @@ mod spurious_read { /// - any access to the same location /// - end of one of them being protected /// - a retag that would change their relative position + /// /// The type `TestEvent` models these kinds of events. /// /// In order to prevent `x` or `y` from losing their protector, @@ -483,7 +484,7 @@ mod spurious_read { /// that causes UB in the target but not in the source. /// This implementation simply explores the reachable space /// by all sequences of `TestEvent`. - /// This function can be instanciated with `RetX` and `RetY` + /// This function can be instantiated with `RetX` and `RetY` /// among `NoRet` or `AllowRet` to resp. forbid/allow `x`/`y` to lose their /// protector. fn distinguishable(&self, other: &Self) -> bool diff --git a/src/tools/miri/src/clock.rs b/src/tools/miri/src/clock.rs index fd0c121626b6..c9bffc449f7f 100644 --- a/src/tools/miri/src/clock.rs +++ b/src/tools/miri/src/clock.rs @@ -6,7 +6,7 @@ use std::time::{Duration, Instant as StdInstant}; /// This number is pretty random, but it has been shown to approximately cause /// some sample programs to run within an order of magnitude of real time on desktop CPUs. /// (See `tests/pass/shims/time-with-isolation*.rs`.) -const NANOSECONDS_PER_BASIC_BLOCK: u64 = 5000; +const NANOSECONDS_PER_BASIC_BLOCK: u128 = 5000; #[derive(Debug)] pub struct Instant { @@ -16,19 +16,24 @@ pub struct Instant { #[derive(Debug)] enum InstantKind { Host(StdInstant), - Virtual { nanoseconds: u64 }, + Virtual { nanoseconds: u128 }, } impl Instant { - pub fn checked_add(&self, duration: Duration) -> Option { + /// Will try to add `duration`, but if that overflows it may add less. + pub fn add_lossy(&self, duration: Duration) -> Instant { match self.kind { - InstantKind::Host(instant) => - instant.checked_add(duration).map(|i| Instant { kind: InstantKind::Host(i) }), - InstantKind::Virtual { nanoseconds } => - u128::from(nanoseconds) - .checked_add(duration.as_nanos()) - .and_then(|n| u64::try_from(n).ok()) - .map(|nanoseconds| Instant { kind: InstantKind::Virtual { nanoseconds } }), + InstantKind::Host(instant) => { + // If this overflows, try adding just 1h and assume that will not overflow. + let i = instant + .checked_add(duration) + .unwrap_or_else(|| instant.checked_add(Duration::from_secs(3600)).unwrap()); + Instant { kind: InstantKind::Host(i) } + } + InstantKind::Virtual { nanoseconds } => { + let n = nanoseconds.saturating_add(duration.as_nanos()); + Instant { kind: InstantKind::Virtual { nanoseconds: n } } + } } } @@ -39,7 +44,17 @@ impl Instant { ( InstantKind::Virtual { nanoseconds }, InstantKind::Virtual { nanoseconds: earlier }, - ) => Duration::from_nanos(nanoseconds.saturating_sub(earlier)), + ) => { + let duration = nanoseconds.saturating_sub(earlier); + // `Duration` does not provide a nice constructor from a `u128` of nanoseconds, + // so we have to implement this ourselves. + // It is possible for second to overflow because u64::MAX < (u128::MAX / 1e9). + // It will be saturated to u64::MAX seconds if the value after division exceeds u64::MAX. + let seconds = u64::try_from(duration / 1_000_000_000).unwrap_or(u64::MAX); + // It is impossible for nanosecond to overflow because u32::MAX > 1e9. + let nanosecond = u32::try_from(duration.wrapping_rem(1_000_000_000)).unwrap(); + Duration::new(seconds, nanosecond) + } _ => panic!("all `Instant` must be of the same kind"), } } @@ -54,12 +69,13 @@ pub struct Clock { #[derive(Debug)] enum ClockKind { Host { - /// The "time anchor" for this machine's monotone clock. - time_anchor: StdInstant, + /// The "epoch" for this machine's monotone clock: + /// the moment we consider to be time = 0. + epoch: StdInstant, }, Virtual { /// The "current virtual time". - nanoseconds: Cell, + nanoseconds: Cell, }, } @@ -67,7 +83,7 @@ impl Clock { /// Create a new clock based on the availability of communication with the host. pub fn new(communicate: bool) -> Self { let kind = if communicate { - ClockKind::Host { time_anchor: StdInstant::now() } + ClockKind::Host { epoch: StdInstant::now() } } else { ClockKind::Virtual { nanoseconds: 0.into() } }; @@ -93,16 +109,19 @@ impl Clock { ClockKind::Host { .. } => std::thread::sleep(duration), ClockKind::Virtual { nanoseconds } => { // Just pretend that we have slept for some time. - let nanos: u64 = duration.as_nanos().try_into().unwrap(); - nanoseconds.update(|x| x + nanos); + let nanos: u128 = duration.as_nanos(); + nanoseconds.update(|x| { + x.checked_add(nanos) + .expect("Miri's virtual clock cannot represent an execution this long") + }); } } } - /// Return the `anchor` instant, to convert between monotone instants and durations relative to the anchor. - pub fn anchor(&self) -> Instant { + /// Return the `epoch` instant (time = 0), to convert between monotone instants and absolute durations. + pub fn epoch(&self) -> Instant { match &self.kind { - ClockKind::Host { time_anchor } => Instant { kind: InstantKind::Host(*time_anchor) }, + ClockKind::Host { epoch } => Instant { kind: InstantKind::Host(*epoch) }, ClockKind::Virtual { .. } => Instant { kind: InstantKind::Virtual { nanoseconds: 0 } }, } } diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index f2bec972b18b..2baa09bec168 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -47,7 +47,7 @@ use std::{ }; use rustc_ast::Mutability; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashSet; use rustc_index::{Idx, IndexVec}; use rustc_middle::{mir, ty::Ty}; use rustc_span::Span; @@ -601,14 +601,14 @@ impl MemoryCellClocks { } /// Evaluation context extensions. -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { /// Perform an atomic read operation at the memory location. fn read_scalar_atomic( &self, - place: &MPlaceTy<'tcx, Provenance>, + place: &MPlaceTy<'tcx>, atomic: AtomicReadOrd, - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_ref(); this.atomic_access_check(place, AtomicAccessType::Load(atomic))?; // This will read from the last store in the modification order of this location. In case @@ -625,8 +625,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { /// Perform an atomic write operation at the memory location. fn write_scalar_atomic( &mut self, - val: Scalar, - dest: &MPlaceTy<'tcx, Provenance>, + val: Scalar, + dest: &MPlaceTy<'tcx>, atomic: AtomicWriteOrd, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -645,20 +645,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { /// Perform an atomic RMW operation on a memory location. fn atomic_rmw_op_immediate( &mut self, - place: &MPlaceTy<'tcx, Provenance>, - rhs: &ImmTy<'tcx, Provenance>, + place: &MPlaceTy<'tcx>, + rhs: &ImmTy<'tcx>, op: mir::BinOp, - neg: bool, + not: bool, atomic: AtomicRwOrd, - ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { + ) -> InterpResult<'tcx, ImmTy<'tcx>> { let this = self.eval_context_mut(); this.atomic_access_check(place, AtomicAccessType::Rmw)?; let old = this.allow_data_races_mut(|this| this.read_immediate(place))?; - // Atomics wrap around on overflow. - let val = this.wrapping_binary_op(op, &old, rhs)?; - let val = if neg { this.wrapping_unary_op(mir::UnOp::Not, &val)? } else { val }; + let val = this.binary_op(op, &old, rhs)?; + let val = if not { this.unary_op(mir::UnOp::Not, &val)? } else { val }; this.allow_data_races_mut(|this| this.write_immediate(*val, place))?; this.validate_atomic_rmw(place, atomic)?; @@ -671,10 +670,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { /// scalar value, the old value is returned. fn atomic_exchange_scalar( &mut self, - place: &MPlaceTy<'tcx, Provenance>, - new: Scalar, + place: &MPlaceTy<'tcx>, + new: Scalar, atomic: AtomicRwOrd, - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.atomic_access_check(place, AtomicAccessType::Rmw)?; @@ -691,16 +690,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { /// scalar value, the old value is returned. fn atomic_min_max_scalar( &mut self, - place: &MPlaceTy<'tcx, Provenance>, - rhs: ImmTy<'tcx, Provenance>, + place: &MPlaceTy<'tcx>, + rhs: ImmTy<'tcx>, min: bool, atomic: AtomicRwOrd, - ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { + ) -> InterpResult<'tcx, ImmTy<'tcx>> { let this = self.eval_context_mut(); this.atomic_access_check(place, AtomicAccessType::Rmw)?; let old = this.allow_data_races_mut(|this| this.read_immediate(place))?; - let lt = this.wrapping_binary_op(mir::BinOp::Lt, &old, &rhs)?.to_scalar().to_bool()?; + let lt = this.binary_op(mir::BinOp::Lt, &old, &rhs)?.to_scalar().to_bool()?; #[rustfmt::skip] // rustfmt makes this unreadable let new_val = if min { @@ -727,9 +726,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { /// identical. fn atomic_compare_exchange_scalar( &mut self, - place: &MPlaceTy<'tcx, Provenance>, - expect_old: &ImmTy<'tcx, Provenance>, - new: Scalar, + place: &MPlaceTy<'tcx>, + expect_old: &ImmTy<'tcx>, + new: Scalar, success: AtomicRwOrd, fail: AtomicReadOrd, can_fail_spuriously: bool, @@ -744,7 +743,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { // Read as immediate for the sake of `binary_op()` let old = this.allow_data_races_mut(|this| this.read_immediate(place))?; // `binary_op` will bail if either of them is not a scalar. - let eq = this.wrapping_binary_op(mir::BinOp::Eq, &old, expect_old)?; + let eq = this.binary_op(mir::BinOp::Eq, &old, expect_old)?; // If the operation would succeed, but is "weak", fail some portion // of the time, based on `success_rate`. let success_rate = 1.0 - this.machine.cmpxchg_weak_failure_rate; @@ -823,6 +822,26 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { assert!(!old, "cannot nest allow_data_races"); } } + + /// Returns the `release` clock of the current thread. + /// Other threads can acquire this clock in the future to establish synchronization + /// with this program point. + fn release_clock<'a>(&'a self) -> Option> + where + 'tcx: 'a, + { + let this = self.eval_context_ref(); + Some(this.machine.data_race.as_ref()?.release_clock(&this.machine.threads)) + } + + /// Acquire the given clock into the current thread, establishing synchronization with + /// the moment when that clock snapshot was taken via `release_clock`. + fn acquire_clock(&self, clock: &VClock) { + let this = self.eval_context_ref(); + if let Some(data_race) = &this.machine.data_race { + data_race.acquire_clock(clock, &this.machine.threads); + } + } } /// Vector clock metadata for a logical memory allocation. @@ -842,7 +861,7 @@ impl VClockAlloc { /// Create a new data-race detector for newly allocated memory. pub fn new_allocation( global: &GlobalState, - thread_mgr: &ThreadManager<'_, '_>, + thread_mgr: &ThreadManager<'_>, len: Size, kind: MemoryKind, current_span: Span, @@ -925,11 +944,11 @@ impl VClockAlloc { #[inline(never)] fn report_data_race<'tcx>( global: &GlobalState, - thread_mgr: &ThreadManager<'_, '_>, + thread_mgr: &ThreadManager<'_>, mem_clocks: &MemoryCellClocks, access: AccessType, access_size: Size, - ptr_dbg: Pointer, + ptr_dbg: interpret::Pointer, ty: Option>, ) -> InterpResult<'tcx> { let (active_index, active_clocks) = global.active_thread_state(thread_mgr); @@ -1023,7 +1042,7 @@ impl VClockAlloc { access_range: AllocRange, read_type: NaReadType, ty: Option>, - machine: &MiriMachine<'_, '_>, + machine: &MiriMachine<'_>, ) -> InterpResult<'tcx> { let current_span = machine.current_span(); let global = machine.data_race.as_ref().unwrap(); @@ -1044,7 +1063,7 @@ impl VClockAlloc { mem_clocks, AccessType::NaRead(read_type), access_range.size, - Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)), + interpret::Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)), ty, ); } @@ -1066,7 +1085,7 @@ impl VClockAlloc { access_range: AllocRange, write_type: NaWriteType, ty: Option>, - machine: &mut MiriMachine<'_, '_>, + machine: &mut MiriMachine<'_>, ) -> InterpResult<'tcx> { let current_span = machine.current_span(); let global = machine.data_race.as_mut().unwrap(); @@ -1089,7 +1108,7 @@ impl VClockAlloc { mem_clocks, AccessType::NaWrite(write_type), access_range.size, - Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)), + interpret::Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)), ty, ); } @@ -1101,17 +1120,17 @@ impl VClockAlloc { } } -impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {} -trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextPrivExt<'tcx> for MiriInterpCx<'tcx> {} +trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { /// Temporarily allow data-races to occur. This should only be used in /// one of these cases: /// - One of the appropriate `validate_atomic` functions will be called to - /// to treat a memory access as atomic. + /// treat a memory access as atomic. /// - The memory being accessed should be treated as internal state, that - /// cannot be accessed by the interpreted program. + /// cannot be accessed by the interpreted program. /// - Execution of the interpreted program execution has halted. #[inline] - fn allow_data_races_ref(&self, op: impl FnOnce(&MiriInterpCx<'mir, 'tcx>) -> R) -> R { + fn allow_data_races_ref(&self, op: impl FnOnce(&MiriInterpCx<'tcx>) -> R) -> R { let this = self.eval_context_ref(); if let Some(data_race) = &this.machine.data_race { let old = data_race.ongoing_action_data_race_free.replace(true); @@ -1128,10 +1147,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { /// so should only be used for atomic operations or internal state that the program cannot /// access. #[inline] - fn allow_data_races_mut( - &mut self, - op: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> R, - ) -> R { + fn allow_data_races_mut(&mut self, op: impl FnOnce(&mut MiriInterpCx<'tcx>) -> R) -> R { let this = self.eval_context_mut(); if let Some(data_race) = &this.machine.data_race { let old = data_race.ongoing_action_data_race_free.replace(true); @@ -1147,7 +1163,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { /// Checks that an atomic access is legal at the given place. fn atomic_access_check( &self, - place: &MPlaceTy<'tcx, Provenance>, + place: &MPlaceTy<'tcx>, access_type: AtomicAccessType, ) -> InterpResult<'tcx> { let this = self.eval_context_ref(); @@ -1203,7 +1219,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { /// associated memory-place and on the current thread. fn validate_atomic_load( &self, - place: &MPlaceTy<'tcx, Provenance>, + place: &MPlaceTy<'tcx>, atomic: AtomicReadOrd, ) -> InterpResult<'tcx> { let this = self.eval_context_ref(); @@ -1225,7 +1241,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { /// associated memory-place and on the current thread. fn validate_atomic_store( &mut self, - place: &MPlaceTy<'tcx, Provenance>, + place: &MPlaceTy<'tcx>, atomic: AtomicWriteOrd, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -1247,7 +1263,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { /// at the associated memory place and on the current thread. fn validate_atomic_rmw( &mut self, - place: &MPlaceTy<'tcx, Provenance>, + place: &MPlaceTy<'tcx>, atomic: AtomicRwOrd, ) -> InterpResult<'tcx> { use AtomicRwOrd::*; @@ -1276,7 +1292,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { /// Generic atomic operation implementation fn validate_atomic_op( &self, - place: &MPlaceTy<'tcx, Provenance>, + place: &MPlaceTy<'tcx>, atomic: A, access: AccessType, mut op: impl FnMut( @@ -1321,7 +1337,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { mem_clocks, access, place.layout.size, - Pointer::new( + interpret::Pointer::new( alloc_id, Size::from_bytes(mem_clocks_range.start), ), @@ -1413,13 +1429,6 @@ pub struct GlobalState { /// active vector-clocks catch up with the threads timestamp. reuse_candidates: RefCell>, - /// This contains threads that have terminated, but not yet joined - /// and so cannot become re-use candidates until a join operation - /// occurs. - /// The associated vector index will be moved into re-use candidates - /// after the join operation occurs. - terminated_threads: RefCell>, - /// The timestamp of last SC fence performed by each thread last_sc_fence: RefCell, @@ -1447,7 +1456,6 @@ impl GlobalState { vector_info: RefCell::new(IndexVec::new()), thread_info: RefCell::new(IndexVec::new()), reuse_candidates: RefCell::new(FxHashSet::default()), - terminated_threads: RefCell::new(FxHashMap::default()), last_sc_fence: RefCell::new(VClock::default()), last_sc_write: RefCell::new(VClock::default()), track_outdated_loads: config.track_outdated_loads, @@ -1481,8 +1489,6 @@ impl GlobalState { fn find_vector_index_reuse_candidate(&self) -> Option { let mut reuse = self.reuse_candidates.borrow_mut(); let vector_clocks = self.vector_clocks.borrow(); - let vector_info = self.vector_info.borrow(); - let terminated_threads = self.terminated_threads.borrow(); for &candidate in reuse.iter() { let target_timestamp = vector_clocks[candidate].clock[candidate]; if vector_clocks.iter_enumerated().all(|(clock_idx, clock)| { @@ -1492,9 +1498,7 @@ impl GlobalState { // The vector represents a thread that has terminated and hence cannot // report a data-race with the candidate index. - let thread_id = vector_info[clock_idx]; - let vector_terminated = - reuse.contains(&clock_idx) || terminated_threads.contains_key(&thread_id); + let vector_terminated = reuse.contains(&clock_idx); // The vector index cannot report a race with the candidate index // and hence allows the candidate index to be re-used. @@ -1516,7 +1520,7 @@ impl GlobalState { #[inline] pub fn thread_created( &mut self, - thread_mgr: &ThreadManager<'_, '_>, + thread_mgr: &ThreadManager<'_>, thread: ThreadId, current_span: Span, ) { @@ -1584,55 +1588,38 @@ impl GlobalState { /// thread (the joinee, the thread that someone waited on) and the current thread (the joiner, /// the thread who was waiting). #[inline] - pub fn thread_joined( - &mut self, - thread_mgr: &ThreadManager<'_, '_>, - joiner: ThreadId, - joinee: ThreadId, - ) { - let clocks_vec = self.vector_clocks.get_mut(); - let thread_info = self.thread_info.get_mut(); - - // Load the vector clock of the current thread. - let current_index = thread_info[joiner] - .vector_index - .expect("Performed thread join on thread with no assigned vector"); - let current = &mut clocks_vec[current_index]; + pub fn thread_joined(&mut self, threads: &ThreadManager<'_>, joinee: ThreadId) { + let thread_info = self.thread_info.borrow(); + let thread_info = &thread_info[joinee]; // Load the associated vector clock for the terminated thread. - let join_clock = thread_info[joinee] + let join_clock = thread_info .termination_vector_clock .as_ref() - .expect("Joined with thread but thread has not terminated"); - - // The join thread happens-before the current thread - // so update the current vector clock. - // Is not a release operation so the clock is not incremented. - current.clock.join(join_clock); + .expect("joined with thread but thread has not terminated"); + // Acquire that into the current thread. + self.acquire_clock(join_clock, threads); // Check the number of live threads, if the value is 1 // then test for potentially disabling multi-threaded execution. - if thread_mgr.get_live_thread_count() == 1 { - // May potentially be able to disable multi-threaded execution. - let current_clock = &clocks_vec[current_index]; - if clocks_vec - .iter_enumerated() - .all(|(idx, clocks)| clocks.clock[idx] <= current_clock.clock[idx]) - { - // All thread terminations happen-before the current clock - // therefore no data-races can be reported until a new thread - // is created, so disable multi-threaded execution. - self.multi_threaded.set(false); + // This has to happen after `acquire_clock`, otherwise there'll always + // be some thread that has not synchronized yet. + if let Some(current_index) = thread_info.vector_index { + if threads.get_live_thread_count() == 1 { + let vector_clocks = self.vector_clocks.get_mut(); + // May potentially be able to disable multi-threaded execution. + let current_clock = &vector_clocks[current_index]; + if vector_clocks + .iter_enumerated() + .all(|(idx, clocks)| clocks.clock[idx] <= current_clock.clock[idx]) + { + // All thread terminations happen-before the current clock + // therefore no data-races can be reported until a new thread + // is created, so disable multi-threaded execution. + self.multi_threaded.set(false); + } } } - - // If the thread is marked as terminated but not joined - // then move the thread to the re-use set. - let termination = self.terminated_threads.get_mut(); - if let Some(index) = termination.remove(&joinee) { - let reuse = self.reuse_candidates.get_mut(); - reuse.insert(index); - } } /// On thread termination, the vector-clock may re-used @@ -1643,29 +1630,18 @@ impl GlobalState { /// This should be called strictly before any calls to /// `thread_joined`. #[inline] - pub fn thread_terminated(&mut self, thread_mgr: &ThreadManager<'_, '_>, current_span: Span) { + pub fn thread_terminated(&mut self, thread_mgr: &ThreadManager<'_>) { + let current_thread = thread_mgr.active_thread(); let current_index = self.active_thread_index(thread_mgr); - // Increment the clock to a unique termination timestamp. - let vector_clocks = self.vector_clocks.get_mut(); - let current_clocks = &mut vector_clocks[current_index]; - current_clocks.increment_clock(current_index, current_span); + // Store the terminaion clock. + let terminaion_clock = self.release_clock(thread_mgr).clone(); + self.thread_info.get_mut()[current_thread].termination_vector_clock = + Some(terminaion_clock); - // Load the current thread id for the executing vector. - let vector_info = self.vector_info.get_mut(); - let current_thread = vector_info[current_index]; - - // Load the current thread metadata, and move to a terminated - // vector state. Setting up the vector clock all join operations - // will use. - let thread_info = self.thread_info.get_mut(); - let current = &mut thread_info[current_thread]; - current.termination_vector_clock = Some(current_clocks.clock.clone()); - - // Add this thread as a candidate for re-use after a thread join - // occurs. - let termination = self.terminated_threads.get_mut(); - termination.insert(current_thread, current_index); + // Add this thread's clock index as a candidate for re-use. + let reuse = self.reuse_candidates.get_mut(); + reuse.insert(current_index); } /// Attempt to perform a synchronized operation, this @@ -1677,7 +1653,7 @@ impl GlobalState { /// operation may create. fn maybe_perform_sync_operation<'tcx>( &self, - thread_mgr: &ThreadManager<'_, '_>, + thread_mgr: &ThreadManager<'_>, current_span: Span, op: impl FnOnce(VectorIdx, RefMut<'_, ThreadClockSet>) -> InterpResult<'tcx, bool>, ) -> InterpResult<'tcx> { @@ -1693,33 +1669,32 @@ impl GlobalState { /// Internal utility to identify a thread stored internally /// returns the id and the name for better diagnostics. - fn print_thread_metadata( - &self, - thread_mgr: &ThreadManager<'_, '_>, - vector: VectorIdx, - ) -> String { + fn print_thread_metadata(&self, thread_mgr: &ThreadManager<'_>, vector: VectorIdx) -> String { let thread = self.vector_info.borrow()[vector]; let thread_name = thread_mgr.get_thread_display_name(thread); format!("thread `{thread_name}`") } - /// Acquire the given clock into the given thread, establishing synchronization with + /// Acquire the given clock into the current thread, establishing synchronization with /// the moment when that clock snapshot was taken via `release_clock`. /// As this is an acquire operation, the thread timestamp is not /// incremented. - pub fn acquire_clock(&self, lock: &VClock, thread: ThreadId) { + pub fn acquire_clock<'tcx>(&self, clock: &VClock, threads: &ThreadManager<'tcx>) { + let thread = threads.active_thread(); let (_, mut clocks) = self.thread_state_mut(thread); - clocks.clock.join(lock); + clocks.clock.join(clock); } - /// Returns the `release` clock of the given thread. + /// Returns the `release` clock of the current thread. /// Other threads can acquire this clock in the future to establish synchronization /// with this program point. - pub fn release_clock(&self, thread: ThreadId, current_span: Span) -> Ref<'_, VClock> { + pub fn release_clock<'tcx>(&self, threads: &ThreadManager<'tcx>) -> Ref<'_, VClock> { + let thread = threads.active_thread(); + let span = threads.active_thread_ref().current_span(); // We increment the clock each time this happens, to ensure no two releases // can be confused with each other. let (index, mut clocks) = self.thread_state_mut(thread); - clocks.increment_clock(index, current_span); + clocks.increment_clock(index, span); drop(clocks); // To return a read-only view, we need to release the RefCell // and borrow it again. @@ -1756,9 +1731,9 @@ impl GlobalState { #[inline] pub(super) fn active_thread_state( &self, - thread_mgr: &ThreadManager<'_, '_>, + thread_mgr: &ThreadManager<'_>, ) -> (VectorIdx, Ref<'_, ThreadClockSet>) { - self.thread_state(thread_mgr.get_active_thread_id()) + self.thread_state(thread_mgr.active_thread()) } /// Load the current vector clock in use and the current set of thread clocks @@ -1766,27 +1741,27 @@ impl GlobalState { #[inline] pub(super) fn active_thread_state_mut( &self, - thread_mgr: &ThreadManager<'_, '_>, + thread_mgr: &ThreadManager<'_>, ) -> (VectorIdx, RefMut<'_, ThreadClockSet>) { - self.thread_state_mut(thread_mgr.get_active_thread_id()) + self.thread_state_mut(thread_mgr.active_thread()) } /// Return the current thread, should be the same /// as the data-race active thread. #[inline] - fn active_thread_index(&self, thread_mgr: &ThreadManager<'_, '_>) -> VectorIdx { - let active_thread_id = thread_mgr.get_active_thread_id(); + fn active_thread_index(&self, thread_mgr: &ThreadManager<'_>) -> VectorIdx { + let active_thread_id = thread_mgr.active_thread(); self.thread_index(active_thread_id) } // SC ATOMIC STORE rule in the paper. - pub(super) fn sc_write(&self, thread_mgr: &ThreadManager<'_, '_>) { + pub(super) fn sc_write(&self, thread_mgr: &ThreadManager<'_>) { let (index, clocks) = self.active_thread_state(thread_mgr); self.last_sc_write.borrow_mut().set_at_index(&clocks.clock, index); } // SC ATOMIC READ rule in the paper. - pub(super) fn sc_read(&self, thread_mgr: &ThreadManager<'_, '_>) { + pub(super) fn sc_read(&self, thread_mgr: &ThreadManager<'_>) { let (.., mut clocks) = self.active_thread_state_mut(thread_mgr); clocks.read_seqcst.join(&self.last_sc_fence.borrow()); } diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs index a01b59c9165b..9145ef32c525 100644 --- a/src/tools/miri/src/concurrency/init_once.rs +++ b/src/tools/miri/src/concurrency/init_once.rs @@ -4,28 +4,10 @@ use rustc_index::Idx; use rustc_middle::ty::layout::TyAndLayout; use super::sync::EvalContextExtPriv as _; -use super::thread::MachineCallback; use super::vector_clock::VClock; use crate::*; -declare_id!(InitOnceId); - -/// A thread waiting on an InitOnce object. -struct InitOnceWaiter<'mir, 'tcx> { - /// The thread that is waiting. - thread: ThreadId, - /// The callback that should be executed, after the thread has been woken up. - callback: Box + 'tcx>, -} - -impl<'mir, 'tcx> std::fmt::Debug for InitOnceWaiter<'mir, 'tcx> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("InitOnce") - .field("thread", &self.thread) - .field("callback", &"dyn MachineCallback") - .finish() - } -} +super::sync::declare_id!(InitOnceId); #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] /// The current status of a one time initialization. @@ -38,60 +20,38 @@ pub enum InitOnceStatus { /// The one time initialization state. #[derive(Default, Debug)] -pub(super) struct InitOnce<'mir, 'tcx> { +pub(super) struct InitOnce { status: InitOnceStatus, - waiters: VecDeque>, + waiters: VecDeque, clock: VClock, } -impl<'mir, 'tcx> VisitProvenance for InitOnce<'mir, 'tcx> { - fn visit_provenance(&self, visit: &mut VisitWith<'_>) { - for waiter in self.waiters.iter() { - waiter.callback.visit_provenance(visit); +impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} +trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Provides the closure with the next InitOnceId. Creates that InitOnce if the closure returns None, + /// otherwise returns the value from the closure. + #[inline] + fn init_once_get_or_create(&mut self, existing: F) -> InterpResult<'tcx, InitOnceId> + where + F: FnOnce(&mut MiriInterpCx<'tcx>, InitOnceId) -> InterpResult<'tcx, Option>, + { + let this = self.eval_context_mut(); + let next_index = this.machine.sync.init_onces.next_index(); + if let Some(old) = existing(this, next_index)? { + Ok(old) + } else { + let new_index = this.machine.sync.init_onces.push(Default::default()); + assert_eq!(next_index, new_index); + Ok(new_index) } } } -impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - /// Synchronize with the previous initialization attempt of an InitOnce. - #[inline] - fn init_once_observe_attempt(&mut self, id: InitOnceId) { - let this = self.eval_context_mut(); - let current_thread = this.get_active_thread(); - - if let Some(data_race) = &this.machine.data_race { - data_race - .acquire_clock(&this.machine.threads.sync.init_onces[id].clock, current_thread); - } - } - - #[inline] - fn init_once_wake_waiter( - &mut self, - id: InitOnceId, - waiter: InitOnceWaiter<'mir, 'tcx>, - ) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - let current_thread = this.get_active_thread(); - - this.unblock_thread(waiter.thread, BlockReason::InitOnce(id)); - - // Call callback, with the woken-up thread as `current`. - this.set_active_thread(waiter.thread); - this.init_once_observe_attempt(id); - waiter.callback.call(this)?; - this.set_active_thread(current_thread); - - Ok(()) - } -} - -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn init_once_get_or_create_id( &mut self, - lock_op: &OpTy<'tcx, Provenance>, + lock_op: &OpTy<'tcx>, lock_layout: TyAndLayout<'tcx>, offset: u64, ) -> InterpResult<'tcx, InitOnceId> { @@ -101,31 +61,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }) } - /// Provides the closure with the next InitOnceId. Creates that InitOnce if the closure returns None, - /// otherwise returns the value from the closure. - #[inline] - fn init_once_get_or_create(&mut self, existing: F) -> InterpResult<'tcx, InitOnceId> - where - F: FnOnce( - &mut MiriInterpCx<'mir, 'tcx>, - InitOnceId, - ) -> InterpResult<'tcx, Option>, - { - let this = self.eval_context_mut(); - let next_index = this.machine.threads.sync.init_onces.next_index(); - if let Some(old) = existing(this, next_index)? { - Ok(old) - } else { - let new_index = this.machine.threads.sync.init_onces.push(Default::default()); - assert_eq!(next_index, new_index); - Ok(new_index) - } - } - #[inline] fn init_once_status(&mut self, id: InitOnceId) -> InitOnceStatus { let this = self.eval_context_ref(); - this.machine.threads.sync.init_onces[id].status + this.machine.sync.init_onces[id].status } /// Put the thread into the queue waiting for the initialization. @@ -133,14 +72,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn init_once_enqueue_and_block( &mut self, id: InitOnceId, - thread: ThreadId, - callback: Box + 'tcx>, + callback: impl UnblockCallback<'tcx> + 'tcx, ) { let this = self.eval_context_mut(); - let init_once = &mut this.machine.threads.sync.init_onces[id]; + let thread = this.active_thread(); + let init_once = &mut this.machine.sync.init_onces[id]; assert_ne!(init_once.status, InitOnceStatus::Complete, "queueing on complete init once"); - init_once.waiters.push_back(InitOnceWaiter { thread, callback }); - this.block_thread(thread, BlockReason::InitOnce(id)); + init_once.waiters.push_back(thread); + this.block_thread(BlockReason::InitOnce(id), None, callback); } /// Begin initializing this InitOnce. Must only be called after checking that it is currently @@ -148,7 +87,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[inline] fn init_once_begin(&mut self, id: InitOnceId) { let this = self.eval_context_mut(); - let init_once = &mut this.machine.threads.sync.init_onces[id]; + let init_once = &mut this.machine.sync.init_onces[id]; assert_eq!( init_once.status, InitOnceStatus::Uninitialized, @@ -160,9 +99,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[inline] fn init_once_complete(&mut self, id: InitOnceId) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let current_thread = this.get_active_thread(); - let current_span = this.machine.current_span(); - let init_once = &mut this.machine.threads.sync.init_onces[id]; + let init_once = &mut this.machine.sync.init_onces[id]; assert_eq!( init_once.status, @@ -174,13 +111,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Each complete happens-before the end of the wait if let Some(data_race) = &this.machine.data_race { - init_once.clock.clone_from(&data_race.release_clock(current_thread, current_span)); + init_once.clock.clone_from(&data_race.release_clock(&this.machine.threads)); } // Wake up everyone. // need to take the queue to avoid having `this` be borrowed multiple times for waiter in std::mem::take(&mut init_once.waiters) { - this.init_once_wake_waiter(id, waiter)?; + this.unblock_thread(waiter, BlockReason::InitOnce(id))?; } Ok(()) @@ -189,26 +126,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[inline] fn init_once_fail(&mut self, id: InitOnceId) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let current_thread = this.get_active_thread(); - let current_span = this.machine.current_span(); - let init_once = &mut this.machine.threads.sync.init_onces[id]; + let init_once = &mut this.machine.sync.init_onces[id]; assert_eq!( init_once.status, InitOnceStatus::Begun, "failing already completed or uninit init once" ); + // This is again uninitialized. + init_once.status = InitOnceStatus::Uninitialized; // Each complete happens-before the end of the wait if let Some(data_race) = &this.machine.data_race { - init_once.clock.clone_from(&data_race.release_clock(current_thread, current_span)); + init_once.clock.clone_from(&data_race.release_clock(&this.machine.threads)); } // Wake up one waiting thread, so they can go ahead and try to init this. if let Some(waiter) = init_once.waiters.pop_front() { - this.init_once_wake_waiter(id, waiter)?; - } else { - // Nobody there to take this, so go back to 'uninit' - init_once.status = InitOnceStatus::Uninitialized; + this.unblock_thread(waiter, BlockReason::InitOnce(id))?; } Ok(()) @@ -226,6 +160,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "observing the completion of incomplete init once" ); - this.init_once_observe_attempt(id); + this.acquire_clock(&this.machine.sync.init_onces[id].clock); } } diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs index 15e1a94d6db0..822d173ac06a 100644 --- a/src/tools/miri/src/concurrency/mod.rs +++ b/src/tools/miri/src/concurrency/mod.rs @@ -1,8 +1,7 @@ pub mod data_race; -mod range_object_map; -#[macro_use] -pub mod sync; pub mod init_once; +mod range_object_map; +pub mod sync; pub mod thread; mod vector_clock; pub mod weak_memory; diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 946769559996..91865a2192cf 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -1,5 +1,6 @@ use std::collections::{hash_map::Entry, VecDeque}; use std::ops::Not; +use std::time::Duration; use rustc_data_structures::fx::FxHashMap; use rustc_index::{Idx, IndexVec}; @@ -25,7 +26,7 @@ macro_rules! declare_id { #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] pub struct $name(std::num::NonZero); - impl SyncId for $name { + impl $crate::concurrency::sync::SyncId for $name { // Panics if `id == 0`. fn from_u32(id: u32) -> Self { Self(std::num::NonZero::new(id).unwrap()) @@ -35,12 +36,16 @@ macro_rules! declare_id { } } + impl $crate::VisitProvenance for $name { + fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {} + } + impl Idx for $name { fn new(idx: usize) -> Self { // We use 0 as a sentinel value (see the comment above) and, // therefore, need to shift by one when converting from an index // into a vector. - let shifted_idx = u32::try_from(idx).unwrap().checked_add(1).unwrap(); + let shifted_idx = u32::try_from(idx).unwrap().strict_add(1); $name(std::num::NonZero::new(shifted_idx).unwrap()) } fn index(self) -> usize { @@ -51,12 +56,13 @@ macro_rules! declare_id { } impl $name { - pub fn to_u32_scalar(&self) -> Scalar { + pub fn to_u32_scalar(&self) -> Scalar { Scalar::from_u32(self.0.get()) } } }; } +pub(super) use declare_id; declare_id!(MutexId); @@ -111,19 +117,10 @@ struct RwLock { declare_id!(CondvarId); -/// A thread waiting on a conditional variable. -#[derive(Debug)] -struct CondvarWaiter { - /// The thread that is waiting on this variable. - thread: ThreadId, - /// The mutex on which the thread is waiting. - lock: MutexId, -} - /// The conditional variable state. #[derive(Default, Debug)] struct Condvar { - waiters: VecDeque, + waiters: VecDeque, /// Tracks the happens-before relationship /// between a cond-var signal and a cond-var /// wait during a non-spurious signal event. @@ -153,36 +150,26 @@ struct FutexWaiter { bitset: u32, } -/// The state of all synchronization variables. +/// The state of all synchronization objects. #[derive(Default, Debug)] -pub(crate) struct SynchronizationState<'mir, 'tcx> { +pub struct SynchronizationObjects { mutexes: IndexVec, rwlocks: IndexVec, condvars: IndexVec, futexes: FxHashMap, - pub(super) init_onces: IndexVec>, -} - -impl<'mir, 'tcx> VisitProvenance for SynchronizationState<'mir, 'tcx> { - fn visit_provenance(&self, visit: &mut VisitWith<'_>) { - for init_once in self.init_onces.iter() { - init_once.visit_provenance(visit); - } - } + pub(super) init_onces: IndexVec, } // Private extension trait for local helper methods -impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub(super) trait EvalContextExtPriv<'mir, 'tcx: 'mir>: - crate::MiriInterpCxExt<'mir, 'tcx> -{ +impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Lazily initialize the ID of this Miri sync structure. /// ('0' indicates uninit.) #[inline] fn get_or_create_id( &mut self, next_id: Id, - lock_op: &OpTy<'tcx, Provenance>, + lock_op: &OpTy<'tcx>, lock_layout: TyAndLayout<'tcx>, offset: u64, ) -> InterpResult<'tcx, Option> { @@ -210,58 +197,98 @@ pub(super) trait EvalContextExtPriv<'mir, 'tcx: 'mir>: }) } - /// Take a reader out of the queue waiting for the lock. - /// Returns `true` if some thread got the rwlock. + /// Provides the closure with the next MutexId. Creates that mutex if the closure returns None, + /// otherwise returns the value from the closure. #[inline] - fn rwlock_dequeue_and_lock_reader(&mut self, id: RwLockId) -> bool { + fn mutex_get_or_create(&mut self, existing: F) -> InterpResult<'tcx, MutexId> + where + F: FnOnce(&mut MiriInterpCx<'tcx>, MutexId) -> InterpResult<'tcx, Option>, + { let this = self.eval_context_mut(); - if let Some(reader) = this.machine.threads.sync.rwlocks[id].reader_queue.pop_front() { - this.unblock_thread(reader, BlockReason::RwLock(id)); - this.rwlock_reader_lock(id, reader); - true + let next_index = this.machine.sync.mutexes.next_index(); + if let Some(old) = existing(this, next_index)? { + if this.machine.sync.mutexes.get(old).is_none() { + throw_ub_format!("mutex has invalid ID"); + } + Ok(old) } else { - false + let new_index = this.machine.sync.mutexes.push(Default::default()); + assert_eq!(next_index, new_index); + Ok(new_index) } } - /// Take the writer out of the queue waiting for the lock. - /// Returns `true` if some thread got the rwlock. + /// Provides the closure with the next RwLockId. Creates that RwLock if the closure returns None, + /// otherwise returns the value from the closure. #[inline] - fn rwlock_dequeue_and_lock_writer(&mut self, id: RwLockId) -> bool { + fn rwlock_get_or_create(&mut self, existing: F) -> InterpResult<'tcx, RwLockId> + where + F: FnOnce(&mut MiriInterpCx<'tcx>, RwLockId) -> InterpResult<'tcx, Option>, + { let this = self.eval_context_mut(); - if let Some(writer) = this.machine.threads.sync.rwlocks[id].writer_queue.pop_front() { - this.unblock_thread(writer, BlockReason::RwLock(id)); - this.rwlock_writer_lock(id, writer); - true + let next_index = this.machine.sync.rwlocks.next_index(); + if let Some(old) = existing(this, next_index)? { + if this.machine.sync.rwlocks.get(old).is_none() { + throw_ub_format!("rwlock has invalid ID"); + } + Ok(old) } else { - false + let new_index = this.machine.sync.rwlocks.push(Default::default()); + assert_eq!(next_index, new_index); + Ok(new_index) } } - /// Take a thread out of the queue waiting for the mutex, and lock - /// the mutex for it. Returns `true` if some thread has the mutex now. + /// Provides the closure with the next CondvarId. Creates that Condvar if the closure returns None, + /// otherwise returns the value from the closure. #[inline] - fn mutex_dequeue_and_lock(&mut self, id: MutexId) -> bool { + fn condvar_get_or_create(&mut self, existing: F) -> InterpResult<'tcx, CondvarId> + where + F: FnOnce(&mut MiriInterpCx<'tcx>, CondvarId) -> InterpResult<'tcx, Option>, + { let this = self.eval_context_mut(); - if let Some(thread) = this.machine.threads.sync.mutexes[id].queue.pop_front() { - this.unblock_thread(thread, BlockReason::Mutex(id)); - this.mutex_lock(id, thread); - true + let next_index = this.machine.sync.condvars.next_index(); + if let Some(old) = existing(this, next_index)? { + if this.machine.sync.condvars.get(old).is_none() { + throw_ub_format!("condvar has invalid ID"); + } + Ok(old) } else { - false + let new_index = this.machine.sync.condvars.push(Default::default()); + assert_eq!(next_index, new_index); + Ok(new_index) } } + + fn condvar_reacquire_mutex( + &mut self, + mutex: MutexId, + retval: Scalar, + dest: MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + if this.mutex_is_locked(mutex) { + assert_ne!(this.mutex_get_owner(mutex), this.active_thread()); + this.mutex_enqueue_and_block(mutex, retval, dest); + } else { + // We can have it right now! + this.mutex_lock(mutex); + // Don't forget to write the return value. + this.write_scalar(retval, &dest)?; + } + Ok(()) + } } // Public interface to synchronization primitives. Please note that in most // cases, the function calls are infallible and it is the client's (shim // implementation's) responsibility to detect and deal with erroneous // situations. -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn mutex_get_or_create_id( &mut self, - lock_op: &OpTy<'tcx, Provenance>, + lock_op: &OpTy<'tcx>, lock_layout: TyAndLayout<'tcx>, offset: u64, ) -> InterpResult<'tcx, MutexId> { @@ -273,7 +300,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn rwlock_get_or_create_id( &mut self, - lock_op: &OpTy<'tcx, Provenance>, + lock_op: &OpTy<'tcx>, lock_layout: TyAndLayout<'tcx>, offset: u64, ) -> InterpResult<'tcx, RwLockId> { @@ -285,7 +312,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn condvar_get_or_create_id( &mut self, - lock_op: &OpTy<'tcx, Provenance>, + lock_op: &OpTy<'tcx>, lock_layout: TyAndLayout<'tcx>, offset: u64, ) -> InterpResult<'tcx, CondvarId> { @@ -295,45 +322,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }) } - #[inline] - /// Provides the closure with the next MutexId. Creates that mutex if the closure returns None, - /// otherwise returns the value from the closure - fn mutex_get_or_create(&mut self, existing: F) -> InterpResult<'tcx, MutexId> - where - F: FnOnce(&mut MiriInterpCx<'mir, 'tcx>, MutexId) -> InterpResult<'tcx, Option>, - { - let this = self.eval_context_mut(); - let next_index = this.machine.threads.sync.mutexes.next_index(); - if let Some(old) = existing(this, next_index)? { - if this.machine.threads.sync.mutexes.get(old).is_none() { - throw_ub_format!("mutex has invalid ID"); - } - Ok(old) - } else { - let new_index = this.machine.threads.sync.mutexes.push(Default::default()); - assert_eq!(next_index, new_index); - Ok(new_index) - } - } - #[inline] /// Get the id of the thread that currently owns this lock. fn mutex_get_owner(&mut self, id: MutexId) -> ThreadId { let this = self.eval_context_ref(); - this.machine.threads.sync.mutexes[id].owner.unwrap() + this.machine.sync.mutexes[id].owner.unwrap() } #[inline] /// Check if locked. fn mutex_is_locked(&self, id: MutexId) -> bool { let this = self.eval_context_ref(); - this.machine.threads.sync.mutexes[id].owner.is_some() + this.machine.sync.mutexes[id].owner.is_some() } /// Lock by setting the mutex owner and increasing the lock count. - fn mutex_lock(&mut self, id: MutexId, thread: ThreadId) { + fn mutex_lock(&mut self, id: MutexId) { let this = self.eval_context_mut(); - let mutex = &mut this.machine.threads.sync.mutexes[id]; + let thread = this.active_thread(); + let mutex = &mut this.machine.sync.mutexes[id]; if let Some(current_owner) = mutex.owner { assert_eq!(thread, current_owner, "mutex already locked by another thread"); assert!( @@ -343,81 +350,77 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } else { mutex.owner = Some(thread); } - mutex.lock_count = mutex.lock_count.checked_add(1).unwrap(); + mutex.lock_count = mutex.lock_count.strict_add(1); if let Some(data_race) = &this.machine.data_race { - data_race.acquire_clock(&mutex.clock, thread); + data_race.acquire_clock(&mutex.clock, &this.machine.threads); } } /// Try unlocking by decreasing the lock count and returning the old lock /// count. If the lock count reaches 0, release the lock and potentially - /// give to a new owner. If the lock was not locked by `expected_owner`, + /// give to a new owner. If the lock was not locked by the current thread, /// return `None`. - fn mutex_unlock(&mut self, id: MutexId, expected_owner: ThreadId) -> Option { + fn mutex_unlock(&mut self, id: MutexId) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); - let current_span = this.machine.current_span(); - let mutex = &mut this.machine.threads.sync.mutexes[id]; - if let Some(current_owner) = mutex.owner { + let mutex = &mut this.machine.sync.mutexes[id]; + Ok(if let Some(current_owner) = mutex.owner { // Mutex is locked. - if current_owner != expected_owner { + if current_owner != this.machine.threads.active_thread() { // Only the owner can unlock the mutex. - return None; + return Ok(None); } let old_lock_count = mutex.lock_count; - mutex.lock_count = old_lock_count - .checked_sub(1) - .expect("invariant violation: lock_count == 0 iff the thread is unlocked"); + mutex.lock_count = old_lock_count.strict_sub(1); if mutex.lock_count == 0 { mutex.owner = None; // The mutex is completely unlocked. Try transferring ownership // to another thread. if let Some(data_race) = &this.machine.data_race { - mutex.clock.clone_from(&data_race.release_clock(current_owner, current_span)); + mutex.clock.clone_from(&data_race.release_clock(&this.machine.threads)); + } + if let Some(thread) = this.machine.sync.mutexes[id].queue.pop_front() { + this.unblock_thread(thread, BlockReason::Mutex(id))?; } - this.mutex_dequeue_and_lock(id); } Some(old_lock_count) } else { // Mutex is not locked. None - } + }) } /// Put the thread into the queue waiting for the mutex. + /// Once the Mutex becomes available, `retval` will be written to `dest`. #[inline] - fn mutex_enqueue_and_block(&mut self, id: MutexId, thread: ThreadId) { + fn mutex_enqueue_and_block(&mut self, id: MutexId, retval: Scalar, dest: MPlaceTy<'tcx>) { let this = self.eval_context_mut(); assert!(this.mutex_is_locked(id), "queing on unlocked mutex"); - this.machine.threads.sync.mutexes[id].queue.push_back(thread); - this.block_thread(thread, BlockReason::Mutex(id)); - } - - /// Provides the closure with the next RwLockId. Creates that RwLock if the closure returns None, - /// otherwise returns the value from the closure - #[inline] - fn rwlock_get_or_create(&mut self, existing: F) -> InterpResult<'tcx, RwLockId> - where - F: FnOnce(&mut MiriInterpCx<'mir, 'tcx>, RwLockId) -> InterpResult<'tcx, Option>, - { - let this = self.eval_context_mut(); - let next_index = this.machine.threads.sync.rwlocks.next_index(); - if let Some(old) = existing(this, next_index)? { - if this.machine.threads.sync.rwlocks.get(old).is_none() { - throw_ub_format!("rwlock has invalid ID"); - } - Ok(old) - } else { - let new_index = this.machine.threads.sync.rwlocks.push(Default::default()); - assert_eq!(next_index, new_index); - Ok(new_index) - } + let thread = this.active_thread(); + this.machine.sync.mutexes[id].queue.push_back(thread); + this.block_thread( + BlockReason::Mutex(id), + None, + callback!( + @capture<'tcx> { + id: MutexId, + retval: Scalar, + dest: MPlaceTy<'tcx>, + } + @unblock = |this| { + assert!(!this.mutex_is_locked(id)); + this.mutex_lock(id); + this.write_scalar(retval, &dest)?; + Ok(()) + } + ), + ); } #[inline] /// Check if locked. fn rwlock_is_locked(&self, id: RwLockId) -> bool { let this = self.eval_context_ref(); - let rwlock = &this.machine.threads.sync.rwlocks[id]; + let rwlock = &this.machine.sync.rwlocks[id]; trace!( "rwlock_is_locked: {:?} writer is {:?} and there are {} reader threads (some of which could hold multiple read locks)", id, @@ -431,48 +434,49 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[inline] fn rwlock_is_write_locked(&self, id: RwLockId) -> bool { let this = self.eval_context_ref(); - let rwlock = &this.machine.threads.sync.rwlocks[id]; + let rwlock = &this.machine.sync.rwlocks[id]; trace!("rwlock_is_write_locked: {:?} writer is {:?}", id, rwlock.writer); rwlock.writer.is_some() } /// Read-lock the lock by adding the `reader` the list of threads that own /// this lock. - fn rwlock_reader_lock(&mut self, id: RwLockId, reader: ThreadId) { + fn rwlock_reader_lock(&mut self, id: RwLockId) { let this = self.eval_context_mut(); + let thread = this.active_thread(); assert!(!this.rwlock_is_write_locked(id), "the lock is write locked"); - trace!("rwlock_reader_lock: {:?} now also held (one more time) by {:?}", id, reader); - let rwlock = &mut this.machine.threads.sync.rwlocks[id]; - let count = rwlock.readers.entry(reader).or_insert(0); - *count = count.checked_add(1).expect("the reader counter overflowed"); + trace!("rwlock_reader_lock: {:?} now also held (one more time) by {:?}", id, thread); + let rwlock = &mut this.machine.sync.rwlocks[id]; + let count = rwlock.readers.entry(thread).or_insert(0); + *count = count.strict_add(1); if let Some(data_race) = &this.machine.data_race { - data_race.acquire_clock(&rwlock.clock_unlocked, reader); + data_race.acquire_clock(&rwlock.clock_unlocked, &this.machine.threads); } } - /// Try read-unlock the lock for `reader` and potentially give the lock to a new owner. + /// Try read-unlock the lock for the current threads and potentially give the lock to a new owner. /// Returns `true` if succeeded, `false` if this `reader` did not hold the lock. - fn rwlock_reader_unlock(&mut self, id: RwLockId, reader: ThreadId) -> bool { + fn rwlock_reader_unlock(&mut self, id: RwLockId) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); - let current_span = this.machine.current_span(); - let rwlock = &mut this.machine.threads.sync.rwlocks[id]; - match rwlock.readers.entry(reader) { + let thread = this.active_thread(); + let rwlock = &mut this.machine.sync.rwlocks[id]; + match rwlock.readers.entry(thread) { Entry::Occupied(mut entry) => { let count = entry.get_mut(); assert!(*count > 0, "rwlock locked with count == 0"); *count -= 1; if *count == 0 { - trace!("rwlock_reader_unlock: {:?} no longer held by {:?}", id, reader); + trace!("rwlock_reader_unlock: {:?} no longer held by {:?}", id, thread); entry.remove(); } else { - trace!("rwlock_reader_unlock: {:?} held one less time by {:?}", id, reader); + trace!("rwlock_reader_unlock: {:?} held one less time by {:?}", id, thread); } } - Entry::Vacant(_) => return false, // we did not even own this lock + Entry::Vacant(_) => return Ok(false), // we did not even own this lock } if let Some(data_race) = &this.machine.data_race { // Add this to the shared-release clock of all concurrent readers. - rwlock.clock_current_readers.join(&data_race.release_clock(reader, current_span)); + rwlock.clock_current_readers.join(&data_race.release_clock(&this.machine.threads)); } // The thread was a reader. If the lock is not held any more, give it to a writer. @@ -480,183 +484,291 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // All the readers are finished, so set the writer data-race handle to the value // of the union of all reader data race handles, since the set of readers // happen-before the writers - let rwlock = &mut this.machine.threads.sync.rwlocks[id]; + let rwlock = &mut this.machine.sync.rwlocks[id]; rwlock.clock_unlocked.clone_from(&rwlock.clock_current_readers); - this.rwlock_dequeue_and_lock_writer(id); + // See if there is a thread to unblock. + if let Some(writer) = rwlock.writer_queue.pop_front() { + this.unblock_thread(writer, BlockReason::RwLock(id))?; + } } - true + Ok(true) } /// Put the reader in the queue waiting for the lock and block it. + /// Once the lock becomes available, `retval` will be written to `dest`. #[inline] - fn rwlock_enqueue_and_block_reader(&mut self, id: RwLockId, reader: ThreadId) { + fn rwlock_enqueue_and_block_reader( + &mut self, + id: RwLockId, + retval: Scalar, + dest: MPlaceTy<'tcx>, + ) { let this = self.eval_context_mut(); + let thread = this.active_thread(); assert!(this.rwlock_is_write_locked(id), "read-queueing on not write locked rwlock"); - this.machine.threads.sync.rwlocks[id].reader_queue.push_back(reader); - this.block_thread(reader, BlockReason::RwLock(id)); + this.machine.sync.rwlocks[id].reader_queue.push_back(thread); + this.block_thread( + BlockReason::RwLock(id), + None, + callback!( + @capture<'tcx> { + id: RwLockId, + retval: Scalar, + dest: MPlaceTy<'tcx>, + } + @unblock = |this| { + this.rwlock_reader_lock(id); + this.write_scalar(retval, &dest)?; + Ok(()) + } + ), + ); } /// Lock by setting the writer that owns the lock. #[inline] - fn rwlock_writer_lock(&mut self, id: RwLockId, writer: ThreadId) { + fn rwlock_writer_lock(&mut self, id: RwLockId) { let this = self.eval_context_mut(); + let thread = this.active_thread(); assert!(!this.rwlock_is_locked(id), "the rwlock is already locked"); - trace!("rwlock_writer_lock: {:?} now held by {:?}", id, writer); - let rwlock = &mut this.machine.threads.sync.rwlocks[id]; - rwlock.writer = Some(writer); + trace!("rwlock_writer_lock: {:?} now held by {:?}", id, thread); + let rwlock = &mut this.machine.sync.rwlocks[id]; + rwlock.writer = Some(thread); if let Some(data_race) = &this.machine.data_race { - data_race.acquire_clock(&rwlock.clock_unlocked, writer); + data_race.acquire_clock(&rwlock.clock_unlocked, &this.machine.threads); } } - /// Try to unlock by removing the writer. + /// Try to unlock an rwlock held by the current thread. + /// Return `false` if it is held by another thread. #[inline] - fn rwlock_writer_unlock(&mut self, id: RwLockId, expected_writer: ThreadId) -> bool { + fn rwlock_writer_unlock(&mut self, id: RwLockId) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); - let current_span = this.machine.current_span(); - let rwlock = &mut this.machine.threads.sync.rwlocks[id]; - if let Some(current_writer) = rwlock.writer { - if current_writer != expected_writer { + let thread = this.active_thread(); + let rwlock = &mut this.machine.sync.rwlocks[id]; + Ok(if let Some(current_writer) = rwlock.writer { + if current_writer != thread { // Only the owner can unlock the rwlock. - return false; + return Ok(false); } rwlock.writer = None; - trace!("rwlock_writer_unlock: {:?} unlocked by {:?}", id, expected_writer); - // Release memory to next lock holder. + trace!("rwlock_writer_unlock: {:?} unlocked by {:?}", id, thread); + // Record release clock for next lock holder. if let Some(data_race) = &this.machine.data_race { - rwlock - .clock_unlocked - .clone_from(&*data_race.release_clock(current_writer, current_span)); + rwlock.clock_unlocked.clone_from(&*data_race.release_clock(&this.machine.threads)); } // The thread was a writer. // // We are prioritizing writers here against the readers. As a // result, not only readers can starve writers, but also writers can // starve readers. - if this.rwlock_dequeue_and_lock_writer(id) { - // Someone got the write lock, nice. + if let Some(writer) = rwlock.writer_queue.pop_front() { + this.unblock_thread(writer, BlockReason::RwLock(id))?; } else { - // Give the lock to all readers. - while this.rwlock_dequeue_and_lock_reader(id) { - // Rinse and repeat. + // Take the entire read queue and wake them all up. + let readers = std::mem::take(&mut rwlock.reader_queue); + for reader in readers { + this.unblock_thread(reader, BlockReason::RwLock(id))?; } } true } else { false - } + }) } /// Put the writer in the queue waiting for the lock. + /// Once the lock becomes available, `retval` will be written to `dest`. #[inline] - fn rwlock_enqueue_and_block_writer(&mut self, id: RwLockId, writer: ThreadId) { + fn rwlock_enqueue_and_block_writer( + &mut self, + id: RwLockId, + retval: Scalar, + dest: MPlaceTy<'tcx>, + ) { let this = self.eval_context_mut(); assert!(this.rwlock_is_locked(id), "write-queueing on unlocked rwlock"); - this.machine.threads.sync.rwlocks[id].writer_queue.push_back(writer); - this.block_thread(writer, BlockReason::RwLock(id)); - } - - /// Provides the closure with the next CondvarId. Creates that Condvar if the closure returns None, - /// otherwise returns the value from the closure - #[inline] - fn condvar_get_or_create(&mut self, existing: F) -> InterpResult<'tcx, CondvarId> - where - F: FnOnce( - &mut MiriInterpCx<'mir, 'tcx>, - CondvarId, - ) -> InterpResult<'tcx, Option>, - { - let this = self.eval_context_mut(); - let next_index = this.machine.threads.sync.condvars.next_index(); - if let Some(old) = existing(this, next_index)? { - if this.machine.threads.sync.condvars.get(old).is_none() { - throw_ub_format!("condvar has invalid ID"); - } - Ok(old) - } else { - let new_index = this.machine.threads.sync.condvars.push(Default::default()); - assert_eq!(next_index, new_index); - Ok(new_index) - } + let thread = this.active_thread(); + this.machine.sync.rwlocks[id].writer_queue.push_back(thread); + this.block_thread( + BlockReason::RwLock(id), + None, + callback!( + @capture<'tcx> { + id: RwLockId, + retval: Scalar, + dest: MPlaceTy<'tcx>, + } + @unblock = |this| { + this.rwlock_writer_lock(id); + this.write_scalar(retval, &dest)?; + Ok(()) + } + ), + ); } /// Is the conditional variable awaited? #[inline] fn condvar_is_awaited(&mut self, id: CondvarId) -> bool { let this = self.eval_context_mut(); - !this.machine.threads.sync.condvars[id].waiters.is_empty() + !this.machine.sync.condvars[id].waiters.is_empty() } - /// Mark that the thread is waiting on the conditional variable. - fn condvar_wait(&mut self, id: CondvarId, thread: ThreadId, lock: MutexId) { + /// Release the mutex and let the current thread wait on the given condition variable. + /// Once it is signaled, the mutex will be acquired and `retval_succ` will be written to `dest`. + /// If the timeout happens first, `retval_timeout` will be written to `dest`. + fn condvar_wait( + &mut self, + condvar: CondvarId, + mutex: MutexId, + timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>, + retval_succ: Scalar, + retval_timeout: Scalar, + dest: MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let waiters = &mut this.machine.threads.sync.condvars[id].waiters; - assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting"); - waiters.push_back(CondvarWaiter { thread, lock }); + if let Some(old_locked_count) = this.mutex_unlock(mutex)? { + if old_locked_count != 1 { + throw_unsup_format!( + "awaiting a condvar on a mutex acquired multiple times is not supported" + ); + } + } else { + throw_ub_format!( + "awaiting a condvar on a mutex that is unlocked or owned by a different thread" + ); + } + let thread = this.active_thread(); + let waiters = &mut this.machine.sync.condvars[condvar].waiters; + waiters.push_back(thread); + this.block_thread( + BlockReason::Condvar(condvar), + timeout, + callback!( + @capture<'tcx> { + condvar: CondvarId, + mutex: MutexId, + retval_succ: Scalar, + retval_timeout: Scalar, + dest: MPlaceTy<'tcx>, + } + @unblock = |this| { + // The condvar was signaled. Make sure we get the clock for that. + if let Some(data_race) = &this.machine.data_race { + data_race.acquire_clock( + &this.machine.sync.condvars[condvar].clock, + &this.machine.threads, + ); + } + // Try to acquire the mutex. + // The timeout only applies to the first wait (until the signal), not for mutex acquisition. + this.condvar_reacquire_mutex(mutex, retval_succ, dest) + } + @timeout = |this| { + // We have to remove the waiter from the queue again. + let thread = this.active_thread(); + let waiters = &mut this.machine.sync.condvars[condvar].waiters; + waiters.retain(|waiter| *waiter != thread); + // Now get back the lock. + this.condvar_reacquire_mutex(mutex, retval_timeout, dest) + } + ), + ); + return Ok(()); } /// Wake up some thread (if there is any) sleeping on the conditional - /// variable. - fn condvar_signal(&mut self, id: CondvarId) -> Option<(ThreadId, MutexId)> { + /// variable. Returns `true` iff any thread was woken up. + fn condvar_signal(&mut self, id: CondvarId) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); - let current_thread = this.get_active_thread(); - let current_span = this.machine.current_span(); - let condvar = &mut this.machine.threads.sync.condvars[id]; + let condvar = &mut this.machine.sync.condvars[id]; let data_race = &this.machine.data_race; // Each condvar signal happens-before the end of the condvar wake if let Some(data_race) = data_race { - condvar.clock.clone_from(&*data_race.release_clock(current_thread, current_span)); + condvar.clock.clone_from(&*data_race.release_clock(&this.machine.threads)); } - condvar.waiters.pop_front().map(|waiter| { - if let Some(data_race) = data_race { - data_race.acquire_clock(&condvar.clock, waiter.thread); - } - (waiter.thread, waiter.lock) - }) + let Some(waiter) = condvar.waiters.pop_front() else { + return Ok(false); + }; + this.unblock_thread(waiter, BlockReason::Condvar(id))?; + Ok(true) } - #[inline] - /// Remove the thread from the queue of threads waiting on this conditional variable. - fn condvar_remove_waiter(&mut self, id: CondvarId, thread: ThreadId) { + /// Wait for the futex to be signaled, or a timeout. + /// On a signal, `retval_succ` is written to `dest`. + /// On a timeout, `retval_timeout` is written to `dest` and `errno_timeout` is set as the last error. + fn futex_wait( + &mut self, + addr: u64, + bitset: u32, + timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>, + retval_succ: Scalar, + retval_timeout: Scalar, + dest: MPlaceTy<'tcx>, + errno_timeout: Scalar, + ) { let this = self.eval_context_mut(); - this.machine.threads.sync.condvars[id].waiters.retain(|waiter| waiter.thread != thread); - } - - fn futex_wait(&mut self, addr: u64, thread: ThreadId, bitset: u32) { - let this = self.eval_context_mut(); - let futex = &mut this.machine.threads.sync.futexes.entry(addr).or_default(); + let thread = this.active_thread(); + let futex = &mut this.machine.sync.futexes.entry(addr).or_default(); let waiters = &mut futex.waiters; assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting"); waiters.push_back(FutexWaiter { thread, bitset }); + this.block_thread( + BlockReason::Futex { addr }, + timeout, + callback!( + @capture<'tcx> { + addr: u64, + retval_succ: Scalar, + retval_timeout: Scalar, + dest: MPlaceTy<'tcx>, + errno_timeout: Scalar, + } + @unblock = |this| { + let futex = this.machine.sync.futexes.get(&addr).unwrap(); + // Acquire the clock of the futex. + if let Some(data_race) = &this.machine.data_race { + data_race.acquire_clock(&futex.clock, &this.machine.threads); + } + // Write the return value. + this.write_scalar(retval_succ, &dest)?; + Ok(()) + } + @timeout = |this| { + // Remove the waiter from the futex. + let thread = this.active_thread(); + let futex = this.machine.sync.futexes.get_mut(&addr).unwrap(); + futex.waiters.retain(|waiter| waiter.thread != thread); + // Set errno and write return value. + this.set_last_error(errno_timeout)?; + this.write_scalar(retval_timeout, &dest)?; + Ok(()) + } + ), + ); } - fn futex_wake(&mut self, addr: u64, bitset: u32) -> Option { + /// Returns whether anything was woken. + fn futex_wake(&mut self, addr: u64, bitset: u32) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); - let current_thread = this.get_active_thread(); - let current_span = this.machine.current_span(); - let futex = &mut this.machine.threads.sync.futexes.get_mut(&addr)?; + let Some(futex) = this.machine.sync.futexes.get_mut(&addr) else { + return Ok(false); + }; let data_race = &this.machine.data_race; // Each futex-wake happens-before the end of the futex wait if let Some(data_race) = data_race { - futex.clock.clone_from(&*data_race.release_clock(current_thread, current_span)); + futex.clock.clone_from(&*data_race.release_clock(&this.machine.threads)); } // Wake up the first thread in the queue that matches any of the bits in the bitset. - futex.waiters.iter().position(|w| w.bitset & bitset != 0).map(|i| { - let waiter = futex.waiters.remove(i).unwrap(); - if let Some(data_race) = data_race { - data_race.acquire_clock(&futex.clock, waiter.thread); - } - waiter.thread - }) - } - - fn futex_remove_waiter(&mut self, addr: u64, thread: ThreadId) { - let this = self.eval_context_mut(); - if let Some(futex) = this.machine.threads.sync.futexes.get_mut(&addr) { - futex.waiters.retain(|waiter| waiter.thread != thread); - } + let Some(i) = futex.waiters.iter().position(|w| w.bitset & bitset != 0) else { + return Ok(false); + }; + let waiter = futex.waiters.remove(i).unwrap(); + this.unblock_thread(waiter.thread, BlockReason::Futex { addr })?; + Ok(true) } } diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 6953ce81c5e8..6a2b99825adc 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -1,7 +1,6 @@ //! Implements threads. -use std::cell::RefCell; -use std::collections::hash_map::Entry; +use std::mem; use std::num::TryFromIntError; use std::sync::atomic::Ordering::Relaxed; use std::task::Poll; @@ -19,7 +18,6 @@ use rustc_span::Span; use rustc_target::spec::abi::Abi; use crate::concurrency::data_race; -use crate::concurrency::sync::SynchronizationState; use crate::shims::tls; use crate::*; @@ -42,12 +40,71 @@ pub enum TlsAllocAction { Leak, } -/// Trait for callbacks that can be executed when some event happens, such as after a timeout. -pub trait MachineCallback<'mir, 'tcx>: VisitProvenance { - fn call(&self, ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>) -> InterpResult<'tcx>; -} +/// Trait for callbacks that are executed when a thread gets unblocked. +pub trait UnblockCallback<'tcx>: VisitProvenance { + /// Will be invoked when the thread was unblocked the "regular" way, + /// i.e. whatever event it was blocking on has happened. + fn unblock(self: Box, ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>) -> InterpResult<'tcx>; -type TimeoutCallback<'mir, 'tcx> = Box + 'tcx>; + /// Will be invoked when the timeout ellapsed without the event the + /// thread was blocking on having occurred. + fn timeout(self: Box, _ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>) + -> InterpResult<'tcx>; +} +type DynUnblockCallback<'tcx> = Box + 'tcx>; + +#[macro_export] +macro_rules! callback { + ( + @capture<$tcx:lifetime $(,)? $($lft:lifetime),*> { $($name:ident: $type:ty),* $(,)? } + @unblock = |$this:ident| $unblock:block + ) => { + callback!( + @capture<$tcx, $($lft),*> { $($name: $type),+ } + @unblock = |$this| $unblock + @timeout = |_this| { + unreachable!( + "timeout on a thread that was blocked without a timeout (or someone forgot to overwrite this method)" + ) + } + ) + }; + ( + @capture<$tcx:lifetime $(,)? $($lft:lifetime),*> { $($name:ident: $type:ty),* $(,)? } + @unblock = |$this:ident| $unblock:block + @timeout = |$this_timeout:ident| $timeout:block + ) => {{ + struct Callback<$tcx, $($lft),*> { + $($name: $type,)* + _phantom: std::marker::PhantomData<&$tcx ()>, + } + + impl<$tcx, $($lft),*> VisitProvenance for Callback<$tcx, $($lft),*> { + #[allow(unused_variables)] + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + $( + self.$name.visit_provenance(visit); + )* + } + } + + impl<$tcx, $($lft),*> UnblockCallback<$tcx> for Callback<$tcx, $($lft),*> { + fn unblock(self: Box, $this: &mut MiriInterpCx<$tcx>) -> InterpResult<$tcx> { + #[allow(unused_variables)] + let Callback { $($name,)* _phantom } = *self; + $unblock + } + + fn timeout(self: Box, $this_timeout: &mut MiriInterpCx<$tcx>) -> InterpResult<$tcx> { + #[allow(unused_variables)] + let Callback { $($name,)* _phantom } = *self; + $timeout + } + } + + Callback { $($name,)* _phantom: std::marker::PhantomData } + }} +} /// A thread identifier. #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] @@ -111,24 +168,48 @@ pub enum BlockReason { Condvar(CondvarId), /// Blocked on a reader-writer lock. RwLock(RwLockId), - /// Blocled on a Futex variable. + /// Blocked on a Futex variable. Futex { addr: u64 }, /// Blocked on an InitOnce. InitOnce(InitOnceId), } /// The state of a thread. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum ThreadState { +enum ThreadState<'tcx> { /// The thread is enabled and can be executed. Enabled, /// The thread is blocked on something. - Blocked(BlockReason), + Blocked { reason: BlockReason, timeout: Option, callback: DynUnblockCallback<'tcx> }, /// The thread has terminated its execution. We do not delete terminated /// threads (FIXME: why?). Terminated, } +impl<'tcx> std::fmt::Debug for ThreadState<'tcx> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Enabled => write!(f, "Enabled"), + Self::Blocked { reason, timeout, .. } => + f.debug_struct("Blocked").field("reason", reason).field("timeout", timeout).finish(), + Self::Terminated => write!(f, "Terminated"), + } + } +} + +impl<'tcx> ThreadState<'tcx> { + fn is_enabled(&self) -> bool { + matches!(self, ThreadState::Enabled) + } + + fn is_terminated(&self) -> bool { + matches!(self, ThreadState::Terminated) + } + + fn is_blocked_on(&self, reason: BlockReason) -> bool { + matches!(*self, ThreadState::Blocked { reason: actual_reason, .. } if actual_reason == reason) + } +} + /// The join status of a thread. #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum ThreadJoinStatus { @@ -142,20 +223,20 @@ enum ThreadJoinStatus { } /// A thread. -pub struct Thread<'mir, 'tcx> { - state: ThreadState, +pub struct Thread<'tcx> { + state: ThreadState<'tcx>, /// Name of the thread. thread_name: Option>, /// The virtual call stack. - stack: Vec>>, + stack: Vec>>, /// The function to call when the stack ran empty, to figure out what to do next. /// Conceptually, this is the interpreter implementation of the things that happen 'after' the /// Rust language entry point for this thread returns (usually implemented by the C or OS runtime). /// (`None` is an error, it means the callback has not been set up yet or is actively running.) - pub(crate) on_stack_empty: Option>, + pub(crate) on_stack_empty: Option>, /// The index of the topmost user-relevant frame in `stack`. This field must contain /// the value produced by `get_top_user_relevant_frame`. @@ -175,16 +256,16 @@ pub struct Thread<'mir, 'tcx> { /// which then forwards it to 'Resume'. However this argument is implicit in MIR, /// so we have to store it out-of-band. When there are multiple active unwinds, /// the innermost one is always caught first, so we can store them as a stack. - pub(crate) panic_payloads: Vec>, + pub(crate) panic_payloads: Vec, /// Last OS error location in memory. It is a 32-bit integer. - pub(crate) last_error: Option>, + pub(crate) last_error: Option>, } -pub type StackEmptyCallback<'mir, 'tcx> = - Box) -> InterpResult<'tcx, Poll<()>> + 'tcx>; +pub type StackEmptyCallback<'tcx> = + Box) -> InterpResult<'tcx, Poll<()>> + 'tcx>; -impl<'mir, 'tcx> Thread<'mir, 'tcx> { +impl<'tcx> Thread<'tcx> { /// Get the name of the current thread if it was set. fn thread_name(&self) -> Option<&[u8]> { self.thread_name.as_deref() @@ -240,7 +321,7 @@ impl<'mir, 'tcx> Thread<'mir, 'tcx> { } } -impl<'mir, 'tcx> std::fmt::Debug for Thread<'mir, 'tcx> { +impl<'tcx> std::fmt::Debug for Thread<'tcx> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, @@ -252,8 +333,8 @@ impl<'mir, 'tcx> std::fmt::Debug for Thread<'mir, 'tcx> { } } -impl<'mir, 'tcx> Thread<'mir, 'tcx> { - fn new(name: Option<&str>, on_stack_empty: Option>) -> Self { +impl<'tcx> Thread<'tcx> { + fn new(name: Option<&str>, on_stack_empty: Option>) -> Self { Self { state: ThreadState::Enabled, thread_name: name.map(|name| Vec::from(name.as_bytes())), @@ -267,7 +348,7 @@ impl<'mir, 'tcx> Thread<'mir, 'tcx> { } } -impl VisitProvenance for Thread<'_, '_> { +impl VisitProvenance for Thread<'_> { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let Thread { panic_payloads: panic_payload, @@ -290,7 +371,7 @@ impl VisitProvenance for Thread<'_, '_> { } } -impl VisitProvenance for Frame<'_, '_, Provenance, FrameExtra<'_>> { +impl VisitProvenance for Frame<'_, Provenance, FrameExtra<'_>> { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let Frame { return_place, @@ -324,87 +405,86 @@ impl VisitProvenance for Frame<'_, '_, Provenance, FrameExtra<'_>> { } } -/// A specific moment in time. +/// The moment in time when a blocked thread should be woken up. #[derive(Debug)] -pub enum CallbackTime { +enum Timeout { Monotonic(Instant), RealTime(SystemTime), } -impl CallbackTime { +impl Timeout { /// How long do we have to wait from now until the specified time? fn get_wait_time(&self, clock: &Clock) -> Duration { match self { - CallbackTime::Monotonic(instant) => instant.duration_since(clock.now()), - CallbackTime::RealTime(time) => - time.duration_since(SystemTime::now()).unwrap_or(Duration::new(0, 0)), + Timeout::Monotonic(instant) => instant.duration_since(clock.now()), + Timeout::RealTime(time) => + time.duration_since(SystemTime::now()).unwrap_or(Duration::ZERO), + } + } + + /// Will try to add `duration`, but if that overflows it may add less. + fn add_lossy(&self, duration: Duration) -> Self { + match self { + Timeout::Monotonic(i) => Timeout::Monotonic(i.add_lossy(duration)), + Timeout::RealTime(s) => { + // If this overflows, try adding just 1h and assume that will not overflow. + Timeout::RealTime( + s.checked_add(duration) + .unwrap_or_else(|| s.checked_add(Duration::from_secs(3600)).unwrap()), + ) + } } } } -/// Callbacks are used to implement timeouts. For example, waiting on a -/// conditional variable with a timeout creates a callback that is called after -/// the specified time and unblocks the thread. If another thread signals on the -/// conditional variable, the signal handler deletes the callback. -struct TimeoutCallbackInfo<'mir, 'tcx> { - /// The callback should be called no earlier than this time. - call_time: CallbackTime, - /// The called function. - callback: TimeoutCallback<'mir, 'tcx>, +/// The clock to use for the timeout you are asking for. +#[derive(Debug, Copy, Clone)] +pub enum TimeoutClock { + Monotonic, + RealTime, } -impl<'mir, 'tcx> std::fmt::Debug for TimeoutCallbackInfo<'mir, 'tcx> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "TimeoutCallback({:?})", self.call_time) - } +/// Whether the timeout is relative or absolute. +#[derive(Debug, Copy, Clone)] +pub enum TimeoutAnchor { + Relative, + Absolute, } /// A set of threads. #[derive(Debug)] -pub struct ThreadManager<'mir, 'tcx> { +pub struct ThreadManager<'tcx> { /// Identifier of the currently active thread. active_thread: ThreadId, /// Threads used in the program. /// /// Note that this vector also contains terminated threads. - threads: IndexVec>, - /// This field is pub(crate) because the synchronization primitives - /// (`crate::sync`) need a way to access it. - pub(crate) sync: SynchronizationState<'mir, 'tcx>, - /// A mapping from a thread-local static to an allocation id of a thread - /// specific allocation. - thread_local_alloc_ids: RefCell>>, + threads: IndexVec>, + /// A mapping from a thread-local static to the thread specific allocation. + thread_local_allocs: FxHashMap<(DefId, ThreadId), StrictPointer>, /// A flag that indicates that we should change the active thread. yield_active_thread: bool, - /// Callbacks that are called once the specified time passes. - timeout_callbacks: FxHashMap>, } -impl VisitProvenance for ThreadManager<'_, '_> { +impl VisitProvenance for ThreadManager<'_> { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let ThreadManager { threads, - thread_local_alloc_ids, - timeout_callbacks, + thread_local_allocs, active_thread: _, yield_active_thread: _, - sync, } = self; for thread in threads { thread.visit_provenance(visit); } - for ptr in thread_local_alloc_ids.borrow().values() { + for ptr in thread_local_allocs.values() { ptr.visit_provenance(visit); } - for callback in timeout_callbacks.values() { - callback.callback.visit_provenance(visit); - } - sync.visit_provenance(visit); } } -impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> { +impl<'tcx> Default for ThreadManager<'tcx> { fn default() -> Self { let mut threads = IndexVec::new(); // Create the main thread and add it to the list of threads. @@ -412,18 +492,16 @@ impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> { Self { active_thread: ThreadId::MAIN_THREAD, threads, - sync: SynchronizationState::default(), - thread_local_alloc_ids: Default::default(), + thread_local_allocs: Default::default(), yield_active_thread: false, - timeout_callbacks: FxHashMap::default(), } } } -impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { +impl<'tcx> ThreadManager<'tcx> { pub(crate) fn init( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - on_main_stack_empty: StackEmptyCallback<'mir, 'tcx>, + ecx: &mut MiriInterpCx<'tcx>, + on_main_stack_empty: StackEmptyCallback<'tcx>, ) { ecx.machine.threads.threads[ThreadId::MAIN_THREAD].on_stack_empty = Some(on_main_stack_empty); @@ -436,40 +514,35 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { /// Check if we have an allocation for the given thread local static for the /// active thread. - fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option> { - self.thread_local_alloc_ids.borrow().get(&(def_id, self.active_thread)).cloned() + fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option { + self.thread_local_allocs.get(&(def_id, self.active_thread)).cloned() } /// Set the pointer for the allocation of the given thread local /// static for the active thread. /// /// Panics if a thread local is initialized twice for the same thread. - fn set_thread_local_alloc(&self, def_id: DefId, ptr: Pointer) { - self.thread_local_alloc_ids - .borrow_mut() - .try_insert((def_id, self.active_thread), ptr) - .unwrap(); + fn set_thread_local_alloc(&mut self, def_id: DefId, ptr: StrictPointer) { + self.thread_local_allocs.try_insert((def_id, self.active_thread), ptr).unwrap(); } /// Borrow the stack of the active thread. - pub fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>] { + pub fn active_thread_stack(&self) -> &[Frame<'tcx, Provenance, FrameExtra<'tcx>>] { &self.threads[self.active_thread].stack } /// Mutably borrow the stack of the active thread. - fn active_thread_stack_mut( - &mut self, - ) -> &mut Vec>> { + fn active_thread_stack_mut(&mut self) -> &mut Vec>> { &mut self.threads[self.active_thread].stack } pub fn all_stacks( &self, - ) -> impl Iterator>])> { + ) -> impl Iterator>])> { self.threads.iter_enumerated().map(|(id, t)| (id, &t.stack[..])) } /// Create a new thread and returns its id. - fn create_thread(&mut self, on_stack_empty: StackEmptyCallback<'mir, 'tcx>) -> ThreadId { + fn create_thread(&mut self, on_stack_empty: StackEmptyCallback<'tcx>) -> ThreadId { let new_thread_id = ThreadId::new(self.threads.len()); self.threads.push(Thread::new(None, Some(on_stack_empty))); new_thread_id @@ -487,7 +560,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { } /// Get the id of the currently active thread. - pub fn get_active_thread_id(&self) -> ThreadId { + pub fn active_thread(&self) -> ThreadId { self.active_thread } @@ -499,17 +572,17 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { /// Get the total of threads that are currently live, i.e., not yet terminated. /// (They might be blocked.) pub fn get_live_thread_count(&self) -> usize { - self.threads.iter().filter(|t| !matches!(t.state, ThreadState::Terminated)).count() + self.threads.iter().filter(|t| !t.state.is_terminated()).count() } /// Has the given thread terminated? fn has_terminated(&self, thread_id: ThreadId) -> bool { - self.threads[thread_id].state == ThreadState::Terminated + self.threads[thread_id].state.is_terminated() } /// Have all threads terminated? fn have_all_terminated(&self) -> bool { - self.threads.iter().all(|thread| thread.state == ThreadState::Terminated) + self.threads.iter().all(|thread| thread.state.is_terminated()) } /// Enable the thread for execution. The thread must be terminated. @@ -519,12 +592,12 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { } /// Get a mutable borrow of the currently active thread. - pub fn active_thread_mut(&mut self) -> &mut Thread<'mir, 'tcx> { + pub fn active_thread_mut(&mut self) -> &mut Thread<'tcx> { &mut self.threads[self.active_thread] } /// Get a shared borrow of the currently active thread. - pub fn active_thread_ref(&self) -> &Thread<'mir, 'tcx> { + pub fn active_thread_ref(&self) -> &Thread<'tcx> { &self.threads[self.active_thread] } @@ -539,8 +612,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { fn detach_thread(&mut self, id: ThreadId, allow_terminated_joined: bool) -> InterpResult<'tcx> { trace!("detaching {:?}", id); - let is_ub = if allow_terminated_joined && self.threads[id].state == ThreadState::Terminated - { + let is_ub = if allow_terminated_joined && self.threads[id].state.is_terminated() { // "Detached" in particular means "not yet joined". Redundant detaching is still UB. self.threads[id].join_status == ThreadJoinStatus::Detached } else { @@ -568,19 +640,33 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { // Mark the joined thread as being joined so that we detect if other // threads try to join it. self.threads[joined_thread_id].join_status = ThreadJoinStatus::Joined; - if self.threads[joined_thread_id].state != ThreadState::Terminated { - // The joined thread is still running, we need to wait for it. - self.active_thread_mut().state = - ThreadState::Blocked(BlockReason::Join(joined_thread_id)); + if !self.threads[joined_thread_id].state.is_terminated() { trace!( "{:?} blocked on {:?} when trying to join", self.active_thread, joined_thread_id ); + // The joined thread is still running, we need to wait for it. + // Unce we get unblocked, perform the appropriate synchronization. + self.block_thread( + BlockReason::Join(joined_thread_id), + None, + callback!( + @capture<'tcx> { + joined_thread_id: ThreadId, + } + @unblock = |this| { + if let Some(data_race) = &mut this.machine.data_race { + data_race.thread_joined(&this.machine.threads, joined_thread_id); + } + Ok(()) + } + ), + ); } else { - // The thread has already terminated - mark join happens-before + // The thread has already terminated - establish happens-before if let Some(data_race) = data_race { - data_race.thread_joined(self, self.active_thread, joined_thread_id); + data_race.thread_joined(self, joined_thread_id); } } Ok(()) @@ -603,9 +689,9 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { // Sanity check `join_status`. assert!( - self.threads.iter().all(|thread| { - thread.state != ThreadState::Blocked(BlockReason::Join(joined_thread_id)) - }), + self.threads + .iter() + .all(|thread| { !thread.state.is_blocked_on(BlockReason::Join(joined_thread_id)) }), "this thread already has threads waiting for its termination" ); @@ -627,18 +713,15 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { } /// Put the thread into the blocked state. - fn block_thread(&mut self, thread: ThreadId, reason: BlockReason) { - let state = &mut self.threads[thread].state; - assert_eq!(*state, ThreadState::Enabled); - *state = ThreadState::Blocked(reason); - } - - /// Put the blocked thread into the enabled state. - /// Sanity-checks that the thread previously was blocked for the right reason. - fn unblock_thread(&mut self, thread: ThreadId, reason: BlockReason) { - let state = &mut self.threads[thread].state; - assert_eq!(*state, ThreadState::Blocked(reason)); - *state = ThreadState::Enabled; + fn block_thread( + &mut self, + reason: BlockReason, + timeout: Option, + callback: impl UnblockCallback<'tcx> + 'tcx, + ) { + let state = &mut self.threads[self.active_thread].state; + assert!(state.is_enabled()); + *state = ThreadState::Blocked { reason, timeout, callback: Box::new(callback) } } /// Change the active thread to some enabled thread. @@ -649,87 +732,18 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { self.yield_active_thread = true; } - /// Register the given `callback` to be called once the `call_time` passes. - /// - /// The callback will be called with `thread` being the active thread, and - /// the callback may not change the active thread. - fn register_timeout_callback( - &mut self, - thread: ThreadId, - call_time: CallbackTime, - callback: TimeoutCallback<'mir, 'tcx>, - ) { - self.timeout_callbacks - .try_insert(thread, TimeoutCallbackInfo { call_time, callback }) - .unwrap(); - } - - /// Unregister the callback for the `thread`. - fn unregister_timeout_callback_if_exists(&mut self, thread: ThreadId) { - self.timeout_callbacks.remove(&thread); - } - - /// Get a callback that is ready to be called. - fn get_ready_callback( - &mut self, - clock: &Clock, - ) -> Option<(ThreadId, TimeoutCallback<'mir, 'tcx>)> { - // We iterate over all threads in the order of their indices because - // this allows us to have a deterministic scheduler. - for thread in self.threads.indices() { - match self.timeout_callbacks.entry(thread) { - Entry::Occupied(entry) => { - if entry.get().call_time.get_wait_time(clock) == Duration::new(0, 0) { - return Some((thread, entry.remove().callback)); - } + /// Get the wait time for the next timeout, or `None` if no timeout is pending. + fn next_callback_wait_time(&self, clock: &Clock) -> Option { + self.threads + .iter() + .filter_map(|t| { + match &t.state { + ThreadState::Blocked { timeout: Some(timeout), .. } => + Some(timeout.get_wait_time(clock)), + _ => None, } - Entry::Vacant(_) => {} - } - } - None - } - - /// Wakes up threads joining on the active one and deallocates thread-local statics. - /// The `AllocId` that can now be freed are returned. - fn thread_terminated( - &mut self, - mut data_race: Option<&mut data_race::GlobalState>, - current_span: Span, - ) -> Vec> { - let mut free_tls_statics = Vec::new(); - { - let mut thread_local_statics = self.thread_local_alloc_ids.borrow_mut(); - thread_local_statics.retain(|&(_def_id, thread), &mut alloc_id| { - if thread != self.active_thread { - // Keep this static around. - return true; - } - // Delete this static from the map and from memory. - // We cannot free directly here as we cannot use `?` in this context. - free_tls_statics.push(alloc_id); - false - }); - } - // Set the thread into a terminated state in the data-race detector. - if let Some(ref mut data_race) = data_race { - data_race.thread_terminated(self, current_span); - } - // Check if we need to unblock any threads. - let mut joined_threads = vec![]; // store which threads joined, we'll need it - for (i, thread) in self.threads.iter_enumerated_mut() { - if thread.state == ThreadState::Blocked(BlockReason::Join(self.active_thread)) { - // The thread has terminated, mark happens-before edge to joining thread - if data_race.is_some() { - joined_threads.push(i); - } - trace!("unblocking {:?} because {:?} terminated", i, self.active_thread); - thread.state = ThreadState::Enabled; - } - } - for &i in &joined_threads { - data_race.as_mut().unwrap().thread_joined(self, i, self.active_thread); - } - free_tls_statics + }) + .min() } /// Decide which action to take next and on which thread. @@ -740,9 +754,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { /// blocked, terminated, or has explicitly asked to be preempted). fn schedule(&mut self, clock: &Clock) -> InterpResult<'tcx, SchedulingAction> { // This thread and the program can keep going. - if self.threads[self.active_thread].state == ThreadState::Enabled - && !self.yield_active_thread - { + if self.threads[self.active_thread].state.is_enabled() && !self.yield_active_thread { // The currently active thread is still enabled, just continue with it. return Ok(SchedulingAction::ExecuteStep); } @@ -752,9 +764,8 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { // `pthread_cond_timedwait`, "an error is returned if [...] the absolute time specified by // abstime has already been passed at the time of the call". // - let potential_sleep_time = - self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time(clock)).min(); - if potential_sleep_time == Some(Duration::new(0, 0)) { + let potential_sleep_time = self.next_callback_wait_time(clock); + if potential_sleep_time == Some(Duration::ZERO) { return Ok(SchedulingAction::ExecuteTimeoutCallback); } // No callbacks immediately scheduled, pick a regular thread to execute. @@ -772,7 +783,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { .chain(self.threads.iter_enumerated().take(self.active_thread.index())); for (id, thread) in threads { debug_assert_ne!(self.active_thread, id); - if thread.state == ThreadState::Enabled { + if thread.state.is_enabled() { info!( "---------- Now executing on thread `{}` (previous: `{}`) ----------------------------------------", self.get_thread_display_name(id), @@ -783,11 +794,11 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { } } self.yield_active_thread = false; - if self.threads[self.active_thread].state == ThreadState::Enabled { + if self.threads[self.active_thread].state.is_enabled() { return Ok(SchedulingAction::ExecuteStep); } // We have not found a thread to execute. - if self.threads.iter().all(|thread| thread.state == ThreadState::Terminated) { + if self.threads.iter().all(|thread| thread.state.is_terminated()) { unreachable!("all threads terminated without the main thread terminating?!"); } else if let Some(sleep_time) = potential_sleep_time { // All threads are currently blocked, but we have unexecuted @@ -800,35 +811,46 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { } } -impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {} -trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextPrivExt<'tcx> for MiriInterpCx<'tcx> {} +trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { /// Execute a timeout callback on the callback's thread. #[inline] fn run_timeout_callback(&mut self) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let (thread, callback) = if let Some((thread, callback)) = - this.machine.threads.get_ready_callback(&this.machine.clock) - { - (thread, callback) - } else { - // get_ready_callback can return None if the computer's clock - // was shifted after calling the scheduler and before the call - // to get_ready_callback (see issue - // https://github.com/rust-lang/miri/issues/1763). In this case, - // just do nothing, which effectively just returns to the - // scheduler. - return Ok(()); - }; - // This back-and-forth with `set_active_thread` is here because of two - // design decisions: - // 1. Make the caller and not the callback responsible for changing - // thread. - // 2. Make the scheduler the only place that can change the active - // thread. - let old_thread = this.set_active_thread(thread); - callback.call(this)?; - this.set_active_thread(old_thread); - Ok(()) + let mut found_callback = None; + // Find a blocked thread that has timed out. + for (id, thread) in this.machine.threads.threads.iter_enumerated_mut() { + match &thread.state { + ThreadState::Blocked { timeout: Some(timeout), .. } + if timeout.get_wait_time(&this.machine.clock) == Duration::ZERO => + { + let old_state = mem::replace(&mut thread.state, ThreadState::Enabled); + let ThreadState::Blocked { callback, .. } = old_state else { unreachable!() }; + found_callback = Some((id, callback)); + // Run the fallback (after the loop because borrow-checking). + break; + } + _ => {} + } + } + if let Some((thread, callback)) = found_callback { + // This back-and-forth with `set_active_thread` is here because of two + // design decisions: + // 1. Make the caller and not the callback responsible for changing + // thread. + // 2. Make the scheduler the only place that can change the active + // thread. + let old_thread = this.machine.threads.set_active_thread_id(thread); + callback.timeout(this)?; + this.machine.threads.set_active_thread_id(old_thread); + } + // found_callback can remain None if the computer's clock + // was shifted after calling the scheduler and before the call + // to get_ready_callback (see issue + // https://github.com/rust-lang/miri/issues/1763). In this case, + // just do nothing, which effectively just returns to the + // scheduler. + return Ok(()); } #[inline] @@ -846,14 +868,14 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { } // Public interface to thread management. -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Get a thread-specific allocation id for the given thread-local static. /// If needed, allocate a new one. fn get_or_create_thread_local_alloc( &mut self, def_id: DefId, - ) -> InterpResult<'tcx, Pointer> { + ) -> InterpResult<'tcx, StrictPointer> { let this = self.eval_context_mut(); let tcx = this.tcx; if let Some(old_alloc) = this.machine.threads.get_thread_local_alloc_id(def_id) { @@ -867,14 +889,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if tcx.is_foreign_item(def_id) { throw_unsup_format!("foreign thread-local statics are not supported"); } - let allocation = this.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?; - let mut allocation = allocation.inner().clone(); + let alloc = this.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?; + // We make a full copy of this allocation. + let mut alloc = + alloc.inner().adjust_from_tcx(&this.tcx, |ptr| this.global_root_pointer(ptr))?; // This allocation will be deallocated when the thread dies, so it is not in read-only memory. - allocation.mutability = Mutability::Mut; + alloc.mutability = Mutability::Mut; // Create a fresh allocation with this content. - let new_alloc = this.allocate_raw_ptr(allocation, MiriMemoryKind::Tls.into())?; - this.machine.threads.set_thread_local_alloc(def_id, new_alloc); - Ok(new_alloc) + let ptr = this.allocate_raw_ptr(alloc, MiriMemoryKind::Tls.into())?; + this.machine.threads.set_thread_local_alloc(def_id, ptr); + Ok(ptr) } } @@ -882,10 +906,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[inline] fn start_regular_thread( &mut self, - thread: Option>, - start_routine: Pointer>, + thread: Option>, + start_routine: Pointer, start_abi: Abi, - func_arg: ImmTy<'tcx, Provenance>, + func_arg: ImmTy<'tcx>, ret_layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, ThreadId> { let this = self.eval_context_mut(); @@ -911,7 +935,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Finally switch to new thread so that we can push the first stackframe. // After this all accesses will be treated as occurring in the new thread. - let old_thread_id = this.set_active_thread(new_thread_id); + let old_thread_id = this.machine.threads.set_active_thread_id(new_thread_id); // Perform the function pointer load in the new thread frame. let instance = this.get_ptr_fn(start_routine)?.as_instance()?; @@ -930,11 +954,125 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { )?; // Restore the old active thread frame. - this.set_active_thread(old_thread_id); + this.machine.threads.set_active_thread_id(old_thread_id); Ok(new_thread_id) } + /// Handles thread termination of the active thread: wakes up threads joining on this one, + /// and deals with the thread's thread-local statics according to `tls_alloc_action`. + /// + /// This is called by the eval loop when a thread's on_stack_empty returns `Ready`. + fn terminate_active_thread(&mut self, tls_alloc_action: TlsAllocAction) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + // Mark thread as terminated. + let thread = this.active_thread_mut(); + assert!(thread.stack.is_empty(), "only threads with an empty stack can be terminated"); + thread.state = ThreadState::Terminated; + if let Some(ref mut data_race) = this.machine.data_race { + data_race.thread_terminated(&this.machine.threads); + } + // Deallocate TLS. + let gone_thread = this.active_thread(); + { + let mut free_tls_statics = Vec::new(); + this.machine.threads.thread_local_allocs.retain(|&(_def_id, thread), &mut alloc_id| { + if thread != gone_thread { + // A different thread, keep this static around. + return true; + } + // Delete this static from the map and from memory. + // We cannot free directly here as we cannot use `?` in this context. + free_tls_statics.push(alloc_id); + false + }); + // Now free the TLS statics. + for ptr in free_tls_statics { + match tls_alloc_action { + TlsAllocAction::Deallocate => + this.deallocate_ptr(ptr.into(), None, MiriMemoryKind::Tls.into())?, + TlsAllocAction::Leak => + if let Some(alloc) = ptr.provenance.get_alloc_id() { + trace!( + "Thread-local static leaked and stored as static root: {:?}", + alloc + ); + this.machine.static_roots.push(alloc); + }, + } + } + } + // Unblock joining threads. + let unblock_reason = BlockReason::Join(gone_thread); + let threads = &this.machine.threads.threads; + let joining_threads = threads + .iter_enumerated() + .filter(|(_, thread)| thread.state.is_blocked_on(unblock_reason)) + .map(|(id, _)| id) + .collect::>(); + for thread in joining_threads { + this.unblock_thread(thread, unblock_reason)?; + } + + Ok(()) + } + + /// Block the current thread, with an optional timeout. + /// The callback will be invoked when the thread gets unblocked. + #[inline] + fn block_thread( + &mut self, + reason: BlockReason, + timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>, + callback: impl UnblockCallback<'tcx> + 'tcx, + ) { + let this = self.eval_context_mut(); + let timeout = timeout.map(|(clock, anchor, duration)| { + let anchor = match clock { + TimeoutClock::RealTime => { + assert!( + this.machine.communicate(), + "cannot have `RealTime` timeout with isolation enabled!" + ); + Timeout::RealTime(match anchor { + TimeoutAnchor::Absolute => SystemTime::UNIX_EPOCH, + TimeoutAnchor::Relative => SystemTime::now(), + }) + } + TimeoutClock::Monotonic => + Timeout::Monotonic(match anchor { + TimeoutAnchor::Absolute => this.machine.clock.epoch(), + TimeoutAnchor::Relative => this.machine.clock.now(), + }), + }; + anchor.add_lossy(duration) + }); + this.machine.threads.block_thread(reason, timeout, callback); + } + + /// Put the blocked thread into the enabled state. + /// Sanity-checks that the thread previously was blocked for the right reason. + fn unblock_thread(&mut self, thread: ThreadId, reason: BlockReason) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let old_state = + mem::replace(&mut this.machine.threads.threads[thread].state, ThreadState::Enabled); + let callback = match old_state { + ThreadState::Blocked { reason: actual_reason, callback, .. } => { + assert_eq!( + reason, actual_reason, + "unblock_thread: thread was blocked for the wrong reason" + ); + callback + } + _ => panic!("unblock_thread: thread was not blocked"), + }; + // The callback must be executed in the previously blocked thread. + let old_thread = this.machine.threads.set_active_thread_id(thread); + callback.unblock(this)?; + this.machine.threads.set_active_thread_id(old_thread); + Ok(()) + } + #[inline] fn detach_thread( &mut self, @@ -962,25 +1100,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } #[inline] - fn set_active_thread(&mut self, thread_id: ThreadId) -> ThreadId { - let this = self.eval_context_mut(); - this.machine.threads.set_active_thread_id(thread_id) - } - - #[inline] - fn get_active_thread(&self) -> ThreadId { + fn active_thread(&self) -> ThreadId { let this = self.eval_context_ref(); - this.machine.threads.get_active_thread_id() + this.machine.threads.active_thread() } #[inline] - fn active_thread_mut(&mut self) -> &mut Thread<'mir, 'tcx> { + fn active_thread_mut(&mut self) -> &mut Thread<'tcx> { let this = self.eval_context_mut(); this.machine.threads.active_thread_mut() } #[inline] - fn active_thread_ref(&self) -> &Thread<'mir, 'tcx> { + fn active_thread_ref(&self) -> &Thread<'tcx> { let this = self.eval_context_ref(); this.machine.threads.active_thread_ref() } @@ -1004,15 +1136,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } #[inline] - fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>] { + fn active_thread_stack<'a>(&'a self) -> &'a [Frame<'tcx, Provenance, FrameExtra<'tcx>>] { let this = self.eval_context_ref(); this.machine.threads.active_thread_stack() } #[inline] - fn active_thread_stack_mut( - &mut self, - ) -> &mut Vec>> { + fn active_thread_stack_mut<'a>( + &'a mut self, + ) -> &'a mut Vec>> { let this = self.eval_context_mut(); this.machine.threads.active_thread_stack_mut() } @@ -1027,21 +1159,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[inline] fn get_thread_name<'c>(&'c self, thread: ThreadId) -> Option<&[u8]> where - 'mir: 'c, + 'tcx: 'c, { self.eval_context_ref().machine.threads.get_thread_name(thread) } - #[inline] - fn block_thread(&mut self, thread: ThreadId, reason: BlockReason) { - self.eval_context_mut().machine.threads.block_thread(thread, reason); - } - - #[inline] - fn unblock_thread(&mut self, thread: ThreadId, reason: BlockReason) { - self.eval_context_mut().machine.threads.unblock_thread(thread, reason); - } - #[inline] fn yield_active_thread(&mut self) { self.eval_context_mut().machine.threads.yield_active_thread(); @@ -1057,26 +1179,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - #[inline] - fn register_timeout_callback( - &mut self, - thread: ThreadId, - call_time: CallbackTime, - callback: TimeoutCallback<'mir, 'tcx>, - ) { - let this = self.eval_context_mut(); - if !this.machine.communicate() && matches!(call_time, CallbackTime::RealTime(..)) { - panic!("cannot have `RealTime` callback with isolation enabled!") - } - this.machine.threads.register_timeout_callback(thread, call_time, callback); - } - - #[inline] - fn unregister_timeout_callback_if_exists(&mut self, thread: ThreadId) { - let this = self.eval_context_mut(); - this.machine.threads.unregister_timeout_callback_if_exists(thread); - } - /// Run the core interpreter loop. Returns only when an interrupt occurs (an error or program /// termination). fn run_threads(&mut self) -> InterpResult<'tcx, !> { @@ -1106,32 +1208,4 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } } - - /// Handles thread termination of the active thread: wakes up threads joining on this one, - /// and deals with the thread's thread-local statics according to `tls_alloc_action`. - /// - /// This is called by the eval loop when a thread's on_stack_empty returns `Ready`. - #[inline] - fn terminate_active_thread(&mut self, tls_alloc_action: TlsAllocAction) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - let thread = this.active_thread_mut(); - assert!(thread.stack.is_empty(), "only threads with an empty stack can be terminated"); - thread.state = ThreadState::Terminated; - - let current_span = this.machine.current_span(); - let thread_local_allocations = - this.machine.threads.thread_terminated(this.machine.data_race.as_mut(), current_span); - for ptr in thread_local_allocations { - match tls_alloc_action { - TlsAllocAction::Deallocate => - this.deallocate_ptr(ptr.into(), None, MiriMemoryKind::Tls.into())?, - TlsAllocAction::Leak => - if let Some(alloc) = ptr.provenance.get_alloc_id() { - trace!("Thread-local static leaked and stored as static root: {:?}", alloc); - this.machine.static_roots.push(alloc); - }, - } - } - Ok(()) - } } diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs index 574962c48d43..e92eaa8f91f7 100644 --- a/src/tools/miri/src/concurrency/weak_memory.rs +++ b/src/tools/miri/src/concurrency/weak_memory.rs @@ -48,7 +48,7 @@ //! One consequence of this difference is that safe/sound Rust allows for more operations on atomic locations //! than the C++20 atomic API was intended to allow, such as non-atomically accessing //! a previously atomically accessed location, or accessing previously atomically accessed locations with a differently sized operation -//! (such as accessing the top 16 bits of an AtomicU32). These scenarios are generally undiscussed in formalisations of C++ memory model. +//! (such as accessing the top 16 bits of an AtomicU32). These scenarios are generally undiscussed in formalizations of C++ memory model. //! In Rust, these operations can only be done through a `&mut AtomicFoo` reference or one derived from it, therefore these operations //! can only happen after all previous accesses on the same locations. This implementation is adapted to allow these operations. //! A mixed atomicity read that races with writes, or a write that races with reads or writes will still cause UBs to be thrown. @@ -148,7 +148,7 @@ struct StoreElement { // FIXME: this means the store must be fully initialized; // we will have to change this if we want to support atomics on // (partially) uninitialized data. - val: Scalar, + val: Scalar, /// Metadata about loads from this store element, /// behind a RefCell to keep load op take &self @@ -197,7 +197,7 @@ impl StoreBufferAlloc { fn get_or_create_store_buffer<'tcx>( &self, range: AllocRange, - init: Scalar, + init: Scalar, ) -> InterpResult<'tcx, Ref<'_, StoreBuffer>> { let access_type = self.store_buffers.borrow().access_type(range); let pos = match access_type { @@ -222,7 +222,7 @@ impl StoreBufferAlloc { fn get_or_create_store_buffer_mut<'tcx>( &mut self, range: AllocRange, - init: Scalar, + init: Scalar, ) -> InterpResult<'tcx, &mut StoreBuffer> { let buffers = self.store_buffers.get_mut(); let access_type = buffers.access_type(range); @@ -243,8 +243,8 @@ impl StoreBufferAlloc { } } -impl<'mir, 'tcx: 'mir> StoreBuffer { - fn new(init: Scalar) -> Self { +impl<'tcx> StoreBuffer { + fn new(init: Scalar) -> Self { let mut buffer = VecDeque::new(); buffer.reserve(STORE_BUFFER_LIMIT); let mut ret = Self { buffer }; @@ -265,7 +265,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer { fn read_from_last_store( &self, global: &DataRaceState, - thread_mgr: &ThreadManager<'_, '_>, + thread_mgr: &ThreadManager<'_>, is_seqcst: bool, ) { let store_elem = self.buffer.back(); @@ -278,11 +278,11 @@ impl<'mir, 'tcx: 'mir> StoreBuffer { fn buffered_read( &self, global: &DataRaceState, - thread_mgr: &ThreadManager<'_, '_>, + thread_mgr: &ThreadManager<'_>, is_seqcst: bool, rng: &mut (impl rand::Rng + ?Sized), validate: impl FnOnce() -> InterpResult<'tcx>, - ) -> InterpResult<'tcx, (Scalar, LoadRecency)> { + ) -> InterpResult<'tcx, (Scalar, LoadRecency)> { // Having a live borrow to store_buffer while calling validate_atomic_load is fine // because the race detector doesn't touch store_buffer @@ -307,9 +307,9 @@ impl<'mir, 'tcx: 'mir> StoreBuffer { fn buffered_write( &mut self, - val: Scalar, + val: Scalar, global: &DataRaceState, - thread_mgr: &ThreadManager<'_, '_>, + thread_mgr: &ThreadManager<'_>, is_seqcst: bool, ) -> InterpResult<'tcx> { let (index, clocks) = global.active_thread_state(thread_mgr); @@ -408,7 +408,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer { /// ATOMIC STORE IMPL in the paper (except we don't need the location's vector clock) fn store_impl( &mut self, - val: Scalar, + val: Scalar, index: VectorIdx, thread_clock: &VClock, is_seqcst: bool, @@ -450,12 +450,7 @@ impl StoreElement { /// buffer regardless of subsequent loads by the same thread; if the earliest load of another /// thread doesn't happen before the current one, then no subsequent load by the other thread /// can happen before the current one. - fn load_impl( - &self, - index: VectorIdx, - clocks: &ThreadClockSet, - is_seqcst: bool, - ) -> Scalar { + fn load_impl(&self, index: VectorIdx, clocks: &ThreadClockSet, is_seqcst: bool) -> Scalar { let mut load_info = self.load_info.borrow_mut(); load_info.sc_loaded |= is_seqcst; let _ = load_info.timestamps.try_insert(index, clocks.clock[index]); @@ -463,16 +458,14 @@ impl StoreElement { } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: - crate::MiriInterpCxExt<'mir, 'tcx> -{ +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn buffered_atomic_rmw( &mut self, - new_val: Scalar, - place: &MPlaceTy<'tcx, Provenance>, + new_val: Scalar, + place: &MPlaceTy<'tcx>, atomic: AtomicRwOrd, - init: Scalar, + init: Scalar, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr())?; @@ -495,11 +488,11 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: fn buffered_atomic_read( &self, - place: &MPlaceTy<'tcx, Provenance>, + place: &MPlaceTy<'tcx>, atomic: AtomicReadOrd, - latest_in_mo: Scalar, + latest_in_mo: Scalar, validate: impl FnOnce() -> InterpResult<'tcx>, - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_ref(); if let Some(global) = &this.machine.data_race { let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr())?; @@ -536,10 +529,10 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: fn buffered_atomic_write( &mut self, - val: Scalar, - dest: &MPlaceTy<'tcx, Provenance>, + val: Scalar, + dest: &MPlaceTy<'tcx>, atomic: AtomicWriteOrd, - init: Scalar, + init: Scalar, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(dest.ptr())?; @@ -581,9 +574,9 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: /// to perform load_impl on the latest store element fn perform_read_on_buffered_latest( &self, - place: &MPlaceTy<'tcx, Provenance>, + place: &MPlaceTy<'tcx>, atomic: AtomicReadOrd, - init: Scalar, + init: Scalar, ) -> InterpResult<'tcx> { let this = self.eval_context_ref(); diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index cb5ed27b6cf1..12fb76f39724 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -42,7 +42,7 @@ pub enum TerminationInfo { }, DataRace { involves_non_atomic: bool, - ptr: Pointer, + ptr: interpret::Pointer, op1: RacingOp, op2: RacingOp, extra: Option<&'static str>, @@ -128,7 +128,7 @@ pub enum NonHaltingDiagnostic { details: bool, }, WeakMemoryOutdatedLoad { - ptr: Pointer>, + ptr: Pointer, }, } @@ -144,7 +144,7 @@ pub enum DiagLevel { /// be pointing to a problem in the Rust runtime itself, and do not prune it at all. pub fn prune_stacktrace<'tcx>( mut stacktrace: Vec>, - machine: &MiriMachine<'_, 'tcx>, + machine: &MiriMachine<'tcx>, ) -> (Vec>, bool) { match machine.backtrace_style { BacktraceStyle::Off => { @@ -201,8 +201,8 @@ pub fn prune_stacktrace<'tcx>( /// Emit a custom diagnostic without going through the miri-engine machinery. /// /// Returns `Some` if this was regular program termination with a given exit code and a `bool` indicating whether a leak check should happen; `None` otherwise. -pub fn report_error<'tcx, 'mir>( - ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, +pub fn report_error<'tcx>( + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, e: InterpErrorInfo<'tcx>, ) -> Option<(i64, bool)> { use InterpError::*; @@ -411,7 +411,7 @@ pub fn report_error<'tcx, 'mir>( vec![], helps, &stacktrace, - Some(ecx.get_active_thread()), + Some(ecx.active_thread()), &ecx.machine, ); @@ -419,7 +419,7 @@ pub fn report_error<'tcx, 'mir>( if show_all_threads { for (thread, stack) in ecx.machine.threads.all_stacks() { - if thread != ecx.get_active_thread() { + if thread != ecx.active_thread() { let stacktrace = Frame::generate_stacktrace_from_stack(stack); let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine); any_pruned |= was_pruned; @@ -457,9 +457,9 @@ pub fn report_error<'tcx, 'mir>( None } -pub fn report_leaks<'mir, 'tcx>( - ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, - leaks: Vec<(AllocId, MemoryKind, Allocation>)>, +pub fn report_leaks<'tcx>( + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + leaks: Vec<(AllocId, MemoryKind, Allocation, MiriAllocBytes>)>, ) { let mut any_pruned = false; for (id, kind, mut alloc) in leaks { @@ -504,7 +504,7 @@ pub fn report_msg<'tcx>( helps: Vec<(Option, String)>, stacktrace: &[FrameInfo<'tcx>], thread: Option, - machine: &MiriMachine<'_, 'tcx>, + machine: &MiriMachine<'tcx>, ) { let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span); let sess = machine.tcx.sess; @@ -566,7 +566,7 @@ pub fn report_msg<'tcx>( let is_local = machine.is_local(frame_info); // No span for non-local frames and the first frame (which is the error site). if is_local && idx > 0 { - err.subdiagnostic(err.dcx, frame_info.as_note(machine.tcx)); + err.subdiagnostic(frame_info.as_note(machine.tcx)); } else { let sm = sess.source_map(); let span = sm.span_to_embeddable_string(frame_info.span); @@ -577,7 +577,7 @@ pub fn report_msg<'tcx>( err.emit(); } -impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { +impl<'tcx> MiriMachine<'tcx> { pub fn emit_diagnostic(&self, e: NonHaltingDiagnostic) { use NonHaltingDiagnostic::*; @@ -684,14 +684,14 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { notes, helps, &stacktrace, - Some(self.threads.get_active_thread_id()), + Some(self.threads.active_thread()), self, ); } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emit_diagnostic(&self, e: NonHaltingDiagnostic) { let this = self.eval_context_ref(); this.machine.emit_diagnostic(e); @@ -712,7 +712,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { vec![], vec![], &stacktrace, - Some(this.get_active_thread()), + Some(this.active_thread()), &this.machine, ); } diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 78c092faf909..35f7f43f123f 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -214,7 +214,7 @@ enum MainThreadState<'tcx> { impl<'tcx> MainThreadState<'tcx> { fn on_main_stack_empty( &mut self, - this: &mut MiriInterpCx<'_, 'tcx>, + this: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, Poll<()>> { use MainThreadState::*; match self { @@ -263,12 +263,12 @@ impl<'tcx> MainThreadState<'tcx> { /// Returns a freshly created `InterpCx`. /// Public because this is also used by `priroda`. -pub fn create_ecx<'mir, 'tcx: 'mir>( +pub fn create_ecx<'tcx>( tcx: TyCtxt<'tcx>, entry_id: DefId, entry_type: EntryFnType, config: &MiriConfig, -) -> InterpResult<'tcx, InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>> { +) -> InterpResult<'tcx, InterpCx<'tcx, MiriMachine<'tcx>>> { let param_env = ty::ParamEnv::reveal_all(); let layout_cx = LayoutCx { tcx, param_env }; let mut ecx = diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 4050ae3c4bf9..843aff024958 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -7,7 +7,7 @@ use std::time::Duration; use rand::RngCore; -use rustc_apfloat::ieee::{Double, Single}; +use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_apfloat::Float; use rustc_hir::{ def::{DefKind, Namespace}, @@ -234,8 +234,8 @@ impl ToSoft for f32 { } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Checks if the given crate/module exists. fn have_module(&self, path: &[&str]) -> bool { try_resolve_did(*self.eval_context_ref().tcx, path, None).is_some() @@ -255,7 +255,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Evaluates the scalar at the specified path. - fn eval_path(&self, path: &[&str]) -> OpTy<'tcx, Provenance> { + fn eval_path(&self, path: &[&str]) -> OpTy<'tcx> { let this = self.eval_context_ref(); let instance = this.resolve_path(path, Namespace::ValueNS); // We don't give a span -- this isn't actually used directly by the program anyway. @@ -264,7 +264,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }); const_val.into() } - fn eval_path_scalar(&self, path: &[&str]) -> Scalar { + fn eval_path_scalar(&self, path: &[&str]) -> Scalar { let this = self.eval_context_ref(); let val = this.eval_path(path); this.read_scalar(&val) @@ -272,7 +272,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Helper function to get a `libc` constant as a `Scalar`. - fn eval_libc(&self, name: &str) -> Scalar { + fn eval_libc(&self, name: &str) -> Scalar { self.eval_path_scalar(&["libc", name]) } @@ -293,7 +293,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Helper function to get a `windows` constant as a `Scalar`. - fn eval_windows(&self, module: &str, name: &str) -> Scalar { + fn eval_windows(&self, module: &str, name: &str) -> Scalar { self.eval_context_ref().eval_path_scalar(&["std", "sys", "pal", "windows", module, name]) } @@ -374,7 +374,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let val = if dest.layout().abi.is_signed() { Scalar::from_int(i, dest.layout().size) } else { - Scalar::from_uint(u64::try_from(i.into()).unwrap(), dest.layout().size) + // `unwrap` can only fail here if `i` is negative + Scalar::from_uint(u128::try_from(i.into()).unwrap(), dest.layout().size) }; self.eval_context_mut().write_scalar(val, dest) } @@ -413,12 +414,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Test if this pointer equals 0. - fn ptr_is_null(&self, ptr: Pointer>) -> InterpResult<'tcx, bool> { + fn ptr_is_null(&self, ptr: Pointer) -> InterpResult<'tcx, bool> { Ok(ptr.addr().bytes() == 0) } /// Generate some random bytes, and write them to `dest`. - fn gen_random(&mut self, ptr: Pointer>, len: u64) -> InterpResult<'tcx> { + fn gen_random(&mut self, ptr: Pointer, len: u64) -> InterpResult<'tcx> { // Some programs pass in a null pointer and a length of 0 // to their platform's random-generation function (e.g. getrandom()) // on Linux. For compatibility with these programs, we don't perform @@ -453,7 +454,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { f: ty::Instance<'tcx>, caller_abi: Abi, args: &[Immediate], - dest: Option<&MPlaceTy<'tcx, Provenance>>, + dest: Option<&MPlaceTy<'tcx>>, stack_pop: StackPopCleanup, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -501,7 +502,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// The range is relative to `place`. fn visit_freeze_sensitive( &self, - place: &MPlaceTy<'tcx, Provenance>, + place: &MPlaceTy<'tcx>, size: Size, mut action: impl FnMut(AllocRange, bool) -> InterpResult<'tcx>, ) -> InterpResult<'tcx> { @@ -520,8 +521,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let mut cur_addr = start_addr; // Called when we detected an `UnsafeCell` at the given offset and size. // Calls `action` and advances `cur_ptr`. - let mut unsafe_cell_action = |unsafe_cell_ptr: &Pointer>, - unsafe_cell_size: Size| { + let mut unsafe_cell_action = |unsafe_cell_ptr: &Pointer, unsafe_cell_size: Size| { // We assume that we are given the fields in increasing offset order, // and nothing else changes. let unsafe_cell_addr = unsafe_cell_ptr.addr(); @@ -573,23 +573,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Visiting the memory covered by a `MemPlace`, being aware of /// whether we are inside an `UnsafeCell` or not. - struct UnsafeCellVisitor<'ecx, 'mir, 'tcx, F> + struct UnsafeCellVisitor<'ecx, 'tcx, F> where - F: FnMut(&MPlaceTy<'tcx, Provenance>) -> InterpResult<'tcx>, + F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>, { - ecx: &'ecx MiriInterpCx<'mir, 'tcx>, + ecx: &'ecx MiriInterpCx<'tcx>, unsafe_cell_action: F, } - impl<'ecx, 'mir, 'tcx: 'mir, F> ValueVisitor<'mir, 'tcx, MiriMachine<'mir, 'tcx>> - for UnsafeCellVisitor<'ecx, 'mir, 'tcx, F> + impl<'ecx, 'tcx, F> ValueVisitor<'tcx, MiriMachine<'tcx>> for UnsafeCellVisitor<'ecx, 'tcx, F> where - F: FnMut(&MPlaceTy<'tcx, Provenance>) -> InterpResult<'tcx>, + F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>, { - type V = MPlaceTy<'tcx, Provenance>; + type V = MPlaceTy<'tcx>; #[inline(always)] - fn ecx(&self) -> &MiriInterpCx<'mir, 'tcx> { + fn ecx(&self) -> &MiriInterpCx<'tcx> { self.ecx } @@ -604,7 +603,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } // Hook to detect `UnsafeCell`. - fn visit_value(&mut self, v: &MPlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { + fn visit_value(&mut self, v: &MPlaceTy<'tcx>) -> InterpResult<'tcx> { trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty); let is_unsafe_cell = match v.layout.ty.kind() { ty::Adt(adt, _) => @@ -650,7 +649,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn visit_union( &mut self, - _v: &MPlaceTy<'tcx, Provenance>, + _v: &MPlaceTy<'tcx>, _fields: NonZero, ) -> InterpResult<'tcx> { bug!("we should have already handled unions in `visit_value`") @@ -721,7 +720,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Get last error variable as a place, lazily allocating thread-local storage for it if /// necessary. - fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { + fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx>> { let this = self.eval_context_mut(); if let Some(errno_place) = this.active_thread_ref().last_error.as_ref() { Ok(errno_place.clone()) @@ -736,14 +735,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Sets the last error variable. - fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { + fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let errno_place = this.last_error_place()?; this.write_scalar(scalar, &errno_place) } /// Gets the last error variable. - fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { + fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let errno_place = this.last_error_place()?; this.read_scalar(&errno_place) @@ -751,7 +750,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// This function tries to produce the most similar OS error from the `std::io::ErrorKind` /// as a platform-specific errnum. - fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar> { + fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_ref(); let target = &this.tcx.sess.target; if target.families.iter().any(|f| f == "unix") { @@ -780,7 +779,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[allow(clippy::needless_return)] fn try_errnum_to_io_error( &self, - errnum: Scalar, + errnum: Scalar, ) -> InterpResult<'tcx, Option> { let this = self.eval_context_ref(); let target = &this.tcx.sess.target; @@ -837,7 +836,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &self, op: &impl Readable<'tcx, Provenance>, layout: TyAndLayout<'tcx>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { let this = self.eval_context_ref(); let ptr = this.read_pointer(op)?; Ok(this.ptr_to_mplace(ptr, layout)) @@ -850,7 +849,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { offset: u64, base_layout: TyAndLayout<'tcx>, value_layout: TyAndLayout<'tcx>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { let this = self.eval_context_ref(); let op_place = this.deref_pointer_as(op, base_layout)?; let offset = Size::from_bytes(offset); @@ -867,7 +866,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { offset: u64, base_layout: TyAndLayout<'tcx>, value_layout: TyAndLayout<'tcx>, - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_ref(); let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?; this.read_scalar(&value_place) @@ -877,7 +876,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, op: &impl Readable<'tcx, Provenance>, offset: u64, - value: impl Into>, + value: impl Into, base_layout: TyAndLayout<'tcx>, value_layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, ()> { @@ -889,10 +888,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Parse a `timespec` struct and return it as a `std::time::Duration`. It returns `None` /// if the value in the `timespec` struct is invalid. Some libc functions will return /// `EINVAL` in this case. - fn read_timespec( - &mut self, - tp: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Option> { + fn read_timespec(&mut self, tp: &MPlaceTy<'tcx>) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); let seconds_place = this.project_field(tp, 0)?; let seconds_scalar = this.read_scalar(&seconds_place)?; @@ -915,12 +911,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Read bytes from a byte slice. - fn read_byte_slice<'a>( - &'a self, - slice: &ImmTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, &'a [u8]> + fn read_byte_slice<'a>(&'a self, slice: &ImmTy<'tcx>) -> InterpResult<'tcx, &'a [u8]> where - 'mir: 'a, + 'tcx: 'a, { let this = self.eval_context_ref(); let (ptr, len) = slice.to_scalar_pair(); @@ -931,9 +924,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Read a sequence of bytes until the first null terminator. - fn read_c_str<'a>(&'a self, ptr: Pointer>) -> InterpResult<'tcx, &'a [u8]> + fn read_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, &'a [u8]> where - 'mir: 'a, + 'tcx: 'a, { let this = self.eval_context_ref(); let size1 = Size::from_bytes(1); @@ -964,7 +957,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn write_c_str( &mut self, c_str: &[u8], - ptr: Pointer>, + ptr: Pointer, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null @@ -983,7 +976,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// until the first null terminator. fn read_c_str_with_char_size( &self, - mut ptr: Pointer>, + mut ptr: Pointer, size: Size, align: Align, ) -> InterpResult<'tcx, Vec> @@ -1015,7 +1008,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Read a sequence of u16 until the first null terminator. - fn read_wide_str(&self, ptr: Pointer>) -> InterpResult<'tcx, Vec> { + fn read_wide_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec> { self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap()) } @@ -1028,7 +1021,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn write_wide_str( &mut self, wide_str: &[u16], - ptr: Pointer>, + ptr: Pointer, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required @@ -1053,7 +1046,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Read a sequence of wchar_t until the first null terminator. /// Always returns a `Vec` no matter the size of `wchar_t`. - fn read_wchar_t_str(&self, ptr: Pointer>) -> InterpResult<'tcx, Vec> { + fn read_wchar_t_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec> { let this = self.eval_context_ref(); let wchar_t = this.libc_ty_layout("wchar_t"); self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi) @@ -1139,17 +1132,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { abi: Abi, exp_abi: Abi, link_name: Symbol, - args: &'a [OpTy<'tcx, Provenance>], - ) -> InterpResult<'tcx, &'a [OpTy<'tcx, Provenance>; N]> + args: &'a [OpTy<'tcx>], + ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> where - &'a [OpTy<'tcx, Provenance>; N]: TryFrom<&'a [OpTy<'tcx, Provenance>]>, + &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>, { self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?; check_arg_count(args) } /// Mark a machine allocation that was just created as immutable. - fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx, Provenance>) { + fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) { let this = self.eval_context_mut(); // This got just allocated, so there definitely is a pointer here. let provenance = mplace.ptr().into_pointer_or_addr().unwrap().provenance; @@ -1169,18 +1162,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Returns `None` if `f` is NaN or out of range. fn float_to_int_checked( &self, - src: &ImmTy<'tcx, Provenance>, + src: &ImmTy<'tcx>, cast_to: TyAndLayout<'tcx>, round: rustc_apfloat::Round, - ) -> InterpResult<'tcx, Option>> { + ) -> InterpResult<'tcx, Option>> { let this = self.eval_context_ref(); fn float_to_int_inner<'tcx, F: rustc_apfloat::Float>( - this: &MiriInterpCx<'_, 'tcx>, + this: &MiriInterpCx<'tcx>, src: F, cast_to: TyAndLayout<'tcx>, round: rustc_apfloat::Round, - ) -> (Scalar, rustc_apfloat::Status) { + ) -> (Scalar, rustc_apfloat::Status) { let int_size = cast_to.layout.size; match cast_to.ty.kind() { // Unsigned @@ -1208,12 +1201,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; let (val, status) = match fty { - FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F16 => + float_to_int_inner::(this, src.to_scalar().to_f16()?, cast_to, round), FloatTy::F32 => float_to_int_inner::(this, src.to_scalar().to_f32()?, cast_to, round), FloatTy::F64 => float_to_int_inner::(this, src.to_scalar().to_f64()?, cast_to, round), - FloatTy::F128 => unimplemented!("f16_f128"), + FloatTy::F128 => + float_to_int_inner::(this, src.to_scalar().to_f128()?, cast_to, round), }; if status.intersects( @@ -1268,10 +1263,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Lookup an array of immediates stored as a linker section of name `name`. - fn lookup_link_section( - &mut self, - name: &str, - ) -> InterpResult<'tcx, Vec>> { + fn lookup_link_section(&mut self, name: &str) -> InterpResult<'tcx, Vec>> { let this = self.eval_context_mut(); let tcx = this.tcx.tcx; @@ -1299,7 +1291,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } -impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { +impl<'tcx> MiriMachine<'tcx> { /// Get the current span in the topmost function which is workspace-local and not /// `#[track_caller]`. /// This function is backed by a cache, and can be assumed to be very fast. @@ -1321,7 +1313,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { self.stack()[frame_idx].current_span() } - fn stack(&self) -> &[Frame<'mir, 'tcx, Provenance, machine::FrameExtra<'tcx>>] { + fn stack(&self) -> &[Frame<'tcx, Provenance, machine::FrameExtra<'tcx>>] { self.threads.active_thread_stack() } @@ -1330,7 +1322,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { } /// This is the source of truth for the `is_user_relevant` flag in our `FrameExtra`. - pub fn is_user_relevant(&self, frame: &Frame<'mir, 'tcx, Provenance>) -> bool { + pub fn is_user_relevant(&self, frame: &Frame<'tcx, Provenance>) -> bool { let def_id = frame.instance.def_id(); (def_id.is_local() || self.local_crates.contains(&def_id.krate)) && !frame.instance.def.requires_caller_location(self.tcx) @@ -1339,10 +1331,10 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { /// Check that the number of args is what we expect. pub fn check_arg_count<'a, 'tcx, const N: usize>( - args: &'a [OpTy<'tcx, Provenance>], -) -> InterpResult<'tcx, &'a [OpTy<'tcx, Provenance>; N]> + args: &'a [OpTy<'tcx>], +) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> where - &'a [OpTy<'tcx, Provenance>; N]: TryFrom<&'a [OpTy<'tcx, Provenance>]>, + &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>, { if let Ok(ops) = args.try_into() { return Ok(ops); @@ -1375,7 +1367,7 @@ pub fn get_local_crates(tcx: TyCtxt<'_>) -> Vec { local_crates } -pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar { +pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar { // SIMD uses all-1 as pattern for "true". In two's complement, // -1 has all its bits set to one and `from_int` will truncate or // sign-extend it to `size` as required. @@ -1383,7 +1375,7 @@ pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar { Scalar::from_int(val, size) } -pub(crate) fn simd_element_to_bool(elem: ImmTy<'_, Provenance>) -> InterpResult<'_, bool> { +pub(crate) fn simd_element_to_bool(elem: ImmTy<'_>) -> InterpResult<'_, bool> { let val = elem.to_scalar().to_int(elem.layout.size)?; Ok(match val { 0 => false, diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs index 40f6b8a64efa..d76f622e84ba 100644 --- a/src/tools/miri/src/intrinsics/atomic.rs +++ b/src/tools/miri/src/intrinsics/atomic.rs @@ -4,128 +4,128 @@ use crate::*; use helpers::check_arg_count; pub enum AtomicOp { - /// The `bool` indicates whether the result of the operation should be negated - /// (must be a boolean-typed operation). + /// The `bool` indicates whether the result of the operation should be negated (`UnOp::Not`, + /// must be a boolean-typed operation). MirOp(mir::BinOp, bool), Max, Min, } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Calls the atomic intrinsic `intrinsic`; the `atomic_` prefix has already been removed. /// Returns `Ok(true)` if the intrinsic was handled. fn emulate_atomic_intrinsic( &mut self, intrinsic_name: &str, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); let intrinsic_structure: Vec<_> = intrinsic_name.split('_').collect(); - fn read_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicReadOrd> { - Ok(match ord { + fn read_ord(ord: &str) -> AtomicReadOrd { + match ord { "seqcst" => AtomicReadOrd::SeqCst, "acquire" => AtomicReadOrd::Acquire, "relaxed" => AtomicReadOrd::Relaxed, - _ => throw_unsup_format!("unsupported read ordering `{ord}`"), - }) + _ => panic!("invalid read ordering `{ord}`"), + } } - fn write_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicWriteOrd> { - Ok(match ord { + fn write_ord(ord: &str) -> AtomicWriteOrd { + match ord { "seqcst" => AtomicWriteOrd::SeqCst, "release" => AtomicWriteOrd::Release, "relaxed" => AtomicWriteOrd::Relaxed, - _ => throw_unsup_format!("unsupported write ordering `{ord}`"), - }) + _ => panic!("invalid write ordering `{ord}`"), + } } - fn rw_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicRwOrd> { - Ok(match ord { + fn rw_ord(ord: &str) -> AtomicRwOrd { + match ord { "seqcst" => AtomicRwOrd::SeqCst, "acqrel" => AtomicRwOrd::AcqRel, "acquire" => AtomicRwOrd::Acquire, "release" => AtomicRwOrd::Release, "relaxed" => AtomicRwOrd::Relaxed, - _ => throw_unsup_format!("unsupported read-write ordering `{ord}`"), - }) + _ => panic!("invalid read-write ordering `{ord}`"), + } } - fn fence_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicFenceOrd> { - Ok(match ord { + fn fence_ord(ord: &str) -> AtomicFenceOrd { + match ord { "seqcst" => AtomicFenceOrd::SeqCst, "acqrel" => AtomicFenceOrd::AcqRel, "acquire" => AtomicFenceOrd::Acquire, "release" => AtomicFenceOrd::Release, - _ => throw_unsup_format!("unsupported fence ordering `{ord}`"), - }) + _ => panic!("invalid fence ordering `{ord}`"), + } } match &*intrinsic_structure { - ["load", ord] => this.atomic_load(args, dest, read_ord(ord)?)?, - ["store", ord] => this.atomic_store(args, write_ord(ord)?)?, + ["load", ord] => this.atomic_load(args, dest, read_ord(ord))?, + ["store", ord] => this.atomic_store(args, write_ord(ord))?, - ["fence", ord] => this.atomic_fence_intrinsic(args, fence_ord(ord)?)?, - ["singlethreadfence", ord] => this.compiler_fence_intrinsic(args, fence_ord(ord)?)?, + ["fence", ord] => this.atomic_fence_intrinsic(args, fence_ord(ord))?, + ["singlethreadfence", ord] => this.compiler_fence_intrinsic(args, fence_ord(ord))?, - ["xchg", ord] => this.atomic_exchange(args, dest, rw_ord(ord)?)?, + ["xchg", ord] => this.atomic_exchange(args, dest, rw_ord(ord))?, ["cxchg", ord1, ord2] => - this.atomic_compare_exchange(args, dest, rw_ord(ord1)?, read_ord(ord2)?)?, + this.atomic_compare_exchange(args, dest, rw_ord(ord1), read_ord(ord2))?, ["cxchgweak", ord1, ord2] => - this.atomic_compare_exchange_weak(args, dest, rw_ord(ord1)?, read_ord(ord2)?)?, + this.atomic_compare_exchange_weak(args, dest, rw_ord(ord1), read_ord(ord2))?, ["or", ord] => - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), rw_ord(ord)?)?, + this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), rw_ord(ord))?, ["xor", ord] => - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), rw_ord(ord)?)?, + this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), rw_ord(ord))?, ["and", ord] => - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), rw_ord(ord)?)?, + this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), rw_ord(ord))?, ["nand", ord] => - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), rw_ord(ord)?)?, + this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), rw_ord(ord))?, ["xadd", ord] => - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), rw_ord(ord)?)?, + this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), rw_ord(ord))?, ["xsub", ord] => - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), rw_ord(ord)?)?, + this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), rw_ord(ord))?, ["min", ord] => { // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Int(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord)?)?; + this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?; } ["umin", ord] => { // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord)?)?; + this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?; } ["max", ord] => { // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Int(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord)?)?; + this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?; } ["umax", ord] => { // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord)?)?; + this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?; } _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } -impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {} -trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextPrivExt<'tcx> for MiriInterpCx<'tcx> {} +trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { fn atomic_load( &mut self, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, atomic: AtomicReadOrd, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -140,11 +140,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { Ok(()) } - fn atomic_store( - &mut self, - args: &[OpTy<'tcx, Provenance>], - atomic: AtomicWriteOrd, - ) -> InterpResult<'tcx> { + fn atomic_store(&mut self, args: &[OpTy<'tcx>], atomic: AtomicWriteOrd) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let [place, val] = check_arg_count(args)?; @@ -159,7 +155,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { fn compiler_fence_intrinsic( &mut self, - args: &[OpTy<'tcx, Provenance>], + args: &[OpTy<'tcx>], atomic: AtomicFenceOrd, ) -> InterpResult<'tcx> { let [] = check_arg_count(args)?; @@ -170,7 +166,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { fn atomic_fence_intrinsic( &mut self, - args: &[OpTy<'tcx, Provenance>], + args: &[OpTy<'tcx>], atomic: AtomicFenceOrd, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -181,8 +177,8 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { fn atomic_rmw_op( &mut self, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, atomic_op: AtomicOp, atomic: AtomicRwOrd, ) -> InterpResult<'tcx> { @@ -213,8 +209,8 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { this.write_immediate(*old, dest)?; // old value is returned Ok(()) } - AtomicOp::MirOp(op, neg) => { - let old = this.atomic_rmw_op_immediate(&place, &rhs, op, neg, atomic)?; + AtomicOp::MirOp(op, not) => { + let old = this.atomic_rmw_op_immediate(&place, &rhs, op, not, atomic)?; this.write_immediate(*old, dest)?; // old value is returned Ok(()) } @@ -223,8 +219,8 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { fn atomic_exchange( &mut self, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, atomic: AtomicRwOrd, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -240,8 +236,8 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { fn atomic_compare_exchange_impl( &mut self, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, success: AtomicRwOrd, fail: AtomicReadOrd, can_fail_spuriously: bool, @@ -269,8 +265,8 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { fn atomic_compare_exchange( &mut self, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, success: AtomicRwOrd, fail: AtomicReadOrd, ) -> InterpResult<'tcx> { @@ -279,8 +275,8 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { fn atomic_compare_exchange_weak( &mut self, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, success: AtomicRwOrd, fail: AtomicReadOrd, ) -> InterpResult<'tcx> { diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 9e7fc7a21fcb..313eac363372 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -1,3 +1,5 @@ +#![warn(clippy::arithmetic_side_effects)] + mod atomic; mod simd; @@ -18,15 +20,15 @@ use atomic::EvalContextExt as _; use helpers::{check_arg_count, ToHost, ToSoft}; use simd::EvalContextExt as _; -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn call_intrinsic( &mut self, instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ret: Option, - _unwind: mir::UnwindAction, + unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option>> { let this = self.eval_context_mut(); @@ -43,30 +45,32 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden { throw_unsup_format!("unimplemented intrinsic: `{intrinsic_name}`") } - let intrinsic_fallback_checks_ub = Symbol::intern("intrinsic_fallback_checks_ub"); + let intrinsic_fallback_is_spec = Symbol::intern("intrinsic_fallback_is_spec"); if this .tcx - .get_attrs_by_path( - instance.def_id(), - &[sym::miri, intrinsic_fallback_checks_ub], - ) + .get_attrs_by_path(instance.def_id(), &[sym::miri, intrinsic_fallback_is_spec]) .next() .is_none() { throw_unsup_format!( - "miri can only use intrinsic fallback bodies that check UB. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that" + "Miri can only use intrinsic fallback bodies that exactly reflect the specification: they fully check for UB and are as non-deterministic as possible. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_is_spec]` attribute to it; also ping @rust-lang/miri when you do that" ); } Ok(Some(ty::Instance { - def: ty::InstanceDef::Item(instance.def_id()), + def: ty::InstanceKind::Item(instance.def_id()), args: instance.args, })) } - EmulateItemResult::NeedsJumping => { + EmulateItemResult::NeedsReturn => { trace!("{:?}", this.dump_place(&dest.clone().into())); this.return_to_block(ret)?; Ok(None) } + EmulateItemResult::NeedsUnwind => { + // Jump to the unwind block to begin unwinding. + this.unwind_to_block(unwind)?; + Ok(None) + } EmulateItemResult::AlreadyJumped => Ok(None), } } @@ -77,8 +81,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, intrinsic_name: &str, generic_args: ty::GenericArgsRef<'tcx>, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ret: Option, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); @@ -167,6 +171,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // This is a "bitwise" operation, so there's no NaN non-determinism. this.write_scalar(Scalar::from_f64(f.abs()), dest)?; } + "floorf32" | "ceilf32" | "truncf32" | "roundf32" | "rintf32" => { let [f] = check_arg_count(args)?; let f = this.read_scalar(f)?.to_f32()?; @@ -182,6 +187,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } + "floorf64" | "ceilf64" | "truncf64" | "roundf64" | "rintf64" => { + let [f] = check_arg_count(args)?; + let f = this.read_scalar(f)?.to_f64()?; + let mode = match intrinsic_name { + "floorf64" => Round::TowardNegative, + "ceilf64" => Round::TowardPositive, + "truncf64" => Round::TowardZero, + "roundf64" => Round::NearestTiesToAway, + "rintf64" => Round::NearestTiesToEven, + _ => bug!(), + }; + let res = f.round_to_integral(mode).value; + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + #[rustfmt::skip] | "sinf32" | "cosf32" @@ -211,22 +232,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } - - "floorf64" | "ceilf64" | "truncf64" | "roundf64" | "rintf64" => { - let [f] = check_arg_count(args)?; - let f = this.read_scalar(f)?.to_f64()?; - let mode = match intrinsic_name { - "floorf64" => Round::TowardNegative, - "ceilf64" => Round::TowardPositive, - "truncf64" => Round::TowardZero, - "roundf64" => Round::NearestTiesToAway, - "rintf64" => Round::NearestTiesToEven, - _ => bug!(), - }; - let res = f.round_to_integral(mode).value; - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } #[rustfmt::skip] | "sinf64" | "cosf64" @@ -257,61 +262,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(res, dest)?; } - #[rustfmt::skip] - | "fadd_fast" - | "fsub_fast" - | "fmul_fast" - | "fdiv_fast" - | "frem_fast" - => { - let [a, b] = check_arg_count(args)?; - let a = this.read_immediate(a)?; - let b = this.read_immediate(b)?; - let op = match intrinsic_name { - "fadd_fast" => mir::BinOp::Add, - "fsub_fast" => mir::BinOp::Sub, - "fmul_fast" => mir::BinOp::Mul, - "fdiv_fast" => mir::BinOp::Div, - "frem_fast" => mir::BinOp::Rem, - _ => bug!(), - }; - let float_finite = |x: &ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> { - let ty::Float(fty) = x.layout.ty.kind() else { - bug!("float_finite: non-float input type {}", x.layout.ty) - }; - Ok(match fty { - FloatTy::F16 => unimplemented!("f16_f128"), - FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(), - FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(), - FloatTy::F128 => unimplemented!("f16_f128"), - }) - }; - match (float_finite(&a)?, float_finite(&b)?) { - (false, false) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as both parameters", - ), - (false, _) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as first parameter", - ), - (_, false) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as second parameter", - ), - _ => {} - } - let res = this.wrapping_binary_op(op, &a, &b)?; - if !float_finite(&res)? { - throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result"); - } - // This cannot be a NaN so we also don't have to apply any non-determinism. - // (Also, `wrapping_binary_op` already called `generate_nan` if needed.) - this.write_immediate(*res, dest)?; - } - - #[rustfmt::skip] - | "minnumf32" - | "maxnumf32" - | "copysignf32" - => { + "minnumf32" | "maxnumf32" | "copysignf32" => { let [a, b] = check_arg_count(args)?; let a = this.read_scalar(a)?.to_f32()?; let b = this.read_scalar(b)?.to_f32()?; @@ -323,12 +274,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; this.write_scalar(Scalar::from_f32(res), dest)?; } - - #[rustfmt::skip] - | "minnumf64" - | "maxnumf64" - | "copysignf64" - => { + "minnumf64" | "maxnumf64" | "copysignf64" => { let [a, b] = check_arg_count(args)?; let a = this.read_scalar(a)?.to_f64()?; let b = this.read_scalar(b)?.to_f64()?; @@ -351,7 +297,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[a, b, c]); this.write_scalar(res, dest)?; } - "fmaf64" => { let [a, b, c] = check_arg_count(args)?; let a = this.read_scalar(a)?.to_f64()?; @@ -372,7 +317,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[f1, f2]); this.write_scalar(res, dest)?; } - "powf64" => { let [f1, f2] = check_arg_count(args)?; let f1 = this.read_scalar(f1)?.to_f64()?; @@ -392,7 +336,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } - "powif64" => { let [f, i] = check_arg_count(args)?; let f = this.read_scalar(f)?.to_f64()?; @@ -403,6 +346,79 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(res, dest)?; } + #[rustfmt::skip] + | "fadd_algebraic" + | "fsub_algebraic" + | "fmul_algebraic" + | "fdiv_algebraic" + | "frem_algebraic" + => { + let [a, b] = check_arg_count(args)?; + let a = this.read_immediate(a)?; + let b = this.read_immediate(b)?; + let op = match intrinsic_name { + "fadd_algebraic" => mir::BinOp::Add, + "fsub_algebraic" => mir::BinOp::Sub, + "fmul_algebraic" => mir::BinOp::Mul, + "fdiv_algebraic" => mir::BinOp::Div, + "frem_algebraic" => mir::BinOp::Rem, + _ => bug!(), + }; + let res = this.binary_op(op, &a, &b)?; + // `binary_op` already called `generate_nan` if necessary. + this.write_immediate(*res, dest)?; + } + + #[rustfmt::skip] + | "fadd_fast" + | "fsub_fast" + | "fmul_fast" + | "fdiv_fast" + | "frem_fast" + => { + let [a, b] = check_arg_count(args)?; + let a = this.read_immediate(a)?; + let b = this.read_immediate(b)?; + let op = match intrinsic_name { + "fadd_fast" => mir::BinOp::Add, + "fsub_fast" => mir::BinOp::Sub, + "fmul_fast" => mir::BinOp::Mul, + "fdiv_fast" => mir::BinOp::Div, + "frem_fast" => mir::BinOp::Rem, + _ => bug!(), + }; + let float_finite = |x: &ImmTy<'tcx>| -> InterpResult<'tcx, bool> { + let ty::Float(fty) = x.layout.ty.kind() else { + bug!("float_finite: non-float input type {}", x.layout.ty) + }; + Ok(match fty { + FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(), + FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(), + FloatTy::F128 => unimplemented!("f16_f128"), + }) + }; + match (float_finite(&a)?, float_finite(&b)?) { + (false, false) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as both parameters", + ), + (false, _) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as first parameter", + ), + (_, false) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as second parameter", + ), + _ => {} + } + let res = this.binary_op(op, &a, &b)?; + if !float_finite(&res)? { + throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result"); + } + // This cannot be a NaN so we also don't have to apply any non-determinism. + // (Also, `binary_op` already called `generate_nan` if needed.) + this.write_immediate(*res, dest)?; + } + "float_to_int_unchecked" => { let [val] = check_arg_count(args)?; let val = this.read_immediate(val)?; @@ -429,6 +445,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index d0a78429ca85..a5afd029b651 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -1,3 +1,5 @@ +use either::Either; + use rustc_apfloat::{Float, Round}; use rustc_middle::ty::layout::{HasParamEnv, LayoutOf}; use rustc_middle::{mir, ty, ty::FloatTy}; @@ -13,16 +15,16 @@ pub(crate) enum MinMax { Max, } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Calls the simd intrinsic `intrinsic`; the `simd_` prefix has already been removed. /// Returns `Ok(true)` if the intrinsic was handled. fn emulate_simd_intrinsic( &mut self, intrinsic_name: &str, generic_args: ty::GenericArgsRef<'tcx>, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); match intrinsic_name { @@ -42,6 +44,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { | "flog2" | "flog10" | "ctlz" + | "ctpop" | "cttz" | "bswap" | "bitreverse" @@ -68,6 +71,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "round" => Op::Round(rustc_apfloat::Round::NearestTiesToAway), "trunc" => Op::Round(rustc_apfloat::Round::TowardZero), "ctlz" => Op::Numeric(sym::ctlz), + "ctpop" => Op::Numeric(sym::ctpop), "cttz" => Op::Numeric(sym::cttz), "bswap" => Op::Numeric(sym::bswap), "bitreverse" => Op::Numeric(sym::bitreverse), @@ -80,7 +84,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let val = match which { Op::MirOp(mir_op) => { // This already does NaN adjustments - this.wrapping_unary_op(mir_op, &op)?.to_scalar() + this.unary_op(mir_op, &op)?.to_scalar() } Op::Abs => { // Works for f32 and f64. @@ -215,8 +219,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "mul" => Op::MirOp(BinOp::Mul), "div" => Op::MirOp(BinOp::Div), "rem" => Op::MirOp(BinOp::Rem), - "shl" => Op::MirOp(BinOp::Shl), - "shr" => Op::MirOp(BinOp::Shr), + "shl" => Op::MirOp(BinOp::ShlUnchecked), + "shr" => Op::MirOp(BinOp::ShrUnchecked), "and" => Op::MirOp(BinOp::BitAnd), "or" => Op::MirOp(BinOp::BitOr), "xor" => Op::MirOp(BinOp::BitXor), @@ -241,15 +245,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let val = match which { Op::MirOp(mir_op) => { // This does NaN adjustments. - let (val, overflowed) = this.overflowing_binary_op(mir_op, &left, &right)?; - if matches!(mir_op, BinOp::Shl | BinOp::Shr) { - // Shifts have extra UB as SIMD operations that the MIR binop does not have. - // See . - if overflowed { - let r_val = right.to_scalar().to_bits(right.layout.size)?; - throw_ub_format!("overflowing shift by {r_val} in `simd_{intrinsic_name}` in SIMD lane {i}"); + let val = this.binary_op(mir_op, &left, &right).map_err(|err| { + match err.kind() { + InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => { + // This resets the interpreter backtrace, but it's not worth avoiding that. + let shift_amount = match shift_amount { + Either::Left(v) => v.to_string(), + Either::Right(v) => v.to_string(), + }; + err_ub_format!("overflowing shift by {shift_amount} in `simd_{intrinsic_name}` in lane {i}").into() + } + _ => err } - } + })?; if matches!(mir_op, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) { // Special handling for boolean-returning operations assert_eq!(val.layout.ty, this.tcx.types.bool); @@ -368,11 +376,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let op = this.read_immediate(&this.project_index(&op, i)?)?; res = match which { Op::MirOp(mir_op) => { - this.wrapping_binary_op(mir_op, &res, &op)? + this.binary_op(mir_op, &res, &op)? } Op::MirOpBool(mir_op) => { let op = imm_from_bool(simd_element_to_bool(op)?); - this.wrapping_binary_op(mir_op, &res, &op)? + this.binary_op(mir_op, &res, &op)? } Op::MinMax(mmop) => { if matches!(res.layout.ty.kind(), ty::Float(_)) { @@ -383,7 +391,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { MinMax::Min => BinOp::Le, MinMax::Max => BinOp::Ge, }; - if this.wrapping_binary_op(mirop, &res, &op)?.to_scalar().to_bool()? { + if this.binary_op(mirop, &res, &op)?.to_scalar().to_bool()? { res } else { op @@ -412,7 +420,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let mut res = init; for i in 0..op_len { let op = this.read_immediate(&this.project_index(&op, i)?)?; - res = this.wrapping_binary_op(mir_op, &res, &op)?; + res = this.binary_op(mir_op, &res, &op)?; } this.write_immediate(*res, dest)?; } @@ -444,28 +452,54 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let (no, no_len) = this.operand_to_simd(no)?; let (dest, dest_len) = this.mplace_to_simd(dest)?; let bitmask_len = dest_len.next_multiple_of(8); + if bitmask_len > 64 { + throw_unsup_format!( + "simd_select_bitmask: vectors larger than 64 elements are currently not supported" + ); + } - // The mask must be an integer or an array. - assert!( - mask.layout.ty.is_integral() - || matches!(mask.layout.ty.kind(), ty::Array(elemty, _) if elemty == &this.tcx.types.u8) - ); - assert!(bitmask_len <= 64); - assert_eq!(bitmask_len, mask.layout.size.bits()); assert_eq!(dest_len, yes_len); assert_eq!(dest_len, no_len); + + // Read the mask, either as an integer or as an array. + let mask: u64 = match mask.layout.ty.kind() { + ty::Uint(_) => { + // Any larger integer type is fine. + assert!(mask.layout.size.bits() >= bitmask_len); + this.read_scalar(mask)?.to_bits(mask.layout.size)?.try_into().unwrap() + } + ty::Array(elem, _len) if elem == &this.tcx.types.u8 => { + // The array must have exactly the right size. + assert_eq!(mask.layout.size.bits(), bitmask_len); + // Read the raw bytes. + let mask = mask.assert_mem_place(); // arrays cannot be immediate + let mask_bytes = + this.read_bytes_ptr_strip_provenance(mask.ptr(), mask.layout.size)?; + // Turn them into a `u64` in the right way. + let mask_size = mask.layout.size.bytes_usize(); + let mut mask_arr = [0u8; 8]; + match this.data_layout().endian { + Endian::Little => { + // Fill the first N bytes. + mask_arr[..mask_size].copy_from_slice(mask_bytes); + u64::from_le_bytes(mask_arr) + } + Endian::Big => { + // Fill the last N bytes. + let i = mask_arr.len().strict_sub(mask_size); + mask_arr[i..].copy_from_slice(mask_bytes); + u64::from_be_bytes(mask_arr) + } + } + } + _ => bug!("simd_select_bitmask: invalid mask type {}", mask.layout.ty), + }; + let dest_len = u32::try_from(dest_len).unwrap(); let bitmask_len = u32::try_from(bitmask_len).unwrap(); - - // To read the mask, we transmute it to an integer. - // That does the right thing wrt endianness. - let mask_ty = this.machine.layouts.uint(mask.layout.size).unwrap(); - let mask = mask.transmute(mask_ty, this)?; - let mask: u64 = this.read_scalar(&mask)?.to_bits(mask_ty.size)?.try_into().unwrap(); - for i in 0..dest_len { let bit_i = simd_bitmask_index(i, dest_len, this.data_layout().endian); - let mask = mask & 1u64.checked_shl(bit_i).unwrap(); + let mask = mask & 1u64.strict_shl(bit_i); let yes = this.read_immediate(&this.project_index(&yes, i.into())?)?; let no = this.read_immediate(&this.project_index(&no, i.into())?)?; let dest = this.project_index(&dest, i.into())?; @@ -477,7 +511,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // If the mask is "padded", ensure that padding is all-zero. // This deliberately does not use `simd_bitmask_index`; these bits are outside // the bitmask. It does not matter in which order we check them. - let mask = mask & 1u64.checked_shl(i).unwrap(); + let mask = mask & 1u64.strict_shl(i); if mask != 0 { throw_ub_format!( "a SIMD bitmask less than 8 bits long must be filled with 0s for the remaining bits" @@ -490,30 +524,49 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [op] = check_arg_count(args)?; let (op, op_len) = this.operand_to_simd(op)?; let bitmask_len = op_len.next_multiple_of(8); + if bitmask_len > 64 { + throw_unsup_format!( + "simd_bitmask: vectors larger than 64 elements are currently not supported" + ); + } - // Returns either an unsigned integer or array of `u8`. - assert!( - dest.layout.ty.is_integral() - || matches!(dest.layout.ty.kind(), ty::Array(elemty, _) if elemty == &this.tcx.types.u8) - ); - assert!(bitmask_len <= 64); - assert_eq!(bitmask_len, dest.layout.size.bits()); let op_len = u32::try_from(op_len).unwrap(); - let mut res = 0u64; for i in 0..op_len { let op = this.read_immediate(&this.project_index(&op, i.into())?)?; if simd_element_to_bool(op)? { - res |= 1u64 - .checked_shl(simd_bitmask_index(i, op_len, this.data_layout().endian)) - .unwrap(); + let bit_i = simd_bitmask_index(i, op_len, this.data_layout().endian); + res |= 1u64.strict_shl(bit_i); } } - // We have to change the type of the place to be able to write `res` into it. This - // transmutes the integer to an array, which does the right thing wrt endianness. - let dest = - dest.transmute(this.machine.layouts.uint(dest.layout.size).unwrap(), this)?; - this.write_int(res, &dest)?; + // Write the result, depending on the `dest` type. + // Returns either an unsigned integer or array of `u8`. + match dest.layout.ty.kind() { + ty::Uint(_) => { + // Any larger integer type is fine, it will be zero-extended. + assert!(dest.layout.size.bits() >= bitmask_len); + this.write_int(res, dest)?; + } + ty::Array(elem, _len) if elem == &this.tcx.types.u8 => { + // The array must have exactly the right size. + assert_eq!(dest.layout.size.bits(), bitmask_len); + // We have to write the result byte-for-byte. + let res_size = dest.layout.size.bytes_usize(); + let res_bytes; + let res_bytes_slice = match this.data_layout().endian { + Endian::Little => { + res_bytes = res.to_le_bytes(); + &res_bytes[..res_size] // take the first N bytes + } + Endian::Big => { + res_bytes = res.to_be_bytes(); + &res_bytes[res_bytes.len().strict_sub(res_size)..] // take the last N bytes + } + }; + this.write_bytes_ptr(dest.ptr(), res_bytes_slice.iter().cloned())?; + } + _ => bug!("simd_bitmask: invalid return type {}", dest.layout.ty), + } } "cast" | "as" | "cast_ptr" | "expose_provenance" | "with_exposed_provenance" => { let [op] = check_arg_count(args)?; @@ -582,6 +635,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { .expect_const() .eval(*this.tcx, this.param_env(), this.tcx.span) .unwrap() + .1 .unwrap_branch(); let index_len = index.len(); @@ -589,17 +643,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { assert_eq!(index_len as u64, dest_len); for i in 0..dest_len { - let src_index: u64 = index[usize::try_from(i).unwrap()] - .unwrap_leaf() - .try_to_u32() - .unwrap() - .into(); + let src_index: u64 = + index[usize::try_from(i).unwrap()].unwrap_leaf().to_u32().into(); let dest = this.project_index(&dest, i)?; let val = if src_index < left_len { this.read_immediate(&this.project_index(&left, src_index)?)? - } else if src_index < left_len.checked_add(right_len).unwrap() { - let right_idx = src_index.checked_sub(left_len).unwrap(); + } else if src_index < left_len.strict_add(right_len) { + let right_idx = src_index.strict_sub(left_len); this.read_immediate(&this.project_index(&right, right_idx)?)? } else { throw_ub_format!( @@ -638,8 +689,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let val = if src_index < left_len { this.read_immediate(&this.project_index(&left, src_index)?)? - } else if src_index < left_len.checked_add(right_len).unwrap() { - let right_idx = src_index.checked_sub(left_len).unwrap(); + } else if src_index < left_len.strict_add(right_len) { + let right_idx = src_index.strict_sub(left_len); this.read_immediate(&this.project_index(&right, right_idx)?)? } else { throw_ub_format!( @@ -746,15 +797,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } fn fminmax_op( &self, op: MinMax, - left: &ImmTy<'tcx, Provenance>, - right: &ImmTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + left: &ImmTy<'tcx>, + right: &ImmTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_ref(); assert_eq!(left.layout.ty, right.layout.ty); let ty::Float(float_ty) = left.layout.ty.kind() else { diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 54eb6a3bd669..f8410db4dd0f 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -13,6 +13,7 @@ #![feature(lint_reasons)] #![feature(trait_upcasting)] #![feature(strict_overflow_ops)] +#![feature(is_none_or)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, @@ -35,6 +36,7 @@ clippy::bool_to_int_with_if, clippy::box_default, clippy::needless_question_mark, + clippy::needless_lifetimes, rustc::diagnostic_outside_of_impl, // We are not implementing queries here so it's fine rustc::potential_query_instability, @@ -52,7 +54,6 @@ // Some "regular" crates we want to share with rustc extern crate either; -#[macro_use] extern crate tracing; // The rustc crates we need @@ -63,7 +64,6 @@ extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_index; -#[macro_use] extern crate rustc_middle; extern crate rustc_session; extern crate rustc_span; @@ -74,6 +74,7 @@ extern crate rustc_target; extern crate rustc_driver; mod alloc_addresses; +mod alloc_bytes; mod borrow_tracker; mod clock; mod concurrency; @@ -89,13 +90,24 @@ mod range_map; mod shims; // Establish a "crate-wide prelude": we often import `crate::*`. +use rustc_middle::{bug, span_bug}; +use tracing::{info, trace}; // Make all those symbols available in the same place as our own. #[doc(no_inline)] pub use rustc_const_eval::interpret::*; // Resolve ambiguity. #[doc(no_inline)] -pub use rustc_const_eval::interpret::{self, AllocMap, PlaceTy, Provenance as _}; +pub use rustc_const_eval::interpret::{self, AllocMap, Provenance as _}; + +// Type aliases that set the provenance parameter. +pub type Pointer = interpret::Pointer>; +pub type StrictPointer = interpret::Pointer; +pub type Scalar = interpret::Scalar; +pub type ImmTy<'tcx> = interpret::ImmTy<'tcx, machine::Provenance>; +pub type OpTy<'tcx> = interpret::OpTy<'tcx, machine::Provenance>; +pub type PlaceTy<'tcx> = interpret::PlaceTy<'tcx, machine::Provenance>; +pub type MPlaceTy<'tcx> = interpret::MPlaceTy<'tcx, machine::Provenance>; pub use crate::intrinsics::EvalContextExt as _; pub use crate::shims::env::{EnvVars, EvalContextExt as _}; @@ -107,6 +119,7 @@ pub use crate::shims::tls::TlsData; pub use crate::shims::EmulateItemResult; pub use crate::alloc_addresses::{EvalContextExt as _, ProvenanceMode}; +pub use crate::alloc_bytes::MiriAllocBytes; pub use crate::borrow_tracker::stacked_borrows::{ EvalContextExt as _, Item, Permission, Stack, Stacks, }; @@ -118,9 +131,10 @@ pub use crate::clock::{Clock, Instant}; pub use crate::concurrency::{ data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _}, init_once::{EvalContextExt as _, InitOnceId}, - sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SyncId}, + sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SynchronizationObjects}, thread::{ - BlockReason, CallbackTime, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, + BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, + TimeoutAnchor, TimeoutClock, UnblockCallback, }, }; pub use crate::diagnostics::{ diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 8854b1852803..0d91279f9f4c 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1,7 +1,6 @@ //! Global machine state as well as implementation of the interpreter engine //! `Machine` trait. -use std::borrow::Cow; use std::cell::RefCell; use std::collections::hash_map::Entry; use std::fmt; @@ -30,14 +29,13 @@ use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi; use crate::{ - concurrency::{data_race, weak_memory}, - shims::unix, + concurrency::{ + data_race::{self, NaReadType, NaWriteType}, + weak_memory, + }, *, }; -use self::concurrency::data_race::NaReadType; -use self::concurrency::data_race::NaWriteType; - /// First real-time signal. /// `signal(7)` says this must be between 32 and 64 and specifies 34 or 35 /// as typical values. @@ -206,11 +204,11 @@ pub enum Provenance { /// whether *some* exposed pointer could have done what we want to do, and if the answer is yes /// then we allow the access. This allows too much code in two ways: /// - The same wildcard pointer can "take the role" of multiple different exposed pointers on - /// subsequenct memory accesses. + /// subsequent memory accesses. /// - In the aliasing model, we don't just have to know the borrow tag of the pointer used for /// the access, we also have to update the aliasing state -- and that update can be very /// different depending on which borrow tag we pick! Stacked Borrows has support for this by - /// switching to a stack that is only approximately known, i.e. we overapproximate the effect + /// switching to a stack that is only approximately known, i.e. we over-approximate the effect /// of using *any* exposed pointer for this access, and only keep information about the borrow /// stack that would be true with all possible choices. Wildcard, @@ -242,12 +240,12 @@ pub enum ProvenanceExtra { } #[cfg(target_pointer_width = "64")] -static_assert_size!(Pointer, 24); +static_assert_size!(StrictPointer, 24); // FIXME: this would with in 24bytes but layout optimizations are not smart enough // #[cfg(target_pointer_width = "64")] -//static_assert_size!(Pointer>, 24); +//static_assert_size!(Pointer, 24); #[cfg(target_pointer_width = "64")] -static_assert_size!(Scalar, 32); +static_assert_size!(Scalar, 32); impl fmt::Debug for Provenance { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -271,7 +269,7 @@ impl fmt::Debug for Provenance { } impl interpret::Provenance for Provenance { - /// We use absolute addresses in the `offset` of a `Pointer`. + /// We use absolute addresses in the `offset` of a `StrictPointer`. const OFFSET_IS_ADDR: bool = true; fn get_alloc_id(self) -> Option { @@ -281,7 +279,7 @@ impl interpret::Provenance for Provenance { } } - fn fmt(ptr: &Pointer, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(ptr: &interpret::Pointer, f: &mut fmt::Formatter<'_>) -> fmt::Result { let (prov, addr) = ptr.into_parts(); // address is absolute write!(f, "{:#x}", addr.bytes())?; if f.alternate() { @@ -374,7 +372,7 @@ pub struct PrimitiveLayouts<'tcx> { pub const_raw_ptr: TyAndLayout<'tcx>, // *const () } -impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> { +impl<'tcx> PrimitiveLayouts<'tcx> { fn new(layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result> { let tcx = layout_cx.tcx; let mut_raw_ptr = Ty::new_mut_ptr(tcx, tcx.types.unit); @@ -426,7 +424,7 @@ impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> { /// /// If you add anything here that stores machine values, remember to update /// `visit_all_machine_values`! -pub struct MiriMachine<'mir, 'tcx> { +pub struct MiriMachine<'tcx> { // We carry a copy of the global `TyCtxt` for convenience, so methods taking just `&Evaluator` have `tcx` access. pub tcx: TyCtxt<'tcx>, @@ -443,14 +441,14 @@ pub struct MiriMachine<'mir, 'tcx> { pub(crate) env_vars: EnvVars<'tcx>, /// Return place of the main function. - pub(crate) main_fn_ret_place: Option>, + pub(crate) main_fn_ret_place: Option>, /// Program arguments (`Option` because we can only initialize them after creating the ecx). /// These are *pointers* to argc/argv because macOS. /// We also need the full command line as one string because of Windows. - pub(crate) argc: Option>>, - pub(crate) argv: Option>>, - pub(crate) cmd_line: Option>>, + pub(crate) argc: Option, + pub(crate) argv: Option, + pub(crate) cmd_line: Option, /// TLS state. pub(crate) tls: TlsData<'tcx>, @@ -464,15 +462,17 @@ pub struct MiriMachine<'mir, 'tcx> { pub(crate) validate: bool, /// The table of file descriptors. - pub(crate) fds: unix::FdTable, + pub(crate) fds: shims::FdTable, /// The table of directory descriptors. - pub(crate) dirs: unix::DirTable, + pub(crate) dirs: shims::DirTable, /// This machine's monotone clock. pub(crate) clock: Clock, /// The set of threads. - pub(crate) threads: ThreadManager<'mir, 'tcx>, + pub(crate) threads: ThreadManager<'tcx>, + /// The state of the primitive synchronization objects. + pub(crate) sync: SynchronizationObjects, /// Precomputed `TyLayout`s for primitive data types that are commonly used inside Miri. pub(crate) layouts: PrimitiveLayouts<'tcx>, @@ -503,7 +503,7 @@ pub struct MiriMachine<'mir, 'tcx> { pub(crate) local_crates: Vec, /// Mapping extern static names to their pointer. - extern_statics: FxHashMap>, + extern_statics: FxHashMap, /// The random number generator used for resolving non-determinism. /// Needs to be queried by ptr_to_int, hence needs interior mutability. @@ -564,7 +564,7 @@ pub struct MiriMachine<'mir, 'tcx> { /// Maps MIR consts to their evaluated result. We combine the const with a "salt" (`usize`) /// that is fixed per stack frame; this lets us have sometimes different results for the /// same const while ensuring consistent results within a single call. - const_cache: RefCell, usize), OpTy<'tcx, Provenance>>>, + const_cache: RefCell, usize), OpTy<'tcx>>>, /// For each allocation, an offset inside that allocation that was deemed aligned even for /// symbolic alignment checks. This cannot be stored in `AllocExtra` since it needs to be @@ -575,7 +575,7 @@ pub struct MiriMachine<'mir, 'tcx> { pub(crate) symbolic_alignment: RefCell>, } -impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { +impl<'tcx> MiriMachine<'tcx> { pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Self { let tcx = layout_cx.tcx; let local_crates = helpers::get_local_crates(tcx); @@ -641,10 +641,11 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { tls: TlsData::default(), isolated_op: config.isolated_op, validate: config.validate, - fds: unix::FdTable::new(config.mute_stdout_stderr), + fds: shims::FdTable::new(config.mute_stdout_stderr), dirs: Default::default(), layouts, threads: ThreadManager::default(), + sync: SynchronizationObjects::default(), static_roots: Vec::new(), profiler, string_cache: Default::default(), @@ -704,9 +705,9 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { } pub(crate) fn late_init( - this: &mut MiriInterpCx<'mir, 'tcx>, + this: &mut MiriInterpCx<'tcx>, config: &MiriConfig, - on_main_stack_empty: StackEmptyCallback<'mir, 'tcx>, + on_main_stack_empty: StackEmptyCallback<'tcx>, ) -> InterpResult<'tcx> { EnvVars::init(this, config)?; MiriMachine::init_extern_statics(this)?; @@ -714,11 +715,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { Ok(()) } - pub(crate) fn add_extern_static( - this: &mut MiriInterpCx<'mir, 'tcx>, - name: &str, - ptr: Pointer>, - ) { + pub(crate) fn add_extern_static(this: &mut MiriInterpCx<'tcx>, name: &str, ptr: Pointer) { // This got just allocated, so there definitely is a pointer here. let ptr = ptr.into_pointer_or_addr().unwrap(); this.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap(); @@ -763,11 +760,12 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { } } -impl VisitProvenance for MiriMachine<'_, '_> { +impl VisitProvenance for MiriMachine<'_> { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { #[rustfmt::skip] let MiriMachine { threads, + sync: _, tls, env_vars, main_fn_ret_place, @@ -834,26 +832,26 @@ impl VisitProvenance for MiriMachine<'_, '_> { } /// A rustc InterpCx for Miri. -pub type MiriInterpCx<'mir, 'tcx> = InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>; +pub type MiriInterpCx<'tcx> = InterpCx<'tcx, MiriMachine<'tcx>>; /// A little trait that's useful to be inherited by extension traits. -pub trait MiriInterpCxExt<'mir, 'tcx> { - fn eval_context_ref<'a>(&'a self) -> &'a MiriInterpCx<'mir, 'tcx>; - fn eval_context_mut<'a>(&'a mut self) -> &'a mut MiriInterpCx<'mir, 'tcx>; +pub trait MiriInterpCxExt<'tcx> { + fn eval_context_ref<'a>(&'a self) -> &'a MiriInterpCx<'tcx>; + fn eval_context_mut<'a>(&'a mut self) -> &'a mut MiriInterpCx<'tcx>; } -impl<'mir, 'tcx> MiriInterpCxExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> { +impl<'tcx> MiriInterpCxExt<'tcx> for MiriInterpCx<'tcx> { #[inline(always)] - fn eval_context_ref(&self) -> &MiriInterpCx<'mir, 'tcx> { + fn eval_context_ref(&self) -> &MiriInterpCx<'tcx> { self } #[inline(always)] - fn eval_context_mut(&mut self) -> &mut MiriInterpCx<'mir, 'tcx> { + fn eval_context_mut(&mut self) -> &mut MiriInterpCx<'tcx> { self } } /// Machine hook implementations. -impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { +impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { type MemoryKind = MiriMemoryKind; type ExtraFnVal = DynSym; @@ -862,7 +860,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { type Provenance = Provenance; type ProvenanceExtra = ProvenanceExtra; - type Bytes = Box<[u8]>; + type Bytes = MiriAllocBytes; type MemoryMap = MonoHashMap)>; @@ -872,13 +870,13 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { const PANIC_ON_ALLOC_FAIL: bool = false; #[inline(always)] - fn enforce_alignment(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool { + fn enforce_alignment(ecx: &MiriInterpCx<'tcx>) -> bool { ecx.machine.check_alignment != AlignmentCheck::None } #[inline(always)] fn alignment_check( - ecx: &MiriInterpCx<'mir, 'tcx>, + ecx: &MiriInterpCx<'tcx>, alloc_id: AllocId, alloc_align: Align, alloc_kind: AllocKind, @@ -923,30 +921,30 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { } #[inline(always)] - fn enforce_validity(ecx: &MiriInterpCx<'mir, 'tcx>, _layout: TyAndLayout<'tcx>) -> bool { + fn enforce_validity(ecx: &MiriInterpCx<'tcx>, _layout: TyAndLayout<'tcx>) -> bool { ecx.machine.validate } #[inline(always)] - fn enforce_abi(_ecx: &MiriInterpCx<'mir, 'tcx>) -> bool { + fn enforce_abi(_ecx: &MiriInterpCx<'tcx>) -> bool { true } #[inline(always)] - fn ignore_optional_overflow_checks(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool { + fn ignore_optional_overflow_checks(ecx: &MiriInterpCx<'tcx>) -> bool { !ecx.tcx.sess.overflow_checks() } #[inline(always)] fn find_mir_or_eval_fn( - ecx: &mut MiriInterpCx<'mir, 'tcx>, + ecx: &mut MiriInterpCx<'tcx>, instance: ty::Instance<'tcx>, abi: Abi, args: &[FnArg<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, - ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> { + ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> { // For foreign items, try to see if we can emulate them. if ecx.tcx.is_foreign_item(instance.def_id()) { // An external function call that does not have a MIR body. We either find MIR elsewhere @@ -966,11 +964,11 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { #[inline(always)] fn call_extra_fn( - ecx: &mut MiriInterpCx<'mir, 'tcx>, + ecx: &mut MiriInterpCx<'tcx>, fn_val: DynSym, abi: Abi, args: &[FnArg<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { @@ -980,10 +978,10 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { #[inline(always)] fn call_intrinsic( - ecx: &mut MiriInterpCx<'mir, 'tcx>, + ecx: &mut MiriInterpCx<'tcx>, instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option>> { @@ -992,19 +990,19 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { #[inline(always)] fn assert_panic( - ecx: &mut MiriInterpCx<'mir, 'tcx>, + ecx: &mut MiriInterpCx<'tcx>, msg: &mir::AssertMessage<'tcx>, unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { ecx.assert_panic(msg, unwind) } - fn panic_nounwind(ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: &str) -> InterpResult<'tcx> { + fn panic_nounwind(ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx> { ecx.start_panic_nounwind(msg) } fn unwind_terminate( - ecx: &mut InterpCx<'mir, 'tcx, Self>, + ecx: &mut InterpCx<'tcx, Self>, reason: mir::UnwindTerminateReason, ) -> InterpResult<'tcx> { // Call the lang item. @@ -1022,11 +1020,11 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { #[inline(always)] fn binary_ptr_op( - ecx: &MiriInterpCx<'mir, 'tcx>, + ecx: &MiriInterpCx<'tcx>, bin_op: mir::BinOp, - left: &ImmTy<'tcx, Provenance>, - right: &ImmTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, (ImmTy<'tcx, Provenance>, bool)> { + left: &ImmTy<'tcx>, + right: &ImmTy<'tcx>, + ) -> InterpResult<'tcx, ImmTy<'tcx>> { ecx.binary_ptr_op(bin_op, left, right) } @@ -1035,23 +1033,23 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { F1: rustc_apfloat::Float + rustc_apfloat::FloatConvert, F2: rustc_apfloat::Float, >( - ecx: &InterpCx<'mir, 'tcx, Self>, + ecx: &InterpCx<'tcx, Self>, inputs: &[F1], ) -> F2 { ecx.generate_nan(inputs) } fn thread_local_static_pointer( - ecx: &mut MiriInterpCx<'mir, 'tcx>, + ecx: &mut MiriInterpCx<'tcx>, def_id: DefId, - ) -> InterpResult<'tcx, Pointer> { + ) -> InterpResult<'tcx, StrictPointer> { ecx.get_or_create_thread_local_alloc(def_id) } fn extern_static_pointer( - ecx: &MiriInterpCx<'mir, 'tcx>, + ecx: &MiriInterpCx<'tcx>, def_id: DefId, - ) -> InterpResult<'tcx, Pointer> { + ) -> InterpResult<'tcx, StrictPointer> { let link_name = ecx.item_link_name(def_id); if let Some(&ptr) = ecx.machine.extern_statics.get(&link_name) { // Various parts of the engine rely on `get_alloc_info` for size and alignment @@ -1083,39 +1081,33 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { } } - fn adjust_allocation<'b>( - ecx: &MiriInterpCx<'mir, 'tcx>, + fn init_alloc_extra( + ecx: &MiriInterpCx<'tcx>, id: AllocId, - alloc: Cow<'b, Allocation>, - kind: Option, - ) -> InterpResult<'tcx, Cow<'b, Allocation>> { - let kind = kind.expect("we set our GLOBAL_KIND so this cannot be None"); + kind: MemoryKind, + size: Size, + align: Align, + ) -> InterpResult<'tcx, Self::AllocExtra> { if ecx.machine.tracked_alloc_ids.contains(&id) { - ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc( - id, - alloc.size(), - alloc.align, - kind, - )); + ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc(id, size, align, kind)); } - let alloc = alloc.into_owned(); let borrow_tracker = ecx .machine .borrow_tracker .as_ref() - .map(|bt| bt.borrow_mut().new_allocation(id, alloc.size(), kind, &ecx.machine)); + .map(|bt| bt.borrow_mut().new_allocation(id, size, kind, &ecx.machine)); - let race_alloc = ecx.machine.data_race.as_ref().map(|data_race| { + let data_race = ecx.machine.data_race.as_ref().map(|data_race| { data_race::AllocState::new_allocation( data_race, &ecx.machine.threads, - alloc.size(), + size, kind, ecx.machine.current_span(), ) }); - let buffer_alloc = ecx.machine.weak_memory.then(weak_memory::AllocState::new_allocation); + let weak_memory = ecx.machine.weak_memory.then(weak_memory::AllocState::new_allocation); // If an allocation is leaked, we want to report a backtrace to indicate where it was // allocated. We don't need to record a backtrace for allocations which are allowed to @@ -1126,17 +1118,6 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { Some(ecx.generate_stacktrace()) }; - let alloc: Allocation = alloc.adjust_from_tcx( - &ecx.tcx, - AllocExtra { - borrow_tracker, - data_race: race_alloc, - weak_memory: buffer_alloc, - backtrace, - }, - |ptr| ecx.global_root_pointer(ptr), - )?; - if matches!(kind, MemoryKind::Machine(kind) if kind.should_save_allocation_span()) { ecx.machine .allocation_spans @@ -1144,14 +1125,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { .insert(id, (ecx.machine.current_span(), None)); } - Ok(Cow::Owned(alloc)) + Ok(AllocExtra { borrow_tracker, data_race, weak_memory, backtrace }) } fn adjust_alloc_root_pointer( - ecx: &MiriInterpCx<'mir, 'tcx>, - ptr: Pointer, + ecx: &MiriInterpCx<'tcx>, + ptr: interpret::Pointer, kind: Option, - ) -> InterpResult<'tcx, Pointer> { + ) -> InterpResult<'tcx, interpret::Pointer> { let kind = kind.expect("we set our GLOBAL_KIND so this cannot be None"); let alloc_id = ptr.provenance.alloc_id(); if cfg!(debug_assertions) { @@ -1178,20 +1159,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { /// Called on `usize as ptr` casts. #[inline(always)] - fn ptr_from_addr_cast( - ecx: &MiriInterpCx<'mir, 'tcx>, - addr: u64, - ) -> InterpResult<'tcx, Pointer>> { + fn ptr_from_addr_cast(ecx: &MiriInterpCx<'tcx>, addr: u64) -> InterpResult<'tcx, Pointer> { ecx.ptr_from_addr_cast(addr) } /// Called on `ptr as usize` casts. /// (Actually computing the resulting `usize` doesn't need machine help, /// that's just `Scalar::try_to_int`.) - fn expose_ptr( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - ptr: Pointer, - ) -> InterpResult<'tcx> { + fn expose_ptr(ecx: &mut InterpCx<'tcx, Self>, ptr: StrictPointer) -> InterpResult<'tcx> { match ptr.provenance { Provenance::Concrete { alloc_id, tag } => ecx.expose_ptr(alloc_id, tag), Provenance::Wildcard => { @@ -1211,8 +1186,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { /// (i.e., we'll never turn the data returned here back into a `Pointer` that might be /// stored in machine state). fn ptr_get_alloc( - ecx: &MiriInterpCx<'mir, 'tcx>, - ptr: Pointer, + ecx: &MiriInterpCx<'tcx>, + ptr: StrictPointer, ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> { let rel = ecx.ptr_get_alloc(ptr); @@ -1308,10 +1283,10 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { #[inline(always)] fn retag_ptr_value( - ecx: &mut InterpCx<'mir, 'tcx, Self>, + ecx: &mut InterpCx<'tcx, Self>, kind: mir::RetagKind, - val: &ImmTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { + val: &ImmTy<'tcx>, + ) -> InterpResult<'tcx, ImmTy<'tcx>> { if ecx.machine.borrow_tracker.is_some() { ecx.retag_ptr_value(kind, val) } else { @@ -1321,9 +1296,9 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { #[inline(always)] fn retag_place_contents( - ecx: &mut InterpCx<'mir, 'tcx, Self>, + ecx: &mut InterpCx<'tcx, Self>, kind: mir::RetagKind, - place: &PlaceTy<'tcx, Provenance>, + place: &PlaceTy<'tcx>, ) -> InterpResult<'tcx> { if ecx.machine.borrow_tracker.is_some() { ecx.retag_place_contents(kind, place)?; @@ -1332,8 +1307,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { } fn protect_in_place_function_argument( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - place: &MPlaceTy<'tcx, Provenance>, + ecx: &mut InterpCx<'tcx, Self>, + place: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { // If we have a borrow tracker, we also have it set up protection so that all reads *and // writes* during this call are insta-UB. @@ -1353,10 +1328,10 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { } #[inline(always)] - fn init_frame_extra( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - frame: Frame<'mir, 'tcx, Provenance>, - ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>> { + fn init_frame( + ecx: &mut InterpCx<'tcx, Self>, + frame: Frame<'tcx, Provenance>, + ) -> InterpResult<'tcx, Frame<'tcx, Provenance, FrameExtra<'tcx>>> { // Start recording our event before doing anything else let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() { let fn_name = frame.instance.to_string(); @@ -1366,7 +1341,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { Some(profiler.start_recording_interval_event_detached( *name, measureme::EventId::from_label(*name), - ecx.get_active_thread().to_u32(), + ecx.active_thread().to_u32(), )) } else { None @@ -1386,18 +1361,18 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { } fn stack<'a>( - ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { + ecx: &'a InterpCx<'tcx, Self>, + ) -> &'a [Frame<'tcx, Self::Provenance, Self::FrameExtra>] { ecx.active_thread_stack() } fn stack_mut<'a>( - ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec> { + ecx: &'a mut InterpCx<'tcx, Self>, + ) -> &'a mut Vec> { ecx.active_thread_stack_mut() } - fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + fn before_terminator(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { ecx.machine.basic_block_count += 1u64; // a u64 that is only incremented by 1 will "never" overflow ecx.machine.since_gc += 1; // Possibly report our progress. @@ -1428,7 +1403,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { } #[inline(always)] - fn after_stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { + fn after_stack_push(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { if ecx.frame().extra.is_user_relevant { // We just pushed a local frame, so we know that the topmost local frame is the topmost // frame. If we push a non-local frame, there's no need to do anything. @@ -1439,8 +1414,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { } fn before_stack_pop( - ecx: &InterpCx<'mir, 'tcx, Self>, - frame: &Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, + ecx: &InterpCx<'tcx, Self>, + frame: &Frame<'tcx, Self::Provenance, Self::FrameExtra>, ) -> InterpResult<'tcx> { // We want this *before* the return value copy, because the return place itself is protected // until we do `end_call` here. @@ -1456,8 +1431,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { #[inline(always)] fn after_stack_pop( - ecx: &mut InterpCx<'mir, 'tcx, Self>, - frame: Frame<'mir, 'tcx, Provenance, FrameExtra<'tcx>>, + ecx: &mut InterpCx<'tcx, Self>, + frame: Frame<'tcx, Provenance, FrameExtra<'tcx>>, unwinding: bool, ) -> InterpResult<'tcx, StackPopJump> { if frame.extra.is_user_relevant { @@ -1486,9 +1461,9 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { } fn after_local_allocated( - ecx: &mut InterpCx<'mir, 'tcx, Self>, + ecx: &mut InterpCx<'tcx, Self>, local: mir::Local, - mplace: &MPlaceTy<'tcx, Provenance>, + mplace: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr().provenance else { panic!("after_local_allocated should only be called on fresh allocations"); @@ -1500,19 +1475,19 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { } fn eval_mir_constant( - ecx: &InterpCx<'mir, 'tcx, Self>, + ecx: &InterpCx<'tcx, Self>, val: mir::Const<'tcx>, span: Span, layout: Option>, eval: F, - ) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>> + ) -> InterpResult<'tcx, OpTy<'tcx>> where F: Fn( - &InterpCx<'mir, 'tcx, Self>, + &InterpCx<'tcx, Self>, mir::Const<'tcx>, Span, Option>, - ) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>>, + ) -> InterpResult<'tcx, OpTy<'tcx>>, { let frame = ecx.active_thread_stack().last().unwrap(); let mut cache = ecx.machine.const_cache.borrow_mut(); diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs index d99be39177bc..bc44e672bd85 100644 --- a/src/tools/miri/src/operator.rs +++ b/src/tools/miri/src/operator.rs @@ -7,14 +7,14 @@ use rustc_target::abi::Size; use crate::*; -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn binary_ptr_op( &self, bin_op: mir::BinOp, - left: &ImmTy<'tcx, Provenance>, - right: &ImmTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, (ImmTy<'tcx, Provenance>, bool)> { + left: &ImmTy<'tcx>, + right: &ImmTy<'tcx>, + ) -> InterpResult<'tcx, ImmTy<'tcx>> { use rustc_middle::mir::BinOp::*; let this = self.eval_context_ref(); @@ -45,7 +45,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ge => left >= right, _ => bug!(), }; - (ImmTy::from_bool(res, *this.tcx), false) + ImmTy::from_bool(res, *this.tcx) } // Some more operations are possible with atomics. @@ -60,16 +60,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { right.to_scalar().to_target_usize(this)?, this.machine.layouts.usize, ); - let (result, overflowing) = this.overflowing_binary_op(bin_op, &left, &right)?; + let result = this.binary_op(bin_op, &left, &right)?; // Construct a new pointer with the provenance of `ptr` (the LHS). let result_ptr = Pointer::new( ptr.provenance, Size::from_bytes(result.to_scalar().to_target_usize(this)?), ); - ( - ImmTy::from_scalar(Scalar::from_maybe_pointer(result_ptr, this), left.layout), - overflowing, - ) + + ImmTy::from_scalar(Scalar::from_maybe_pointer(result_ptr, this), left.layout) } _ => span_bug!(this.cur_span(), "Invalid operator on pointers: {:?}", bin_op), diff --git a/src/tools/miri/src/provenance_gc.rs b/src/tools/miri/src/provenance_gc.rs index f23d7dfd52d5..af980ca48197 100644 --- a/src/tools/miri/src/provenance_gc.rs +++ b/src/tools/miri/src/provenance_gc.rs @@ -10,6 +10,18 @@ pub trait VisitProvenance { fn visit_provenance(&self, visit: &mut VisitWith<'_>); } +// Trivial impls for types that do not contain any provenance +macro_rules! no_provenance { + ($($ty:ident)+) => { + $( + impl VisitProvenance for $ty { + fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {} + } + )+ + } +} +no_provenance!(i8 i16 i32 i64 isize u8 u16 u32 u64 usize ThreadId); + impl VisitProvenance for Option { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { if let Some(x) = self { @@ -44,21 +56,21 @@ impl VisitProvenance for Provenance { } } -impl VisitProvenance for Pointer { +impl VisitProvenance for StrictPointer { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let (prov, _offset) = self.into_parts(); prov.visit_provenance(visit); } } -impl VisitProvenance for Pointer> { +impl VisitProvenance for Pointer { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { let (prov, _offset) = self.into_parts(); prov.visit_provenance(visit); } } -impl VisitProvenance for Scalar { +impl VisitProvenance for Scalar { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { match self { Scalar::Ptr(ptr, _) => ptr.visit_provenance(visit), @@ -91,20 +103,20 @@ impl VisitProvenance for MemPlaceMeta { } } -impl VisitProvenance for ImmTy<'_, Provenance> { +impl VisitProvenance for ImmTy<'_> { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { (**self).visit_provenance(visit) } } -impl VisitProvenance for MPlaceTy<'_, Provenance> { +impl VisitProvenance for MPlaceTy<'_> { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { self.ptr().visit_provenance(visit); self.meta().visit_provenance(visit); } } -impl VisitProvenance for PlaceTy<'_, Provenance> { +impl VisitProvenance for PlaceTy<'_> { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { match self.as_mplace_or_local() { Either::Left(mplace) => mplace.visit_provenance(visit), @@ -113,7 +125,7 @@ impl VisitProvenance for PlaceTy<'_, Provenance> { } } -impl VisitProvenance for OpTy<'_, Provenance> { +impl VisitProvenance for OpTy<'_> { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { match self.as_mplace_or_imm() { Either::Left(mplace) => mplace.visit_provenance(visit), @@ -122,7 +134,7 @@ impl VisitProvenance for OpTy<'_, Provenance> { } } -impl VisitProvenance for Allocation> { +impl VisitProvenance for Allocation, MiriAllocBytes> { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { for prov in self.provenance().provenances() { prov.visit_provenance(visit); @@ -132,7 +144,7 @@ impl VisitProvenance for Allocation> { } } -impl VisitProvenance for crate::MiriInterpCx<'_, '_> { +impl VisitProvenance for crate::MiriInterpCx<'_> { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { // Visit the contents of the allocations and the IDs themselves, to account for all // live allocation IDs and all provenance in the allocation bytes, even if they are leaked. @@ -150,19 +162,19 @@ impl VisitProvenance for crate::MiriInterpCx<'_, '_> { } } -pub struct LiveAllocs<'a, 'mir, 'tcx> { +pub struct LiveAllocs<'a, 'tcx> { collected: FxHashSet, - ecx: &'a MiriInterpCx<'mir, 'tcx>, + ecx: &'a MiriInterpCx<'tcx>, } -impl LiveAllocs<'_, '_, '_> { +impl LiveAllocs<'_, '_> { pub fn is_live(&self, id: AllocId) -> bool { self.collected.contains(&id) || self.ecx.is_alloc_live(id) } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { fn run_provenance_gc(&mut self) { // We collect all tags from various parts of the interpreter, but also let this = self.eval_context_mut(); diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 1deb9a5654ed..7b0c54d763e4 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -17,8 +17,8 @@ pub(super) fn check_alloc_request<'tcx>(size: u64, align: u64) -> InterpResult<' Ok(()) } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Returns the alignment that `malloc` would guarantee for requests of the given size. fn malloc_align(&self, size: u64) -> Align { let this = self.eval_context_ref(); @@ -67,7 +67,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Emulates calling the internal __rust_* allocator functions fn emulate_allocator( &mut self, - default: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx>, + default: impl FnOnce(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); @@ -87,16 +87,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } AllocatorKind::Default => { default(this)?; - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } } - fn malloc( - &mut self, - size: u64, - zero_init: bool, - ) -> InterpResult<'tcx, Pointer>> { + fn malloc(&mut self, size: u64, zero_init: bool) -> InterpResult<'tcx, Pointer> { let this = self.eval_context_mut(); let align = this.malloc_align(size); let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into())?; @@ -111,7 +107,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(ptr.into()) } - fn free(&mut self, ptr: Pointer>) -> InterpResult<'tcx> { + fn posix_memalign( + &mut self, + memptr: &OpTy<'tcx>, + align: &OpTy<'tcx>, + size: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + let memptr = this.deref_pointer(memptr)?; + let align = this.read_target_usize(align)?; + let size = this.read_target_usize(size)?; + + // Align must be power of 2, and also at least ptr-sized (POSIX rules). + // But failure to adhere to this is not UB, it's an error condition. + if !align.is_power_of_two() || align < this.pointer_size().bytes() { + Ok(this.eval_libc("EINVAL")) + } else { + let ptr = this.allocate_ptr( + Size::from_bytes(size), + Align::from_bytes(align).unwrap(), + MiriMemoryKind::C.into(), + )?; + this.write_pointer(ptr, &memptr)?; + Ok(Scalar::from_i32(0)) + } + } + + fn free(&mut self, ptr: Pointer) -> InterpResult<'tcx> { let this = self.eval_context_mut(); if !this.ptr_is_null(ptr)? { this.deallocate_ptr(ptr, None, MiriMemoryKind::C.into())?; @@ -119,11 +141,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } - fn realloc( - &mut self, - old_ptr: Pointer>, - new_size: u64, - ) -> InterpResult<'tcx, Pointer>> { + fn realloc(&mut self, old_ptr: Pointer, new_size: u64) -> InterpResult<'tcx, Pointer> { let this = self.eval_context_mut(); let new_align = this.malloc_align(new_size); if this.ptr_is_null(old_ptr)? { @@ -146,4 +164,45 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } } + + fn aligned_alloc( + &mut self, + align: &OpTy<'tcx>, + size: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Pointer> { + let this = self.eval_context_mut(); + let align = this.read_target_usize(align)?; + let size = this.read_target_usize(size)?; + + // Alignment must be a power of 2, and "supported by the implementation". + // We decide that "supported by the implementation" means that the + // size must be a multiple of the alignment. (This restriction seems common + // enough that it is stated on + // as a general rule, but the actual standard has no such rule.) + // If any of these are violated, we have to return NULL. + // All fundamental alignments must be supported. + // + // macOS and Illumos are buggy in that they require the alignment + // to be at least the size of a pointer, so they do not support all fundamental + // alignments. We do not emulate those platform bugs. + // + // Linux also sets errno to EINVAL, but that's non-standard behavior that we do not + // emulate. + // FreeBSD says some of these cases are UB but that's violating the C standard. + // http://en.cppreference.com/w/cpp/memory/c/aligned_alloc + // Linux: https://linux.die.net/man/3/aligned_alloc + // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=aligned_alloc&apropos=0&sektion=3&manpath=FreeBSD+9-current&format=html + match size.checked_rem(align) { + Some(0) if align.is_power_of_two() => { + let align = align.max(this.malloc_align(size).bytes()); + let ptr = this.allocate_ptr( + Size::from_bytes(size), + Align::from_bytes(align).unwrap(), + MiriMemoryKind::C.into(), + )?; + Ok(ptr.into()) + } + _ => Ok(Pointer::null()), + } + } } diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index 294ad50c33f2..06be9c1e63e0 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -5,14 +5,14 @@ use rustc_middle::ty::{self, Instance, Ty}; use rustc_span::{hygiene, BytePos, Loc, Symbol}; use rustc_target::{abi::Size, spec::abi::Abi}; -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn handle_miri_backtrace_size( &mut self, abi: Abi, link_name: Symbol, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let [flags] = this.check_shim(abi, Abi::Rust, link_name, args)?; @@ -31,8 +31,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, abi: Abi, link_name: Symbol, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let tcx = this.tcx; @@ -110,7 +110,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn resolve_frame_pointer( &mut self, - ptr: &OpTy<'tcx, Provenance>, + ptr: &OpTy<'tcx>, ) -> InterpResult<'tcx, (Instance<'tcx>, Loc, String, String)> { let this = self.eval_context_mut(); @@ -140,8 +140,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, abi: Abi, link_name: Symbol, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let [ptr, flags] = this.check_shim(abi, Abi::Rust, link_name, args)?; @@ -218,7 +218,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, abi: Abi, link_name: Symbol, - args: &[OpTy<'tcx, Provenance>], + args: &[OpTy<'tcx>], ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs index 695d1138756c..7ad395cccb79 100644 --- a/src/tools/miri/src/shims/env.rs +++ b/src/tools/miri/src/shims/env.rs @@ -24,8 +24,8 @@ impl VisitProvenance for EnvVars<'_> { } impl<'tcx> EnvVars<'tcx> { - pub(crate) fn init<'mir>( - ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, + pub(crate) fn init( + ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>, config: &MiriConfig, ) -> InterpResult<'tcx> { // Initialize the `env_vars` map. @@ -58,9 +58,7 @@ impl<'tcx> EnvVars<'tcx> { Ok(()) } - pub(crate) fn cleanup<'mir>( - ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, - ) -> InterpResult<'tcx> { + pub(crate) fn cleanup(ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>) -> InterpResult<'tcx> { let this = ecx.eval_context_mut(); match this.machine.env_vars { EnvVars::Unix(_) => UnixEnvVars::cleanup(this), @@ -98,8 +96,8 @@ impl<'tcx> EnvVars<'tcx> { } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Try to get an environment variable from the interpreted program's environment. This is /// useful for implementing shims which are documented to read from the environment. fn get_env_var(&mut self, name: &OsStr) -> InterpResult<'tcx, Option> { diff --git a/src/tools/miri/src/shims/extern_static.rs b/src/tools/miri/src/shims/extern_static.rs index c3c7ef7c1fd8..17ac2638a69f 100644 --- a/src/tools/miri/src/shims/extern_static.rs +++ b/src/tools/miri/src/shims/extern_static.rs @@ -2,11 +2,11 @@ use crate::*; -impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { +impl<'tcx> MiriMachine<'tcx> { fn alloc_extern_static( - this: &mut MiriInterpCx<'mir, 'tcx>, + this: &mut MiriInterpCx<'tcx>, name: &str, - val: ImmTy<'tcx, Provenance>, + val: ImmTy<'tcx>, ) -> InterpResult<'tcx> { let place = this.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?; this.write_immediate(*val, &place)?; @@ -19,7 +19,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { /// symbol is not supported, and triggering fallback code which ends up calling /// some other shim that we do support). fn null_ptr_extern_statics( - this: &mut MiriInterpCx<'mir, 'tcx>, + this: &mut MiriInterpCx<'tcx>, names: &[&str], ) -> InterpResult<'tcx> { for name in names { @@ -31,7 +31,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { /// Extern statics that are initialized with function pointers to the symbols of the same name. fn weak_symbol_extern_statics( - this: &mut MiriInterpCx<'mir, 'tcx>, + this: &mut MiriInterpCx<'tcx>, names: &[&str], ) -> InterpResult<'tcx> { for name in names { @@ -45,7 +45,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { } /// Sets up the "extern statics" for this machine. - pub fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { + pub fn init_extern_statics(this: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx> { // "__rust_no_alloc_shim_is_unstable" let val = ImmTy::from_int(0, this.machine.layouts.u8); // always 0, value does not matter Self::alloc_extern_static(this, "__rust_no_alloc_shim_is_unstable", val)?; @@ -55,6 +55,12 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { let val = ImmTy::from_int(val, this.machine.layouts.u8); Self::alloc_extern_static(this, "__rust_alloc_error_handler_should_panic", val)?; + if this.target_os_is_unix() { + // "environ" is mandated by POSIX. + let environ = this.machine.env_vars.unix().environ(); + Self::add_extern_static(this, "environ", environ); + } + match this.tcx.sess.target.os.as_ref() { "linux" => { Self::null_ptr_extern_statics( @@ -62,19 +68,13 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { &["__cxa_thread_atexit_impl", "__clock_gettime64"], )?; Self::weak_symbol_extern_statics(this, &["getrandom", "statx"])?; - // "environ" - let environ = this.machine.env_vars.unix().environ(); - Self::add_extern_static(this, "environ", environ); } "freebsd" => { Self::null_ptr_extern_statics(this, &["__cxa_thread_atexit_impl"])?; - // "environ" - let environ = this.machine.env_vars.unix().environ(); - Self::add_extern_static(this, "environ", environ); } "android" => { Self::null_ptr_extern_statics(this, &["bsd_signal"])?; - Self::weak_symbol_extern_statics(this, &["signal"])?; + Self::weak_symbol_extern_statics(this, &["signal", "getrandom"])?; } "windows" => { // "_tls_used" @@ -82,6 +82,9 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { let val = ImmTy::from_int(0, this.machine.layouts.u8); Self::alloc_extern_static(this, "_tls_used", val)?; } + "illumos" | "solaris" => { + Self::weak_symbol_extern_statics(this, &["pthread_setname_np"])?; + } _ => {} // No "extern statics" supported on this target } Ok(()) diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index d431c28d55a5..911958e558c5 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -28,8 +28,8 @@ impl DynSym { } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Emulates calling a foreign item, failing if the item is not supported. /// This function will handle `goto_block` if needed. /// Returns Ok(None) if the foreign item was completely handled @@ -40,11 +40,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, - ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> { + ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> { let this = self.eval_context_mut(); let tcx = this.tcx.tcx; @@ -82,11 +82,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } // The rest either implements the logic, or falls back to `lookup_exported_symbol`. - match this.emulate_foreign_item_inner(link_name, abi, args, dest, unwind)? { - EmulateItemResult::NeedsJumping => { + match this.emulate_foreign_item_inner(link_name, abi, args, dest)? { + EmulateItemResult::NeedsReturn => { trace!("{:?}", this.dump_place(&dest.clone().into())); this.return_to_block(ret)?; } + EmulateItemResult::NeedsUnwind => { + // Jump to the unwind block to begin unwinding. + this.unwind_to_block(unwind)?; + } EmulateItemResult::AlreadyJumped => (), EmulateItemResult::NotSupported => { if let Some(body) = this.lookup_exported_symbol(link_name)? { @@ -108,6 +112,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_ref(); match this.tcx.sess.target.os.as_ref() { os if this.target_os_is_unix() => shims::unix::foreign_items::is_dyn_sym(name, os), + "wasi" => shims::wasi::foreign_items::is_dyn_sym(name), "windows" => shims::windows::foreign_items::is_dyn_sym(name), _ => false, } @@ -118,8 +123,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, sym: DynSym, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { @@ -132,7 +137,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn lookup_exported_symbol( &mut self, link_name: Symbol, - ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> { + ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> { let this = self.eval_context_mut(); let tcx = this.tcx.tcx; @@ -197,15 +202,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } -impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} +trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_foreign_item_inner( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, - unwind: mir::UnwindAction, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); @@ -217,7 +221,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // by the specified `.so` file; we should continue and check if it corresponds to // a provided shim. if this.call_native_fn(link_name, dest, args)? { - return Ok(EmulateItemResult::NeedsJumping); + return Ok(EmulateItemResult::NeedsReturn); } } @@ -234,11 +238,11 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // ``` // fn shim_name( // &mut self, - // arg1: &OpTy<'tcx, Provenance>, - // arg2: &OpTy<'tcx, Provenance>, - // arg3: &OpTy<'tcx, Provenance>, - // arg4: &OpTy<'tcx, Provenance>) - // -> InterpResult<'tcx, Scalar> { + // arg1: &OpTy<'tcx>, + // arg2: &OpTy<'tcx>, + // arg3: &OpTy<'tcx>, + // arg4: &OpTy<'tcx>) + // -> InterpResult<'tcx, Scalar> { // let this = self.eval_context_mut(); // // // First thing: load all the arguments. Details depend on the shim. @@ -262,9 +266,9 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { match link_name.as_str() { // Miri-specific extern functions "miri_start_unwind" => { - // `check_shim` happens inside `handle_miri_start_unwind`. - this.handle_miri_start_unwind(abi, link_name, args, unwind)?; - return Ok(EmulateItemResult::AlreadyJumped); + let [payload] = this.check_shim(abi, Abi::Rust, link_name, args)?; + this.handle_miri_start_unwind(payload)?; + return Ok(EmulateItemResult::NeedsUnwind); } "miri_run_provenance_gc" => { let [] = this.check_shim(abi, Abi::Rust, link_name, args)?; @@ -392,12 +396,12 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // If the newly promised alignment is bigger than the native alignment of this // allocation, and bigger than the previously promised alignment, then set it. if align > alloc_align - && !this + && this .machine .symbolic_alignment .get_mut() .get(&alloc_id) - .is_some_and(|&(_, old_align)| align <= old_align) + .is_none_or(|&(_, old_align)| align > old_align) { this.machine.symbolic_alignment.get_mut().insert(alloc_id, (offset, align)); } @@ -451,7 +455,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Rust allocation "__rust_alloc" | "miri_alloc" => { - let default = |this: &mut MiriInterpCx<'mir, 'tcx>| { + let default = |this: &mut MiriInterpCx<'tcx>| { // Only call `check_shim` when `#[global_allocator]` isn't used. When that // macro is used, we act like no shim exists, so that the exported function can run. let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?; @@ -479,7 +483,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "__rust_alloc" => return this.emulate_allocator(default), "miri_alloc" => { default(this)?; - return Ok(EmulateItemResult::NeedsJumping); + return Ok(EmulateItemResult::NeedsReturn); } _ => unreachable!(), } @@ -510,7 +514,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }); } "__rust_dealloc" | "miri_dealloc" => { - let default = |this: &mut MiriInterpCx<'mir, 'tcx>| { + let default = |this: &mut MiriInterpCx<'tcx>| { // See the comment for `__rust_alloc` why `check_shim` is only called in the // default case. let [ptr, old_size, align] = @@ -539,7 +543,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } "miri_dealloc" => { default(this)?; - return Ok(EmulateItemResult::NeedsJumping); + return Ok(EmulateItemResult::NeedsReturn); } _ => unreachable!(), } @@ -947,6 +951,10 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_inner( this, link_name, abi, args, dest, ), + "wasi" => + shims::wasi::foreign_items::EvalContextExt::emulate_foreign_item_inner( + this, link_name, abi, args, dest, + ), "windows" => shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_inner( this, link_name, abi, args, dest, @@ -956,6 +964,6 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; // We only fall through to here if we did *not* hit the `_` arm above, // i.e., if we actually emulated the function with one of the shims. - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index aaa3c69b92da..a41a2883c915 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -2,25 +2,30 @@ mod alloc; mod backtrace; -pub mod foreign_items; #[cfg(target_os = "linux")] -pub mod native_lib; -pub mod unix; -pub mod windows; +mod native_lib; +mod unix; +mod wasi; +mod windows; mod x86; pub mod env; pub mod extern_static; +pub mod foreign_items; pub mod os_str; pub mod panic; pub mod time; pub mod tls; +pub use unix::{DirTable, FdTable}; + /// What needs to be done after emulating an item (a shim or an intrinsic) is done. pub enum EmulateItemResult { /// The caller is expected to jump to the return block. - NeedsJumping, - /// Jumping has already been taken care of. + NeedsReturn, + /// The caller is expected to jump to the unwind block. + NeedsUnwind, + /// Jumping to the next block has already been taken care of. AlreadyJumped, /// The item is not supported. NotSupported, diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs index f9b8563b4b04..40697e17ba19 100644 --- a/src/tools/miri/src/shims/native_lib.rs +++ b/src/tools/miri/src/shims/native_lib.rs @@ -8,16 +8,16 @@ use rustc_target::abi::{Abi, HasDataLayout}; use crate::*; -impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} +trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Call native host function and return the output as an immediate. fn call_native_with_args<'a>( &mut self, link_name: Symbol, - dest: &MPlaceTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx>, ptr: CodePtr, libffi_args: Vec>, - ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { + ) -> InterpResult<'tcx, ImmTy<'tcx>> { let this = self.eval_context_mut(); // Call the function (`ptr`) with arguments `libffi_args`, and obtain the return value @@ -122,8 +122,8 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Call the native host function, with supplied arguments. /// Needs to convert all the arguments from their Miri representations to /// a native form (through `libffi` call). @@ -132,8 +132,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn call_native_fn( &mut self, link_name: Symbol, - dest: &MPlaceTy<'tcx, Provenance>, - args: &[OpTy<'tcx, Provenance>], + dest: &MPlaceTy<'tcx>, + args: &[OpTy<'tcx>], ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); // Get the pointer to the function in the shared object file if it exists. @@ -216,10 +216,7 @@ impl<'a> CArg { /// Extract the scalar value from the result of reading a scalar from the machine, /// and convert it to a `CArg`. -fn imm_to_carg<'tcx>( - v: ImmTy<'tcx, Provenance>, - cx: &impl HasDataLayout, -) -> InterpResult<'tcx, CArg> { +fn imm_to_carg<'tcx>(v: ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'tcx, CArg> { Ok(match v.layout.ty.kind() { // If the primitive provided can be converted to a type matching the type pattern // then create a `CArg` of this primitive value with the corresponding `CArg` constructor. diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs index 5fcea9ced691..533992e35ab1 100644 --- a/src/tools/miri/src/shims/os_str.rs +++ b/src/tools/miri/src/shims/os_str.rs @@ -30,17 +30,13 @@ pub fn bytes_to_os_str<'tcx>(bytes: &[u8]) -> InterpResult<'tcx, &OsStr> { Ok(OsStr::new(s)) } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Helper function to read an OsString from a null-terminated sequence of bytes, which is what /// the Unix APIs usually handle. - fn read_os_str_from_c_str<'a>( - &'a self, - ptr: Pointer>, - ) -> InterpResult<'tcx, &'a OsStr> + fn read_os_str_from_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, &'a OsStr> where 'tcx: 'a, - 'mir: 'a, { let this = self.eval_context_ref(); let bytes = this.read_c_str(ptr)?; @@ -49,13 +45,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Helper function to read an OsString from a 0x0000-terminated sequence of u16, /// which is what the Windows APIs usually handle. - fn read_os_str_from_wide_str<'a>( - &'a self, - ptr: Pointer>, - ) -> InterpResult<'tcx, OsString> + fn read_os_str_from_wide_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, OsString> where 'tcx: 'a, - 'mir: 'a, { #[cfg(windows)] pub fn u16vec_to_osstring<'tcx>(u16_vec: Vec) -> InterpResult<'tcx, OsString> { @@ -78,7 +70,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn write_os_str_to_c_str( &mut self, os_str: &OsStr, - ptr: Pointer>, + ptr: Pointer, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { let bytes = os_str.as_encoded_bytes(); @@ -90,7 +82,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn write_os_str_to_wide_str_helper( &mut self, os_str: &OsStr, - ptr: Pointer>, + ptr: Pointer, size: u64, truncate: bool, ) -> InterpResult<'tcx, (bool, u64)> { @@ -127,7 +119,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn write_os_str_to_wide_str( &mut self, os_str: &OsStr, - ptr: Pointer>, + ptr: Pointer, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { self.write_os_str_to_wide_str_helper(os_str, ptr, size, /*truncate*/ false) @@ -138,7 +130,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn write_os_str_to_wide_str_truncated( &mut self, os_str: &OsStr, - ptr: Pointer>, + ptr: Pointer, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { self.write_os_str_to_wide_str_helper(os_str, ptr, size, /*truncate*/ true) @@ -149,8 +141,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, os_str: &OsStr, memkind: MemoryKind, - ) -> InterpResult<'tcx, Pointer>> { - let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator. + ) -> InterpResult<'tcx, Pointer> { + let size = u64::try_from(os_str.len()).unwrap().strict_add(1); // Make space for `0` terminator. let this = self.eval_context_mut(); let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u8, size); @@ -165,8 +157,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, os_str: &OsStr, memkind: MemoryKind, - ) -> InterpResult<'tcx, Pointer>> { - let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0x0000` terminator. + ) -> InterpResult<'tcx, Pointer> { + let size = u64::try_from(os_str.len()).unwrap().strict_add(1); // Make space for `0x0000` terminator. let this = self.eval_context_mut(); let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u16, size); @@ -177,13 +169,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Read a null-terminated sequence of bytes, and perform path separator conversion if needed. - fn read_path_from_c_str<'a>( - &'a self, - ptr: Pointer>, - ) -> InterpResult<'tcx, Cow<'a, Path>> + fn read_path_from_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, Cow<'a, Path>> where 'tcx: 'a, - 'mir: 'a, { let this = self.eval_context_ref(); let os_str = this.read_os_str_from_c_str(ptr)?; @@ -195,10 +183,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Read a null-terminated sequence of `u16`s, and perform path separator conversion if needed. - fn read_path_from_wide_str( - &self, - ptr: Pointer>, - ) -> InterpResult<'tcx, PathBuf> { + fn read_path_from_wide_str(&self, ptr: Pointer) -> InterpResult<'tcx, PathBuf> { let this = self.eval_context_ref(); let os_str = this.read_os_str_from_wide_str(ptr)?; @@ -210,7 +195,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn write_path_to_c_str( &mut self, path: &Path, - ptr: Pointer>, + ptr: Pointer, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { let this = self.eval_context_mut(); @@ -224,7 +209,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn write_path_to_wide_str( &mut self, path: &Path, - ptr: Pointer>, + ptr: Pointer, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { let this = self.eval_context_mut(); @@ -238,7 +223,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn write_path_to_wide_str_truncated( &mut self, path: &Path, - ptr: Pointer>, + ptr: Pointer, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { let this = self.eval_context_mut(); @@ -253,7 +238,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, path: &Path, memkind: MemoryKind, - ) -> InterpResult<'tcx, Pointer>> { + ) -> InterpResult<'tcx, Pointer> { let this = self.eval_context_mut(); let os_str = this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget); @@ -266,7 +251,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, path: &Path, memkind: MemoryKind, - ) -> InterpResult<'tcx, Pointer>> { + ) -> InterpResult<'tcx, Pointer> { let this = self.eval_context_mut(); let os_str = this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget); diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index 4444d297469f..ef832f5bbbd1 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -13,7 +13,6 @@ use rustc_ast::Mutability; use rustc_middle::{mir, ty}; -use rustc_span::Symbol; use rustc_target::spec::abi::Abi; use rustc_target::spec::PanicStrategy; @@ -24,11 +23,11 @@ use helpers::check_arg_count; #[derive(Debug)] pub struct CatchUnwindData<'tcx> { /// The `catch_fn` callback to call in case of a panic. - catch_fn: Pointer>, + catch_fn: Pointer, /// The `data` argument for that callback. - data: Scalar, + data: Scalar, /// The return place from the original call to `try`. - dest: MPlaceTy<'tcx, Provenance>, + dest: MPlaceTy<'tcx>, /// The return block from the original call to `try`. ret: Option, } @@ -42,37 +41,27 @@ impl VisitProvenance for CatchUnwindData<'_> { } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Handles the special `miri_start_unwind` intrinsic, which is called /// by libpanic_unwind to delegate the actual unwinding process to Miri. - fn handle_miri_start_unwind( - &mut self, - abi: Abi, - link_name: Symbol, - args: &[OpTy<'tcx, Provenance>], - unwind: mir::UnwindAction, - ) -> InterpResult<'tcx> { + fn handle_miri_start_unwind(&mut self, payload: &OpTy<'tcx>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); trace!("miri_start_unwind: {:?}", this.frame().instance); - // Get the raw pointer stored in arg[0] (the panic payload). - let [payload] = this.check_shim(abi, Abi::Rust, link_name, args)?; let payload = this.read_scalar(payload)?; let thread = this.active_thread_mut(); thread.panic_payloads.push(payload); - // Jump to the unwind block to begin unwinding. - this.unwind_to_block(unwind)?; Ok(()) } /// Handles the `try` intrinsic, the underlying implementation of `std::panicking::try`. fn handle_catch_unwind( &mut self, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ret: Option, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index 8d1f07f916c9..ae17196f0b78 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -6,7 +6,6 @@ use std::time::{Duration, SystemTime}; use chrono::{DateTime, Datelike, Offset, Timelike, Utc}; use chrono_tz::Tz; -use crate::concurrency::thread::MachineCallback; use crate::*; /// Returns the time elapsed between the provided time and the unix epoch as a `Duration`. @@ -15,13 +14,13 @@ pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Du .map_err(|_| err_unsup_format!("times before the Unix epoch are not supported").into()) } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn clock_gettime( &mut self, - clk_id_op: &OpTy<'tcx, Provenance>, - tp_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + clk_id_op: &OpTy<'tcx>, + tp_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { // This clock support is deliberately minimal because a lot of clock types have fiddly // properties (is it possible for Miri to be suspended independently of the host?). If you // have a use for another clock type, please open an issue. @@ -62,6 +61,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // We need to support it because std uses it. relative_clocks.push(this.eval_libc_i32("CLOCK_UPTIME_RAW")); } + "solaris" | "illumos" => { + // The REALTIME clock returns the actual time since the Unix epoch. + absolute_clocks = vec![this.eval_libc_i32("CLOCK_REALTIME")]; + // MONOTONIC, in the other hand, is the high resolution, non-adjustable + // clock from an arbitrary time in the past. + // Note that the man page mentions HIGHRES but it is just + // an alias of MONOTONIC and the libc crate does not expose it anyway. + // https://docs.oracle.com/cd/E23824_01/html/821-1465/clock-gettime-3c.html + relative_clocks = vec![this.eval_libc_i32("CLOCK_MONOTONIC")]; + } target => throw_unsup_format!("`clock_gettime` is not supported on target OS {target}"), } @@ -69,7 +78,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.check_no_isolation("`clock_gettime` with `REALTIME` clocks")?; system_time_to_duration(&SystemTime::now())? } else if relative_clocks.contains(&clk_id) { - this.machine.clock.now().duration_since(this.machine.clock.anchor()) + this.machine.clock.now().duration_since(this.machine.clock.epoch()) } else { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; @@ -84,11 +93,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(Scalar::from_i32(0)) } - fn gettimeofday( - &mut self, - tv_op: &OpTy<'tcx, Provenance>, - tz_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + fn gettimeofday(&mut self, tv_op: &OpTy<'tcx>, tz_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("gettimeofday"); @@ -118,9 +123,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // https://linux.die.net/man/3/localtime_r fn localtime_r( &mut self, - timep: &OpTy<'tcx, Provenance>, - result_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Pointer>> { + timep: &OpTy<'tcx>, + result_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Pointer> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("localtime_r"); @@ -153,30 +158,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // chrono crate yet. // This may not be consistent with libc::localtime_r's result. let tm_isdst = -1; - - // tm_zone represents the timezone value in the form of: +0730, +08, -0730 or -08. - // This may not be consistent with libc::localtime_r's result. - let offset_in_seconds = dt.offset().fix().local_minus_utc(); - let tm_gmtoff = offset_in_seconds; - let mut tm_zone = String::new(); - if offset_in_seconds < 0 { - tm_zone.push('-'); - } else { - tm_zone.push('+'); - } - let offset_hour = offset_in_seconds.abs() / 3600; - write!(tm_zone, "{:02}", offset_hour).unwrap(); - let offset_min = (offset_in_seconds.abs() % 3600) / 60; - if offset_min != 0 { - write!(tm_zone, "{:02}", offset_min).unwrap(); - } - - // FIXME: String de-duplication is needed so that we only allocate this string only once - // even when there are multiple calls to this function. - let tm_zone_ptr = - this.alloc_os_str_as_c_str(&OsString::from(tm_zone), MiriMemoryKind::Machine.into())?; - - this.write_pointer(tm_zone_ptr, &this.project_field_named(&result, "tm_zone")?)?; this.write_int_fields_named( &[ ("tm_sec", dt.second().into()), @@ -188,18 +169,46 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ("tm_wday", dt.weekday().num_days_from_sunday().into()), ("tm_yday", dt.ordinal0().into()), ("tm_isdst", tm_isdst), - ("tm_gmtoff", tm_gmtoff.into()), ], &result, )?; + // solaris/illumos system tm struct does not have + // the additional tm_zone/tm_gmtoff fields. + // https://docs.oracle.com/cd/E36784_01/html/E36874/localtime-r-3c.html + if !matches!(&*this.tcx.sess.target.os, "solaris" | "illumos") { + // tm_zone represents the timezone value in the form of: +0730, +08, -0730 or -08. + // This may not be consistent with libc::localtime_r's result. + let offset_in_seconds = dt.offset().fix().local_minus_utc(); + let tm_gmtoff = offset_in_seconds; + let mut tm_zone = String::new(); + if offset_in_seconds < 0 { + tm_zone.push('-'); + } else { + tm_zone.push('+'); + } + let offset_hour = offset_in_seconds.abs() / 3600; + write!(tm_zone, "{:02}", offset_hour).unwrap(); + let offset_min = (offset_in_seconds.abs() % 3600) / 60; + if offset_min != 0 { + write!(tm_zone, "{:02}", offset_min).unwrap(); + } + + // FIXME: String de-duplication is needed so that we only allocate this string only once + // even when there are multiple calls to this function. + let tm_zone_ptr = this + .alloc_os_str_as_c_str(&OsString::from(tm_zone), MiriMemoryKind::Machine.into())?; + + this.write_pointer(tm_zone_ptr, &this.project_field_named(&result, "tm_zone")?)?; + this.write_int_fields_named(&[("tm_gmtoff", tm_gmtoff.into())], &result)?; + } Ok(result.ptr()) } #[allow(non_snake_case, clippy::arithmetic_side_effects)] fn GetSystemTimeAsFileTime( &mut self, shim_name: &str, - LPFILETIME_op: &OpTy<'tcx, Provenance>, + LPFILETIME_op: &OpTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -229,15 +238,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[allow(non_snake_case)] fn QueryPerformanceCounter( &mut self, - lpPerformanceCount_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + lpPerformanceCount_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os("windows", "QueryPerformanceCounter"); // QueryPerformanceCounter uses a hardware counter as its basis. // Miri will emulate a counter with a resolution of 1 nanosecond. - let duration = this.machine.clock.now().duration_since(this.machine.clock.anchor()); + let duration = this.machine.clock.now().duration_since(this.machine.clock.epoch()); let qpc = i64::try_from(duration.as_nanos()).map_err(|_| { err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported") })?; @@ -248,8 +257,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[allow(non_snake_case)] fn QueryPerformanceFrequency( &mut self, - lpFrequency_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + lpFrequency_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os("windows", "QueryPerformanceFrequency"); @@ -266,24 +275,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(Scalar::from_i32(-1)) // Return non-zero on success } - fn mach_absolute_time(&self) -> InterpResult<'tcx, Scalar> { + fn mach_absolute_time(&self) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_ref(); this.assert_target_os("macos", "mach_absolute_time"); // This returns a u64, with time units determined dynamically by `mach_timebase_info`. // We return plain nanoseconds. - let duration = this.machine.clock.now().duration_since(this.machine.clock.anchor()); + let duration = this.machine.clock.now().duration_since(this.machine.clock.epoch()); let res = u64::try_from(duration.as_nanos()).map_err(|_| { err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported") })?; Ok(Scalar::from_u64(res)) } - fn mach_timebase_info( - &mut self, - info_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + fn mach_timebase_info(&mut self, info_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os("macos", "mach_timebase_info"); @@ -300,8 +306,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn nanosleep( &mut self, - req_op: &OpTy<'tcx, Provenance>, - _rem: &OpTy<'tcx, Provenance>, // Signal handlers are not supported, so rem will never be written to. + req_op: &OpTy<'tcx>, + _rem: &OpTy<'tcx>, // Signal handlers are not supported, so rem will never be written to. ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -317,26 +323,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { return Ok(-1); } }; - // If adding the duration overflows, let's just sleep for an hour. Waking up early is always acceptable. - let now = this.machine.clock.now(); - let timeout_time = now - .checked_add(duration) - .unwrap_or_else(|| now.checked_add(Duration::from_secs(3600)).unwrap()); - let active_thread = this.get_active_thread(); - this.block_thread(active_thread, BlockReason::Sleep); - - this.register_timeout_callback( - active_thread, - CallbackTime::Monotonic(timeout_time), - Box::new(UnblockCallback { thread_to_unblock: active_thread }), + this.block_thread( + BlockReason::Sleep, + Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)), + callback!( + @capture<'tcx> {} + @unblock = |_this| { panic!("sleeping thread unblocked before time is up") } + @timeout = |_this| { Ok(()) } + ), ); - Ok(0) } #[allow(non_snake_case)] - fn Sleep(&mut self, timeout: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { + fn Sleep(&mut self, timeout: &OpTy<'tcx>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); this.assert_target_os("windows", "Sleep"); @@ -344,32 +345,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let timeout_ms = this.read_scalar(timeout)?.to_u32()?; let duration = Duration::from_millis(timeout_ms.into()); - let timeout_time = this.machine.clock.now().checked_add(duration).unwrap(); - let active_thread = this.get_active_thread(); - this.block_thread(active_thread, BlockReason::Sleep); - - this.register_timeout_callback( - active_thread, - CallbackTime::Monotonic(timeout_time), - Box::new(UnblockCallback { thread_to_unblock: active_thread }), + this.block_thread( + BlockReason::Sleep, + Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)), + callback!( + @capture<'tcx> {} + @unblock = |_this| { panic!("sleeping thread unblocked before time is up") } + @timeout = |_this| { Ok(()) } + ), ); - - Ok(()) - } -} - -struct UnblockCallback { - thread_to_unblock: ThreadId, -} - -impl VisitProvenance for UnblockCallback { - fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {} -} - -impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for UnblockCallback { - fn call(&self, ecx: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { - ecx.unblock_thread(self.thread_to_unblock, BlockReason::Sleep); Ok(()) } } diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs index 0dec12f0b65f..c91386fa877f 100644 --- a/src/tools/miri/src/shims/tls.rs +++ b/src/tools/miri/src/shims/tls.rs @@ -16,7 +16,7 @@ pub type TlsKey = u128; pub struct TlsEntry<'tcx> { /// The data for this key. None is used to represent NULL. /// (We normalize this early to avoid having to do a NULL-ptr-test each time we access the data.) - data: BTreeMap>, + data: BTreeMap, dtor: Option>, } @@ -38,7 +38,7 @@ pub struct TlsData<'tcx> { /// A single per thread destructor of the thread local storage (that's how /// things work on macOS) with a data argument. - macos_thread_dtors: BTreeMap, Scalar)>, + macos_thread_dtors: BTreeMap, Scalar)>, } impl<'tcx> Default for TlsData<'tcx> { @@ -86,7 +86,7 @@ impl<'tcx> TlsData<'tcx> { key: TlsKey, thread_id: ThreadId, cx: &impl HasDataLayout, - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, Scalar> { match self.keys.get(&key) { Some(TlsEntry { data, .. }) => { let value = data.get(&thread_id).copied(); @@ -101,7 +101,7 @@ impl<'tcx> TlsData<'tcx> { &mut self, key: TlsKey, thread_id: ThreadId, - new_data: Scalar, + new_data: Scalar, cx: &impl HasDataLayout, ) -> InterpResult<'tcx> { match self.keys.get_mut(&key) { @@ -132,7 +132,7 @@ impl<'tcx> TlsData<'tcx> { &mut self, thread: ThreadId, dtor: ty::Instance<'tcx>, - data: Scalar, + data: Scalar, ) -> InterpResult<'tcx> { if self.macos_thread_dtors.insert(thread, (dtor, data)).is_some() { throw_unsup_format!( @@ -165,7 +165,7 @@ impl<'tcx> TlsData<'tcx> { &mut self, key: Option, thread_id: ThreadId, - ) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)> { + ) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)> { use std::ops::Bound::*; let thread_local = &mut self.keys; @@ -228,14 +228,14 @@ enum TlsDtorsStatePriv<'tcx> { PthreadDtors(RunningDtorState), /// For Windows Dtors, we store the list of functions that we still have to call. /// These are functions from the magic `.CRT$XLB` linker section. - WindowsDtors(Vec>), + WindowsDtors(Vec>), Done, } impl<'tcx> TlsDtorsState<'tcx> { pub fn on_stack_empty( &mut self, - this: &mut MiriInterpCx<'_, 'tcx>, + this: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, Poll<()>> { use TlsDtorsStatePriv::*; let new_state = 'new_state: { @@ -282,7 +282,7 @@ impl<'tcx> TlsDtorsState<'tcx> { } } Done => { - this.machine.tls.delete_all_thread_tls(this.get_active_thread()); + this.machine.tls.delete_all_thread_tls(this.active_thread()); return Ok(Poll::Ready(())); } } @@ -293,11 +293,11 @@ impl<'tcx> TlsDtorsState<'tcx> { } } -impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {} +trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Schedule TLS destructors for Windows. /// On windows, TLS destructors are managed by std. - fn lookup_windows_tls_dtors(&mut self) -> InterpResult<'tcx, Vec>> { + fn lookup_windows_tls_dtors(&mut self) -> InterpResult<'tcx, Vec>> { let this = self.eval_context_mut(); // Windows has a special magic linker section that is run on certain events. @@ -305,7 +305,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(this.lookup_link_section(".CRT$XLB")?) } - fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx, Provenance>) -> InterpResult<'tcx> { + fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let dtor = dtor.to_scalar().to_pointer(this)?; @@ -332,7 +332,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// executed. fn schedule_macos_tls_dtor(&mut self) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let thread_id = this.get_active_thread(); + let thread_id = this.active_thread(); if let Some((instance, data)) = this.machine.tls.macos_thread_dtors.remove(&thread_id) { trace!("Running macos dtor {:?} on {:?} at {:?}", instance, data, thread_id); @@ -354,7 +354,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { state: &mut RunningDtorState, ) -> InterpResult<'tcx, Poll<()>> { let this = self.eval_context_mut(); - let active_thread = this.get_active_thread(); + let active_thread = this.active_thread(); // Fetch next dtor after `key`. let dtor = match this.machine.tls.fetch_tls_dtor(state.last_key, active_thread) { diff --git a/src/tools/miri/src/shims/unix/android/foreign_items.rs b/src/tools/miri/src/shims/unix/android/foreign_items.rs new file mode 100644 index 000000000000..42552a51edae --- /dev/null +++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs @@ -0,0 +1,32 @@ +use rustc_span::Symbol; +use rustc_target::spec::abi::Abi; + +use crate::*; + +pub fn is_dyn_sym(_name: &str) -> bool { + false +} + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn emulate_foreign_item_inner( + &mut self, + link_name: Symbol, + abi: Abi, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx, EmulateItemResult> { + let this = self.eval_context_mut(); + match link_name.as_str() { + // Miscellaneous + "__errno" => { + let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let errno_place = this.last_error_place()?; + this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; + } + + _ => return Ok(EmulateItemResult::NotSupported), + } + Ok(EmulateItemResult::NeedsReturn) + } +} diff --git a/src/tools/miri/src/shims/unix/android/mod.rs b/src/tools/miri/src/shims/unix/android/mod.rs new file mode 100644 index 000000000000..09c6507b24f8 --- /dev/null +++ b/src/tools/miri/src/shims/unix/android/mod.rs @@ -0,0 +1 @@ +pub mod foreign_items; diff --git a/src/tools/miri/src/shims/unix/env.rs b/src/tools/miri/src/shims/unix/env.rs index 6fcfb6939153..2f78d0f42967 100644 --- a/src/tools/miri/src/shims/unix/env.rs +++ b/src/tools/miri/src/shims/unix/env.rs @@ -13,10 +13,10 @@ use crate::*; pub struct UnixEnvVars<'tcx> { /// Stores pointers to the environment variables. These variables must be stored as /// null-terminated target strings (c_str or wide_str) with the `"{name}={value}"` format. - map: FxHashMap>>, + map: FxHashMap, /// Place where the `environ` static is stored. Lazily initialized, but then never changes. - environ: MPlaceTy<'tcx, Provenance>, + environ: MPlaceTy<'tcx>, } impl VisitProvenance for UnixEnvVars<'_> { @@ -31,8 +31,8 @@ impl VisitProvenance for UnixEnvVars<'_> { } impl<'tcx> UnixEnvVars<'tcx> { - pub(crate) fn new<'mir>( - ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, + pub(crate) fn new( + ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>, env_vars: FxHashMap, ) -> InterpResult<'tcx, Self> { // Allocate memory for all these env vars. @@ -51,9 +51,7 @@ impl<'tcx> UnixEnvVars<'tcx> { Ok(UnixEnvVars { map: env_vars_machine, environ }) } - pub(crate) fn cleanup<'mir>( - ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, - ) -> InterpResult<'tcx> { + pub(crate) fn cleanup(ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>) -> InterpResult<'tcx> { // Deallocate individual env vars. let env_vars = mem::take(&mut ecx.machine.env_vars.unix_mut().map); for (_name, ptr) in env_vars { @@ -67,15 +65,15 @@ impl<'tcx> UnixEnvVars<'tcx> { Ok(()) } - pub(crate) fn environ(&self) -> Pointer> { + pub(crate) fn environ(&self) -> Pointer { self.environ.ptr() } - fn get_ptr<'mir>( + fn get_ptr( &self, - ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, name: &OsStr, - ) -> InterpResult<'tcx, Option>>> { + ) -> InterpResult<'tcx, Option> { // We don't care about the value as we have the `map` to keep track of everything, // but we do want to do this read so it shows up as a data race. let _vars_ptr = ecx.read_pointer(&self.environ)?; @@ -92,9 +90,9 @@ impl<'tcx> UnixEnvVars<'tcx> { /// Implementation detail for [`InterpCx::get_env_var`]. This basically does `getenv`, complete /// with the reads of the environment, but returns an [`OsString`] instead of a pointer. - pub(crate) fn get<'mir>( + pub(crate) fn get( &self, - ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, name: &OsStr, ) -> InterpResult<'tcx, Option> { let var_ptr = self.get_ptr(ecx, name)?; @@ -107,11 +105,11 @@ impl<'tcx> UnixEnvVars<'tcx> { } } -fn alloc_env_var<'mir, 'tcx>( - ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, +fn alloc_env_var<'tcx>( + ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>, name: &OsStr, value: &OsStr, -) -> InterpResult<'tcx, Pointer>> { +) -> InterpResult<'tcx, Pointer> { let mut name_osstring = name.to_os_string(); name_osstring.push("="); name_osstring.push(value); @@ -119,10 +117,10 @@ fn alloc_env_var<'mir, 'tcx>( } /// Allocates an `environ` block with the given list of pointers. -fn alloc_environ_block<'mir, 'tcx>( - ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, - mut vars: Vec>>, -) -> InterpResult<'tcx, Pointer>> { +fn alloc_environ_block<'tcx>( + ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>, + mut vars: Vec, +) -> InterpResult<'tcx, Pointer> { // Add trailing null. vars.push(Pointer::null()); // Make an array with all these pointers inside Miri. @@ -139,12 +137,9 @@ fn alloc_environ_block<'mir, 'tcx>( Ok(vars_place.ptr()) } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - fn getenv( - &mut self, - name_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Pointer>> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn getenv(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, Pointer> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("getenv"); @@ -155,11 +150,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(var_ptr.unwrap_or_else(Pointer::null)) } - fn setenv( - &mut self, - name_op: &OpTy<'tcx, Provenance>, - value_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + fn setenv(&mut self, name_op: &OpTy<'tcx>, value_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("setenv"); @@ -189,7 +180,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn unsetenv(&mut self, name_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { + fn unsetenv(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("unsetenv"); @@ -215,11 +206,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn getcwd( - &mut self, - buf_op: &OpTy<'tcx, Provenance>, - size_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Pointer>> { + fn getcwd(&mut self, buf_op: &OpTy<'tcx>, size_op: &OpTy<'tcx>) -> InterpResult<'tcx, Pointer> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("getcwd"); @@ -247,7 +234,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(Pointer::null()) } - fn chdir(&mut self, path_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { + fn chdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("chdir"); diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index a53cd607ef06..b6ac841dc9f4 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -7,7 +7,6 @@ use std::collections::BTreeMap; use std::io::{self, ErrorKind, IsTerminal, Read, SeekFrom, Write}; use std::rc::Rc; -use rustc_middle::ty::TyCtxt; use rustc_target::abi::Size; use crate::shims::unix::*; @@ -22,7 +21,7 @@ pub trait FileDescription: std::fmt::Debug + Any { &mut self, _communicate_allowed: bool, _bytes: &mut [u8], - _tcx: TyCtxt<'tcx>, + _ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result> { throw_unsup_format!("cannot read from {}", self.name()); } @@ -32,7 +31,7 @@ pub trait FileDescription: std::fmt::Debug + Any { &mut self, _communicate_allowed: bool, _bytes: &[u8], - _tcx: TyCtxt<'tcx>, + _ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result> { throw_unsup_format!("cannot write to {}", self.name()); } @@ -82,7 +81,7 @@ impl FileDescription for io::Stdin { &mut self, communicate_allowed: bool, bytes: &mut [u8], - _tcx: TyCtxt<'tcx>, + _ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result> { if !communicate_allowed { // We want isolation mode to be deterministic, so we have to disallow all reads, even stdin. @@ -105,7 +104,7 @@ impl FileDescription for io::Stdout { &mut self, _communicate_allowed: bool, bytes: &[u8], - _tcx: TyCtxt<'tcx>, + _ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result> { // We allow writing to stderr even with isolation enabled. let result = Write::write(self, bytes); @@ -133,7 +132,7 @@ impl FileDescription for io::Stderr { &mut self, _communicate_allowed: bool, bytes: &[u8], - _tcx: TyCtxt<'tcx>, + _ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result> { // We allow writing to stderr even with isolation enabled. // No need to flush, stderr is not buffered. @@ -158,7 +157,7 @@ impl FileDescription for NullOutput { &mut self, _communicate_allowed: bool, bytes: &[u8], - _tcx: TyCtxt<'tcx>, + _ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result> { // We just don't write anything, but report to the user that we did. Ok(Ok(bytes.len())) @@ -173,6 +172,14 @@ impl FileDescriptor { FileDescriptor(Rc::new(RefCell::new(Box::new(fd)))) } + pub fn borrow(&self) -> Ref<'_, dyn FileDescription> { + Ref::map(self.0.borrow(), |fd| fd.as_ref()) + } + + pub fn borrow_mut(&self) -> RefMut<'_, dyn FileDescription> { + RefMut::map(self.0.borrow_mut(), |fd| fd.as_mut()) + } + pub fn close<'ctx>(self, communicate_allowed: bool) -> InterpResult<'ctx, io::Result<()>> { // Destroy this `Rc` using `into_inner` so we can call `close` instead of // implicitly running the destructor of the file description. @@ -242,12 +249,12 @@ impl FdTable { pub fn get(&self, fd: i32) -> Option> { let fd = self.fds.get(&fd)?; - Some(Ref::map(fd.0.borrow(), |fd| fd.as_ref())) + Some(fd.borrow()) } pub fn get_mut(&self, fd: i32) -> Option> { let fd = self.fds.get(&fd)?; - Some(RefMut::map(fd.0.borrow_mut(), |fd| fd.as_mut())) + Some(fd.borrow_mut()) } pub fn dup(&self, fd: i32) -> Option { @@ -264,9 +271,9 @@ impl FdTable { } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - fn fcntl(&mut self, args: &[OpTy<'tcx, Provenance>]) -> InterpResult<'tcx, i32> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); if args.len() < 2 { @@ -322,7 +329,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn close(&mut self, fd_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, Scalar> { + fn close(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let fd = this.read_scalar(fd_op)?.to_i32()?; @@ -348,12 +355,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok((-1).into()) } - fn read( - &mut self, - fd: i32, - buf: Pointer>, - count: u64, - ) -> InterpResult<'tcx, i64> { + fn read(&mut self, fd: i32, buf: Pointer, count: u64) -> InterpResult<'tcx, i64> { let this = self.eval_context_mut(); // Isolation check is done via `FileDescriptor` trait. @@ -370,7 +372,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { .min(u64::try_from(isize::MAX).unwrap()); let communicate = this.machine.communicate(); - let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else { + // We temporarily dup the FD to be able to retain mutable access to `this`. + let Some(file_descriptor) = this.machine.fds.dup(fd) else { trace!("read: FD not found"); return this.fd_not_found(); }; @@ -383,7 +386,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // `File::read` never returns a value larger than `count`, // so this cannot fail. let result = file_descriptor - .read(communicate, &mut bytes, *this.tcx)? + .borrow_mut() + .read(communicate, &mut bytes, this)? .map(|c| i64::try_from(c).unwrap()); drop(file_descriptor); @@ -400,12 +404,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn write( - &mut self, - fd: i32, - buf: Pointer>, - count: u64, - ) -> InterpResult<'tcx, i64> { + fn write(&mut self, fd: i32, buf: Pointer, count: u64) -> InterpResult<'tcx, i64> { let this = self.eval_context_mut(); // Isolation check is done via `FileDescriptor` trait. @@ -421,12 +420,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let communicate = this.machine.communicate(); let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned(); - let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else { + // We temporarily dup the FD to be able to retain mutable access to `this`. + let Some(file_descriptor) = this.machine.fds.dup(fd) else { return this.fd_not_found(); }; let result = file_descriptor - .write(communicate, &bytes, *this.tcx)? + .borrow_mut() + .write(communicate, &bytes, this)? .map(|c| i64::try_from(c).unwrap()); drop(file_descriptor); diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 08f64e499b55..2282099fa0d2 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -3,13 +3,13 @@ use std::str; use rustc_middle::ty::layout::LayoutOf; use rustc_span::Symbol; -use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi; use crate::shims::alloc::EvalContextExt as _; use crate::shims::unix::*; use crate::*; +use shims::unix::android::foreign_items as android; use shims::unix::freebsd::foreign_items as freebsd; use shims::unix::linux::foreign_items as linux; use shims::unix::macos::foreign_items as macos; @@ -27,6 +27,7 @@ pub fn is_dyn_sym(name: &str, target_os: &str) -> bool { // Give specific OSes a chance to allow their symbols. _ => match target_os { + "android" => android::is_dyn_sym(name), "freebsd" => freebsd::is_dyn_sym(name), "linux" => linux::is_dyn_sym(name), "macos" => macos::is_dyn_sym(name), @@ -36,14 +37,14 @@ pub fn is_dyn_sym(name: &str, target_os: &str) -> bool { } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_foreign_item_inner( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); @@ -249,28 +250,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Allocation "posix_memalign" => { - let [ret, align, size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let ret = this.deref_pointer(ret)?; - let align = this.read_target_usize(align)?; - let size = this.read_target_usize(size)?; - // Align must be power of 2, and also at least ptr-sized (POSIX rules). - // But failure to adhere to this is not UB, it's an error condition. - if !align.is_power_of_two() || align < this.pointer_size().bytes() { - let einval = this.eval_libc_i32("EINVAL"); - this.write_int(einval, dest)?; - } else { - if size == 0 { - this.write_null(&ret)?; - } else { - let ptr = this.allocate_ptr( - Size::from_bytes(size), - Align::from_bytes(align).unwrap(), - MiriMemoryKind::C.into(), - )?; - this.write_pointer(ptr, &ret)?; - } - this.write_null(dest)?; - } + let [memptr, align, size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let result = this.posix_memalign(memptr, align, size)?; + this.write_scalar(result, dest)?; } "mmap" => { @@ -287,7 +269,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "reallocarray" => { // Currently this function does not exist on all Unixes, e.g. on macOS. - if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "android") { throw_unsup_format!( "`reallocarray` is not supported on {}", this.tcx.sess.target.os @@ -315,6 +297,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } } + "aligned_alloc" => { + // This is a C11 function, we assume all Unixes have it. + // (MSVC explicitly does not support this.) + let [align, size] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let res = this.aligned_alloc(align, size)?; + this.write_pointer(res, dest)?; + } // Dynamic symbol loading "dlsym" => { @@ -336,7 +326,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let name = this.read_scalar(name)?.to_i32()?; // FIXME: Which of these are POSIX, and which are GNU/Linux? // At least the names seem to all also exist on macOS. - let sysconfs: &[(&str, fn(&MiriInterpCx<'_, '_>) -> Scalar)] = &[ + let sysconfs: &[(&str, fn(&MiriInterpCx<'_>) -> Scalar)] = &[ ("_SC_PAGESIZE", |this| Scalar::from_int(this.machine.page_size, this.pointer_size())), ("_SC_NPROCESSORS_CONF", |this| Scalar::from_int(this.machine.num_cpus, this.pointer_size())), ("_SC_NPROCESSORS_ONLN", |this| Scalar::from_int(this.machine.num_cpus, this.pointer_size())), @@ -398,14 +388,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "pthread_getspecific" => { let [key] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; - let active_thread = this.get_active_thread(); + let active_thread = this.active_thread(); let ptr = this.machine.tls.load_tls(key, active_thread, this)?; this.write_scalar(ptr, dest)?; } "pthread_setspecific" => { let [key, new_ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; - let active_thread = this.get_active_thread(); + let active_thread = this.active_thread(); let new_data = this.read_scalar(new_ptr)?; this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?; @@ -436,8 +426,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } "pthread_mutex_lock" => { let [mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_mutex_lock(mutex)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_mutex_lock(mutex, dest)?; } "pthread_mutex_trylock" => { let [mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -456,8 +445,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } "pthread_rwlock_rdlock" => { let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_rwlock_rdlock(rwlock)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_rwlock_rdlock(rwlock, dest)?; } "pthread_rwlock_tryrdlock" => { let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -466,8 +454,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } "pthread_rwlock_wrlock" => { let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_rwlock_wrlock(rwlock)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_rwlock_wrlock(rwlock, dest)?; } "pthread_rwlock_trywrlock" => { let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -523,8 +510,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } "pthread_cond_wait" => { let [cond, mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_cond_wait(cond, mutex)?; - this.write_scalar(Scalar::from_i32(result), dest)?; + this.pthread_cond_wait(cond, mutex, dest)?; } "pthread_cond_timedwait" => { let [cond, mutex, abstime] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -605,7 +591,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "getentropy" => { // This function is non-standard but exists with the same signature and behavior on // Linux, macOS, FreeBSD and Solaris/Illumos. - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "freebsd" | "illumos" | "solaris") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "freebsd" | "illumos" | "solaris" | "android") { throw_unsup_format!( "`getentropy` is not supported on {}", this.tcx.sess.target.os @@ -634,9 +620,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "getrandom" => { // This function is non-standard but exists with the same signature and behavior on // Linux, FreeBSD and Solaris/Illumos. - if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris" | "android") { throw_unsup_format!( - "`getentropy` is not supported on {}", + "`getrandom` is not supported on {}", this.tcx.sess.target.os ); } @@ -649,6 +635,31 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.gen_random(ptr, len)?; this.write_scalar(Scalar::from_target_usize(len, this), dest)?; } + "_Unwind_RaiseException" => { + // This is not formally part of POSIX, but it is very wide-spread on POSIX systems. + // It was originally specified as part of the Itanium C++ ABI: + // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw. + // On Linux it is + // documented as part of the LSB: + // https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/baselib--unwind-raiseexception.html + // Basically every other UNIX uses the exact same api though. Arm also references + // back to the Itanium C++ ABI for the definition of `_Unwind_RaiseException` for + // arm64: + // https://github.com/ARM-software/abi-aa/blob/main/cppabi64/cppabi64.rst#toc-entry-35 + // For arm32 they did something custom, but similar enough that the same + // `_Unwind_RaiseException` impl in miri should work: + // https://github.com/ARM-software/abi-aa/blob/main/ehabi32/ehabi32.rst + if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris" | "android" | "macos") { + throw_unsup_format!( + "`_Unwind_RaiseException` is not supported on {}", + this.tcx.sess.target.os + ); + } + // This function looks and behaves excatly like miri_start_unwind. + let [payload] = this.check_shim(abi, Abi::C { unwind: true }, link_name, args)?; + this.handle_miri_start_unwind(payload)?; + return Ok(EmulateItemResult::NeedsUnwind); + } // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. @@ -718,8 +729,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_int(super::UID, dest)?; } - "getpwuid_r" + "getpwuid_r" | "__posix_getpwuid_r" if this.frame_in_std() => { + // getpwuid_r is the standard name, __posix_getpwuid_r is used on solarish let [uid, pwd, buf, buflen, result] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; this.check_no_isolation("`getpwuid_r`")?; @@ -759,6 +771,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => { let target_os = &*this.tcx.sess.target.os; return match target_os { + "android" => android::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), "freebsd" => freebsd::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), "linux" => linux::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), "macos" => macos::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), @@ -768,6 +781,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } }; - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index e70cd35dda6d..36f25767a8ea 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -8,14 +8,14 @@ pub fn is_dyn_sym(_name: &str) -> bool { false } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_foreign_item_inner( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); match link_name.as_str() { @@ -86,6 +86,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index eb241556a569..262e71756c64 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -9,7 +9,6 @@ use std::path::{Path, PathBuf}; use std::time::SystemTime; use rustc_data_structures::fx::FxHashMap; -use rustc_middle::ty::TyCtxt; use rustc_target::abi::Size; use crate::shims::os_str::bytes_to_os_str; @@ -34,7 +33,7 @@ impl FileDescription for FileHandle { &mut self, communicate_allowed: bool, bytes: &mut [u8], - _tcx: TyCtxt<'tcx>, + _ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result> { assert!(communicate_allowed, "isolation should have prevented even opening a file"); Ok(self.file.read(bytes)) @@ -44,7 +43,7 @@ impl FileDescription for FileHandle { &mut self, communicate_allowed: bool, bytes: &[u8], - _tcx: TyCtxt<'tcx>, + _ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result> { assert!(communicate_allowed, "isolation should have prevented even opening a file"); Ok(self.file.write(bytes)) @@ -87,12 +86,12 @@ impl FileDescription for FileHandle { } } -impl<'mir, 'tcx: 'mir> EvalContextExtPrivate<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExtPrivate<'tcx> for crate::MiriInterpCx<'tcx> {} +trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> { fn macos_stat_write_buf( &mut self, metadata: FileMetadata, - buf_op: &OpTy<'tcx, Provenance>, + buf_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -181,7 +180,7 @@ struct OpenDir { read_dir: ReadDir, /// The most recent entry returned by readdir(). /// Will be freed by the next call. - entry: Option>>, + entry: Option, } impl OpenDir { @@ -255,9 +254,9 @@ fn maybe_sync_file( } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - fn open(&mut self, args: &[OpTy<'tcx, Provenance>]) -> InterpResult<'tcx, i32> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn open(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, i32> { if args.len() < 2 { throw_ub_format!( "incorrect number of arguments for `open`: got {}, expected at least 2", @@ -390,12 +389,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.try_unwrap_io_result(fd) } - fn lseek64( - &mut self, - fd: i32, - offset: i128, - whence: i32, - ) -> InterpResult<'tcx, Scalar> { + fn lseek64(&mut self, fd: i32, offset: i128, whence: i32) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); // Isolation check is done via `FileDescriptor` trait. @@ -426,7 +420,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(Scalar::from_i64(result)) } - fn unlink(&mut self, path_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { + fn unlink(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; @@ -444,8 +438,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn symlink( &mut self, - target_op: &OpTy<'tcx, Provenance>, - linkpath_op: &OpTy<'tcx, Provenance>, + target_op: &OpTy<'tcx>, + linkpath_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, i32> { #[cfg(unix)] fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> { @@ -475,9 +469,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn macos_fbsd_stat( &mut self, - path_op: &OpTy<'tcx, Provenance>, - buf_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + path_op: &OpTy<'tcx>, + buf_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") { @@ -507,9 +501,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // `lstat` is used to get symlink metadata. fn macos_fbsd_lstat( &mut self, - path_op: &OpTy<'tcx, Provenance>, - buf_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + path_op: &OpTy<'tcx>, + buf_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") { @@ -537,9 +531,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn macos_fbsd_fstat( &mut self, - fd_op: &OpTy<'tcx, Provenance>, - buf_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + fd_op: &OpTy<'tcx>, + buf_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") { @@ -564,11 +558,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn linux_statx( &mut self, - dirfd_op: &OpTy<'tcx, Provenance>, // Should be an `int` - pathname_op: &OpTy<'tcx, Provenance>, // Should be a `const char *` - flags_op: &OpTy<'tcx, Provenance>, // Should be an `int` - mask_op: &OpTy<'tcx, Provenance>, // Should be an `unsigned int` - statxbuf_op: &OpTy<'tcx, Provenance>, // Should be a `struct statx *` + dirfd_op: &OpTy<'tcx>, // Should be an `int` + pathname_op: &OpTy<'tcx>, // Should be a `const char *` + flags_op: &OpTy<'tcx>, // Should be an `int` + mask_op: &OpTy<'tcx>, // Should be an `unsigned int` + statxbuf_op: &OpTy<'tcx>, // Should be a `struct statx *` ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -746,8 +740,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn rename( &mut self, - oldpath_op: &OpTy<'tcx, Provenance>, - newpath_op: &OpTy<'tcx, Provenance>, + oldpath_op: &OpTy<'tcx>, + newpath_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -775,11 +769,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.try_unwrap_io_result(result) } - fn mkdir( - &mut self, - path_op: &OpTy<'tcx, Provenance>, - mode_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + fn mkdir(&mut self, path_op: &OpTy<'tcx>, mode_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); #[cfg_attr(not(unix), allow(unused_variables))] @@ -814,7 +804,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.try_unwrap_io_result(result) } - fn rmdir(&mut self, path_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { + fn rmdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; @@ -831,10 +821,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.try_unwrap_io_result(result) } - fn opendir( - &mut self, - name_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + fn opendir(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let name = this.read_path_from_c_str(this.read_pointer(name_op)?)?; @@ -865,10 +852,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn linux_readdir64( - &mut self, - dirp_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + fn linux_readdir64(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os("linux", "readdir64"); @@ -909,14 +893,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let dirent64_layout = this.libc_ty_layout("dirent64"); let d_name_offset = dirent64_layout.fields.offset(4 /* d_name */).bytes(); - let size = d_name_offset.checked_add(name_len).unwrap(); + let size = d_name_offset.strict_add(name_len); let entry = this.allocate_ptr( Size::from_bytes(size), dirent64_layout.align.abi, MiriMemoryKind::Runtime.into(), )?; - let entry: Pointer> = entry.into(); + let entry: Pointer = entry.into(); // If the host is a Unix system, fill in the inode number with its real value. // If not, use 0 as a fallback value. @@ -963,10 +947,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn macos_fbsd_readdir_r( &mut self, - dirp_op: &OpTy<'tcx, Provenance>, - entry_op: &OpTy<'tcx, Provenance>, - result_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + dirp_op: &OpTy<'tcx>, + entry_op: &OpTy<'tcx>, + result_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") { @@ -991,7 +975,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // The name is written with write_os_str_to_c_str, while the rest of the // dirent struct is written using write_int_fields. - // For reference: + // For reference, on macOS this looks like: // pub struct dirent { // pub d_ino: u64, // pub d_seekoff: u64, @@ -1010,7 +994,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { name_place.ptr(), name_place.layout.size.bytes(), )?; - let file_name_len = file_name_buf_len.checked_sub(1).unwrap(); + let file_name_len = file_name_buf_len.strict_sub(1); if !name_fits { throw_unsup_format!( "a directory entry had a name too large to fit in libc::dirent" @@ -1026,40 +1010,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let file_type = this.file_type_to_d_type(dir_entry.file_type())?; - // macOS offset field is d_seekoff - if this.projectable_has_field(&entry_place, "d_seekoff") { - this.write_int_fields_named( - &[ - ("d_ino", ino.into()), - ("d_seekoff", 0), - ("d_reclen", 0), - ("d_namlen", file_name_len.into()), - ("d_type", file_type.into()), - ], - &entry_place, - )?; - } else if this.projectable_has_field(&entry_place, "d_off") { - // freebsd 12 and onwards had added the d_off field - this.write_int_fields_named( - &[ - ("d_fileno", ino.into()), - ("d_off", 0), - ("d_reclen", 0), - ("d_type", file_type.into()), - ("d_namlen", file_name_len.into()), - ], - &entry_place, - )?; - } else { - this.write_int_fields_named( - &[ - ("d_fileno", ino.into()), - ("d_reclen", 0), - ("d_type", file_type.into()), - ("d_namlen", file_name_len.into()), - ], - &entry_place, - )?; + // Common fields. + this.write_int_fields_named( + &[ + ("d_reclen", 0), + ("d_namlen", file_name_len.into()), + ("d_type", file_type.into()), + ], + &entry_place, + )?; + // Special fields. + match &*this.tcx.sess.target.os { + "macos" => { + #[rustfmt::skip] + this.write_int_fields_named( + &[ + ("d_ino", ino.into()), + ("d_seekoff", 0), + ], + &entry_place, + )?; + } + "freebsd" => { + this.write_int(ino, &this.project_field_named(&entry_place, "d_fileno")?)?; + // `d_off` only exists on FreeBSD 12+, but we support v11 as well. + // `libc` uses a build script to determine which version of the API to use, + // and cross-builds always end up using v11. + // To support both v11 and v12+, we dynamically check whether the field exists. + if this.projectable_has_field(&entry_place, "d_off") { + this.write_int(0, &this.project_field_named(&entry_place, "d_off")?)?; + } + } + _ => unreachable!(), } let result_place = this.deref_pointer(result_op)?; @@ -1086,7 +1068,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { })) } - fn closedir(&mut self, dirp_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { + fn closedir(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let dirp = this.read_target_usize(dirp_op)?; @@ -1109,7 +1091,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn ftruncate64(&mut self, fd: i32, length: i128) -> InterpResult<'tcx, Scalar> { + fn ftruncate64(&mut self, fd: i32, length: i128) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); // Reject if isolation is enabled. @@ -1150,7 +1132,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn fsync(&mut self, fd_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { + fn fsync(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { // On macOS, `fsync` (unlike `fcntl(F_FULLFSYNC)`) does not wait for the // underlying disk to finish writing. In the interest of host compatibility, // we conservatively implement this with `sync_all`, which @@ -1185,7 +1167,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.try_unwrap_io_result(io_result) } - fn fdatasync(&mut self, fd_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { + fn fdatasync(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let fd = this.read_scalar(fd_op)?.to_i32()?; @@ -1212,11 +1194,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn sync_file_range( &mut self, - fd_op: &OpTy<'tcx, Provenance>, - offset_op: &OpTy<'tcx, Provenance>, - nbytes_op: &OpTy<'tcx, Provenance>, - flags_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + fd_op: &OpTy<'tcx>, + offset_op: &OpTy<'tcx>, + nbytes_op: &OpTy<'tcx>, + flags_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let fd = this.read_scalar(fd_op)?.to_i32()?; @@ -1262,9 +1244,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn readlink( &mut self, - pathname_op: &OpTy<'tcx, Provenance>, - buf_op: &OpTy<'tcx, Provenance>, - bufsize_op: &OpTy<'tcx, Provenance>, + pathname_op: &OpTy<'tcx>, + buf_op: &OpTy<'tcx>, + bufsize_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, i64> { let this = self.eval_context_mut(); @@ -1305,10 +1287,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn isatty( - &mut self, - miri_fd: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + fn isatty(&mut self, miri_fd: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); // "returns 1 if fd is an open file descriptor referring to a terminal; // otherwise 0 is returned, and errno is set to indicate the error" @@ -1329,9 +1308,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn realpath( &mut self, - path_op: &OpTy<'tcx, Provenance>, - processed_path_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + path_op: &OpTy<'tcx>, + processed_path_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("realpath"); @@ -1387,7 +1366,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } } - fn mkstemp(&mut self, template_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { + fn mkstemp(&mut self, template_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { use rand::seq::SliceRandom; // POSIX defines the template string. @@ -1534,7 +1513,7 @@ fn extract_sec_and_nsec<'tcx>( /// Stores a file's metadata in order to avoid code duplication in the different metadata related /// shims. struct FileMetadata { - mode: Scalar, + mode: Scalar, size: u64, created: Option<(u64, u32)>, accessed: Option<(u64, u32)>, @@ -1543,7 +1522,7 @@ struct FileMetadata { impl FileMetadata { fn from_path<'tcx>( - ecx: &mut MiriInterpCx<'_, 'tcx>, + ecx: &mut MiriInterpCx<'tcx>, path: &Path, follow_symlink: bool, ) -> InterpResult<'tcx, Option> { @@ -1554,7 +1533,7 @@ impl FileMetadata { } fn from_fd<'tcx>( - ecx: &mut MiriInterpCx<'_, 'tcx>, + ecx: &mut MiriInterpCx<'tcx>, fd: i32, ) -> InterpResult<'tcx, Option> { let Some(file_descriptor) = ecx.machine.fds.get(fd) else { @@ -1576,7 +1555,7 @@ impl FileMetadata { } fn from_meta<'tcx>( - ecx: &mut MiriInterpCx<'_, 'tcx>, + ecx: &mut MiriInterpCx<'tcx>, metadata: Result, ) -> InterpResult<'tcx, Option> { let metadata = match metadata { diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index 48a0ba019760..a5661460e95c 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -25,10 +25,10 @@ struct Epoll { struct EpollEvent { #[allow(dead_code)] events: u32, - /// `Scalar` is used to represent the + /// `Scalar` is used to represent the /// `epoll_data` type union. #[allow(dead_code)] - data: Scalar, + data: Scalar, } impl FileDescription for Epoll { @@ -44,26 +44,26 @@ impl FileDescription for Epoll { } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// This function returns a file descriptor referring to the new `Epoll` instance. This file /// descriptor is used for all subsequent calls to the epoll interface. If the `flags` argument /// is 0, then this function is the same as `epoll_create()`. /// /// - fn epoll_create1( - &mut self, - flags: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + fn epoll_create1(&mut self, flags: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let flags = this.read_scalar(flags)?.to_i32()?; let epoll_cloexec = this.eval_libc_i32("EPOLL_CLOEXEC"); - if flags == epoll_cloexec { - // Miri does not support exec, so this flag has no effect. - } else if flags != 0 { - throw_unsup_format!("epoll_create1 flags {flags} are not implemented"); + + // Miri does not support exec, so EPOLL_CLOEXEC flag has no effect. + if flags != epoll_cloexec && flags != 0 { + throw_unsup_format!( + "epoll_create1: flag {:#x} is unsupported, only 0 or EPOLL_CLOEXEC are allowed", + flags + ); } let fd = this.machine.fds.insert_fd(FileDescriptor::new(Epoll::default())); @@ -85,11 +85,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// fn epoll_ctl( &mut self, - epfd: &OpTy<'tcx, Provenance>, - op: &OpTy<'tcx, Provenance>, - fd: &OpTy<'tcx, Provenance>, - event: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + epfd: &OpTy<'tcx>, + op: &OpTy<'tcx>, + fd: &OpTy<'tcx>, + event: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let epfd = this.read_scalar(epfd)?.to_i32()?; @@ -167,11 +167,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// fn epoll_wait( &mut self, - epfd: &OpTy<'tcx, Provenance>, - events: &OpTy<'tcx, Provenance>, - maxevents: &OpTy<'tcx, Provenance>, - timeout: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + epfd: &OpTy<'tcx>, + events: &OpTy<'tcx>, + maxevents: &OpTy<'tcx>, + timeout: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let epfd = this.read_scalar(epfd)?.to_i32()?; diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux/eventfd.rs index a865f2efff95..cae5abca3bd0 100644 --- a/src/tools/miri/src/shims/unix/linux/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux/eventfd.rs @@ -1,15 +1,21 @@ //! Linux `eventfd` implementation. -//! Currently just a stub. use std::io; +use std::io::{Error, ErrorKind}; +use std::mem; -use rustc_middle::ty::TyCtxt; use rustc_target::abi::Endian; use crate::shims::unix::*; -use crate::*; +use crate::{concurrency::VClock, *}; use self::shims::unix::fd::FileDescriptor; +// We'll only do reads and writes in chunks of size u64. +const U64_ARRAY_SIZE: usize = mem::size_of::(); + +/// Maximum value that the eventfd counter can hold. +const MAX_COUNTER: u64 = u64::MAX - 1; + /// A kind of file descriptor created by `eventfd`. /// The `Event` type isn't currently written to by `eventfd`. /// The interface is meant to keep track of objects associated @@ -21,7 +27,9 @@ use self::shims::unix::fd::FileDescriptor; struct Event { /// The object contains an unsigned 64-bit integer (uint64_t) counter that is maintained by the /// kernel. This counter is initialized with the value specified in the argument initval. - val: u64, + counter: u64, + is_nonblock: bool, + clock: VClock, } impl FileDescription for Event { @@ -36,6 +44,38 @@ impl FileDescription for Event { Ok(Ok(())) } + /// Read the counter in the buffer and return the counter if succeeded. + fn read<'tcx>( + &mut self, + _communicate_allowed: bool, + bytes: &mut [u8], + ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, io::Result> { + // Check the size of slice, and return error only if the size of the slice < 8. + let Some(bytes) = bytes.first_chunk_mut::() else { + return Ok(Err(Error::from(ErrorKind::InvalidInput))); + }; + // Block when counter == 0. + if self.counter == 0 { + if self.is_nonblock { + return Ok(Err(Error::from(ErrorKind::WouldBlock))); + } else { + //FIXME: blocking is not supported + throw_unsup_format!("eventfd: blocking is unsupported"); + } + } else { + // Synchronize with all prior `write` calls to this FD. + ecx.acquire_clock(&self.clock); + // Return the counter in the host endianness using the buffer provided by caller. + *bytes = match ecx.tcx.sess.target.endian { + Endian::Little => self.counter.to_le_bytes(), + Endian::Big => self.counter.to_be_bytes(), + }; + self.counter = 0; + return Ok(Ok(U64_ARRAY_SIZE)); + } + } + /// A write call adds the 8-byte integer value supplied in /// its buffer (in native endianness) to the counter. The maximum value that may be /// stored in the counter is the largest unsigned 64-bit value @@ -52,23 +92,46 @@ impl FileDescription for Event { &mut self, _communicate_allowed: bool, bytes: &[u8], - tcx: TyCtxt<'tcx>, + ecx: &mut MiriInterpCx<'tcx>, ) -> InterpResult<'tcx, io::Result> { - let bytes: [u8; 8] = bytes.try_into().unwrap(); // FIXME fail gracefully when this has the wrong size - // Convert from target endianness to host endianness. - let num = match tcx.sess.target.endian { - Endian::Little => u64::from_le_bytes(bytes), - Endian::Big => u64::from_be_bytes(bytes), + // Check the size of slice, and return error only if the size of the slice < 8. + let Some(bytes) = bytes.first_chunk::() else { + return Ok(Err(Error::from(ErrorKind::InvalidInput))); }; - // FIXME handle blocking when addition results in exceeding the max u64 value - // or fail with EAGAIN if the file descriptor is nonblocking. - self.val = self.val.checked_add(num).unwrap(); - Ok(Ok(8)) + // Convert from bytes to int according to host endianness. + let num = match ecx.tcx.sess.target.endian { + Endian::Little => u64::from_le_bytes(*bytes), + Endian::Big => u64::from_be_bytes(*bytes), + }; + // u64::MAX as input is invalid because the maximum value of counter is u64::MAX - 1. + if num == u64::MAX { + return Ok(Err(Error::from(ErrorKind::InvalidInput))); + } + // If the addition does not let the counter to exceed the maximum value, update the counter. + // Else, block. + match self.counter.checked_add(num) { + Some(new_count @ 0..=MAX_COUNTER) => { + // Future `read` calls will synchronize with this write, so update the FD clock. + if let Some(clock) = &ecx.release_clock() { + self.clock.join(clock); + } + self.counter = new_count; + } + None | Some(u64::MAX) => { + if self.is_nonblock { + return Ok(Err(Error::from(ErrorKind::WouldBlock))); + } else { + //FIXME: blocking is not supported + throw_unsup_format!("eventfd: blocking is unsupported"); + } + } + }; + Ok(Ok(U64_ARRAY_SIZE)) } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// This function creates an `Event` that is used as an event wait/notify mechanism by /// user-space applications, and by the kernel to notify user-space applications of events. /// The `Event` contains an `u64` counter maintained by the kernel. The counter is initialized @@ -85,34 +148,43 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// `EFD_SEMAPHORE` - miri does not support semaphore-like semantics. /// /// - fn eventfd( - &mut self, - val: &OpTy<'tcx, Provenance>, - flags: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + fn eventfd(&mut self, val: &OpTy<'tcx>, flags: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); + // eventfd is Linux specific. + this.assert_target_os("linux", "eventfd"); + let val = this.read_scalar(val)?.to_u32()?; - let flags = this.read_scalar(flags)?.to_i32()?; + let mut flags = this.read_scalar(flags)?.to_i32()?; let efd_cloexec = this.eval_libc_i32("EFD_CLOEXEC"); let efd_nonblock = this.eval_libc_i32("EFD_NONBLOCK"); let efd_semaphore = this.eval_libc_i32("EFD_SEMAPHORE"); - if flags & (efd_cloexec | efd_nonblock | efd_semaphore) != flags { - throw_unsup_format!("eventfd: flag {flags:#x} is unsupported"); - } - if flags & efd_cloexec == efd_cloexec { - // cloexec does nothing as we don't support `exec` - } - if flags & efd_nonblock == efd_nonblock { - // FIXME remember the nonblock flag - } if flags & efd_semaphore == efd_semaphore { throw_unsup_format!("eventfd: EFD_SEMAPHORE is unsupported"); } - let fd = this.machine.fds.insert_fd(FileDescriptor::new(Event { val: val.into() })); + let mut is_nonblock = false; + // Unset the flag that we support. + // After unloading, flags != 0 means other flags are used. + if flags & efd_cloexec == efd_cloexec { + // cloexec is ignored because Miri does not support exec. + flags &= !efd_cloexec; + } + if flags & efd_nonblock == efd_nonblock { + flags &= !efd_nonblock; + is_nonblock = true; + } + if flags != 0 { + throw_unsup_format!("eventfd: encountered unknown unsupported flags {:#x}", flags); + } + + let fd = this.machine.fds.insert_fd(FileDescriptor::new(Event { + counter: val.into(), + is_nonblock, + clock: VClock::default(), + })); Ok(Scalar::from_i32(fd)) } } diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index 7cd749a41072..e31d43d9190a 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -14,14 +14,14 @@ pub fn is_dyn_sym(name: &str) -> bool { matches!(name, "statx") } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_foreign_item_inner( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); @@ -203,6 +203,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), }; - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs index 3948216f7297..c430eff0180e 100644 --- a/src/tools/miri/src/shims/unix/linux/mem.rs +++ b/src/tools/miri/src/shims/unix/linux/mem.rs @@ -4,15 +4,15 @@ use crate::*; use rustc_target::abi::Size; -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn mremap( &mut self, - old_address: &OpTy<'tcx, Provenance>, - old_size: &OpTy<'tcx, Provenance>, - new_size: &OpTy<'tcx, Provenance>, - flags: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + old_address: &OpTy<'tcx>, + old_size: &OpTy<'tcx>, + new_size: &OpTy<'tcx>, + flags: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let old_address = this.read_pointer(old_address)?; diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs index d4a6cd96f48d..bd2120390741 100644 --- a/src/tools/miri/src/shims/unix/linux/sync.rs +++ b/src/tools/miri/src/shims/unix/linux/sync.rs @@ -1,14 +1,11 @@ -use std::time::SystemTime; - -use crate::concurrency::thread::MachineCallback; use crate::*; /// Implementation of the SYS_futex syscall. /// `args` is the arguments *after* the syscall number. pub fn futex<'tcx>( - this: &mut MiriInterpCx<'_, 'tcx>, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + this: &mut MiriInterpCx<'tcx>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { // The amount of arguments used depends on the type of futex operation. // The full futex syscall takes six arguments (excluding the syscall @@ -32,7 +29,6 @@ pub fn futex<'tcx>( let op = this.read_scalar(&args[1])?.to_i32()?; let val = this.read_scalar(&args[2])?.to_i32()?; - let thread = this.get_active_thread(); // This is a vararg function so we have to bring our own type for this pointer. let addr = this.ptr_to_mplace(addr, this.machine.layouts.i32); let addr_usize = addr.ptr().addr().bytes(); @@ -86,15 +82,9 @@ pub fn futex<'tcx>( } let timeout = this.deref_pointer_as(&args[3], this.libc_ty_layout("timespec"))?; - let timeout_time = if this.ptr_is_null(timeout.ptr())? { + let timeout = if this.ptr_is_null(timeout.ptr())? { None } else { - let realtime = op & futex_realtime == futex_realtime; - if realtime { - this.check_no_isolation( - "`futex` syscall with `op=FUTEX_WAIT` and non-null timeout with `FUTEX_CLOCK_REALTIME`", - )?; - } let duration = match this.read_timespec(&timeout)? { Some(duration) => duration, None => { @@ -104,27 +94,22 @@ pub fn futex<'tcx>( return Ok(()); } }; - Some(if wait_bitset { + let timeout_clock = if op & futex_realtime == futex_realtime { + this.check_no_isolation( + "`futex` syscall with `op=FUTEX_WAIT` and non-null timeout with `FUTEX_CLOCK_REALTIME`", + )?; + TimeoutClock::RealTime + } else { + TimeoutClock::Monotonic + }; + let timeout_anchor = if wait_bitset { // FUTEX_WAIT_BITSET uses an absolute timestamp. - if realtime { - CallbackTime::RealTime( - SystemTime::UNIX_EPOCH.checked_add(duration).unwrap(), - ) - } else { - CallbackTime::Monotonic( - this.machine.clock.anchor().checked_add(duration).unwrap(), - ) - } + TimeoutAnchor::Absolute } else { // FUTEX_WAIT uses a relative timestamp. - if realtime { - CallbackTime::RealTime(SystemTime::now().checked_add(duration).unwrap()) - } else { - CallbackTime::Monotonic( - this.machine.clock.now().checked_add(duration).unwrap(), - ) - } - }) + TimeoutAnchor::Relative + }; + Some((timeout_clock, timeout_anchor, duration)) }; // There may be a concurrent thread changing the value of addr // and then invoking the FUTEX_WAKE syscall. It is critical that the @@ -158,7 +143,7 @@ pub fn futex<'tcx>( // to see an up-to-date value. // // The above case distinction is valid since both FUTEX_WAIT and FUTEX_WAKE - // contain a SeqCst fence, therefore inducting a total order between the operations. + // contain a SeqCst fence, therefore inducing a total order between the operations. // It is also critical that the fence, the atomic load, and the comparison in FUTEX_WAIT // altogether happen atomically. If the other thread's fence in FUTEX_WAKE // gets interleaved after our fence, then we lose the guarantee on the @@ -174,48 +159,16 @@ pub fn futex<'tcx>( // It's not uncommon for `addr` to be passed as another type than `*mut i32`, such as `*const AtomicI32`. let futex_val = this.read_scalar_atomic(&addr, AtomicReadOrd::Relaxed)?.to_i32()?; if val == futex_val { - // The value still matches, so we block the thread make it wait for FUTEX_WAKE. - this.block_thread(thread, BlockReason::Futex { addr: addr_usize }); - this.futex_wait(addr_usize, thread, bitset); - // Succesfully waking up from FUTEX_WAIT always returns zero. - this.write_scalar(Scalar::from_target_isize(0, this), dest)?; - // Register a timeout callback if a timeout was specified. - // This callback will override the return value when the timeout triggers. - if let Some(timeout_time) = timeout_time { - struct Callback<'tcx> { - thread: ThreadId, - addr_usize: u64, - dest: MPlaceTy<'tcx, Provenance>, - } - - impl<'tcx> VisitProvenance for Callback<'tcx> { - fn visit_provenance(&self, visit: &mut VisitWith<'_>) { - let Callback { thread: _, addr_usize: _, dest } = self; - dest.visit_provenance(visit); - } - } - - impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> { - fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { - this.unblock_thread( - self.thread, - BlockReason::Futex { addr: self.addr_usize }, - ); - this.futex_remove_waiter(self.addr_usize, self.thread); - let etimedout = this.eval_libc("ETIMEDOUT"); - this.set_last_error(etimedout)?; - this.write_scalar(Scalar::from_target_isize(-1, this), &self.dest)?; - - Ok(()) - } - } - - this.register_timeout_callback( - thread, - timeout_time, - Box::new(Callback { thread, addr_usize, dest: dest.clone() }), - ); - } + // The value still matches, so we block the thread and make it wait for FUTEX_WAKE. + this.futex_wait( + addr_usize, + bitset, + timeout, + Scalar::from_target_isize(0, this), // retval_succ + Scalar::from_target_isize(-1, this), // retval_timeout + dest.clone(), + this.eval_libc("ETIMEDOUT"), + ); } else { // The futex value doesn't match the expected value, so we return failure // right away without sleeping: -1 and errno set to EAGAIN. @@ -257,9 +210,7 @@ pub fn futex<'tcx>( let mut n = 0; #[allow(clippy::arithmetic_side_effects)] for _ in 0..val { - if let Some(thread) = this.futex_wake(addr_usize, bitset) { - this.unblock_thread(thread, BlockReason::Futex { addr: addr_usize }); - this.unregister_timeout_callback_if_exists(thread); + if this.futex_wake(addr_usize, bitset)? { n += 1; } else { break; diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index 912623b72257..25002f0a611c 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -8,14 +8,14 @@ pub fn is_dyn_sym(_name: &str) -> bool { false } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_foreign_item_inner( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); @@ -131,7 +131,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let dtor = this.read_pointer(dtor)?; let dtor = this.get_ptr_fn(dtor)?.as_instance()?; let data = this.read_scalar(data)?; - let active_thread = this.get_active_thread(); + let active_thread = this.active_thread(); this.machine.tls.set_macos_thread_dtor(active_thread, dtor, data)?; } @@ -177,6 +177,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), }; - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index 0254735ac138..de5a5d0759c4 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -11,23 +11,23 @@ //! calls to munmap, but for a very different reason. In principle, according to the man pages, it //! is possible to unmap arbitrary regions of address space. But in a high-level language like Rust //! this amounts to partial deallocation, which LLVM does not support. So any attempt to call our -//! munmap shim which would partily unmap a region of address space previously mapped by mmap will +//! munmap shim which would partially unmap a region of address space previously mapped by mmap will //! report UB. use crate::*; use rustc_target::abi::Size; -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn mmap( &mut self, - addr: &OpTy<'tcx, Provenance>, - length: &OpTy<'tcx, Provenance>, - prot: &OpTy<'tcx, Provenance>, - flags: &OpTy<'tcx, Provenance>, - fd: &OpTy<'tcx, Provenance>, + addr: &OpTy<'tcx>, + length: &OpTy<'tcx>, + prot: &OpTy<'tcx>, + flags: &OpTy<'tcx>, + fd: &OpTy<'tcx>, offset: i128, - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); // We do not support MAP_FIXED, so the addr argument is always ignored (except for the MacOS hack) @@ -71,24 +71,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_unsup_format!("Miri does not support file-backed memory mappings"); } - // POSIX says: - // [ENOTSUP] - // * MAP_FIXED or MAP_PRIVATE was specified in the flags argument and the implementation - // does not support this functionality. - // * The implementation does not support the combination of accesses requested in the - // prot argument. - // - // Miri doesn't support MAP_FIXED or any any protections other than PROT_READ|PROT_WRITE. - if flags & map_fixed != 0 || prot != prot_read | prot_write { - this.set_last_error(this.eval_libc("ENOTSUP"))?; - return Ok(this.eval_libc("MAP_FAILED")); + // Miri doesn't support MAP_FIXED. + if flags & map_fixed != 0 { + throw_unsup_format!( + "Miri does not support calls to mmap with MAP_FIXED as part of the flags argument", + ); + } + + // Miri doesn't support protections other than PROT_READ|PROT_WRITE. + if prot != prot_read | prot_write { + throw_unsup_format!( + "Miri does not support calls to mmap with protections other than \ + PROT_READ|PROT_WRITE", + ); } // Miri does not support shared mappings, or any of the other extensions that for example // Linux has added to the flags arguments. if flags != map_private | map_anonymous { throw_unsup_format!( - "Miri only supports calls to mmap which set the flags argument to MAP_PRIVATE|MAP_ANONYMOUS" + "Miri only supports calls to mmap which set the flags argument to \ + MAP_PRIVATE|MAP_ANONYMOUS", ); } @@ -120,11 +123,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(Scalar::from_pointer(ptr, this)) } - fn munmap( - &mut self, - addr: &OpTy<'tcx, Provenance>, - length: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + fn munmap(&mut self, addr: &OpTy<'tcx>, length: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let addr = this.read_pointer(addr)?; diff --git a/src/tools/miri/src/shims/unix/mod.rs b/src/tools/miri/src/shims/unix/mod.rs index 6dee30d895c7..dc9068fddde1 100644 --- a/src/tools/miri/src/shims/unix/mod.rs +++ b/src/tools/miri/src/shims/unix/mod.rs @@ -8,6 +8,7 @@ mod socket; mod sync; mod thread; +mod android; mod freebsd; mod linux; mod macos; diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index 11fd83f57e67..c639ea2f8460 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -1,15 +1,38 @@ +use std::cell::RefCell; +use std::collections::VecDeque; use std::io; +use std::io::{Error, ErrorKind, Read}; +use std::rc::{Rc, Weak}; use crate::shims::unix::*; -use crate::*; +use crate::{concurrency::VClock, *}; use self::fd::FileDescriptor; +/// The maximum capacity of the socketpair buffer in bytes. +/// This number is arbitrary as the value can always +/// be configured in the real system. +const MAX_SOCKETPAIR_BUFFER_CAPACITY: usize = 212992; + /// Pair of connected sockets. -/// -/// We currently don't allow sending any data through this pair, so this can be just a dummy. #[derive(Debug)] -struct SocketPair; +struct SocketPair { + // By making the write link weak, a `write` can detect when all readers are + // gone, and trigger EPIPE as appropriate. + writebuf: Weak>, + readbuf: Rc>, + is_nonblock: bool, +} + +#[derive(Debug)] +struct Buffer { + buf: VecDeque, + clock: VClock, + /// Indicates if there is at least one active writer to this buffer. + /// If all writers of this buffer are dropped, buf_has_writer becomes false and we + /// indicate EOF instead of blocking. + buf_has_writer: bool, +} impl FileDescription for SocketPair { fn name(&self) -> &'static str { @@ -20,40 +43,188 @@ impl FileDescription for SocketPair { self: Box, _communicate_allowed: bool, ) -> InterpResult<'tcx, io::Result<()>> { + // This is used to signal socketfd of other side that there is no writer to its readbuf. + // If the upgrade fails, there is no need to update as all read ends have been dropped. + if let Some(writebuf) = self.writebuf.upgrade() { + writebuf.borrow_mut().buf_has_writer = false; + }; Ok(Ok(())) } + + fn read<'tcx>( + &mut self, + _communicate_allowed: bool, + bytes: &mut [u8], + ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, io::Result> { + let request_byte_size = bytes.len(); + let mut readbuf = self.readbuf.borrow_mut(); + + // Always succeed on read size 0. + if request_byte_size == 0 { + return Ok(Ok(0)); + } + + if readbuf.buf.is_empty() { + if !readbuf.buf_has_writer { + // Socketpair with no writer and empty buffer. + // 0 bytes successfully read indicates end-of-file. + return Ok(Ok(0)); + } else { + if self.is_nonblock { + // Non-blocking socketpair with writer and empty buffer. + // https://linux.die.net/man/2/read + // EAGAIN or EWOULDBLOCK can be returned for socket, + // POSIX.1-2001 allows either error to be returned for this case. + // Since there is no ErrorKind for EAGAIN, WouldBlock is used. + return Ok(Err(Error::from(ErrorKind::WouldBlock))); + } else { + // Blocking socketpair with writer and empty buffer. + // FIXME: blocking is currently not supported + throw_unsup_format!("socketpair read: blocking isn't supported yet"); + } + } + } + + // Synchronize with all previous writes to this buffer. + // FIXME: this over-synchronizes; a more precise approach would be to + // only sync with the writes whose data we will read. + ecx.acquire_clock(&readbuf.clock); + // Do full read / partial read based on the space available. + // Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior. + let actual_read_size = readbuf.buf.read(bytes).unwrap(); + return Ok(Ok(actual_read_size)); + } + + fn write<'tcx>( + &mut self, + _communicate_allowed: bool, + bytes: &[u8], + ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, io::Result> { + let write_size = bytes.len(); + // Always succeed on write size 0. + // ("If count is zero and fd refers to a file other than a regular file, the results are not specified.") + if write_size == 0 { + return Ok(Ok(0)); + } + + let Some(writebuf) = self.writebuf.upgrade() else { + // If the upgrade from Weak to Rc fails, it indicates that all read ends have been + // closed. + return Ok(Err(Error::from(ErrorKind::BrokenPipe))); + }; + let mut writebuf = writebuf.borrow_mut(); + let data_size = writebuf.buf.len(); + let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.checked_sub(data_size).unwrap(); + if available_space == 0 { + if self.is_nonblock { + // Non-blocking socketpair with a full buffer. + return Ok(Err(Error::from(ErrorKind::WouldBlock))); + } else { + // Blocking socketpair with a full buffer. + throw_unsup_format!("socketpair write: blocking isn't supported yet"); + } + } + // Remember this clock so `read` can synchronize with us. + if let Some(clock) = &ecx.release_clock() { + writebuf.clock.join(clock); + } + // Do full write / partial write based on the space available. + let actual_write_size = write_size.min(available_space); + writebuf.buf.extend(&bytes[..actual_write_size]); + return Ok(Ok(actual_write_size)); + } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - /// Currently this function this function is a stub. Eventually we need to - /// properly implement an FD type for sockets and have this function create - /// two sockets and associated FDs such that writing to one will produce - /// data that can be read from the other. - /// +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// For more information on the arguments see the socketpair manpage: /// fn socketpair( &mut self, - domain: &OpTy<'tcx, Provenance>, - type_: &OpTy<'tcx, Provenance>, - protocol: &OpTy<'tcx, Provenance>, - sv: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + domain: &OpTy<'tcx>, + type_: &OpTy<'tcx>, + protocol: &OpTy<'tcx>, + sv: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let _domain = this.read_scalar(domain)?.to_i32()?; - let _type_ = this.read_scalar(type_)?.to_i32()?; - let _protocol = this.read_scalar(protocol)?.to_i32()?; + let domain = this.read_scalar(domain)?.to_i32()?; + let mut type_ = this.read_scalar(type_)?.to_i32()?; + let protocol = this.read_scalar(protocol)?.to_i32()?; let sv = this.deref_pointer(sv)?; - // FIXME: fail on unsupported inputs + let mut is_sock_nonblock = false; + + // Parse and remove the type flags that we support. If type != 0 after removing, + // unsupported flags are used. + if type_ & this.eval_libc_i32("SOCK_STREAM") == this.eval_libc_i32("SOCK_STREAM") { + type_ &= !(this.eval_libc_i32("SOCK_STREAM")); + } + + // SOCK_NONBLOCK only exists on Linux. + if this.tcx.sess.target.os == "linux" { + if type_ & this.eval_libc_i32("SOCK_NONBLOCK") == this.eval_libc_i32("SOCK_NONBLOCK") { + is_sock_nonblock = true; + type_ &= !(this.eval_libc_i32("SOCK_NONBLOCK")); + } + if type_ & this.eval_libc_i32("SOCK_CLOEXEC") == this.eval_libc_i32("SOCK_CLOEXEC") { + type_ &= !(this.eval_libc_i32("SOCK_CLOEXEC")); + } + } + + // Fail on unsupported input. + // AF_UNIX and AF_LOCAL are synonyms, so we accept both in case + // their values differ. + if domain != this.eval_libc_i32("AF_UNIX") && domain != this.eval_libc_i32("AF_LOCAL") { + throw_unsup_format!( + "socketpair: domain {:#x} is unsupported, only AF_UNIX \ + and AF_LOCAL are allowed", + domain + ); + } else if type_ != 0 { + throw_unsup_format!( + "socketpair: type {:#x} is unsupported, only SOCK_STREAM, \ + SOCK_CLOEXEC and SOCK_NONBLOCK are allowed", + type_ + ); + } else if protocol != 0 { + throw_unsup_format!( + "socketpair: socket protocol {protocol} is unsupported, \ + only 0 is allowed", + ); + } + + let buffer1 = Rc::new(RefCell::new(Buffer { + buf: VecDeque::new(), + clock: VClock::default(), + buf_has_writer: true, + })); + + let buffer2 = Rc::new(RefCell::new(Buffer { + buf: VecDeque::new(), + clock: VClock::default(), + buf_has_writer: true, + })); + + let socketpair_0 = SocketPair { + writebuf: Rc::downgrade(&buffer1), + readbuf: Rc::clone(&buffer2), + is_nonblock: is_sock_nonblock, + }; + + let socketpair_1 = SocketPair { + writebuf: Rc::downgrade(&buffer2), + readbuf: Rc::clone(&buffer1), + is_nonblock: is_sock_nonblock, + }; let fds = &mut this.machine.fds; - let sv0 = fds.insert_fd(FileDescriptor::new(SocketPair)); - let sv0 = Scalar::try_from_int(sv0, sv.layout.size).unwrap(); - let sv1 = fds.insert_fd(FileDescriptor::new(SocketPair)); - let sv1 = Scalar::try_from_int(sv1, sv.layout.size).unwrap(); + let sv0 = fds.insert_fd(FileDescriptor::new(socketpair_0)); + let sv0 = Scalar::from_int(sv0, sv.layout.size); + let sv1 = fds.insert_fd(FileDescriptor::new(socketpair_1)); + let sv1 = Scalar::from_int(sv1, sv.layout.size); this.write_scalar(sv0, &sv)?; this.write_scalar(sv1, &sv.offset(sv.layout.size, sv.layout, this)?)?; diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs index c216d8afd773..a0cc4a62bfd2 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -1,23 +1,49 @@ use rustc_span::Symbol; use rustc_target::spec::abi::Abi; +use crate::shims::unix::*; use crate::*; -pub fn is_dyn_sym(_name: &str) -> bool { - false +pub fn is_dyn_sym(name: &str) -> bool { + matches!(name, "pthread_setname_np") } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_foreign_item_inner( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); match link_name.as_str() { + // Threading + "pthread_setname_np" => { + let [thread, name] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + // THREAD_NAME_MAX allows a thread name of 31+1 length + // https://github.com/illumos/illumos-gate/blob/7671517e13b8123748eda4ef1ee165c6d9dba7fe/usr/src/uts/common/sys/thread.h#L613 + let max_len = 32; + let res = this.pthread_setname_np( + this.read_scalar(thread)?, + this.read_scalar(name)?, + max_len, + )?; + this.write_scalar(res, dest)?; + } + "pthread_getname_np" => { + let [thread, name, len] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let res = this.pthread_getname_np( + this.read_scalar(thread)?, + this.read_scalar(name)?, + this.read_scalar(len)?, + )?; + this.write_scalar(res, dest)?; + } + // Miscellaneous "___errno" => { let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -43,8 +69,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_null(dest)?; } + "pset_info" => { + let [pset, tpe, cpus, list] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + // We do not need to handle the current process cpu mask, available_parallelism + // implementation pass null anyway. We only care for the number of + // cpus. + // https://docs.oracle.com/cd/E88353_01/html/E37841/pset-info-2.html + + let pset = this.read_scalar(pset)?.to_i32()?; + let tpe = this.read_pointer(tpe)?; + let list = this.read_pointer(list)?; + + let ps_myid = this.eval_libc_i32("PS_MYID"); + if ps_myid != pset { + throw_unsup_format!("pset_info is only supported with pset==PS_MYID"); + } + + if !this.ptr_is_null(tpe)? { + throw_unsup_format!("pset_info is only supported with type==NULL"); + } + + if !this.ptr_is_null(list)? { + throw_unsup_format!("pset_info is only supported with list==NULL"); + } + + let cpus = this.deref_pointer(cpus)?; + this.write_scalar(Scalar::from_u32(this.machine.num_cpus), &cpus)?; + this.write_null(dest)?; + } + _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index f24f279ab0f9..be6732b1b67e 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -1,9 +1,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; -use std::time::SystemTime; use rustc_target::abi::Size; -use crate::concurrency::thread::MachineCallback; use crate::*; // pthread_mutexattr_t is either 4 or 8 bytes, depending on the platform. @@ -11,18 +9,16 @@ use crate::*; // - kind: i32 #[inline] -fn mutexattr_kind_offset<'mir, 'tcx: 'mir>( - ecx: &MiriInterpCx<'mir, 'tcx>, -) -> InterpResult<'tcx, u64> { +fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { Ok(match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" | "macos" => 0, os => throw_unsup_format!("`pthread_mutexattr` is not supported on {os}"), }) } -fn mutexattr_get_kind<'mir, 'tcx: 'mir>( - ecx: &MiriInterpCx<'mir, 'tcx>, - attr_op: &OpTy<'tcx, Provenance>, +fn mutexattr_get_kind<'tcx>( + ecx: &MiriInterpCx<'tcx>, + attr_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, i32> { ecx.deref_pointer_and_read( attr_op, @@ -33,9 +29,9 @@ fn mutexattr_get_kind<'mir, 'tcx: 'mir>( .to_i32() } -fn mutexattr_set_kind<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - attr_op: &OpTy<'tcx, Provenance>, +fn mutexattr_set_kind<'tcx>( + ecx: &mut MiriInterpCx<'tcx>, + attr_op: &OpTy<'tcx>, kind: i32, ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( @@ -54,17 +50,11 @@ fn mutexattr_set_kind<'mir, 'tcx: 'mir>( /// in `pthread_mutexattr_settype` function. const PTHREAD_MUTEX_NORMAL_FLAG: i32 = 0x8000000; -fn is_mutex_kind_default<'mir, 'tcx: 'mir>( - ecx: &MiriInterpCx<'mir, 'tcx>, - kind: i32, -) -> InterpResult<'tcx, bool> { +fn is_mutex_kind_default<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, bool> { Ok(kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")) } -fn is_mutex_kind_normal<'mir, 'tcx: 'mir>( - ecx: &MiriInterpCx<'mir, 'tcx>, - kind: i32, -) -> InterpResult<'tcx, bool> { +fn is_mutex_kind_normal<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, bool> { let mutex_normal_kind = ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL"); Ok(kind == (mutex_normal_kind | PTHREAD_MUTEX_NORMAL_FLAG)) } @@ -74,7 +64,7 @@ fn is_mutex_kind_normal<'mir, 'tcx: 'mir>( // - id: u32 // - kind: i32 -fn mutex_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx, u64> { +fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { let offset = match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" => 0, // macOS stores a signature in the first bytes, so we have to move to offset 4. @@ -100,7 +90,7 @@ fn mutex_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> InterpRe Ok(offset) } -fn mutex_kind_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> u64 { +fn mutex_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 { // These offsets are picked for compatibility with Linux's static initializer // macros, e.g. PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.) let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 }; @@ -124,9 +114,9 @@ fn mutex_kind_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> u64 { offset } -fn mutex_get_id<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - mutex_op: &OpTy<'tcx, Provenance>, +fn mutex_get_id<'tcx>( + ecx: &mut MiriInterpCx<'tcx>, + mutex_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, MutexId> { ecx.mutex_get_or_create_id( mutex_op, @@ -135,9 +125,9 @@ fn mutex_get_id<'mir, 'tcx: 'mir>( ) } -fn mutex_reset_id<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - mutex_op: &OpTy<'tcx, Provenance>, +fn mutex_reset_id<'tcx>( + ecx: &mut MiriInterpCx<'tcx>, + mutex_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( mutex_op, @@ -148,9 +138,9 @@ fn mutex_reset_id<'mir, 'tcx: 'mir>( ) } -fn mutex_get_kind<'mir, 'tcx: 'mir>( - ecx: &MiriInterpCx<'mir, 'tcx>, - mutex_op: &OpTy<'tcx, Provenance>, +fn mutex_get_kind<'tcx>( + ecx: &MiriInterpCx<'tcx>, + mutex_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, i32> { ecx.deref_pointer_and_read( mutex_op, @@ -161,9 +151,9 @@ fn mutex_get_kind<'mir, 'tcx: 'mir>( .to_i32() } -fn mutex_set_kind<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - mutex_op: &OpTy<'tcx, Provenance>, +fn mutex_set_kind<'tcx>( + ecx: &mut MiriInterpCx<'tcx>, + mutex_op: &OpTy<'tcx>, kind: i32, ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( @@ -179,7 +169,7 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>( // We ignore the platform layout and store our own fields: // - id: u32 -fn rwlock_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx, u64> { +fn rwlock_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { let offset = match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" => 0, // macOS stores a signature in the first bytes, so we have to move to offset 4. @@ -205,9 +195,9 @@ fn rwlock_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> InterpR Ok(offset) } -fn rwlock_get_id<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - rwlock_op: &OpTy<'tcx, Provenance>, +fn rwlock_get_id<'tcx>( + ecx: &mut MiriInterpCx<'tcx>, + rwlock_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, RwLockId> { ecx.rwlock_get_or_create_id( rwlock_op, @@ -221,9 +211,7 @@ fn rwlock_get_id<'mir, 'tcx: 'mir>( // - clock: i32 #[inline] -fn condattr_clock_offset<'mir, 'tcx: 'mir>( - ecx: &MiriInterpCx<'mir, 'tcx>, -) -> InterpResult<'tcx, u64> { +fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { Ok(match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" => 0, // macOS does not have a clock attribute. @@ -231,9 +219,9 @@ fn condattr_clock_offset<'mir, 'tcx: 'mir>( }) } -fn condattr_get_clock_id<'mir, 'tcx: 'mir>( - ecx: &MiriInterpCx<'mir, 'tcx>, - attr_op: &OpTy<'tcx, Provenance>, +fn condattr_get_clock_id<'tcx>( + ecx: &MiriInterpCx<'tcx>, + attr_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, i32> { ecx.deref_pointer_and_read( attr_op, @@ -244,9 +232,9 @@ fn condattr_get_clock_id<'mir, 'tcx: 'mir>( .to_i32() } -fn condattr_set_clock_id<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - attr_op: &OpTy<'tcx, Provenance>, +fn condattr_set_clock_id<'tcx>( + ecx: &mut MiriInterpCx<'tcx>, + attr_op: &OpTy<'tcx>, clock_id: i32, ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( @@ -263,7 +251,7 @@ fn condattr_set_clock_id<'mir, 'tcx: 'mir>( // - id: u32 // - clock: i32 -fn cond_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx, u64> { +fn cond_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { let offset = match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" => 0, // macOS stores a signature in the first bytes, so we have to move to offset 4. @@ -290,7 +278,7 @@ fn cond_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> InterpRes } /// Determines whether this clock represents the real-time clock, CLOCK_REALTIME. -fn is_cond_clock_realtime<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>, clock_id: i32) -> bool { +fn is_cond_clock_realtime<'tcx>(ecx: &MiriInterpCx<'tcx>, clock_id: i32) -> bool { // To ensure compatibility with PTHREAD_COND_INITIALIZER on all platforms, // we can't just compare with CLOCK_REALTIME: on Solarish, PTHREAD_COND_INITIALIZER // makes the clock 0 but CLOCK_REALTIME is 3. @@ -299,7 +287,7 @@ fn is_cond_clock_realtime<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>, cloc || (clock_id == 0 && clock_id != ecx.eval_libc_i32("CLOCK_MONOTONIC")) } -fn cond_clock_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> u64 { +fn cond_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 { // macOS doesn't have a clock attribute, but to keep the code uniform we store // a clock ID in the pthread_cond_t anyway. There's enough space. let offset = 8; @@ -322,9 +310,9 @@ fn cond_clock_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> u64 { offset } -fn cond_get_id<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - cond_op: &OpTy<'tcx, Provenance>, +fn cond_get_id<'tcx>( + ecx: &mut MiriInterpCx<'tcx>, + cond_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, CondvarId> { ecx.condvar_get_or_create_id( cond_op, @@ -333,9 +321,9 @@ fn cond_get_id<'mir, 'tcx: 'mir>( ) } -fn cond_reset_id<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - cond_op: &OpTy<'tcx, Provenance>, +fn cond_reset_id<'tcx>( + ecx: &mut MiriInterpCx<'tcx>, + cond_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( cond_op, @@ -346,9 +334,9 @@ fn cond_reset_id<'mir, 'tcx: 'mir>( ) } -fn cond_get_clock_id<'mir, 'tcx: 'mir>( - ecx: &MiriInterpCx<'mir, 'tcx>, - cond_op: &OpTy<'tcx, Provenance>, +fn cond_get_clock_id<'tcx>( + ecx: &MiriInterpCx<'tcx>, + cond_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, i32> { ecx.deref_pointer_and_read( cond_op, @@ -359,9 +347,9 @@ fn cond_get_clock_id<'mir, 'tcx: 'mir>( .to_i32() } -fn cond_set_clock_id<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - cond_op: &OpTy<'tcx, Provenance>, +fn cond_set_clock_id<'tcx>( + ecx: &mut MiriInterpCx<'tcx>, + cond_op: &OpTy<'tcx>, clock_id: i32, ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( @@ -373,64 +361,9 @@ fn cond_set_clock_id<'mir, 'tcx: 'mir>( ) } -/// Try to reacquire the mutex associated with the condition variable after we -/// were signaled. -fn reacquire_cond_mutex<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - thread: ThreadId, - condvar: CondvarId, - mutex: MutexId, -) -> InterpResult<'tcx> { - ecx.unblock_thread(thread, BlockReason::Condvar(condvar)); - if ecx.mutex_is_locked(mutex) { - ecx.mutex_enqueue_and_block(mutex, thread); - } else { - ecx.mutex_lock(mutex, thread); - } - Ok(()) -} - -/// After a thread waiting on a condvar was signalled: -/// Reacquire the conditional variable and remove the timeout callback if any -/// was registered. -fn post_cond_signal<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - thread: ThreadId, - condvar: CondvarId, - mutex: MutexId, -) -> InterpResult<'tcx> { - reacquire_cond_mutex(ecx, thread, condvar, mutex)?; - // Waiting for the mutex is not included in the waiting time because we need - // to acquire the mutex always even if we get a timeout. - ecx.unregister_timeout_callback_if_exists(thread); - Ok(()) -} - -/// Release the mutex associated with the condition variable because we are -/// entering the waiting state. -fn release_cond_mutex_and_block<'mir, 'tcx: 'mir>( - ecx: &mut MiriInterpCx<'mir, 'tcx>, - active_thread: ThreadId, - condvar: CondvarId, - mutex: MutexId, -) -> InterpResult<'tcx> { - if let Some(old_locked_count) = ecx.mutex_unlock(mutex, active_thread) { - if old_locked_count != 1 { - throw_unsup_format!("awaiting on a lock acquired multiple times is not supported"); - } - } else { - throw_ub_format!("awaiting on unlocked or owned by a different thread mutex"); - } - ecx.block_thread(active_thread, BlockReason::Condvar(condvar)); - Ok(()) -} - -impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - fn pthread_mutexattr_init( - &mut self, - attr_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn pthread_mutexattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let default_kind = this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"); @@ -441,8 +374,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn pthread_mutexattr_settype( &mut self, - attr_op: &OpTy<'tcx, Provenance>, - kind_op: &OpTy<'tcx, Provenance>, + attr_op: &OpTy<'tcx>, + kind_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -480,10 +413,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(0) } - fn pthread_mutexattr_destroy( - &mut self, - attr_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + fn pthread_mutexattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); // Destroying an uninit pthread_mutexattr is UB, so check to make sure it's not uninit. @@ -510,8 +440,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn pthread_mutex_init( &mut self, - mutex_op: &OpTy<'tcx, Provenance>, - attr_op: &OpTy<'tcx, Provenance>, + mutex_op: &OpTy<'tcx>, + attr_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -530,19 +460,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(0) } - fn pthread_mutex_lock(&mut self, mutex_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { + fn pthread_mutex_lock( + &mut self, + mutex_op: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let kind = mutex_get_kind(this, mutex_op)?; let id = mutex_get_id(this, mutex_op)?; - let active_thread = this.get_active_thread(); - if this.mutex_is_locked(id) { + let ret = if this.mutex_is_locked(id) { let owner_thread = this.mutex_get_owner(id); - if owner_thread != active_thread { - // Enqueue the active thread. - this.mutex_enqueue_and_block(id, active_thread); - Ok(0) + if owner_thread != this.active_thread() { + this.mutex_enqueue_and_block(id, Scalar::from_i32(0), dest.clone()); + return Ok(()); } else { // Trying to acquire the same mutex again. if is_mutex_kind_default(this, kind)? { @@ -550,10 +482,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } else if is_mutex_kind_normal(this, kind)? { throw_machine_stop!(TerminationInfo::Deadlock); } else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") { - Ok(this.eval_libc_i32("EDEADLK")) + this.eval_libc_i32("EDEADLK") } else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") { - this.mutex_lock(id, active_thread); - Ok(0) + this.mutex_lock(id); + 0 } else { throw_unsup_format!( "called pthread_mutex_lock on an unsupported type of mutex" @@ -562,24 +494,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } else { // The mutex is unlocked. Let's lock it. - this.mutex_lock(id, active_thread); - Ok(0) - } + this.mutex_lock(id); + 0 + }; + this.write_scalar(Scalar::from_i32(ret), dest)?; + Ok(()) } - fn pthread_mutex_trylock( - &mut self, - mutex_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let kind = mutex_get_kind(this, mutex_op)?; let id = mutex_get_id(this, mutex_op)?; - let active_thread = this.get_active_thread(); if this.mutex_is_locked(id) { let owner_thread = this.mutex_get_owner(id); - if owner_thread != active_thread { + if owner_thread != this.active_thread() { Ok(this.eval_libc_i32("EBUSY")) } else { if is_mutex_kind_default(this, kind)? @@ -588,7 +518,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { { Ok(this.eval_libc_i32("EBUSY")) } else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") { - this.mutex_lock(id, active_thread); + this.mutex_lock(id); Ok(0) } else { throw_unsup_format!( @@ -598,22 +528,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } else { // The mutex is unlocked. Let's lock it. - this.mutex_lock(id, active_thread); + this.mutex_lock(id); Ok(0) } } - fn pthread_mutex_unlock( - &mut self, - mutex_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let kind = mutex_get_kind(this, mutex_op)?; let id = mutex_get_id(this, mutex_op)?; - let active_thread = this.get_active_thread(); - if let Some(_old_locked_count) = this.mutex_unlock(id, active_thread) { + if let Some(_old_locked_count) = this.mutex_unlock(id)? { // The mutex was locked by the current thread. Ok(0) } else { @@ -638,10 +564,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn pthread_mutex_destroy( - &mut self, - mutex_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + fn pthread_mutex_destroy(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = mutex_get_id(this, mutex_op)?; @@ -665,47 +588,44 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn pthread_rwlock_rdlock( &mut self, - rwlock_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + rwlock_op: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let id = rwlock_get_id(this, rwlock_op)?; - let active_thread = this.get_active_thread(); if this.rwlock_is_write_locked(id) { - this.rwlock_enqueue_and_block_reader(id, active_thread); - Ok(0) + this.rwlock_enqueue_and_block_reader(id, Scalar::from_i32(0), dest.clone()); } else { - this.rwlock_reader_lock(id, active_thread); - Ok(0) + this.rwlock_reader_lock(id); + this.write_null(dest)?; } + + Ok(()) } - fn pthread_rwlock_tryrdlock( - &mut self, - rwlock_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = rwlock_get_id(this, rwlock_op)?; - let active_thread = this.get_active_thread(); if this.rwlock_is_write_locked(id) { Ok(this.eval_libc_i32("EBUSY")) } else { - this.rwlock_reader_lock(id, active_thread); + this.rwlock_reader_lock(id); Ok(0) } } fn pthread_rwlock_wrlock( &mut self, - rwlock_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + rwlock_op: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let id = rwlock_get_id(this, rwlock_op)?; - let active_thread = this.get_active_thread(); if this.rwlock_is_locked(id) { // Note: this will deadlock if the lock is already locked by this @@ -720,54 +640,44 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // report the deadlock only when no thread can continue execution, // but we could detect that this lock is already locked and report // an error.) - this.rwlock_enqueue_and_block_writer(id, active_thread); + this.rwlock_enqueue_and_block_writer(id, Scalar::from_i32(0), dest.clone()); } else { - this.rwlock_writer_lock(id, active_thread); + this.rwlock_writer_lock(id); + this.write_null(dest)?; } - Ok(0) + Ok(()) } - fn pthread_rwlock_trywrlock( - &mut self, - rwlock_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + fn pthread_rwlock_trywrlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = rwlock_get_id(this, rwlock_op)?; - let active_thread = this.get_active_thread(); if this.rwlock_is_locked(id) { Ok(this.eval_libc_i32("EBUSY")) } else { - this.rwlock_writer_lock(id, active_thread); + this.rwlock_writer_lock(id); Ok(0) } } - fn pthread_rwlock_unlock( - &mut self, - rwlock_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = rwlock_get_id(this, rwlock_op)?; - let active_thread = this.get_active_thread(); #[allow(clippy::if_same_then_else)] - if this.rwlock_reader_unlock(id, active_thread) { + if this.rwlock_reader_unlock(id)? { Ok(0) - } else if this.rwlock_writer_unlock(id, active_thread) { + } else if this.rwlock_writer_unlock(id)? { Ok(0) } else { throw_ub_format!("unlocked an rwlock that was not locked by the active thread"); } } - fn pthread_rwlock_destroy( - &mut self, - rwlock_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + fn pthread_rwlock_destroy(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = rwlock_get_id(this, rwlock_op)?; @@ -788,10 +698,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(0) } - fn pthread_condattr_init( - &mut self, - attr_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + fn pthread_condattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); // no clock attribute on macOS @@ -808,9 +715,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn pthread_condattr_setclock( &mut self, - attr_op: &OpTy<'tcx, Provenance>, - clock_id_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + attr_op: &OpTy<'tcx>, + clock_id_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let clock_id = this.read_scalar(clock_id_op)?.to_i32()?; @@ -828,9 +735,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn pthread_condattr_getclock( &mut self, - attr_op: &OpTy<'tcx, Provenance>, - clk_id_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + attr_op: &OpTy<'tcx>, + clk_id_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let clock_id = condattr_get_clock_id(this, attr_op)?; @@ -839,10 +746,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(Scalar::from_i32(0)) } - fn pthread_condattr_destroy( - &mut self, - attr_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + fn pthread_condattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); // Destroying an uninit pthread_condattr is UB, so check to make sure it's not uninit. @@ -862,8 +766,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn pthread_cond_init( &mut self, - cond_op: &OpTy<'tcx, Provenance>, - attr_op: &OpTy<'tcx, Provenance>, + cond_op: &OpTy<'tcx>, + attr_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -883,59 +787,54 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(0) } - fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { + fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = cond_get_id(this, cond_op)?; - if let Some((thread, mutex)) = this.condvar_signal(id) { - post_cond_signal(this, thread, id, mutex)?; - } - + this.condvar_signal(id)?; Ok(0) } - fn pthread_cond_broadcast( - &mut self, - cond_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + fn pthread_cond_broadcast(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = cond_get_id(this, cond_op)?; - - while let Some((thread, mutex)) = this.condvar_signal(id) { - post_cond_signal(this, thread, id, mutex)?; - } - + while this.condvar_signal(id)? {} Ok(0) } fn pthread_cond_wait( &mut self, - cond_op: &OpTy<'tcx, Provenance>, - mutex_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { - let this = self.eval_context_mut(); - - let id = cond_get_id(this, cond_op)?; - let mutex_id = mutex_get_id(this, mutex_op)?; - let active_thread = this.get_active_thread(); - - release_cond_mutex_and_block(this, active_thread, id, mutex_id)?; - this.condvar_wait(id, active_thread, mutex_id); - - Ok(0) - } - - fn pthread_cond_timedwait( - &mut self, - cond_op: &OpTy<'tcx, Provenance>, - mutex_op: &OpTy<'tcx, Provenance>, - abstime_op: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + cond_op: &OpTy<'tcx>, + mutex_op: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let id = cond_get_id(this, cond_op)?; + let mutex_id = mutex_get_id(this, mutex_op)?; + + this.condvar_wait( + id, + mutex_id, + None, // no timeout + Scalar::from_i32(0), + Scalar::from_i32(0), // retval_timeout -- unused + dest.clone(), + )?; + + Ok(()) + } + + fn pthread_cond_timedwait( + &mut self, + cond_op: &OpTy<'tcx>, + mutex_op: &OpTy<'tcx>, + abstime_op: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let id = cond_get_id(this, cond_op)?; let mutex_id = mutex_get_id(this, mutex_op)?; - let active_thread = this.get_active_thread(); // Extract the timeout. let clock_id = cond_get_clock_id(this, cond_op)?; @@ -949,68 +848,28 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { return Ok(()); } }; - - let timeout_time = if is_cond_clock_realtime(this, clock_id) { + let timeout_clock = if is_cond_clock_realtime(this, clock_id) { this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?; - CallbackTime::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap()) + TimeoutClock::RealTime } else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC") { - CallbackTime::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap()) + TimeoutClock::Monotonic } else { throw_unsup_format!("unsupported clock id: {}", clock_id); }; - release_cond_mutex_and_block(this, active_thread, id, mutex_id)?; - this.condvar_wait(id, active_thread, mutex_id); - - // We return success for now and override it in the timeout callback. - this.write_scalar(Scalar::from_i32(0), dest)?; - - struct Callback<'tcx> { - active_thread: ThreadId, - mutex_id: MutexId, - id: CondvarId, - dest: MPlaceTy<'tcx, Provenance>, - } - - impl<'tcx> VisitProvenance for Callback<'tcx> { - fn visit_provenance(&self, visit: &mut VisitWith<'_>) { - let Callback { active_thread: _, mutex_id: _, id: _, dest } = self; - dest.visit_provenance(visit); - } - } - - impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> { - fn call(&self, ecx: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { - // We are not waiting for the condvar any more, wait for the - // mutex instead. - reacquire_cond_mutex(ecx, self.active_thread, self.id, self.mutex_id)?; - - // Remove the thread from the conditional variable. - ecx.condvar_remove_waiter(self.id, self.active_thread); - - // Set the return value: we timed out. - let etimedout = ecx.eval_libc("ETIMEDOUT"); - ecx.write_scalar(etimedout, &self.dest)?; - - Ok(()) - } - } - - // Register the timeout callback. - let dest = dest.clone(); - this.register_timeout_callback( - active_thread, - timeout_time, - Box::new(Callback { active_thread, mutex_id, id, dest }), - ); + this.condvar_wait( + id, + mutex_id, + Some((timeout_clock, TimeoutAnchor::Absolute, duration)), + Scalar::from_i32(0), + this.eval_libc("ETIMEDOUT"), // retval_timeout + dest.clone(), + )?; Ok(()) } - fn pthread_cond_destroy( - &mut self, - cond_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { + fn pthread_cond_destroy(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = cond_get_id(this, cond_op)?; diff --git a/src/tools/miri/src/shims/unix/thread.rs b/src/tools/miri/src/shims/unix/thread.rs index 9e09401a8158..6fe331ba623a 100644 --- a/src/tools/miri/src/shims/unix/thread.rs +++ b/src/tools/miri/src/shims/unix/thread.rs @@ -2,14 +2,14 @@ use crate::*; use rustc_middle::ty::layout::LayoutOf; use rustc_target::spec::abi::Abi; -impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn pthread_create( &mut self, - thread: &OpTy<'tcx, Provenance>, - _attr: &OpTy<'tcx, Provenance>, - start_routine: &OpTy<'tcx, Provenance>, - arg: &OpTy<'tcx, Provenance>, + thread: &OpTy<'tcx>, + _attr: &OpTy<'tcx>, + start_routine: &OpTy<'tcx>, + arg: &OpTy<'tcx>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -32,8 +32,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn pthread_join( &mut self, - thread: &OpTy<'tcx, Provenance>, - retval: &OpTy<'tcx, Provenance>, + thread: &OpTy<'tcx>, + retval: &OpTy<'tcx>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -48,7 +48,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(0) } - fn pthread_detach(&mut self, thread: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { + fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?; @@ -60,10 +60,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(0) } - fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar> { + fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let thread_id = this.get_active_thread(); + let thread_id = this.active_thread(); Ok(Scalar::from_uint(thread_id.to_u32(), this.libc_ty_layout("pthread_t").size)) } @@ -71,10 +71,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// including the null terminator. fn pthread_setname_np( &mut self, - thread: Scalar, - name: Scalar, + thread: Scalar, + name: Scalar, max_name_len: usize, - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?; @@ -95,10 +95,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn pthread_getname_np( &mut self, - thread: Scalar, - name_out: Scalar, - len: Scalar, - ) -> InterpResult<'tcx, Scalar> { + thread: Scalar, + name_out: Scalar, + len: Scalar, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?; diff --git a/src/tools/miri/src/shims/wasi/foreign_items.rs b/src/tools/miri/src/shims/wasi/foreign_items.rs new file mode 100644 index 000000000000..e9fe90081d09 --- /dev/null +++ b/src/tools/miri/src/shims/wasi/foreign_items.rs @@ -0,0 +1,40 @@ +use rustc_span::Symbol; +use rustc_target::spec::abi::Abi; + +use crate::shims::alloc::EvalContextExt as _; +use crate::*; + +pub fn is_dyn_sym(_name: &str) -> bool { + false +} + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn emulate_foreign_item_inner( + &mut self, + link_name: Symbol, + abi: Abi, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx, EmulateItemResult> { + let this = self.eval_context_mut(); + match link_name.as_str() { + // Allocation + "posix_memalign" => { + let [memptr, align, size] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let result = this.posix_memalign(memptr, align, size)?; + this.write_scalar(result, dest)?; + } + "aligned_alloc" => { + let [align, size] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let res = this.aligned_alloc(align, size)?; + this.write_pointer(res, dest)?; + } + + _ => return Ok(EmulateItemResult::NotSupported), + } + Ok(EmulateItemResult::NeedsReturn) + } +} diff --git a/src/tools/miri/src/shims/wasi/mod.rs b/src/tools/miri/src/shims/wasi/mod.rs new file mode 100644 index 000000000000..09c6507b24f8 --- /dev/null +++ b/src/tools/miri/src/shims/wasi/mod.rs @@ -0,0 +1 @@ +pub mod foreign_items; diff --git a/src/tools/miri/src/shims/windows/env.rs b/src/tools/miri/src/shims/windows/env.rs index 0e52959b762e..ed3eb6979863 100644 --- a/src/tools/miri/src/shims/windows/env.rs +++ b/src/tools/miri/src/shims/windows/env.rs @@ -20,8 +20,8 @@ impl VisitProvenance for WindowsEnvVars { } impl WindowsEnvVars { - pub(crate) fn new<'mir, 'tcx>( - _ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, + pub(crate) fn new<'tcx>( + _ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>, env_vars: FxHashMap, ) -> InterpResult<'tcx, Self> { Ok(Self { map: env_vars }) @@ -33,15 +33,15 @@ impl WindowsEnvVars { } } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { #[allow(non_snake_case)] fn GetEnvironmentVariableW( &mut self, - name_op: &OpTy<'tcx, Provenance>, // LPCWSTR - buf_op: &OpTy<'tcx, Provenance>, // LPWSTR - size_op: &OpTy<'tcx, Provenance>, // DWORD - ) -> InterpResult<'tcx, Scalar> { + name_op: &OpTy<'tcx>, // LPCWSTR + buf_op: &OpTy<'tcx>, // LPWSTR + size_op: &OpTy<'tcx>, // DWORD + ) -> InterpResult<'tcx, Scalar> { // ^ Returns DWORD (u32 on Windows) let this = self.eval_context_mut(); @@ -71,7 +71,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } #[allow(non_snake_case)] - fn GetEnvironmentStringsW(&mut self) -> InterpResult<'tcx, Pointer>> { + fn GetEnvironmentStringsW(&mut self) -> InterpResult<'tcx, Pointer> { let this = self.eval_context_mut(); this.assert_target_os("windows", "GetEnvironmentStringsW"); @@ -93,10 +93,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } #[allow(non_snake_case)] - fn FreeEnvironmentStringsW( - &mut self, - env_block_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + fn FreeEnvironmentStringsW(&mut self, env_block_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os("windows", "FreeEnvironmentStringsW"); @@ -109,9 +106,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[allow(non_snake_case)] fn SetEnvironmentVariableW( &mut self, - name_op: &OpTy<'tcx, Provenance>, // LPCWSTR - value_op: &OpTy<'tcx, Provenance>, // LPCWSTR - ) -> InterpResult<'tcx, Scalar> { + name_op: &OpTy<'tcx>, // LPCWSTR + value_op: &OpTy<'tcx>, // LPCWSTR + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os("windows", "SetEnvironmentVariableW"); @@ -142,9 +139,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[allow(non_snake_case)] fn GetCurrentDirectoryW( &mut self, - size_op: &OpTy<'tcx, Provenance>, // DWORD - buf_op: &OpTy<'tcx, Provenance>, // LPTSTR - ) -> InterpResult<'tcx, Scalar> { + size_op: &OpTy<'tcx>, // DWORD + buf_op: &OpTy<'tcx>, // LPTSTR + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os("windows", "GetCurrentDirectoryW"); @@ -174,8 +171,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[allow(non_snake_case)] fn SetCurrentDirectoryW( &mut self, - path_op: &OpTy<'tcx, Provenance>, // LPCTSTR - ) -> InterpResult<'tcx, Scalar> { + path_op: &OpTy<'tcx>, // LPCTSTR + ) -> InterpResult<'tcx, Scalar> { // ^ Returns BOOL (i32 on Windows) let this = self.eval_context_mut(); @@ -211,10 +208,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[allow(non_snake_case)] fn GetUserProfileDirectoryW( &mut self, - token: &OpTy<'tcx, Provenance>, // HANDLE - buf: &OpTy<'tcx, Provenance>, // LPWSTR - size: &OpTy<'tcx, Provenance>, // LPDWORD - ) -> InterpResult<'tcx, Scalar> // returns BOOL + token: &OpTy<'tcx>, // HANDLE + buf: &OpTy<'tcx>, // LPWSTR + size: &OpTy<'tcx>, // LPDWORD + ) -> InterpResult<'tcx, Scalar> // returns BOOL { let this = self.eval_context_mut(); this.assert_target_os("windows", "GetUserProfileDirectoryW"); diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 086abf19c5cf..bfa14bcb5fad 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -76,14 +76,14 @@ fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { Ok(path::absolute(bytes_to_os_str(&result)?)) } -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_foreign_item_inner( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); @@ -354,7 +354,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "TlsGetValue" => { let [key] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; let key = u128::from(this.read_scalar(key)?.to_u32()?); - let active_thread = this.get_active_thread(); + let active_thread = this.active_thread(); let ptr = this.machine.tls.load_tls(key, active_thread, this)?; this.write_scalar(ptr, dest)?; } @@ -362,7 +362,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [key, new_ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; let key = u128::from(this.read_scalar(key)?.to_u32()?); - let active_thread = this.get_active_thread(); + let active_thread = this.active_thread(); let new_data = this.read_scalar(new_ptr)?; this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?; @@ -423,8 +423,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "InitOnceBeginInitialize" => { let [ptr, flags, pending, context] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; - let result = this.InitOnceBeginInitialize(ptr, flags, pending, context)?; - this.write_scalar(result, dest)?; + this.InitOnceBeginInitialize(ptr, flags, pending, context, dest)?; } "InitOnceComplete" => { let [ptr, flags, context] = @@ -502,7 +501,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let thread = match Handle::from_scalar(handle, this)? { Some(Handle::Thread(thread)) => thread, - Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.get_active_thread(), + Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(), _ => this.invalid_handle("SetThreadDescription")?, }; @@ -520,7 +519,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let thread = match Handle::from_scalar(handle, this)? { Some(Handle::Thread(thread)) => thread, - Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.get_active_thread(), + Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(), _ => this.invalid_handle("SetThreadDescription")?, }; // Looks like the default thread name is empty. @@ -762,6 +761,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/windows/handle.rs b/src/tools/miri/src/shims/windows/handle.rs index d9ae1b22409d..58c8683ff277 100644 --- a/src/tools/miri/src/shims/windows/handle.rs +++ b/src/tools/miri/src/shims/windows/handle.rs @@ -119,7 +119,7 @@ impl Handle { Self::new(discriminant, data) } - pub fn to_scalar(self, cx: &impl HasDataLayout) -> Scalar { + pub fn to_scalar(self, cx: &impl HasDataLayout) -> Scalar { // 64-bit handles are sign extended 32-bit handles // see https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication #[allow(clippy::cast_possible_wrap)] // we want it to wrap @@ -128,7 +128,7 @@ impl Handle { } pub fn from_scalar<'tcx>( - handle: Scalar, + handle: Scalar, cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Option> { let sign_extended_handle = handle.to_target_isize(cx)?; @@ -145,17 +145,17 @@ impl Handle { } } -impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} #[allow(non_snake_case)] -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn invalid_handle(&mut self, function_name: &str) -> InterpResult<'tcx, !> { throw_machine_stop!(TerminationInfo::Abort(format!( "invalid handle passed to `{function_name}`" ))) } - fn CloseHandle(&mut self, handle_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { + fn CloseHandle(&mut self, handle_op: &OpTy<'tcx>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let handle = this.read_scalar(handle_op)?; diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 836b9e925959..e1fbb77037cf 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -3,35 +3,56 @@ use std::time::Duration; use rustc_target::abi::Size; use crate::concurrency::init_once::InitOnceStatus; -use crate::concurrency::thread::MachineCallback; use crate::*; -impl<'mir, 'tcx> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} +trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Windows sync primitives are pointer sized. // We only use the first 4 bytes for the id. - fn init_once_get_id( - &mut self, - init_once_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, InitOnceId> { + fn init_once_get_id(&mut self, init_once_op: &OpTy<'tcx>) -> InterpResult<'tcx, InitOnceId> { let this = self.eval_context_mut(); this.init_once_get_or_create_id(init_once_op, this.windows_ty_layout("INIT_ONCE"), 0) } + + /// Returns `true` if we were succssful, `false` if we would block. + fn init_once_try_begin( + &mut self, + id: InitOnceId, + pending_place: &MPlaceTy<'tcx>, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx, bool> { + let this = self.eval_context_mut(); + Ok(match this.init_once_status(id) { + InitOnceStatus::Uninitialized => { + this.init_once_begin(id); + this.write_scalar(this.eval_windows("c", "TRUE"), pending_place)?; + this.write_scalar(this.eval_windows("c", "TRUE"), dest)?; + true + } + InitOnceStatus::Complete => { + this.init_once_observe_completed(id); + this.write_scalar(this.eval_windows("c", "FALSE"), pending_place)?; + this.write_scalar(this.eval_windows("c", "TRUE"), dest)?; + true + } + InitOnceStatus::Begun => false, + }) + } } -impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} #[allow(non_snake_case)] -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn InitOnceBeginInitialize( &mut self, - init_once_op: &OpTy<'tcx, Provenance>, - flags_op: &OpTy<'tcx, Provenance>, - pending_op: &OpTy<'tcx, Provenance>, - context_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + init_once_op: &OpTy<'tcx>, + flags_op: &OpTy<'tcx>, + pending_op: &OpTy<'tcx>, + context_op: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let active_thread = this.get_active_thread(); let id = this.init_once_get_id(init_once_op)?; let flags = this.read_scalar(flags_op)?.to_u32()?; @@ -46,66 +67,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_unsup_format!("non-null `lpContext` in `InitOnceBeginInitialize`"); } - match this.init_once_status(id) { - InitOnceStatus::Uninitialized => { - this.init_once_begin(id); - this.write_scalar(this.eval_windows("c", "TRUE"), &pending_place)?; - } - InitOnceStatus::Begun => { - // Someone else is already on it. - // Block this thread until they are done. - // When we are woken up, set the `pending` flag accordingly. - struct Callback<'tcx> { - init_once_id: InitOnceId, - pending_place: MPlaceTy<'tcx, Provenance>, - } - - impl<'tcx> VisitProvenance for Callback<'tcx> { - fn visit_provenance(&self, visit: &mut VisitWith<'_>) { - let Callback { init_once_id: _, pending_place } = self; - pending_place.visit_provenance(visit); - } - } - - impl<'mir, 'tcx> MachineCallback<'mir, 'tcx> for Callback<'tcx> { - fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { - let pending = match this.init_once_status(self.init_once_id) { - InitOnceStatus::Uninitialized => - unreachable!( - "status should have either been set to begun or complete" - ), - InitOnceStatus::Begun => this.eval_windows("c", "TRUE"), - InitOnceStatus::Complete => this.eval_windows("c", "FALSE"), - }; - - this.write_scalar(pending, &self.pending_place)?; - - Ok(()) - } - } - - this.init_once_enqueue_and_block( - id, - active_thread, - Box::new(Callback { init_once_id: id, pending_place }), - ) - } - InitOnceStatus::Complete => { - this.init_once_observe_completed(id); - this.write_scalar(this.eval_windows("c", "FALSE"), &pending_place)?; - } + if this.init_once_try_begin(id, &pending_place, dest)? { + // Done! + return Ok(()); } - // This always succeeds (even if the thread is blocked, we will succeed if we ever unblock). - Ok(this.eval_windows("c", "TRUE")) + // We have to block, and then try again when we are woken up. + let dest = dest.clone(); + this.init_once_enqueue_and_block( + id, + callback!( + @capture<'tcx> { + id: InitOnceId, + pending_place: MPlaceTy<'tcx>, + dest: MPlaceTy<'tcx>, + } + @unblock = |this| { + let ret = this.init_once_try_begin(id, &pending_place, &dest)?; + assert!(ret, "we were woken up but init_once_try_begin still failed"); + Ok(()) + } + ), + ); + return Ok(()); } fn InitOnceComplete( &mut self, - init_once_op: &OpTy<'tcx, Provenance>, - flags_op: &OpTy<'tcx, Provenance>, - context_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + init_once_op: &OpTy<'tcx>, + flags_op: &OpTy<'tcx>, + context_op: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let id = this.init_once_get_id(init_once_op)?; @@ -142,11 +134,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn WaitOnAddress( &mut self, - ptr_op: &OpTy<'tcx, Provenance>, - compare_op: &OpTy<'tcx, Provenance>, - size_op: &OpTy<'tcx, Provenance>, - timeout_op: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + ptr_op: &OpTy<'tcx>, + compare_op: &OpTy<'tcx>, + size_op: &OpTy<'tcx>, + timeout_op: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -155,7 +147,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let size = this.read_target_usize(size_op)?; let timeout_ms = this.read_scalar(timeout_op)?.to_u32()?; - let thread = this.get_active_thread(); let addr = ptr.addr().bytes(); if size > 8 || !size.is_power_of_two() { @@ -166,11 +157,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; let size = Size::from_bytes(size); - let timeout_time = if timeout_ms == this.eval_windows_u32("c", "INFINITE") { + let timeout = if timeout_ms == this.eval_windows_u32("c", "INFINITE") { None } else { let duration = Duration::from_millis(timeout_ms.into()); - Some(CallbackTime::Monotonic(this.machine.clock.now().checked_add(duration).unwrap())) + Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)) }; // See the Linux futex implementation for why this fence exists. @@ -183,41 +174,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if futex_val == compare_val { // If the values are the same, we have to block. - this.block_thread(thread, BlockReason::Futex { addr }); - this.futex_wait(addr, thread, u32::MAX); - - if let Some(timeout_time) = timeout_time { - struct Callback<'tcx> { - thread: ThreadId, - addr: u64, - dest: MPlaceTy<'tcx, Provenance>, - } - - impl<'tcx> VisitProvenance for Callback<'tcx> { - fn visit_provenance(&self, visit: &mut VisitWith<'_>) { - let Callback { thread: _, addr: _, dest } = self; - dest.visit_provenance(visit); - } - } - - impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> { - fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { - this.unblock_thread(self.thread, BlockReason::Futex { addr: self.addr }); - this.futex_remove_waiter(self.addr, self.thread); - let error_timeout = this.eval_windows("c", "ERROR_TIMEOUT"); - this.set_last_error(error_timeout)?; - this.write_scalar(Scalar::from_i32(0), &self.dest)?; - - Ok(()) - } - } - - this.register_timeout_callback( - thread, - timeout_time, - Box::new(Callback { thread, addr, dest: dest.clone() }), - ); - } + this.futex_wait( + addr, + u32::MAX, // bitset + timeout, + Scalar::from_i32(1), // retval_succ + Scalar::from_i32(0), // retval_timeout + dest.clone(), + this.eval_windows("c", "ERROR_TIMEOUT"), + ); } this.write_scalar(Scalar::from_i32(1), dest)?; @@ -225,7 +190,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } - fn WakeByAddressSingle(&mut self, ptr_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { + fn WakeByAddressSingle(&mut self, ptr_op: &OpTy<'tcx>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let ptr = this.read_pointer(ptr_op)?; @@ -234,14 +199,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.atomic_fence(AtomicFenceOrd::SeqCst)?; let addr = ptr.addr().bytes(); - if let Some(thread) = this.futex_wake(addr, u32::MAX) { - this.unblock_thread(thread, BlockReason::Futex { addr }); - this.unregister_timeout_callback_if_exists(thread); - } + this.futex_wake(addr, u32::MAX)?; Ok(()) } - fn WakeByAddressAll(&mut self, ptr_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { + fn WakeByAddressAll(&mut self, ptr_op: &OpTy<'tcx>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let ptr = this.read_pointer(ptr_op)?; @@ -250,10 +212,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.atomic_fence(AtomicFenceOrd::SeqCst)?; let addr = ptr.addr().bytes(); - while let Some(thread) = this.futex_wake(addr, u32::MAX) { - this.unblock_thread(thread, BlockReason::Futex { addr }); - this.unregister_timeout_callback_if_exists(thread); - } + while this.futex_wake(addr, u32::MAX)? {} Ok(()) } diff --git a/src/tools/miri/src/shims/windows/thread.rs b/src/tools/miri/src/shims/windows/thread.rs index 3953a881a720..f3ddf6072af1 100644 --- a/src/tools/miri/src/shims/windows/thread.rs +++ b/src/tools/miri/src/shims/windows/thread.rs @@ -4,18 +4,18 @@ use rustc_target::spec::abi::Abi; use crate::*; use shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle}; -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} #[allow(non_snake_case)] -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn CreateThread( &mut self, - security_op: &OpTy<'tcx, Provenance>, - stacksize_op: &OpTy<'tcx, Provenance>, - start_op: &OpTy<'tcx, Provenance>, - arg_op: &OpTy<'tcx, Provenance>, - flags_op: &OpTy<'tcx, Provenance>, - thread_op: &OpTy<'tcx, Provenance>, + security_op: &OpTy<'tcx>, + stacksize_op: &OpTy<'tcx>, + start_op: &OpTy<'tcx>, + arg_op: &OpTy<'tcx>, + flags_op: &OpTy<'tcx>, + thread_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, ThreadId> { let this = self.eval_context_mut(); @@ -57,8 +57,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn WaitForSingleObject( &mut self, - handle_op: &OpTy<'tcx, Provenance>, - timeout_op: &OpTy<'tcx, Provenance>, + handle_op: &OpTy<'tcx>, + timeout_op: &OpTy<'tcx>, ) -> InterpResult<'tcx, u32> { let this = self.eval_context_mut(); @@ -69,7 +69,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Some(Handle::Thread(thread)) => thread, // Unlike on posix, the outcome of joining the current thread is not documented. // On current Windows, it just deadlocks. - Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.get_active_thread(), + Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(), _ => this.invalid_handle("WaitForSingleObject")?, }; diff --git a/src/tools/miri/src/shims/x86/aesni.rs b/src/tools/miri/src/shims/x86/aesni.rs index 8dc3748a12d4..e4e1531157a4 100644 --- a/src/tools/miri/src/shims/x86/aesni.rs +++ b/src/tools/miri/src/shims/x86/aesni.rs @@ -5,16 +5,14 @@ use rustc_target::spec::abi::Abi; use crate::*; -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: - crate::MiriInterpCxExt<'mir, 'tcx> -{ +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_x86_aesni_intrinsic( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "aes")?; @@ -127,17 +125,17 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: // with an external crate. _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } // Performs an AES round (given by `f`) on each 128-bit word of // `state` with the corresponding 128-bit key of `key`. fn aes_round<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - state: &OpTy<'tcx, Provenance>, - key: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + state: &OpTy<'tcx>, + key: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, f: impl Fn(u128, u128) -> u128, ) -> InterpResult<'tcx, ()> { assert_eq!(dest.layout.size, state.layout.size); diff --git a/src/tools/miri/src/shims/x86/avx.rs b/src/tools/miri/src/shims/x86/avx.rs index 86ef180a4489..0d2977b7b6fe 100644 --- a/src/tools/miri/src/shims/x86/avx.rs +++ b/src/tools/miri/src/shims/x86/avx.rs @@ -12,16 +12,14 @@ use super::{ }; use crate::*; -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: - crate::MiriInterpCxExt<'mir, 'tcx> -{ +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_x86_avx_intrinsic( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "avx")?; @@ -178,8 +176,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: // of 4. let chunk_base = i & !0b11; let src_i = u64::from(this.read_scalar(&control)?.to_u32()? & 0b11) - .checked_add(chunk_base) - .unwrap(); + .strict_add(chunk_base); this.copy_op( &this.project_index(&data, src_i)?, @@ -212,9 +209,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: // second instead of the first, ask Intel). To read the value from the current // chunk, add the destination index truncated to a multiple of 2. let chunk_base = i & !1; - let src_i = ((this.read_scalar(&control)?.to_u64()? >> 1) & 1) - .checked_add(chunk_base) - .unwrap(); + let src_i = + ((this.read_scalar(&control)?.to_u64()? >> 1) & 1).strict_add(chunk_base); this.copy_op( &this.project_index(&data, src_i)?, @@ -344,6 +340,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/avx2.rs b/src/tools/miri/src/shims/x86/avx2.rs index bbde5b49588c..016c525e57b4 100644 --- a/src/tools/miri/src/shims/x86/avx2.rs +++ b/src/tools/miri/src/shims/x86/avx2.rs @@ -10,16 +10,14 @@ use super::{ }; use crate::*; -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: - crate::MiriInterpCxExt<'mir, 'tcx> -{ +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_x86_avx2_intrinsic( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "avx2")?; @@ -81,7 +79,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let scale = this.read_scalar(scale)?.to_i8()?; if !matches!(scale, 1 | 2 | 4 | 8) { - throw_unsup_format!("invalid gather scale {scale}"); + panic!("invalid gather scale {scale}"); } let scale = i64::from(scale); @@ -440,6 +438,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index e519fa5508d2..b71aec02166b 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -18,18 +18,17 @@ mod sse; mod sse2; mod sse3; mod sse41; +mod sse42; mod ssse3; -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: - crate::MiriInterpCxExt<'mir, 'tcx> -{ +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_x86_intrinsic( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); // Prefix should have already been checked. @@ -50,13 +49,16 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let a = this.read_immediate(a)?; let b = this.read_immediate(b)?; - let (sum, overflow1) = this.overflowing_binary_op(mir::BinOp::Add, &a, &b)?; - let (sum, overflow2) = this.overflowing_binary_op( - mir::BinOp::Add, - &sum, - &ImmTy::from_uint(c_in, a.layout), - )?; - let c_out = overflow1 | overflow2; + let (sum, overflow1) = + this.binary_op(mir::BinOp::AddWithOverflow, &a, &b)?.to_pair(this); + let (sum, overflow2) = this + .binary_op( + mir::BinOp::AddWithOverflow, + &sum, + &ImmTy::from_uint(c_in, a.layout), + )? + .to_pair(this); + let c_out = overflow1.to_scalar().to_bool()? | overflow2.to_scalar().to_bool()?; this.write_scalar(Scalar::from_u8(c_out.into()), &this.project_field(dest, 0)?)?; this.write_immediate(*sum, &this.project_field(dest, 1)?)?; @@ -76,13 +78,16 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let a = this.read_immediate(a)?; let b = this.read_immediate(b)?; - let (sub, overflow1) = this.overflowing_binary_op(mir::BinOp::Sub, &a, &b)?; - let (sub, overflow2) = this.overflowing_binary_op( - mir::BinOp::Sub, - &sub, - &ImmTy::from_uint(b_in, a.layout), - )?; - let b_out = overflow1 | overflow2; + let (sub, overflow1) = + this.binary_op(mir::BinOp::SubWithOverflow, &a, &b)?.to_pair(this); + let (sub, overflow2) = this + .binary_op( + mir::BinOp::SubWithOverflow, + &sub, + &ImmTy::from_uint(b_in, a.layout), + )? + .to_pair(this); + let b_out = overflow1.to_scalar().to_bool()? | overflow2.to_scalar().to_bool()?; this.write_scalar(Scalar::from_u8(b_out.into()), &this.project_field(dest, 0)?)?; this.write_immediate(*sub, &this.project_field(dest, 1)?)?; @@ -101,6 +106,13 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } } + "pclmulqdq" => { + let [left, right, imm] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + pclmulqdq(this, left, right, imm, dest)?; + } + name if name.starts_with("sse.") => { return sse::EvalContextExt::emulate_x86_sse_intrinsic( this, link_name, abi, args, dest, @@ -126,6 +138,11 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this, link_name, abi, args, dest, ); } + name if name.starts_with("sse42.") => { + return sse42::EvalContextExt::emulate_x86_sse42_intrinsic( + this, link_name, abi, args, dest, + ); + } name if name.starts_with("aesni.") => { return aesni::EvalContextExt::emulate_x86_aesni_intrinsic( this, link_name, abi, args, dest, @@ -144,7 +161,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } @@ -194,13 +211,13 @@ impl FloatBinOp { /// Convert from the `imm` argument used to specify the comparison /// operation in intrinsics such as `llvm.x86.sse.cmp.ss`. fn cmp_from_imm<'tcx>( - this: &crate::MiriInterpCx<'_, 'tcx>, + this: &crate::MiriInterpCx<'tcx>, imm: i8, intrinsic: Symbol, ) -> InterpResult<'tcx, Self> { // Only bits 0..=4 are used, remaining should be zero. if imm & !0b1_1111 != 0 { - throw_unsup_format!("invalid `imm` parameter of {intrinsic}: 0x{imm:x}"); + panic!("invalid `imm` parameter of {intrinsic}: 0x{imm:x}"); } // Bit 4 specifies whether the operation is quiet or signaling, which // we do not care in Miri. @@ -238,14 +255,14 @@ impl FloatBinOp { /// Performs `which` scalar operation on `left` and `right` and returns /// the result. fn bin_op_float<'tcx, F: rustc_apfloat::Float>( - this: &crate::MiriInterpCx<'_, 'tcx>, + this: &crate::MiriInterpCx<'tcx>, which: FloatBinOp, - left: &ImmTy<'tcx, Provenance>, - right: &ImmTy<'tcx, Provenance>, -) -> InterpResult<'tcx, Scalar> { + left: &ImmTy<'tcx>, + right: &ImmTy<'tcx>, +) -> InterpResult<'tcx, Scalar> { match which { FloatBinOp::Arith(which) => { - let res = this.wrapping_binary_op(which, left, right)?; + let res = this.binary_op(which, left, right)?; Ok(res.to_scalar()) } FloatBinOp::Cmp { gt, lt, eq, unord } => { @@ -300,11 +317,11 @@ fn bin_op_float<'tcx, F: rustc_apfloat::Float>( /// Performs `which` operation on the first component of `left` and `right` /// and copies the other components from `left`. The result is stored in `dest`. fn bin_op_simd_float_first<'tcx, F: rustc_apfloat::Float>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, + this: &mut crate::MiriInterpCx<'tcx>, which: FloatBinOp, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { let (left, left_len) = this.operand_to_simd(left)?; let (right, right_len) = this.operand_to_simd(right)?; @@ -331,11 +348,11 @@ fn bin_op_simd_float_first<'tcx, F: rustc_apfloat::Float>( /// Performs `which` operation on each component of `left` and /// `right`, storing the result is stored in `dest`. fn bin_op_simd_float_all<'tcx, F: rustc_apfloat::Float>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, + this: &mut crate::MiriInterpCx<'tcx>, which: FloatBinOp, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { let (left, left_len) = this.operand_to_simd(left)?; let (right, right_len) = this.operand_to_simd(right)?; @@ -378,10 +395,10 @@ enum FloatUnaryOp { /// Performs `which` scalar operation on `op` and returns the result. #[allow(clippy::arithmetic_side_effects)] // floating point operations without side effects fn unary_op_f32<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, + this: &mut crate::MiriInterpCx<'tcx>, which: FloatUnaryOp, - op: &ImmTy<'tcx, Provenance>, -) -> InterpResult<'tcx, Scalar> { + op: &ImmTy<'tcx>, +) -> InterpResult<'tcx, Scalar> { match which { FloatUnaryOp::Sqrt => { let op = op.to_scalar(); @@ -412,7 +429,7 @@ fn unary_op_f32<'tcx>( /// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale). #[allow(clippy::arithmetic_side_effects)] // floating point arithmetic cannot panic fn apply_random_float_error( - this: &mut crate::MiriInterpCx<'_, '_>, + this: &mut crate::MiriInterpCx<'_>, val: F, err_scale: i32, ) -> F { @@ -429,10 +446,10 @@ fn apply_random_float_error( /// Performs `which` operation on the first component of `op` and copies /// the other components. The result is stored in `dest`. fn unary_op_ss<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, + this: &mut crate::MiriInterpCx<'tcx>, which: FloatUnaryOp, - op: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + op: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { let (op, op_len) = this.operand_to_simd(op)?; let (dest, dest_len) = this.mplace_to_simd(dest)?; @@ -452,10 +469,10 @@ fn unary_op_ss<'tcx>( /// Performs `which` operation on each component of `op`, storing the /// result is stored in `dest`. fn unary_op_ps<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, + this: &mut crate::MiriInterpCx<'tcx>, which: FloatUnaryOp, - op: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + op: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { let (op, op_len) = this.operand_to_simd(op)?; let (dest, dest_len) = this.mplace_to_simd(dest)?; @@ -489,11 +506,11 @@ enum ShiftOp { /// For arithmetic right-shifts, when right is larger than BITS - 1, the sign /// bit is copied to all bits. fn shift_simd_by_scalar<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, which: ShiftOp, - dest: &MPlaceTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { let (left, left_len) = this.operand_to_simd(left)?; let (dest, dest_len) = this.mplace_to_simd(dest)?; @@ -545,11 +562,11 @@ fn shift_simd_by_scalar<'tcx>( /// For arithmetic right-shifts, when right is larger than BITS - 1, the sign /// bit is copied to all bits. fn shift_simd_by_simd<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, which: ShiftOp, - dest: &MPlaceTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { let (left, left_len) = this.operand_to_simd(left)?; let (right, right_len) = this.operand_to_simd(right)?; @@ -597,8 +614,8 @@ fn shift_simd_by_simd<'tcx>( /// Takes a 128-bit vector, transmutes it to `[u64; 2]` and extracts /// the first value. fn extract_first_u64<'tcx>( - this: &crate::MiriInterpCx<'_, 'tcx>, - op: &OpTy<'tcx, Provenance>, + this: &crate::MiriInterpCx<'tcx>, + op: &OpTy<'tcx>, ) -> InterpResult<'tcx, u64> { // Transmute vector to `[u64; 2]` let array_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u64, 2))?; @@ -611,11 +628,11 @@ fn extract_first_u64<'tcx>( // Rounds the first element of `right` according to `rounding` // and copies the remaining elements from `left`. fn round_first<'tcx, F: rustc_apfloat::Float>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, - rounding: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + rounding: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { let (left, left_len) = this.operand_to_simd(left)?; let (right, right_len) = this.operand_to_simd(right)?; @@ -642,10 +659,10 @@ fn round_first<'tcx, F: rustc_apfloat::Float>( // Rounds all elements of `op` according to `rounding`. fn round_all<'tcx, F: rustc_apfloat::Float>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - op: &OpTy<'tcx, Provenance>, - rounding: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + op: &OpTy<'tcx>, + rounding: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { let (op, op_len) = this.operand_to_simd(op)?; let (dest, dest_len) = this.mplace_to_simd(dest)?; @@ -683,7 +700,7 @@ fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::R // SSE status register. Since we do not support modifying it from // Miri (or Rust), we assume it to be at its default mode (round-to-nearest). 0b100..=0b111 => Ok(rustc_apfloat::Round::NearestTiesToEven), - rounding => throw_unsup_format!("unsupported rounding mode 0x{rounding:02x}"), + rounding => panic!("invalid rounding mode 0x{rounding:02x}"), } } @@ -694,10 +711,10 @@ fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::R /// If `op` has more elements than `dest`, extra elements are ignored. If `op` /// has less elements than `dest`, the rest is filled with zeros. fn convert_float_to_int<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - op: &OpTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + op: &OpTy<'tcx>, rnd: rustc_apfloat::Round, - dest: &MPlaceTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { let (op, op_len) = this.operand_to_simd(op)?; let (dest, dest_len) = this.mplace_to_simd(dest)?; @@ -729,9 +746,9 @@ fn convert_float_to_int<'tcx>( /// In case of overflow (when the operand is the minimum value), the operation /// will wrap around. fn int_abs<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - op: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + op: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { let (op, op_len) = this.operand_to_simd(op)?; let (dest, dest_len) = this.mplace_to_simd(dest)?; @@ -744,12 +761,9 @@ fn int_abs<'tcx>( let op = this.read_immediate(&this.project_index(&op, i)?)?; let dest = this.project_index(&dest, i)?; - let lt_zero = this.wrapping_binary_op(mir::BinOp::Lt, &op, &zero)?; - let res = if lt_zero.to_scalar().to_bool()? { - this.wrapping_unary_op(mir::UnOp::Neg, &op)? - } else { - op - }; + let lt_zero = this.binary_op(mir::BinOp::Lt, &op, &zero)?; + let res = + if lt_zero.to_scalar().to_bool()? { this.unary_op(mir::UnOp::Neg, &op)? } else { op }; this.write_immediate(*res, &dest)?; } @@ -757,7 +771,7 @@ fn int_abs<'tcx>( Ok(()) } -/// Splits `op` (which must be a SIMD vector) into 128-bit chuncks. +/// Splits `op` (which must be a SIMD vector) into 128-bit chunks. /// /// Returns a tuple where: /// * The first element is the number of 128-bit chunks (let's call it `N`). @@ -765,7 +779,7 @@ fn int_abs<'tcx>( /// * The third element is the `op` vector split into chunks, i.e, it's /// type is `[[T; M]; N]` where `T` is the element type of `op`. fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, + this: &mut crate::MiriInterpCx<'tcx>, op: &P, ) -> InterpResult<'tcx, (u64, u64, P)> { let simd_layout = op.layout(); @@ -788,7 +802,7 @@ fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>( Ok((num_chunks, items_per_chunk, chunked_op)) } -/// Horizontaly performs `which` operation on adjacent values of +/// Horizontally performs `which` operation on adjacent values of /// `left` and `right` SIMD vectors and stores the result in `dest`. /// "Horizontal" means that the i-th output element is calculated /// from the elements 2*i and 2*i+1 of the concatenation of `left` and @@ -798,12 +812,12 @@ fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>( /// the is i-th 128-bit chunk of `dest` is calculated with the i-th /// 128-bit chunks of `left` and `right`). fn horizontal_bin_op<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, + this: &mut crate::MiriInterpCx<'tcx>, which: mir::BinOp, saturating: bool, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { assert_eq!(left.layout, dest.layout); assert_eq!(right.layout, dest.layout); @@ -832,7 +846,7 @@ fn horizontal_bin_op<'tcx>( let res = if saturating { Immediate::from(this.saturating_arith(which, &lhs, &rhs)?) } else { - *this.wrapping_binary_op(which, &lhs, &rhs)? + *this.binary_op(which, &lhs, &rhs)? }; this.write_immediate(res, &this.project_index(&dest, j)?)?; @@ -851,11 +865,11 @@ fn horizontal_bin_op<'tcx>( /// the is i-th 128-bit chunk of `dest` is calculated with the i-th /// 128-bit blocks of `left` and `right`). fn conditional_dot_product<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, - imm: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + imm: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { assert_eq!(left.layout, dest.layout); assert_eq!(right.layout, dest.layout); @@ -884,8 +898,8 @@ fn conditional_dot_product<'tcx>( let left = this.read_immediate(&this.project_index(&left, j)?)?; let right = this.read_immediate(&this.project_index(&right, j)?)?; - let mul = this.wrapping_binary_op(mir::BinOp::Mul, &left, &right)?; - sum = this.wrapping_binary_op(mir::BinOp::Add, &sum, &mul)?; + let mul = this.binary_op(mir::BinOp::Mul, &left, &right)?; + sum = this.binary_op(mir::BinOp::Add, &sum, &mul)?; } } @@ -909,9 +923,9 @@ fn conditional_dot_product<'tcx>( /// The first is true when all the bits of `op & mask` are zero. /// The second is true when `(op & mask) == mask` fn test_bits_masked<'tcx>( - this: &crate::MiriInterpCx<'_, 'tcx>, - op: &OpTy<'tcx, Provenance>, - mask: &OpTy<'tcx, Provenance>, + this: &crate::MiriInterpCx<'tcx>, + op: &OpTy<'tcx>, + mask: &OpTy<'tcx>, ) -> InterpResult<'tcx, (bool, bool)> { assert_eq!(op.layout, mask.layout); @@ -940,9 +954,9 @@ fn test_bits_masked<'tcx>( /// The first is true when the highest bit of each element of `op & mask` is zero. /// The second is true when the highest bit of each element of `!op & mask` is zero. fn test_high_bits_masked<'tcx>( - this: &crate::MiriInterpCx<'_, 'tcx>, - op: &OpTy<'tcx, Provenance>, - mask: &OpTy<'tcx, Provenance>, + this: &crate::MiriInterpCx<'tcx>, + op: &OpTy<'tcx>, + mask: &OpTy<'tcx>, ) -> InterpResult<'tcx, (bool, bool)> { assert_eq!(op.layout, mask.layout); @@ -971,10 +985,10 @@ fn test_high_bits_masked<'tcx>( /// Conditionally loads from `ptr` according the high bit of each /// element of `mask`. `ptr` does not need to be aligned. fn mask_load<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - ptr: &OpTy<'tcx, Provenance>, - mask: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + ptr: &OpTy<'tcx>, + mask: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { let (mask, mask_len) = this.operand_to_simd(mask)?; let (dest, dest_len) = this.mplace_to_simd(dest)?; @@ -1004,10 +1018,10 @@ fn mask_load<'tcx>( /// Conditionally stores into `ptr` according the high bit of each /// element of `mask`. `ptr` does not need to be aligned. fn mask_store<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - ptr: &OpTy<'tcx, Provenance>, - mask: &OpTy<'tcx, Provenance>, - value: &OpTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + ptr: &OpTy<'tcx>, + mask: &OpTy<'tcx>, + value: &OpTy<'tcx>, ) -> InterpResult<'tcx, ()> { let (mask, mask_len) = this.operand_to_simd(mask)?; let (value, value_len) = this.operand_to_simd(value)?; @@ -1044,11 +1058,11 @@ fn mask_store<'tcx>( /// the is i-th 128-bit chunk of `dest` is calculated with the i-th /// 128-bit chunks of `left` and `right`). fn mpsadbw<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, - imm: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + imm: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { assert_eq!(left.layout, right.layout); assert_eq!(left.layout.size, dest.layout.size); @@ -1101,10 +1115,10 @@ fn mpsadbw<'tcx>( /// /// fn pmulhrsw<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { let (left, left_len) = this.operand_to_simd(left)?; let (right, right_len) = this.operand_to_simd(right)?; @@ -1132,6 +1146,68 @@ fn pmulhrsw<'tcx>( Ok(()) } +/// Perform a carry-less multiplication of two 64-bit integers, selected from `left` and `right` according to `imm8`, +/// and store the results in `dst`. +/// +/// `left` and `right` are both vectors of type 2 x i64. Only bits 0 and 4 of `imm8` matter; +/// they select the element of `left` and `right`, respectively. +/// +/// +fn pclmulqdq<'tcx>( + this: &mut MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + imm8: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, +) -> InterpResult<'tcx, ()> { + assert_eq!(left.layout, right.layout); + assert_eq!(left.layout.size, dest.layout.size); + + // Transmute to `[u64; 2]` + + let array_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u64, 2))?; + let left = left.transmute(array_layout, this)?; + let right = right.transmute(array_layout, this)?; + let dest = dest.transmute(array_layout, this)?; + + let imm8 = this.read_scalar(imm8)?.to_u8()?; + + // select the 64-bit integer from left that the user specified (low or high) + let index = if (imm8 & 0x01) == 0 { 0 } else { 1 }; + let left = this.read_scalar(&this.project_index(&left, index)?)?.to_u64()?; + + // select the 64-bit integer from right that the user specified (low or high) + let index = if (imm8 & 0x10) == 0 { 0 } else { 1 }; + let right = this.read_scalar(&this.project_index(&right, index)?)?.to_u64()?; + + // Perform carry-less multiplication + // + // This operation is like long multiplication, but ignores all carries. + // That idea corresponds to the xor operator, which is used in the implementation. + // + // Wikipedia has an example https://en.wikipedia.org/wiki/Carry-less_product#Example + let mut result: u128 = 0; + + for i in 0..64 { + // if the i-th bit in right is set + if (right & (1 << i)) != 0 { + // xor result with `left` shifted to the left by i positions + result ^= (left as u128) << i; + } + } + + let result_low = (result & 0xFFFF_FFFF_FFFF_FFFF) as u64; + let result_high = (result >> 64) as u64; + + let dest_low = this.project_index(&dest, 0)?; + this.write_scalar(Scalar::from_u64(result_low), &dest_low)?; + + let dest_high = this.project_index(&dest, 1)?; + this.write_scalar(Scalar::from_u64(result_high), &dest_high)?; + + Ok(()) +} + /// Packs two N-bit integer vectors to a single N/2-bit integers. /// /// The conversion from N-bit to N/2-bit should be provided by `f`. @@ -1140,11 +1216,11 @@ fn pmulhrsw<'tcx>( /// the is i-th 128-bit chunk of `dest` is calculated with the i-th /// 128-bit chunks of `left` and `right`). fn pack_generic<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, - f: impl Fn(Scalar) -> InterpResult<'tcx, Scalar>, + this: &mut crate::MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, + f: impl Fn(Scalar) -> InterpResult<'tcx, Scalar>, ) -> InterpResult<'tcx, ()> { assert_eq!(left.layout, right.layout); assert_eq!(left.layout.size, dest.layout.size); @@ -1185,10 +1261,10 @@ fn pack_generic<'tcx>( /// the is i-th 128-bit chunk of `dest` is calculated with the i-th /// 128-bit chunks of `left` and `right`). fn packsswb<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { pack_generic(this, left, right, dest, |op| { let op = op.to_i16()?; @@ -1204,10 +1280,10 @@ fn packsswb<'tcx>( /// the is i-th 128-bit chunk of `dest` is calculated with the i-th /// 128-bit chunks of `left` and `right`). fn packuswb<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { pack_generic(this, left, right, dest, |op| { let op = op.to_i16()?; @@ -1223,10 +1299,10 @@ fn packuswb<'tcx>( /// the is i-th 128-bit chunk of `dest` is calculated with the i-th /// 128-bit chunks of `left` and `right`). fn packssdw<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { pack_generic(this, left, right, dest, |op| { let op = op.to_i32()?; @@ -1242,10 +1318,10 @@ fn packssdw<'tcx>( /// the is i-th 128-bit chunk of `dest` is calculated with the i-th /// 128-bit chunks of `left` and `right`). fn packusdw<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { pack_generic(this, left, right, dest, |op| { let op = op.to_i32()?; @@ -1256,13 +1332,13 @@ fn packusdw<'tcx>( /// Negates elements from `left` when the corresponding element in /// `right` is negative. If an element from `right` is zero, zero -/// is writen to the corresponding output element. +/// is written to the corresponding output element. /// In other words, multiplies `left` with `right.signum()`. fn psign<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, + this: &mut crate::MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, ()> { let (left, left_len) = this.operand_to_simd(left)?; let (right, right_len) = this.operand_to_simd(right)?; @@ -1276,11 +1352,8 @@ fn psign<'tcx>( let left = this.read_immediate(&this.project_index(&left, i)?)?; let right = this.read_scalar(&this.project_index(&right, i)?)?.to_int(dest.layout.size)?; - let res = this.wrapping_binary_op( - mir::BinOp::Mul, - &left, - &ImmTy::from_int(right.signum(), dest.layout), - )?; + let res = + this.binary_op(mir::BinOp::Mul, &left, &ImmTy::from_int(right.signum(), dest.layout))?; this.write_immediate(*res, &dest)?; } diff --git a/src/tools/miri/src/shims/x86/sse.rs b/src/tools/miri/src/shims/x86/sse.rs index ea967a8cf72b..32e8e8a66c13 100644 --- a/src/tools/miri/src/shims/x86/sse.rs +++ b/src/tools/miri/src/shims/x86/sse.rs @@ -9,16 +9,14 @@ use super::{ }; use crate::*; -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: - crate::MiriInterpCxExt<'mir, 'tcx> -{ +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_x86_sse_intrinsic( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "sse")?; @@ -212,6 +210,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs index 31fa66a496f3..e10047fefe6a 100644 --- a/src/tools/miri/src/shims/x86/sse2.rs +++ b/src/tools/miri/src/shims/x86/sse2.rs @@ -8,16 +8,14 @@ use super::{ }; use crate::*; -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: - crate::MiriInterpCxExt<'mir, 'tcx> -{ +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_x86_sse2_intrinsic( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "sse2")?; @@ -388,6 +386,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse3.rs b/src/tools/miri/src/shims/x86/sse3.rs index 8de339eb9e58..ef5a55d6eb28 100644 --- a/src/tools/miri/src/shims/x86/sse3.rs +++ b/src/tools/miri/src/shims/x86/sse3.rs @@ -5,16 +5,14 @@ use rustc_target::spec::abi::Abi; use super::horizontal_bin_op; use crate::*; -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: - crate::MiriInterpCxExt<'mir, 'tcx> -{ +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_x86_sse3_intrinsic( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "sse3")?; @@ -51,6 +49,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse41.rs b/src/tools/miri/src/shims/x86/sse41.rs index 011a7a16c68b..9e048fb9eb84 100644 --- a/src/tools/miri/src/shims/x86/sse41.rs +++ b/src/tools/miri/src/shims/x86/sse41.rs @@ -4,16 +4,14 @@ use rustc_target::spec::abi::Abi; use super::{conditional_dot_product, mpsadbw, packusdw, round_all, round_first, test_bits_masked}; use crate::*; -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: - crate::MiriInterpCxExt<'mir, 'tcx> -{ +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_x86_sse41_intrinsic( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "sse4.1")?; @@ -176,6 +174,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse42.rs b/src/tools/miri/src/shims/x86/sse42.rs new file mode 100644 index 000000000000..4e50d5e5dcfb --- /dev/null +++ b/src/tools/miri/src/shims/x86/sse42.rs @@ -0,0 +1,500 @@ +use rustc_middle::mir; +use rustc_middle::ty::layout::LayoutOf as _; +use rustc_middle::ty::Ty; +use rustc_span::Symbol; +use rustc_target::abi::Size; +use rustc_target::spec::abi::Abi; + +use crate::*; + +/// A bitmask constant for scrutinizing the immediate byte provided +/// to the string comparison intrinsics. It distinuishes between +/// 16-bit integers and 8-bit integers. See [`compare_strings`] +/// for more details about the immediate byte. +const USE_WORDS: u8 = 1; + +/// A bitmask constant for scrutinizing the immediate byte provided +/// to the string comparison intrinsics. It distinuishes between +/// signed integers and unsigned integers. See [`compare_strings`] +/// for more details about the immediate byte. +const USE_SIGNED: u8 = 2; + +/// The main worker for the string comparison intrinsics, where the given +/// strings are analyzed according to the given immediate byte. +/// +/// # Arguments +/// +/// * `str1` - The first string argument. It is always a length 16 array of bytes +/// or a length 8 array of two-byte words. +/// * `str2` - The second string argument. It is always a length 16 array of bytes +/// or a length 8 array of two-byte words. +/// * `len` is the length values of the supplied strings. It is distinct from the operand length +/// in that it describes how much of `str1` and `str2` will be used for the calculation and may +/// be smaller than the array length of `str1` and `str2`. The string length is counted in bytes +/// if using byte operands and in two-byte words when using two-byte word operands. +/// If the value is `None`, the length of a string is determined by the first +/// null value inside the string. +/// * `imm` is the immediate byte argument supplied to the intrinsic. The byte influences +/// the operation as follows: +/// +/// ```text +/// 0babccddef +/// || | |||- Use of bytes vs use of two-byte words inside the operation. +/// || | || +/// || | ||- Use of signed values versus use of unsigned values. +/// || | | +/// || | |- The comparison operation performed. A total of four operations are available. +/// || | * Equal any: Checks which characters of `str2` are inside `str1`. +/// || | * String ranges: Check if characters in `str2` are inside the provided character ranges. +/// || | Adjacent characters in `str1` constitute one range. +/// || | * String comparison: Mark positions where `str1` and `str2` have the same character. +/// || | * Substring search: Mark positions where `str1` is a substring in `str2`. +/// || | +/// || |- Result Polarity. The result bits may be subjected to a bitwise complement +/// || if these bits are set. +/// || +/// ||- Output selection. This bit has two meanings depending on the instruction. +/// | If the instruction is generating a mask, it distinguishes between a bit mask +/// | and a byte mask. Otherwise it distinguishes between the most significand bit +/// | and the least significand bit when generating an index. +/// | +/// |- This bit is ignored. It is expected that this bit is set to zero, but it is +/// not a requirement. +/// ``` +/// +/// # Returns +/// +/// A result mask. The bit at index `i` inside the mask is set if 'str2' starting at `i` +/// fulfills the test as defined inside the immediate byte. +/// The mask may be negated if negation flags inside the immediate byte are set. +/// +/// For more information, see the Intel Software Developer's Manual, Vol. 2b, Chapter 4.1. +#[allow(clippy::arithmetic_side_effects)] +fn compare_strings<'tcx>( + this: &mut MiriInterpCx<'tcx>, + str1: &OpTy<'tcx>, + str2: &OpTy<'tcx>, + len: Option<(u64, u64)>, + imm: u8, +) -> InterpResult<'tcx, i32> { + let default_len = default_len::(imm); + let (len1, len2) = if let Some(t) = len { + t + } else { + let len1 = implicit_len(this, str1, imm)?.unwrap_or(default_len); + let len2 = implicit_len(this, str2, imm)?.unwrap_or(default_len); + (len1, len2) + }; + + let mut result = 0; + match (imm >> 2) & 3 { + 0 => { + // Equal any: Checks which characters of `str2` are inside `str1`. + for i in 0..len2 { + let ch2 = this.read_immediate(&this.project_index(str2, i)?)?; + + for j in 0..len1 { + let ch1 = this.read_immediate(&this.project_index(str1, j)?)?; + + let eq = this.binary_op(mir::BinOp::Eq, &ch1, &ch2)?; + if eq.to_scalar().to_bool()? { + result |= 1 << i; + break; + } + } + } + } + 1 => { + // String ranges: Check if characters in `str2` are inside the provided character ranges. + // Adjacent characters in `str1` constitute one range. + let len1 = len1 - (len1 & 1); + let get_ch = |ch: Scalar| -> InterpResult<'tcx, i32> { + let result = match (imm & USE_WORDS != 0, imm & USE_SIGNED != 0) { + (true, true) => i32::from(ch.to_i16()?), + (true, false) => i32::from(ch.to_u16()?), + (false, true) => i32::from(ch.to_i8()?), + (false, false) => i32::from(ch.to_u8()?), + }; + Ok(result) + }; + + for i in 0..len2 { + for j in (0..len1).step_by(2) { + let ch2 = get_ch(this.read_scalar(&this.project_index(str2, i)?)?)?; + let ch1_1 = get_ch(this.read_scalar(&this.project_index(str1, j)?)?)?; + let ch1_2 = get_ch(this.read_scalar(&this.project_index(str1, j + 1)?)?)?; + + if ch1_1 <= ch2 && ch2 <= ch1_2 { + result |= 1 << i; + } + } + } + } + 2 => { + // String comparison: Mark positions where `str1` and `str2` have the same character. + result = (1 << default_len) - 1; + result ^= (1 << len1.max(len2)) - 1; + + for i in 0..len1.min(len2) { + let ch1 = this.read_immediate(&this.project_index(str1, i)?)?; + let ch2 = this.read_immediate(&this.project_index(str2, i)?)?; + let eq = this.binary_op(mir::BinOp::Eq, &ch1, &ch2)?; + result |= i32::from(eq.to_scalar().to_bool()?) << i; + } + } + 3 => { + // Substring search: Mark positions where `str1` is a substring in `str2`. + if len1 == 0 { + result = (1 << default_len) - 1; + } else if len1 <= len2 { + for i in 0..len2 { + if len1 > len2 - i { + break; + } + + result |= 1 << i; + + for j in 0..len1 { + let k = i + j; + + if k >= default_len { + break; + } else { + let ch1 = this.read_immediate(&this.project_index(str1, j)?)?; + let ch2 = this.read_immediate(&this.project_index(str2, k)?)?; + let ne = this.binary_op(mir::BinOp::Ne, &ch1, &ch2)?; + + if ne.to_scalar().to_bool()? { + result &= !(1 << i); + break; + } + } + } + } + } + } + _ => unreachable!(), + } + + // Polarity: Possibly perform a bitwise complement on the result. + match (imm >> 4) & 3 { + 3 => result ^= (1 << len1) - 1, + 1 => result ^= (1 << default_len) - 1, + _ => (), + } + + Ok(result) +} + +/// Obtain the arguments of the intrinsic based on its name. +/// The result is a tuple with the following values: +/// * The first string argument. +/// * The second string argument. +/// * The string length values, if the intrinsic requires them. +/// * The immediate instruction byte. +/// +/// The string arguments will be transmuted into arrays of bytes +/// or two-byte words, depending on the value of the immediate byte. +/// Originally, they are [__m128i](https://doc.rust-lang.org/stable/core/arch/x86_64/struct.__m128i.html) values +/// corresponding to the x86 128-bit integer SIMD type. +fn deconstruct_args<'tcx>( + unprefixed_name: &str, + this: &mut MiriInterpCx<'tcx>, + link_name: Symbol, + abi: Abi, + args: &[OpTy<'tcx>], +) -> InterpResult<'tcx, (OpTy<'tcx>, OpTy<'tcx>, Option<(u64, u64)>, u8)> { + let array_layout_fn = |this: &mut MiriInterpCx<'tcx>, imm: u8| { + if imm & USE_WORDS != 0 { + this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u16, 8)) + } else { + this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u8, 16)) + } + }; + + // The fourth letter of each string comparison intrinsic is either 'e' for "explicit" or 'i' for "implicit". + // The distinction will correspond to the intrinsics type signature. In this constext, "explicit" and "implicit" + // refer to the way the string length is determined. The length is either passed explicitly in the "explicit" + // case or determined by a null terminator in the "implicit" case. + let is_explicit = match unprefixed_name.as_bytes().get(4) { + Some(&b'e') => true, + Some(&b'i') => false, + _ => unreachable!(), + }; + + if is_explicit { + let [str1, len1, str2, len2, imm] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let imm = this.read_scalar(imm)?.to_u8()?; + + let default_len = default_len::(imm); + let len1 = u64::from(this.read_scalar(len1)?.to_u32()?.min(default_len)); + let len2 = u64::from(this.read_scalar(len2)?.to_u32()?.min(default_len)); + + let array_layout = array_layout_fn(this, imm)?; + let str1 = str1.transmute(array_layout, this)?; + let str2 = str2.transmute(array_layout, this)?; + + Ok((str1, str2, Some((len1, len2)), imm)) + } else { + let [str1, str2, imm] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let imm = this.read_scalar(imm)?.to_u8()?; + + let array_layout = array_layout_fn(this, imm)?; + let str1 = str1.transmute(array_layout, this)?; + let str2 = str2.transmute(array_layout, this)?; + + Ok((str1, str2, None, imm)) + } +} + +/// Calculate the c-style string length for a given string `str`. +/// The string is either a length 16 array of bytes a length 8 array of two-byte words. +fn implicit_len<'tcx>( + this: &mut MiriInterpCx<'tcx>, + str: &OpTy<'tcx>, + imm: u8, +) -> InterpResult<'tcx, Option> { + let mut result = None; + let zero = ImmTy::from_int(0, str.layout.field(this, 0)); + + for i in 0..default_len::(imm) { + let ch = this.read_immediate(&this.project_index(str, i)?)?; + let is_zero = this.binary_op(mir::BinOp::Eq, &ch, &zero)?; + if is_zero.to_scalar().to_bool()? { + result = Some(i); + break; + } + } + Ok(result) +} + +#[inline] +fn default_len>(imm: u8) -> T { + if imm & USE_WORDS != 0 { T::from(8u8) } else { T::from(16u8) } +} + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn emulate_x86_sse42_intrinsic( + &mut self, + link_name: Symbol, + abi: Abi, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx, EmulateItemResult> { + let this = self.eval_context_mut(); + this.expect_target_feature_for_intrinsic(link_name, "sse4.2")?; + // Prefix should have already been checked. + let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse42.").unwrap(); + + match unprefixed_name { + // Used to implement the `_mm_cmpestrm` and the `_mm_cmpistrm` functions. + // These functions compare the input strings and return the resulting mask. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=1044,922 + "pcmpistrm128" | "pcmpestrm128" => { + let (str1, str2, len, imm) = + deconstruct_args(unprefixed_name, this, link_name, abi, args)?; + let mask = compare_strings(this, &str1, &str2, len, imm)?; + + // The sixth bit inside the immediate byte distiguishes + // between a bit mask or a byte mask when generating a mask. + if imm & 0b100_0000 != 0 { + let (array_layout, size) = if imm & USE_WORDS != 0 { + (this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u16, 8))?, 2) + } else { + (this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u8, 16))?, 1) + }; + let size = Size::from_bytes(size); + let dest = dest.transmute(array_layout, this)?; + + for i in 0..default_len::(imm) { + let result = helpers::bool_to_simd_element(mask & (1 << i) != 0, size); + this.write_scalar(result, &this.project_index(&dest, i)?)?; + } + } else { + let layout = this.layout_of(this.tcx.types.i128)?; + let dest = dest.transmute(layout, this)?; + this.write_scalar(Scalar::from_i128(i128::from(mask)), &dest)?; + } + } + + // Used to implement the `_mm_cmpestra` and the `_mm_cmpistra` functions. + // These functions compare the input strings and return `1` if the end of the second + // input string is not reached and the resulting mask is zero, and `0` otherwise. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=919,1041 + "pcmpistria128" | "pcmpestria128" => { + let (str1, str2, len, imm) = + deconstruct_args(unprefixed_name, this, link_name, abi, args)?; + let result = if compare_strings(this, &str1, &str2, len, imm)? != 0 { + false + } else if let Some((_, len)) = len { + len >= default_len::(imm) + } else { + implicit_len(this, &str1, imm)?.is_some() + }; + + this.write_scalar(Scalar::from_i32(i32::from(result)), dest)?; + } + + // Used to implement the `_mm_cmpestri` and the `_mm_cmpistri` functions. + // These functions compare the input strings and return the bit index + // for most significant or least significant bit of the resulting mask. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=921,1043 + "pcmpistri128" | "pcmpestri128" => { + let (str1, str2, len, imm) = + deconstruct_args(unprefixed_name, this, link_name, abi, args)?; + let mask = compare_strings(this, &str1, &str2, len, imm)?; + + let len = default_len::(imm); + // The sixth bit inside the immediate byte distiguishes between the least + // significant bit and the most significant bit when generating an index. + let result = if imm & 0b100_0000 != 0 { + // most significant bit + 31u32.wrapping_sub(mask.leading_zeros()).min(len) + } else { + // least significant bit + mask.trailing_zeros().min(len) + }; + this.write_scalar(Scalar::from_i32(i32::try_from(result).unwrap()), dest)?; + } + + // Used to implement the `_mm_cmpestro` and the `_mm_cmpistro` functions. + // These functions compare the input strings and return the lowest bit of the + // resulting mask. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=923,1045 + "pcmpistrio128" | "pcmpestrio128" => { + let (str1, str2, len, imm) = + deconstruct_args(unprefixed_name, this, link_name, abi, args)?; + let mask = compare_strings(this, &str1, &str2, len, imm)?; + this.write_scalar(Scalar::from_i32(mask & 1), dest)?; + } + + // Used to implement the `_mm_cmpestrc` and the `_mm_cmpistrc` functions. + // These functions compare the input strings and return `1` if the resulting + // mask was non-zero, and `0` otherwise. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=920,1042 + "pcmpistric128" | "pcmpestric128" => { + let (str1, str2, len, imm) = + deconstruct_args(unprefixed_name, this, link_name, abi, args)?; + let mask = compare_strings(this, &str1, &str2, len, imm)?; + this.write_scalar(Scalar::from_i32(i32::from(mask != 0)), dest)?; + } + + // Used to implement the `_mm_cmpistrz` and the `_mm_cmpistrs` functions. + // These functions return `1` if the string end has been reached and `0` otherwise. + // Since these functions define the string length implicitly, it is equal to a + // search for a null terminator (see `deconstruct_args` for more details). + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=924,925 + "pcmpistriz128" | "pcmpistris128" => { + let [str1, str2, imm] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let imm = this.read_scalar(imm)?.to_u8()?; + + let str = if unprefixed_name == "pcmpistris128" { str1 } else { str2 }; + let array_layout = if imm & USE_WORDS != 0 { + this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u16, 8))? + } else { + this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u8, 16))? + }; + let str = str.transmute(array_layout, this)?; + let result = implicit_len(this, &str, imm)?.is_some(); + + this.write_scalar(Scalar::from_i32(i32::from(result)), dest)?; + } + + // Used to implement the `_mm_cmpestrz` and the `_mm_cmpestrs` functions. + // These functions return 1 if the explicitly passed string length is smaller + // than 16 for byte-sized operands or 8 for word-sized operands. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=1046,1047 + "pcmpestriz128" | "pcmpestris128" => { + let [_, len1, _, len2, imm] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let len = if unprefixed_name == "pcmpestris128" { len1 } else { len2 }; + let len = this.read_scalar(len)?.to_i32()?; + let imm = this.read_scalar(imm)?.to_u8()?; + this.write_scalar( + Scalar::from_i32(i32::from(len < default_len::(imm))), + dest, + )?; + } + + // Used to implement the `_mm_crc32_u{8, 16, 32, 64}` functions. + // These functions calculate a 32-bit CRC using `0x11EDC6F41` + // as the polynomial, also known as CRC32C. + // https://datatracker.ietf.org/doc/html/rfc3720#section-12.1 + "crc32.32.8" | "crc32.32.16" | "crc32.32.32" | "crc32.64.64" => { + let bit_size = match unprefixed_name { + "crc32.32.8" => 8, + "crc32.32.16" => 16, + "crc32.32.32" => 32, + "crc32.64.64" => 64, + _ => unreachable!(), + }; + + if bit_size == 64 && this.tcx.sess.target.arch != "x86_64" { + return Ok(EmulateItemResult::NotSupported); + } + + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let left = this.read_scalar(left)?; + let right = this.read_scalar(right)?; + + let crc = if bit_size == 64 { + // The 64-bit version will only consider the lower 32 bits, + // while the upper 32 bits get discarded. + #[allow(clippy::cast_possible_truncation)] + u128::from((left.to_u64()? as u32).reverse_bits()) + } else { + u128::from(left.to_u32()?.reverse_bits()) + }; + let v = match bit_size { + 8 => u128::from(right.to_u8()?.reverse_bits()), + 16 => u128::from(right.to_u16()?.reverse_bits()), + 32 => u128::from(right.to_u32()?.reverse_bits()), + 64 => u128::from(right.to_u64()?.reverse_bits()), + _ => unreachable!(), + }; + + // Perform polynomial division modulo 2. + // The algorithm for the division is an adapted version of the + // schoolbook division algorithm used for normal integer or polynomial + // division. In this context, the quotient is not calculated, since + // only the remainder is needed. + // + // The algorithm works as follows: + // 1. Pull down digits until division can be performed. In the context of division + // modulo 2 it means locating the most significant digit of the dividend and shifting + // the divisor such that the position of the divisors most significand digit and the + // dividends most significand digit match. + // 2. Perform a division and determine the remainder. Since it is arithmetic modulo 2, + // this operation is a simple bitwise exclusive or. + // 3. Repeat steps 1. and 2. until the full remainder is calculated. This is the case + // once the degree of the remainder polynomial is smaller than the degree of the + // divisor polynomial. In other words, the number of leading zeros of the remainder + // is larger than the number of leading zeros of the divisor. It is important to + // note that standard arithmetic comparison is not applicable here: + // 0b10011 / 0b11111 = 0b01100 is a valid division, even though the dividend is + // smaller than the divisor. + let mut dividend = (crc << bit_size) ^ (v << 32); + const POLYNOMIAL: u128 = 0x11EDC6F41; + while dividend.leading_zeros() <= POLYNOMIAL.leading_zeros() { + dividend ^= + (POLYNOMIAL << POLYNOMIAL.leading_zeros()) >> dividend.leading_zeros(); + } + + let result = u32::try_from(dividend).unwrap().reverse_bits(); + let result = if bit_size == 64 { + Scalar::from_u64(u64::from(result)) + } else { + Scalar::from_u32(result) + }; + + this.write_scalar(result, dest)?; + } + _ => return Ok(EmulateItemResult::NotSupported), + } + Ok(EmulateItemResult::NeedsReturn) + } +} diff --git a/src/tools/miri/src/shims/x86/ssse3.rs b/src/tools/miri/src/shims/x86/ssse3.rs index d30de4300886..6a815e4cea3c 100644 --- a/src/tools/miri/src/shims/x86/ssse3.rs +++ b/src/tools/miri/src/shims/x86/ssse3.rs @@ -5,16 +5,14 @@ use rustc_target::spec::abi::Abi; use super::{horizontal_bin_op, int_abs, pmulhrsw, psign}; use crate::*; -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: - crate::MiriInterpCxExt<'mir, 'tcx> -{ +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_x86_ssse3_intrinsic( &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "ssse3")?; @@ -137,6 +135,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/test-cargo-miri/Cargo.lock b/src/tools/miri/test-cargo-miri/Cargo.lock index 4783f79ea8fb..8f618e7ffb38 100644 --- a/src/tools/miri/test-cargo-miri/Cargo.lock +++ b/src/tools/miri/test-cargo-miri/Cargo.lock @@ -123,6 +123,10 @@ dependencies = [ "byteorder 1.5.0", ] +[[package]] +name = "test-local-crate-detection" +version = "0.1.0" + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/src/tools/miri/test-cargo-miri/Cargo.toml b/src/tools/miri/test-cargo-miri/Cargo.toml index bfef388669d1..574f1d05a6fa 100644 --- a/src/tools/miri/test-cargo-miri/Cargo.toml +++ b/src/tools/miri/test-cargo-miri/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["subcrate", "issue-1567", "exported-symbol-dep"] +members = ["subcrate", "issue-1567", "exported-symbol-dep", "test-local-crate-detection"] exclude = ["no-std-smoke"] # it wants to be panic="abort" [package] diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index 83f3e4c919b9..d855c333a756 100755 --- a/src/tools/miri/test-cargo-miri/run-test.py +++ b/src/tools/miri/test-cargo-miri/run-test.py @@ -131,6 +131,10 @@ def test_cargo_miri_run(): cargo_miri("run") + ["--target-dir=custom-run", "--", "--target-dir=target/custom-run"], "run.args.stdout.ref", "run.custom-target-dir.stderr.ref", ) + test("`cargo miri run --package=test-local-crate-detection` (test local crate detection)", + cargo_miri("run") + ["--package=test-local-crate-detection"], + "run.local_crate.stdout.ref", "run.local_crate.stderr.ref", + ) def test_cargo_miri_test(): # rustdoc is not run on foreign targets diff --git a/src/tools/miri/test-cargo-miri/run.local_crate.stderr.ref b/src/tools/miri/test-cargo-miri/run.local_crate.stderr.ref new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/tools/miri/test-cargo-miri/run.local_crate.stdout.ref b/src/tools/miri/test-cargo-miri/run.local_crate.stdout.ref new file mode 100644 index 000000000000..1587de9ff3f8 --- /dev/null +++ b/src/tools/miri/test-cargo-miri/run.local_crate.stdout.ref @@ -0,0 +1 @@ +subcrate,issue_1567,exported_symbol_dep,test_local_crate_detection,cargo_miri_test,cdylib,exported_symbol,issue_1691,issue_1705,issue_rust_86261,proc_macro_crate diff --git a/src/tools/miri/test-cargo-miri/test-local-crate-detection/Cargo.toml b/src/tools/miri/test-cargo-miri/test-local-crate-detection/Cargo.toml new file mode 100644 index 000000000000..2d41b210d4c1 --- /dev/null +++ b/src/tools/miri/test-cargo-miri/test-local-crate-detection/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "test-local-crate-detection" +version = "0.1.0" +edition = "2021" diff --git a/src/tools/miri/test-cargo-miri/test-local-crate-detection/src/main.rs b/src/tools/miri/test-cargo-miri/test-local-crate-detection/src/main.rs new file mode 100644 index 000000000000..0991aa384607 --- /dev/null +++ b/src/tools/miri/test-cargo-miri/test-local-crate-detection/src/main.rs @@ -0,0 +1,5 @@ +fn main() { + // Make sure we detect all crates from this workspace as "local". + // The env var is set during the "build" so we can use `env!` to access it directly. + println!("{}", env!("MIRI_LOCAL_CRATES")); +} diff --git a/src/tools/miri/test_dependencies/Cargo.lock b/src/tools/miri/test_dependencies/Cargo.lock index c73d13a4620c..d534fdab2914 100644 --- a/src/tools/miri/test_dependencies/Cargo.lock +++ b/src/tools/miri/test_dependencies/Cargo.lock @@ -253,6 +253,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "socket2" version = "0.5.7" @@ -297,6 +306,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.48.0", diff --git a/src/tools/miri/test_dependencies/Cargo.toml b/src/tools/miri/test_dependencies/Cargo.toml index 1894f53ce49c..ce11a8abb0ea 100644 --- a/src/tools/miri/test_dependencies/Cargo.toml +++ b/src/tools/miri/test_dependencies/Cargo.toml @@ -11,14 +11,16 @@ edition = "2021" # all dependencies (and their transitive ones) listed here can be used in `tests/`. libc = "0.2" num_cpus = "1.10.1" -tempfile = "3" getrandom_01 = { package = "getrandom", version = "0.1" } getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] } [target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dependencies] +tempfile = "3" page_size = "0.6" -tokio = { version = "1.24", features = ["macros", "rt-multi-thread", "time", "net"] } +# Avoid pulling in all of tokio's dependencies. +# However, without `net` and `signal`, tokio uses fewer relevant system APIs. +tokio = { version = "1.24", features = ["macros", "rt-multi-thread", "time", "net", "fs", "sync", "signal"] } [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.52", features = [ "Win32_Foundation", "Win32_System_Threading" ] } diff --git a/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.rs b/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.rs new file mode 100644 index 000000000000..9a33cdccd270 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.rs @@ -0,0 +1,15 @@ +//@ignore-target-windows: Windows does not support the standard C11 aligned_alloc. + +fn main() { + // libc doesn't have this function (https://github.com/rust-lang/libc/issues/3689), + // so we declare it ourselves. + extern "C" { + fn aligned_alloc(alignment: libc::size_t, size: libc::size_t) -> *mut libc::c_void; + } + + // Make sure even zero-sized allocations need to be freed. + + unsafe { + aligned_alloc(2, 0); //~ERROR: memory leaked + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.stderr b/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.stderr new file mode 100644 index 000000000000..b0756d572120 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.stderr @@ -0,0 +1,15 @@ +error: memory leaked: ALLOC (C heap, size: 0, align: 2), allocated here: + --> $DIR/aligned_alloc_size_zero_leak.rs:LL:CC + | +LL | aligned_alloc(2, 0); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: BACKTRACE: + = note: inside `main` at $DIR/aligned_alloc_size_zero_leak.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.rs b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.rs new file mode 100644 index 000000000000..fb9a23206c60 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.rs @@ -0,0 +1,11 @@ +//@only-target-linux +fn main() { + // eventfd read will block when EFD_NONBLOCK flag is clear and counter = 0. + // This will pass when blocking is implemented. + let flags = libc::EFD_CLOEXEC; + let fd = unsafe { libc::eventfd(0, flags) }; + let mut buf: [u8; 8] = [0; 8]; + let _res: i32 = unsafe { + libc::read(fd, buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap() //~ERROR: blocking is unsupported + }; +} diff --git a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.stderr b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.stderr new file mode 100644 index 000000000000..fdd0b4272caf --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.stderr @@ -0,0 +1,14 @@ +error: unsupported operation: eventfd: blocking is unsupported + --> $DIR/libc_eventfd_read_block.rs:LL:CC + | +LL | libc::read(fd, buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ eventfd: blocking is unsupported + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = note: BACKTRACE: + = note: inside `main` at $DIR/libc_eventfd_read_block.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.rs b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.rs new file mode 100644 index 000000000000..2037a516dea1 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.rs @@ -0,0 +1,21 @@ +//@only-target-linux +fn main() { + // eventfd write will block when EFD_NONBLOCK flag is clear + // and the addition caused counter to exceed u64::MAX - 1. + // This will pass when blocking is implemented. + let flags = libc::EFD_CLOEXEC; + let fd = unsafe { libc::eventfd(0, flags) }; + // Write u64 - 1. + let mut sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes(); + let res: i64 = unsafe { + libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap() + }; + assert_eq!(res, 8); + + // Write 1. + sized_8_data = 1_u64.to_ne_bytes(); + // Write 1 to the counter. + let _res: i64 = unsafe { + libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap() //~ERROR: blocking is unsupported + }; +} diff --git a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.stderr b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.stderr new file mode 100644 index 000000000000..f12c0ddfb171 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.stderr @@ -0,0 +1,14 @@ +error: unsupported operation: eventfd: blocking is unsupported + --> $DIR/libc_eventfd_write_block.rs:LL:CC + | +LL | libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ eventfd: blocking is unsupported + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = note: BACKTRACE: + = note: inside `main` at $DIR/libc_eventfd_write_block.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_double_free.rs b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_double_free.rs new file mode 100644 index 000000000000..b6b7b007f2b6 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_double_free.rs @@ -0,0 +1,14 @@ +//@ignore-target-windows: No posix_memalign on Windows + +use std::ptr; + +fn main() { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 64; + let size = 0; + unsafe { + let _ = libc::posix_memalign(&mut ptr, align, size); + libc::free(ptr); + libc::free(ptr); //~ERROR: dangling + } +} diff --git a/src/tools/miri/tests/fail/zst2.stderr b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_double_free.stderr similarity index 51% rename from src/tools/miri/tests/fail/zst2.stderr rename to src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_double_free.stderr index b3f65e7866dc..3ed117c5a0ad 100644 --- a/src/tools/miri/tests/fail/zst2.stderr +++ b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_double_free.stderr @@ -1,23 +1,23 @@ error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling - --> $DIR/zst2.rs:LL:CC + --> $DIR/posix_memalign_size_zero_double_free.rs:LL:CC | -LL | unsafe { *x = zst_val }; - | ^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling +LL | libc::free(ptr); + | ^^^^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information help: ALLOC was allocated here: - --> $DIR/zst2.rs:LL:CC + --> $DIR/posix_memalign_size_zero_double_free.rs:LL:CC | -LL | let mut x_box = Box::new(1u8); - | ^^^^^^^^^^^^^ +LL | let _ = libc::posix_memalign(&mut ptr, align, size); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: ALLOC was deallocated here: - --> $DIR/zst2.rs:LL:CC + --> $DIR/posix_memalign_size_zero_double_free.rs:LL:CC | -LL | drop(x_box); - | ^^^^^^^^^^^ +LL | libc::free(ptr); + | ^^^^^^^^^^^^^^^ = note: BACKTRACE (of the first span): - = note: inside `main` at $DIR/zst2.rs:LL:CC + = note: inside `main` at $DIR/posix_memalign_size_zero_double_free.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_leak.rs b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_leak.rs new file mode 100644 index 000000000000..1a4c9605fe01 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_leak.rs @@ -0,0 +1,10 @@ +//@ignore-target-windows: No posix_memalign on Windows + +use std::ptr; + +fn main() { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 64; + let size = 0; + let _ = unsafe { libc::posix_memalign(&mut ptr, align, size) }; //~ERROR: memory leak +} diff --git a/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_leak.stderr b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_leak.stderr new file mode 100644 index 000000000000..7ea0fa314697 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_leak.stderr @@ -0,0 +1,15 @@ +error: memory leaked: ALLOC (C heap, size: 0, align: 64), allocated here: + --> $DIR/posix_memalign_size_zero_leak.rs:LL:CC + | +LL | let _ = unsafe { libc::posix_memalign(&mut ptr, align, size) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: BACKTRACE: + = note: inside `main` at $DIR/posix_memalign_size_zero_leak.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.rs new file mode 100644 index 000000000000..c28a6d966fe5 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.rs @@ -0,0 +1,12 @@ +//@ignore-target-windows: no libc socketpair on Windows + +// This is temporarily here because blocking on fd is not supported yet. +// When blocking is eventually supported, this will be moved to pass-dep/libc/libc-socketpair + +fn main() { + let mut fds = [-1, -1]; + let _ = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; + // The read below will be blocked because the buffer is empty. + let mut buf: [u8; 3] = [0; 3]; + let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; //~ERROR: blocking isn't supported +} diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.stderr new file mode 100644 index 000000000000..b5ed72d9f1b1 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_read_blocking.stderr @@ -0,0 +1,14 @@ +error: unsupported operation: socketpair read: blocking isn't supported yet + --> $DIR/socketpair_read_blocking.rs:LL:CC + | +LL | let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ socketpair read: blocking isn't supported yet + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = note: BACKTRACE: + = note: inside `main` at $DIR/socketpair_read_blocking.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.rs new file mode 100644 index 000000000000..e2fbc0ae4b42 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.rs @@ -0,0 +1,16 @@ +//@ignore-target-windows: no libc socketpair on Windows +// This is temporarily here because blocking on fd is not supported yet. +// When blocking is eventually supported, this will be moved to pass-dep/libc/libc-socketpair +fn main() { + let mut fds = [-1, -1]; + let _ = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; + // Write size > buffer capacity + // Used up all the space in the buffer. + let arr1: [u8; 212992] = [1; 212992]; + let _ = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) }; + let data = "abc".as_bytes().as_ptr(); + // The write below will be blocked as the buffer is full. + let _ = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; //~ERROR: blocking isn't supported + let mut buf: [u8; 3] = [0; 3]; + let _res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; +} diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.stderr new file mode 100644 index 000000000000..7b3a0d27636d --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_write_blocking.stderr @@ -0,0 +1,14 @@ +error: unsupported operation: socketpair write: blocking isn't supported yet + --> $DIR/socketpair_write_blocking.rs:LL:CC + | +LL | let _ = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ socketpair write: blocking isn't supported yet + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = note: BACKTRACE: + = note: inside `main` at $DIR/socketpair_write_blocking.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs b/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs deleted file mode 100644 index a1fefe04ab69..000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Make sure we find these even with many checks disabled. -//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation - -fn main() { - let p = { - let b = Box::new(42); - &*b as *const i32 as *const () - }; - let _x = unsafe { *p }; //~ ERROR: has been freed -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr deleted file mode 100644 index 72b9a4a2d6ca..000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling - --> $DIR/dangling_zst_deref.rs:LL:CC - | -LL | let _x = unsafe { *p }; - | ^^ memory access failed: ALLOC has been freed, so this pointer is dangling - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information -help: ALLOC was allocated here: - --> $DIR/dangling_zst_deref.rs:LL:CC - | -LL | let b = Box::new(42); - | ^^^^^^^^^^^^ -help: ALLOC was deallocated here: - --> $DIR/dangling_zst_deref.rs:LL:CC - | -LL | }; - | ^ - = note: BACKTRACE (of the first span): - = note: inside `main` at $DIR/dangling_zst_deref.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs deleted file mode 100644 index 73d0b1206801..000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - // This pointer *could* be NULL so we cannot load from it, not even at ZST - let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *const (); - let _x: () = unsafe { *ptr }; //~ ERROR: out-of-bounds -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr deleted file mode 100644 index 13c53e20b8a6..000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds - --> $DIR/maybe_null_pointer_deref_zst.rs:LL:CC - | -LL | let _x: () = unsafe { *ptr }; - | ^^^^ memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/maybe_null_pointer_deref_zst.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs deleted file mode 100644 index 5537207ae424..000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs +++ /dev/null @@ -1,8 +0,0 @@ -fn main() { - // This pointer *could* be NULL so we cannot load from it, not even at ZST. - // Not using the () type here, as writes of that type do not even have MIR generated. - // Also not assigning directly as that's array initialization, not assignment. - let zst_val = [1u8; 0]; - let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *mut [u8; 0]; - unsafe { *ptr = zst_val }; //~ ERROR: out-of-bounds -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs deleted file mode 100644 index f8af43ff3529..000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[allow(deref_nullptr)] -fn main() { - let x: () = unsafe { *std::ptr::null() }; //~ ERROR: memory access failed: null pointer is a dangling pointer - panic!("this should never print: {:?}", x); -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr deleted file mode 100644 index 1a8794f3ceb7..000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) - --> $DIR/null_pointer_deref_zst.rs:LL:CC - | -LL | let x: () = unsafe { *std::ptr::null() }; - | ^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/null_pointer_deref_zst.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs deleted file mode 100644 index edd6c8fadce4..000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[allow(deref_nullptr)] -fn main() { - // Not using the () type here, as writes of that type do not even have MIR generated. - // Also not assigning directly as that's array initialization, not assignment. - let zst_val = [1u8; 0]; - unsafe { std::ptr::null_mut::<[u8; 0]>().write(zst_val) }; - //~^ERROR: memory access failed: null pointer is a dangling pointer -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr deleted file mode 100644 index 1d4704e2a0e8..000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) - --> $DIR/null_pointer_write_zst.rs:LL:CC - | -LL | unsafe { std::ptr::null_mut::<[u8; 0]>().write(zst_val) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/null_pointer_write_zst.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs index f71df9a1c909..982d57b73720 100644 --- a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs +++ b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs @@ -1,5 +1,5 @@ -// Validation stops this too early. -//@compile-flags: -Zmiri-disable-validation +// Validation and SB stop this too early. +//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows trait T1 { #[allow(dead_code)] diff --git a/src/tools/miri/tests/fail/environ-gets-deallocated.rs b/src/tools/miri/tests/fail/environ-gets-deallocated.rs index 6dcf1fdb1c7d..5391a9176d0d 100644 --- a/src/tools/miri/tests/fail/environ-gets-deallocated.rs +++ b/src/tools/miri/tests/fail/environ-gets-deallocated.rs @@ -1,6 +1,5 @@ //@ignore-target-windows: Windows does not have a global environ list that the program can access directly -#[cfg(any(target_os = "linux", target_os = "freebsd"))] fn get_environ() -> *const *const u8 { extern "C" { static mut environ: *const *const u8; @@ -8,14 +7,6 @@ fn get_environ() -> *const *const u8 { unsafe { environ } } -#[cfg(target_os = "macos")] -fn get_environ() -> *const *const u8 { - extern "C" { - fn _NSGetEnviron() -> *mut *const *const u8; - } - unsafe { *_NSGetEnviron() } -} - fn main() { let pointer = get_environ(); let _x = unsafe { *pointer }; diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr index d88781ed2254..112a9687837e 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr @@ -16,7 +16,7 @@ LL | ABORT(); = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC = note: inside `core::panicking::panic_cannot_unwind` at RUSTLIB/core/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr index d88781ed2254..112a9687837e 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr @@ -16,7 +16,7 @@ LL | ABORT(); = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC = note: inside `core::panicking::panic_cannot_unwind` at RUSTLIB/core/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.stderr b/src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.stderr deleted file mode 100644 index 699dda52096f..000000000000 --- a/src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: unsupported operation: miri can only use intrinsic fallback bodies that check UB. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that - --> $DIR/intrinsic_fallback_checks_ub.rs:LL:CC - | -LL | ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ miri can only use intrinsic fallback bodies that check UB. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that - | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = note: BACKTRACE: - = note: inside `main` at $DIR/intrinsic_fallback_checks_ub.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.rs b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs similarity index 75% rename from src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.rs rename to src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs index 93c9d3d7814c..888c548e49b5 100644 --- a/src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.rs +++ b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs @@ -10,5 +10,5 @@ pub const fn ptr_guaranteed_cmp(ptr: *const T, other: *const T) -> u8 { fn main() { ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null()); - //~^ ERROR: can only use intrinsic fallback bodies that check UB. + //~^ ERROR: can only use intrinsic fallback bodies that exactly reflect the specification } diff --git a/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.stderr b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.stderr new file mode 100644 index 000000000000..db3941a32a56 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.stderr @@ -0,0 +1,14 @@ +error: unsupported operation: Miri can only use intrinsic fallback bodies that exactly reflect the specification: they fully check for UB and are as non-deterministic as possible. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_is_spec]` attribute to it; also ping @rust-lang/miri when you do that + --> $DIR/intrinsic_fallback_is_spec.rs:LL:CC + | +LL | ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Miri can only use intrinsic fallback bodies that exactly reflect the specification: they fully check for UB and are as non-deterministic as possible. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_is_spec]` attribute to it; also ping @rust-lang/miri when you do that + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = note: BACKTRACE: + = note: inside `main` at $DIR/intrinsic_fallback_is_spec.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/intrinsics/copy_null.rs b/src/tools/miri/tests/fail/intrinsics/copy_null.rs deleted file mode 100644 index 237e517f2875..000000000000 --- a/src/tools/miri/tests/fail/intrinsics/copy_null.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![feature(intrinsics)] - -// Directly call intrinsic to avoid debug assertions in libstd -extern "rust-intrinsic" { - fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); -} - -fn main() { - let mut data = [0u16; 4]; - let ptr = &mut data[0] as *mut u16; - // Even copying 0 elements from NULL should error. - unsafe { - copy_nonoverlapping(std::ptr::null(), ptr, 0); //~ ERROR: memory access failed: null pointer is a dangling pointer - } -} diff --git a/src/tools/miri/tests/fail/intrinsics/copy_null.stderr b/src/tools/miri/tests/fail/intrinsics/copy_null.stderr deleted file mode 100644 index d73c03475d69..000000000000 --- a/src/tools/miri/tests/fail/intrinsics/copy_null.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) - --> $DIR/copy_null.rs:LL:CC - | -LL | copy_nonoverlapping(std::ptr::null(), ptr, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/copy_null.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs new file mode 100644 index 000000000000..0c305eed6e18 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs @@ -0,0 +1,24 @@ +//@compile-flags: -Zmiri-disable-validation +#![feature(core_intrinsics, custom_mir)] +use std::intrinsics::mir::*; + +// This disables validation and uses custom MIR hit exactly the UB in the intrinsic, +// rather than getting UB from the typed load or parameter passing. + +#[custom_mir(dialect = "runtime")] +pub unsafe fn deref_meta(p: *const *const [i32]) -> usize { + mir! { + { + RET = PtrMetadata(*p); //~ ERROR: Undefined Behavior: using uninitialized data + Return() + } + } +} + +fn main() { + let mut p = std::mem::MaybeUninit::<*const [i32]>::uninit(); + unsafe { + (*p.as_mut_ptr().cast::<[usize; 2]>())[1] = 4; + let _meta = deref_meta(p.as_ptr().cast()); + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr new file mode 100644 index 000000000000..6478dcc2507f --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory + --> $DIR/ptr_metadata_uninit_slice_data.rs:LL:CC + | +LL | RET = PtrMetadata(*p); + | ^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `deref_meta` at $DIR/ptr_metadata_uninit_slice_data.rs:LL:CC +note: inside `main` + --> $DIR/ptr_metadata_uninit_slice_data.rs:LL:CC + | +LL | let _meta = deref_meta(p.as_ptr().cast()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs new file mode 100644 index 000000000000..a2ffdc92c4e2 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs @@ -0,0 +1,24 @@ +//@compile-flags: -Zmiri-disable-validation +#![feature(core_intrinsics, custom_mir)] +use std::intrinsics::mir::*; + +// This disables validation and uses custom MIR hit exactly the UB in the intrinsic, +// rather than getting UB from the typed load or parameter passing. + +#[custom_mir(dialect = "runtime")] +pub unsafe fn deref_meta(p: *const *const [i32]) -> usize { + mir! { + { + RET = PtrMetadata(*p); //~ ERROR: Undefined Behavior: using uninitialized data + Return() + } + } +} + +fn main() { + let mut p = std::mem::MaybeUninit::<*const [i32]>::uninit(); + unsafe { + (*p.as_mut_ptr().cast::<[*const i32; 2]>())[0] = 4 as *const i32; + let _meta = deref_meta(p.as_ptr().cast()); + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr new file mode 100644 index 000000000000..4e2e72184323 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr @@ -0,0 +1,35 @@ +warning: integer-to-pointer cast + --> $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC + | +LL | (*p.as_mut_ptr().cast::<[*const i32; 2]>())[0] = 4 as *const i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast + | + = help: This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, + = help: which means that Miri might miss pointer bugs in this program. + = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation. + = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead. + = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `with_exposed_provenance` semantics. + = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning. + = note: BACKTRACE: + = note: inside `main` at $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC + +error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory + --> $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC + | +LL | RET = PtrMetadata(*p); + | ^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `deref_meta` at $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC +note: inside `main` + --> $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC + | +LL | let _meta = deref_meta(p.as_ptr().cast()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs new file mode 100644 index 000000000000..e5a51289a8ae --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs @@ -0,0 +1,25 @@ +//@compile-flags: -Zmiri-disable-validation +#![feature(core_intrinsics, custom_mir)] +use std::intrinsics::mir::*; + +// This disables validation and uses custom MIR hit exactly the UB in the intrinsic, +// rather than getting UB from the typed load or parameter passing. + +#[custom_mir(dialect = "runtime")] +pub unsafe fn deref_meta(p: *const *const i32) -> () { + mir! { + { + RET = PtrMetadata(*p); //~ ERROR: Undefined Behavior: using uninitialized data + Return() + } + } +} + +fn main() { + // Even though the meta is the trivially-valid `()`, this is still UB + + let p = std::mem::MaybeUninit::<*const i32>::uninit(); + unsafe { + let _meta = deref_meta(p.as_ptr()); + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr new file mode 100644 index 000000000000..0e218de0eeb5 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory + --> $DIR/ptr_metadata_uninit_thin.rs:LL:CC + | +LL | RET = PtrMetadata(*p); + | ^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `deref_meta` at $DIR/ptr_metadata_uninit_thin.rs:LL:CC +note: inside `main` + --> $DIR/ptr_metadata_uninit_thin.rs:LL:CC + | +LL | let _meta = deref_meta(p.as_ptr()); + | ^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs deleted file mode 100644 index e2329c131398..000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@compile-flags: -Zmiri-permissive-provenance - -#[rustfmt::skip] // fails with "left behind trailing whitespace" -fn main() { - let x = 0 as *mut i32; - let _x = x.wrapping_offset(8); // ok, this has no inbounds tag - let _x = unsafe { x.offset(0) }; // UB despite offset 0, NULL is never inbounds - //~^ERROR: null pointer is a dangling pointer -} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr deleted file mode 100644 index a8984c7fa167..000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance) - --> $DIR/ptr_offset_0_plus_0.rs:LL:CC - | -LL | let _x = unsafe { x.offset(0) }; // UB despite offset 0, NULL is never inbounds - | ^^^^^^^^^^^ out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance) - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/ptr_offset_0_plus_0.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs deleted file mode 100644 index 0e5acf08b203..000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() { - let start_ptr = &4 as *const _ as *const u8; - let length = 10; - let end_ptr = start_ptr.wrapping_add(length); - // Even if the offset is 0, a dangling OOB pointer is not allowed. - unsafe { end_ptr.offset_from(end_ptr) }; //~ERROR: pointer at offset 10 is out-of-bounds -} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr deleted file mode 100644 index 32a4461d6bf0..000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: out-of-bounds `offset_from`: ALLOC has size 4, so pointer at offset 10 is out-of-bounds - --> $DIR/ptr_offset_from_oob.rs:LL:CC - | -LL | unsafe { end_ptr.offset_from(end_ptr) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC has size 4, so pointer at offset 10 is out-of-bounds - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/ptr_offset_from_oob.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs deleted file mode 100644 index 575e28854b1a..000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[rustfmt::skip] // fails with "left behind trailing whitespace" -fn main() { - let x = Box::into_raw(Box::new(0u32)); - let x = x.wrapping_offset(8); // ok, this has no inbounds tag - let _x = unsafe { x.offset(0) }; // UB despite offset 0, the pointer is not inbounds of the only object it can point to - //~^ERROR: pointer at offset 32 is out-of-bounds -} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr deleted file mode 100644 index 304d362bbb9f..000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer at offset 32 is out-of-bounds - --> $DIR/ptr_offset_ptr_plus_0.rs:LL:CC - | -LL | let _x = unsafe { x.offset(0) }; // UB despite offset 0, the pointer is not inbounds of the only object it can point to - | ^^^^^^^^^^^ out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer at offset 32 is out-of-bounds - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information -help: ALLOC was allocated here: - --> $DIR/ptr_offset_ptr_plus_0.rs:LL:CC - | -LL | let x = Box::into_raw(Box::new(0u32)); - | ^^^^^^^^^^^^^^ - = note: BACKTRACE (of the first span): - = note: inside `main` at $DIR/ptr_offset_ptr_plus_0.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.rs b/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.rs index 8a49c8403ae3..12aa7c10af4e 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.rs +++ b/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.rs @@ -10,6 +10,6 @@ fn main() { unsafe { let x = i32x2(1, 1); let y = i32x2(100, 0); - simd_shl(x, y); //~ERROR: overflowing shift by 100 in `simd_shl` in SIMD lane 0 + simd_shl(x, y); //~ERROR: overflowing shift by 100 in `simd_shl` in lane 0 } } diff --git a/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.stderr b/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.stderr index 3a4ec008b449..475067db801e 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.stderr +++ b/src/tools/miri/tests/fail/intrinsics/simd-shl-too-far.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: overflowing shift by 100 in `simd_shl` in SIMD lane 0 +error: Undefined Behavior: overflowing shift by 100 in `simd_shl` in lane 0 --> $DIR/simd-shl-too-far.rs:LL:CC | LL | simd_shl(x, y); - | ^^^^^^^^^^^^^^ overflowing shift by 100 in `simd_shl` in SIMD lane 0 + | ^^^^^^^^^^^^^^ overflowing shift by 100 in `simd_shl` in lane 0 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.rs b/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.rs index 433998cbde66..ada7cf408c4e 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.rs +++ b/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.rs @@ -10,6 +10,6 @@ fn main() { unsafe { let x = i32x2(1, 1); let y = i32x2(20, 40); - simd_shr(x, y); //~ERROR: overflowing shift by 40 in `simd_shr` in SIMD lane 1 + simd_shr(x, y); //~ERROR: overflowing shift by 40 in `simd_shr` in lane 1 } } diff --git a/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.stderr b/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.stderr index 07636b8c0bbd..0d6307837deb 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.stderr +++ b/src/tools/miri/tests/fail/intrinsics/simd-shr-too-far.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: overflowing shift by 40 in `simd_shr` in SIMD lane 1 +error: Undefined Behavior: overflowing shift by 40 in `simd_shr` in lane 1 --> $DIR/simd-shr-too-far.rs:LL:CC | LL | simd_shr(x, y); - | ^^^^^^^^^^^^^^ overflowing shift by 40 in `simd_shr` in SIMD lane 1 + | ^^^^^^^^^^^^^^ overflowing shift by 40 in `simd_shr` in lane 1 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_add1.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_add1.rs index 3f8b4e55151e..0ed758da2a3c 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_add1.rs +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_add1.rs @@ -1,4 +1,4 @@ fn main() { // MAX overflow - let _val = unsafe { 40000u16.unchecked_add(30000) }; //~ ERROR: overflow executing `unchecked_add` + let _val = unsafe { 40000u16.unchecked_add(30000) }; //~ ERROR: arithmetic overflow in `unchecked_add` } diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_add1.stderr b/src/tools/miri/tests/fail/intrinsics/unchecked_add1.stderr index 922de4226fa8..eae9ec7a44dc 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_add1.stderr +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_add1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: overflow executing `unchecked_add` +error: Undefined Behavior: arithmetic overflow in `unchecked_add` --> $DIR/unchecked_add1.rs:LL:CC | LL | let _val = unsafe { 40000u16.unchecked_add(30000) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_add` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_add` | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_add2.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_add2.rs index 3283dbf8e7ef..3b495203eb41 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_add2.rs +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_add2.rs @@ -1,4 +1,4 @@ fn main() { // MIN overflow - let _val = unsafe { (-30000i16).unchecked_add(-8000) }; //~ ERROR: overflow executing `unchecked_add` + let _val = unsafe { (-30000i16).unchecked_add(-8000) }; //~ ERROR: arithmetic overflow in `unchecked_add` } diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_add2.stderr b/src/tools/miri/tests/fail/intrinsics/unchecked_add2.stderr index 05456eaf1950..6a0dcfcd227e 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_add2.stderr +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_add2.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: overflow executing `unchecked_add` +error: Undefined Behavior: arithmetic overflow in `unchecked_add` --> $DIR/unchecked_add2.rs:LL:CC | LL | let _val = unsafe { (-30000i16).unchecked_add(-8000) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_add` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_add` | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.rs index 2feed7759ec1..3dc83a2dcef2 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.rs +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.rs @@ -1,4 +1,4 @@ fn main() { // MAX overflow - let _val = unsafe { 300u16.unchecked_mul(250u16) }; //~ ERROR: overflow executing `unchecked_mul` + let _val = unsafe { 300u16.unchecked_mul(250u16) }; //~ ERROR: arithmetic overflow in `unchecked_mul` } diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.stderr b/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.stderr index 533beaa65ff7..e37d9827c8cf 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.stderr +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_mul1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: overflow executing `unchecked_mul` +error: Undefined Behavior: arithmetic overflow in `unchecked_mul` --> $DIR/unchecked_mul1.rs:LL:CC | LL | let _val = unsafe { 300u16.unchecked_mul(250u16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_mul` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_mul` | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.rs index 42cd509404a5..49cd293737c7 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.rs +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.rs @@ -1,4 +1,4 @@ fn main() { // MIN overflow - let _val = unsafe { 1_000_000_000i32.unchecked_mul(-4) }; //~ ERROR: overflow executing `unchecked_mul` + let _val = unsafe { 1_000_000_000i32.unchecked_mul(-4) }; //~ ERROR: arithmetic overflow in `unchecked_mul` } diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.stderr b/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.stderr index 4c6bae6f0bf6..949077ce61d8 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.stderr +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_mul2.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: overflow executing `unchecked_mul` +error: Undefined Behavior: arithmetic overflow in `unchecked_mul` --> $DIR/unchecked_mul2.rs:LL:CC | LL | let _val = unsafe { 1_000_000_000i32.unchecked_mul(-4) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_mul` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_mul` | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.rs index e5178bf4effa..f24c97420948 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.rs +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.rs @@ -1,4 +1,4 @@ fn main() { // MIN overflow - let _val = unsafe { 14u32.unchecked_sub(22) }; //~ ERROR: overflow executing `unchecked_sub` + let _val = unsafe { 14u32.unchecked_sub(22) }; //~ ERROR: arithmetic overflow in `unchecked_sub` } diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.stderr b/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.stderr index 72187e20e23c..39bfd8a23840 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.stderr +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_sub1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: overflow executing `unchecked_sub` +error: Undefined Behavior: arithmetic overflow in `unchecked_sub` --> $DIR/unchecked_sub1.rs:LL:CC | LL | let _val = unsafe { 14u32.unchecked_sub(22) }; - | ^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_sub` + | ^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_sub` | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.rs index ac9fd1e5d062..74b08b1dfb4e 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.rs +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.rs @@ -1,4 +1,4 @@ fn main() { // MAX overflow - let _val = unsafe { 30000i16.unchecked_sub(-7000) }; //~ ERROR: overflow executing `unchecked_sub` + let _val = unsafe { 30000i16.unchecked_sub(-7000) }; //~ ERROR: arithmetic overflow in `unchecked_sub` } diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.stderr b/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.stderr index ec28384f6336..604eba99e014 100644 --- a/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.stderr +++ b/src/tools/miri/tests/fail/intrinsics/unchecked_sub2.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: overflow executing `unchecked_sub` +error: Undefined Behavior: arithmetic overflow in `unchecked_sub` --> $DIR/unchecked_sub2.rs:LL:CC | LL | let _val = unsafe { 30000i16.unchecked_sub(-7000) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_sub` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_sub` | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr index 447f7cae6ce9..8dd76edafea2 100644 --- a/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr +++ b/src/tools/miri/tests/fail/intrinsics/uninit_uninhabited_type.stderr @@ -13,7 +13,7 @@ LL | ABORT(); = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs b/src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs deleted file mode 100644 index 2f46c820fb73..000000000000 --- a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![feature(intrinsics)] - -// Directly call intrinsic to avoid debug assertions in libstd -extern "rust-intrinsic" { - fn write_bytes(dst: *mut T, val: u8, count: usize); -} - -fn main() { - unsafe { write_bytes::(std::ptr::null_mut(), 0, 0) }; //~ ERROR: memory access failed: null pointer is a dangling pointer -} diff --git a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr b/src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr deleted file mode 100644 index def180935cc3..000000000000 --- a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) - --> $DIR/write_bytes_null.rs:LL:CC - | -LL | unsafe { write_bytes::(std::ptr::null_mut(), 0, 0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/write_bytes_null.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr index bae34149807f..55f66a275b6b 100644 --- a/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr +++ b/src/tools/miri/tests/fail/intrinsics/zero_fn_ptr.stderr @@ -13,7 +13,7 @@ LL | ABORT(); = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail/issue-miri-2432.rs b/src/tools/miri/tests/fail/issue-miri-2432.rs deleted file mode 100644 index f822479c4368..000000000000 --- a/src/tools/miri/tests/fail/issue-miri-2432.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![allow(where_clauses_object_safety)] - -trait Trait {} - -trait X { - fn foo(&self) - where - Self: Trait; -} - -impl X for () { - fn foo(&self) {} -} - -impl Trait for dyn X {} - -pub fn main() { - ::foo(&()); //~ERROR: trying to call something that is not a method -} diff --git a/src/tools/miri/tests/fail/panic/double_panic.stderr b/src/tools/miri/tests/fail/panic/double_panic.stderr index 3b7a1511fa0b..5829c1897bba 100644 --- a/src/tools/miri/tests/fail/panic/double_panic.stderr +++ b/src/tools/miri/tests/fail/panic/double_panic.stderr @@ -18,7 +18,7 @@ LL | ABORT(); = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `core::panicking::panic_nounwind_nobacktrace` at RUSTLIB/core/src/panicking.rs:LL:CC = note: inside `core::panicking::panic_in_cleanup` at RUSTLIB/core/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/fail/panic/panic_abort1.stderr b/src/tools/miri/tests/fail/panic/panic_abort1.stderr index 7694cc70b227..d4abf19cd1e5 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort1.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort1.stderr @@ -14,7 +14,7 @@ LL | ABORT(); = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC note: inside `main` --> $DIR/panic_abort1.rs:LL:CC diff --git a/src/tools/miri/tests/fail/panic/panic_abort2.stderr b/src/tools/miri/tests/fail/panic/panic_abort2.stderr index e6a4380ea518..507f17abf4e7 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort2.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort2.stderr @@ -14,7 +14,7 @@ LL | ABORT(); = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC note: inside `main` --> $DIR/panic_abort2.rs:LL:CC diff --git a/src/tools/miri/tests/fail/panic/panic_abort3.stderr b/src/tools/miri/tests/fail/panic/panic_abort3.stderr index 23e2021eeef8..a5d8b4d2eebd 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort3.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort3.stderr @@ -14,7 +14,7 @@ LL | ABORT(); = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC note: inside `main` --> $DIR/panic_abort3.rs:LL:CC diff --git a/src/tools/miri/tests/fail/panic/panic_abort4.stderr b/src/tools/miri/tests/fail/panic/panic_abort4.stderr index 20a0ddb90192..62fbbf942cb0 100644 --- a/src/tools/miri/tests/fail/panic/panic_abort4.stderr +++ b/src/tools/miri/tests/fail/panic/panic_abort4.stderr @@ -14,7 +14,7 @@ LL | ABORT(); = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC note: inside `main` --> $DIR/panic_abort4.rs:LL:CC diff --git a/src/tools/miri/tests/fail/reading_half_a_pointer.rs b/src/tools/miri/tests/fail/reading_half_a_pointer.rs index 7dd98eab7852..feac30b83c30 100644 --- a/src/tools/miri/tests/fail/reading_half_a_pointer.rs +++ b/src/tools/miri/tests/fail/reading_half_a_pointer.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] // We use packed structs to get around alignment restrictions -#[repr(packed)] +#[repr(C, packed)] struct Data { pad: u8, ptr: &'static i32, diff --git a/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs b/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs index 97a70103e646..a20539ee7c70 100644 --- a/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs +++ b/src/tools/miri/tests/fail/shims/backtrace/bad-backtrace-decl.rs @@ -5,7 +5,7 @@ extern "Rust" { fn main() { let frames = unsafe { miri_get_backtrace(0) }; - for frame in frames.into_iter() { + for frame in frames.iter() { unsafe { miri_resolve_frame(*frame, 0); //~ ERROR: Undefined Behavior: bad declaration of miri_resolve_frame - should return a struct with 5 fields } diff --git a/src/tools/miri/tests/fail/storage-live-dead-var.rs b/src/tools/miri/tests/fail/storage-live-dead-var.rs new file mode 100644 index 000000000000..83ab98d79d1a --- /dev/null +++ b/src/tools/miri/tests/fail/storage-live-dead-var.rs @@ -0,0 +1,14 @@ +#![feature(core_intrinsics, custom_mir)] +use std::intrinsics::mir::*; + +#[custom_mir(dialect = "runtime")] +fn main() { + mir! { + let val: i32; + { + val = 42; //~ERROR: accessing a dead local variable + StorageLive(val); // too late... (but needs to be here to make `val` not implicitly live) + Return() + } + } +} diff --git a/src/tools/miri/tests/fail/issue-miri-2432.stderr b/src/tools/miri/tests/fail/storage-live-dead-var.stderr similarity index 57% rename from src/tools/miri/tests/fail/issue-miri-2432.stderr rename to src/tools/miri/tests/fail/storage-live-dead-var.stderr index 3befe31dc5a0..ccc77b1c978d 100644 --- a/src/tools/miri/tests/fail/issue-miri-2432.stderr +++ b/src/tools/miri/tests/fail/storage-live-dead-var.stderr @@ -1,13 +1,13 @@ -error: Undefined Behavior: `dyn` call trying to call something that is not a method - --> $DIR/issue-miri-2432.rs:LL:CC +error: Undefined Behavior: accessing a dead local variable + --> $DIR/storage-live-dead-var.rs:LL:CC | -LL | ::foo(&()); - | ^^^^^^^^^^^^^^^^^^^^^^ `dyn` call trying to call something that is not a method +LL | val = 42; + | ^^^^^^^^ accessing a dead local variable | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: - = note: inside `main` at $DIR/issue-miri-2432.rs:LL:CC + = note: inside `main` at $DIR/storage-live-dead-var.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/storage-live-resets-var.rs b/src/tools/miri/tests/fail/storage-live-resets-var.rs new file mode 100644 index 000000000000..bfdd9e78943e --- /dev/null +++ b/src/tools/miri/tests/fail/storage-live-resets-var.rs @@ -0,0 +1,17 @@ +#![feature(core_intrinsics, custom_mir)] +use std::intrinsics::mir::*; + +#[custom_mir(dialect = "runtime")] +fn main() { + mir! { + let val: i32; + let _val2: i32; + { + StorageLive(val); + val = 42; + StorageLive(val); // reset val to `uninit` + _val2 = val; //~ERROR: uninitialized + Return() + } + } +} diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr b/src/tools/miri/tests/fail/storage-live-resets-var.stderr similarity index 51% rename from src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr rename to src/tools/miri/tests/fail/storage-live-resets-var.stderr index e4e23e8ace1c..07d39cc9d6b3 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr +++ b/src/tools/miri/tests/fail/storage-live-resets-var.stderr @@ -1,13 +1,13 @@ -error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds - --> $DIR/maybe_null_pointer_write_zst.rs:LL:CC +error: Undefined Behavior: constructing invalid value: encountered uninitialized memory, but expected an integer + --> $DIR/storage-live-resets-var.rs:LL:CC | -LL | unsafe { *ptr = zst_val }; - | ^^^^^^^^^^^^^^ memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds +LL | _val2 = val; + | ^^^^^^^^^^^ constructing invalid value: encountered uninitialized memory, but expected an integer | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: - = note: inside `main` at $DIR/maybe_null_pointer_write_zst.rs:LL:CC + = note: inside `main` at $DIR/storage-live-resets-var.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/terminate-terminator.stderr b/src/tools/miri/tests/fail/terminate-terminator.stderr index f737adc561d9..a5fa0b3e07a6 100644 --- a/src/tools/miri/tests/fail/terminate-terminator.stderr +++ b/src/tools/miri/tests/fail/terminate-terminator.stderr @@ -18,7 +18,7 @@ LL | ABORT(); = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC = note: inside `core::panicking::panic_cannot_unwind` at RUSTLIB/core/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment2.rs b/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment2.rs index 8459c64ed2d3..06dd97deedae 100644 --- a/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment2.rs +++ b/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment2.rs @@ -7,7 +7,7 @@ pub struct Aligned { _pad: [u8; 11], packed: Packed, } -#[repr(packed)] +#[repr(C, packed)] #[derive(Default, Copy, Clone)] pub struct Packed { _pad: [u8; 5], diff --git a/src/tools/miri/tests/fail/unwind-action-terminate.stderr b/src/tools/miri/tests/fail/unwind-action-terminate.stderr index 7e722f7be328..547d550d3d0d 100644 --- a/src/tools/miri/tests/fail/unwind-action-terminate.stderr +++ b/src/tools/miri/tests/fail/unwind-action-terminate.stderr @@ -16,7 +16,7 @@ LL | ABORT(); = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC = note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::begin_panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC = note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `core::panicking::panic_nounwind` at RUSTLIB/core/src/panicking.rs:LL:CC = note: inside `core::panicking::panic_cannot_unwind` at RUSTLIB/core/src/panicking.rs:LL:CC diff --git a/src/tools/miri/tests/fail/validity/invalid_char_cast.rs b/src/tools/miri/tests/fail/validity/invalid_char_cast.rs new file mode 100644 index 000000000000..6a590dc7ba10 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/invalid_char_cast.rs @@ -0,0 +1,21 @@ +// Make sure we find these even with many checks disabled. +//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation +#![feature(core_intrinsics)] +#![feature(custom_mir)] + +use std::intrinsics::mir::*; + +#[custom_mir(dialect = "runtime", phase = "optimized")] +fn cast(ptr: *const char) -> u32 { + mir! { + { + RET = *ptr as u32; //~ERROR: interpreting an invalid 32-bit value as a char + Return() + } + } +} + +pub fn main() { + let v = u32::MAX; + cast(&v as *const u32 as *const char); +} diff --git a/src/tools/miri/tests/fail/validity/invalid_char_cast.stderr b/src/tools/miri/tests/fail/validity/invalid_char_cast.stderr new file mode 100644 index 000000000000..1b5c838cf463 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/invalid_char_cast.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: interpreting an invalid 32-bit value as a char: $HEX + --> $DIR/invalid_char_cast.rs:LL:CC + | +LL | RET = *ptr as u32; + | ^^^^^^^^^^^^^^^^^ interpreting an invalid 32-bit value as a char: $HEX + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `cast` at $DIR/invalid_char_cast.rs:LL:CC +note: inside `main` + --> $DIR/invalid_char_cast.rs:LL:CC + | +LL | cast(&v as *const u32 as *const char); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/invalid_char_match.rs b/src/tools/miri/tests/fail/validity/invalid_char_match.rs new file mode 100644 index 000000000000..6c2e65b2bb74 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/invalid_char_match.rs @@ -0,0 +1,26 @@ +// Make sure we find these even with many checks disabled. +//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation +#![feature(core_intrinsics)] +#![feature(custom_mir)] + +use std::intrinsics::mir::*; + +#[custom_mir(dialect = "runtime", phase = "optimized")] +fn switch_int(ptr: *const char) { + mir! { + { + match *ptr { //~ERROR: interpreting an invalid 32-bit value as a char + '0' => ret, + _ => ret, + } + } + ret = { + Return() + } + } +} + +pub fn main() { + let v = u32::MAX; + switch_int(&v as *const u32 as *const char); +} diff --git a/src/tools/miri/tests/fail/validity/invalid_char_match.stderr b/src/tools/miri/tests/fail/validity/invalid_char_match.stderr new file mode 100644 index 000000000000..7706ed97316c --- /dev/null +++ b/src/tools/miri/tests/fail/validity/invalid_char_match.stderr @@ -0,0 +1,23 @@ +error: Undefined Behavior: interpreting an invalid 32-bit value as a char: $HEX + --> $DIR/invalid_char_match.rs:LL:CC + | +LL | / match *ptr { +LL | | '0' => ret, +LL | | _ => ret, +LL | | } + | |_____________^ interpreting an invalid 32-bit value as a char: $HEX + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `switch_int` at $DIR/invalid_char_match.rs:LL:CC +note: inside `main` + --> $DIR/invalid_char_match.rs:LL:CC + | +LL | switch_int(&v as *const u32 as *const char); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/invalid_enum_cast.rs b/src/tools/miri/tests/fail/validity/invalid_enum_cast.rs new file mode 100644 index 000000000000..ed451a435b95 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/invalid_enum_cast.rs @@ -0,0 +1,21 @@ +// Make sure we find these even with many checks disabled. +//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation + +#[derive(Copy, Clone)] +#[allow(unused)] +enum E { + A, + B, + C, +} + +fn cast(ptr: *const E) { + unsafe { + let _val = *ptr as u32; //~ERROR: enum value has invalid tag + } +} + +pub fn main() { + let v = u32::MAX; + cast(&v as *const u32 as *const E); +} diff --git a/src/tools/miri/tests/fail/validity/invalid_enum_cast.stderr b/src/tools/miri/tests/fail/validity/invalid_enum_cast.stderr new file mode 100644 index 000000000000..30afb5e8087b --- /dev/null +++ b/src/tools/miri/tests/fail/validity/invalid_enum_cast.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: enum value has invalid tag: 0xff + --> $DIR/invalid_enum_cast.rs:LL:CC + | +LL | let _val = *ptr as u32; + | ^^^^^^^^^^^ enum value has invalid tag: 0xff + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `cast` at $DIR/invalid_enum_cast.rs:LL:CC +note: inside `main` + --> $DIR/invalid_enum_cast.rs:LL:CC + | +LL | cast(&v as *const u32 as *const E); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/zst2.rs b/src/tools/miri/tests/fail/zst2.rs deleted file mode 100644 index 04218c264a3e..000000000000 --- a/src/tools/miri/tests/fail/zst2.rs +++ /dev/null @@ -1,12 +0,0 @@ -fn main() { - // Not using the () type here, as writes of that type do not even have MIR generated. - // Also not assigning directly as that's array initialization, not assignment. - let zst_val = [1u8; 0]; - - // make sure ZST accesses are checked against being "truly" dangling pointers - // (into deallocated allocations). - let mut x_box = Box::new(1u8); - let x = &mut *x_box as *mut _ as *mut [u8; 0]; - drop(x_box); - unsafe { *x = zst_val }; //~ ERROR: has been freed -} diff --git a/src/tools/miri/tests/fail/zst3.rs b/src/tools/miri/tests/fail/zst3.rs deleted file mode 100644 index 454bef25f223..000000000000 --- a/src/tools/miri/tests/fail/zst3.rs +++ /dev/null @@ -1,15 +0,0 @@ -fn main() { - // Not using the () type here, as writes of that type do not even have MIR generated. - // Also not assigning directly as that's array initialization, not assignment. - let zst_val = [1u8; 0]; - - // make sure ZST accesses are checked against being "truly" dangling pointers - // (that are out-of-bounds). - let mut x_box = Box::new(1u8); - let x = (&mut *x_box as *mut u8).wrapping_offset(1); - // This one is just "at the edge", but still okay - unsafe { *(x as *mut [u8; 0]) = zst_val }; - // One byte further is OOB. - let x = x.wrapping_offset(1); - unsafe { *(x as *mut [u8; 0]) = zst_val }; //~ ERROR: out-of-bounds -} diff --git a/src/tools/miri/tests/fail/zst3.stderr b/src/tools/miri/tests/fail/zst3.stderr deleted file mode 100644 index b9495fbd2074..000000000000 --- a/src/tools/miri/tests/fail/zst3.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset 2 is out-of-bounds - --> $DIR/zst3.rs:LL:CC - | -LL | unsafe { *(x as *mut [u8; 0]) = zst_val }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has size 1, so pointer at offset 2 is out-of-bounds - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information -help: ALLOC was allocated here: - --> $DIR/zst3.rs:LL:CC - | -LL | let mut x_box = Box::new(1u8); - | ^^^^^^^^^^^^^ - = note: BACKTRACE (of the first span): - = note: inside `main` at $DIR/zst3.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/zst1.rs b/src/tools/miri/tests/fail/zst_local_oob.rs similarity index 100% rename from src/tools/miri/tests/fail/zst1.rs rename to src/tools/miri/tests/fail/zst_local_oob.rs diff --git a/src/tools/miri/tests/fail/zst1.stderr b/src/tools/miri/tests/fail/zst_local_oob.stderr similarity index 88% rename from src/tools/miri/tests/fail/zst1.stderr rename to src/tools/miri/tests/fail/zst_local_oob.stderr index cda837da7e71..ba1ccaa0a3c8 100644 --- a/src/tools/miri/tests/fail/zst1.stderr +++ b/src/tools/miri/tests/fail/zst_local_oob.stderr @@ -1,5 +1,5 @@ error: Undefined Behavior: memory access failed: ALLOC has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds - --> $DIR/zst1.rs:LL:CC + --> $DIR/zst_local_oob.rs:LL:CC | LL | let _val = unsafe { *x }; | ^^ memory access failed: ALLOC has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds @@ -7,7 +7,7 @@ LL | let _val = unsafe { *x }; = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: - = note: inside `main` at $DIR/zst1.rs:LL:CC + = note: inside `main` at $DIR/zst_local_oob.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/panic/mir-validation.stderr b/src/tools/miri/tests/panic/mir-validation.stderr index d158c996dc3d..534e2d5881ff 100644 --- a/src/tools/miri/tests/panic/mir-validation.stderr +++ b/src/tools/miri/tests/panic/mir-validation.stderr @@ -1,6 +1,6 @@ -thread 'rustc' panicked at compiler/rustc_const_eval/src/transform/validate.rs:LL:CC: +thread 'rustc' panicked at compiler/rustc_mir_transform/src/validate.rs:LL:CC: broken MIR in Item(DefId) (after phase change to runtime-optimized) at bb0[1]: -(*(_2.0: *mut i32)), has deref at the wrong place +place (*(_2.0: *mut i32)) has deref as a later projection (it is only permitted as the first projection) stack backtrace: error: the compiler unexpectedly panicked. this is a bug. diff --git a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs new file mode 100644 index 000000000000..a3567eeb7cb4 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs @@ -0,0 +1,108 @@ +//@only-target-linux +// test_race depends on a deterministic schedule. +//@compile-flags: -Zmiri-preemption-rate=0 + +use std::thread; + +fn main() { + test_read_write(); + test_race(); +} + +fn read_bytes(fd: i32, buf: &mut [u8; N]) -> i32 { + let res: i32 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), N).try_into().unwrap() }; + return res; +} + +fn write_bytes(fd: i32, data: [u8; N]) -> i32 { + let res: i32 = + unsafe { libc::write(fd, data.as_ptr() as *const libc::c_void, N).try_into().unwrap() }; + return res; +} + +fn test_read_write() { + let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; + let fd = unsafe { libc::eventfd(0, flags) }; + let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); + // Write 1 to the counter. + let res = write_bytes(fd, sized_8_data); + assert_eq!(res, 8); + + // Read 1 from the counter. + let mut buf: [u8; 8] = [0; 8]; + let res = read_bytes(fd, &mut buf); + // Read returns number of bytes has been read, which is always 8. + assert_eq!(res, 8); + // Check the value of counter read. + let counter = u64::from_ne_bytes(buf); + assert_eq!(counter, 1); + + // After read, the counter is currently 0, read counter 0 should fail with return + // value -1. + let mut buf: [u8; 8] = [0; 8]; + let res = read_bytes(fd, &mut buf); + let e = std::io::Error::last_os_error(); + assert_eq!(e.raw_os_error(), Some(libc::EAGAIN)); + assert_eq!(res, -1); + + // Write with supplied buffer bigger than 8 bytes should be allowed. + let sized_9_data: [u8; 9]; + if cfg!(target_endian = "big") { + // Adjust the data based on the endianness of host system. + sized_9_data = [0, 0, 0, 0, 0, 0, 0, 1, 0]; + } else { + sized_9_data = [1, 0, 0, 0, 0, 0, 0, 0, 0]; + } + let res = write_bytes(fd, sized_9_data); + assert_eq!(res, 8); + + // Read with supplied buffer smaller than 8 bytes should fail with return + // value -1. + let mut buf: [u8; 7] = [1; 7]; + let res = read_bytes(fd, &mut buf); + let e = std::io::Error::last_os_error(); + assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); + assert_eq!(res, -1); + + // Write with supplied buffer smaller than 8 bytes should fail with return + // value -1. + let size_7_data: [u8; 7] = [1; 7]; + let res = write_bytes(fd, size_7_data); + let e = std::io::Error::last_os_error(); + assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); + assert_eq!(res, -1); + + // Read with supplied buffer bigger than 8 bytes should be allowed. + let mut buf: [u8; 9] = [1; 9]; + let res = read_bytes(fd, &mut buf); + assert_eq!(res, 8); + + // Write u64::MAX should fail. + let u64_max_bytes: [u8; 8] = [255; 8]; + let res = write_bytes(fd, u64_max_bytes); + let e = std::io::Error::last_os_error(); + assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); + assert_eq!(res, -1); +} + +fn test_race() { + static mut VAL: u8 = 0; + let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; + let fd = unsafe { libc::eventfd(0, flags) }; + let thread1 = thread::spawn(move || { + let mut buf: [u8; 8] = [0; 8]; + let res = read_bytes(fd, &mut buf); + // read returns number of bytes has been read, which is always 8. + assert_eq!(res, 8); + let counter = u64::from_ne_bytes(buf); + assert_eq!(counter, 1); + unsafe { assert_eq!(VAL, 1) }; + }); + unsafe { VAL = 1 }; + let data: [u8; 8] = 1_u64.to_ne_bytes(); + let res = write_bytes(fd, data); + // write returns number of bytes written, which is always 8. + assert_eq!(res, 8); + thread::yield_now(); + thread1.join().unwrap(); +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs index 5df3ace7496f..aa383a99bc28 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs @@ -148,54 +148,55 @@ fn test_calloc() { #[cfg(not(target_os = "windows"))] fn test_memalign() { - // A normal allocation. - unsafe { - let mut ptr: *mut libc::c_void = ptr::null_mut(); - let align = 8; - let size = 64; - assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); - assert!(!ptr.is_null()); - assert!(ptr.is_aligned_to(align)); - ptr.cast::().write_bytes(1, size); - libc::free(ptr); - } + for _ in 0..16 { + // A normal allocation. + unsafe { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 8; + let size = 64; + assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); + assert!(!ptr.is_null()); + assert!(ptr.is_aligned_to(align)); + ptr.cast::().write_bytes(1, size); + libc::free(ptr); + } - // Align > size. - unsafe { - let mut ptr: *mut libc::c_void = ptr::null_mut(); - let align = 64; - let size = 8; - assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); - assert!(!ptr.is_null()); - assert!(ptr.is_aligned_to(align)); - ptr.cast::().write_bytes(1, size); - libc::free(ptr); - } + // Align > size. + unsafe { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 64; + let size = 8; + assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); + assert!(!ptr.is_null()); + assert!(ptr.is_aligned_to(align)); + ptr.cast::().write_bytes(1, size); + libc::free(ptr); + } - // Size not multiple of align - unsafe { - let mut ptr: *mut libc::c_void = ptr::null_mut(); - let align = 16; - let size = 31; - assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); - assert!(!ptr.is_null()); - assert!(ptr.is_aligned_to(align)); - ptr.cast::().write_bytes(1, size); - libc::free(ptr); - } + // Size not multiple of align + unsafe { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 16; + let size = 31; + assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); + assert!(!ptr.is_null()); + assert!(ptr.is_aligned_to(align)); + ptr.cast::().write_bytes(1, size); + libc::free(ptr); + } - // Size == 0 - unsafe { - let mut ptr: *mut libc::c_void = ptr::null_mut(); - let align = 64; - let size = 0; - assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); - // We are not required to return null if size == 0, but we currently do. - // It's fine to remove this assert if we start returning non-null pointers. - assert!(ptr.is_null()); - assert!(ptr.is_aligned_to(align)); - // Regardless of what we return, it must be `free`able. - libc::free(ptr); + // Size == 0 + unsafe { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 64; + let size = 0; + assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); + // Non-null pointer is returned if size == 0. + // (This is not a guarantee, it just reflects our current behavior.) + assert!(!ptr.is_null()); + assert!(ptr.is_aligned_to(align)); + libc::free(ptr); + } } // Non-power of 2 align @@ -227,7 +228,8 @@ fn test_memalign() { target_os = "windows", target_os = "macos", target_os = "illumos", - target_os = "solaris" + target_os = "solaris", + target_os = "wasi", )))] fn test_reallocarray() { unsafe { @@ -241,6 +243,44 @@ fn test_reallocarray() { } } +#[cfg(not(target_os = "windows"))] +fn test_aligned_alloc() { + // libc doesn't have this function (https://github.com/rust-lang/libc/issues/3689), + // so we declare it ourselves. + extern "C" { + fn aligned_alloc(alignment: libc::size_t, size: libc::size_t) -> *mut libc::c_void; + } + // size not a multiple of the alignment + unsafe { + let p = aligned_alloc(16, 3); + assert_eq!(p, ptr::null_mut()); + } + + // alignment not power of 2 + unsafe { + let p = aligned_alloc(63, 8); + assert_eq!(p, ptr::null_mut()); + } + + // repeated tests on correct alignment/size + for _ in 0..16 { + // alignment 1, size 4 should succeed and actually must align to 4 (because C says so...) + unsafe { + let p = aligned_alloc(1, 4); + assert!(!p.is_null()); + assert!(p.is_aligned_to(4)); + libc::free(p); + } + + unsafe { + let p = aligned_alloc(64, 64); + assert!(!p.is_null()); + assert!(p.is_aligned_to(64)); + libc::free(p); + } + } +} + fn main() { test_malloc(); test_calloc(); @@ -250,9 +290,12 @@ fn main() { target_os = "windows", target_os = "macos", target_os = "illumos", - target_os = "solaris" + target_os = "solaris", + target_os = "wasi", )))] test_reallocarray(); + #[cfg(not(target_os = "windows"))] + test_aligned_alloc(); test_memcpy(); test_strcpy(); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs index 736e0bf8eb7f..f7e1d9faa6a2 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs @@ -10,9 +10,11 @@ use std::mem::transmute; fn test_thread_local_errno() { #[cfg(any(target_os = "illumos", target_os = "solaris"))] use libc::___errno as __errno_location; + #[cfg(target_os = "android")] + use libc::__errno as __errno_location; #[cfg(target_os = "linux")] use libc::__errno_location; - #[cfg(any(target_os = "macos", target_os = "freebsd"))] + #[cfg(any(target_os = "freebsd", target_os = "macos"))] use libc::__error as __errno_location; unsafe { @@ -28,6 +30,21 @@ fn test_thread_local_errno() { } } +fn test_environ() { + // Just a smoke test for now, checking that the extern static exists. + extern "C" { + static mut environ: *const *const libc::c_char; + } + + unsafe { + let mut e = environ; + // Iterate to the end. + while !(*e).is_null() { + e = e.add(1); + } + } +} + #[cfg(target_os = "linux")] fn test_sigrt() { let min = libc::SIGRTMIN(); @@ -60,6 +77,7 @@ fn test_dlsym() { fn main() { test_thread_local_errno(); + test_environ(); test_dlsym(); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs new file mode 100644 index 000000000000..324c0127ee97 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs @@ -0,0 +1,124 @@ +//@ignore-target-windows: No libc socketpair on Windows +// test_race depends on a deterministic schedule. +//@compile-flags: -Zmiri-preemption-rate=0 +use std::thread; +fn main() { + test_socketpair(); + test_socketpair_threaded(); + test_race(); +} + +fn test_socketpair() { + let mut fds = [-1, -1]; + let mut res = + unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; + assert_eq!(res, 0); + + // Read size == data available in buffer. + let data = "abcde".as_bytes().as_ptr(); + res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5).try_into().unwrap() }; + assert_eq!(res, 5); + let mut buf: [u8; 5] = [0; 5]; + res = unsafe { + libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap() + }; + assert_eq!(res, 5); + assert_eq!(buf, "abcde".as_bytes()); + + // Read size > data available in buffer. + let data = "abc".as_bytes().as_ptr(); + res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3).try_into().unwrap() }; + assert_eq!(res, 3); + let mut buf2: [u8; 5] = [0; 5]; + res = unsafe { + libc::read(fds[1], buf2.as_mut_ptr().cast(), buf2.len() as libc::size_t).try_into().unwrap() + }; + assert_eq!(res, 3); + assert_eq!(&buf2[0..3], "abc".as_bytes()); + + // Test read and write from another direction. + // Read size == data available in buffer. + let data = "12345".as_bytes().as_ptr(); + res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5).try_into().unwrap() }; + assert_eq!(res, 5); + let mut buf3: [u8; 5] = [0; 5]; + res = unsafe { + libc::read(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t).try_into().unwrap() + }; + assert_eq!(res, 5); + assert_eq!(buf3, "12345".as_bytes()); + + // Read size > data available in buffer. + let data = "123".as_bytes().as_ptr(); + res = unsafe { libc::write(fds[1], data as *const libc::c_void, 3).try_into().unwrap() }; + assert_eq!(res, 3); + let mut buf4: [u8; 5] = [0; 5]; + res = unsafe { + libc::read(fds[0], buf4.as_mut_ptr().cast(), buf4.len() as libc::size_t).try_into().unwrap() + }; + assert_eq!(res, 3); + assert_eq!(&buf4[0..3], "123".as_bytes()); +} + +fn test_socketpair_threaded() { + let mut fds = [-1, -1]; + let mut res = + unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; + assert_eq!(res, 0); + + let data = "abcde".as_bytes().as_ptr(); + res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5).try_into().unwrap() }; + assert_eq!(res, 5); + let thread1 = thread::spawn(move || { + let mut buf: [u8; 5] = [0; 5]; + let res: i64 = unsafe { + libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + .try_into() + .unwrap() + }; + assert_eq!(res, 5); + assert_eq!(buf, "abcde".as_bytes()); + }); + thread1.join().unwrap(); + + // Read and write from different direction + let thread2 = thread::spawn(move || { + let data = "12345".as_bytes().as_ptr(); + let res: i64 = + unsafe { libc::write(fds[0], data as *const libc::c_void, 5).try_into().unwrap() }; + assert_eq!(res, 5); + }); + thread2.join().unwrap(); + let mut buf: [u8; 5] = [0; 5]; + res = unsafe { + libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap() + }; + assert_eq!(res, 5); + assert_eq!(buf, "12345".as_bytes()); +} +fn test_race() { + static mut VAL: u8 = 0; + let mut fds = [-1, -1]; + let mut res = + unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; + assert_eq!(res, 0); + let thread1 = thread::spawn(move || { + let mut buf: [u8; 1] = [0; 1]; + // write() from the main thread will occur before the read() here + // because preemption is disabled and the main thread yields after write(). + let res: i32 = unsafe { + libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + .try_into() + .unwrap() + }; + assert_eq!(res, 1); + assert_eq!(buf, "a".as_bytes()); + unsafe { assert_eq!(VAL, 1) }; + }); + unsafe { VAL = 1 }; + let data = "a".as_bytes().as_ptr(); + res = unsafe { libc::write(fds[0], data as *const libc::c_void, 1).try_into().unwrap() }; + assert_eq!(res, 1); + thread::yield_now(); + thread1.join().unwrap(); +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-time.rs b/src/tools/miri/tests/pass-dep/libc/libc-time.rs index ea1e49a07257..5e4bb73e3688 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-time.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-time.rs @@ -1,6 +1,5 @@ //@ignore-target-windows: no libc time APIs on Windows //@compile-flags: -Zmiri-disable-isolation -use std::ffi::CStr; use std::{env, mem, ptr}; fn main() { @@ -64,7 +63,9 @@ fn test_localtime_r() { tm_wday: 0, tm_yday: 0, tm_isdst: 0, + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] tm_gmtoff: 0, + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] tm_zone: std::ptr::null_mut::(), }; let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) }; @@ -82,7 +83,7 @@ fn test_localtime_r() { assert_eq!(tm.tm_gmtoff, 0); #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] unsafe { - assert_eq!(CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00") + assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00") }; // The returned value is the pointer passed in. diff --git a/src/tools/miri/tests/pass-dep/libc/mmap.rs b/src/tools/miri/tests/pass-dep/libc/mmap.rs index a0787c689077..fd874dbe89e5 100644 --- a/src/tools/miri/tests/pass-dep/libc/mmap.rs +++ b/src/tools/miri/tests/pass-dep/libc/mmap.rs @@ -69,36 +69,6 @@ fn test_mmap( assert_eq!(ptr, libc::MAP_FAILED); assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); - let ptr = unsafe { - mmap( - ptr::without_provenance_mut(page_size * 64), - page_size, - libc::PROT_READ | libc::PROT_WRITE, - // We don't support MAP_FIXED - libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_FIXED, - -1, - Default::default(), - ) - }; - assert_eq!(ptr, libc::MAP_FAILED); - assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOTSUP); - - // We don't support protections other than read+write - for prot in [libc::PROT_NONE, libc::PROT_EXEC, libc::PROT_READ, libc::PROT_WRITE] { - let ptr = unsafe { - mmap( - ptr::null_mut(), - page_size, - prot, - libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, - -1, - Default::default(), - ) - }; - assert_eq!(ptr, libc::MAP_FAILED); - assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOTSUP); - } - // We report an error for mappings whose length cannot be rounded up to a multiple of // the page size. let ptr = unsafe { diff --git a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs index 4c4f542dfd4c..d66cd3bbb034 100644 --- a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs +++ b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs @@ -10,7 +10,7 @@ fn main() { .collect::(); fn set_thread_name(name: &CStr) -> i32 { - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris"))] return unsafe { libc::pthread_setname_np(libc::pthread_self(), name.as_ptr().cast()) }; #[cfg(target_os = "freebsd")] unsafe { diff --git a/src/tools/miri/tests/pass/align_offset_symbolic.rs b/src/tools/miri/tests/pass/align_offset_symbolic.rs index dec3d779a789..9647277821fa 100644 --- a/src/tools/miri/tests/pass/align_offset_symbolic.rs +++ b/src/tools/miri/tests/pass/align_offset_symbolic.rs @@ -118,10 +118,9 @@ fn vtable() { let parts: (*const (), *const u8) = unsafe { mem::transmute(ptr) }; let vtable = parts.1; let offset = vtable.align_offset(mem::align_of::()); - let _vtable_aligned = vtable.wrapping_add(offset) as *const [TWOPTR; 0]; - // FIXME: we can't actually do the access since vtable pointers act like zero-sized allocations. - // Enable the next line once https://github.com/rust-lang/rust/issues/117945 is implemented. - //let _place = unsafe { &*vtable_aligned }; + let vtable_aligned = vtable.wrapping_add(offset) as *const [TWOPTR; 0]; + // Zero-sized deref, so no in-bounds requirement. + let _place = unsafe { &*vtable_aligned }; } fn main() { diff --git a/src/tools/miri/tests/pass/async-drop.rs b/src/tools/miri/tests/pass/async-drop.rs index f16206f3db62..92ecbdd29fdb 100644 --- a/src/tools/miri/tests/pass/async-drop.rs +++ b/src/tools/miri/tests/pass/async-drop.rs @@ -1,6 +1,11 @@ //@revisions: stack tree //@compile-flags: -Zmiri-strict-provenance //@[tree]compile-flags: -Zmiri-tree-borrows + +// WARNING: If you would ever want to modify this test, +// please consider modifying rustc's async drop test at +// `tests/ui/async-await/async-drop.rs`. + #![feature(async_drop, impl_trait_in_assoc_type, noop_waker, async_closure)] #![allow(incomplete_features, dead_code)] diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs b/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs index 8d3173da400f..3fff7921aff7 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs +++ b/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs @@ -27,7 +27,7 @@ fn func_d() -> Box<[*mut ()]> { fn main() { let mut seen_main = false; let frames = func_a(); - for frame in frames.into_iter() { + for frame in frames.iter() { let miri_frame = unsafe { miri_resolve_frame(*frame, 0) }; let name = String::from_utf8(miri_frame.name.into()).unwrap(); let filename = String::from_utf8(miri_frame.filename.into()).unwrap(); diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr index bc24d6de734d..c05950ebdc74 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr @@ -4,7 +4,7 @@ $DIR/backtrace-api-v0.rs:LL:CC (func_b) $DIR/backtrace-api-v0.rs:LL:CC (func_a) $DIR/backtrace-api-v0.rs:LL:CC (main) RUSTLIB/core/src/ops/function.rs:LL:CC (>::call_once - shim(fn())) -RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC (std::sys_common::backtrace::__rust_begin_short_backtrace) +RUSTLIB/std/src/sys/backtrace.rs:LL:CC (std::sys::backtrace::__rust_begin_short_backtrace) RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start::{closure#0}) RUSTLIB/core/src/ops/function.rs:LL:CC (std::ops::function::impls::call_once) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call) diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.rs b/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.rs index ad05271ca519..a3060abc3940 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.rs +++ b/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.rs @@ -32,7 +32,7 @@ fn func_d() -> Box<[*mut ()]> { fn main() { let mut seen_main = false; let frames = func_a(); - for frame in frames.into_iter() { + for frame in frames.iter() { let miri_frame = unsafe { miri_resolve_frame(*frame, 1) }; let mut name = vec![0; miri_frame.name_len]; diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr index 246e54becd82..b56d983d4298 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-api-v1.stderr @@ -4,7 +4,7 @@ $DIR/backtrace-api-v1.rs:LL:CC (func_b) $DIR/backtrace-api-v1.rs:LL:CC (func_a) $DIR/backtrace-api-v1.rs:LL:CC (main) RUSTLIB/core/src/ops/function.rs:LL:CC (>::call_once - shim(fn())) -RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC (std::sys_common::backtrace::__rust_begin_short_backtrace) +RUSTLIB/std/src/sys/backtrace.rs:LL:CC (std::sys::backtrace::__rust_begin_short_backtrace) RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start::{closure#0}) RUSTLIB/core/src/ops/function.rs:LL:CC (std::ops::function::impls::call_once) RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call) diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr index c48061d64d08..b06dd1da3c64 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-global-alloc.stderr @@ -2,8 +2,8 @@ at $DIR/backtrace-global-alloc.rs:LL:CC 1: >::call_once - shim(fn()) at RUSTLIB/core/src/ops/function.rs:LL:CC - 2: std::sys_common::backtrace::__rust_begin_short_backtrace - at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + 2: std::sys::backtrace::__rust_begin_short_backtrace + at RUSTLIB/std/src/sys/backtrace.rs:LL:CC 3: std::rt::lang_start::{closure#0} at RUSTLIB/std/src/rt.rs:LL:CC 4: std::ops::function::impls::call_once diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr index 4596cadb958d..84bdda59fce9 100644 --- a/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr +++ b/src/tools/miri/tests/pass/backtrace/backtrace-std.stderr @@ -10,8 +10,8 @@ at $DIR/backtrace-std.rs:LL:CC 5: >::call_once - shim(fn()) at RUSTLIB/core/src/ops/function.rs:LL:CC - 6: std::sys_common::backtrace::__rust_begin_short_backtrace - at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC + 6: std::sys::backtrace::__rust_begin_short_backtrace + at RUSTLIB/std/src/sys/backtrace.rs:LL:CC 7: std::rt::lang_start::{closure#0} at RUSTLIB/std/src/rt.rs:LL:CC 8: std::ops::function::impls::call_once diff --git a/src/tools/miri/tests/pass/drop_in_place.rs b/src/tools/miri/tests/pass/drop_in_place.rs new file mode 100644 index 000000000000..cac8d76dd9d4 --- /dev/null +++ b/src/tools/miri/tests/pass/drop_in_place.rs @@ -0,0 +1,10 @@ +// Miri currently doesn't require types without drop glue to be +// valid when dropped. This test confirms that behavior. +// This is not a stable guarantee! + +use std::ptr; + +fn main() { + let mut not_a_bool = 13u8; + unsafe { ptr::drop_in_place(&mut not_a_bool as *mut u8 as *mut bool) }; +} diff --git a/src/tools/miri/tests/pass/empty_main.rs b/src/tools/miri/tests/pass/empty_main.rs new file mode 100644 index 000000000000..d081b6db6702 --- /dev/null +++ b/src/tools/miri/tests/pass/empty_main.rs @@ -0,0 +1,3 @@ +// This may look trivial, but a bunch of code runs in std before +// `main` is called, so we are ensuring that that all works. +fn main() {} diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 1bb44d56bf6b..5464627fa144 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -1,5 +1,8 @@ #![feature(stmt_expr_attributes)] #![feature(float_gamma)] +#![feature(core_intrinsics)] +#![feature(f128)] +#![feature(f16)] #![allow(arithmetic_overflow)] use std::fmt::Debug; @@ -22,6 +25,8 @@ fn main() { rounding(); mul_add(); libm(); + test_fast(); + test_algebraic(); } // Helper function to avoid promotion so that this tests "run-time" casts, not CTFE. @@ -38,103 +43,23 @@ trait FloatToInt: Copy { unsafe fn cast_unchecked(self) -> Int; } -impl FloatToInt for f32 { - fn cast(self) -> i8 { - self as _ - } - unsafe fn cast_unchecked(self) -> i8 { - self.to_int_unchecked() - } -} -impl FloatToInt for f32 { - fn cast(self) -> i32 { - self as _ - } - unsafe fn cast_unchecked(self) -> i32 { - self.to_int_unchecked() - } -} -impl FloatToInt for f32 { - fn cast(self) -> u32 { - self as _ - } - unsafe fn cast_unchecked(self) -> u32 { - self.to_int_unchecked() - } -} -impl FloatToInt for f32 { - fn cast(self) -> i64 { - self as _ - } - unsafe fn cast_unchecked(self) -> i64 { - self.to_int_unchecked() - } -} -impl FloatToInt for f32 { - fn cast(self) -> u64 { - self as _ - } - unsafe fn cast_unchecked(self) -> u64 { - self.to_int_unchecked() - } +macro_rules! float_to_int { + ($fty:ty => $($ity:ty),+ $(,)?) => { + $( + impl FloatToInt<$ity> for $fty { + fn cast(self) -> $ity { + self as _ + } + unsafe fn cast_unchecked(self) -> $ity { + self.to_int_unchecked() + } + } + )* + }; } -impl FloatToInt for f64 { - fn cast(self) -> i8 { - self as _ - } - unsafe fn cast_unchecked(self) -> i8 { - self.to_int_unchecked() - } -} -impl FloatToInt for f64 { - fn cast(self) -> i32 { - self as _ - } - unsafe fn cast_unchecked(self) -> i32 { - self.to_int_unchecked() - } -} -impl FloatToInt for f64 { - fn cast(self) -> u32 { - self as _ - } - unsafe fn cast_unchecked(self) -> u32 { - self.to_int_unchecked() - } -} -impl FloatToInt for f64 { - fn cast(self) -> i64 { - self as _ - } - unsafe fn cast_unchecked(self) -> i64 { - self.to_int_unchecked() - } -} -impl FloatToInt for f64 { - fn cast(self) -> u64 { - self as _ - } - unsafe fn cast_unchecked(self) -> u64 { - self.to_int_unchecked() - } -} -impl FloatToInt for f64 { - fn cast(self) -> i128 { - self as _ - } - unsafe fn cast_unchecked(self) -> i128 { - self.to_int_unchecked() - } -} -impl FloatToInt for f64 { - fn cast(self) -> u128 { - self as _ - } - unsafe fn cast_unchecked(self) -> u128 { - self.to_int_unchecked() - } -} +float_to_int!(f32 => i8, u8, i16, u16, i32, u32, i64, u64, i128, u128); +float_to_int!(f64 => i8, u8, i16, u16, i32, u32, i64, u64, i128, u128); /// Test this cast both via `as` and via `approx_unchecked` (i.e., it must not saturate). #[track_caller] @@ -150,18 +75,29 @@ where fn basic() { // basic arithmetic + assert_eq(6.0_f16 * 6.0_f16, 36.0_f16); assert_eq(6.0_f32 * 6.0_f32, 36.0_f32); assert_eq(6.0_f64 * 6.0_f64, 36.0_f64); + assert_eq(6.0_f128 * 6.0_f128, 36.0_f128); + assert_eq(-{ 5.0_f16 }, -5.0_f16); assert_eq(-{ 5.0_f32 }, -5.0_f32); assert_eq(-{ 5.0_f64 }, -5.0_f64); + assert_eq(-{ 5.0_f128 }, -5.0_f128); + // infinities, NaN + // FIXME(f16_f128): add when constants and `is_infinite` are available assert!((5.0_f32 / 0.0).is_infinite()); assert_ne!({ 5.0_f32 / 0.0 }, { -5.0_f32 / 0.0 }); assert!((5.0_f64 / 0.0).is_infinite()); assert_ne!({ 5.0_f64 / 0.0 }, { 5.0_f64 / -0.0 }); assert_ne!(f32::NAN, f32::NAN); assert_ne!(f64::NAN, f64::NAN); + // negative zero + let posz = 0.0f16; + let negz = -0.0f16; + assert_eq(posz, negz); + assert_ne!(posz.to_bits(), negz.to_bits()); let posz = 0.0f32; let negz = -0.0f32; assert_eq(posz, negz); @@ -170,15 +106,30 @@ fn basic() { let negz = -0.0f64; assert_eq(posz, negz); assert_ne!(posz.to_bits(), negz.to_bits()); + let posz = 0.0f128; + let negz = -0.0f128; + assert_eq(posz, negz); + assert_ne!(posz.to_bits(), negz.to_bits()); + // byte-level transmute - let x: u64 = unsafe { std::mem::transmute(42.0_f64) }; - let y: f64 = unsafe { std::mem::transmute(x) }; - assert_eq(y, 42.0_f64); + let x: u16 = unsafe { std::mem::transmute(42.0_f16) }; + let y: f16 = unsafe { std::mem::transmute(x) }; + assert_eq(y, 42.0_f16); let x: u32 = unsafe { std::mem::transmute(42.0_f32) }; let y: f32 = unsafe { std::mem::transmute(x) }; assert_eq(y, 42.0_f32); + let x: u64 = unsafe { std::mem::transmute(42.0_f64) }; + let y: f64 = unsafe { std::mem::transmute(x) }; + assert_eq(y, 42.0_f64); + let x: u128 = unsafe { std::mem::transmute(42.0_f128) }; + let y: f128 = unsafe { std::mem::transmute(x) }; + assert_eq(y, 42.0_f128); // `%` sign behavior, some of this used to be buggy + assert!((black_box(1.0f16) % 1.0).is_sign_positive()); + assert!((black_box(1.0f16) % -1.0).is_sign_positive()); + assert!((black_box(-1.0f16) % 1.0).is_sign_negative()); + assert!((black_box(-1.0f16) % -1.0).is_sign_negative()); assert!((black_box(1.0f32) % 1.0).is_sign_positive()); assert!((black_box(1.0f32) % -1.0).is_sign_positive()); assert!((black_box(-1.0f32) % 1.0).is_sign_negative()); @@ -187,7 +138,12 @@ fn basic() { assert!((black_box(1.0f64) % -1.0).is_sign_positive()); assert!((black_box(-1.0f64) % 1.0).is_sign_negative()); assert!((black_box(-1.0f64) % -1.0).is_sign_negative()); + assert!((black_box(1.0f128) % 1.0).is_sign_positive()); + assert!((black_box(1.0f128) % -1.0).is_sign_positive()); + assert!((black_box(-1.0f128) % 1.0).is_sign_negative()); + assert!((black_box(-1.0f128) % -1.0).is_sign_negative()); + // FIXME(f16_f128): add when `abs` is available assert_eq!((-1.0f32).abs(), 1.0f32); assert_eq!(34.2f64.abs(), 34.2f64); } @@ -751,3 +707,67 @@ pub fn libm() { assert_approx_eq!(val, (2.0 * f64::consts::PI.sqrt()).ln()); assert_eq!(sign, -1); } + +fn test_fast() { + use std::intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast}; + + #[inline(never)] + pub fn test_operations_f64(a: f64, b: f64) { + // make sure they all map to the correct operation + unsafe { + assert_eq!(fadd_fast(a, b), a + b); + assert_eq!(fsub_fast(a, b), a - b); + assert_eq!(fmul_fast(a, b), a * b); + assert_eq!(fdiv_fast(a, b), a / b); + assert_eq!(frem_fast(a, b), a % b); + } + } + + #[inline(never)] + pub fn test_operations_f32(a: f32, b: f32) { + // make sure they all map to the correct operation + unsafe { + assert_eq!(fadd_fast(a, b), a + b); + assert_eq!(fsub_fast(a, b), a - b); + assert_eq!(fmul_fast(a, b), a * b); + assert_eq!(fdiv_fast(a, b), a / b); + assert_eq!(frem_fast(a, b), a % b); + } + } + + test_operations_f64(1., 2.); + test_operations_f64(10., 5.); + test_operations_f32(11., 2.); + test_operations_f32(10., 15.); +} + +fn test_algebraic() { + use std::intrinsics::{ + fadd_algebraic, fdiv_algebraic, fmul_algebraic, frem_algebraic, fsub_algebraic, + }; + + #[inline(never)] + pub fn test_operations_f64(a: f64, b: f64) { + // make sure they all map to the correct operation + assert_eq!(fadd_algebraic(a, b), a + b); + assert_eq!(fsub_algebraic(a, b), a - b); + assert_eq!(fmul_algebraic(a, b), a * b); + assert_eq!(fdiv_algebraic(a, b), a / b); + assert_eq!(frem_algebraic(a, b), a % b); + } + + #[inline(never)] + pub fn test_operations_f32(a: f32, b: f32) { + // make sure they all map to the correct operation + assert_eq!(fadd_algebraic(a, b), a + b); + assert_eq!(fsub_algebraic(a, b), a - b); + assert_eq!(fmul_algebraic(a, b), a * b); + assert_eq!(fdiv_algebraic(a, b), a / b); + assert_eq!(frem_algebraic(a, b), a % b); + } + + test_operations_f64(1., 2.); + test_operations_f64(10., 5.); + test_operations_f32(11., 2.); + test_operations_f32(10., 15.); +} diff --git a/src/tools/miri/tests/pass/float_fast_math.rs b/src/tools/miri/tests/pass/float_fast_math.rs deleted file mode 100644 index 52d985667df2..000000000000 --- a/src/tools/miri/tests/pass/float_fast_math.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![feature(core_intrinsics)] - -use std::intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast}; - -#[inline(never)] -pub fn test_operations_f64(a: f64, b: f64) { - // make sure they all map to the correct operation - unsafe { - assert_eq!(fadd_fast(a, b), a + b); - assert_eq!(fsub_fast(a, b), a - b); - assert_eq!(fmul_fast(a, b), a * b); - assert_eq!(fdiv_fast(a, b), a / b); - assert_eq!(frem_fast(a, b), a % b); - } -} - -#[inline(never)] -pub fn test_operations_f32(a: f32, b: f32) { - // make sure they all map to the correct operation - unsafe { - assert_eq!(fadd_fast(a, b), a + b); - assert_eq!(fsub_fast(a, b), a - b); - assert_eq!(fmul_fast(a, b), a * b); - assert_eq!(fdiv_fast(a, b), a / b); - assert_eq!(frem_fast(a, b), a % b); - } -} - -fn main() { - test_operations_f64(1., 2.); - test_operations_f64(10., 5.); - test_operations_f32(11., 2.); - test_operations_f32(10., 15.); -} diff --git a/src/tools/miri/tests/pass/function_calls/abi_compat.rs b/src/tools/miri/tests/pass/function_calls/abi_compat.rs index 136660a305af..0cfcd532ff4b 100644 --- a/src/tools/miri/tests/pass/function_calls/abi_compat.rs +++ b/src/tools/miri/tests/pass/function_calls/abi_compat.rs @@ -83,12 +83,24 @@ fn main() { test_abi_compat(main as fn(), id:: as fn(i32) -> i32); // - 1-ZST test_abi_compat((), [0u8; 0]); - // - Guaranteed null-pointer-optimizations (RFC 3391). + // - Guaranteed Option null-pointer-optimizations (RFC 3391). test_abi_compat(&0u32 as *const u32, Some(&0u32)); test_abi_compat(main as fn(), Some(main as fn())); test_abi_compat(0u32, Some(num::NonZero::new(1u32).unwrap())); test_abi_compat(&0u32 as *const u32, Some(Wrapper(&0u32))); - test_abi_compat(0u32, Some(Wrapper(num::NonZero::new(1u32).unwrap()))); + test_abi_compat(0u32, Some(Wrapper(num::NonZeroU32::new(1u32).unwrap()))); + // - Guaranteed Result does the same as Option (RFC 3391) + test_abi_compat(&0u32 as *const u32, Result::<_, ()>::Ok(&0u32)); + test_abi_compat(main as fn(), Result::<_, ()>::Ok(main as fn())); + test_abi_compat(0u32, Result::<_, ()>::Ok(num::NonZeroU32::new(1).unwrap())); + test_abi_compat(&0u32 as *const u32, Result::<_, ()>::Ok(Wrapper(&0u32))); + test_abi_compat(0u32, Result::<_, ()>::Ok(Wrapper(num::NonZeroU32::new(1).unwrap()))); + // - Guaranteed Result also does the same as Option (RFC 3391) + test_abi_compat(&0u32 as *const u32, Result::<(), _>::Err(&0u32)); + test_abi_compat(main as fn(), Result::<(), _>::Err(main as fn())); + test_abi_compat(0u32, Result::<(), _>::Err(num::NonZeroU32::new(1).unwrap())); + test_abi_compat(&0u32 as *const u32, Result::<(), _>::Err(Wrapper(&0u32))); + test_abi_compat(0u32, Result::<(), _>::Err(Wrapper(num::NonZeroU32::new(1).unwrap()))); // These must work for *any* type, since we guarantee that `repr(transparent)` is ABI-compatible // with the wrapped field. diff --git a/src/tools/miri/tests/pass/integer-ops.rs b/src/tools/miri/tests/pass/integer-ops.rs index 0ec1f8e9c693..3f8ac34e7d10 100644 --- a/src/tools/miri/tests/pass/integer-ops.rs +++ b/src/tools/miri/tests/pass/integer-ops.rs @@ -1,7 +1,64 @@ //@compile-flags: -Coverflow-checks=off #![allow(arithmetic_overflow)] +fn basic() { + fn ret() -> i64 { + 1 + } + + fn neg() -> i64 { + -1 + } + + fn add() -> i64 { + 1 + 2 + } + + fn indirect_add() -> i64 { + let x = 1; + let y = 2; + x + y + } + + fn arith() -> i32 { + 3 * 3 + 4 * 4 + } + + fn match_int() -> i16 { + let n = 2; + match n { + 0 => 0, + 1 => 10, + 2 => 20, + 3 => 30, + _ => 100, + } + } + + fn match_int_range() -> i64 { + let n = 42; + match n { + 0..=9 => 0, + 10..=19 => 1, + 20..=29 => 2, + 30..=39 => 3, + 40..=42 => 4, + _ => 5, + } + } + + assert_eq!(ret(), 1); + assert_eq!(neg(), -1); + assert_eq!(add(), 3); + assert_eq!(indirect_add(), 3); + assert_eq!(arith(), 5 * 5); + assert_eq!(match_int(), 20); + assert_eq!(match_int_range(), 4); +} + pub fn main() { + basic(); + // This tests that we do (not) do sign extension properly when loading integers assert_eq!(u32::MAX as i64, 4294967295); assert_eq!(i32::MIN as i64, -2147483648); @@ -152,6 +209,10 @@ pub fn main() { assert_eq!(5i32.overflowing_mul(2), (10, false)); assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true)); + assert_eq!(i64::MIN.overflowing_mul(-1), (i64::MIN, true)); + assert_eq!(i32::MIN.overflowing_mul(-1), (i32::MIN, true)); + assert_eq!(i16::MIN.overflowing_mul(-1), (i16::MIN, true)); + assert_eq!(i8::MIN.overflowing_mul(-1), (i8::MIN, true)); assert_eq!(5i32.overflowing_div(2), (2, false)); assert_eq!(i32::MIN.overflowing_div(-1), (i32::MIN, true)); diff --git a/src/tools/miri/tests/pass/integers.rs b/src/tools/miri/tests/pass/integers.rs deleted file mode 100644 index c04c6921f3c4..000000000000 --- a/src/tools/miri/tests/pass/integers.rs +++ /dev/null @@ -1,58 +0,0 @@ -fn ret() -> i64 { - 1 -} - -fn neg() -> i64 { - -1 -} - -fn add() -> i64 { - 1 + 2 -} - -fn indirect_add() -> i64 { - let x = 1; - let y = 2; - x + y -} - -fn arith() -> i32 { - 3 * 3 + 4 * 4 -} - -fn match_int() -> i16 { - let n = 2; - match n { - 0 => 0, - 1 => 10, - 2 => 20, - 3 => 30, - _ => 100, - } -} - -fn match_int_range() -> i64 { - let n = 42; - match n { - 0..=9 => 0, - 10..=19 => 1, - 20..=29 => 2, - 30..=39 => 3, - 40..=42 => 4, - _ => 5, - } -} - -fn main() { - assert_eq!(ret(), 1); - assert_eq!(neg(), -1); - assert_eq!(add(), 3); - assert_eq!(indirect_add(), 3); - assert_eq!(arith(), 5 * 5); - assert_eq!(match_int(), 20); - assert_eq!(match_int_range(), 4); - assert_eq!(i64::MIN.overflowing_mul(-1), (i64::MIN, true)); - assert_eq!(i32::MIN.overflowing_mul(-1), (i32::MIN, true)); - assert_eq!(i16::MIN.overflowing_mul(-1), (i16::MIN, true)); - assert_eq!(i8::MIN.overflowing_mul(-1), (i8::MIN, true)); -} diff --git a/src/tools/miri/tests/pass/intrinsics/intrinsics.rs b/src/tools/miri/tests/pass/intrinsics/intrinsics.rs index 0dda5aadce20..89289a25d50a 100644 --- a/src/tools/miri/tests/pass/intrinsics/intrinsics.rs +++ b/src/tools/miri/tests/pass/intrinsics/intrinsics.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-permissive-provenance -#![feature(core_intrinsics, layout_for_ptr)] +#![feature(core_intrinsics, layout_for_ptr, ptr_metadata)] //! Tests for various intrinsics that do not fit anywhere else. use std::intrinsics; @@ -57,4 +57,10 @@ fn main() { // Make sure that even if the discriminant is stored together with data, the intrinsic returns // only the discriminant, nothing about the data. assert_eq!(discriminant(&Some(false)), discriminant(&Some(true))); + + let () = intrinsics::ptr_metadata(&[1, 2, 3]); + let len = intrinsics::ptr_metadata(&[1, 2, 3][..]); + assert_eq!(len, 3); + let dyn_meta = intrinsics::ptr_metadata(&[1, 2, 3] as &dyn std::fmt::Debug); + assert_eq!(dyn_meta.size_of(), 12); } diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index 1fc713d48dcc..03d9fc0a76fe 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-strict-provenance -#![feature(portable_simd, adt_const_params, core_intrinsics)] +#![feature(portable_simd, adt_const_params, core_intrinsics, repr_simd)] #![allow(incomplete_features, internal_features)] use std::intrinsics::simd as intrinsics; use std::ptr; @@ -318,6 +318,83 @@ fn simd_mask() { assert_eq!(selected1, i32x4::from_array([0, 0, 0, 1])); assert_eq!(selected2, selected1); } + + // Non-power-of-2 multi-byte mask. + #[repr(simd, packed)] + #[allow(non_camel_case_types)] + #[derive(Copy, Clone, Debug, PartialEq)] + struct i32x10([i32; 10]); + impl i32x10 { + fn splat(x: i32) -> Self { + Self([x; 10]) + } + } + unsafe { + let mask = i32x10([!0, !0, 0, !0, 0, 0, !0, 0, !0, 0]); + let mask_bits = if cfg!(target_endian = "little") { 0b0101001011 } else { 0b1101001010 }; + let mask_bytes = + if cfg!(target_endian = "little") { [0b01001011, 0b01] } else { [0b11, 0b01001010] }; + + let bitmask1: u16 = simd_bitmask(mask); + let bitmask2: [u8; 2] = simd_bitmask(mask); + assert_eq!(bitmask1, mask_bits); + assert_eq!(bitmask2, mask_bytes); + + let selected1 = simd_select_bitmask::( + mask_bits, + i32x10::splat(!0), // yes + i32x10::splat(0), // no + ); + let selected2 = simd_select_bitmask::<[u8; 2], _>( + mask_bytes, + i32x10::splat(!0), // yes + i32x10::splat(0), // no + ); + assert_eq!(selected1, mask); + assert_eq!(selected2, mask); + } + + // Test for a mask where the next multiple of 8 is not a power of two. + #[repr(simd, packed)] + #[allow(non_camel_case_types)] + #[derive(Copy, Clone, Debug, PartialEq)] + struct i32x20([i32; 20]); + impl i32x20 { + fn splat(x: i32) -> Self { + Self([x; 20]) + } + } + unsafe { + let mask = i32x20([!0, !0, 0, !0, 0, 0, !0, 0, !0, 0, 0, 0, 0, !0, !0, !0, !0, !0, !0, !0]); + let mask_bits = if cfg!(target_endian = "little") { + 0b11111110000101001011 + } else { + 0b11010010100001111111 + }; + let mask_bytes = if cfg!(target_endian = "little") { + [0b01001011, 0b11100001, 0b1111] + } else { + [0b1101, 0b00101000, 0b01111111] + }; + + let bitmask1: u32 = simd_bitmask(mask); + let bitmask2: [u8; 3] = simd_bitmask(mask); + assert_eq!(bitmask1, mask_bits); + assert_eq!(bitmask2, mask_bytes); + + let selected1 = simd_select_bitmask::( + mask_bits, + i32x20::splat(!0), // yes + i32x20::splat(0), // no + ); + let selected2 = simd_select_bitmask::<[u8; 3], _>( + mask_bytes, + i32x20::splat(!0), // yes + i32x20::splat(0), // no + ); + assert_eq!(selected1, mask); + assert_eq!(selected2, mask); + } } fn simd_cast() { @@ -505,6 +582,21 @@ fn simd_intrinsics() { assert!(simd_reduce_all(i32x4::splat(-1))); assert!(!simd_reduce_all(i32x2::from_array([0, -1]))); + assert_eq!( + simd_ctlz(i32x4::from_array([0, i32::MAX, i32::MIN, -1_i32])), + i32x4::from_array([32, 1, 0, 0]) + ); + + assert_eq!( + simd_ctpop(i32x4::from_array([0, i32::MAX, i32::MIN, -1_i32])), + i32x4::from_array([0, 31, 1, 32]) + ); + + assert_eq!( + simd_cttz(i32x4::from_array([0, i32::MAX, i32::MIN, -1_i32])), + i32x4::from_array([32, 0, 31, 0]) + ); + assert_eq!( simd_select(i8x4::from_array([0, -1, -1, 0]), a, b), i32x4::from_array([1, 10, 10, 4]) @@ -566,11 +658,32 @@ fn simd_masked_loadstore() { assert_eq!(buf, [2, 3, 4]); } +fn simd_ops_non_pow2() { + // Just a little smoke test for operations on non-power-of-two vectors. + #[repr(simd, packed)] + #[derive(Copy, Clone)] + pub struct SimdPacked([T; N]); + #[repr(simd)] + #[derive(Copy, Clone)] + pub struct SimdPadded([T; N]); + + let x = SimdPacked([1u32; 3]); + let y = SimdPacked([2u32; 3]); + let z = unsafe { intrinsics::simd_add(x, y) }; + assert_eq!({ z.0 }, [3u32; 3]); + + let x = SimdPadded([1u32; 3]); + let y = SimdPadded([2u32; 3]); + let z = unsafe { intrinsics::simd_add(x, y) }; + assert_eq!(z.0, [3u32; 3]); +} + fn main() { simd_mask(); simd_ops_f32(); simd_ops_f64(); simd_ops_i32(); + simd_ops_non_pow2(); simd_cast(); simd_swizzle(); simd_gather_scatter(); diff --git a/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs b/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs new file mode 100644 index 000000000000..c46031de2d84 --- /dev/null +++ b/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs @@ -0,0 +1,40 @@ +#![feature(ptr_metadata)] +// This test is the result of minimizing the `emplacable` crate to reproduce +// . + +use std::{ops::FnMut, ptr::Pointee}; + +pub type EmplacerFn<'a, T> = dyn for<'b> FnMut(::Metadata) + 'a; + +#[repr(transparent)] +pub struct Emplacer<'a, T>(EmplacerFn<'a, T>) +where + T: ?Sized; + +impl<'a, T> Emplacer<'a, T> +where + T: ?Sized, +{ + pub unsafe fn from_fn<'b>(emplacer_fn: &'b mut EmplacerFn<'a, T>) -> &'b mut Self { + // This used to trigger: + // constructing invalid value: wrong trait in wide pointer vtable: expected + // `std::ops::FnMut(<[std::boxed::Box] as std::ptr::Pointee>::Metadata)`, but encountered + // `std::ops::FnMut<(usize,)>`. + unsafe { &mut *((emplacer_fn as *mut EmplacerFn<'a, T>) as *mut Self) } + } +} + +pub fn box_new_with() +where + T: ?Sized, +{ + let emplacer_closure = &mut |_meta| { + unreachable!(); + }; + + unsafe { Emplacer::::from_fn(emplacer_closure) }; +} + +fn main() { + box_new_with::<[Box]>(); +} diff --git a/src/tools/miri/tests/pass/panic/unwind_dwarf.rs b/src/tools/miri/tests/pass/panic/unwind_dwarf.rs new file mode 100644 index 000000000000..f690be471ba7 --- /dev/null +++ b/src/tools/miri/tests/pass/panic/unwind_dwarf.rs @@ -0,0 +1,96 @@ +//@ignore-target-windows: Windows uses a different unwinding mechanism +#![feature(core_intrinsics, panic_unwind, rustc_attrs)] +#![allow(internal_features)] + +//! Unwinding using `_Unwind_RaiseException` + +extern crate unwind as uw; + +use std::any::Any; +use std::ptr; + +#[repr(C)] +struct Exception { + _uwe: uw::_Unwind_Exception, + cause: Box, +} + +pub fn panic(data: Box) -> u32 { + extern "C" fn exception_cleanup( + _unwind_code: uw::_Unwind_Reason_Code, + _exception: *mut uw::_Unwind_Exception, + ) { + std::process::abort(); + } + + let exception = Box::new(Exception { + _uwe: uw::_Unwind_Exception { + exception_class: miri_exception_class(), + exception_cleanup: Some(exception_cleanup), + private: [core::ptr::null(); uw::unwinder_private_data_size], + }, + cause: data, + }); + let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception; + return unsafe { uw::_Unwind_RaiseException(exception_param) as u32 }; +} + +pub unsafe fn rust_panic_cleanup(ptr: *mut u8) -> Box { + let exception = ptr as *mut uw::_Unwind_Exception; + if (*exception).exception_class != miri_exception_class() { + std::process::abort(); + } + + let exception = exception.cast::(); + + let exception = Box::from_raw(exception as *mut Exception); + exception.cause +} + +fn miri_exception_class() -> uw::_Unwind_Exception_Class { + // M O Z \0 M I R I -- vendor, language + // (Miri's own exception class is just used for testing) + 0x4d4f5a_00_4d495249 +} + +pub fn catch_unwind R>(f: F) -> Result> { + struct Data { + f: Option, + r: Option, + p: Option>, + } + + let mut data = Data { f: Some(f), r: None, p: None }; + + let data_ptr = ptr::addr_of_mut!(data) as *mut u8; + unsafe { + return if std::intrinsics::catch_unwind(do_call::, data_ptr, do_catch::) == 0 { + Ok(data.r.take().unwrap()) + } else { + Err(data.p.take().unwrap()) + }; + } + + fn do_call R, R>(data: *mut u8) { + unsafe { + let data = &mut *data.cast::>(); + let f = data.f.take().unwrap(); + data.r = Some(f()); + } + } + + #[rustc_nounwind] + fn do_catch R, R>(data: *mut u8, payload: *mut u8) { + unsafe { + let obj = rust_panic_cleanup(payload); + (*data.cast::>()).p = Some(obj); + } + } +} + +fn main() { + assert_eq!( + catch_unwind(|| panic(Box::new(42))).unwrap_err().downcast::().unwrap(), + Box::new(42) + ); +} diff --git a/src/tools/miri/tests/pass/shims/env/home.rs b/src/tools/miri/tests/pass/shims/env/home.rs index c237f9ed9ffa..8b4b907a51dd 100644 --- a/src/tools/miri/tests/pass/shims/env/home.rs +++ b/src/tools/miri/tests/pass/shims/env/home.rs @@ -2,8 +2,12 @@ use std::env; fn main() { - env::remove_var("HOME"); // make sure we enter the interesting codepath - env::remove_var("USERPROFILE"); // Windows also looks as this env var + // Remove the env vars to hit the underlying shim -- except + // on android where the env var is all we have. + #[cfg(not(target_os = "android"))] + env::remove_var("HOME"); + env::remove_var("USERPROFILE"); + #[allow(deprecated)] env::home_dir().unwrap(); } diff --git a/src/tools/miri/tests/pass/shims/env/var.rs b/src/tools/miri/tests/pass/shims/env/var.rs index babaf00578a7..a576c1fc8bb8 100644 --- a/src/tools/miri/tests/pass/shims/env/var.rs +++ b/src/tools/miri/tests/pass/shims/env/var.rs @@ -1,3 +1,4 @@ +//@compile-flags: -Zmiri-preemption-rate=0 use std::env; use std::thread; @@ -26,6 +27,8 @@ fn main() { println!("{:#?}", env::vars().collect::>()); // Do things concurrently, to make sure there's no data race. + // We disable preemption to make sure the lock is not contended; + // that means we don't hit e.g. the futex codepath on Android (which we don't support). let t = thread::spawn(|| { env::set_var("MIRI_TEST", "42"); }); diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs new file mode 100644 index 000000000000..2f242dd5379d --- /dev/null +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs @@ -0,0 +1,48 @@ +// Ignore everything except x86 and x86_64 +// Any new targets that are added to CI should be ignored here. +// (We cannot use `cfg`-based tricks here since the `target-feature` flags below only work on x86.) +//@ignore-target-aarch64 +//@ignore-target-arm +//@ignore-target-avr +//@ignore-target-s390x +//@ignore-target-thumbv7em +//@ignore-target-wasm32 +//@compile-flags: -C target-feature=+pclmulqdq + +#[cfg(target_arch = "x86")] +use std::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use std::arch::x86_64::*; + +fn main() { + assert!(is_x86_feature_detected!("pclmulqdq")); + + let a = (0x7fffffffffffffff, 0x4317e40ab4ddcf05); + let b = (0xdd358416f52ecd34, 0x633d11cc638ca16b); + + unsafe { + assert_eq!(clmulepi64_si128::<0x00>(a, b), (13036940098130298092, 2704901987789626761)); + assert_eq!(clmulepi64_si128::<0x01>(a, b), (6707488474444649956, 3901733953304450635)); + assert_eq!(clmulepi64_si128::<0x10>(a, b), (11607166829323378905, 1191897396234301548)); + assert_eq!(clmulepi64_si128::<0x11>(a, b), (7731954893213347271, 1760130762532070957)); + } +} + +#[target_feature(enable = "pclmulqdq")] +unsafe fn clmulepi64_si128( + (a1, a2): (u64, u64), + (b1, b2): (u64, u64), +) -> (u64, u64) { + // SAFETY: There are no safety requirements for calling `_mm_clmulepi64_si128`. + // It's just unsafe for API consistency with other intrinsics. + unsafe { + let a = core::mem::transmute::<_, __m128i>([a1, a2]); + let b = core::mem::transmute::<_, __m128i>([b1, b2]); + + let out = _mm_clmulepi64_si128::(a, b); + + let [c1, c2] = core::mem::transmute::<_, [u64; 2]>(out); + + (c1, c2) + } +} diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs new file mode 100644 index 000000000000..3ac53ea8b933 --- /dev/null +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs @@ -0,0 +1,443 @@ +// Ignore everything except x86 and x86_64 +// Any new targets that are added to CI should be ignored here. +// (We cannot use `cfg`-based tricks here since the `target-feature` flags below only work on x86.) +//@ignore-target-aarch64 +//@ignore-target-arm +//@ignore-target-avr +//@ignore-target-s390x +//@ignore-target-thumbv7em +//@ignore-target-wasm32 +//@compile-flags: -C target-feature=+sse4.2 + +#[cfg(target_arch = "x86")] +use std::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use std::arch::x86_64::*; +use std::mem::transmute; + +fn main() { + assert!(is_x86_feature_detected!("sse4.2")); + + unsafe { + test_sse42(); + } +} + +#[target_feature(enable = "sse4.2")] +unsafe fn test_sse42() { + // Mostly copied from library/stdarch/crates/core_arch/src/x86/sse42.rs + + test_crc(); + test_cmp(); + test_str(); +} + +#[target_feature(enable = "sse4.2")] +unsafe fn test_crc() { + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_crc32_u8() { + let crc = 0x2aa1e72b; + let v = 0x2a; + let i = _mm_crc32_u8(crc, v); + assert_eq!(i, 0xf24122e4); + + let crc = 0x61343ec4; + let v = 0xef; + let i = _mm_crc32_u8(crc, v); + assert_eq!(i, 0xb95511db); + + let crc = 0xbadeafe; + let v = 0xc0; + let i = _mm_crc32_u8(crc, v); + assert_eq!(i, 0x9c905b7c); + } + test_mm_crc32_u8(); + + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_crc32_u16() { + let crc = 0x8ecec3b5; + let v = 0x22b; + let i = _mm_crc32_u16(crc, v); + assert_eq!(i, 0x13bb2fb); + + let crc = 0x150bc664; + let v = 0xa6c0; + let i = _mm_crc32_u16(crc, v); + assert_eq!(i, 0xab04fe4e); + + let crc = 0xbadeafe; + let v = 0xc0fe; + let i = _mm_crc32_u16(crc, v); + assert_eq!(i, 0x4b5fad4b); + } + test_mm_crc32_u16(); + + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_crc32_u32() { + let crc = 0xae2912c8; + let v = 0x845fed; + let i = _mm_crc32_u32(crc, v); + assert_eq!(i, 0xffae2ed1); + + let crc = 0x1a198fe3; + let v = 0x885585c2; + let i = _mm_crc32_u32(crc, v); + assert_eq!(i, 0x22443a7b); + + let crc = 0xbadeafe; + let v = 0xc0febeef; + let i = _mm_crc32_u32(crc, v); + assert_eq!(i, 0xb309502f); + } + test_mm_crc32_u32(); + + #[cfg(target_arch = "x86_64")] + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_crc32_u64() { + let crc = 0x7819dccd3e824; + let v = 0x2a22b845fed; + let i = _mm_crc32_u64(crc, v); + assert_eq!(i, 0xbb6cdc6c); + + let crc = 0x6dd960387fe13819; + let v = 0x1a7ea8fb571746b0; + let i = _mm_crc32_u64(crc, v); + assert_eq!(i, 0x315b4f6); + + let crc = 0xbadeafe; + let v = 0xc0febeefdadafefe; + let i = _mm_crc32_u64(crc, v); + assert_eq!(i, 0x5b44f54f); + } + #[cfg(not(target_arch = "x86_64"))] + unsafe fn test_mm_crc32_u64() {} + test_mm_crc32_u64(); +} + +#[target_feature(enable = "sse4.2")] +unsafe fn test_cmp() { + let a = _mm_set_epi64x(0x2a, 0); + let b = _mm_set1_epi64x(0x00); + let i = _mm_cmpgt_epi64(a, b); + assert_eq_m128i(i, _mm_set_epi64x(0xffffffffffffffffu64 as i64, 0x00)); +} + +#[target_feature(enable = "sse4.2")] +unsafe fn test_str() { + #[target_feature(enable = "sse4.2")] + unsafe fn str_to_m128i(s: &[u8]) -> __m128i { + assert!(s.len() <= 16); + let slice = &mut [0u8; 16]; + std::ptr::copy_nonoverlapping(s.as_ptr(), slice.as_mut_ptr(), s.len()); + _mm_loadu_si128(slice.as_ptr() as *const _) + } + + // Test the `_mm_cmpistrm` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpistrm() { + let a = str_to_m128i(b"Hello! Good-Bye!"); + let b = str_to_m128i(b"hello! good-bye!"); + let i = _mm_cmpistrm::<_SIDD_UNIT_MASK>(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi8( + 0x00, !0, !0, !0, !0, !0, !0, 0x00, + !0, !0, !0, !0, 0x00, !0, !0, !0, + ); + assert_eq_m128i(i, res); + } + test_mm_cmpistrm(); + + // Test the `_mm_cmpistri` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpistri() { + let a = str_to_m128i(b"Hello"); + let b = str_to_m128i(b" Hello "); + let i = _mm_cmpistri::<_SIDD_CMP_EQUAL_ORDERED>(a, b); + assert_eq!(3, i); + } + test_mm_cmpistri(); + + // Test the `_mm_cmpistrz` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpistrz() { + let a = str_to_m128i(b""); + let b = str_to_m128i(b"Hello"); + let i = _mm_cmpistrz::<_SIDD_CMP_EQUAL_ORDERED>(a, b); + assert_eq!(1, i); + } + test_mm_cmpistrz(); + + // Test the `_mm_cmpistrc` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpistrc() { + let a = str_to_m128i(b" "); + let b = str_to_m128i(b" ! "); + let i = _mm_cmpistrc::<_SIDD_UNIT_MASK>(a, b); + assert_eq!(1, i); + } + test_mm_cmpistrc(); + + // Test the `_mm_cmpistrs` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpistrs() { + let a = str_to_m128i(b"Hello"); + let b = str_to_m128i(b""); + let i = _mm_cmpistrs::<_SIDD_CMP_EQUAL_ORDERED>(a, b); + assert_eq!(1, i); + } + test_mm_cmpistrs(); + + // Test the `_mm_cmpistro` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpistro() { + #[rustfmt::skip] + let a_bytes = _mm_setr_epi8( + 0x00, 0x47, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, + 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ); + #[rustfmt::skip] + let b_bytes = _mm_setr_epi8( + 0x00, 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, + 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ); + let a = a_bytes; + let b = b_bytes; + let i = _mm_cmpistro::<{ _SIDD_UWORD_OPS | _SIDD_UNIT_MASK }>(a, b); + assert_eq!(0, i); + } + test_mm_cmpistro(); + + // Test the `_mm_cmpistra` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpistra() { + let a = str_to_m128i(b""); + let b = str_to_m128i(b"Hello!!!!!!!!!!!"); + let i = _mm_cmpistra::<_SIDD_UNIT_MASK>(a, b); + assert_eq!(1, i); + } + test_mm_cmpistra(); + + // Test the `_mm_cmpestrm` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpestrm() { + let a = str_to_m128i(b"Hello!"); + let b = str_to_m128i(b"Hello."); + let i = _mm_cmpestrm::<_SIDD_UNIT_MASK>(a, 5, b, 5); + #[rustfmt::skip] + let r = _mm_setr_epi8( + !0, !0, !0, !0, !0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ); + assert_eq_m128i(i, r); + } + test_mm_cmpestrm(); + + // Test the `_mm_cmpestri` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpestri() { + let a = str_to_m128i(b"bar - garbage"); + let b = str_to_m128i(b"foobar"); + let i = _mm_cmpestri::<_SIDD_CMP_EQUAL_ORDERED>(a, 3, b, 6); + assert_eq!(3, i); + } + test_mm_cmpestri(); + + // Test the `_mm_cmpestrz` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpestrz() { + let a = str_to_m128i(b""); + let b = str_to_m128i(b"Hello"); + let i = _mm_cmpestrz::<_SIDD_CMP_EQUAL_ORDERED>(a, 16, b, 6); + assert_eq!(1, i); + } + test_mm_cmpestrz(); + + // Test the `_mm_cmpestrs` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpestrc() { + let va = str_to_m128i(b"!!!!!!!!"); + let vb = str_to_m128i(b" "); + let i = _mm_cmpestrc::<_SIDD_UNIT_MASK>(va, 7, vb, 7); + assert_eq!(0, i); + } + test_mm_cmpestrc(); + + // Test the `_mm_cmpestrs` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpestrs() { + #[rustfmt::skip] + let a_bytes = _mm_setr_epi8( + 0x00, 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, + 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ); + let a = a_bytes; + let b = _mm_set1_epi8(0x00); + let i = _mm_cmpestrs::<_SIDD_UWORD_OPS>(a, 8, b, 0); + assert_eq!(0, i); + } + test_mm_cmpestrs(); + + // Test the `_mm_cmpestro` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpestro() { + let a = str_to_m128i(b"Hello"); + let b = str_to_m128i(b"World"); + let i = _mm_cmpestro::<_SIDD_UBYTE_OPS>(a, 5, b, 5); + assert_eq!(0, i); + } + test_mm_cmpestro(); + + // Test the `_mm_cmpestra` intrinsic. + #[target_feature(enable = "sse4.2")] + unsafe fn test_mm_cmpestra() { + let a = str_to_m128i(b"Cannot match a"); + let b = str_to_m128i(b"Null after 14"); + let i = _mm_cmpestra::<{ _SIDD_CMP_EQUAL_EACH | _SIDD_UNIT_MASK }>(a, 14, b, 16); + assert_eq!(1, i); + } + test_mm_cmpestra(); + + // Additional tests not inside the standard library. + + // Test the subset functionality of the intrinsic. + unsafe fn test_subset() { + let a = str_to_m128i(b"ABCDEFG"); + let b = str_to_m128i(b"ABC UVW XYZ EFG"); + + let i = _mm_cmpistrm::<{ _SIDD_CMP_EQUAL_ANY | _SIDD_UNIT_MASK }>(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi8( + !0, !0, !0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, !0, !0, !0, 0x00, + ); + assert_eq_m128i(i, res); + } + test_subset(); + + // Properly test index generation. + unsafe fn test_index() { + let a = str_to_m128i(b"Hello"); + let b = str_to_m128i(b"Hello Hello H"); + + let i = _mm_cmpistri::<{ _SIDD_CMP_EQUAL_EACH | _SIDD_LEAST_SIGNIFICANT }>(a, b); + assert_eq!(i, 0); + + let i = _mm_cmpistri::<{ _SIDD_CMP_EQUAL_EACH | _SIDD_MOST_SIGNIFICANT }>(a, b); + assert_eq!(i, 15); + + let a = str_to_m128i(b"Hello"); + let b = str_to_m128i(b" "); + let i = _mm_cmpistri::<{ _SIDD_CMP_EQUAL_EACH | _SIDD_MOST_SIGNIFICANT }>(a, b); + assert_eq!(i, 16); + } + test_index(); + + // Properly test the substring functionality of the intrinsics. + #[target_feature(enable = "sse4.2")] + unsafe fn test_substring() { + let a = str_to_m128i(b"Hello"); + let b = str_to_m128i(b"Hello Hello H"); + + let i = _mm_cmpistrm::<{ _SIDD_CMP_EQUAL_ORDERED | _SIDD_UNIT_MASK }>(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi8( + !0, 0x00, 0x00, 0x00, 0x00, 0x00, !0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ); + assert_eq_m128i(i, res); + } + test_substring(); + + // Test the range functionality of the intrinsics. + // Will also test signed values and word-sized values. + #[target_feature(enable = "sse4.2")] + unsafe fn test_ranges() { + let a = _mm_setr_epi16(0, 1, 7, 8, 0, 0, -100, 100); + let b = _mm_setr_epi16(1, 2, 3, 4, 5, 6, 7, 8); + + let i = + _mm_cmpestrm::<{ _SIDD_SWORD_OPS | _SIDD_CMP_RANGES | _SIDD_UNIT_MASK }>(a, 2, b, 8); + let res = _mm_setr_epi16(!0, 0, 0, 0, 0, 0, 0, 0); + assert_eq_m128i(i, res); + + let i = + _mm_cmpestrm::<{ _SIDD_SWORD_OPS | _SIDD_CMP_RANGES | _SIDD_UNIT_MASK }>(a, 3, b, 8); + let res = _mm_setr_epi16(!0, 0, 0, 0, 0, 0, 0, 0); + assert_eq_m128i(i, res); + + let i = + _mm_cmpestrm::<{ _SIDD_SWORD_OPS | _SIDD_CMP_RANGES | _SIDD_UNIT_MASK }>(a, 4, b, 8); + let res = _mm_setr_epi16(!0, 0, 0, 0, 0, 0, !0, !0); + assert_eq_m128i(i, res); + + let i = + _mm_cmpestrm::<{ _SIDD_SWORD_OPS | _SIDD_CMP_RANGES | _SIDD_UNIT_MASK }>(a, 6, b, 8); + let res = _mm_setr_epi16(!0, 0, 0, 0, 0, 0, !0, !0); + assert_eq_m128i(i, res); + + let i = + _mm_cmpestrm::<{ _SIDD_SWORD_OPS | _SIDD_CMP_RANGES | _SIDD_UNIT_MASK }>(a, 8, b, 8); + let res = _mm_setr_epi16(!0, !0, !0, !0, !0, !0, !0, !0); + assert_eq_m128i(i, res); + } + test_ranges(); + + // Confirm that the polarity bits work as indended. + #[target_feature(enable = "sse4.2")] + unsafe fn test_polarity() { + let a = str_to_m128i(b"Hello!"); + let b = str_to_m128i(b"hello?"); + + let i = _mm_cmpistrm::< + { (_SIDD_MASKED_NEGATIVE_POLARITY ^ _SIDD_NEGATIVE_POLARITY) | _SIDD_UNIT_MASK }, + >(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi8( + 0x00, !0, !0, !0, !0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ); + assert_eq_m128i(i, res); + + let i = _mm_cmpistrm::<{ _SIDD_MASKED_NEGATIVE_POLARITY | _SIDD_UNIT_MASK }>(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi8( + !0, 0x00, 0x00, 0x00, 0x00, !0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ); + assert_eq_m128i(i, res); + + let i = _mm_cmpistrm::<{ _SIDD_NEGATIVE_POLARITY | _SIDD_UNIT_MASK }>(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi8( + !0, 0x00, 0x00, 0x00, 0x00, !0, !0, !0, + !0, !0, !0, !0, !0, !0, !0, !0, + ); + assert_eq_m128i(i, res); + } + test_polarity(); + + // Test the code path in which the intrinsic is supposed to + // return a bit mask instead of a byte mask. + #[target_feature(enable = "sse4.2")] + unsafe fn test_bitmask() { + let a = str_to_m128i(b"Hello! Good-Bye!"); + let b = str_to_m128i(b"hello! good-bye!"); + + let i = _mm_cmpistrm::<0>(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi32(0b11101111_01111110, 0, 0, 0); + assert_eq_m128i(i, res); + + let i = _mm_cmpistrm::<_SIDD_MASKED_NEGATIVE_POLARITY>(a, b); + #[rustfmt::skip] + let res = _mm_setr_epi32(0b00010000_10000001, 0, 0, 0); + assert_eq_m128i(i, res); + } + test_bitmask(); +} + +#[track_caller] +#[target_feature(enable = "sse2")] +pub unsafe fn assert_eq_m128i(a: __m128i, b: __m128i) { + assert_eq!(transmute::<_, [u64; 2]>(a), transmute::<_, [u64; 2]>(b)) +} diff --git a/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs new file mode 100644 index 000000000000..2d142bef73c4 --- /dev/null +++ b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs @@ -0,0 +1,59 @@ +//! Tests specific for : zero-sized operations. +#![feature(strict_provenance)] + +use std::ptr; + +fn main() { + // Null. + test_ptr(ptr::null_mut::<()>()); + // No provenance. + test_ptr(ptr::without_provenance_mut::<()>(1)); + // Out-of-bounds. + let mut b = Box::new(0i32); + let ptr = ptr::addr_of_mut!(*b) as *mut (); + test_ptr(ptr.wrapping_byte_add(2)); + // Dangling (use-after-free). + drop(b); + test_ptr(ptr); +} + +fn test_ptr(ptr: *mut ()) { + unsafe { + // Reads and writes. + let mut val = *ptr; + *ptr = val; + ptr.read(); + ptr.write(()); + // Memory access intrinsics. + // - memcpy (1st and 2nd argument) + ptr.copy_from_nonoverlapping(&(), 1); + ptr.copy_to_nonoverlapping(&mut val, 1); + // - memmove (1st and 2nd argument) + ptr.copy_from(&(), 1); + ptr.copy_to(&mut val, 1); + // - memset + ptr.write_bytes(0u8, 1); + // Offset. + let _ = ptr.offset(0); + let _ = ptr.offset(1); // this is still 0 bytes + // Distance. + let ptr = ptr.cast::(); + ptr.offset_from(ptr); + /* + FIXME: this is disabled for now as these cases are not yet allowed. + // Distance from other "bad" pointers that have the same address, but different provenance. Some + // of this is library UB, but we don't want it to be language UB since that would violate + // provenance monotonicity: if we allow computing the distance between two ptrs with no + // provenance, we have to allow computing it between two ptrs with arbitrary provenance. + // - Distance from "no provenance" + ptr.offset_from(ptr::without_provenance_mut(ptr.addr())); + // - Distance from out-of-bounds pointer + let mut b = Box::new(0i32); + let other_ptr = ptr::addr_of_mut!(*b); + ptr.offset_from(other_ptr.with_addr(ptr.addr())); + // - Distance from use-after-free pointer + drop(b); + ptr.offset_from(other_ptr.with_addr(ptr.addr())); + */ + } +} diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml index 1ff410e723af..88e8640d56ab 100644 --- a/src/tools/opt-dist/Cargo.toml +++ b/src/tools/opt-dist/Cargo.toml @@ -13,8 +13,6 @@ humansize = "2" sysinfo = { version = "0.30", default-features = false } fs_extra = "1" camino = "1" -reqwest = { version = "0.11", features = ["blocking"] } -zip = { version = "0.6", default-features = false, features = ["deflate"] } tar = "0.4" xz = { version = "0.1", package = "xz2" } serde = { version = "1", features = ["derive"] } diff --git a/src/tools/opt-dist/src/environment.rs b/src/tools/opt-dist/src/environment.rs index ff782a1687e2..bc01b7fb8a37 100644 --- a/src/tools/opt-dist/src/environment.rs +++ b/src/tools/opt-dist/src/environment.rs @@ -17,6 +17,9 @@ pub struct Environment { host_llvm_dir: Utf8PathBuf, /// List of test paths that should be skipped when testing the optimized artifacts. skipped_tests: Vec, + /// Arguments passed to `rustc-perf --cargo-config ` when running benchmarks. + #[builder(default)] + benchmark_cargo_config: Vec, /// Directory containing a pre-built rustc-perf checkout. #[builder(default)] prebuilt_rustc_perf: Option, @@ -94,6 +97,10 @@ impl Environment { pub fn skipped_tests(&self) -> &[String] { &self.skipped_tests } + + pub fn benchmark_cargo_config(&self) -> &[String] { + &self.benchmark_cargo_config + } } /// What is the extension of binary executables on this platform? diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs index bd0a3815855d..e4271a6e2dd9 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -3,10 +3,7 @@ use anyhow::Context; use camino::{Utf8Path, Utf8PathBuf}; use clap::Parser; use log::LevelFilter; -use std::io::Cursor; -use std::time::Duration; use utils::io; -use zip::ZipArchive; use crate::environment::{Environment, EnvironmentBuilder}; use crate::exec::{cmd, Bootstrap}; @@ -17,9 +14,9 @@ use crate::training::{ rustc_benchmarks, }; use crate::utils::artifact_size::print_binary_sizes; -use crate::utils::io::{copy_directory, move_directory, reset_directory}; +use crate::utils::io::{copy_directory, reset_directory}; use crate::utils::{ - clear_llvm_files, format_env_variables, print_free_disk_space, retry_action, with_log_group, + clear_llvm_files, format_env_variables, print_free_disk_space, with_log_group, write_timer_to_summary, }; @@ -69,7 +66,12 @@ enum EnvironmentCmd { #[arg(long, default_value = "opt-artifacts")] artifact_dir: Utf8PathBuf, - /// Checkout directory of `rustc-perf`, it will be fetched automatically if unspecified. + /// Checkout directory of `rustc-perf`. + /// + /// If unspecified, defaults to the rustc-perf submodule in the rustc checkout dir + /// (`src/tools/rustc-perf`), which should have been initialized when building this tool. + // FIXME: Move update_submodule into build_helper, that way we can also ensure the submodule + // is updated when _running_ opt-dist, rather than building. #[arg(long)] rustc_perf_checkout_dir: Option, @@ -88,6 +90,10 @@ enum EnvironmentCmd { #[clap(flatten)] shared: SharedArgs, + + /// Arguments passed to `rustc-perf --cargo-config ` when running benchmarks. + #[arg(long)] + benchmark_cargo_config: Vec, }, /// Perform an optimized build on Linux CI, from inside Docker. LinuxCi { @@ -117,6 +123,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> llvm_shared, use_bolt, skipped_tests, + benchmark_cargo_config, shared, } => { let env = EnvironmentBuilder::default() @@ -130,6 +137,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> .shared_llvm(llvm_shared) .use_bolt(use_bolt) .skipped_tests(skipped_tests) + .benchmark_cargo_config(benchmark_cargo_config) .build()?; (env, shared.build_args) @@ -146,8 +154,6 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> .host_llvm_dir(Utf8PathBuf::from("/rustroot")) .artifact_dir(Utf8PathBuf::from("/tmp/tmp-multistage/opt-artifacts")) .build_dir(checkout_dir.join("obj")) - // /tmp/rustc-perf comes from the x64 dist Dockerfile - .prebuilt_rustc_perf(Some(Utf8PathBuf::from("/tmp/rustc-perf"))) .shared_llvm(true) .use_bolt(true) .skipped_tests(vec![ @@ -191,9 +197,12 @@ fn execute_pipeline( ) -> anyhow::Result<()> { reset_directory(&env.artifact_dir())?; - with_log_group("Building rustc-perf", || match env.prebuilt_rustc_perf() { - Some(dir) => copy_rustc_perf(env, &dir), - None => download_rustc_perf(env), + with_log_group("Building rustc-perf", || { + let rustc_perf_checkout_dir = match env.prebuilt_rustc_perf() { + Some(dir) => dir, + None => env.checkout_path().join("src").join("tools").join("rustc-perf"), + }; + copy_rustc_perf(env, &rustc_perf_checkout_dir) })?; // Stage 1: Build PGO instrumented rustc @@ -409,36 +418,6 @@ fn copy_rustc_perf(env: &Environment, dir: &Utf8Path) -> anyhow::Result<()> { build_rustc_perf(env) } -// Download and build rustc-perf into the given environment. -fn download_rustc_perf(env: &Environment) -> anyhow::Result<()> { - reset_directory(&env.rustc_perf_dir())?; - - // FIXME: add some mechanism for synchronization of this commit SHA with - // Linux (which builds rustc-perf in a Dockerfile) - // rustc-perf version from 2023-10-22 - const PERF_COMMIT: &str = "4f313add609f43e928e98132358e8426ed3969ae"; - - let url = format!("https://ci-mirrors.rust-lang.org/rustc/rustc-perf-{PERF_COMMIT}.zip"); - let client = reqwest::blocking::Client::builder() - .timeout(Duration::from_secs(60 * 2)) - .connect_timeout(Duration::from_secs(60 * 2)) - .build()?; - let response = retry_action( - || Ok(client.get(&url).send()?.error_for_status()?.bytes()?.to_vec()), - "Download rustc-perf archive", - 5, - )?; - - let mut archive = ZipArchive::new(Cursor::new(response))?; - archive.extract(env.rustc_perf_dir())?; - move_directory( - &env.rustc_perf_dir().join(format!("rustc-perf-{PERF_COMMIT}")), - &env.rustc_perf_dir(), - )?; - - build_rustc_perf(env) -} - fn build_rustc_perf(env: &Environment) -> anyhow::Result<()> { cmd(&[env.cargo_stage_0().as_str(), "build", "-p", "collector"]) .workdir(&env.rustc_perf_dir()) diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs index 46b0a5438025..d03d1936e086 100644 --- a/src/tools/opt-dist/src/tests.rs +++ b/src/tools/opt-dist/src/tests.rs @@ -59,26 +59,17 @@ pub fn run_tests(env: &Environment) -> anyhow::Result<()> { .join(format!("llvm-config{}", executable_extension())); assert!(llvm_config.is_file()); - let config_content = format!( - r#"profile = "user" -change-id = 115898 + let rustc = format!("build.rustc={}", rustc_path.to_string().replace('\\', "/")); + let cargo = format!("build.cargo={}", cargo_path.to_string().replace('\\', "/")); + let llvm_config = + format!("target.{host_triple}.llvm-config={}", llvm_config.to_string().replace('\\', "/")); -[build] -rustc = "{rustc}" -cargo = "{cargo}" - -[target.{host_triple}] -llvm-config = "{llvm_config}" -"#, - rustc = rustc_path.to_string().replace('\\', "/"), - cargo = cargo_path.to_string().replace('\\', "/"), - llvm_config = llvm_config.to_string().replace('\\', "/") - ); - log::info!("Using following `config.toml` for running tests:\n{config_content}"); + log::info!("Set the following configurations for running tests:"); + log::info!("\t{rustc}"); + log::info!("\t{cargo}"); + log::info!("\t{llvm_config}"); // Simulate a stage 0 compiler with the extracted optimized dist artifacts. - std::fs::write("config.toml", config_content)?; - let x_py = env.checkout_path().join("x.py"); let mut args = vec![ env.python_binary(), @@ -97,6 +88,12 @@ llvm-config = "{llvm_config}" "tests/run-pass-valgrind", "tests/ui", "tests/crashes", + "--set", + &rustc, + "--set", + &cargo, + "--set", + &llvm_config, ]; for test_path in env.skipped_tests() { args.extend(["--skip", test_path]); diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs index 46040e32a039..1237951b3f04 100644 --- a/src/tools/opt-dist/src/training.rs +++ b/src/tools/opt-dist/src/training.rs @@ -3,30 +3,10 @@ use crate::exec::{cmd, CmdBuilder}; use crate::utils::io::{count_files, delete_directory}; use crate::utils::with_log_group; use anyhow::Context; +use build_helper::{LLVM_PGO_CRATES, RUSTC_PGO_CRATES}; use camino::{Utf8Path, Utf8PathBuf}; use humansize::BINARY; -const LLVM_PGO_CRATES: &[&str] = &[ - "syn-1.0.89", - "cargo-0.60.0", - "serde-1.0.136", - "ripgrep-13.0.0", - "regex-1.5.5", - "clap-3.1.6", - "hyper-0.14.18", -]; - -const RUSTC_PGO_CRATES: &[&str] = &[ - "externs", - "ctfe-stress-5", - "cargo-0.60.0", - "token-stream-stress", - "match-stress", - "tuple-stress", - "diesel-1.4.8", - "bitmaps-3.1.0", -]; - fn init_compiler_benchmarks( env: &Environment, profiles: &[&str], @@ -36,7 +16,7 @@ fn init_compiler_benchmarks( // Run rustc-perf benchmarks // Benchmark using profile_local with eprintln, which essentially just means // don't actually benchmark -- just make sure we run rustc a bunch of times. - cmd(&[ + let mut cmd = cmd(&[ env.cargo_stage_0().as_str(), "run", "-p", @@ -61,7 +41,17 @@ fn init_compiler_benchmarks( .env("RUST_LOG", "collector=debug") .env("RUSTC", env.rustc_stage_0().as_str()) .env("RUSTC_BOOTSTRAP", "1") - .workdir(&env.rustc_perf_dir()) + .workdir(&env.rustc_perf_dir()); + + // This propagates cargo configs to `rustc-perf --cargo-config`, + // which is particularly useful when the environment is air-gapped, + // and you want to use the default set of training crates vendored + // in the rustc-src tarball. + for config in env.benchmark_cargo_config() { + cmd = cmd.arg("--cargo-config").arg(config); + } + + cmd } /// Describes which `llvm-profdata` binary should be used for merging PGO profiles. @@ -226,11 +216,9 @@ pub fn gather_bolt_profiles( log::info!("Profile file count: {}", profiles.len()); // Delete the gathered profiles - for profile in glob::glob(&format!("{profile_prefix}*"))?.into_iter() { - if let Ok(profile) = profile { - if let Err(error) = std::fs::remove_file(&profile) { - log::error!("Cannot delete BOLT profile {}: {error:?}", profile.display()); - } + for profile in glob::glob(&format!("{profile_prefix}*"))?.flatten() { + if let Err(error) = std::fs::remove_file(&profile) { + log::error!("Cannot delete BOLT profile {}: {error:?}", profile.display()); } } diff --git a/src/tools/publish_toolstate.py b/src/tools/publish_toolstate.py index f9421117eaa2..860d21876de0 100755 --- a/src/tools/publish_toolstate.py +++ b/src/tools/publish_toolstate.py @@ -3,8 +3,7 @@ # This script computes the new "current" toolstate for the toolstate repo (not to be # confused with publishing the test results, which happens in `src/bootstrap/toolstate.rs`). -# It gets called from `src/ci/publish_toolstate.sh` when a new commit lands on `master` -# (i.e., after it passed all checks on `auto`). +# It gets called from `src/ci/publish_toolstate.sh` at the end of an `auto` build. from __future__ import print_function diff --git a/src/tools/remote-test-client/src/main.rs b/src/tools/remote-test-client/src/main.rs index 590c735596ed..dd2c09c430b6 100644 --- a/src/tools/remote-test-client/src/main.rs +++ b/src/tools/remote-test-client/src/main.rs @@ -317,13 +317,11 @@ fn run(support_lib_count: usize, exe: String, all_args: Vec) { t!(io::copy(&mut (&mut client).take(amt), &mut stdout)); t!(stdout.flush()); } + } else if amt == 0 { + stderr_done = true; } else { - if amt == 0 { - stderr_done = true; - } else { - t!(io::copy(&mut (&mut client).take(amt), &mut stderr)); - t!(stderr.flush()); - } + t!(io::copy(&mut (&mut client).take(amt), &mut stderr)); + t!(stderr.flush()); } } diff --git a/src/tools/remote-test-server/src/main.rs b/src/tools/remote-test-server/src/main.rs index 68d7aa56c438..79f96c502238 100644 --- a/src/tools/remote-test-server/src/main.rs +++ b/src/tools/remote-test-server/src/main.rs @@ -282,7 +282,7 @@ fn handle_run(socket: TcpStream, work: &Path, tmp: &Path, lock: &Mutex<()>, conf cmd.env(library_path, env::join_paths(paths).unwrap()); // Some tests assume RUST_TEST_TMPDIR exists - cmd.env("RUST_TEST_TMPDIR", tmp.to_owned()); + cmd.env("RUST_TEST_TMPDIR", tmp); let socket = Arc::new(Mutex::new(reader.into_inner())); diff --git a/src/tools/run-make-support/CHANGELOG.md b/src/tools/run-make-support/CHANGELOG.md new file mode 100644 index 000000000000..c1b7b618a921 --- /dev/null +++ b/src/tools/run-make-support/CHANGELOG.md @@ -0,0 +1,83 @@ +# Changelog + +All notable changes to the `run_make_support` library should be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the support +library should adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) even if it's +not intended for public consumption (it's moreso to help internally, to help test writers track +changes to the support library). + +This support library will probably never reach 1.0. Please bump the minor version in `Cargo.toml` if +you make any breaking changes or other significant changes, or bump the patch version for bug fixes. + +## [0.2.0] - 2024-06-11 + +### Added + +- Added `fs_wrapper` module which provides panic-on-fail helpers for their respective `std::fs` + counterparts, the motivation is to: + - Reduce littering `.unwrap()` or `.expect()` everywhere for fs operations + - Help the test writer avoid forgetting to check fs results (even though enforced by + `-Dunused_must_use`) + - Provide better panic messages by default +- Added `path()` helper which creates a `Path` relative to `cwd()` (but is less noisy). + +### Changed + +- Marked many functions with `#[must_use]`, and rmake.rs are now compiled with `-Dunused_must_use`. + +## [0.1.0] - 2024-06-09 + +### Changed + +- Use *drop bombs* to enforce that commands are executed; a command invocation will panic if it is + constructed but never executed. Execution methods `Command::{run, run_fail}` will defuse the drop + bomb. +- Added `Command` helpers that forward to `std::process::Command` counterparts. + +### Removed + +- The `env_var` method which was incorrectly named and is `env_clear` underneath and is a footgun + from `impl_common_helpers`. For example, removing `TMPDIR` on Unix and `TMP`/`TEMP` breaks + `std::env::temp_dir` and wrecks anything using that, such as rustc's codgen. +- Removed `Deref`/`DerefMut` for `run_make_support::Command` -> `std::process::Command` because it + causes a method chain like `htmldocck().arg().run()` to fail, because `arg()` resolves to + `std::process::Command` which also returns a `&mut std::process::Command`, causing the `run()` to + be not found. + +## [0.0.0] - 2024-06-09 + +Consider this version to contain all changes made to the support library before we started to track +changes in this changelog. + +### Added + +- Custom command wrappers around `std::process::Command` (`run_make_support::Command`) and custom + wrapper around `std::process::Output` (`CompletedProcess`) to make it more convenient to work with + commands and their output, and help avoid forgetting to check for exit status. + - `Command`: `set_stdin`, `run`, `run_fail`. + - `CompletedProcess`: `std{err,out}_utf8`, `status`, `assert_std{err,out}_{equals, contains, + not_contains}`, `assert_exit_code`. +- `impl_common_helpers` macro to avoid repeating adding common convenience methods, including: + - Environment manipulation methods: `env`, `env_remove` + - Command argument providers: `arg`, `args` + - Common invocation inspection (of the command invocation up until `inspect` is called): + `inspect` + - Execution methods: `run` (for commands expected to succeed execution, exit status `0`) and + `run_fail` (for commands expected to fail execution, exit status non-zero). +- Command wrappers around: `rustc`, `clang`, `cc`, `rustc`, `rustdoc`, `llvm-readobj`. +- Thin helpers to construct `python` and `htmldocck` commands. +- `run` and `run_fail` (like `Command::{run, run_fail}`) for running binaries, which sets suitable + env vars (like `LD_LIB_PATH` or equivalent, `TARGET_RPATH_ENV`, `PATH` on Windows). +- Pseudo command `diff` which has similar functionality as the cli util but not the same API. +- Convenience panic-on-fail helpers `env_var`, `env_var_os`, `cwd` for their `std::env` conterparts. +- Convenience panic-on-fail helpers for reading respective env vars: `target`, `source_root`. +- Platform check helpers: `is_windows`, `is_msvc`, `cygpath_windows`, `uname`. +- fs helpers: `copy_dir_all`. +- `recursive_diff` helper. +- Generic `assert_not_contains` helper. +- Scoped run-with-teardown helper `run_in_tmpdir` which is designed to run commands in a temporary + directory that is cleared when closure returns. +- Helpers for constructing the name of binaries and libraries: `rust_lib_name`, `static_lib_name`, + `bin_name`, `dynamic_lib_name`. +- Re-export libraries: `gimli`, `object`, `regex`, `wasmparsmer`. diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index cf4ae4b16cd5..2f7f51442f16 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "run_make_support" -version = "0.0.0" +version = "0.2.0" edition = "2021" [dependencies] diff --git a/src/tools/run-make-support/src/cc.rs b/src/tools/run-make-support/src/cc.rs index a67f5c8a9ee4..31b9e8a23b88 100644 --- a/src/tools/run-make-support/src/cc.rs +++ b/src/tools/run-make-support/src/cc.rs @@ -1,13 +1,13 @@ -use std::env; use std::path::Path; -use std::process::Command; -use crate::{bin_name, cygpath_windows, handle_failed_output, is_msvc, is_windows, tmp_dir, uname}; +use crate::command::Command; +use crate::{bin_name, cygpath_windows, env_var, is_msvc, is_windows, uname}; /// Construct a new platform-specific C compiler invocation. /// /// WARNING: This means that what flags are accepted by the underlying C compiler is /// platform- AND compiler-specific. Consult the relevant docs for `gcc`, `clang` and `mvsc`. +#[track_caller] pub fn cc() -> Cc { Cc::new() } @@ -15,6 +15,7 @@ pub fn cc() -> Cc { /// A platform-specific C compiler invocation builder. The specific C compiler used is /// passed down from compiletest. #[derive(Debug)] +#[must_use] pub struct Cc { cmd: Command, } @@ -26,12 +27,13 @@ impl Cc { /// /// WARNING: This means that what flags are accepted by the underlying C compile is /// platform- AND compiler-specific. Consult the relevant docs for `gcc`, `clang` and `mvsc`. + #[track_caller] pub fn new() -> Self { - let compiler = env::var("CC").unwrap(); + let compiler = env_var("CC"); let mut cmd = Command::new(compiler); - let default_cflags = env::var("CC_DEFAULT_FLAGS").unwrap(); + let default_cflags = env_var("CC_DEFAULT_FLAGS"); for flag in default_cflags.split(char::is_whitespace) { cmd.arg(flag); } @@ -45,8 +47,15 @@ impl Cc { self } - /// Specify `-o` or `-Fe`/`-Fo` depending on platform/compiler. This assumes that the executable - /// is under `$TMPDIR`. + /// Adds directories to the list that the linker searches for libraries. + /// Equivalent to `-L`. + pub fn library_search_path>(&mut self, path: P) -> &mut Self { + self.cmd.arg("-L"); + self.cmd.arg(path.as_ref()); + self + } + + /// Specify `-o` or `-Fe`/`-Fo` depending on platform/compiler. pub fn out_exe(&mut self, name: &str) -> &mut Self { // Ref: tools.mk (irrelevant lines omitted): // @@ -60,21 +69,23 @@ impl Cc { // ``` if is_msvc() { - let fe_path = cygpath_windows(tmp_dir().join(bin_name(name))); - let fo_path = cygpath_windows(tmp_dir().join(format!("{name}.obj"))); + let fe_path = cygpath_windows(bin_name(name)); + let fo_path = cygpath_windows(format!("{name}.obj")); self.cmd.arg(format!("-Fe:{fe_path}")); self.cmd.arg(format!("-Fo:{fo_path}")); } else { self.cmd.arg("-o"); - self.cmd.arg(tmp_dir().join(name)); + self.cmd.arg(name); } self } - /// Get the [`Output`][::std::process::Output] of the finished process. - pub fn command_output(&mut self) -> ::std::process::Output { - self.cmd.output().expect("failed to get output of finished process") + /// Specify path of the output binary. + pub fn output>(&mut self, path: P) -> &mut Self { + self.cmd.arg("-o"); + self.cmd.arg(path.as_ref()); + self } } diff --git a/src/tools/run-make-support/src/clang.rs b/src/tools/run-make-support/src/clang.rs index 6ccce67b250d..c23e41ebe219 100644 --- a/src/tools/run-make-support/src/clang.rs +++ b/src/tools/run-make-support/src/clang.rs @@ -1,16 +1,17 @@ -use std::env; use std::path::Path; -use std::process::Command; -use crate::{bin_name, handle_failed_output, tmp_dir}; +use crate::command::Command; +use crate::{bin_name, env_var}; /// Construct a new `clang` invocation. `clang` is not always available for all targets. +#[track_caller] pub fn clang() -> Clang { Clang::new() } /// A `clang` invocation builder. #[derive(Debug)] +#[must_use] pub struct Clang { cmd: Command, } @@ -19,9 +20,9 @@ crate::impl_common_helpers!(Clang); impl Clang { /// Construct a new `clang` invocation. `clang` is not always available for all targets. + #[track_caller] pub fn new() -> Self { - let clang = - env::var("CLANG").expect("`CLANG` not specified, but this is required to find `clang`"); + let clang = env_var("CLANG"); let cmd = Command::new(clang); Self { cmd } } @@ -32,11 +33,11 @@ impl Clang { self } - /// Specify the name of the executable. The executable will be placed under `$TMPDIR`, and the - /// extension will be determined by [`bin_name`]. + /// Specify the name of the executable. The executable will be placed under the current directory + /// and the extension will be determined by [`bin_name`]. pub fn out_exe(&mut self, name: &str) -> &mut Self { self.cmd.arg("-o"); - self.cmd.arg(tmp_dir().join(bin_name(name))); + self.cmd.arg(bin_name(name)); self } @@ -70,9 +71,4 @@ impl Clang { self.cmd.arg(format!("-fuse-ld={ld}")); self } - - /// Get the [`Output`][::std::process::Output] of the finished process. - pub fn command_output(&mut self) -> ::std::process::Output { - self.cmd.output().expect("failed to get output of finished process") - } } diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs new file mode 100644 index 000000000000..f39bcfd60dfb --- /dev/null +++ b/src/tools/run-make-support/src/command.rs @@ -0,0 +1,229 @@ +use std::ffi; +use std::ffi::OsStr; +use std::io::Write; +use std::panic; +use std::path::Path; +use std::process::{Command as StdCommand, ExitStatus, Output, Stdio}; + +use crate::drop_bomb::DropBomb; +use crate::{assert_contains, assert_equals, assert_not_contains, handle_failed_output}; + +/// This is a custom command wrapper that simplifies working with commands and makes it easier to +/// ensure that we check the exit status of executed processes. +/// +/// # A [`Command`] must be executed +/// +/// A [`Command`] is armed by a [`DropBomb`] on construction to enforce that it will be executed. If +/// a [`Command`] is constructed but never executed, the drop bomb will explode and cause the test +/// to panic. Execution methods [`run`] and [`run_fail`] will defuse the drop bomb. A test +/// containing constructed but never executed commands is dangerous because it can give a false +/// sense of confidence. +/// +/// [`run`]: Self::run +/// [`run_fail`]: Self::run_fail +/// [`run_unchecked`]: Self::run_unchecked +#[derive(Debug)] +pub struct Command { + cmd: StdCommand, + stdin: Option>, + drop_bomb: DropBomb, +} + +impl Command { + #[track_caller] + pub fn new>(program: P) -> Self { + let program = program.as_ref(); + Self { cmd: StdCommand::new(program), stdin: None, drop_bomb: DropBomb::arm(program) } + } + + pub fn set_stdin(&mut self, stdin: Box<[u8]>) { + self.stdin = Some(stdin); + } + + /// Specify an environment variable. + pub fn env(&mut self, key: K, value: V) -> &mut Self + where + K: AsRef, + V: AsRef, + { + self.cmd.env(key, value); + self + } + + /// Remove an environmental variable. + pub fn env_remove(&mut self, key: K) -> &mut Self + where + K: AsRef, + { + self.cmd.env_remove(key); + self + } + + /// Generic command argument provider. Prefer specific helper methods if possible. + /// Note that for some executables, arguments might be platform specific. For C/C++ + /// compilers, arguments might be platform *and* compiler specific. + pub fn arg(&mut self, arg: S) -> &mut Self + where + S: AsRef, + { + self.cmd.arg(arg); + self + } + + /// Generic command arguments provider. Prefer specific helper methods if possible. + /// Note that for some executables, arguments might be platform specific. For C/C++ + /// compilers, arguments might be platform *and* compiler specific. + pub fn args(&mut self, args: &[S]) -> &mut Self + where + S: AsRef, + { + self.cmd.args(args); + self + } + + /// Inspect what the underlying [`std::process::Command`] is up to the + /// current construction. + pub fn inspect(&mut self, inspector: I) -> &mut Self + where + I: FnOnce(&StdCommand), + { + inspector(&self.cmd); + self + } + + /// Set the path where the command will be run. + pub fn current_dir>(&mut self, path: P) -> &mut Self { + self.cmd.current_dir(path); + self + } + + /// Run the constructed command and assert that it is successfully run. + #[track_caller] + pub fn run(&mut self) -> CompletedProcess { + let output = self.command_output(); + if !output.status().success() { + handle_failed_output(&self, output, panic::Location::caller().line()); + } + output + } + + /// Run the constructed command and assert that it does not successfully run. + #[track_caller] + pub fn run_fail(&mut self) -> CompletedProcess { + let output = self.command_output(); + if output.status().success() { + handle_failed_output(&self, output, panic::Location::caller().line()); + } + output + } + + /// Run the command but do not check its exit status. + /// Only use if you explicitly don't care about the exit status. + /// Prefer to use [`Self::run`] and [`Self::run_fail`] + /// whenever possible. + #[track_caller] + pub fn run_unchecked(&mut self) -> CompletedProcess { + self.command_output() + } + + #[track_caller] + fn command_output(&mut self) -> CompletedProcess { + self.drop_bomb.defuse(); + // let's make sure we piped all the input and outputs + self.cmd.stdin(Stdio::piped()); + self.cmd.stdout(Stdio::piped()); + self.cmd.stderr(Stdio::piped()); + + let output = if let Some(input) = &self.stdin { + let mut child = self.cmd.spawn().unwrap(); + + { + let mut stdin = child.stdin.take().unwrap(); + stdin.write_all(input.as_ref()).unwrap(); + } + + child.wait_with_output().expect("failed to get output of finished process") + } else { + self.cmd.output().expect("failed to get output of finished process") + }; + output.into() + } +} + +/// Represents the result of an executed process. +/// The various `assert_` helper methods should preferably be used for +/// checking the contents of stdout/stderr. +pub struct CompletedProcess { + output: Output, +} + +impl CompletedProcess { + #[must_use] + pub fn stdout_utf8(&self) -> String { + String::from_utf8(self.output.stdout.clone()).expect("stdout is not valid UTF-8") + } + + #[must_use] + pub fn stderr_utf8(&self) -> String { + String::from_utf8(self.output.stderr.clone()).expect("stderr is not valid UTF-8") + } + + #[must_use] + pub fn status(&self) -> ExitStatus { + self.output.status + } + + /// Checks that trimmed `stdout` matches trimmed `expected`. + #[track_caller] + pub fn assert_stdout_equals>(&self, expected: S) -> &Self { + assert_equals(self.stdout_utf8().trim(), expected.as_ref().trim()); + self + } + + /// Checks that `stdout` does not contain `unexpected`. + #[track_caller] + pub fn assert_stdout_not_contains>(&self, unexpected: S) -> &Self { + assert_not_contains(&self.stdout_utf8(), unexpected.as_ref()); + self + } + + /// Checks that `stdout` contains `expected`. + #[track_caller] + pub fn assert_stdout_contains>(&self, expected: S) -> &Self { + assert_contains(&self.stdout_utf8(), expected.as_ref()); + self + } + + /// Checks that trimmed `stderr` matches trimmed `expected`. + #[track_caller] + pub fn assert_stderr_equals>(&self, expected: S) -> &Self { + assert_equals(self.stderr_utf8().trim(), expected.as_ref().trim()); + self + } + + /// Checks that `stderr` contains `expected`. + #[track_caller] + pub fn assert_stderr_contains>(&self, expected: S) -> &Self { + assert_contains(&self.stderr_utf8(), expected.as_ref()); + self + } + + /// Checks that `stderr` does not contain `unexpected`. + #[track_caller] + pub fn assert_stderr_not_contains>(&self, unexpected: S) -> &Self { + assert_not_contains(&self.stdout_utf8(), unexpected.as_ref()); + self + } + + #[track_caller] + pub fn assert_exit_code(&self, code: i32) -> &Self { + assert!(self.output.status.code() == Some(code)); + self + } +} + +impl From for CompletedProcess { + fn from(output: Output) -> Self { + Self { output } + } +} diff --git a/src/tools/run-make-support/src/diff/mod.rs b/src/tools/run-make-support/src/diff/mod.rs index 332126939c08..3e0bdc1c6f64 100644 --- a/src/tools/run-make-support/src/diff/mod.rs +++ b/src/tools/run-make-support/src/diff/mod.rs @@ -1,41 +1,52 @@ use regex::Regex; use similar::TextDiff; -use std::path::Path; +use std::path::{Path, PathBuf}; + +use crate::drop_bomb::DropBomb; +use crate::fs_wrapper; #[cfg(test)] mod tests; +#[track_caller] pub fn diff() -> Diff { Diff::new() } #[derive(Debug)] +#[must_use] pub struct Diff { expected: Option, expected_name: Option, + expected_file: Option, actual: Option, actual_name: Option, normalizers: Vec<(String, String)>, + drop_bomb: DropBomb, } impl Diff { /// Construct a bare `diff` invocation. + #[track_caller] pub fn new() -> Self { Self { expected: None, expected_name: None, + expected_file: None, actual: None, actual_name: None, normalizers: Vec::new(), + drop_bomb: DropBomb::arm("diff"), } } /// Specify the expected output for the diff from a file. pub fn expected_file>(&mut self, path: P) -> &mut Self { let path = path.as_ref(); - let content = std::fs::read_to_string(path).expect("failed to read file"); + let content = fs_wrapper::read_to_string(path); let name = path.to_string_lossy().to_string(); + self.expected_file = Some(path.into()); self.expected = Some(content); self.expected_name = Some(name); self @@ -51,7 +62,7 @@ impl Diff { /// Specify the actual output for the diff from a file. pub fn actual_file>(&mut self, path: P) -> &mut Self { let path = path.as_ref(); - let content = std::fs::read_to_string(path).expect("failed to read file"); + let content = fs_wrapper::read_to_string(path); let name = path.to_string_lossy().to_string(); self.actual = Some(content); @@ -76,9 +87,9 @@ impl Diff { self } - /// Executes the diff process, prints any differences to the standard error. #[track_caller] - pub fn run(&self) { + pub fn run(&mut self) { + self.drop_bomb.defuse(); let expected = self.expected.as_ref().expect("expected text not set"); let mut actual = self.actual.as_ref().expect("actual text not set").to_string(); let expected_name = self.expected_name.as_ref().unwrap(); @@ -94,6 +105,15 @@ impl Diff { .to_string(); if !output.is_empty() { + // If we can bless (meaning we have a file to write into and the `RUSTC_BLESS_TEST` + // environment variable set), then we write into the file and return. + if let Some(ref expected_file) = self.expected_file { + if std::env::var("RUSTC_BLESS_TEST").is_ok() { + println!("Blessing `{}`", expected_file.display()); + fs_wrapper::write(expected_file, actual); + return; + } + } panic!( "test failed: `{}` is different from `{}`\n\n{}", expected_name, actual_name, output diff --git a/src/tools/run-make-support/src/drop_bomb/mod.rs b/src/tools/run-make-support/src/drop_bomb/mod.rs new file mode 100644 index 000000000000..2fc84892c1bd --- /dev/null +++ b/src/tools/run-make-support/src/drop_bomb/mod.rs @@ -0,0 +1,50 @@ +//! This module implements "drop bombs" intended for use by command wrappers to ensure that the +//! constructed commands are *eventually* executed. This is exactly like `rustc_errors::Diag` where +//! we force every `Diag` to be consumed or we emit a bug, but we panic instead. +//! +//! This is adapted from and simplified for our +//! purposes. + +use std::ffi::{OsStr, OsString}; +use std::panic; + +#[cfg(test)] +mod tests; + +#[derive(Debug)] +pub(crate) struct DropBomb { + command: OsString, + defused: bool, + armed_line: u32, +} + +impl DropBomb { + /// Arm a [`DropBomb`]. If the value is dropped without being [`defused`][Self::defused], then + /// it will panic. It is expected that the command wrapper uses `#[track_caller]` to help + /// propagate the caller info from rmake.rs. + #[track_caller] + pub(crate) fn arm>(command: S) -> DropBomb { + DropBomb { + command: command.as_ref().into(), + defused: false, + armed_line: panic::Location::caller().line(), + } + } + + /// Defuse the [`DropBomb`]. This will prevent the drop bomb from panicking when dropped. + pub(crate) fn defuse(&mut self) { + self.defused = true; + } +} + +impl Drop for DropBomb { + fn drop(&mut self) { + if !self.defused && !std::thread::panicking() { + panic!( + "command constructed but not executed at line {}: `{}`", + self.armed_line, + self.command.to_string_lossy() + ) + } + } +} diff --git a/src/tools/run-make-support/src/drop_bomb/tests.rs b/src/tools/run-make-support/src/drop_bomb/tests.rs new file mode 100644 index 000000000000..4a488c0f6708 --- /dev/null +++ b/src/tools/run-make-support/src/drop_bomb/tests.rs @@ -0,0 +1,15 @@ +use super::DropBomb; + +#[test] +#[should_panic] +fn test_arm() { + let bomb = DropBomb::arm("hi :3"); + drop(bomb); // <- armed bomb should explode when not defused +} + +#[test] +fn test_defuse() { + let mut bomb = DropBomb::arm("hi :3"); + bomb.defuse(); + drop(bomb); // <- defused bomb should not explode +} diff --git a/src/tools/run-make-support/src/fs_wrapper.rs b/src/tools/run-make-support/src/fs_wrapper.rs new file mode 100644 index 000000000000..8a2bfce8b4a7 --- /dev/null +++ b/src/tools/run-make-support/src/fs_wrapper.rs @@ -0,0 +1,113 @@ +use std::fs; +use std::path::Path; + +/// A wrapper around [`std::fs::remove_file`] which includes the file path in the panic message.. +#[track_caller] +pub fn remove_file>(path: P) { + fs::remove_file(path.as_ref()) + .expect(&format!("the file in path \"{}\" could not be removed", path.as_ref().display())); +} + +/// A wrapper around [`std::fs::copy`] which includes the file path in the panic message. +#[track_caller] +pub fn copy, Q: AsRef>(from: P, to: Q) { + fs::copy(from.as_ref(), to.as_ref()).expect(&format!( + "the file \"{}\" could not be copied over to \"{}\"", + from.as_ref().display(), + to.as_ref().display(), + )); +} + +/// A wrapper around [`std::fs::File::create`] which includes the file path in the panic message.. +#[track_caller] +pub fn create_file>(path: P) { + fs::File::create(path.as_ref()) + .expect(&format!("the file in path \"{}\" could not be created", path.as_ref().display())); +} + +/// A wrapper around [`std::fs::read`] which includes the file path in the panic message.. +#[track_caller] +pub fn read>(path: P) -> Vec { + fs::read(path.as_ref()) + .expect(&format!("the file in path \"{}\" could not be read", path.as_ref().display())) +} + +/// A wrapper around [`std::fs::read_to_string`] which includes the file path in the panic message.. +#[track_caller] +pub fn read_to_string>(path: P) -> String { + fs::read_to_string(path.as_ref()).expect(&format!( + "the file in path \"{}\" could not be read into a String", + path.as_ref().display() + )) +} + +/// A wrapper around [`std::fs::read_dir`] which includes the file path in the panic message.. +#[track_caller] +pub fn read_dir>(path: P) -> fs::ReadDir { + fs::read_dir(path.as_ref()) + .expect(&format!("the directory in path \"{}\" could not be read", path.as_ref().display())) +} + +/// A wrapper around [`std::fs::write`] which includes the file path in the panic message.. +#[track_caller] +pub fn write, C: AsRef<[u8]>>(path: P, contents: C) { + fs::write(path.as_ref(), contents.as_ref()).expect(&format!( + "the file in path \"{}\" could not be written to", + path.as_ref().display() + )); +} + +/// A wrapper around [`std::fs::remove_dir_all`] which includes the file path in the panic message.. +#[track_caller] +pub fn remove_dir_all>(path: P) { + fs::remove_dir_all(path.as_ref()).expect(&format!( + "the directory in path \"{}\" could not be removed alongside all its contents", + path.as_ref().display(), + )); +} + +/// A wrapper around [`std::fs::create_dir`] which includes the file path in the panic message.. +#[track_caller] +pub fn create_dir>(path: P) { + fs::create_dir(path.as_ref()).expect(&format!( + "the directory in path \"{}\" could not be created", + path.as_ref().display() + )); +} + +/// A wrapper around [`std::fs::create_dir_all`] which includes the file path in the panic message.. +#[track_caller] +pub fn create_dir_all>(path: P) { + fs::create_dir_all(path.as_ref()).expect(&format!( + "the directory (and all its parents) in path \"{}\" could not be created", + path.as_ref().display() + )); +} + +/// A wrapper around [`std::fs::metadata`] which includes the file path in the panic message.. +#[track_caller] +pub fn metadata>(path: P) -> fs::Metadata { + fs::metadata(path.as_ref()).expect(&format!( + "the file's metadata in path \"{}\" could not be read", + path.as_ref().display() + )) +} + +/// A wrapper around [`std::fs::rename`] which includes the file path in the panic message. +#[track_caller] +pub fn rename, Q: AsRef>(from: P, to: Q) { + fs::rename(from.as_ref(), to.as_ref()).expect(&format!( + "the file \"{}\" could not be moved over to \"{}\"", + from.as_ref().display(), + to.as_ref().display(), + )); +} + +/// A wrapper around [`std::fs::set_permissions`] which includes the file path in the panic message. +#[track_caller] +pub fn set_permissions>(path: P, perm: fs::Permissions) { + fs::set_permissions(path.as_ref(), perm).expect(&format!( + "the file's permissions in path \"{}\" could not be changed", + path.as_ref().display() + )); +} diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 9854d91e19e3..f4c101cf81c4 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -5,15 +5,21 @@ pub mod cc; pub mod clang; +mod command; pub mod diff; -pub mod llvm_readobj; +mod drop_bomb; +pub mod fs_wrapper; +pub mod llvm; pub mod run; pub mod rustc; pub mod rustdoc; use std::env; +use std::ffi::OsString; +use std::fs; +use std::io; +use std::panic; use std::path::{Path, PathBuf}; -use std::process::{Command, Output}; pub use gimli; pub use object; @@ -23,58 +29,112 @@ pub use wasmparser; pub use cc::{cc, extra_c_flags, extra_cxx_flags, Cc}; pub use clang::{clang, Clang}; pub use diff::{diff, Diff}; -pub use llvm_readobj::{llvm_readobj, LlvmReadobj}; -pub use run::{run, run_fail}; +pub use llvm::{ + llvm_filecheck, llvm_objdump, llvm_profdata, llvm_readobj, LlvmFilecheck, LlvmObjdump, + LlvmProfdata, LlvmReadobj, +}; +pub use run::{cmd, run, run_fail, run_with_args}; pub use rustc::{aux_build, rustc, Rustc}; pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc}; -/// Path of `TMPDIR` (a temporary build directory, not under `/tmp`). -pub fn tmp_dir() -> PathBuf { - env::var_os("TMPDIR").unwrap().into() +#[track_caller] +#[must_use] +pub fn env_var(name: &str) -> String { + match env::var(name) { + Ok(v) => v, + Err(err) => panic!("failed to retrieve environment variable {name:?}: {err:?}"), + } +} + +#[track_caller] +#[must_use] +pub fn env_var_os(name: &str) -> OsString { + match env::var_os(name) { + Some(v) => v, + None => panic!("failed to retrieve environment variable {name:?}"), + } } /// `TARGET` +#[must_use] pub fn target() -> String { - env::var("TARGET").unwrap() + env_var("TARGET") } /// Check if target is windows-like. +#[must_use] pub fn is_windows() -> bool { target().contains("windows") } /// Check if target uses msvc. +#[must_use] pub fn is_msvc() -> bool { target().contains("msvc") } /// Check if target uses macOS. +#[must_use] pub fn is_darwin() -> bool { target().contains("darwin") } -/// Construct a path to a static library under `$TMPDIR` given the library name. This will return a -/// path with `$TMPDIR` joined with platform-and-compiler-specific library name. -pub fn static_lib(name: &str) -> PathBuf { - tmp_dir().join(static_lib_name(name)) -} - +#[track_caller] +#[must_use] pub fn python_command() -> Command { - let python_path = std::env::var("PYTHON").expect("PYTHON environment variable does not exist"); + let python_path = env_var("PYTHON"); Command::new(python_path) } +#[track_caller] +#[must_use] pub fn htmldocck() -> Command { let mut python = python_command(); - python.arg(source_path().join("src/etc/htmldocck.py")); + python.arg(source_root().join("src/etc/htmldocck.py")); python } -pub fn source_path() -> PathBuf { - std::env::var("S").expect("S variable does not exist").into() +/// Returns the path for a local test file. +pub fn path>(p: P) -> PathBuf { + cwd().join(p.as_ref()) +} + +/// Path to the root rust-lang/rust source checkout. +#[must_use] +pub fn source_root() -> PathBuf { + env_var("SOURCE_ROOT").into() +} + +/// Creates a new symlink to a path on the filesystem, adjusting for Windows or Unix. +#[cfg(target_family = "windows")] +pub fn create_symlink, Q: AsRef>(original: P, link: Q) { + if link.as_ref().exists() { + std::fs::remove_dir(link.as_ref()).unwrap(); + } + use std::os::windows::fs; + fs::symlink_file(original.as_ref(), link.as_ref()).expect(&format!( + "failed to create symlink {:?} for {:?}", + link.as_ref().display(), + original.as_ref().display(), + )); +} + +/// Creates a new symlink to a path on the filesystem, adjusting for Windows or Unix. +#[cfg(target_family = "unix")] +pub fn create_symlink, Q: AsRef>(original: P, link: Q) { + if link.as_ref().exists() { + std::fs::remove_dir(link.as_ref()).unwrap(); + } + use std::os::unix::fs; + fs::symlink(original.as_ref(), link.as_ref()).expect(&format!( + "failed to create symlink {:?} for {:?}", + link.as_ref().display(), + original.as_ref().display(), + )); } /// Construct the static library name based on the platform. +#[must_use] pub fn static_lib_name(name: &str) -> String { // See tools.mk (irrelevant lines omitted): // @@ -98,13 +158,8 @@ pub fn static_lib_name(name: &str) -> String { if is_msvc() { format!("{name}.lib") } else { format!("lib{name}.a") } } -/// Construct a path to a dynamic library under `$TMPDIR` given the library name. This will return a -/// path with `$TMPDIR` joined with platform-and-compiler-specific library name. -pub fn dynamic_lib(name: &str) -> PathBuf { - tmp_dir().join(dynamic_lib_name(name)) -} - /// Construct the dynamic library name based on the platform. +#[must_use] pub fn dynamic_lib_name(name: &str) -> String { // See tools.mk (irrelevant lines omitted): // @@ -121,119 +176,305 @@ pub fn dynamic_lib_name(name: &str) -> String { // ``` assert!(!name.contains(char::is_whitespace), "dynamic library name cannot contain whitespace"); + let extension = dynamic_lib_extension(); if is_darwin() { - format!("lib{name}.dylib") + format!("lib{name}.{extension}") } else if is_windows() { - format!("{name}.dll") + format!("{name}.{extension}") } else { - format!("lib{name}.so") + format!("lib{name}.{extension}") } } -/// Construct a path to a rust library (rlib) under `$TMPDIR` given the library name. This will return a -/// path with `$TMPDIR` joined with the library name. -pub fn rust_lib(name: &str) -> PathBuf { - tmp_dir().join(format!("lib{name}.rlib")) +#[must_use] +pub fn dynamic_lib_extension() -> &'static str { + if is_darwin() { + "dylib" + } else if is_windows() { + "dll" + } else { + "so" + } +} + +/// Generate the name a rust library (rlib) would have. +#[must_use] +pub fn rust_lib_name(name: &str) -> String { + format!("lib{name}.rlib") } /// Construct the binary name based on platform. +#[must_use] pub fn bin_name(name: &str) -> String { if is_windows() { format!("{name}.exe") } else { name.to_string() } } +/// Return the current working directory. +#[must_use] +pub fn cwd() -> PathBuf { + env::current_dir().unwrap() +} + +// FIXME(Oneirical): This will no longer be required after compiletest receives the ability +// to manipulate read-only files. See https://github.com/rust-lang/rust/issues/126334 +/// Ensure that the path P is read-only while the test runs, and restore original permissions +/// at the end so compiletest can clean up. +/// This will panic on Windows if the path is a directory (as it would otherwise do nothing) +#[track_caller] +pub fn test_while_readonly, F: FnOnce() + std::panic::UnwindSafe>( + path: P, + closure: F, +) { + let path = path.as_ref(); + if is_windows() && path.is_dir() { + eprintln!("This helper function cannot be used on Windows to make directories readonly."); + eprintln!( + "See the official documentation: + https://doc.rust-lang.org/std/fs/struct.Permissions.html#method.set_readonly" + ); + panic!("`test_while_readonly` on directory detected while on Windows."); + } + let metadata = fs_wrapper::metadata(&path); + let original_perms = metadata.permissions(); + + let mut new_perms = original_perms.clone(); + new_perms.set_readonly(true); + fs_wrapper::set_permissions(&path, new_perms); + + let success = std::panic::catch_unwind(closure); + + fs_wrapper::set_permissions(&path, original_perms); + success.unwrap(); +} + /// Use `cygpath -w` on a path to get a Windows path string back. This assumes that `cygpath` is /// available on the platform! #[track_caller] +#[must_use] pub fn cygpath_windows>(path: P) -> String { - let caller_location = std::panic::Location::caller(); - let caller_line_number = caller_location.line(); - + let caller = panic::Location::caller(); let mut cygpath = Command::new("cygpath"); cygpath.arg("-w"); cygpath.arg(path.as_ref()); - let output = cygpath.output().unwrap(); - if !output.status.success() { - handle_failed_output(&cygpath, output, caller_line_number); + let output = cygpath.run(); + if !output.status().success() { + handle_failed_output(&cygpath, output, caller.line()); } - let s = String::from_utf8(output.stdout).unwrap(); // cygpath -w can attach a newline - s.trim().to_string() + output.stdout_utf8().trim().to_string() } /// Run `uname`. This assumes that `uname` is available on the platform! #[track_caller] +#[must_use] pub fn uname() -> String { - let caller_location = std::panic::Location::caller(); - let caller_line_number = caller_location.line(); - + let caller = panic::Location::caller(); let mut uname = Command::new("uname"); - let output = uname.output().unwrap(); - if !output.status.success() { - handle_failed_output(&uname, output, caller_line_number); + let output = uname.run(); + if !output.status().success() { + handle_failed_output(&uname, output, caller.line()); } - String::from_utf8(output.stdout).unwrap() + output.stdout_utf8() } -fn handle_failed_output(cmd: &Command, output: Output, caller_line_number: u32) -> ! { - if output.status.success() { +fn handle_failed_output(cmd: &Command, output: CompletedProcess, caller_line_number: u32) -> ! { + if output.status().success() { eprintln!("command unexpectedly succeeded at line {caller_line_number}"); } else { eprintln!("command failed at line {caller_line_number}"); } eprintln!("{cmd:?}"); - eprintln!("output status: `{}`", output.status); - eprintln!("=== STDOUT ===\n{}\n\n", String::from_utf8(output.stdout).unwrap()); - eprintln!("=== STDERR ===\n{}\n\n", String::from_utf8(output.stderr).unwrap()); + eprintln!("output status: `{}`", output.status()); + eprintln!("=== STDOUT ===\n{}\n\n", output.stdout_utf8()); + eprintln!("=== STDERR ===\n{}\n\n", output.stderr_utf8()); std::process::exit(1) } /// Set the runtime library path as needed for running the host rustc/rustdoc/etc. pub fn set_host_rpath(cmd: &mut Command) { - let ld_lib_path_envvar = env::var("LD_LIB_PATH_ENVVAR").unwrap(); + let ld_lib_path_envvar = env_var("LD_LIB_PATH_ENVVAR"); cmd.env(&ld_lib_path_envvar, { let mut paths = vec![]; - paths.push(PathBuf::from(env::var("TMPDIR").unwrap())); - paths.push(PathBuf::from(env::var("HOST_RPATH_DIR").unwrap())); - for p in env::split_paths(&env::var(&ld_lib_path_envvar).unwrap()) { + paths.push(cwd()); + paths.push(PathBuf::from(env_var("HOST_RPATH_DIR"))); + for p in env::split_paths(&env_var(&ld_lib_path_envvar)) { paths.push(p.to_path_buf()); } env::join_paths(paths.iter()).unwrap() }); } +/// Read the contents of a file that cannot simply be read by +/// read_to_string, due to invalid utf8 data, then assert that it contains `expected`. +#[track_caller] +pub fn invalid_utf8_contains>(path: P, expected: &str) { + let buffer = fs_wrapper::read(path.as_ref()); + if !String::from_utf8_lossy(&buffer).contains(expected) { + eprintln!("=== FILE CONTENTS (LOSSY) ==="); + eprintln!("{}", String::from_utf8_lossy(&buffer)); + eprintln!("=== SPECIFIED TEXT ==="); + eprintln!("{}", expected); + panic!("specified text was not found in file"); + } +} + +/// Read the contents of a file that cannot simply be read by +/// read_to_string, due to invalid utf8 data, then assert that it does not contain `expected`. +#[track_caller] +pub fn invalid_utf8_not_contains>(path: P, expected: &str) { + let buffer = fs_wrapper::read(path.as_ref()); + if String::from_utf8_lossy(&buffer).contains(expected) { + eprintln!("=== FILE CONTENTS (LOSSY) ==="); + eprintln!("{}", String::from_utf8_lossy(&buffer)); + eprintln!("=== SPECIFIED TEXT ==="); + eprintln!("{}", expected); + panic!("specified text was unexpectedly found in file"); + } +} + +/// Copy a directory into another. +pub fn copy_dir_all(src: impl AsRef, dst: impl AsRef) { + fn copy_dir_all_inner(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { + let dst = dst.as_ref(); + if !dst.is_dir() { + std::fs::create_dir_all(&dst)?; + } + for entry in std::fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_all_inner(entry.path(), dst.join(entry.file_name()))?; + } else { + std::fs::copy(entry.path(), dst.join(entry.file_name()))?; + } + } + Ok(()) + } + + if let Err(e) = copy_dir_all_inner(&src, &dst) { + // Trying to give more context about what exactly caused the failure + panic!( + "failed to copy `{}` to `{}`: {:?}", + src.as_ref().display(), + dst.as_ref().display(), + e + ); + } +} + +/// Check that all files in `dir1` exist and have the same content in `dir2`. Panic otherwise. +pub fn recursive_diff(dir1: impl AsRef, dir2: impl AsRef) { + let dir2 = dir2.as_ref(); + read_dir(dir1, |entry_path| { + let entry_name = entry_path.file_name().unwrap(); + if entry_path.is_dir() { + recursive_diff(&entry_path, &dir2.join(entry_name)); + } else { + let path2 = dir2.join(entry_name); + let file1 = fs_wrapper::read(&entry_path); + let file2 = fs_wrapper::read(&path2); + + // We don't use `assert_eq!` because they are `Vec`, so not great for display. + // Why not using String? Because there might be minified files or even potentially + // binary ones, so that would display useless output. + assert!( + file1 == file2, + "`{}` and `{}` have different content", + entry_path.display(), + path2.display(), + ); + } + }); +} + +pub fn read_dir(dir: impl AsRef, callback: F) { + for entry in fs_wrapper::read_dir(dir) { + callback(&entry.unwrap().path()); + } +} + +/// Check that `actual` is equal to `expected`. Panic otherwise. +#[track_caller] +pub fn assert_equals(actual: &str, expected: &str) { + if actual != expected { + eprintln!("=== ACTUAL TEXT ==="); + eprintln!("{}", actual); + eprintln!("=== EXPECTED ==="); + eprintln!("{}", expected); + panic!("expected text was not found in actual text"); + } +} + +/// Check that `haystack` contains `needle`. Panic otherwise. +#[track_caller] +pub fn assert_contains(haystack: &str, needle: &str) { + if !haystack.contains(needle) { + eprintln!("=== HAYSTACK ==="); + eprintln!("{}", haystack); + eprintln!("=== NEEDLE ==="); + eprintln!("{}", needle); + panic!("needle was not found in haystack"); + } +} + +/// Check that `haystack` does not contain `needle`. Panic otherwise. +#[track_caller] +pub fn assert_not_contains(haystack: &str, needle: &str) { + if haystack.contains(needle) { + eprintln!("=== HAYSTACK ==="); + eprintln!("{}", haystack); + eprintln!("=== NEEDLE ==="); + eprintln!("{}", needle); + panic!("needle was unexpectedly found in haystack"); + } +} + +/// This function is designed for running commands in a temporary directory +/// that is cleared after the function ends. +/// +/// What this function does: +/// 1) Creates a temporary directory (`tmpdir`) +/// 2) Copies all files from the current directory to `tmpdir` +/// 3) Changes the current working directory to `tmpdir` +/// 4) Calls `callback` +/// 5) Switches working directory back to the original one +/// 6) Removes `tmpdir` +pub fn run_in_tmpdir(callback: F) { + let original_dir = cwd(); + let tmpdir = original_dir.join("../temporary-directory"); + copy_dir_all(".", &tmpdir); + + env::set_current_dir(&tmpdir).unwrap(); + callback(); + env::set_current_dir(original_dir).unwrap(); + fs::remove_dir_all(tmpdir).unwrap(); +} + /// Implement common helpers for command wrappers. This assumes that the command wrapper is a struct -/// containing a `cmd: Command` field and a `output` function. The provided helpers are: +/// containing a `cmd: Command` field. The provided helpers are: /// /// 1. Generic argument acceptors: `arg` and `args` (delegated to [`Command`]). These are intended /// to be *fallback* argument acceptors, when specific helpers don't make sense. Prefer to add /// new specific helper methods over relying on these generic argument providers. /// 2. Environment manipulation methods: `env`, `env_remove` and `env_clear`: these delegate to /// methods of the same name on [`Command`]. -/// 3. Output and execution: `output`, `run` and `run_fail` are provided. `output` waits for the -/// command to finish running and returns the process's [`Output`]. `run` and `run_fail` are -/// higher-level convenience methods which waits for the command to finish running and assert -/// that the command successfully ran or failed as expected. Prefer `run` and `run_fail` when -/// possible. +/// 3. Output and execution: `run` and `run_fail` are provided. These are +/// higher-level convenience methods which wait for the command to finish running and assert +/// that the command successfully ran or failed as expected. They return +/// [`CompletedProcess`], which can be used to assert the stdout/stderr/exit code of the executed +/// process. /// /// Example usage: /// /// ```ignore (illustrative) /// struct CommandWrapper { cmd: Command } // <- required `cmd` field /// -/// impl CommandWrapper { -/// /// Get the [`Output`][::std::process::Output] of the finished process. -/// pub fn command_output(&mut self) -> Output { /* ... */ } // <- required `command_output()` method -/// } -/// /// crate::impl_common_helpers!(CommandWrapper); /// /// impl CommandWrapper { /// // ... additional specific helper methods /// } /// ``` -/// -/// [`Command`]: ::std::process::Command -/// [`Output`]: ::std::process::Output macro_rules! impl_common_helpers { ($wrapper: ident) => { impl $wrapper { @@ -256,12 +497,6 @@ macro_rules! impl_common_helpers { self } - /// Clear all environmental variables. - pub fn env_var(&mut self) -> &mut Self { - self.cmd.env_clear(); - self - } - /// Generic command argument provider. Prefer specific helper methods if possible. /// Note that for some executables, arguments might be platform specific. For C/C++ /// compilers, arguments might be platform *and* compiler specific. @@ -284,44 +519,39 @@ macro_rules! impl_common_helpers { self } - /// Inspect what the underlying [`Command`][::std::process::Command] is up to the + /// Inspect what the underlying [`Command`] is up to the /// current construction. pub fn inspect(&mut self, inspector: I) -> &mut Self where I: FnOnce(&::std::process::Command), { - inspector(&self.cmd); + self.cmd.inspect(inspector); self } /// Run the constructed command and assert that it is successfully run. #[track_caller] - pub fn run(&mut self) -> ::std::process::Output { - let caller_location = ::std::panic::Location::caller(); - let caller_line_number = caller_location.line(); - - let output = self.command_output(); - if !output.status.success() { - handle_failed_output(&self.cmd, output, caller_line_number); - } - output + pub fn run(&mut self) -> crate::command::CompletedProcess { + self.cmd.run() } /// Run the constructed command and assert that it does not successfully run. #[track_caller] - pub fn run_fail(&mut self) -> ::std::process::Output { - let caller_location = ::std::panic::Location::caller(); - let caller_line_number = caller_location.line(); + pub fn run_fail(&mut self) -> crate::command::CompletedProcess { + self.cmd.run_fail() + } - let output = self.command_output(); - if output.status.success() { - handle_failed_output(&self.cmd, output, caller_line_number); - } - output + /// Run the command but do not check its exit status. + /// Only use if you explicitly don't care about the exit status. + /// Prefer to use [`Self::run`] and [`Self::run_fail`] + /// whenever possible. + #[track_caller] + pub fn run_unchecked(&mut self) -> crate::command::CompletedProcess { + self.cmd.run_unchecked() } /// Set the path where the command will be run. - pub fn current_dir>(&mut self, path: P) -> &mut Self { + pub fn current_dir>(&mut self, path: P) -> &mut Self { self.cmd.current_dir(path); self } @@ -329,4 +559,5 @@ macro_rules! impl_common_helpers { }; } +use crate::command::{Command, CompletedProcess}; pub(crate) use impl_common_helpers; diff --git a/src/tools/run-make-support/src/llvm.rs b/src/tools/run-make-support/src/llvm.rs new file mode 100644 index 000000000000..99bce08fc238 --- /dev/null +++ b/src/tools/run-make-support/src/llvm.rs @@ -0,0 +1,181 @@ +use std::path::{Path, PathBuf}; + +use crate::{env_var, Command}; + +/// Construct a new `llvm-readobj` invocation with the `GNU` output style. +/// This assumes that `llvm-readobj` is available at `$LLVM_BIN_DIR/llvm-readobj`. +#[track_caller] +pub fn llvm_readobj() -> LlvmReadobj { + LlvmReadobj::new() +} + +/// Construct a new `llvm-profdata` invocation. This assumes that `llvm-profdata` is available +/// at `$LLVM_BIN_DIR/llvm-profdata`. +#[track_caller] +pub fn llvm_profdata() -> LlvmProfdata { + LlvmProfdata::new() +} + +/// Construct a new `llvm-filecheck` invocation. This assumes that `llvm-filecheck` is available +/// at `$LLVM_FILECHECK`. +#[track_caller] +pub fn llvm_filecheck() -> LlvmFilecheck { + LlvmFilecheck::new() +} + +/// Construct a new `llvm-objdump` invocation. This assumes that `llvm-objdump` is available +/// at `$LLVM_BIN_DIR/llvm-objdump`. +pub fn llvm_objdump() -> LlvmObjdump { + LlvmObjdump::new() +} + +/// A `llvm-readobj` invocation builder. +#[derive(Debug)] +#[must_use] +pub struct LlvmReadobj { + cmd: Command, +} + +/// A `llvm-profdata` invocation builder. +#[derive(Debug)] +#[must_use] +pub struct LlvmProfdata { + cmd: Command, +} + +/// A `llvm-filecheck` invocation builder. +#[derive(Debug)] +#[must_use] +pub struct LlvmFilecheck { + cmd: Command, +} + +/// A `llvm-objdump` invocation builder. +#[derive(Debug)] +#[must_use] +pub struct LlvmObjdump { + cmd: Command, +} + +crate::impl_common_helpers!(LlvmReadobj); +crate::impl_common_helpers!(LlvmProfdata); +crate::impl_common_helpers!(LlvmFilecheck); +crate::impl_common_helpers!(LlvmObjdump); + +/// Generate the path to the bin directory of LLVM. +#[must_use] +pub fn llvm_bin_dir() -> PathBuf { + let llvm_bin_dir = env_var("LLVM_BIN_DIR"); + PathBuf::from(llvm_bin_dir) +} + +impl LlvmReadobj { + /// Construct a new `llvm-readobj` invocation with the `GNU` output style. + /// This assumes that `llvm-readobj` is available at `$LLVM_BIN_DIR/llvm-readobj`. + #[track_caller] + pub fn new() -> Self { + let llvm_readobj = llvm_bin_dir().join("llvm-readobj"); + let cmd = Command::new(llvm_readobj); + let mut readobj = Self { cmd }; + readobj.elf_output_style("GNU"); + readobj + } + + /// Specify the format of the ELF information. + /// + /// Valid options are `LLVM` (default), `GNU`, and `JSON`. + pub fn elf_output_style(&mut self, style: &str) -> &mut Self { + self.cmd.arg("--elf-output-style"); + self.cmd.arg(style); + self + } + + /// Provide an input file. + pub fn input>(&mut self, path: P) -> &mut Self { + self.cmd.arg(path.as_ref()); + self + } + + /// Pass `--file-header` to display file headers. + pub fn file_header(&mut self) -> &mut Self { + self.cmd.arg("--file-header"); + self + } + + /// Specify the section to display. + pub fn section(&mut self, section: &str) -> &mut Self { + self.cmd.arg("--string-dump"); + self.cmd.arg(section); + self + } +} + +impl LlvmProfdata { + /// Construct a new `llvm-profdata` invocation. This assumes that `llvm-profdata` is available + /// at `$LLVM_BIN_DIR/llvm-profdata`. + #[track_caller] + pub fn new() -> Self { + let llvm_profdata = llvm_bin_dir().join("llvm-profdata"); + let cmd = Command::new(llvm_profdata); + Self { cmd } + } + + /// Provide an input file. + pub fn input>(&mut self, path: P) -> &mut Self { + self.cmd.arg(path.as_ref()); + self + } + + /// Specify the output file path. + pub fn output>(&mut self, path: P) -> &mut Self { + self.cmd.arg("-o"); + self.cmd.arg(path.as_ref()); + self + } + + /// Take several profile data files generated by PGO instrumentation and merge them + /// together into a single indexed profile data file. + pub fn merge(&mut self) -> &mut Self { + self.cmd.arg("merge"); + self + } +} + +impl LlvmFilecheck { + /// Construct a new `llvm-filecheck` invocation. This assumes that `llvm-filecheck` is available + /// at `$LLVM_FILECHECK`. + #[track_caller] + pub fn new() -> Self { + let llvm_filecheck = env_var("LLVM_FILECHECK"); + let cmd = Command::new(llvm_filecheck); + Self { cmd } + } + + /// Pipe a read file into standard input containing patterns that will be matched against the .patterns(path) call. + pub fn stdin>(&mut self, input: I) -> &mut Self { + self.cmd.set_stdin(input.as_ref().to_vec().into_boxed_slice()); + self + } + + /// Provide the patterns that need to be matched. + pub fn patterns>(&mut self, path: P) -> &mut Self { + self.cmd.arg(path.as_ref()); + self + } +} + +impl LlvmObjdump { + /// Construct a new `llvm-objdump` invocation. This assumes that `llvm-objdump` is available + /// at `$LLVM_BIN_DIR/llvm-objdump`. + pub fn new() -> Self { + let llvm_objdump = llvm_bin_dir().join("llvm-objdump"); + let cmd = Command::new(llvm_objdump); + Self { cmd } + } + + /// Provide an input file. + pub fn input>(&mut self, path: P) -> &mut Self { + self.cmd.arg(path.as_ref()); + self + } +} diff --git a/src/tools/run-make-support/src/llvm_readobj.rs b/src/tools/run-make-support/src/llvm_readobj.rs deleted file mode 100644 index f114aacfa3fc..000000000000 --- a/src/tools/run-make-support/src/llvm_readobj.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::env; -use std::path::{Path, PathBuf}; -use std::process::Command; - -use crate::handle_failed_output; - -/// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available -/// at `$LLVM_BIN_DIR/llvm-readobj`. -pub fn llvm_readobj() -> LlvmReadobj { - LlvmReadobj::new() -} - -/// A `llvm-readobj` invocation builder. -#[derive(Debug)] -pub struct LlvmReadobj { - cmd: Command, -} - -crate::impl_common_helpers!(LlvmReadobj); - -impl LlvmReadobj { - /// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available - /// at `$LLVM_BIN_DIR/llvm-readobj`. - pub fn new() -> Self { - let llvm_bin_dir = env::var("LLVM_BIN_DIR") - .expect("`LLVM_BIN_DIR` not specified, but this is required to find `llvm-readobj`"); - let llvm_bin_dir = PathBuf::from(llvm_bin_dir); - let llvm_readobj = llvm_bin_dir.join("llvm-readobj"); - let cmd = Command::new(llvm_readobj); - Self { cmd } - } - - /// Provide an input file. - pub fn input>(&mut self, path: P) -> &mut Self { - self.cmd.arg(path.as_ref()); - self - } - - /// Pass `--file-header` to display file headers. - pub fn file_header(&mut self) -> &mut Self { - self.cmd.arg("--file-header"); - self - } - - /// Get the [`Output`][::std::process::Output] of the finished process. - #[track_caller] - pub fn command_output(&mut self) -> ::std::process::Output { - self.cmd.output().expect("failed to get output of finished process") - } -} diff --git a/src/tools/run-make-support/src/run.rs b/src/tools/run-make-support/src/run.rs index 9aad91f1b463..54730bb7de73 100644 --- a/src/tools/run-make-support/src/run.rs +++ b/src/tools/run-make-support/src/run.rs @@ -1,24 +1,32 @@ use std::env; +use std::ffi::OsStr; +use std::panic; use std::path::{Path, PathBuf}; -use std::process::{Command, Output}; -use crate::is_windows; +use crate::command::{Command, CompletedProcess}; +use crate::{cwd, env_var, is_windows, set_host_rpath}; -use super::{bin_name, handle_failed_output}; +use super::handle_failed_output; -fn run_common(name: &str) -> (Command, Output) { +#[track_caller] +fn run_common(name: &str, args: Option<&[&str]>) -> Command { let mut bin_path = PathBuf::new(); - bin_path.push(env::var("TMPDIR").unwrap()); - bin_path.push(&bin_name(name)); - let ld_lib_path_envvar = env::var("LD_LIB_PATH_ENVVAR").unwrap(); + bin_path.push(cwd()); + bin_path.push(name); + let ld_lib_path_envvar = env_var("LD_LIB_PATH_ENVVAR"); let mut cmd = Command::new(bin_path); + if let Some(args) = args { + for arg in args { + cmd.arg(arg); + } + } cmd.env(&ld_lib_path_envvar, { let mut paths = vec![]; - paths.push(PathBuf::from(env::var("TMPDIR").unwrap())); - for p in env::split_paths(&env::var("TARGET_RPATH_ENV").unwrap()) { + paths.push(cwd()); + for p in env::split_paths(&env_var("TARGET_RPATH_ENV")) { paths.push(p.to_path_buf()); } - for p in env::split_paths(&env::var(&ld_lib_path_envvar).unwrap()) { + for p in env::split_paths(&env_var(&ld_lib_path_envvar)) { paths.push(p.to_path_buf()); } env::join_paths(paths.iter()).unwrap() @@ -29,36 +37,54 @@ fn run_common(name: &str) -> (Command, Output) { for p in env::split_paths(&std::env::var("PATH").unwrap_or(String::new())) { paths.push(p.to_path_buf()); } - paths.push(Path::new(&std::env::var("TARGET_RPATH_DIR").unwrap()).to_path_buf()); + paths.push(Path::new(&env_var("TARGET_RPATH_DIR")).to_path_buf()); cmd.env("PATH", env::join_paths(paths.iter()).unwrap()); } - let output = cmd.output().unwrap(); - (cmd, output) + cmd } /// Run a built binary and make sure it succeeds. #[track_caller] -pub fn run(name: &str) -> Output { - let caller_location = std::panic::Location::caller(); - let caller_line_number = caller_location.line(); +pub fn run(name: &str) -> CompletedProcess { + let caller = panic::Location::caller(); + let mut cmd = run_common(name, None); + let output = cmd.run(); + if !output.status().success() { + handle_failed_output(&cmd, output, caller.line()); + } + output +} - let (cmd, output) = run_common(name); - if !output.status.success() { - handle_failed_output(&cmd, output, caller_line_number); +/// Run a built binary with one or more argument(s) and make sure it succeeds. +#[track_caller] +pub fn run_with_args(name: &str, args: &[&str]) -> CompletedProcess { + let caller = panic::Location::caller(); + let mut cmd = run_common(name, Some(args)); + let output = cmd.run(); + if !output.status().success() { + handle_failed_output(&cmd, output, caller.line()); } output } /// Run a built binary and make sure it fails. #[track_caller] -pub fn run_fail(name: &str) -> Output { - let caller_location = std::panic::Location::caller(); - let caller_line_number = caller_location.line(); - - let (cmd, output) = run_common(name); - if output.status.success() { - handle_failed_output(&cmd, output, caller_line_number); +pub fn run_fail(name: &str) -> CompletedProcess { + let caller = panic::Location::caller(); + let mut cmd = run_common(name, None); + let output = cmd.run_fail(); + if output.status().success() { + handle_failed_output(&cmd, output, caller.line()); } output } + +/// Create a new custom [`Command`]. This should be preferred to creating [`std::process::Command`] +/// directly. +#[track_caller] +pub fn cmd>(program: S) -> Command { + let mut command = Command::new(program); + set_host_rpath(&mut command); + command +} diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index f581204d5f1c..331e1a5ab8b3 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -1,35 +1,36 @@ -use std::env; +use command::Command; use std::ffi::{OsStr, OsString}; -use std::io::Write; use std::path::Path; -use std::process::{Command, Output, Stdio}; -use crate::{handle_failed_output, set_host_rpath, tmp_dir}; +use crate::{command, cwd, env_var, set_host_rpath}; /// Construct a new `rustc` invocation. +#[track_caller] pub fn rustc() -> Rustc { Rustc::new() } /// Construct a new `rustc` aux-build invocation. +#[track_caller] pub fn aux_build() -> Rustc { Rustc::new_aux_build() } /// A `rustc` invocation builder. #[derive(Debug)] +#[must_use] pub struct Rustc { cmd: Command, - stdin: Option>, } crate::impl_common_helpers!(Rustc); +#[track_caller] fn setup_common() -> Command { - let rustc = env::var("RUSTC").unwrap(); + let rustc = env_var("RUSTC"); let mut cmd = Command::new(rustc); set_host_rpath(&mut cmd); - cmd.arg("--out-dir").arg(tmp_dir()).arg("-L").arg(tmp_dir()); + cmd.arg("-L").arg(cwd()); cmd } @@ -37,16 +38,18 @@ impl Rustc { // `rustc` invocation constructor methods /// Construct a new `rustc` invocation. + #[track_caller] pub fn new() -> Self { let cmd = setup_common(); - Self { cmd, stdin: None } + Self { cmd } } /// Construct a new `rustc` invocation with `aux_build` preset (setting `--crate-type=lib`). + #[track_caller] pub fn new_aux_build() -> Self { let mut cmd = setup_common(); cmd.arg("--crate-type=lib"); - Self { cmd, stdin: None } + Self { cmd } } // Argument provider methods @@ -70,6 +73,12 @@ impl Rustc { self } + /// Add a suffix in each output filename. + pub fn extra_filename(&mut self, suffix: &str) -> &mut Self { + self.cmd.arg(format!("-Cextra-filename={suffix}")); + self + } + /// Specify type(s) of output files to generate. pub fn emit(&mut self, kinds: &str) -> &mut Self { self.cmd.arg(format!("--emit={kinds}")); @@ -97,6 +106,12 @@ impl Rustc { self } + //Adjust the backtrace level, displaying more detailed information at higher levels. + pub fn set_backtrace_level>(&mut self, level: R) -> &mut Self { + self.cmd.env("RUST_BACKTRACE", level); + self + } + /// Specify path to the output file. Equivalent to `-o`` in rustc. pub fn output>(&mut self, path: P) -> &mut Self { self.cmd.arg("-o"); @@ -104,6 +119,13 @@ impl Rustc { self } + /// Specify path to the output directory. Equivalent to `--out-dir`` in rustc. + pub fn out_dir>(&mut self, path: P) -> &mut Self { + self.cmd.arg("--out-dir"); + self.cmd.arg(path.as_ref()); + self + } + /// This flag defers LTO optimizations to the linker. pub fn linker_plugin_lto(&mut self, option: &str) -> &mut Self { self.cmd.arg(format!("-Clinker-plugin-lto={option}")); @@ -131,6 +153,24 @@ impl Rustc { self } + /// Specify directory path used for profile generation + pub fn profile_generate>(&mut self, path: P) -> &mut Self { + let mut arg = OsString::new(); + arg.push("-Cprofile-generate="); + arg.push(path.as_ref()); + self.cmd.arg(&arg); + self + } + + /// Specify directory path used for profile usage + pub fn profile_use>(&mut self, path: P) -> &mut Self { + let mut arg = OsString::new(); + arg.push("-Cprofile-use="); + arg.push(path.as_ref()); + self.cmd.arg(&arg); + self + } + /// Specify error format to use pub fn error_format(&mut self, format: &str) -> &mut Self { self.cmd.arg(format!("--error-format={format}")); @@ -156,13 +196,20 @@ impl Rustc { self } - /// Add a directory to the library search path. Equivalent to `-L`` in rustc. + /// Add a directory to the library search path. Equivalent to `-L` in rustc. pub fn library_search_path>(&mut self, path: P) -> &mut Self { self.cmd.arg("-L"); self.cmd.arg(path.as_ref()); self } + /// Override the system root. Equivalent to `--sysroot` in rustc. + pub fn sysroot>(&mut self, path: P) -> &mut Self { + self.cmd.arg("--sysroot"); + self.cmd.arg(path.as_ref()); + self + } + /// Specify the edition year. pub fn edition(&mut self, edition: &str) -> &mut Self { self.cmd.arg("--edition"); @@ -185,7 +232,7 @@ impl Rustc { /// Specify a stdin input pub fn stdin>(&mut self, input: I) -> &mut Self { - self.stdin = Some(input.as_ref().to_vec().into_boxed_slice()); + self.cmd.set_stdin(input.as_ref().to_vec().into_boxed_slice()); self } @@ -196,37 +243,9 @@ impl Rustc { self } - /// Get the [`Output`][::std::process::Output] of the finished process. - #[track_caller] - pub fn command_output(&mut self) -> ::std::process::Output { - // let's make sure we piped all the input and outputs - self.cmd.stdin(Stdio::piped()); - self.cmd.stdout(Stdio::piped()); - self.cmd.stderr(Stdio::piped()); - - if let Some(input) = &self.stdin { - let mut child = self.cmd.spawn().unwrap(); - - { - let mut stdin = child.stdin.take().unwrap(); - stdin.write_all(input.as_ref()).unwrap(); - } - - child.wait_with_output().expect("failed to get output of finished process") - } else { - self.cmd.output().expect("failed to get output of finished process") - } - } - - #[track_caller] - pub fn run_fail_assert_exit_code(&mut self, code: i32) -> Output { - let caller_location = std::panic::Location::caller(); - let caller_line_number = caller_location.line(); - - let output = self.command_output(); - if output.status.code().unwrap() != code { - handle_failed_output(&self.cmd, output, caller_line_number); - } - output + /// Specify the linker + pub fn linker(&mut self, linker: &str) -> &mut Self { + self.cmd.arg(format!("-Clinker={linker}")); + self } } diff --git a/src/tools/run-make-support/src/rustdoc.rs b/src/tools/run-make-support/src/rustdoc.rs index c4f4e9f9bd23..930785612545 100644 --- a/src/tools/run-make-support/src/rustdoc.rs +++ b/src/tools/run-make-support/src/rustdoc.rs @@ -1,31 +1,32 @@ -use std::env; use std::ffi::OsStr; -use std::io::Write; use std::path::Path; -use std::process::{Command, Output, Stdio}; -use crate::{handle_failed_output, set_host_rpath}; +use crate::command::Command; +use crate::{env_var, env_var_os, set_host_rpath}; /// Construct a plain `rustdoc` invocation with no flags set. +#[track_caller] pub fn bare_rustdoc() -> Rustdoc { Rustdoc::bare() } /// Construct a new `rustdoc` invocation with `-L $(TARGET_RPATH_DIR)` set. +#[track_caller] pub fn rustdoc() -> Rustdoc { Rustdoc::new() } #[derive(Debug)] +#[must_use] pub struct Rustdoc { cmd: Command, - stdin: Option>, } crate::impl_common_helpers!(Rustdoc); +#[track_caller] fn setup_common() -> Command { - let rustdoc = env::var("RUSTDOC").unwrap(); + let rustdoc = env_var("RUSTDOC"); let mut cmd = Command::new(rustdoc); set_host_rpath(&mut cmd); cmd @@ -33,17 +34,19 @@ fn setup_common() -> Command { impl Rustdoc { /// Construct a bare `rustdoc` invocation. + #[track_caller] pub fn bare() -> Self { let cmd = setup_common(); - Self { cmd, stdin: None } + Self { cmd } } /// Construct a `rustdoc` invocation with `-L $(TARGET_RPATH_DIR)` set. + #[track_caller] pub fn new() -> Self { let mut cmd = setup_common(); - let target_rpath_dir = env::var_os("TARGET_RPATH_DIR").unwrap(); + let target_rpath_dir = env_var_os("TARGET_RPATH_DIR"); cmd.arg(format!("-L{}", target_rpath_dir.to_string_lossy())); - Self { cmd, stdin: None } + Self { cmd } } /// Specify where an external library is located. @@ -89,33 +92,10 @@ impl Rustdoc { /// Specify a stdin input pub fn stdin>(&mut self, input: I) -> &mut Self { - self.cmd.stdin(Stdio::piped()); - self.stdin = Some(input.as_ref().to_vec().into_boxed_slice()); + self.cmd.set_stdin(input.as_ref().to_vec().into_boxed_slice()); self } - /// Get the [`Output`][::std::process::Output] of the finished process. - #[track_caller] - pub fn command_output(&mut self) -> ::std::process::Output { - // let's make sure we piped all the input and outputs - self.cmd.stdin(Stdio::piped()); - self.cmd.stdout(Stdio::piped()); - self.cmd.stderr(Stdio::piped()); - - if let Some(input) = &self.stdin { - let mut child = self.cmd.spawn().unwrap(); - - { - let mut stdin = child.stdin.take().unwrap(); - stdin.write_all(input.as_ref()).unwrap(); - } - - child.wait_with_output().expect("failed to get output of finished process") - } else { - self.cmd.output().expect("failed to get output of finished process") - } - } - /// Specify the edition year. pub fn edition(&mut self, edition: &str) -> &mut Self { self.cmd.arg("--edition"); @@ -151,15 +131,10 @@ impl Rustdoc { self } - #[track_caller] - pub fn run_fail_assert_exit_code(&mut self, code: i32) -> Output { - let caller_location = std::panic::Location::caller(); - let caller_line_number = caller_location.line(); - - let output = self.command_output(); - if output.status.code().unwrap() != code { - handle_failed_output(&self.cmd, output, caller_line_number); - } - output + /// Specify the output format. + pub fn output_format(&mut self, format: &str) -> &mut Self { + self.cmd.arg("--output-format"); + self.cmd.arg(format); + self } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index ff78fe0d54ef..cd31845a5140 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1426,6 +1426,7 @@ fn generic_args_sans_defaults<'ga>( } // otherwise, if the arg is equal to the param default, hide it (unless the // default is an error which can happen for the trait Self type) + #[allow(unstable_name_collisions)] default_parameters.get(i).is_none_or(|default_parameter| { // !is_err(default_parameter.skip_binders()) // && diff --git a/src/tools/rust-demangler/Cargo.toml b/src/tools/rust-demangler/Cargo.toml deleted file mode 100644 index 2bb73b3262d8..000000000000 --- a/src/tools/rust-demangler/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "rust-demangler" -version = "0.0.1" -edition = "2021" - -[dependencies] -regex = "1.0" -rustc-demangle = "0.1.17" - -[lib] -name = "rust_demangler" -doctest = false - -[[bin]] -name = "rust-demangler" -test = false diff --git a/src/tools/rust-demangler/README.md b/src/tools/rust-demangler/README.md deleted file mode 100644 index 4e8a689a13a4..000000000000 --- a/src/tools/rust-demangler/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# rust-demangler - -_Demangles rustc mangled names._ - -`rust-demangler` supports the requirements of the [`llvm-cov show -Xdemangler` -option](https://llvm.org/docs/CommandGuide/llvm-cov.html#cmdoption-llvm-cov-show-xdemangler), -to perform Rust-specific symbol demangling: - -> _The demangler is expected to read a newline-separated list of symbols from -> stdin and write a newline-separated list of the same length to stdout._ - -To use `rust-demangler` with `llvm-cov` for example: - -```shell -$ TARGET="${PWD}/build/x86_64-unknown-linux-gnu" -$ "${TARGET}"/llvm/bin/llvm-cov show \ - --Xdemangler=path/to/rust-demangler \ - --instr-profile=main.profdata ./main --show-line-counts-or-regions -``` - -`rust-demangler` is a Rust "extended tool", used in Rust compiler tests, and -optionally included in Rust distributions that enable coverage profiling. Symbol -demangling is implemented using the -[rustc-demangle](https://crates.io/crates/rustc-demangle) crate. - -_(Note, for Rust developers, the third-party tool -[`rustfilt`](https://crates.io/crates/rustfilt) also supports `llvm-cov` symbol -demangling. `rustfilt` is a more generalized tool that searches any body of -text, using pattern matching, to find and demangle Rust symbols.)_ - -## License - -Rust-demangler is distributed under the terms of both the MIT license and the -Apache License (Version 2.0). - -See [LICENSE-APACHE](/LICENSE-APACHE) and [LICENSE-MIT](/LICENSE-MIT) for details. diff --git a/src/tools/rust-demangler/src/lib.rs b/src/tools/rust-demangler/src/lib.rs deleted file mode 100644 index 1d972229d953..000000000000 --- a/src/tools/rust-demangler/src/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -use regex::Regex; -use rustc_demangle::demangle; -use std::str::Lines; - -const REPLACE_COLONS: &str = "::"; - -pub fn create_disambiguator_re() -> Regex { - Regex::new(r"\[[a-f0-9]{5,16}\]::").unwrap() -} - -pub fn demangle_lines(lines: Lines<'_>, strip_crate_disambiguators: Option) -> Vec { - let mut demangled_lines = Vec::new(); - for mangled in lines { - let mut demangled = demangle(mangled).to_string(); - if let Some(re) = &strip_crate_disambiguators { - demangled = re.replace_all(&demangled, REPLACE_COLONS).to_string(); - } - demangled_lines.push(demangled); - } - demangled_lines -} diff --git a/src/tools/rust-demangler/src/main.rs b/src/tools/rust-demangler/src/main.rs deleted file mode 100644 index 1b5ef5d2442b..000000000000 --- a/src/tools/rust-demangler/src/main.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! Demangles rustc mangled names. -//! -//! Note regarding crate disambiguators: -//! -//! Some demangled symbol paths can include "crate disambiguator" suffixes, represented as a large -//! hexadecimal value enclosed in square braces, and appended to the name of the crate. a suffix to the -//! original crate name. For example, the `core` crate, here, includes a disambiguator: -//! -//! ```rust -//! as core[a7a74cee373f048]::ops::drop::Drop>::drop -//! ``` -//! -//! These disambiguators are known to vary depending on environmental circumstances. As a result, -//! tests that compare results including demangled names can fail across development environments, -//! particularly with cross-platform testing. Also, the resulting crate paths are not syntactically -//! valid, and don't match the original source symbol paths, which can impact development tools. -//! -//! For these reasons, by default, `rust-demangler` uses a heuristic to remove crate disambiguators -//! from their original demangled representation before printing them to standard output. If crate -//! disambiguators are required, add the `-d` (or `--disambiguators`) flag, and the disambiguators -//! will not be removed. -//! -//! Also note that the disambiguators are stripped by a Regex pattern that is tolerant to some -//! variation in the number of hexadecimal digits. The disambiguators come from a hash value, which -//! typically generates a 16-digit hex representation on a 64-bit architecture; however, leading -//! zeros are not included, which can shorten the hex digit length, and a different hash algorithm -//! that might also be dependent on the architecture, might shorten the length even further. A -//! minimum length of 5 digits is assumed, which should be more than sufficient to support hex -//! representations that generate only 8-digits of precision with an extremely rare (but not -//! impossible) result with up to 3 leading zeros. -//! -//! Using a minimum number of digits less than 5 risks the possibility of stripping demangled name -//! components with a similar pattern. For example, some closures instantiated multiple times -//! include their own disambiguators, demangled as non-hashed zero-based indexes in square brackets. -//! These disambiguators seem to have more analytical value (for instance, in coverage analysis), so -//! they are not removed. - -use rust_demangler::*; -use std::io::{self, Read, Write}; - -fn main() -> io::Result<()> { - // FIXME(richkadel): In Issue #77615 discussed updating the `rustc-demangle` library, to provide - // an option to generate demangled names without including crate disambiguators. If that - // happens, update this tool to use that option (if the `-d` flag is not set) instead stripping - // them via the Regex heuristic. The update the doc comments and help. - - // Strip hashed hexadecimal crate disambiguators. Leading zeros are not enforced, and can be - // different across different platform/architecture types, so while 16 hex digits are common, - // they can also be shorter. - // - // Also note that a demangled symbol path may include the `[]` pattern, with zero-based - // indexes (such as for closures, and possibly for types defined in anonymous scopes). Preferably - // these should not be stripped. - // - // The minimum length of 5 digits supports the possibility that some target architecture (maybe - // a 32-bit or smaller architecture) could generate a hash value with a maximum of 8 digits, - // and more than three leading zeros should be extremely unlikely. Conversely, it should be - // sufficient to assume the zero-based indexes for closures and anonymous scopes will never - // exceed the value 9999. - let mut strip_crate_disambiguators = Some(create_disambiguator_re()); - - let mut args = std::env::args(); - let progname = args.next().unwrap(); - for arg in args { - if arg == "--disambiguators" || arg == "-d" { - strip_crate_disambiguators = None; - } else { - eprintln!(); - eprintln!("Usage: {} [-d|--disambiguators]", progname); - eprintln!(); - eprintln!( - "This tool converts a list of Rust mangled symbols (one per line) into a\n\ - corresponding list of demangled symbols." - ); - eprintln!(); - eprintln!( - "With -d (--disambiguators), Rust symbols mangled with the v0 symbol mangler may\n\ - include crate disambiguators (a hexadecimal hash value, typically up to 16 digits\n\ - long, enclosed in square brackets)." - ); - eprintln!(); - eprintln!( - "By default, crate disambiguators are removed, using a heuristics-based regular\n\ - expression. (See the `rust-demangler` doc comments for more information.)" - ); - eprintln!(); - std::process::exit(1) - } - } - - let mut buffer = String::new(); - io::stdin().read_to_string(&mut buffer)?; - let mut demangled_lines = demangle_lines(buffer.lines(), strip_crate_disambiguators); - demangled_lines.push("".to_string()); // ensure a trailing newline - io::stdout().write_all(demangled_lines.join("\n").as_bytes())?; - Ok(()) -} diff --git a/src/tools/rust-demangler/tests/lib.rs b/src/tools/rust-demangler/tests/lib.rs deleted file mode 100644 index 85019df7867d..000000000000 --- a/src/tools/rust-demangler/tests/lib.rs +++ /dev/null @@ -1,84 +0,0 @@ -use rust_demangler::*; - -const MANGLED_INPUT: &str = r" -_RNvC6_123foo3bar -_RNqCs4fqI2P2rA04_11utf8_identsu30____7hkackfecea1cbdathfdh9hlq6y -_RNCNCNgCs6DXkGYLi8lr_2cc5spawn00B5_ -_RNCINkXs25_NgCsbmNqQUJIY6D_4core5sliceINyB9_4IterhENuNgNoBb_4iter8iterator8Iterator9rpositionNCNgNpB9_6memchr7memrchrs_0E0Bb_ -_RINbNbCskIICzLVDPPb_5alloc5alloc8box_freeDINbNiB4_5boxed5FnBoxuEp6OutputuEL_ECs1iopQbuBiw2_3std -INtC8arrayvec8ArrayVechKj7b_E -_RMCs4fqI2P2rA04_13const_genericINtB0_8UnsignedKhb_E -_RMCs4fqI2P2rA04_13const_genericINtB0_6SignedKs98_E -_RMCs4fqI2P2rA04_13const_genericINtB0_6SignedKanb_E -_RMCs4fqI2P2rA04_13const_genericINtB0_4BoolKb0_E -_RMCs4fqI2P2rA04_13const_genericINtB0_4BoolKb1_E -_RMCs4fqI2P2rA04_13const_genericINtB0_4CharKc76_E -_RMCs4fqI2P2rA04_13const_genericINtB0_4CharKca_E -_RMCs4fqI2P2rA04_13const_genericINtB0_4CharKc2202_E -_RNvNvMCs4fqI2P2rA04_13const_genericINtB4_3FooKpE3foo3FOO -_RC3foo.llvm.9D1C9369 -_RC3foo.llvm.9D1C9369@@16 -_RNvC9backtrace3foo.llvm.A5310EB9 -_RNvNtNtNtNtCs92dm3009vxr_4rand4rngs7adapter9reseeding4fork23FORK_HANDLER_REGISTERED.0.0 -"; - -const DEMANGLED_OUTPUT: &str = r" -123foo[0]::bar -utf8_idents[317d481089b8c8fe]::საჭმელად_გემრიელი_სადილი -cc[4d6468d6c9fd4bb3]::spawn::{closure#0}::{closure#0} - as core[846817f741e54dfd]::iter::iterator::Iterator>::rposition::::{closure#0} -alloc[f15a878b47eb696b]::alloc::box_free::> -INtC8arrayvec8ArrayVechKj7b_E -> -> -> -> -> -> -> -> ->::foo::FOO -foo[0] -foo[0] -backtrace[0]::foo -rand[693ea8e72247470f]::rngs::adapter::reseeding::fork::FORK_HANDLER_REGISTERED.0.0 -"; - -const DEMANGLED_OUTPUT_NO_CRATE_DISAMBIGUATORS: &str = r" -123foo[0]::bar -utf8_idents::საჭმელად_გემრიელი_სადილი -cc::spawn::{closure#0}::{closure#0} - as core::iter::iterator::Iterator>::rposition::::{closure#0} -alloc::alloc::box_free::> -INtC8arrayvec8ArrayVechKj7b_E -> -> -> -> -> -> -> -> ->::foo::FOO -foo[0] -foo[0] -backtrace[0]::foo -rand::rngs::adapter::reseeding::fork::FORK_HANDLER_REGISTERED.0.0 -"; - -#[test] -fn test_demangle_lines() { - let demangled_lines = demangle_lines(MANGLED_INPUT.lines(), None); - for (expected, actual) in DEMANGLED_OUTPUT.lines().zip(demangled_lines) { - assert_eq!(expected, actual); - } -} - -#[test] -fn test_demangle_lines_no_crate_disambiguators() { - let demangled_lines = demangle_lines(MANGLED_INPUT.lines(), Some(create_disambiguator_re())); - for (expected, actual) in DEMANGLED_OUTPUT_NO_CRATE_DISAMBIGUATORS.lines().zip(demangled_lines) - { - assert_eq!(expected, actual); - } -} diff --git a/src/tools/rust-installer/src/compression.rs b/src/tools/rust-installer/src/compression.rs index 4e840dbfbb44..40c7c597e9b4 100644 --- a/src/tools/rust-installer/src/compression.rs +++ b/src/tools/rust-installer/src/compression.rs @@ -214,22 +214,16 @@ impl Write for CombinedEncoder { } fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { - self.encoders - .par_iter_mut() - .map(|w| w.write_all(buf)) - .collect::>>()?; - Ok(()) + self.encoders.par_iter_mut().try_for_each(|w| w.write_all(buf)) } fn flush(&mut self) -> std::io::Result<()> { - self.encoders.par_iter_mut().map(|w| w.flush()).collect::>>()?; - Ok(()) + self.encoders.par_iter_mut().try_for_each(Write::flush) } } impl Encoder for CombinedEncoder { fn finish(self: Box) -> Result<(), Error> { - self.encoders.into_par_iter().map(|e| e.finish()).collect::, Error>>()?; - Ok(()) + self.encoders.into_par_iter().try_for_each(Encoder::finish) } } diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml index 2ef9004c476e..95f1a5d6e1d3 100644 --- a/src/tools/rustbook/Cargo.toml +++ b/src/tools/rustbook/Cargo.toml @@ -7,6 +7,9 @@ edition = "2021" [dependencies] clap = "4.0.32" env_logger = "0.11" +mdbook-trpl-listing = { path = "../../doc/book/packages/mdbook-trpl-listing" } +mdbook-trpl-note = { path = "../../doc/book/packages/mdbook-trpl-note" } +mdbook-i18n-helpers = "0.3.3" [dependencies.mdbook] version = "0.4.37" diff --git a/src/tools/rustbook/src/main.rs b/src/tools/rustbook/src/main.rs index 1368ec653de1..31bba56addeb 100644 --- a/src/tools/rustbook/src/main.rs +++ b/src/tools/rustbook/src/main.rs @@ -7,6 +7,10 @@ use clap::{arg, ArgMatches, Command}; use mdbook::errors::Result as Result3; use mdbook::MDBook; +use mdbook_i18n_helpers::preprocessors::Gettext; + +use mdbook_trpl_listing::TrplListing; +use mdbook_trpl_note::TrplNote; fn main() { let crate_version = concat!("v", crate_version!()); @@ -16,6 +20,11 @@ fn main() { .required(false) .value_parser(clap::value_parser!(PathBuf)); + let l_arg = arg!(-l --"lang" +"The output language") + .required(false) + .value_parser(clap::value_parser!(String)); + let dir_arg = arg!([dir] "Root directory for the book\n\ (Defaults to the current directory when omitted)") .value_parser(clap::value_parser!(PathBuf)); @@ -30,6 +39,7 @@ fn main() { Command::new("build") .about("Build the book from the markdown files") .arg(d_arg) + .arg(l_arg) .arg(&dir_arg), ) .subcommand( @@ -60,6 +70,12 @@ pub fn build(args: &ArgMatches) -> Result3<()> { let book_dir = get_book_dir(args); let mut book = load_book(&book_dir)?; + if let Some(lang) = args.get_one::("lang") { + let gettext = Gettext; + book.with_preprocessor(gettext); + book.config.set("book.language", lang).unwrap(); + } + // Set this to allow us to catch bugs in advance. book.config.build.create_missing = false; @@ -67,6 +83,14 @@ pub fn build(args: &ArgMatches) -> Result3<()> { book.config.build.build_dir = dest_dir.into(); } + if book.config.get_preprocessor("trpl-note").is_some() { + book.with_preprocessor(TrplNote); + } + + if book.config.get_preprocessor("trpl-listing").is_some() { + book.with_preprocessor(TrplListing); + } + book.build()?; Ok(()) diff --git a/src/tools/rustc-perf b/src/tools/rustc-perf new file mode 160000 index 000000000000..c64bb60dd163 --- /dev/null +++ b/src/tools/rustc-perf @@ -0,0 +1 @@ +Subproject commit c64bb60dd1636922b1ccbb82867bed934a99dbcb diff --git a/src/tools/rustdoc-gui-test/src/main.rs b/src/tools/rustdoc-gui-test/src/main.rs index 0ddd2c66cf9e..bf581279f2ac 100644 --- a/src/tools/rustdoc-gui-test/src/main.rs +++ b/src/tools/rustdoc-gui-test/src/main.rs @@ -123,9 +123,7 @@ If you want to install the `browser-ui-test` dependency, run `npm install browse cargo.env("RUSTDOCFLAGS", test_props.compile_flags.join(" ")); } - if let Some(flags) = &test_props.run_flags { - cargo.arg(flags); - } + cargo.args(&test_props.run_flags); } if try_run(&mut cargo, config.verbose).is_err() { diff --git a/src/tools/rustdoc-gui/.eslintrc.js b/src/tools/rustdoc-gui/.eslintrc.js index f4aadc071998..3eccbc74cb95 100644 --- a/src/tools/rustdoc-gui/.eslintrc.js +++ b/src/tools/rustdoc-gui/.eslintrc.js @@ -6,7 +6,7 @@ module.exports = { }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": 2018, + "ecmaVersion": 2019, "sourceType": "module" }, "rules": { diff --git a/src/tools/rustdoc-js/.eslintrc.js b/src/tools/rustdoc-js/.eslintrc.js index b9d0e251c242..3eccbc74cb95 100644 --- a/src/tools/rustdoc-js/.eslintrc.js +++ b/src/tools/rustdoc-js/.eslintrc.js @@ -6,7 +6,7 @@ module.exports = { }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": 8, + "ecmaVersion": 2019, "sourceType": "module" }, "rules": { diff --git a/src/tools/rustfmt/src/parse/macros/cfg_if.rs b/src/tools/rustfmt/src/parse/macros/cfg_if.rs index 5fc988e43193..b91d203d5311 100644 --- a/src/tools/rustfmt/src/parse/macros/cfg_if.rs +++ b/src/tools/rustfmt/src/parse/macros/cfg_if.rs @@ -67,7 +67,7 @@ fn parse_cfg_if_inner<'a>( Ok(None) => continue, Err(err) => { err.cancel(); - parser.psess.dcx.reset_err_count(); + parser.psess.dcx().reset_err_count(); return Err( "Expected item inside cfg_if block, but failed to parse it as an item", ); diff --git a/src/tools/rustfmt/src/parse/macros/lazy_static.rs b/src/tools/rustfmt/src/parse/macros/lazy_static.rs index badd95699500..7026935294ac 100644 --- a/src/tools/rustfmt/src/parse/macros/lazy_static.rs +++ b/src/tools/rustfmt/src/parse/macros/lazy_static.rs @@ -16,8 +16,8 @@ pub(crate) fn parse_lazy_static( ($method:ident $(,)* $($arg:expr),* $(,)*) => { match parser.$method($($arg,)*) { Ok(val) => { - if parser.psess.dcx.has_errors().is_some() { - parser.psess.dcx.reset_err_count(); + if parser.psess.dcx().has_errors().is_some() { + parser.psess.dcx().reset_err_count(); return None; } else { val @@ -25,7 +25,7 @@ pub(crate) fn parse_lazy_static( } Err(err) => { err.cancel(); - parser.psess.dcx.reset_err_count(); + parser.psess.dcx().reset_err_count(); return None; } } diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs index 3cf133c647cc..89169e10715b 100644 --- a/src/tools/rustfmt/src/parse/macros/mod.rs +++ b/src/tools/rustfmt/src/parse/macros/mod.rs @@ -2,7 +2,7 @@ use rustc_ast::token::{Delimiter, NonterminalKind, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{ast, ptr}; use rustc_parse::parser::{ForceCollect, Parser, Recovery}; -use rustc_parse::{stream_to_parser, MACRO_ARGUMENTS}; +use rustc_parse::MACRO_ARGUMENTS; use rustc_session::parse::ParseSess; use rustc_span::symbol::{self, kw}; use rustc_span::Symbol; @@ -15,7 +15,7 @@ pub(crate) mod cfg_if; pub(crate) mod lazy_static; fn build_stream_parser<'a>(psess: &'a ParseSess, tokens: TokenStream) -> Parser<'a> { - stream_to_parser(psess, tokens, MACRO_ARGUMENTS).recovery(Recovery::Forbidden) + Parser::new(psess, tokens, MACRO_ARGUMENTS).recovery(Recovery::Forbidden) } fn build_parser<'a>(context: &RewriteContext<'a>, tokens: TokenStream) -> Parser<'a> { @@ -29,8 +29,8 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { if Parser::nonterminal_may_begin_with($nt_kind, &cloned_parser.token) { match $try_parse(&mut cloned_parser) { Ok(x) => { - if parser.psess.dcx.has_errors().is_some() { - parser.psess.dcx.reset_err_count(); + if parser.psess.dcx().has_errors().is_some() { + parser.psess.dcx().reset_err_count(); } else { // Parsing succeeded. *parser = cloned_parser; @@ -39,7 +39,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { } Err(e) => { e.cancel(); - parser.psess.dcx.reset_err_count(); + parser.psess.dcx().reset_err_count(); } } } diff --git a/src/tools/rustfmt/src/parse/parser.rs b/src/tools/rustfmt/src/parse/parser.rs index 5dcdca1d9538..6051241309d1 100644 --- a/src/tools/rustfmt/src/parse/parser.rs +++ b/src/tools/rustfmt/src/parse/parser.rs @@ -4,7 +4,8 @@ use std::path::{Path, PathBuf}; use rustc_ast::token::TokenKind; use rustc_ast::{ast, attr, ptr}; use rustc_errors::Diag; -use rustc_parse::{new_parser_from_file, parser::Parser as RawParser}; +use rustc_parse::parser::Parser as RawParser; +use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_span::{sym, Span}; use thin_vec::ThinVec; @@ -50,12 +51,9 @@ impl<'a> ParserBuilder<'a> { let parser = match Self::parser(psess.inner(), input) { Ok(p) => p, - Err(db) => { - if let Some(diagnostics) = db { - psess.emit_diagnostics(diagnostics); - return Err(ParserError::ParserCreationError); - } - return Err(ParserError::ParsePanicError); + Err(diagnostics) => { + psess.emit_diagnostics(diagnostics); + return Err(ParserError::ParserCreationError); } }; @@ -65,18 +63,14 @@ impl<'a> ParserBuilder<'a> { fn parser( psess: &'a rustc_session::parse::ParseSess, input: Input, - ) -> Result, Option>>> { + ) -> Result, Vec>> { match input { - Input::File(ref file) => catch_unwind(AssertUnwindSafe(move || { - new_parser_from_file(psess, file, None) - })) - .map_err(|_| None), - Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str( + Input::File(ref file) => new_parser_from_file(psess, file, None), + Input::Text(text) => new_parser_from_source_str( psess, rustc_span::FileName::Custom("stdin".to_owned()), text, - ) - .map_err(Some), + ), } } } @@ -111,7 +105,8 @@ impl<'a> Parser<'a> { span: Span, ) -> Result<(ast::AttrVec, ThinVec>, Span), ParserError> { let result = catch_unwind(AssertUnwindSafe(|| { - let mut parser = new_parser_from_file(psess.inner(), path, Some(span)); + let mut parser = + unwrap_or_emit_fatal(new_parser_from_file(psess.inner(), path, Some(span))); match parser.parse_mod(&TokenKind::Eof) { Ok((a, i, spans)) => Some((a, i, spans.inner_span)), Err(e) => { diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 1a39d212386b..f4fbabaf6c91 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -210,7 +210,9 @@ impl ParseSess { rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false, ); - self.raw_psess.dcx.make_silent(fallback_bundle, None, false); + self.raw_psess + .dcx() + .make_silent(fallback_bundle, None, false); } pub(crate) fn span_to_filename(&self, span: Span) -> FileName { @@ -286,11 +288,11 @@ impl ParseSess { } pub(super) fn has_errors(&self) -> bool { - self.raw_psess.dcx.has_errors().is_some() + self.raw_psess.dcx().has_errors().is_some() } pub(super) fn reset_errors(&self) { - self.raw_psess.dcx.reset_err_count(); + self.raw_psess.dcx().reset_err_count(); } } diff --git a/src/tools/rustfmt/src/spanned.rs b/src/tools/rustfmt/src/spanned.rs index 4aaf7fdb27fb..28911f8af1df 100644 --- a/src/tools/rustfmt/src/spanned.rs +++ b/src/tools/rustfmt/src/spanned.rs @@ -181,6 +181,7 @@ impl Spanned for ast::GenericBound { match *self { ast::GenericBound::Trait(ref ptr, _) => ptr.span, ast::GenericBound::Outlives(ref l) => l.ident.span, + ast::GenericBound::Use(_, span) => span, } } } diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 75dea90d994c..c2c192738c91 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -140,7 +140,7 @@ pub(crate) enum SegmentParam<'a> { Const(&'a ast::AnonConst), LifeTime(&'a ast::Lifetime), Type(&'a ast::Ty), - Binding(&'a ast::AssocConstraint), + Binding(&'a ast::AssocItemConstraint), } impl<'a> SegmentParam<'a> { @@ -175,9 +175,9 @@ impl<'a> Rewrite for SegmentParam<'a> { } } -impl Rewrite for ast::AssocConstraint { +impl Rewrite for ast::AssocItemConstraint { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { - use ast::AssocConstraintKind::{Bound, Equality}; + use ast::AssocItemConstraintKind::{Bound, Equality}; let mut result = String::with_capacity(128); result.push_str(rewrite_ident(context, self.ident)); @@ -205,14 +205,14 @@ impl Rewrite for ast::AssocConstraint { } } -impl Rewrite for ast::AssocConstraintKind { +impl Rewrite for ast::AssocItemConstraintKind { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option { match self { - ast::AssocConstraintKind::Equality { term } => match term { + ast::AssocItemConstraintKind::Equality { term } => match term { Term::Ty(ty) => ty.rewrite(context, shape), Term::Const(c) => c.rewrite(context, shape), }, - ast::AssocConstraintKind::Bound { bounds } => bounds.rewrite(context, shape), + ast::AssocItemConstraintKind::Bound { bounds } => bounds.rewrite(context, shape), } } } @@ -563,6 +563,8 @@ impl Rewrite for ast::GenericBound { .map(|s| if has_paren { format!("({})", s) } else { s }) } ast::GenericBound::Outlives(ref lifetime) => lifetime.rewrite(context, shape), + // FIXME(precise_capturing): Should implement formatting before stabilization. + ast::GenericBound::Use(..) => None, } } } @@ -843,11 +845,7 @@ impl Rewrite for ast::Ty { rewrite_macro(mac, None, context, shape, MacroPosition::Expression) } ast::TyKind::ImplicitSelf => Some(String::from("")), - ast::TyKind::ImplTrait(_, ref it, ref captures) => { - // FIXME(precise_capturing): Implement formatting. - if captures.is_some() { - return None; - } + ast::TyKind::ImplTrait(_, ref it) => { // Empty trait is not a parser error. if it.is_empty() { return Some("impl".to_owned()); @@ -932,6 +930,8 @@ fn is_generic_bounds_in_order(generic_bounds: &[ast::GenericBound]) -> bool { let is_trait = |b: &ast::GenericBound| match b { ast::GenericBound::Outlives(..) => false, ast::GenericBound::Trait(..) => true, + // FIXME(precise_capturing): This ordering fn should be reworked. + ast::GenericBound::Use(..) => false, }; let is_lifetime = |b: &ast::GenericBound| !is_trait(b); let last_trait_index = generic_bounds.iter().rposition(is_trait); @@ -966,6 +966,8 @@ fn join_bounds_inner( let is_bound_extendable = |s: &str, b: &ast::GenericBound| match b { ast::GenericBound::Outlives(..) => true, ast::GenericBound::Trait(..) => last_line_extendable(s), + // FIXME(precise_capturing): This ordering fn should be reworked. + ast::GenericBound::Use(..) => true, }; // Whether a GenericBound item is a PathSegment segment that includes internal array @@ -1110,8 +1112,7 @@ fn join_bounds_inner( pub(crate) fn opaque_ty(ty: &Option>) -> Option<&ast::GenericBounds> { ty.as_ref().and_then(|t| match &t.kind { - // FIXME(precise_capturing): Implement support here - ast::TyKind::ImplTrait(_, bounds, _) => Some(bounds), + ast::TyKind::ImplTrait(_, bounds) => Some(bounds), _ => None, }) } diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 09e1dbde1d05..fd59aedadfed 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -111,6 +111,7 @@ pub(crate) fn format_defaultness(defaultness: ast::Defaultness) -> &'static str pub(crate) fn format_safety(unsafety: ast::Safety) -> &'static str { match unsafety { ast::Safety::Unsafe(..) => "unsafe ", + ast::Safety::Safe(..) => "safe ", ast::Safety::Default => "", } } diff --git a/src/tools/rustfmt/tests/source/attrib.rs b/src/tools/rustfmt/tests/source/attrib.rs index fc13cd02b03e..d45fba552243 100644 --- a/src/tools/rustfmt/tests/source/attrib.rs +++ b/src/tools/rustfmt/tests/source/attrib.rs @@ -214,8 +214,8 @@ type Os = NoSource; // #3313 fn stmt_expr_attributes() { let foo ; - (#[must_use] - foo) = false ; + #[must_use] + foo = false ; } // #3509 diff --git a/src/tools/rustfmt/tests/target/attrib.rs b/src/tools/rustfmt/tests/target/attrib.rs index 7b3309676de2..7e61f68d76a1 100644 --- a/src/tools/rustfmt/tests/target/attrib.rs +++ b/src/tools/rustfmt/tests/target/attrib.rs @@ -248,8 +248,8 @@ type Os = NoSource; // #3313 fn stmt_expr_attributes() { let foo; - (#[must_use] - foo) = false; + #[must_use] + foo = false; } // #3509 diff --git a/src/tools/tidy/config/black.toml b/src/tools/tidy/config/black.toml index 51a722979f55..e73847a93ba4 100644 --- a/src/tools/tidy/config/black.toml +++ b/src/tools/tidy/config/black.toml @@ -11,5 +11,6 @@ extend-exclude = """(\ src/doc/edition-guide/|\ src/llvm-project/|\ src/doc/embedded-book/|\ + src/tools/rustc-perf/|\ library/backtrace/ )""" diff --git a/src/tools/tidy/config/requirements.in b/src/tools/tidy/config/requirements.in index 882e09dae459..047617c65598 100644 --- a/src/tools/tidy/config/requirements.in +++ b/src/tools/tidy/config/requirements.in @@ -1,10 +1,10 @@ # requirements.in This is the source file for our pinned version requirements # file "requirements.txt" To regenerate that file, pip-tools is required -# (`python -m pip install pip-tools`). Once installed, run: `pip-compile -# --generate-hashes src/tools/tidy/config/requirements.in` +# (`python -m pip install pip-tools==7.4.1`). Once installed, run: `pip-compile +# --generate-hashes --strip-extras src/tools/tidy/config/requirements.in` # # Note: this generation step should be run with the oldest supported python -# version (currently 3.7) to ensure backward compatibility +# version (currently 3.9) to ensure backward compatibility black==23.3.0 ruff==0.0.272 diff --git a/src/tools/tidy/config/requirements.txt b/src/tools/tidy/config/requirements.txt index 9fd617ad6219..a53c98cac7a0 100644 --- a/src/tools/tidy/config/requirements.txt +++ b/src/tools/tidy/config/requirements.txt @@ -1,8 +1,8 @@ # -# This file is autogenerated by pip-compile with Python 3.11 +# This file is autogenerated by pip-compile with Python 3.9 # by the following command: # -# pip-compile --generate-hashes src/tools/tidy/config/requirements.in +# pip-compile --generate-hashes --strip-extras src/tools/tidy/config/requirements.in # black==23.3.0 \ --hash=sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5 \ @@ -35,10 +35,6 @@ click==8.1.3 \ --hash=sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e \ --hash=sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48 # via black -importlib-metadata==6.7.0 \ - --hash=sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4 \ - --hash=sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5 - # via click mypy-extensions==1.0.0 \ --hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \ --hash=sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782 @@ -78,40 +74,7 @@ tomli==2.0.1 \ --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f # via black -typed-ast==1.5.4 \ - --hash=sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2 \ - --hash=sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1 \ - --hash=sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6 \ - --hash=sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62 \ - --hash=sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac \ - --hash=sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d \ - --hash=sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc \ - --hash=sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2 \ - --hash=sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97 \ - --hash=sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35 \ - --hash=sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6 \ - --hash=sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1 \ - --hash=sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4 \ - --hash=sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c \ - --hash=sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e \ - --hash=sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec \ - --hash=sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f \ - --hash=sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72 \ - --hash=sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47 \ - --hash=sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72 \ - --hash=sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe \ - --hash=sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6 \ - --hash=sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3 \ - --hash=sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66 +typing-extensions==4.12.2 \ + --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ + --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8 # via black -typing-extensions==4.6.3 \ - --hash=sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26 \ - --hash=sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5 - # via - # black - # importlib-metadata - # platformdirs -zipp==3.15.0 \ - --hash=sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b \ - --hash=sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556 - # via importlib-metadata diff --git a/src/tools/tidy/config/ruff.toml b/src/tools/tidy/config/ruff.toml index cf08c62648b3..cf89ffd9ac73 100644 --- a/src/tools/tidy/config/ruff.toml +++ b/src/tools/tidy/config/ruff.toml @@ -26,6 +26,7 @@ extend-exclude = [ "src/llvm-project/", "src/doc/embedded-book/", "library/backtrace/", + "src/tools/rustc-perf/", # Hack: CI runs from a subdirectory under the main checkout "../src/doc/nomicon/", "../src/tools/cargo/", @@ -38,4 +39,5 @@ extend-exclude = [ "../src/llvm-project/", "../src/doc/embedded-book/", "../library/backtrace/", + "../src/tools/rustc-perf/", ] diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index d1ae24007b3d..2b273b92ec2d 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -1,13 +1,8 @@ -run-make/allocator-shim-circular-deps/Makefile -run-make/allow-non-lint-warnings-cmdline/Makefile -run-make/allow-warnings-cmdline-stability/Makefile run-make/archive-duplicate-names/Makefile run-make/atomic-lock-free/Makefile -run-make/bare-outfile/Makefile run-make/branch-protection-check-IBT/Makefile run-make/c-dynamic-dylib/Makefile run-make/c-dynamic-rlib/Makefile -run-make/c-link-to-rust-dylib/Makefile run-make/c-static-dylib/Makefile run-make/c-static-rlib/Makefile run-make/c-unwind-abi-catch-lib-panic/Makefile @@ -15,18 +10,11 @@ run-make/c-unwind-abi-catch-panic/Makefile run-make/cat-and-grep-sanity-check/Makefile run-make/cdylib-dylib-linkage/Makefile run-make/cdylib-fewer-symbols/Makefile -run-make/cdylib/Makefile -run-make/codegen-options-parsing/Makefile run-make/comment-section/Makefile -run-make/compile-stdin/Makefile run-make/compiler-lookup-paths-2/Makefile run-make/compiler-lookup-paths/Makefile run-make/compiler-rt-works-on-mingw/Makefile run-make/compressed-debuginfo/Makefile -run-make/const-prop-lint/Makefile -run-make/const_fn_mir/Makefile -run-make/core-no-oom-handling/Makefile -run-make/crate-data-smoke/Makefile run-make/crate-hash-rustc-version/Makefile run-make/crate-name-priority/Makefile run-make/cross-lang-lto-clang/Makefile @@ -35,27 +23,21 @@ run-make/cross-lang-lto-upstream-rlibs/Makefile run-make/cross-lang-lto/Makefile run-make/debug-assertions/Makefile run-make/debugger-visualizer-dep-info/Makefile -run-make/dep-graph/Makefile run-make/dep-info-doesnt-run-much/Makefile run-make/dep-info-spaces/Makefile run-make/dep-info/Makefile run-make/dump-ice-to-disk/Makefile run-make/dump-mono-stats/Makefile -run-make/duplicate-output-flavors/Makefile run-make/dylib-chain/Makefile -run-make/emit-named-files/Makefile run-make/emit-path-unhashed/Makefile run-make/emit-shared-files/Makefile run-make/emit-stack-sizes/Makefile run-make/emit-to-stdout/Makefile -run-make/emit/Makefile run-make/env-dep-info/Makefile -run-make/error-found-staticlib-instead-crate/Makefile run-make/error-writing-dependencies/Makefile run-make/export-executable-symbols/Makefile run-make/extern-diff-internal-name/Makefile run-make/extern-flag-disambiguates/Makefile -run-make/extern-flag-fun/Makefile run-make/extern-flag-pathless/Makefile run-make/extern-flag-rename-transitive/Makefile run-make/extern-fn-explicit-align/Makefile @@ -75,48 +57,31 @@ run-make/forced-unwind-terminate-pof/Makefile run-make/foreign-double-unwind/Makefile run-make/foreign-exceptions/Makefile run-make/foreign-rust-exceptions/Makefile -run-make/fpic/Makefile -run-make/glibc-staticlib-args/Makefile -run-make/inaccessible-temp-dir/Makefile run-make/include_bytes_deps/Makefile run-make/incr-add-rust-src-component/Makefile run-make/incr-foreign-head-span/Makefile -run-make/incr-prev-body-beyond-eof/Makefile -run-make/incremental-debugger-visualizer/Makefile -run-make/incremental-session-fail/Makefile run-make/inline-always-many-cgu/Makefile run-make/interdependent-c-libraries/Makefile run-make/intrinsic-unreachable/Makefile run-make/invalid-library/Makefile run-make/invalid-so/Makefile -run-make/invalid-staticlib/Makefile run-make/issue-107094/Makefile -run-make/issue-10971-temps-dir/Makefile run-make/issue-109934-lto-debuginfo/Makefile run-make/issue-14698/Makefile run-make/issue-15460/Makefile run-make/issue-18943/Makefile run-make/issue-20626/Makefile run-make/issue-22131/Makefile -run-make/issue-24445/Makefile run-make/issue-25581/Makefile run-make/issue-26006/Makefile -run-make/issue-26092/Makefile run-make/issue-28595/Makefile -run-make/issue-30063/Makefile run-make/issue-33329/Makefile run-make/issue-35164/Makefile run-make/issue-36710/Makefile run-make/issue-37839/Makefile -run-make/issue-37893/Makefile -run-make/issue-38237/Makefile run-make/issue-40535/Makefile -run-make/issue-46239/Makefile run-make/issue-47384/Makefile run-make/issue-47551/Makefile -run-make/issue-51671/Makefile -run-make/issue-53964/Makefile -run-make/issue-64153/Makefile run-make/issue-68794-textrel-on-minimal-lib/Makefile run-make/issue-69368/Makefile run-make/issue-83045/Makefile @@ -124,7 +89,6 @@ run-make/issue-83112-incr-test-moved-file/Makefile run-make/issue-84395-lto-embed-bitcode/Makefile run-make/issue-85019-moved-src-dir/Makefile run-make/issue-85401-static-mir/Makefile -run-make/issue-85441/Makefile run-make/issue-88756-default-output/Makefile run-make/issue-97463-abi-param-passing/Makefile run-make/jobserver-error/Makefile @@ -133,15 +97,12 @@ run-make/libtest-json/Makefile run-make/libtest-junit/Makefile run-make/libtest-padding/Makefile run-make/libtest-thread-limit/Makefile -run-make/link-arg/Makefile run-make/link-args-order/Makefile run-make/link-cfg/Makefile -run-make/link-dedup/Makefile run-make/link-framework/Makefile run-make/link-path-order/Makefile run-make/linkage-attr-on-static/Makefile run-make/llvm-ident/Makefile -run-make/llvm-outputs/Makefile run-make/long-linker-command-lines-cmd-exe/Makefile run-make/long-linker-command-lines/Makefile run-make/longjmp-across-rust/Makefile @@ -152,10 +113,8 @@ run-make/lto-linkage-used-attr/Makefile run-make/lto-no-link-whole-rlib/Makefile run-make/lto-readonly-lib/Makefile run-make/lto-smoke-c/Makefile -run-make/lto-smoke/Makefile run-make/macos-deployment-target/Makefile run-make/macos-fat-archive/Makefile -run-make/manual-crate-name/Makefile run-make/manual-link/Makefile run-make/many-crates-but-no-match/Makefile run-make/metadata-dep-info/Makefile @@ -164,27 +123,17 @@ run-make/min-global-align/Makefile run-make/mingw-export-call-convention/Makefile run-make/mismatching-target-triples/Makefile run-make/missing-crate-dependency/Makefile -run-make/mixing-deps/Makefile -run-make/mixing-formats/Makefile run-make/mixing-libs/Makefile run-make/msvc-opt-minsize/Makefile -run-make/multiple-emits/Makefile run-make/native-link-modifier-bundle/Makefile -run-make/native-link-modifier-verbatim-linker/Makefile -run-make/native-link-modifier-verbatim-rustc/Makefile run-make/native-link-modifier-whole-archive/Makefile run-make/no-alloc-shim/Makefile run-make/no-builtins-attribute/Makefile -run-make/no-builtins-lto/Makefile run-make/no-duplicate-libs/Makefile -run-make/no-intermediate-extras/Makefile run-make/obey-crate-type-flag/Makefile run-make/optimization-remarks-dir-pgo/Makefile run-make/optimization-remarks-dir/Makefile -run-make/output-filename-conflicts-with-directory/Makefile -run-make/output-filename-overwrites-input/Makefile run-make/output-type-permutations/Makefile -run-make/output-with-hyphens/Makefile run-make/override-aliased-flags/Makefile run-make/overwrite-input/Makefile run-make/panic-abort-eh_frame/Makefile @@ -194,15 +143,12 @@ run-make/pass-linker-flags/Makefile run-make/pass-non-c-like-enum-to-c/Makefile run-make/pdb-alt-path/Makefile run-make/pdb-buildinfo-cl-cmd/Makefile -run-make/pgo-branch-weights/Makefile run-make/pgo-gen-lto/Makefile run-make/pgo-gen-no-imp-symbols/Makefile run-make/pgo-gen/Makefile run-make/pgo-indirect-call-promotion/Makefile run-make/pgo-use/Makefile run-make/pointer-auth-link-with-c/Makefile -run-make/prefer-dylib/Makefile -run-make/prefer-rlib/Makefile run-make/pretty-print-to-file/Makefile run-make/pretty-print-with-dep-file/Makefile run-make/print-calling-conventions/Makefile @@ -224,7 +170,6 @@ run-make/remap-path-prefix-dwarf/Makefile run-make/remap-path-prefix/Makefile run-make/reproducible-build-2/Makefile run-make/reproducible-build/Makefile -run-make/resolve-rename/Makefile run-make/return-non-c-like-enum-from-c/Makefile run-make/return-non-c-like-enum/Makefile run-make/rlib-chain/Makefile @@ -234,27 +179,15 @@ run-make/rlib-format-packed-bundled-libs/Makefile run-make/rmeta-preferred/Makefile run-make/rustc-macro-dep-files/Makefile run-make/rustdoc-io-error/Makefile -run-make/rustdoc-scrape-examples-macros/Makefile -run-make/rustdoc-scrape-examples-multiple/Makefile -run-make/rustdoc-scrape-examples-test/Makefile -run-make/rustdoc-scrape-examples-whitespace/Makefile -run-make/rustdoc-verify-output-files/Makefile -run-make/rustdoc-with-output-option/Makefile -run-make/rustdoc-with-short-out-dir-option/Makefile run-make/sanitizer-cdylib-link/Makefile run-make/sanitizer-dylib-link/Makefile run-make/sanitizer-staticlib-link/Makefile -run-make/separate-link-fail/Makefile -run-make/separate-link/Makefile run-make/sepcomp-cci-copies/Makefile run-make/sepcomp-inlining/Makefile run-make/sepcomp-separate/Makefile run-make/share-generics-dylib/Makefile -run-make/short-ice/Makefile run-make/silly-file-names/Makefile run-make/simd-ffi/Makefile -run-make/simple-dylib/Makefile -run-make/simple-rlib/Makefile run-make/split-debuginfo/Makefile run-make/stable-symbol-names/Makefile run-make/static-dylib-by-default/Makefile @@ -263,13 +196,9 @@ run-make/static-pie/Makefile run-make/staticlib-blank-lib/Makefile run-make/staticlib-dylib-linkage/Makefile run-make/std-core-cycle/Makefile -run-make/suspicious-library/Makefile run-make/symbol-mangling-hashed/Makefile run-make/symbol-visibility/Makefile run-make/symbols-include-type-name/Makefile -run-make/symlinked-extern/Makefile -run-make/symlinked-libraries/Makefile -run-make/symlinked-rlib/Makefile run-make/sysroot-crates-are-unstable/Makefile run-make/target-cpu-native/Makefile run-make/target-specs/Makefile @@ -286,13 +215,8 @@ run-make/unknown-mod-stdin/Makefile run-make/unstable-flag-required/Makefile run-make/use-suggestions-rust-2018/Makefile run-make/used-cdylib-macos/Makefile -run-make/used/Makefile run-make/volatile-intrinsics/Makefile run-make/wasm-exceptions-nostd/Makefile run-make/wasm-override-linker/Makefile run-make/weird-output-filenames/Makefile -run-make/windows-binary-no-external-deps/Makefile -run-make/windows-safeseh/Makefile -run-make/windows-spawn/Makefile -run-make/windows-subsystem/Makefile run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile diff --git a/src/tools/tidy/src/alphabetical.rs b/src/tools/tidy/src/alphabetical.rs index 150a9594350e..a29286fa2c59 100644 --- a/src/tools/tidy/src/alphabetical.rs +++ b/src/tools/tidy/src/alphabetical.rs @@ -88,7 +88,7 @@ fn check_section<'a>( let trimmed_line = line.trim_start_matches(' '); if trimmed_line.starts_with("//") - || (trimmed_line.starts_with("#") && !trimmed_line.starts_with("#!")) + || (trimmed_line.starts_with('#') && !trimmed_line.starts_with("#!")) || trimmed_line.starts_with(is_close_bracket) { continue; diff --git a/src/tools/tidy/src/bins.rs b/src/tools/tidy/src/bins.rs index 64ba79dc1857..c82e8b6fee98 100644 --- a/src/tools/tidy/src/bins.rs +++ b/src/tools/tidy/src/bins.rs @@ -61,7 +61,7 @@ mod os_impl { fs::remove_file(&path).expect("Deleted temp file"); // If the file is executable, then we assume that this // filesystem does not track executability, so skip this check. - return if exec { Unsupported } else { Supported }; + if exec { Unsupported } else { Supported } } Err(e) => { // If the directory is read-only or we otherwise don't have rights, @@ -76,7 +76,7 @@ mod os_impl { panic!("unable to create temporary file `{:?}`: {:?}", path, e); } - }; + } } for &source_dir in sources { @@ -92,7 +92,7 @@ mod os_impl { } } - return true; + true } // FIXME: check when rust-installer test sh files will be removed, diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 1b73b67b7557..7337c9843c7b 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -27,7 +27,8 @@ const LICENSES: &[&str] = &[ "MIT OR Zlib OR Apache-2.0", // miniz_oxide "MIT", "MIT/Apache-2.0", - "Unicode-DFS-2016", // tinystr and icu4x + "Unicode-3.0", // icu4x + "Unicode-DFS-2016", // tinystr "Unlicense OR MIT", "Unlicense/MIT", "Zlib OR Apache-2.0 OR MIT", // tinyvec @@ -67,6 +68,7 @@ pub(crate) const WORKSPACES: &[(&str, ExceptionList, Option<(&[&str], &[&str])>) //("src/tools/miri/test-cargo-miri", &[], None), // FIXME uncomment once all deps are vendored //("src/tools/miri/test_dependencies", &[], None), // FIXME uncomment once all deps are vendored ("src/tools/rust-analyzer", EXCEPTIONS_RUST_ANALYZER, None), + ("src/tools/rustc-perf", EXCEPTIONS_RUSTC_PERF, None), ("src/tools/x", &[], None), // tidy-alphabetical-end ]; @@ -81,12 +83,10 @@ const EXCEPTIONS: ExceptionList = &[ ("ar_archive_writer", "Apache-2.0 WITH LLVM-exception"), // rustc ("colored", "MPL-2.0"), // rustfmt ("dissimilar", "Apache-2.0"), // rustdoc, rustc_lexer (few tests) via expect-test, (dev deps) - ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"), // opt-dist ("fluent-langneg", "Apache-2.0"), // rustc (fluent translations) ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target. FIXME: this dependency violates the documentation comment above. ("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot ("mdbook", "MPL-2.0"), // mdbook - ("openssl", "Apache-2.0"), // opt-dist ("option-ext", "MPL-2.0"), // cargo-miri (via `directories`) ("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), // rustc (license is the same as LLVM uses) ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 // cargo/... (because of serde) @@ -135,6 +135,7 @@ const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[ // tidy-alphabetical-start ("dissimilar", "Apache-2.0"), ("notify", "CC0-1.0"), + ("option-ext", "MPL-2.0"), ("pulldown-cmark-to-cmark", "Apache-2.0"), ("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 @@ -143,6 +144,22 @@ const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[ // tidy-alphabetical-end ]; +const EXCEPTIONS_RUSTC_PERF: ExceptionList = &[ + // tidy-alphabetical-start + ("alloc-no-stdlib", "BSD-3-Clause"), + ("alloc-stdlib", "BSD-3-Clause"), + ("brotli", "BSD-3-Clause/MIT"), + ("brotli-decompressor", "BSD-3-Clause/MIT"), + ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"), + ("inferno", "CDDL-1.0"), + ("instant", "BSD-3-Clause"), + ("ring", NON_STANDARD_LICENSE), // see EXCEPTIONS_NON_STANDARD_LICENSE_DEPS for more. + ("ryu", "Apache-2.0 OR BSL-1.0"), + ("snap", "BSD-3-Clause"), + ("subtle", "BSD-3-Clause"), + // tidy-alphabetical-end +]; + const EXCEPTIONS_CRANELIFT: ExceptionList = &[ // tidy-alphabetical-start ("cranelift-bforest", "Apache-2.0 WITH LLVM-exception"), @@ -179,6 +196,20 @@ const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[ ("r-efi", "MIT OR Apache-2.0 OR LGPL-2.1-or-later"), // LGPL is not acceptible, but we use it under MIT OR Apache-2.0 ]; +/// Placeholder for non-standard license file. +const NON_STANDARD_LICENSE: &str = "NON_STANDARD_LICENSE"; + +/// These dependencies have non-standard licenses but are genenrally permitted. +const EXCEPTIONS_NON_STANDARD_LICENSE_DEPS: &[&str] = &[ + // `ring` is included because it is an optional dependency of `hyper`, + // which is a training data in rustc-perf for optimized build. + // The license of it is generally `ISC AND MIT AND OpenSSL`, + // though the `package.license` field is not set. + // + // See https://github.com/briansmith/ring/issues/902 + "ring", +]; + /// These are the root crates that are part of the runtime. The licenses for /// these and all their dependencies *must not* be in the exception list. const RUNTIME_CRATES: &[&str] = &["std", "core", "alloc", "test", "panic_abort", "panic_unwind"]; @@ -412,32 +443,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ // tidy-alphabetical-end ]; -// These crates come from ICU4X and are licensed under the unicode license. -// It currently doesn't have an SPDX identifier, so they cannot put one there. -// See https://github.com/unicode-org/icu4x/pull/3875 -// FIXME: This should be removed once ICU4X crates update. -const ICU4X_UNICODE_LICENSE_DEPENDENCIES: &[&str] = &[ - // tidy-alphabetical-start - "icu_list", - "icu_list_data", - "icu_locid", - "icu_locid_transform", - "icu_locid_transform_data", - "icu_provider", - "icu_provider_adapters", - "icu_provider_macros", - "litemap", - "tinystr", - "writeable", - "yoke", - "yoke-derive", - "zerofrom", - "zerofrom-derive", - "zerovec", - "zerovec-derive", - // tidy-alphabetical-end -]; - const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ // tidy-alphabetical-start "ahash", @@ -611,6 +616,11 @@ fn check_license_exceptions(metadata: &Metadata, exceptions: &[(&str, &str)], ba for pkg in metadata.packages.iter().filter(|p| p.name == *name) { match &pkg.license { None => { + if *license == NON_STANDARD_LICENSE + && EXCEPTIONS_NON_STANDARD_LICENSE_DEPS.contains(&pkg.name.as_str()) + { + continue; + } tidy_error!( bad, "dependency exception `{}` does not declare a license expression", @@ -643,10 +653,6 @@ fn check_license_exceptions(metadata: &Metadata, exceptions: &[(&str, &str)], ba let license = match &pkg.license { Some(license) => license, None => { - if ICU4X_UNICODE_LICENSE_DEPENDENCIES.contains(&pkg.name.as_str()) { - // See the comment on ICU4X_UNICODE_LICENSE_DEPENDENCIES. - continue; - } tidy_error!(bad, "dependency `{}` does not define a license expression", pkg.id); continue; } @@ -693,11 +699,9 @@ fn check_permitted_dependencies( for dep in deps { let dep = pkg_from_id(metadata, dep); // If this path is in-tree, we don't require it to be explicitly permitted. - if dep.source.is_some() { - if !permitted_dependencies.contains(dep.name.as_str()) { - tidy_error!(bad, "Dependency for {descr} not explicitly permitted: {}", dep.id); - has_permitted_dep_error = true; - } + if dep.source.is_some() && !permitted_dependencies.contains(dep.name.as_str()) { + tidy_error!(bad, "Dependency for {descr} not explicitly permitted: {}", dep.id); + has_permitted_dep_error = true; } } diff --git a/src/tools/tidy/src/error_codes.rs b/src/tools/tidy/src/error_codes.rs index 39f7e70b6939..47e2be761e6a 100644 --- a/src/tools/tidy/src/error_codes.rs +++ b/src/tools/tidy/src/error_codes.rs @@ -308,11 +308,9 @@ fn check_error_codes_tests( for line in file.lines() { let s = line.trim(); // Assuming the line starts with `error[E`, we can substring the error code out. - if s.starts_with("error[E") { - if &s[6..11] == code { - found_code = true; - break; - } + if s.starts_with("error[E") && &s[6..11] == code { + found_code = true; + break; }; } diff --git a/src/tools/tidy/src/ext_tool_checks.rs b/src/tools/tidy/src/ext_tool_checks.rs index 40e75d1d3fac..995ad58cb621 100644 --- a/src/tools/tidy/src/ext_tool_checks.rs +++ b/src/tools/tidy/src/ext_tool_checks.rs @@ -24,9 +24,8 @@ use std::io; use std::path::{Path, PathBuf}; use std::process::Command; -/// Minimum python revision is 3.7 for ruff -const MIN_PY_REV: (u32, u32) = (3, 7); -const MIN_PY_REV_STR: &str = "≥3.7"; +const MIN_PY_REV: (u32, u32) = (3, 9); +const MIN_PY_REV_STR: &str = "≥3.9"; /// Path to find the python executable within a virtual environment #[cfg(target_os = "windows")] @@ -78,9 +77,9 @@ fn check_impl( let mut py_path = None; let (cfg_args, file_args): (Vec<_>, Vec<_>) = pos_args - .into_iter() + .iter() .map(OsStr::new) - .partition(|arg| arg.to_str().is_some_and(|s| s.starts_with("-"))); + .partition(|arg| arg.to_str().is_some_and(|s| s.starts_with('-'))); if python_lint || python_fmt { let venv_path = outdir.join("venv"); @@ -223,17 +222,8 @@ fn get_or_create_venv(venv_path: &Path, src_reqs_path: &Path) -> Result Result<(), Error> { /// Preferred python versions in order. Newest to oldest then current /// development versions - const TRY_PY: &[&str] = &[ - "python3.11", - "python3.10", - "python3.9", - "python3.8", - "python3.7", - "python3", - "python", - "python3.12", - "python3.13", - ]; + const TRY_PY: &[&str] = + &["python3.11", "python3.10", "python3.9", "python3", "python", "python3.12", "python3.13"]; let mut sys_py = None; let mut found = Vec::new(); @@ -274,13 +264,19 @@ fn create_venv_at_path(path: &Path) -> Result<(), Error> { if out.status.success() { return Ok(()); } - let err = if String::from_utf8_lossy(&out.stderr).contains("No module named virtualenv") { - Error::Generic(format!( + + let stderr = String::from_utf8_lossy(&out.stderr); + let err = if stderr.contains("No module named virtualenv") { + Error::Generic( "virtualenv not found: you may need to install it \ (`python3 -m pip install venv`)" - )) + .to_owned(), + ) } else { - Error::Generic(format!("failed to create venv at '{}' using {sys_py}", path.display())) + Error::Generic(format!( + "failed to create venv at '{}' using {sys_py}: {stderr}", + path.display() + )) }; Err(err) } @@ -320,7 +316,7 @@ fn install_requirements( } let stat = Command::new(py_path) - .args(["-m", "pip", "install", "--require-hashes", "-r"]) + .args(["-m", "pip", "install", "--quiet", "--require-hashes", "-r"]) .arg(src_reqs_path) .status()?; if !stat.success() { diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index ff71ca537256..2118de5f2041 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -4,7 +4,11 @@ use std::fs; use std::path::Path; /// List of allowed sources for packages. -const ALLOWED_SOURCES: &[&str] = &["\"registry+https://github.com/rust-lang/crates.io-index\""]; +const ALLOWED_SOURCES: &[&str] = &[ + r#""registry+https://github.com/rust-lang/crates.io-index""#, + // This is `rust_team_data` used by `site` in src/tools/rustc-perf, + r#""git+https://github.com/rust-lang/team#a5260e76d3aa894c64c56e6ddc8545b9a98043ec""#, +]; /// Checks for external package sources. `root` is the path to the directory that contains the /// workspace `Cargo.toml`. diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 6b8106bbc214..3c7284ce6db6 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -2122,7 +2122,6 @@ ui/issues/issue-33687.rs ui/issues/issue-33770.rs ui/issues/issue-3389.rs ui/issues/issue-33941.rs -ui/issues/issue-33992.rs ui/issues/issue-34047.rs ui/issues/issue-34074.rs ui/issues/issue-34209.rs @@ -2446,7 +2445,6 @@ ui/issues/issue-53300.rs ui/issues/issue-53333.rs ui/issues/issue-53348.rs ui/issues/issue-53419.rs -ui/issues/issue-53498.rs ui/issues/issue-53568.rs ui/issues/issue-5358-1.rs ui/issues/issue-53728.rs @@ -2762,7 +2760,6 @@ ui/lint/issue-1866.rs ui/lint/issue-19102.rs ui/lint/issue-20343.rs ui/lint/issue-30302.rs -ui/lint/issue-31924-non-snake-ffi.rs ui/lint/issue-34798.rs ui/lint/issue-35075.rs ui/lint/issue-47775-nested-macro-unnecessary-parens-arg.rs @@ -2771,7 +2768,6 @@ ui/lint/issue-54099-camel-case-underscore-types.rs ui/lint/issue-57410-1.rs ui/lint/issue-57410.rs ui/lint/issue-63364.rs -ui/lint/issue-66362-no-snake-case-warning-for-field-puns.rs ui/lint/issue-70819-dont-override-forbid-in-same-scope.rs ui/lint/issue-79546-fuel-ice.rs ui/lint/issue-79744.rs @@ -2779,7 +2775,6 @@ ui/lint/issue-80988.rs ui/lint/issue-81218.rs ui/lint/issue-83477.rs ui/lint/issue-87274-paren-parent.rs -ui/lint/issue-89469.rs ui/lint/issue-90614-accept-allow-text-direction-codepoint-in-comment-lint.rs ui/lint/issue-97094.rs ui/lint/issue-99387.rs @@ -3988,8 +3983,6 @@ ui/test-attrs/issue-52557.rs ui/test-attrs/issue-53675-a-test-called-panic.rs ui/threads-sendsync/issue-24313.rs ui/threads-sendsync/issue-29488.rs -ui/threads-sendsync/issue-43733-2.rs -ui/threads-sendsync/issue-43733.rs ui/threads-sendsync/issue-4446.rs ui/threads-sendsync/issue-4448.rs ui/threads-sendsync/issue-8827.rs diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index 9cabab582d0d..7bcb85335e07 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -243,7 +243,7 @@ pub fn is_in(full_path: &Path, parent_folder_to_find: &str, folder_to_find: &str if parent.file_name().map_or_else( || false, |f| { - f.to_string_lossy() == folder_to_find + f == folder_to_find && parent .parent() .and_then(|f| f.file_name()) @@ -444,7 +444,9 @@ pub fn check(path: &Path, bad: &mut bool) { suppressible_tidy_err!(err, skip_cr, "CR character"); } if filename != "style.rs" { - if trimmed.contains("TODO") { + // Allow using TODO in diagnostic suggestions by marking the + // relevant line with `// ignore-tidy-todo`. + if trimmed.contains("TODO") && !trimmed.contains("ignore-tidy-todo") { err( "TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME", ) @@ -461,10 +463,13 @@ pub fn check(path: &Path, bad: &mut bool) { } } // for now we just check libcore - if trimmed.contains("unsafe {") && !trimmed.starts_with("//") && !last_safety_comment { - if file.components().any(|c| c.as_os_str() == "core") && !is_test { - suppressible_tidy_err!(err, skip_undocumented_unsafe, "undocumented unsafe"); - } + if trimmed.contains("unsafe {") + && !trimmed.starts_with("//") + && !last_safety_comment + && file.components().any(|c| c.as_os_str() == "core") + && !is_test + { + suppressible_tidy_err!(err, skip_undocumented_unsafe, "undocumented unsafe"); } if trimmed.contains("// SAFETY:") { last_safety_comment = true; @@ -485,10 +490,10 @@ pub fn check(path: &Path, bad: &mut bool) { "copyright notices attributed to the Rust Project Developers are deprecated" ); } - if !file.components().any(|c| c.as_os_str() == "rustc_baked_icu_data") { - if is_unexplained_ignore(&extension, line) { - err(UNEXPLAINED_IGNORE_DOCTEST_INFO); - } + if !file.components().any(|c| c.as_os_str() == "rustc_baked_icu_data") + && is_unexplained_ignore(&extension, line) + { + err(UNEXPLAINED_IGNORE_DOCTEST_INFO); } if filename.ends_with(".cpp") && line.contains("llvm_unreachable") { @@ -523,26 +528,24 @@ pub fn check(path: &Path, bad: &mut bool) { backtick_count += comment_text.chars().filter(|ch| *ch == '`').count(); } comment_block = Some((start_line, backtick_count)); - } else { - if let Some((start_line, backtick_count)) = comment_block.take() { - if backtick_count % 2 == 1 { - let mut err = |msg: &str| { - tidy_error!(bad, "{}:{start_line}: {msg}", file.display()); - }; - let block_len = (i + 1) - start_line; - if block_len == 1 { - suppressible_tidy_err!( - err, - skip_odd_backticks, - "comment with odd number of backticks" - ); - } else { - suppressible_tidy_err!( - err, - skip_odd_backticks, - "{block_len}-line comment block with odd number of backticks" - ); - } + } else if let Some((start_line, backtick_count)) = comment_block.take() { + if backtick_count % 2 == 1 { + let mut err = |msg: &str| { + tidy_error!(bad, "{}:{start_line}: {msg}", file.display()); + }; + let block_len = (i + 1) - start_line; + if block_len == 1 { + suppressible_tidy_err!( + err, + skip_odd_backticks, + "comment with odd number of backticks" + ); + } else { + suppressible_tidy_err!( + err, + skip_odd_backticks, + "{block_len}-line comment block with odd number of backticks" + ); } } } diff --git a/src/tools/tidy/src/target_policy.rs b/src/tools/tidy/src/target_policy.rs index 382488e57211..06210c8cdb20 100644 --- a/src/tools/tidy/src/target_policy.rs +++ b/src/tools/tidy/src/target_policy.rs @@ -12,6 +12,10 @@ const EXCEPTIONS: &[&str] = &[ // FIXME: disabled since it fails on CI saying the csky component is missing "csky_unknown_linux_gnuabiv2", "csky_unknown_linux_gnuabiv2hf", + // FIXME: disabled since it requires a custom LLVM until the upstream LLVM adds support for the target (https://github.com/espressif/llvm-project/issues/4) + "xtensa_esp32_none_elf", + "xtensa_esp32s2_none_elf", + "xtensa_esp32s3_none_elf", ]; pub fn check(root_path: &Path, bad: &mut bool) { diff --git a/src/tools/tidy/src/target_specific_tests.rs b/src/tools/tidy/src/target_specific_tests.rs index c876aae494dd..8be27d1e117c 100644 --- a/src/tools/tidy/src/target_specific_tests.rs +++ b/src/tools/tidy/src/target_specific_tests.rs @@ -10,6 +10,26 @@ use crate::walk::filter_not_rust; const LLVM_COMPONENTS_HEADER: &str = "needs-llvm-components:"; const COMPILE_FLAGS_HEADER: &str = "compile-flags:"; +const KNOWN_LLVM_COMPONENTS: &[&str] = &[ + "aarch64", + "arm", + "avr", + "bpf", + "csky", + "hexagon", + "loongarch", + "m68k", + "mips", + "msp430", + "nvptx", + "powerpc", + "riscv", + "sparc", + "systemz", + "webassembly", + "x86", +]; + #[derive(Default, Debug)] struct RevisionInfo<'a> { target_arch: Option<&'a str>, @@ -33,9 +53,9 @@ pub fn check(path: &Path, bad: &mut bool) { } else if directive.starts_with(COMPILE_FLAGS_HEADER) { let compile_flags = &directive[COMPILE_FLAGS_HEADER.len()..]; if let Some((_, v)) = compile_flags.split_once("--target") { - if let Some((arch, _)) = - v.trim_start_matches(|c| c == ' ' || c == '=').split_once("-") - { + let v = v.trim_start_matches(|c| c == ' ' || c == '='); + let v = if v == "{{target}}" { Some((v, v)) } else { v.split_once("-") }; + if let Some((arch, _)) = v { let info = header_map.entry(revision).or_insert(RevisionInfo::default()); info.target_arch.replace(arch); } else { @@ -68,6 +88,20 @@ pub fn check(path: &Path, bad: &mut bool) { // gathered. } } + if let Some(llvm_components) = llvm_components { + for component in llvm_components { + // Ensure the given component even exists. + // This is somewhat redundant with COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS, + // but helps detect such problems earlier (PR CI rather than bors CI). + if !KNOWN_LLVM_COMPONENTS.contains(component) { + eprintln!( + "{}: revision {} specifies unknown LLVM component `{}`", + file, rev, component + ); + *bad = true; + } + } + } } }); } diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 6e92dab1abc1..f2eeda339d80 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -12,11 +12,10 @@ use std::path::{Path, PathBuf}; // should all be 1000 or lower. Limits significantly smaller than 1000 are also // desirable, because large numbers of files are unwieldy in general. See issue // #73494. -const ENTRY_LIMIT: usize = 900; +const ENTRY_LIMIT: u32 = 900; // FIXME: The following limits should be reduced eventually. -const ISSUES_ENTRY_LIMIT: usize = 1676; -const ROOT_ENTRY_LIMIT: usize = 859; +const ISSUES_ENTRY_LIMIT: u32 = 1672; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files @@ -54,7 +53,7 @@ const EXTENSION_EXCEPTION_PATHS: &[&str] = &[ ]; fn check_entries(tests_path: &Path, bad: &mut bool) { - let mut directories: HashMap = HashMap::new(); + let mut directories: HashMap = HashMap::new(); for dir in Walk::new(&tests_path.join("ui")) { if let Ok(entry) = dir { @@ -63,14 +62,10 @@ fn check_entries(tests_path: &Path, bad: &mut bool) { } } - let (mut max, mut max_root, mut max_issues) = (0usize, 0usize, 0usize); + let (mut max, mut max_issues) = (0, 0); for (dir_path, count) in directories { - // Use special values for these dirs. - let is_root = tests_path.join("ui") == dir_path; let is_issues_dir = tests_path.join("ui/issues") == dir_path; - let (limit, maxcnt) = if is_root { - (ROOT_ENTRY_LIMIT, &mut max_root) - } else if is_issues_dir { + let (limit, maxcnt) = if is_issues_dir { (ISSUES_ENTRY_LIMIT, &mut max_issues) } else { (ENTRY_LIMIT, &mut max) @@ -87,12 +82,6 @@ fn check_entries(tests_path: &Path, bad: &mut bool) { ); } } - if ROOT_ENTRY_LIMIT > max_root { - tidy_error!( - bad, - "`ROOT_ENTRY_LIMIT` is too high (is {ROOT_ENTRY_LIMIT}, should be {max_root})" - ); - } if ISSUES_ENTRY_LIMIT > max_issues { tidy_error!( bad, diff --git a/src/tools/tidy/src/walk.rs b/src/tools/tidy/src/walk.rs index 851c21f1c0f0..1cecf998e284 100644 --- a/src/tools/tidy/src/walk.rs +++ b/src/tools/tidy/src/walk.rs @@ -16,8 +16,10 @@ pub fn filter_dirs(path: &Path) -> bool { "library/stdarch", "src/tools/cargo", "src/tools/clippy", + "src/tools/libcxx-version", "src/tools/miri", "src/tools/rust-analyzer", + "src/tools/rustc-perf", "src/tools/rustfmt", "src/doc/book", "src/doc/edition-guide", @@ -77,13 +79,11 @@ pub(crate) fn walk_no_read( let walker = walker.filter_entry(move |e| { !skip(e.path(), e.file_type().map(|ft| ft.is_dir()).unwrap_or(false)) }); - for entry in walker.build() { - if let Ok(entry) = entry { - if entry.file_type().map_or(true, |kind| kind.is_dir() || kind.is_symlink()) { - continue; - } - f(&entry); + for entry in walker.build().flatten() { + if entry.file_type().map_or(true, |kind| kind.is_dir() || kind.is_symlink()) { + continue; } + f(&entry); } } @@ -95,11 +95,9 @@ pub(crate) fn walk_dir( ) { let mut walker = ignore::WalkBuilder::new(path); let walker = walker.filter_entry(move |e| !skip(e.path())); - for entry in walker.build() { - if let Ok(entry) = entry { - if entry.path().is_dir() { - f(&entry); - } + for entry in walker.build().flatten() { + if entry.path().is_dir() { + f(&entry); } } } diff --git a/src/tools/tidy/src/x_version.rs b/src/tools/tidy/src/x_version.rs index c470d502a654..55bfbed8b0e1 100644 --- a/src/tools/tidy/src/x_version.rs +++ b/src/tools/tidy/src/x_version.rs @@ -52,7 +52,7 @@ pub fn check(root: &Path, cargo: &Path, bad: &mut bool) { ); } } else { - return tidy_error!(bad, "failed to check version of `x`: {}", cargo_list.status); + tidy_error!(bad, "failed to check version of `x`: {}", cargo_list.status) } } diff --git a/src/version b/src/version index aaceec04e040..dbd41264aa9f 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.80.0 +1.81.0 diff --git a/tests/assembly/align_offset.rs b/tests/assembly/align_offset.rs index dbf599a741fd..d9902ce336b0 100644 --- a/tests/assembly/align_offset.rs +++ b/tests/assembly/align_offset.rs @@ -1,7 +1,7 @@ //@ assembly-output: emit-asm //@ compile-flags: -Copt-level=1 //@ only-x86_64 -#![crate_type="rlib"] +#![crate_type = "rlib"] // CHECK-LABEL: align_offset_byte_ptr // CHECK: leaq 31 diff --git a/tests/assembly/asm/global_asm.rs b/tests/assembly/asm/global_asm.rs index 8f824563e8ae..22cf4bdb15b7 100644 --- a/tests/assembly/asm/global_asm.rs +++ b/tests/assembly/asm/global_asm.rs @@ -25,9 +25,9 @@ global_asm!("movl ${}, %ecx", const 5, options(att_syntax)); global_asm!("call {}", sym my_func); // CHECK: lea rax, [rip + MY_STATIC] global_asm!("lea rax, [rip + {}]", sym MY_STATIC); -// CHECK: call _RNvCsddMtV7nAi4C_10global_asm6foobar +// CHECK: call _RNvC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_10global_asm6foobar global_asm!("call {}", sym foobar); -// CHECK: _RNvCsddMtV7nAi4C_10global_asm6foobar: +// CHECK: _RNvC[[CRATE_IDENT]]_10global_asm6foobar: fn foobar() { loop {} } diff --git a/tests/assembly/asm/inline-asm-avx.rs b/tests/assembly/asm/inline-asm-avx.rs index 7e52a798dedb..630acbb971a8 100644 --- a/tests/assembly/asm/inline-asm-avx.rs +++ b/tests/assembly/asm/inline-asm-avx.rs @@ -5,8 +5,8 @@ #![feature(portable_simd)] -use std::simd::Simd; use std::arch::asm; +use std::simd::Simd; #[target_feature(enable = "avx")] #[no_mangle] diff --git a/tests/assembly/asm/x86-types.rs b/tests/assembly/asm/x86-types.rs index 2b4ebb05349b..8e229614420b 100644 --- a/tests/assembly/asm/x86-types.rs +++ b/tests/assembly/asm/x86-types.rs @@ -7,7 +7,7 @@ //@ compile-flags: -C llvm-args=--x86-asm-syntax=intel //@ compile-flags: -C target-feature=+avx512bw -#![feature(no_core, lang_items, rustc_attrs, repr_simd)] +#![feature(no_core, lang_items, rustc_attrs, repr_simd, f16, f128)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register, non_camel_case_types)] @@ -41,6 +41,8 @@ pub struct i32x4(i32, i32, i32, i32); #[repr(simd)] pub struct i64x2(i64, i64); #[repr(simd)] +pub struct f16x8(f16, f16, f16, f16, f16, f16, f16, f16); +#[repr(simd)] pub struct f32x4(f32, f32, f32, f32); #[repr(simd)] pub struct f64x2(f64, f64); @@ -87,6 +89,8 @@ pub struct i32x8(i32, i32, i32, i32, i32, i32, i32, i32); #[repr(simd)] pub struct i64x4(i64, i64, i64, i64); #[repr(simd)] +pub struct f16x16(f16, f16, f16, f16, f16, f16, f16, f16, f16, f16, f16, f16, f16, f16, f16, f16); +#[repr(simd)] pub struct f32x8(f32, f32, f32, f32, f32, f32, f32, f32); #[repr(simd)] pub struct f64x4(f64, f64, f64, f64); @@ -198,35 +202,59 @@ pub struct i32x16(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i3 #[repr(simd)] pub struct i64x8(i64, i64, i64, i64, i64, i64, i64, i64); #[repr(simd)] +pub struct f16x32( + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, + f16, +); +#[repr(simd)] pub struct f32x16(f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32); #[repr(simd)] pub struct f64x8(f64, f64, f64, f64, f64, f64, f64, f64); -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for f32 {} -impl Copy for i64 {} -impl Copy for f64 {} -impl Copy for ptr {} -impl Copy for i8x16 {} -impl Copy for i16x8 {} -impl Copy for i32x4 {} -impl Copy for i64x2 {} -impl Copy for f32x4 {} -impl Copy for f64x2 {} -impl Copy for i8x32 {} -impl Copy for i16x16 {} -impl Copy for i32x8 {} -impl Copy for i64x4 {} -impl Copy for f32x8 {} -impl Copy for f64x4 {} -impl Copy for i8x64 {} -impl Copy for i16x32 {} -impl Copy for i32x16 {} -impl Copy for i64x8 {} -impl Copy for f32x16 {} -impl Copy for f64x8 {} +macro_rules! impl_copy { + ($($ty:ident)*) => { + $( + impl Copy for $ty {} + )* + }; +} + +impl_copy!( + i8 i16 f16 i32 f32 i64 f64 f128 ptr + i8x16 i16x8 i32x4 i64x2 f16x8 f32x4 f64x2 + i8x32 i16x16 i32x8 i64x4 f16x16 f32x8 f64x4 + i8x64 i16x32 i32x16 i64x8 f16x32 f32x16 f64x8 +); extern "C" { fn extern_func(); @@ -292,6 +320,13 @@ macro_rules! check_reg { // CHECK: #NO_APP check!(reg_i16 i16 reg "mov"); +// CHECK-LABEL: reg_f16: +// CHECK: #APP +// x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} +// i686: mov e{{[a-z0-9]+}}, e{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_f16 f16 reg "mov"); + // CHECK-LABEL: reg_i32: // CHECK: #APP // x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} @@ -334,6 +369,13 @@ check!(reg_ptr ptr reg "mov"); // CHECK: #NO_APP check!(reg_abcd_i16 i16 reg_abcd "mov"); +// CHECK-LABEL: reg_abcd_f16: +// CHECK: #APP +// x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} +// i686: mov e{{[a-z0-9]+}}, e{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(reg_abcd_f16 f16 reg_abcd "mov"); + // CHECK-LABEL: reg_abcd_i32: // CHECK: #APP // x86_64: mov r{{[a-z0-9]+}}, r{{[a-z0-9]+}} @@ -375,6 +417,12 @@ check!(reg_abcd_ptr ptr reg_abcd "mov"); // CHECK: #NO_APP check!(reg_byte i8 reg_byte "mov"); +// CHECK-LABEL: xmm_reg_f16: +// CHECK: #APP +// CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} +// CHECK: #NO_APP +check!(xmm_reg_f16 f16 xmm_reg "movaps"); + // CHECK-LABEL: xmm_reg_i32: // CHECK: #APP // CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} @@ -399,6 +447,12 @@ check!(xmm_reg_i64 i64 xmm_reg "movaps"); // CHECK: #NO_APP check!(xmm_reg_f64 f64 xmm_reg "movaps"); +// CHECK-LABEL: xmm_reg_f128: +// CHECK: #APP +// CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} +// CHECK: #NO_APP +check!(xmm_reg_f128 f128 xmm_reg "movaps"); + // CHECK-LABEL: xmm_reg_ptr: // CHECK: #APP // CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} @@ -429,6 +483,12 @@ check!(xmm_reg_i32x4 i32x4 xmm_reg "movaps"); // CHECK: #NO_APP check!(xmm_reg_i64x2 i64x2 xmm_reg "movaps"); +// CHECK-LABEL: xmm_reg_f16x8: +// CHECK: #APP +// CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} +// CHECK: #NO_APP +check!(xmm_reg_f16x8 f16x8 xmm_reg "movaps"); + // CHECK-LABEL: xmm_reg_f32x4: // CHECK: #APP // CHECK: movaps xmm{{[0-9]+}}, xmm{{[0-9]+}} @@ -441,6 +501,12 @@ check!(xmm_reg_f32x4 f32x4 xmm_reg "movaps"); // CHECK: #NO_APP check!(xmm_reg_f64x2 f64x2 xmm_reg "movaps"); +// CHECK-LABEL: ymm_reg_f16: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_f16 f16 ymm_reg "vmovaps"); + // CHECK-LABEL: ymm_reg_i32: // CHECK: #APP // CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} @@ -465,6 +531,12 @@ check!(ymm_reg_i64 i64 ymm_reg "vmovaps"); // CHECK: #NO_APP check!(ymm_reg_f64 f64 ymm_reg "vmovaps"); +// CHECK-LABEL: ymm_reg_f128: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_f128 f128 ymm_reg "vmovaps"); + // CHECK-LABEL: ymm_reg_ptr: // CHECK: #APP // CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} @@ -495,6 +567,12 @@ check!(ymm_reg_i32x4 i32x4 ymm_reg "vmovaps"); // CHECK: #NO_APP check!(ymm_reg_i64x2 i64x2 ymm_reg "vmovaps"); +// CHECK-LABEL: ymm_reg_f16x8: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_f16x8 f16x8 ymm_reg "vmovaps"); + // CHECK-LABEL: ymm_reg_f32x4: // CHECK: #APP // CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} @@ -531,6 +609,12 @@ check!(ymm_reg_i32x8 i32x8 ymm_reg "vmovaps"); // CHECK: #NO_APP check!(ymm_reg_i64x4 i64x4 ymm_reg "vmovaps"); +// CHECK-LABEL: ymm_reg_f16x16: +// CHECK: #APP +// CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} +// CHECK: #NO_APP +check!(ymm_reg_f16x16 f16x16 ymm_reg "vmovaps"); + // CHECK-LABEL: ymm_reg_f32x8: // CHECK: #APP // CHECK: vmovaps ymm{{[0-9]+}}, ymm{{[0-9]+}} @@ -543,6 +627,12 @@ check!(ymm_reg_f32x8 f32x8 ymm_reg "vmovaps"); // CHECK: #NO_APP check!(ymm_reg_f64x4 f64x4 ymm_reg "vmovaps"); +// CHECK-LABEL: zmm_reg_f16: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_f16 f16 zmm_reg "vmovaps"); + // CHECK-LABEL: zmm_reg_i32: // CHECK: #APP // CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} @@ -567,6 +657,12 @@ check!(zmm_reg_i64 i64 zmm_reg "vmovaps"); // CHECK: #NO_APP check!(zmm_reg_f64 f64 zmm_reg "vmovaps"); +// CHECK-LABEL: zmm_reg_f128: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_f128 f128 zmm_reg "vmovaps"); + // CHECK-LABEL: zmm_reg_ptr: // CHECK: #APP // CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} @@ -597,6 +693,12 @@ check!(zmm_reg_i32x4 i32x4 zmm_reg "vmovaps"); // CHECK: #NO_APP check!(zmm_reg_i64x2 i64x2 zmm_reg "vmovaps"); +// CHECK-LABEL: zmm_reg_f16x8: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_f16x8 f16x8 zmm_reg "vmovaps"); + // CHECK-LABEL: zmm_reg_f32x4: // CHECK: #APP // CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} @@ -633,6 +735,12 @@ check!(zmm_reg_i32x8 i32x8 zmm_reg "vmovaps"); // CHECK: #NO_APP check!(zmm_reg_i64x4 i64x4 zmm_reg "vmovaps"); +// CHECK-LABEL: zmm_reg_f16x16: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_f16x16 f16x16 zmm_reg "vmovaps"); + // CHECK-LABEL: zmm_reg_f32x8: // CHECK: #APP // CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} @@ -669,6 +777,12 @@ check!(zmm_reg_i32x16 i32x16 zmm_reg "vmovaps"); // CHECK: #NO_APP check!(zmm_reg_i64x8 i64x8 zmm_reg "vmovaps"); +// CHECK-LABEL: zmm_reg_f16x32: +// CHECK: #APP +// CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} +// CHECK: #NO_APP +check!(zmm_reg_f16x32 f16x32 zmm_reg "vmovaps"); + // CHECK-LABEL: zmm_reg_f32x16: // CHECK: #APP // CHECK: vmovaps zmm{{[0-9]+}}, zmm{{[0-9]+}} @@ -717,6 +831,12 @@ check!(kreg_ptr ptr kreg "kmovq"); // CHECK: #NO_APP check_reg!(eax_i16 i16 "eax" "mov"); +// CHECK-LABEL: eax_f16: +// CHECK: #APP +// CHECK: mov eax, eax +// CHECK: #NO_APP +check_reg!(eax_f16 f16 "eax" "mov"); + // CHECK-LABEL: eax_i32: // CHECK: #APP // CHECK: mov eax, eax @@ -756,6 +876,12 @@ check_reg!(eax_ptr ptr "eax" "mov"); #[cfg(i686)] check_reg!(ah_byte i8 "ah" "mov"); +// CHECK-LABEL: xmm0_f16: +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check_reg!(xmm0_f16 f16 "xmm0" "movaps"); + // CHECK-LABEL: xmm0_i32: // CHECK: #APP // CHECK: movaps xmm0, xmm0 @@ -780,6 +906,12 @@ check_reg!(xmm0_i64 i64 "xmm0" "movaps"); // CHECK: #NO_APP check_reg!(xmm0_f64 f64 "xmm0" "movaps"); +// CHECK-LABEL: xmm0_f128: +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check_reg!(xmm0_f128 f128 "xmm0" "movaps"); + // CHECK-LABEL: xmm0_ptr: // CHECK: #APP // CHECK: movaps xmm0, xmm0 @@ -810,6 +942,12 @@ check_reg!(xmm0_i32x4 i32x4 "xmm0" "movaps"); // CHECK: #NO_APP check_reg!(xmm0_i64x2 i64x2 "xmm0" "movaps"); +// CHECK-LABEL: xmm0_f16x8: +// CHECK: #APP +// CHECK: movaps xmm0, xmm0 +// CHECK: #NO_APP +check_reg!(xmm0_f16x8 f16x8 "xmm0" "movaps"); + // CHECK-LABEL: xmm0_f32x4: // CHECK: #APP // CHECK: movaps xmm0, xmm0 @@ -822,6 +960,12 @@ check_reg!(xmm0_f32x4 f32x4 "xmm0" "movaps"); // CHECK: #NO_APP check_reg!(xmm0_f64x2 f64x2 "xmm0" "movaps"); +// CHECK-LABEL: ymm0_f16: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_f16 f16 "ymm0" "vmovaps"); + // CHECK-LABEL: ymm0_i32: // CHECK: #APP // CHECK: vmovaps ymm0, ymm0 @@ -846,6 +990,12 @@ check_reg!(ymm0_i64 i64 "ymm0" "vmovaps"); // CHECK: #NO_APP check_reg!(ymm0_f64 f64 "ymm0" "vmovaps"); +// CHECK-LABEL: ymm0_f128: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_f128 f128 "ymm0" "vmovaps"); + // CHECK-LABEL: ymm0_ptr: // CHECK: #APP // CHECK: vmovaps ymm0, ymm0 @@ -876,6 +1026,12 @@ check_reg!(ymm0_i32x4 i32x4 "ymm0" "vmovaps"); // CHECK: #NO_APP check_reg!(ymm0_i64x2 i64x2 "ymm0" "vmovaps"); +// CHECK-LABEL: ymm0_f16x8: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_f16x8 f16x8 "ymm0" "vmovaps"); + // CHECK-LABEL: ymm0_f32x4: // CHECK: #APP // CHECK: vmovaps ymm0, ymm0 @@ -912,6 +1068,12 @@ check_reg!(ymm0_i32x8 i32x8 "ymm0" "vmovaps"); // CHECK: #NO_APP check_reg!(ymm0_i64x4 i64x4 "ymm0" "vmovaps"); +// CHECK-LABEL: ymm0_f16x16: +// CHECK: #APP +// CHECK: vmovaps ymm0, ymm0 +// CHECK: #NO_APP +check_reg!(ymm0_f16x16 f16x16 "ymm0" "vmovaps"); + // CHECK-LABEL: ymm0_f32x8: // CHECK: #APP // CHECK: vmovaps ymm0, ymm0 @@ -924,6 +1086,12 @@ check_reg!(ymm0_f32x8 f32x8 "ymm0" "vmovaps"); // CHECK: #NO_APP check_reg!(ymm0_f64x4 f64x4 "ymm0" "vmovaps"); +// CHECK-LABEL: zmm0_f16: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_f16 f16 "zmm0" "vmovaps"); + // CHECK-LABEL: zmm0_i32: // CHECK: #APP // CHECK: vmovaps zmm0, zmm0 @@ -948,6 +1116,12 @@ check_reg!(zmm0_i64 i64 "zmm0" "vmovaps"); // CHECK: #NO_APP check_reg!(zmm0_f64 f64 "zmm0" "vmovaps"); +// CHECK-LABEL: zmm0_f128: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_f128 f128 "zmm0" "vmovaps"); + // CHECK-LABEL: zmm0_ptr: // CHECK: #APP // CHECK: vmovaps zmm0, zmm0 @@ -978,6 +1152,12 @@ check_reg!(zmm0_i32x4 i32x4 "zmm0" "vmovaps"); // CHECK: #NO_APP check_reg!(zmm0_i64x2 i64x2 "zmm0" "vmovaps"); +// CHECK-LABEL: zmm0_f16x8: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_f16x8 f16x8 "zmm0" "vmovaps"); + // CHECK-LABEL: zmm0_f32x4: // CHECK: #APP // CHECK: vmovaps zmm0, zmm0 @@ -1014,6 +1194,12 @@ check_reg!(zmm0_i32x8 i32x8 "zmm0" "vmovaps"); // CHECK: #NO_APP check_reg!(zmm0_i64x4 i64x4 "zmm0" "vmovaps"); +// CHECK-LABEL: zmm0_f16x16: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_f16x16 f16x16 "zmm0" "vmovaps"); + // CHECK-LABEL: zmm0_f32x8: // CHECK: #APP // CHECK: vmovaps zmm0, zmm0 @@ -1050,6 +1236,12 @@ check_reg!(zmm0_i32x16 i32x16 "zmm0" "vmovaps"); // CHECK: #NO_APP check_reg!(zmm0_i64x8 i64x8 "zmm0" "vmovaps"); +// CHECK-LABEL: zmm0_f16x32: +// CHECK: #APP +// CHECK: vmovaps zmm0, zmm0 +// CHECK: #NO_APP +check_reg!(zmm0_f16x32 f16x32 "zmm0" "vmovaps"); + // CHECK-LABEL: zmm0_f32x16: // CHECK: #APP // CHECK: vmovaps zmm0, zmm0 diff --git a/tests/assembly/closure-inherit-target-feature.rs b/tests/assembly/closure-inherit-target-feature.rs index cafe9e7ca6fa..4692653d91fc 100644 --- a/tests/assembly/closure-inherit-target-feature.rs +++ b/tests/assembly/closure-inherit-target-feature.rs @@ -18,7 +18,8 @@ pub unsafe fn sse41_blend_nofeature(x: __m128, y: __m128) -> __m128 { // CHECK: {{call .*_mm_blend_ps.*}} // CHECK-NOT: blendps // CHECK: ret - #[inline(never)] |x, y| _mm_blend_ps(x, y, 0b0101) + #[inline(never)] + |x, y| _mm_blend_ps(x, y, 0b0101) }; f(x, y) } @@ -33,9 +34,8 @@ pub fn sse41_blend_noinline(x: __m128, y: __m128) -> __m128 { // CHECK: blendps // CHECK-NOT: _mm_blend_ps // CHECK: ret - #[inline(never)] |x, y| unsafe { - _mm_blend_ps(x, y, 0b0101) - } + #[inline(never)] + |x, y| unsafe { _mm_blend_ps(x, y, 0b0101) } }; f(x, y) } @@ -52,9 +52,8 @@ pub fn sse41_blend_doinline(x: __m128, y: __m128) -> __m128 { // CHECK-NOT: _mm_blend_ps // CHECK: ret let f = { - #[inline] |x, y| unsafe { - _mm_blend_ps(x, y, 0b0101) - } + #[inline] + |x, y| unsafe { _mm_blend_ps(x, y, 0b0101) } }; f(x, y) } diff --git a/tests/assembly/is_aligned.rs b/tests/assembly/is_aligned.rs index 14423a52064a..ab8f7dea808d 100644 --- a/tests/assembly/is_aligned.rs +++ b/tests/assembly/is_aligned.rs @@ -4,8 +4,7 @@ //@ revisions: opt-speed opt-size //@ [opt-speed] compile-flags: -Copt-level=2 -Cdebug-assertions=no //@ [opt-size] compile-flags: -Copt-level=s -Cdebug-assertions=no -#![crate_type="rlib"] - +#![crate_type = "rlib"] #![feature(core_intrinsics)] #![feature(pointer_is_aligned_to)] @@ -16,9 +15,7 @@ // CHECK: retq #[no_mangle] pub unsafe fn is_aligned_to_unchecked(ptr: *const u8, align: usize) -> bool { - unsafe { - std::intrinsics::assume(align.is_power_of_two()) - } + unsafe { std::intrinsics::assume(align.is_power_of_two()) } ptr.is_aligned_to(align) } diff --git a/tests/assembly/issue-83585-small-pod-struct-equality.rs b/tests/assembly/issue-83585-small-pod-struct-equality.rs new file mode 100644 index 000000000000..14bec1337f0b --- /dev/null +++ b/tests/assembly/issue-83585-small-pod-struct-equality.rs @@ -0,0 +1,27 @@ +//@ assembly-output: emit-asm +//@ compile-flags: -Copt-level=3 +//@ only-x86_64 + +#![crate_type = "lib"] + +type T = u8; +type T1 = (T, T, T, T, T, T, T, T); + +// CHECK-LABEL: foo1a +// CHECK: cmpq +// CHECK-NEXT: sete +// CHECK-NEXT: {{retq|popq}} +#[no_mangle] +pub fn foo1a(a: T1, b: T1) -> bool { + a == b +} + +// CHECK-LABEL: foo1b +// CHECK: movq +// CHECK: cmpq +// CHECK-NEXT: sete +// CHECK-NEXT: {{retq|popq}} +#[no_mangle] +pub fn foo1b(a: &T1, b: &T1) -> bool { + a == b +} diff --git a/tests/assembly/nvptx-c-abi-arg-v7.rs b/tests/assembly/nvptx-c-abi-arg-v7.rs new file mode 100644 index 000000000000..27b64b58f04c --- /dev/null +++ b/tests/assembly/nvptx-c-abi-arg-v7.rs @@ -0,0 +1,216 @@ +//@ assembly-output: ptx-linker +//@ compile-flags: --crate-type cdylib -C target-cpu=sm_86 -Z unstable-options -Clinker-flavor=llbc +//@ only-nvptx64 + +// The PTX ABI stability is tied to major versions of the PTX ISA +// These tests assume major version 7 + +// CHECK: .version 7 + +#![feature(abi_ptx, lang_items, no_core)] +#![no_core] + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +#[repr(C)] +pub struct SingleU8 { + f: u8, +} + +#[repr(C)] +pub struct DoubleU8 { + f: u8, + g: u8, +} + +#[repr(C)] +pub struct TripleU8 { + f: u8, + g: u8, + h: u8, +} + +#[repr(C)] +pub struct TripleU16 { + f: u16, + g: u16, + h: u16, +} +#[repr(C)] +pub struct DoubleI32 { + f: i32, + g: i32, +} +#[repr(C)] +pub struct TripleU32 { + f: u32, + g: u32, + h: u32, +} +#[repr(C)] +pub struct TripleU64 { + f: u64, + g: u64, + h: u64, +} + +#[repr(C)] +pub struct DoubleFloat { + f: f32, + g: f32, +} + +#[repr(C)] +pub struct TripleFloat { + f: f32, + g: f32, + h: f32, +} + +#[repr(C)] +pub struct TripleDouble { + f: f64, + g: f64, + h: f64, +} + +#[repr(C)] +pub struct ManyIntegers { + f: u8, + g: u16, + h: u32, + i: u64, +} + +#[repr(C)] +pub struct ManyNumerics { + f: u8, + g: u16, + h: u32, + i: u64, + j: f32, + k: f64, +} + +// CHECK: .visible .func f_u8_arg( +// CHECK: .param .b32 f_u8_arg_param_0 +#[no_mangle] +pub unsafe extern "C" fn f_u8_arg(_a: u8) {} + +// CHECK: .visible .func f_u16_arg( +// CHECK: .param .b32 f_u16_arg_param_0 +#[no_mangle] +pub unsafe extern "C" fn f_u16_arg(_a: u16) {} + +// CHECK: .visible .func f_u32_arg( +// CHECK: .param .b32 f_u32_arg_param_0 +#[no_mangle] +pub unsafe extern "C" fn f_u32_arg(_a: u32) {} + +// CHECK: .visible .func f_u64_arg( +// CHECK: .param .b64 f_u64_arg_param_0 +#[no_mangle] +pub unsafe extern "C" fn f_u64_arg(_a: u64) {} + +// CHECK: .visible .func f_u128_arg( +// CHECK: .param .align 16 .b8 f_u128_arg_param_0[16] +#[no_mangle] +pub unsafe extern "C" fn f_u128_arg(_a: u128) {} + +// CHECK: .visible .func f_i8_arg( +// CHECK: .param .b32 f_i8_arg_param_0 +#[no_mangle] +pub unsafe extern "C" fn f_i8_arg(_a: i8) {} + +// CHECK: .visible .func f_i16_arg( +// CHECK: .param .b32 f_i16_arg_param_0 +#[no_mangle] +pub unsafe extern "C" fn f_i16_arg(_a: i16) {} + +// CHECK: .visible .func f_i32_arg( +// CHECK: .param .b32 f_i32_arg_param_0 +#[no_mangle] +pub unsafe extern "C" fn f_i32_arg(_a: i32) {} + +// CHECK: .visible .func f_i64_arg( +// CHECK: .param .b64 f_i64_arg_param_0 +#[no_mangle] +pub unsafe extern "C" fn f_i64_arg(_a: i64) {} + +// CHECK: .visible .func f_i128_arg( +// CHECK: .param .align 16 .b8 f_i128_arg_param_0[16] +#[no_mangle] +pub unsafe extern "C" fn f_i128_arg(_a: i128) {} + +// CHECK: .visible .func f_f32_arg( +// CHECK: .param .b32 f_f32_arg_param_0 +#[no_mangle] +pub unsafe extern "C" fn f_f32_arg(_a: f32) {} + +// CHECK: .visible .func f_f64_arg( +// CHECK: .param .b64 f_f64_arg_param_0 +#[no_mangle] +pub unsafe extern "C" fn f_f64_arg(_a: f64) {} + +// CHECK: .visible .func f_single_u8_arg( +// CHECK: .param .align 1 .b8 f_single_u8_arg_param_0[1] +#[no_mangle] +pub unsafe extern "C" fn f_single_u8_arg(_a: SingleU8) {} + +// CHECK: .visible .func f_double_u8_arg( +// CHECK: .param .align 1 .b8 f_double_u8_arg_param_0[2] +#[no_mangle] +pub unsafe extern "C" fn f_double_u8_arg(_a: DoubleU8) {} + +// CHECK: .visible .func f_triple_u8_arg( +// CHECK: .param .align 1 .b8 f_triple_u8_arg_param_0[3] +#[no_mangle] +pub unsafe extern "C" fn f_triple_u8_arg(_a: TripleU8) {} + +// CHECK: .visible .func f_triple_u16_arg( +// CHECK: .param .align 2 .b8 f_triple_u16_arg_param_0[6] +#[no_mangle] +pub unsafe extern "C" fn f_triple_u16_arg(_a: TripleU16) {} + +// CHECK: .visible .func f_triple_u32_arg( +// CHECK: .param .align 4 .b8 f_triple_u32_arg_param_0[12] +#[no_mangle] +pub unsafe extern "C" fn f_triple_u32_arg(_a: TripleU32) {} + +// CHECK: .visible .func f_double_i32_arg( +// CHECK: .param .align 4 .b8 f_double_i32_arg_param_0[8] +#[no_mangle] +pub unsafe extern "C" fn f_double_i32_arg(_a: DoubleI32) {} + +// CHECK: .visible .func f_triple_u64_arg( +// CHECK: .param .align 8 .b8 f_triple_u64_arg_param_0[24] +#[no_mangle] +pub unsafe extern "C" fn f_triple_u64_arg(_a: TripleU64) {} + +// CHECK: .visible .func f_many_integers_arg( +// CHECK: .param .align 8 .b8 f_many_integers_arg_param_0[16] +#[no_mangle] +pub unsafe extern "C" fn f_many_integers_arg(_a: ManyIntegers) {} + +// CHECK: .visible .func f_double_float_arg( +// CHECK: .param .align 4 .b8 f_double_float_arg_param_0[8] +#[no_mangle] +pub unsafe extern "C" fn f_double_float_arg(_a: DoubleFloat) {} + +// CHECK: .visible .func f_triple_float_arg( +// CHECK: .param .align 4 .b8 f_triple_float_arg_param_0[12] +#[no_mangle] +pub unsafe extern "C" fn f_triple_float_arg(_a: TripleFloat) {} + +// CHECK: .visible .func f_triple_double_arg( +// CHECK: .param .align 8 .b8 f_triple_double_arg_param_0[24] +#[no_mangle] +pub unsafe extern "C" fn f_triple_double_arg(_a: TripleDouble) {} + +// CHECK: .visible .func f_many_numerics_arg( +// CHECK: .param .align 8 .b8 f_many_numerics_arg_param_0[32] +#[no_mangle] +pub unsafe extern "C" fn f_many_numerics_arg(_a: ManyNumerics) {} diff --git a/tests/assembly/nvptx-c-abi-ret-v7.rs b/tests/assembly/nvptx-c-abi-ret-v7.rs new file mode 100644 index 000000000000..56ab182fcce2 --- /dev/null +++ b/tests/assembly/nvptx-c-abi-ret-v7.rs @@ -0,0 +1,240 @@ +//@ assembly-output: ptx-linker +//@ compile-flags: --crate-type cdylib -C target-cpu=sm_86 -Z unstable-options -Clinker-flavor=llbc +//@ only-nvptx64 + +// The PTX ABI stability is tied to major versions of the PTX ISA +// These tests assume major version 7 + +// CHECK: .version 7 + +#![feature(abi_ptx, lang_items, no_core)] +#![no_core] + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +#[repr(C)] +pub struct SingleU8 { + f: u8, +} + +#[repr(C)] +pub struct DoubleU8 { + f: u8, + g: u8, +} + +#[repr(C)] +pub struct TripleU8 { + f: u8, + g: u8, + h: u8, +} + +#[repr(C)] +pub struct TripleU16 { + f: u16, + g: u16, + h: u16, +} +#[repr(C)] +pub struct DoubleI32 { + f: i32, + g: i32, +} +#[repr(C)] +pub struct TripleU32 { + f: u32, + g: u32, + h: u32, +} +#[repr(C)] +pub struct TripleU64 { + f: u64, + g: u64, + h: u64, +} + +#[repr(C)] +pub struct DoubleFloat { + f: f32, + g: f32, +} + +#[repr(C)] +pub struct TripleFloat { + f: f32, + g: f32, + h: f32, +} + +#[repr(C)] +pub struct TripleDouble { + f: f64, + g: f64, + h: f64, +} + +#[repr(C)] +pub struct ManyIntegers { + f: u8, + g: u16, + h: u32, + i: u64, +} + +#[repr(C)] +pub struct ManyNumerics { + f: u8, + g: u16, + h: u32, + i: u64, + j: f32, + k: f64, +} + +// CHECK: .visible .func (.param .b32 func_retval0) f_u8_ret( +#[no_mangle] +pub unsafe extern "C" fn f_u8_ret() -> u8 { + 0 +} + +// CHECK: .visible .func (.param .b32 func_retval0) f_u16_ret( +#[no_mangle] +pub unsafe extern "C" fn f_u16_ret() -> u16 { + 1 +} + +// CHECK: .visible .func (.param .b32 func_retval0) f_u32_ret( +#[no_mangle] +pub unsafe extern "C" fn f_u32_ret() -> u32 { + 2 +} + +// CHECK: .visible .func (.param .b64 func_retval0) f_u64_ret( +#[no_mangle] +pub unsafe extern "C" fn f_u64_ret() -> u64 { + 3 +} + +// CHECK: .visible .func (.param .align 16 .b8 func_retval0[16]) f_u128_ret( +#[no_mangle] +pub unsafe extern "C" fn f_u128_ret() -> u128 { + 4 +} + +// CHECK: .visible .func (.param .b32 func_retval0) f_i8_ret( +#[no_mangle] +pub unsafe extern "C" fn f_i8_ret() -> i8 { + 5 +} + +// CHECK: .visible .func (.param .b32 func_retval0) f_i16_ret( +#[no_mangle] +pub unsafe extern "C" fn f_i16_ret() -> i16 { + 6 +} + +// CHECK: .visible .func (.param .b32 func_retval0) f_i32_ret( +#[no_mangle] +pub unsafe extern "C" fn f_i32_ret() -> i32 { + 7 +} + +// CHECK: .visible .func (.param .b64 func_retval0) f_i64_ret( +#[no_mangle] +pub unsafe extern "C" fn f_i64_ret() -> i64 { + 8 +} + +// CHECK: .visible .func (.param .align 16 .b8 func_retval0[16]) f_i128_ret( +#[no_mangle] +pub unsafe extern "C" fn f_i128_ret() -> i128 { + 9 +} + +// CHECK: .visible .func (.param .b32 func_retval0) f_f32_ret( +#[no_mangle] +pub unsafe extern "C" fn f_f32_ret() -> f32 { + 10.0 +} + +// CHECK: .visible .func (.param .b64 func_retval0) f_f64_ret( +#[no_mangle] +pub unsafe extern "C" fn f_f64_ret() -> f64 { + 11.0 +} + +// CHECK: .visible .func (.param .align 1 .b8 func_retval0[1]) f_single_u8_ret( +#[no_mangle] +pub unsafe extern "C" fn f_single_u8_ret() -> SingleU8 { + SingleU8 { f: 12 } +} + +// CHECK: .visible .func (.param .align 1 .b8 func_retval0[2]) f_double_u8_ret( +#[no_mangle] +pub unsafe extern "C" fn f_double_u8_ret() -> DoubleU8 { + DoubleU8 { f: 13, g: 14 } +} + +// CHECK: .visible .func (.param .align 1 .b8 func_retval0[3]) f_triple_u8_ret( +#[no_mangle] +pub unsafe extern "C" fn f_triple_u8_ret() -> TripleU8 { + TripleU8 { f: 15, g: 16, h: 17 } +} + +// CHECK: .visible .func (.param .align 2 .b8 func_retval0[6]) f_triple_u16_ret( +#[no_mangle] +pub unsafe extern "C" fn f_triple_u16_ret() -> TripleU16 { + TripleU16 { f: 18, g: 19, h: 20 } +} + +// CHECK: .visible .func (.param .align 4 .b8 func_retval0[8]) f_double_i32_ret( +#[no_mangle] +pub unsafe extern "C" fn f_double_i32_ret() -> DoubleI32 { + DoubleI32 { f: 1, g: 2 } +} + +// CHECK: .visible .func (.param .align 4 .b8 func_retval0[12]) f_triple_u32_ret( +#[no_mangle] +pub unsafe extern "C" fn f_triple_u32_ret() -> TripleU32 { + TripleU32 { f: 20, g: 21, h: 22 } +} + +// CHECK: .visible .func (.param .align 8 .b8 func_retval0[24]) f_triple_u64_ret( +#[no_mangle] +pub unsafe extern "C" fn f_triple_u64_ret() -> TripleU64 { + TripleU64 { f: 23, g: 24, h: 25 } +} + +// CHECK: .visible .func (.param .align 8 .b8 func_retval0[16]) f_many_integers_ret( +#[no_mangle] +pub unsafe extern "C" fn f_many_integers_ret() -> ManyIntegers { + ManyIntegers { f: 26, g: 27, h: 28, i: 29 } +} + +// CHECK: .visible .func (.param .align 4 .b8 func_retval0[8]) f_double_float_ret( +#[no_mangle] +pub unsafe extern "C" fn f_double_float_ret() -> DoubleFloat { + DoubleFloat { f: 29.0, g: 30.0 } +} + +// CHECK: .visible .func (.param .align 4 .b8 func_retval0[12]) f_triple_float_ret( +#[no_mangle] +pub unsafe extern "C" fn f_triple_float_ret() -> TripleFloat { + TripleFloat { f: 31.0, g: 32.0, h: 33.0 } +} + +// CHECK: .visible .func (.param .align 8 .b8 func_retval0[24]) f_triple_double_ret( +#[no_mangle] +pub unsafe extern "C" fn f_triple_double_ret() -> TripleDouble { + TripleDouble { f: 34.0, g: 35.0, h: 36.0 } +} + +// CHECK: .visible .func (.param .align 8 .b8 func_retval0[32]) f_many_numerics_ret( +#[no_mangle] +pub unsafe extern "C" fn f_many_numerics_ret() -> ManyNumerics { + ManyNumerics { f: 37, g: 38, h: 39, i: 40, j: 41.0, k: 43.0 } +} diff --git a/tests/assembly/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs b/tests/assembly/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs index ce1d732f3677..fb3a325a41f8 100644 --- a/tests/assembly/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs +++ b/tests/assembly/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs @@ -50,6 +50,11 @@ pub struct TripleU16 { h: u16, } #[repr(C)] +pub struct DoubleI32 { + f: i32, + g: i32, +} +#[repr(C)] pub struct TripleU32 { f: u32, g: u32, @@ -180,6 +185,11 @@ pub unsafe extern "ptx-kernel" fn f_triple_u8_arg(_a: TripleU8) {} #[no_mangle] pub unsafe extern "ptx-kernel" fn f_triple_u16_arg(_a: TripleU16) {} +// CHECK: .visible .entry f_double_i32_arg( +// CHECK: .param .align 4 .b8 f_double_i32_arg_param_0[8] +#[no_mangle] +pub unsafe extern "ptx-kernel" fn f_double_i32_arg(_a: DoubleI32) {} + // CHECK: .visible .entry f_triple_u32_arg( // CHECK: .param .align 4 .b8 f_triple_u32_arg_param_0[12] #[no_mangle] diff --git a/tests/assembly/pic-relocation-model.rs b/tests/assembly/pic-relocation-model.rs index 453fd6a70476..73db94791ec6 100644 --- a/tests/assembly/pic-relocation-model.rs +++ b/tests/assembly/pic-relocation-model.rs @@ -3,10 +3,9 @@ //@ [x64] compile-flags: --target x86_64-unknown-linux-gnu -Crelocation-model=pic //@ [x64] needs-llvm-components: x86 - #![feature(no_core, lang_items)] #![no_core] -#![crate_type="rlib"] +#![crate_type = "rlib"] #[lang = "sized"] trait Sized {} @@ -17,9 +16,7 @@ trait Copy {} // CHECK: {{(jmpq|callq)}} *other_fn@GOTPCREL(%rip) #[no_mangle] pub fn call_other_fn() -> u8 { - unsafe { - other_fn() - } + unsafe { other_fn() } } // CHECK-LABEL: other_fn: @@ -27,9 +24,9 @@ pub fn call_other_fn() -> u8 { #[no_mangle] #[inline(never)] pub fn other_fn() -> u8 { - unsafe { - foreign_fn() - } + unsafe { foreign_fn() } } -extern "C" {fn foreign_fn() -> u8;} +extern "C" { + fn foreign_fn() -> u8; +} diff --git a/tests/assembly/pie-relocation-model.rs b/tests/assembly/pie-relocation-model.rs index 6ff6b7708bbf..e632b4dd5823 100644 --- a/tests/assembly/pie-relocation-model.rs +++ b/tests/assembly/pie-relocation-model.rs @@ -3,10 +3,9 @@ //@ [x64] compile-flags: --target x86_64-unknown-linux-gnu -Crelocation-model=pie //@ [x64] needs-llvm-components: x86 - #![feature(no_core, lang_items)] #![no_core] -#![crate_type="rlib"] +#![crate_type = "rlib"] #[lang = "sized"] trait Sized {} @@ -18,9 +17,7 @@ trait Copy {} // CHECK: {{(jmp|callq)}} other_fn #[no_mangle] pub fn call_other_fn() -> u8 { - unsafe { - other_fn() - } + unsafe { other_fn() } } // CHECK-LABEL: other_fn: @@ -30,9 +27,9 @@ pub fn call_other_fn() -> u8 { #[no_mangle] #[inline(never)] pub fn other_fn() -> u8 { - unsafe { - foreign_fn() - } + unsafe { foreign_fn() } } -extern "C" {fn foreign_fn() -> u8;} +extern "C" { + fn foreign_fn() -> u8; +} diff --git a/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs b/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs index 51b4dc4e1699..2a8251785e77 100644 --- a/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs +++ b/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs @@ -10,12 +10,9 @@ //@ compile-flags: -C opt-level=2 -Z merge-functions=disabled #![crate_type = "lib"] - #![allow(incomplete_features)] - #![feature(unsized_locals, unsized_fn_params)] - // CHECK-LABEL: emptyfn: #[no_mangle] pub fn emptyfn() { @@ -139,7 +136,6 @@ pub fn local_var_addr_used_indirectly(f: fn(bool)) { // missing-NOT: __security_check_cookie } - // CHECK-LABEL: local_string_addr_taken #[no_mangle] pub fn local_string_addr_taken(f: fn(&String)) { @@ -205,7 +201,7 @@ pub struct Gigastruct { not: u64, have: u64, array: u64, - members: u64 + members: u64, } // CHECK-LABEL: local_large_var_moved @@ -259,7 +255,6 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) { // EOF // ``` - // all: __security_check_cookie // strong: __security_check_cookie // basic: __security_check_cookie @@ -267,7 +262,6 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) { // missing-NOT: __security_check_cookie } - extern "C" { // A call to an external `alloca` function is *not* recognized as an // `alloca(3)` operation. This function is a compiler built-in, as the @@ -320,7 +314,6 @@ pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) { // missing-NOT: __security_check_cookie } - // CHECK-LABEL: alloca_dynamic_arg #[no_mangle] pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) { @@ -340,7 +333,6 @@ pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) { // this is support for the "unsized locals" unstable feature: // https://doc.rust-lang.org/unstable-book/language-features/unsized-locals.html. - // CHECK-LABEL: unsized_fn_param #[no_mangle] pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) { @@ -354,7 +346,6 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) { // alloca, and is therefore not protected by the `strong` or `basic` // heuristics. - // We should have a __security_check_cookie call in `all` and `strong` modes but // LLVM does not support generating stack protectors in functions with funclet // based EH personalities. diff --git a/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs b/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs index c5915262c093..9729da4e5d25 100644 --- a/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs +++ b/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs @@ -10,12 +10,9 @@ //@ compile-flags: -C opt-level=2 -Z merge-functions=disabled #![crate_type = "lib"] - #![allow(incomplete_features)] - #![feature(unsized_locals, unsized_fn_params)] - // CHECK-LABEL: emptyfn: #[no_mangle] pub fn emptyfn() { @@ -139,7 +136,6 @@ pub fn local_var_addr_used_indirectly(f: fn(bool)) { // missing-NOT: __security_check_cookie } - // CHECK-LABEL: local_string_addr_taken #[no_mangle] pub fn local_string_addr_taken(f: fn(&String)) { @@ -213,7 +209,7 @@ pub struct Gigastruct { not: u64, have: u64, array: u64, - members: u64 + members: u64, } // CHECK-LABEL: local_large_var_moved @@ -267,7 +263,6 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) { // EOF // ``` - // all: __security_check_cookie // strong: __security_check_cookie // basic: __security_check_cookie @@ -275,7 +270,6 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) { // missing-NOT: __security_check_cookie } - extern "C" { // A call to an external `alloca` function is *not* recognized as an // `alloca(3)` operation. This function is a compiler built-in, as the @@ -328,7 +322,6 @@ pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) { // missing-NOT: __security_check_cookie } - // CHECK-LABEL: alloca_dynamic_arg #[no_mangle] pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) { @@ -348,7 +341,6 @@ pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) { // this is support for the "unsized locals" unstable feature: // https://doc.rust-lang.org/unstable-book/language-features/unsized-locals.html. - // CHECK-LABEL: unsized_fn_param #[no_mangle] pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) { @@ -362,7 +354,6 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) { // alloca, and is therefore not protected by the `strong` or `basic` // heuristics. - // We should have a __security_check_cookie call in `all` and `strong` modes but // LLVM does not support generating stack protectors in functions with funclet // based EH personalities. diff --git a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs index 8e32d170244a..5ed0b6c50a73 100644 --- a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs +++ b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs @@ -1,6 +1,6 @@ //@ revisions: all strong basic none missing //@ assembly-output: emit-asm -//@ ignore-macos slightly different policy on stack protection of arrays +//@ ignore-apple slightly different policy on stack protection of arrays //@ ignore-msvc stack check code uses different function names //@ ignore-nvptx64 stack protector is not supported //@ ignore-wasm32-bare @@ -17,12 +17,9 @@ // See comments on https://github.com/rust-lang/rust/issues/114903. #![crate_type = "lib"] - #![allow(incomplete_features)] - #![feature(unsized_locals, unsized_fn_params)] - // CHECK-LABEL: emptyfn: #[no_mangle] pub fn emptyfn() { @@ -146,7 +143,6 @@ pub fn local_var_addr_used_indirectly(f: fn(bool)) { // missing-NOT: __stack_chk_fail } - // CHECK-LABEL: local_string_addr_taken #[no_mangle] pub fn local_string_addr_taken(f: fn(&String)) { @@ -197,7 +193,7 @@ pub struct Gigastruct { not: u64, have: u64, array: u64, - members: u64 + members: u64, } // CHECK-LABEL: local_large_var_moved @@ -258,7 +254,6 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) { // missing-NOT: __stack_chk_fail } - extern "C" { // A call to an external `alloca` function is *not* recognized as an // `alloca(3)` operation. This function is a compiler built-in, as the @@ -311,7 +306,6 @@ pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) { // missing-NOT: __stack_chk_fail } - // CHECK-LABEL: alloca_dynamic_arg #[no_mangle] pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) { @@ -331,7 +325,6 @@ pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) { // this is support for the "unsized locals" unstable feature: // https://doc.rust-lang.org/unstable-book/language-features/unsized-locals.html. - // CHECK-LABEL: unsized_fn_param #[no_mangle] pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) { @@ -345,7 +338,6 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) { // alloca, and is therefore not protected by the `strong` or `basic` // heuristics. - // all: __stack_chk_fail // strong-NOT: __stack_chk_fail // basic-NOT: __stack_chk_fail diff --git a/tests/assembly/static-relocation-model.rs b/tests/assembly/static-relocation-model.rs index 50527b853451..eafdfd485baa 100644 --- a/tests/assembly/static-relocation-model.rs +++ b/tests/assembly/static-relocation-model.rs @@ -9,15 +9,15 @@ #![feature(no_core, lang_items)] #![no_core] -#![crate_type="rlib"] +#![crate_type = "rlib"] -#[lang="sized"] +#[lang = "sized"] trait Sized {} -#[lang="copy"] +#[lang = "copy"] trait Copy {} -#[lang="sync"] +#[lang = "sync"] trait Sync {} #[lang = "drop_in_place"] @@ -42,9 +42,7 @@ extern "C" { // A64-NEXT: ldrb {{[a-z0-9]+}}, {{\[}}[[REG]], :lo12:chaenomeles] #[no_mangle] pub fn banana() -> u8 { - unsafe { - *(chaenomeles as *mut u8) - } + unsafe { *(chaenomeles as *mut u8) } } // CHECK-LABEL: peach: @@ -53,9 +51,7 @@ pub fn banana() -> u8 { // A64-NEXT: ldrb {{[a-z0-9]+}}, {{\[}}[[REG2]], :lo12:banana] #[no_mangle] pub fn peach() -> u8 { - unsafe { - *(banana as *mut u8) - } + unsafe { *(banana as *mut u8) } } // CHECK-LABEL: mango: @@ -65,9 +61,7 @@ pub fn peach() -> u8 { // A64-NEXT: ldr {{[a-z0-9]+}}, {{\[}}[[REG2]], :lo12:EXOCHORDA] #[no_mangle] pub fn mango() -> u8 { - unsafe { - *EXOCHORDA - } + unsafe { *EXOCHORDA } } // CHECK-LABEL: orange: diff --git a/tests/assembly/targets/targets-elf.rs b/tests/assembly/targets/targets-elf.rs index 0db2a3580026..b069e667bf5d 100644 --- a/tests/assembly/targets/targets-elf.rs +++ b/tests/assembly/targets/targets-elf.rs @@ -228,6 +228,9 @@ //@ revisions: i686_unknown_openbsd //@ [i686_unknown_openbsd] compile-flags: --target i686-unknown-openbsd //@ [i686_unknown_openbsd] needs-llvm-components: x86 +//@ revisions: i686_unknown_redox +//@ [i686_unknown_redox] compile-flags: --target i686-unknown-redox +//@ [i686_unknown_redox] needs-llvm-components: x86 //@ revisions: i686_wrs_vxworks //@ [i686_wrs_vxworks] compile-flags: --target i686-wrs-vxworks //@ [i686_wrs_vxworks] needs-llvm-components: x86 @@ -573,7 +576,18 @@ //@ revisions: x86_64_wrs_vxworks //@ [x86_64_wrs_vxworks] compile-flags: --target x86_64-wrs-vxworks //@ [x86_64_wrs_vxworks] needs-llvm-components: x86 - +// FIXME: disabled since it requires a custom LLVM until the upstream LLVM adds support for the target (https://github.com/espressif/llvm-project/issues/4) +/* + revisions: xtensa_esp32_none_elf + [xtensa_esp32_none_elf] compile-flags: --target xtensa-esp32-none-elf + [xtensa_esp32_none_elf] needs-llvm-components: xtensa + revisions: xtensa_esp32s2_none_elf + [xtensa_esp32s2_none_elf] compile-flags: --target xtensa-esp32s2-none-elf + [xtensa_esp32s2_none_elf] needs-llvm-components: xtensa + revisions: xtensa_esp32s3_none_elf + [xtensa_esp32s3_none_elf] compile-flags: --target xtensa-esp32s3-none-elf + [xtensa_esp32s3_none_elf] needs-llvm-components: xtensa +*/ // Sanity-check that each target can produce assembly code. #![feature(no_core, lang_items)] diff --git a/tests/assembly/thin-lto.rs b/tests/assembly/thin-lto.rs index 182115662bfd..7b67b2de1e63 100644 --- a/tests/assembly/thin-lto.rs +++ b/tests/assembly/thin-lto.rs @@ -4,5 +4,4 @@ // CHECK: main -pub fn main() { -} +pub fn main() {} diff --git a/tests/assembly/wasm_exceptions.rs b/tests/assembly/wasm_exceptions.rs index 3d3b13ff32b7..7bdf7f1287c2 100644 --- a/tests/assembly/wasm_exceptions.rs +++ b/tests/assembly/wasm_exceptions.rs @@ -8,7 +8,7 @@ #![feature(core_intrinsics)] #![feature(rustc_attrs)] -extern { +extern "C" { fn may_panic(); #[rustc_nounwind] @@ -19,7 +19,9 @@ struct LogOnDrop; impl Drop for LogOnDrop { fn drop(&mut self) { - unsafe { log_number(0); } + unsafe { + log_number(0); + } } } @@ -27,7 +29,9 @@ impl Drop for LogOnDrop { #[no_mangle] pub fn test_cleanup() { let _log_on_drop = LogOnDrop; - unsafe { may_panic(); } + unsafe { + may_panic(); + } // CHECK-NOT: call // CHECK: try @@ -41,12 +45,16 @@ pub fn test_cleanup() { #[no_mangle] pub fn test_rtry() { unsafe { - core::intrinsics::catch_unwind(|_| { - may_panic(); - }, core::ptr::null_mut(), |data, exception| { - log_number(data as usize); - log_number(exception as usize); - }); + core::intrinsics::catch_unwind( + |_| { + may_panic(); + }, + core::ptr::null_mut(), + |data, exception| { + log_number(data as usize); + log_number(exception as usize); + }, + ); } // CHECK-NOT: call diff --git a/tests/assembly/x86_64-array-pair-load-store-merge.rs b/tests/assembly/x86_64-array-pair-load-store-merge.rs index 9cf54ae14a16..849f34e72e51 100644 --- a/tests/assembly/x86_64-array-pair-load-store-merge.rs +++ b/tests/assembly/x86_64-array-pair-load-store-merge.rs @@ -2,7 +2,7 @@ //@ compile-flags: --crate-type=lib -O -C llvm-args=-x86-asm-syntax=intel //@ only-x86_64 //@ ignore-sgx -//@ ignore-macos (manipulates rsp too) +//@ ignore-apple (manipulates rsp too) // Depending on various codegen choices, this might end up copying // a `<2 x i8>`, an `i16`, or two `i8`s. diff --git a/tests/assembly/x86_64-fortanix-unknown-sgx-lvi-generic-load.rs b/tests/assembly/x86_64-fortanix-unknown-sgx-lvi-generic-load.rs index 7215e354d0dc..f5e2f18e68e4 100644 --- a/tests/assembly/x86_64-fortanix-unknown-sgx-lvi-generic-load.rs +++ b/tests/assembly/x86_64-fortanix-unknown-sgx-lvi-generic-load.rs @@ -5,7 +5,7 @@ //@ only-x86_64-fortanix-unknown-sgx #[no_mangle] -pub extern fn plus_one(r: &mut u64) { +pub extern "C" fn plus_one(r: &mut u64) { *r = *r + 1; } diff --git a/tests/assembly/x86_64-fortanix-unknown-sgx-lvi-generic-ret.rs b/tests/assembly/x86_64-fortanix-unknown-sgx-lvi-generic-ret.rs index 5ae9dd11859e..f16d68fa2554 100644 --- a/tests/assembly/x86_64-fortanix-unknown-sgx-lvi-generic-ret.rs +++ b/tests/assembly/x86_64-fortanix-unknown-sgx-lvi-generic-ret.rs @@ -5,7 +5,7 @@ //@ only-x86_64-fortanix-unknown-sgx #[no_mangle] -pub extern fn myret() {} +pub extern "C" fn myret() {} // CHECK: myret: // CHECK: popq [[REGISTER:%[a-z]+]] // CHECK-NEXT: lfence diff --git a/tests/assembly/x86_64-function-return.rs b/tests/assembly/x86_64-function-return.rs index 64eb05062cbc..7cfdf5bce0c1 100644 --- a/tests/assembly/x86_64-function-return.rs +++ b/tests/assembly/x86_64-function-return.rs @@ -9,7 +9,7 @@ //@ [keep-thunk-extern] compile-flags: -Zfunction-return=keep -Zfunction-return=thunk-extern //@ [thunk-extern-keep] compile-flags: -Zfunction-return=thunk-extern -Zfunction-return=keep //@ only-x86_64 -//@ ignore-x86_64-apple-darwin Symbol is called `___x86_return_thunk` (Darwin's extra underscore) +//@ ignore-apple Symbol is called `___x86_return_thunk` (Darwin's extra underscore) //@ ignore-sgx Tests incompatible with LVI mitigations #![crate_type = "lib"] diff --git a/tests/codegen-units/item-collection/auxiliary/cgu_export_trait_method.rs b/tests/codegen-units/item-collection/auxiliary/cgu_export_trait_method.rs index 5566bb4e4b28..1992baf8c765 100644 --- a/tests/codegen-units/item-collection/auxiliary/cgu_export_trait_method.rs +++ b/tests/codegen-units/item-collection/auxiliary/cgu_export_trait_method.rs @@ -2,25 +2,43 @@ #![crate_type = "lib"] -pub trait Trait : Sized { +pub trait Trait: Sized { fn without_self() -> u32; - fn without_self_default() -> u32 { 0 } + fn without_self_default() -> u32 { + 0 + } - fn with_default_impl(self) -> Self { self } - fn with_default_impl_generic(self, x: T) -> (Self, T) { (self, x) } + fn with_default_impl(self) -> Self { + self + } + fn with_default_impl_generic(self, x: T) -> (Self, T) { + (self, x) + } fn without_default_impl(x: u32) -> (Self, u32); fn without_default_impl_generic(x: T) -> (Self, T); } impl Trait for char { - fn without_self() -> u32 { 2 } - fn without_default_impl(x: u32) -> (Self, u32) { ('c', x) } - fn without_default_impl_generic(x: T) -> (Self, T) { ('c', x) } + fn without_self() -> u32 { + 2 + } + fn without_default_impl(x: u32) -> (Self, u32) { + ('c', x) + } + fn without_default_impl_generic(x: T) -> (Self, T) { + ('c', x) + } } impl Trait for u32 { - fn without_self() -> u32 { 1 } - fn without_default_impl(x: u32) -> (Self, u32) { (0, x) } - fn without_default_impl_generic(x: T) -> (Self, T) { (0, x) } + fn without_self() -> u32 { + 1 + } + fn without_default_impl(x: u32) -> (Self, u32) { + (0, x) + } + fn without_default_impl_generic(x: T) -> (Self, T) { + (0, x) + } } diff --git a/tests/codegen-units/item-collection/auxiliary/cgu_extern_closures.rs b/tests/codegen-units/item-collection/auxiliary/cgu_extern_closures.rs index 05ea0a89ff29..0192a3d4188c 100644 --- a/tests/codegen-units/item-collection/auxiliary/cgu_extern_closures.rs +++ b/tests/codegen-units/item-collection/auxiliary/cgu_extern_closures.rs @@ -2,22 +2,19 @@ #[inline] pub fn inlined_fn(x: i32, y: i32) -> i32 { - - let closure = |a, b| { a + b }; + let closure = |a, b| a + b; closure(x, y) } pub fn inlined_fn_generic(x: i32, y: i32, z: T) -> (i32, T) { - - let closure = |a, b| { a + b }; + let closure = |a, b| a + b; (closure(x, y), z) } pub fn non_inlined_fn(x: i32, y: i32) -> i32 { - - let closure = |a, b| { a + b }; + let closure = |a, b| a + b; closure(x, y) } diff --git a/tests/codegen-units/item-collection/cross-crate-closures.rs b/tests/codegen-units/item-collection/cross-crate-closures.rs index 4ec7f17d5847..2dab19401ed7 100644 --- a/tests/codegen-units/item-collection/cross-crate-closures.rs +++ b/tests/codegen-units/item-collection/cross-crate-closures.rs @@ -14,7 +14,6 @@ extern crate cgu_extern_closures; //~ MONO_ITEM fn cross_crate_closures::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn cgu_extern_closures::inlined_fn[0] //~ MONO_ITEM fn cgu_extern_closures::inlined_fn[0]::{{closure}}[0] let _ = cgu_extern_closures::inlined_fn(1, 2); diff --git a/tests/codegen-units/item-collection/cross-crate-trait-method.rs b/tests/codegen-units/item-collection/cross-crate-trait-method.rs index 84977328e490..997777603155 100644 --- a/tests/codegen-units/item-collection/cross-crate-trait-method.rs +++ b/tests/codegen-units/item-collection/cross-crate-trait-method.rs @@ -24,8 +24,6 @@ fn start(_: isize, _: *const *const u8) -> isize { //~ MONO_ITEM fn ::with_default_impl let _ = Trait::with_default_impl('c'); - - //~ MONO_ITEM fn ::with_default_impl_generic::<&str> let _ = Trait::with_default_impl_generic(0u32, "abc"); //~ MONO_ITEM fn ::with_default_impl_generic:: diff --git a/tests/codegen-units/item-collection/drop_in_place_intrinsic.rs b/tests/codegen-units/item-collection/drop_in_place_intrinsic.rs index 66dcda260660..947faa165a9c 100644 --- a/tests/codegen-units/item-collection/drop_in_place_intrinsic.rs +++ b/tests/codegen-units/item-collection/drop_in_place_intrinsic.rs @@ -15,7 +15,6 @@ impl Drop for StructWithDtor { //~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn std::ptr::drop_in_place::<[StructWithDtor; 2]> - shim(Some([StructWithDtor; 2])) @@ drop_in_place_intrinsic-cgu.0[Internal] let x = [StructWithDtor(0), StructWithDtor(1)]; diff --git a/tests/codegen-units/item-collection/function-as-argument.rs b/tests/codegen-units/item-collection/function-as-argument.rs index 4e6fd99d29ed..4be713dc3673 100644 --- a/tests/codegen-units/item-collection/function-as-argument.rs +++ b/tests/codegen-units/item-collection/function-as-argument.rs @@ -16,7 +16,6 @@ fn take_fn_pointer(f: fn(T1, T2), x: T1, y: T2) { //~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn take_fn_once::}> //~ MONO_ITEM fn function:: //~ MONO_ITEM fn } as std::ops::FnOnce<(u32, &str)>>::call_once - shim(fn(u32, &str) {function::}) diff --git a/tests/codegen-units/item-collection/generic-drop-glue.rs b/tests/codegen-units/item-collection/generic-drop-glue.rs index 99250dc7dc67..d861d269fae4 100644 --- a/tests/codegen-units/item-collection/generic-drop-glue.rs +++ b/tests/codegen-units/item-collection/generic-drop-glue.rs @@ -21,7 +21,7 @@ struct StructNoDrop { enum EnumWithDrop { A(T1), - B(T2) + B(T2), } impl Drop for EnumWithDrop { @@ -30,10 +30,9 @@ impl Drop for EnumWithDrop { enum EnumNoDrop { A(T1), - B(T2) + B(T2), } - struct NonGenericNoDrop(#[allow(dead_code)] i32); struct NonGenericWithDrop(#[allow(dead_code)] i32); @@ -67,24 +66,24 @@ fn start(_: isize, _: *const *const u8) -> isize { //~ MONO_ITEM fn as std::ops::Drop>::drop let _ = match EnumWithDrop::A::(0) { EnumWithDrop::A(x) => x, - EnumWithDrop::B(x) => x as i32 + EnumWithDrop::B(x) => x as i32, }; //~ MONO_ITEM fn std::ptr::drop_in_place::> - shim(Some(EnumWithDrop)) @@ generic_drop_glue-cgu.0[Internal] //~ MONO_ITEM fn as std::ops::Drop>::drop let _ = match EnumWithDrop::B::(1.0) { EnumWithDrop::A(x) => x, - EnumWithDrop::B(x) => x as f64 + EnumWithDrop::B(x) => x as f64, }; let _ = match EnumNoDrop::A::(0) { EnumNoDrop::A(x) => x, - EnumNoDrop::B(x) => x as i32 + EnumNoDrop::B(x) => x as i32, }; let _ = match EnumNoDrop::B::(1.0) { EnumNoDrop::A(x) => x, - EnumNoDrop::B(x) => x as f64 + EnumNoDrop::B(x) => x as f64, }; 0 diff --git a/tests/codegen-units/item-collection/generic-impl.rs b/tests/codegen-units/item-collection/generic-impl.rs index 6e60907c1857..b4cd99272b15 100644 --- a/tests/codegen-units/item-collection/generic-impl.rs +++ b/tests/codegen-units/item-collection/generic-impl.rs @@ -8,15 +8,13 @@ struct Struct { f: fn(x: T) -> T, } -fn id(x: T) -> T { x } +fn id(x: T) -> T { + x +} impl Struct { - fn new(x: T) -> Struct { - Struct { - x: x, - f: id - } + Struct { x: x, f: id } } fn get(self, x: T2) -> (T, T2) { @@ -24,17 +22,16 @@ impl Struct { } } -pub struct LifeTimeOnly<'a> { - _a: &'a u32 +pub struct _LifeTimeOnly<'a> { + _a: &'a u32, } -impl<'a> LifeTimeOnly<'a> { - - //~ MONO_ITEM fn LifeTimeOnly::<'_>::foo +impl<'a> _LifeTimeOnly<'a> { + //~ MONO_ITEM fn _LifeTimeOnly::<'_>::foo pub fn foo(&self) {} - //~ MONO_ITEM fn LifeTimeOnly::<'_>::bar + //~ MONO_ITEM fn _LifeTimeOnly::<'_>::bar pub fn bar(&'a self) {} - //~ MONO_ITEM fn LifeTimeOnly::<'_>::baz + //~ MONO_ITEM fn _LifeTimeOnly::<'_>::baz pub fn baz<'b>(&'b self) {} pub fn non_instantiated(&self) {} diff --git a/tests/codegen-units/item-collection/instantiation-through-vtable.rs b/tests/codegen-units/item-collection/instantiation-through-vtable.rs index 08e8c03a7328..59dd4311a03b 100644 --- a/tests/codegen-units/item-collection/instantiation-through-vtable.rs +++ b/tests/codegen-units/item-collection/instantiation-through-vtable.rs @@ -10,11 +10,13 @@ trait Trait { } struct Struct { - _a: T + _a: T, } impl Trait for Struct { - fn foo(&self) -> u32 { 0 } + fn foo(&self) -> u32 { + 0 + } fn bar(&self) {} } diff --git a/tests/codegen-units/item-collection/non-generic-drop-glue.rs b/tests/codegen-units/item-collection/non-generic-drop-glue.rs index d74b17463bf1..f7bb2f3f2f4d 100644 --- a/tests/codegen-units/item-collection/non-generic-drop-glue.rs +++ b/tests/codegen-units/item-collection/non-generic-drop-glue.rs @@ -7,7 +7,7 @@ //~ MONO_ITEM fn std::ptr::drop_in_place:: - shim(Some(StructWithDrop)) @@ non_generic_drop_glue-cgu.0[Internal] struct StructWithDrop { - x: i32 + x: i32, } impl Drop for StructWithDrop { @@ -16,12 +16,12 @@ impl Drop for StructWithDrop { } struct StructNoDrop { - x: i32 + x: i32, } //~ MONO_ITEM fn std::ptr::drop_in_place:: - shim(Some(EnumWithDrop)) @@ non_generic_drop_glue-cgu.0[Internal] enum EnumWithDrop { - A(i32) + A(i32), } impl Drop for EnumWithDrop { @@ -30,7 +30,7 @@ impl Drop for EnumWithDrop { } enum EnumNoDrop { - A(i32) + A(i32), } //~ MONO_ITEM fn start @@ -39,10 +39,10 @@ fn start(_: isize, _: *const *const u8) -> isize { let _ = StructWithDrop { x: 0 }.x; let _ = StructNoDrop { x: 0 }.x; let _ = match EnumWithDrop::A(0) { - EnumWithDrop::A(x) => x + EnumWithDrop::A(x) => x, }; let _ = match EnumNoDrop::A(0) { - EnumNoDrop::A(x) => x + EnumNoDrop::A(x) => x, }; 0 diff --git a/tests/codegen-units/item-collection/non-generic-functions.rs b/tests/codegen-units/item-collection/non-generic-functions.rs index 49a999a0d7c8..d4d7d221827d 100644 --- a/tests/codegen-units/item-collection/non-generic-functions.rs +++ b/tests/codegen-units/item-collection/non-generic-functions.rs @@ -25,7 +25,9 @@ fn bar() { baz(); } -struct Struct { _x: i32 } +struct Struct { + _x: i32, +} impl Struct { //~ MONO_ITEM fn Struct::foo diff --git a/tests/codegen-units/item-collection/overloaded-operators.rs b/tests/codegen-units/item-collection/overloaded-operators.rs index 23141c27de6e..e00e22dbab99 100644 --- a/tests/codegen-units/item-collection/overloaded-operators.rs +++ b/tests/codegen-units/item-collection/overloaded-operators.rs @@ -1,58 +1,48 @@ //@ compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] -#![crate_type="lib"] +#![crate_type = "lib"] -use std::ops::{Index, IndexMut, Add, Deref}; +use std::ops::{Add, Deref, Index, IndexMut}; -pub struct Indexable { - data: [u8; 3] +pub struct _Indexable { + data: [u8; 3], } -impl Index for Indexable { +impl Index for _Indexable { type Output = u8; - //~ MONO_ITEM fn >::index + //~ MONO_ITEM fn <_Indexable as std::ops::Index>::index fn index(&self, index: usize) -> &Self::Output { - if index >= 3 { - &self.data[0] - } else { - &self.data[index] - } + if index >= 3 { &self.data[0] } else { &self.data[index] } } } -impl IndexMut for Indexable { - //~ MONO_ITEM fn >::index_mut +impl IndexMut for _Indexable { + //~ MONO_ITEM fn <_Indexable as std::ops::IndexMut>::index_mut fn index_mut(&mut self, index: usize) -> &mut Self::Output { - if index >= 3 { - &mut self.data[0] - } else { - &mut self.data[index] - } + if index >= 3 { &mut self.data[0] } else { &mut self.data[index] } } } - -//~ MONO_ITEM fn ::eq -//~ MONO_ITEM fn ::ne +//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::eq +//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::ne #[derive(PartialEq)] -pub struct Equatable(u32); +pub struct _Equatable(u32); - -impl Add for Equatable { +impl Add for _Equatable { type Output = u32; - //~ MONO_ITEM fn >::add + //~ MONO_ITEM fn <_Equatable as std::ops::Add>::add fn add(self, rhs: u32) -> u32 { self.0 + rhs } } -impl Deref for Equatable { +impl Deref for _Equatable { type Target = u32; - //~ MONO_ITEM fn ::deref + //~ MONO_ITEM fn <_Equatable as std::ops::Deref>::deref fn deref(&self) -> &Self::Target { &self.0 } diff --git a/tests/codegen-units/item-collection/static-init.rs b/tests/codegen-units/item-collection/static-init.rs index b357f5cd66b2..1406fba2b984 100644 --- a/tests/codegen-units/item-collection/static-init.rs +++ b/tests/codegen-units/item-collection/static-init.rs @@ -2,9 +2,9 @@ #![feature(start)] -pub static FN : fn() = foo::; +pub static FN: fn() = foo::; -pub fn foo() { } +pub fn foo() {} //~ MONO_ITEM fn foo:: //~ MONO_ITEM static FN diff --git a/tests/codegen-units/item-collection/trait-implementations.rs b/tests/codegen-units/item-collection/trait-implementations.rs index b364cc5b3336..e4c444499e05 100644 --- a/tests/codegen-units/item-collection/trait-implementations.rs +++ b/tests/codegen-units/item-collection/trait-implementations.rs @@ -9,7 +9,6 @@ pub trait SomeTrait { } impl SomeTrait for i64 { - //~ MONO_ITEM fn ::foo fn foo(&self) {} @@ -17,7 +16,6 @@ impl SomeTrait for i64 { } impl SomeTrait for i32 { - //~ MONO_ITEM fn ::foo fn foo(&self) {} @@ -31,7 +29,6 @@ pub trait SomeGenericTrait { // Concrete impl of generic trait impl SomeGenericTrait for f64 { - //~ MONO_ITEM fn >::foo fn foo(&self, _: u32) {} @@ -40,7 +37,6 @@ impl SomeGenericTrait for f64 { // Generic impl of generic trait impl SomeGenericTrait for f32 { - fn foo(&self, _: T) {} fn bar(&self, _: T, _: T2) {} } @@ -48,26 +44,26 @@ impl SomeGenericTrait for f32 { //~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn ::bar:: - 0i32.bar('x'); + //~ MONO_ITEM fn ::bar:: + 0i32.bar('x'); - //~ MONO_ITEM fn >::bar::<&str> - 0f64.bar(0u32, "&str"); + //~ MONO_ITEM fn >::bar::<&str> + 0f64.bar(0u32, "&str"); - //~ MONO_ITEM fn >::bar::<()> - 0f64.bar(0u32, ()); + //~ MONO_ITEM fn >::bar::<()> + 0f64.bar(0u32, ()); - //~ MONO_ITEM fn >::foo - 0f32.foo('x'); + //~ MONO_ITEM fn >::foo + 0f32.foo('x'); - //~ MONO_ITEM fn >::foo - 0f32.foo(-1i64); + //~ MONO_ITEM fn >::foo + 0f32.foo(-1i64); - //~ MONO_ITEM fn >::bar::<()> - 0f32.bar(0u32, ()); + //~ MONO_ITEM fn >::bar::<()> + 0f32.bar(0u32, ()); - //~ MONO_ITEM fn >::bar::<&str> - 0f32.bar("&str", "&str"); + //~ MONO_ITEM fn >::bar::<&str> + 0f32.bar("&str", "&str"); - 0 + 0 } diff --git a/tests/codegen-units/item-collection/trait-method-as-argument.rs b/tests/codegen-units/item-collection/trait-method-as-argument.rs index c25e3ea45ecc..10cf2a0e967e 100644 --- a/tests/codegen-units/item-collection/trait-method-as-argument.rs +++ b/tests/codegen-units/item-collection/trait-method-as-argument.rs @@ -3,16 +3,19 @@ #![deny(dead_code)] #![feature(start)] -trait Trait : Sized { - fn foo(self) -> Self { self } +trait Trait: Sized { + fn foo(self) -> Self { + self + } } impl Trait for u32 { - fn foo(self) -> u32 { self } + fn foo(self) -> u32 { + self + } } -impl Trait for char { -} +impl Trait for char {} fn take_foo_once T>(f: F, arg: T) -> T { (f)(arg) diff --git a/tests/codegen-units/item-collection/trait-method-default-impl.rs b/tests/codegen-units/item-collection/trait-method-default-impl.rs index 89fec350f09c..b0a43d28e40b 100644 --- a/tests/codegen-units/item-collection/trait-method-default-impl.rs +++ b/tests/codegen-units/item-collection/trait-method-default-impl.rs @@ -4,8 +4,10 @@ #![feature(start)] trait SomeTrait { - fn foo(&self) { } - fn bar(&self, x: T) -> T { x } + fn foo(&self) {} + fn bar(&self, x: T) -> T { + x + } } impl SomeTrait for i8 { @@ -17,7 +19,7 @@ impl SomeTrait for i8 { } trait SomeGenericTrait { - fn foo(&self) { } + fn foo(&self) {} fn bar(&self, x: T1, y: T2) {} } diff --git a/tests/codegen-units/item-collection/unsizing.rs b/tests/codegen-units/item-collection/unsizing.rs index 1e2d7f174845..5ea8b47962a8 100644 --- a/tests/codegen-units/item-collection/unsizing.rs +++ b/tests/codegen-units/item-collection/unsizing.rs @@ -27,7 +27,7 @@ impl Trait for char { struct Struct { _a: u32, _b: i32, - _c: T + _c: T, } impl Trait for f64 { @@ -60,11 +60,7 @@ fn start(_: isize, _: *const *const u8) -> isize { let _char_unsized = char_sized as &Trait; // struct field - let struct_sized = &Struct { - _a: 1, - _b: 2, - _c: 3.0f64 - }; + let struct_sized = &Struct { _a: 1, _b: 2, _c: 3.0f64 }; //~ MONO_ITEM fn std::ptr::drop_in_place:: - shim(None) @@ unsizing-cgu.0[Internal] //~ MONO_ITEM fn ::foo let _struct_unsized = struct_sized as &Struct; diff --git a/tests/codegen-units/item-collection/unused-traits-and-generics.rs b/tests/codegen-units/item-collection/unused-traits-and-generics.rs index 27cdae2c0962..d5088d3f8937 100644 --- a/tests/codegen-units/item-collection/unused-traits-and-generics.rs +++ b/tests/codegen-units/item-collection/unused-traits-and-generics.rs @@ -1,6 +1,6 @@ //@ compile-flags:-Zprint-mono-items=eager -#![crate_type="lib"] +#![crate_type = "lib"] #![deny(dead_code)] // This test asserts that no codegen items are generated for generic items that @@ -16,7 +16,7 @@ pub fn foo(x: T) -> (T, T) { } pub struct Struct { - x: T + x: T, } impl Struct { @@ -29,7 +29,7 @@ impl Struct { pub enum Enum { A(T), - B { x: T } + B { x: T }, } impl Enum { @@ -56,7 +56,7 @@ impl TupleStruct { pub type Pair = (T, T); pub struct NonGeneric { - x: i32 + x: i32, } impl NonGeneric { diff --git a/tests/codegen-units/partitioning/auxiliary/shared_generics_aux.rs b/tests/codegen-units/partitioning/auxiliary/shared_generics_aux.rs index 158932d165df..b6c568ed387a 100644 --- a/tests/codegen-units/partitioning/auxiliary/shared_generics_aux.rs +++ b/tests/codegen-units/partitioning/auxiliary/shared_generics_aux.rs @@ -3,7 +3,7 @@ //@ compile-flags:-Zshare-generics=yes -Copt-level=0 //@ no-prefer-dynamic -#![crate_type="rlib"] +#![crate_type = "rlib"] pub fn generic_fn(x: T, y: T) -> (T, T) { (x, y) diff --git a/tests/codegen-units/partitioning/extern-generic.rs b/tests/codegen-units/partitioning/extern-generic.rs index 0e4b6a37f712..602a5240283b 100644 --- a/tests/codegen-units/partitioning/extern-generic.rs +++ b/tests/codegen-units/partitioning/extern-generic.rs @@ -3,7 +3,7 @@ //@ compile-flags:-Zprint-mono-items=eager -Zshare-generics=y #![allow(dead_code)] -#![crate_type="lib"] +#![crate_type = "lib"] //@ aux-build:cgu_generic_function.rs extern crate cgu_generic_function; diff --git a/tests/codegen-units/partitioning/inlining-from-extern-crate.rs b/tests/codegen-units/partitioning/inlining-from-extern-crate.rs index d021f467f1f9..b007ffe1cb51 100644 --- a/tests/codegen-units/partitioning/inlining-from-extern-crate.rs +++ b/tests/codegen-units/partitioning/inlining-from-extern-crate.rs @@ -3,7 +3,7 @@ //@ compile-flags:-Zprint-mono-items=lazy //@ compile-flags:-Zinline-in-all-cgus -#![crate_type="lib"] +#![crate_type = "lib"] //@ aux-build:cgu_explicit_inlining.rs extern crate cgu_explicit_inlining; @@ -15,8 +15,7 @@ extern crate cgu_explicit_inlining; //~ MONO_ITEM fn cgu_explicit_inlining::always_inlined @@ inlining_from_extern_crate[Internal] inlining_from_extern_crate-mod2[Internal] //~ MONO_ITEM fn user @@ inlining_from_extern_crate[External] -pub fn user() -{ +pub fn user() { cgu_explicit_inlining::inlined(); cgu_explicit_inlining::always_inlined(); @@ -28,8 +27,7 @@ pub mod mod1 { use cgu_explicit_inlining; //~ MONO_ITEM fn mod1::user @@ inlining_from_extern_crate-mod1[External] - pub fn user() - { + pub fn user() { cgu_explicit_inlining::inlined(); // does not generate a monomorphization in this crate @@ -41,8 +39,7 @@ pub mod mod2 { use cgu_explicit_inlining; //~ MONO_ITEM fn mod2::user @@ inlining_from_extern_crate-mod2[External] - pub fn user() - { + pub fn user() { cgu_explicit_inlining::always_inlined(); // does not generate a monomorphization in this crate diff --git a/tests/codegen-units/partitioning/local-generic.rs b/tests/codegen-units/partitioning/local-generic.rs index 06f46b23db6a..0cfc572650c1 100644 --- a/tests/codegen-units/partitioning/local-generic.rs +++ b/tests/codegen-units/partitioning/local-generic.rs @@ -3,13 +3,15 @@ //@ compile-flags:-Zprint-mono-items=eager #![allow(dead_code)] -#![crate_type="lib"] +#![crate_type = "lib"] //~ MONO_ITEM fn generic:: @@ local_generic.volatile[External] //~ MONO_ITEM fn generic:: @@ local_generic.volatile[External] //~ MONO_ITEM fn generic:: @@ local_generic.volatile[External] //~ MONO_ITEM fn generic::<&str> @@ local_generic.volatile[External] -pub fn generic(x: T) -> T { x } +pub fn generic(x: T) -> T { + x +} //~ MONO_ITEM fn user @@ local_generic[Internal] fn user() { diff --git a/tests/codegen-units/partitioning/local-inlining-but-not-all.rs b/tests/codegen-units/partitioning/local-inlining-but-not-all.rs index 2f9cbe83f1d5..454de255254a 100644 --- a/tests/codegen-units/partitioning/local-inlining-but-not-all.rs +++ b/tests/codegen-units/partitioning/local-inlining-but-not-all.rs @@ -4,16 +4,13 @@ //@ compile-flags:-Zinline-in-all-cgus=no #![allow(dead_code)] -#![crate_type="lib"] +#![crate_type = "lib"] mod inline { //~ MONO_ITEM fn inline::inlined_function @@ local_inlining_but_not_all-inline[External] #[inline] - pub fn inlined_function() - { - - } + pub fn inlined_function() {} } pub mod user1 { @@ -37,7 +34,5 @@ pub mod user2 { pub mod non_user { //~ MONO_ITEM fn non_user::baz @@ local_inlining_but_not_all-non_user[External] - pub fn baz() { - - } + pub fn baz() {} } diff --git a/tests/codegen-units/partitioning/local-inlining.rs b/tests/codegen-units/partitioning/local-inlining.rs index 2329a876c960..42c68b5c6218 100644 --- a/tests/codegen-units/partitioning/local-inlining.rs +++ b/tests/codegen-units/partitioning/local-inlining.rs @@ -4,17 +4,14 @@ //@ compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] -#![crate_type="lib"] +#![crate_type = "lib"] mod inline { // Important: This function should show up in all codegen units where it is inlined //~ MONO_ITEM fn inline::inlined_function @@ local_inlining-user1[Internal] local_inlining-user2[Internal] #[inline(always)] - pub fn inlined_function() - { - - } + pub fn inlined_function() {} } pub mod user1 { @@ -38,7 +35,5 @@ pub mod user2 { pub mod non_user { //~ MONO_ITEM fn non_user::baz @@ local_inlining-non_user[External] - pub fn baz() { - - } + pub fn baz() {} } diff --git a/tests/codegen-units/partitioning/local-transitive-inlining.rs b/tests/codegen-units/partitioning/local-transitive-inlining.rs index 4ccc53aea1d1..0d279ebe7404 100644 --- a/tests/codegen-units/partitioning/local-transitive-inlining.rs +++ b/tests/codegen-units/partitioning/local-transitive-inlining.rs @@ -4,16 +4,13 @@ //@ compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] -#![crate_type="rlib"] +#![crate_type = "rlib"] mod inline { //~ MONO_ITEM fn inline::inlined_function @@ local_transitive_inlining-indirect_user[Internal] #[inline(always)] - pub fn inlined_function() - { - - } + pub fn inlined_function() {} } mod direct_user { @@ -38,7 +35,5 @@ pub mod indirect_user { pub mod non_user { //~ MONO_ITEM fn non_user::baz @@ local_transitive_inlining-non_user[External] - pub fn baz() { - - } + pub fn baz() {} } diff --git a/tests/codegen-units/partitioning/methods-are-with-self-type.rs b/tests/codegen-units/partitioning/methods-are-with-self-type.rs index 3ba53334cc90..901e7507d738 100644 --- a/tests/codegen-units/partitioning/methods-are-with-self-type.rs +++ b/tests/codegen-units/partitioning/methods-are-with-self-type.rs @@ -15,7 +15,7 @@ struct SomeType; struct SomeGenericType(T1, T2); mod mod1 { - use super::{SomeType, SomeGenericType}; + use super::{SomeGenericType, SomeType}; // Even though the impl is in `mod1`, the methods should end up in the // parent module, since that is where their self-type is. @@ -40,8 +40,7 @@ trait Trait { // We provide an implementation of `Trait` for all types. The corresponding // monomorphizations should end up in whichever module the concrete `T` is. -impl Trait for T -{ +impl Trait for T { fn foo(&self) {} } diff --git a/tests/codegen-units/partitioning/regular-modules.rs b/tests/codegen-units/partitioning/regular-modules.rs index 68601975d062..294971464155 100644 --- a/tests/codegen-units/partitioning/regular-modules.rs +++ b/tests/codegen-units/partitioning/regular-modules.rs @@ -3,7 +3,7 @@ //@ compile-flags:-Zprint-mono-items=eager #![allow(dead_code)] -#![crate_type="lib"] +#![crate_type = "lib"] //~ MONO_ITEM fn foo @@ regular_modules[Internal] fn foo() {} diff --git a/tests/codegen-units/partitioning/shared-generics.rs b/tests/codegen-units/partitioning/shared-generics.rs index 5b78794316c6..ea312719ac91 100644 --- a/tests/codegen-units/partitioning/shared-generics.rs +++ b/tests/codegen-units/partitioning/shared-generics.rs @@ -4,14 +4,13 @@ //@ incremental //@ compile-flags:-Zprint-mono-items=eager -Zshare-generics=yes -Copt-level=0 -#![crate_type="rlib"] +#![crate_type = "rlib"] //@ aux-build:shared_generics_aux.rs extern crate shared_generics_aux; //~ MONO_ITEM fn foo pub fn foo() { - //~ MONO_ITEM fn shared_generics_aux::generic_fn:: @@ shared_generics_aux-in-shared_generics.volatile[External] let _ = shared_generics_aux::generic_fn(0u16, 1u16); diff --git a/tests/codegen-units/partitioning/statics.rs b/tests/codegen-units/partitioning/statics.rs index c7eef1f3789d..00dd6d877e1e 100644 --- a/tests/codegen-units/partitioning/statics.rs +++ b/tests/codegen-units/partitioning/statics.rs @@ -2,7 +2,7 @@ //@ incremental //@ compile-flags:-Zprint-mono-items=lazy -#![crate_type="rlib"] +#![crate_type = "rlib"] //~ MONO_ITEM static FOO @@ statics[Internal] static FOO: u32 = 0; diff --git a/tests/codegen-units/partitioning/vtable-through-const.rs b/tests/codegen-units/partitioning/vtable-through-const.rs index ca4d3822b549..a9186cea9c89 100644 --- a/tests/codegen-units/partitioning/vtable-through-const.rs +++ b/tests/codegen-units/partitioning/vtable-through-const.rs @@ -9,79 +9,94 @@ #![feature(start)] mod mod1 { + struct NeedsDrop; + + impl Drop for NeedsDrop { + fn drop(&mut self) {} + } + pub trait Trait1 { fn do_something(&self) {} fn do_something_else(&self) {} } - impl Trait1 for u32 {} + impl Trait1 for NeedsDrop {} pub trait Trait1Gen { fn do_something(&self, x: T) -> T; fn do_something_else(&self, x: T) -> T; } - impl Trait1Gen for u32 { - fn do_something(&self, x: T) -> T { x } - fn do_something_else(&self, x: T) -> T { x } + impl Trait1Gen for NeedsDrop { + fn do_something(&self, x: T) -> T { + x + } + fn do_something_else(&self, x: T) -> T { + x + } } //~ MONO_ITEM fn mod1::id:: @@ vtable_through_const-mod1.volatile[Internal] - fn id(x: T) -> T { x } + fn id(x: T) -> T { + x + } // These are referenced, so they produce mono-items (see start()) - pub const TRAIT1_REF: &'static Trait1 = &0u32 as &Trait1; - pub const TRAIT1_GEN_REF: &'static Trait1Gen = &0u32 as &Trait1Gen; + pub const TRAIT1_REF: &'static Trait1 = &NeedsDrop as &Trait1; + pub const TRAIT1_GEN_REF: &'static Trait1Gen = &NeedsDrop as &Trait1Gen; pub const ID_CHAR: fn(char) -> char = id::; - - pub trait Trait2 { fn do_something(&self) {} fn do_something_else(&self) {} } - //~ MONO_ITEM fn ::do_something @@ vtable_through_const-mod1.volatile[Internal] - //~ MONO_ITEM fn ::do_something_else @@ vtable_through_const-mod1.volatile[Internal] - impl Trait2 for u32 {} + //~ MONO_ITEM fn ::do_something @@ vtable_through_const-mod1.volatile[Internal] + //~ MONO_ITEM fn ::do_something_else @@ vtable_through_const-mod1.volatile[Internal] + impl Trait2 for NeedsDrop {} pub trait Trait2Gen { fn do_something(&self, x: T) -> T; fn do_something_else(&self, x: T) -> T; } - impl Trait2Gen for u32 { - fn do_something(&self, x: T) -> T { x } - fn do_something_else(&self, x: T) -> T { x } + impl Trait2Gen for NeedsDrop { + fn do_something(&self, x: T) -> T { + x + } + fn do_something_else(&self, x: T) -> T { + x + } } // These are not referenced, so they do not produce mono-items - pub const TRAIT2_REF: &'static Trait2 = &0u32 as &Trait2; - pub const TRAIT2_GEN_REF: &'static Trait2Gen = &0u32 as &Trait2Gen; + pub const TRAIT2_REF: &'static Trait2 = &NeedsDrop as &Trait2; + pub const TRAIT2_GEN_REF: &'static Trait2Gen = &NeedsDrop as &Trait2Gen; pub const ID_I64: fn(i64) -> i64 = id::; } //~ MONO_ITEM fn start #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ MONO_ITEM fn std::ptr::drop_in_place:: - shim(None) @@ vtable_through_const[Internal] + //~ MONO_ITEM fn ::drop @@ vtable_through_const-fallback.cgu[Internal] + //~ MONO_ITEM fn std::ptr::drop_in_place:: - shim(Some(mod1::NeedsDrop)) @@ vtable_through_const-fallback.cgu[External] // Since Trait1::do_something() is instantiated via its default implementation, // it is considered a generic and is instantiated here only because it is // referenced in this module. - //~ MONO_ITEM fn ::do_something_else @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn ::do_something_else @@ vtable_through_const-mod1.volatile[External] // Although it is never used, Trait1::do_something_else() has to be - // instantiated locally here too, otherwise the <&u32 as &Trait1> vtable + // instantiated locally here too, otherwise the <&NeedsDrop as &Trait1> vtable // could not be fully constructed. - //~ MONO_ITEM fn ::do_something @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn ::do_something @@ vtable_through_const-mod1.volatile[External] mod1::TRAIT1_REF.do_something(); // Same as above - //~ MONO_ITEM fn >::do_something @@ vtable_through_const-mod1.volatile[External] - //~ MONO_ITEM fn >::do_something_else @@ vtable_through_const-mod1.volatile[External] - //~ MONO_ITEM fn >::do_something @@ vtable_through_const-mod1.volatile[Internal] - //~ MONO_ITEM fn >::do_something_else @@ vtable_through_const-mod1.volatile[Internal] + //~ MONO_ITEM fn >::do_something @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn >::do_something_else @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn >::do_something @@ vtable_through_const-mod1.volatile[Internal] + //~ MONO_ITEM fn >::do_something_else @@ vtable_through_const-mod1.volatile[Internal] mod1::TRAIT1_GEN_REF.do_something(0u8); //~ MONO_ITEM fn mod1::id:: @@ vtable_through_const-mod1.volatile[External] diff --git a/tests/codegen-units/polymorphization/unused_type_parameters.rs b/tests/codegen-units/polymorphization/unused_type_parameters.rs index cf5f7c320985..438305f112f3 100644 --- a/tests/codegen-units/polymorphization/unused_type_parameters.rs +++ b/tests/codegen-units/polymorphization/unused_type_parameters.rs @@ -9,54 +9,51 @@ mod functions { // Function doesn't have any type parameters to be unused. pub fn no_parameters() {} -//~ MONO_ITEM fn functions::no_parameters + //~ MONO_ITEM fn functions::no_parameters // Function has an unused type parameter. - pub fn unused() { - } + pub fn unused() {} -//~ MONO_ITEM fn functions::unused:: + //~ MONO_ITEM fn functions::unused:: // Function uses type parameter in value of a binding. pub fn used_binding_value() { let _: T = Default::default(); } -//~ MONO_ITEM fn functions::used_binding_value:: -//~ MONO_ITEM fn functions::used_binding_value:: + //~ MONO_ITEM fn functions::used_binding_value:: + //~ MONO_ITEM fn functions::used_binding_value:: // Function uses type parameter in type of a binding. pub fn used_binding_type() { let _: Option = None; } -//~ MONO_ITEM fn functions::used_binding_type:: -//~ MONO_ITEM fn functions::used_binding_type:: + //~ MONO_ITEM fn functions::used_binding_type:: + //~ MONO_ITEM fn functions::used_binding_type:: // Function uses type parameter in argument. - pub fn used_argument(_: T) { - } + pub fn used_argument(_: T) {} -//~ MONO_ITEM fn functions::used_argument:: -//~ MONO_ITEM fn functions::used_argument:: -// + //~ MONO_ITEM fn functions::used_argument:: + //~ MONO_ITEM fn functions::used_argument:: + // // Function uses type parameter in substitutions to another function. pub fn used_substs() { unused::() } -//~ MONO_ITEM fn functions::used_substs:: -//~ MONO_ITEM fn functions::used_substs:: + //~ MONO_ITEM fn functions::used_substs:: + //~ MONO_ITEM fn functions::used_substs:: } - mod closures { // Function doesn't have any type parameters to be unused. pub fn no_parameters() { let _ = || {}; } -//~ MONO_ITEM fn closures::no_parameters + //~ MONO_ITEM fn closures::no_parameters // Function has an unused type parameter in parent and closure. pub fn unused() -> u32 { @@ -64,8 +61,8 @@ mod closures { add_one(3) } -//~ MONO_ITEM fn closures::unused::::{closure#0} -//~ MONO_ITEM fn closures::unused:: + //~ MONO_ITEM fn closures::unused::::{closure#0} + //~ MONO_ITEM fn closures::unused:: // Function has an unused type parameter in closure, but not in parent. pub fn used_parent() -> u32 { @@ -74,9 +71,9 @@ mod closures { add_one(3) } -//~ MONO_ITEM fn closures::used_parent::::{closure#0} -//~ MONO_ITEM fn closures::used_parent:: -//~ MONO_ITEM fn closures::used_parent:: + //~ MONO_ITEM fn closures::used_parent::::{closure#0} + //~ MONO_ITEM fn closures::used_parent:: + //~ MONO_ITEM fn closures::used_parent:: // Function uses type parameter in value of a binding in closure. pub fn used_binding_value() -> T { @@ -88,10 +85,10 @@ mod closures { x() } -//~ MONO_ITEM fn closures::used_binding_value::::{closure#0} -//~ MONO_ITEM fn closures::used_binding_value::::{closure#0} -//~ MONO_ITEM fn closures::used_binding_value:: -//~ MONO_ITEM fn closures::used_binding_value:: + //~ MONO_ITEM fn closures::used_binding_value::::{closure#0} + //~ MONO_ITEM fn closures::used_binding_value::::{closure#0} + //~ MONO_ITEM fn closures::used_binding_value:: + //~ MONO_ITEM fn closures::used_binding_value:: // Function uses type parameter in type of a binding in closure. pub fn used_binding_type() -> Option { @@ -103,10 +100,10 @@ mod closures { x() } -//~ MONO_ITEM fn closures::used_binding_type::::{closure#0} -//~ MONO_ITEM fn closures::used_binding_type::::{closure#0} -//~ MONO_ITEM fn closures::used_binding_type:: -//~ MONO_ITEM fn closures::used_binding_type:: + //~ MONO_ITEM fn closures::used_binding_type::::{closure#0} + //~ MONO_ITEM fn closures::used_binding_type::::{closure#0} + //~ MONO_ITEM fn closures::used_binding_type:: + //~ MONO_ITEM fn closures::used_binding_type:: // Function and closure uses type parameter in argument. pub fn used_argument(t: T) -> u32 { @@ -114,10 +111,10 @@ mod closures { x(t) } -//~ MONO_ITEM fn closures::used_argument::::{closure#0} -//~ MONO_ITEM fn closures::used_argument::::{closure#0} -//~ MONO_ITEM fn closures::used_argument:: -//~ MONO_ITEM fn closures::used_argument:: + //~ MONO_ITEM fn closures::used_argument::::{closure#0} + //~ MONO_ITEM fn closures::used_argument::::{closure#0} + //~ MONO_ITEM fn closures::used_argument:: + //~ MONO_ITEM fn closures::used_argument:: // Closure uses type parameter in argument. pub fn used_argument_closure() -> u32 { @@ -126,10 +123,10 @@ mod closures { x(t) } -//~ MONO_ITEM fn closures::used_argument_closure::::{closure#0} -//~ MONO_ITEM fn closures::used_argument_closure::::{closure#0} -//~ MONO_ITEM fn closures::used_argument_closure:: -//~ MONO_ITEM fn closures::used_argument_closure:: + //~ MONO_ITEM fn closures::used_argument_closure::::{closure#0} + //~ MONO_ITEM fn closures::used_argument_closure::::{closure#0} + //~ MONO_ITEM fn closures::used_argument_closure:: + //~ MONO_ITEM fn closures::used_argument_closure:: // Closure uses type parameter as upvar. pub fn used_upvar() -> T { @@ -138,10 +135,10 @@ mod closures { y() } -//~ MONO_ITEM fn closures::used_upvar::::{closure#0} -//~ MONO_ITEM fn closures::used_upvar::::{closure#0} -//~ MONO_ITEM fn closures::used_upvar:: -//~ MONO_ITEM fn closures::used_upvar:: + //~ MONO_ITEM fn closures::used_upvar::::{closure#0} + //~ MONO_ITEM fn closures::used_upvar::::{closure#0} + //~ MONO_ITEM fn closures::used_upvar:: + //~ MONO_ITEM fn closures::used_upvar:: // Closure uses type parameter in substitutions to another function. pub fn used_substs() { @@ -149,10 +146,10 @@ mod closures { x() } -//~ MONO_ITEM fn closures::used_substs::::{closure#0} -//~ MONO_ITEM fn closures::used_substs::::{closure#0} -//~ MONO_ITEM fn closures::used_substs:: -//~ MONO_ITEM fn closures::used_substs:: + //~ MONO_ITEM fn closures::used_substs::::{closure#0} + //~ MONO_ITEM fn closures::used_substs::::{closure#0} + //~ MONO_ITEM fn closures::used_substs:: + //~ MONO_ITEM fn closures::used_substs:: } mod methods { @@ -160,32 +157,30 @@ mod methods { impl Foo { // Function has an unused type parameter from impl. - pub fn unused_impl() { - } + pub fn unused_impl() {} -//~ MONO_ITEM fn methods::Foo::::unused_impl + //~ MONO_ITEM fn methods::Foo::::unused_impl // Function has an unused type parameter from impl and fn. - pub fn unused_both() { - } + pub fn unused_both() {} -//~ MONO_ITEM fn methods::Foo::::unused_both:: + //~ MONO_ITEM fn methods::Foo::::unused_both:: // Function uses type parameter from impl. pub fn used_impl() { let _: F = Default::default(); } -//~ MONO_ITEM fn methods::Foo::::used_impl -//~ MONO_ITEM fn methods::Foo::::used_impl + //~ MONO_ITEM fn methods::Foo::::used_impl + //~ MONO_ITEM fn methods::Foo::::used_impl // Function uses type parameter from impl. pub fn used_fn() { let _: G = Default::default(); } -//~ MONO_ITEM fn methods::Foo::::used_fn:: -//~ MONO_ITEM fn methods::Foo::::used_fn:: + //~ MONO_ITEM fn methods::Foo::::used_fn:: + //~ MONO_ITEM fn methods::Foo::::used_fn:: // Function uses type parameter from impl. pub fn used_both() { @@ -193,16 +188,16 @@ mod methods { let _: G = Default::default(); } -//~ MONO_ITEM fn methods::Foo::::used_both:: -//~ MONO_ITEM fn methods::Foo::::used_both:: + //~ MONO_ITEM fn methods::Foo::::used_both:: + //~ MONO_ITEM fn methods::Foo::::used_both:: // Function uses type parameter in substitutions to another function. pub fn used_substs() { super::functions::unused::() } -//~ MONO_ITEM fn methods::Foo::::used_substs -//~ MONO_ITEM fn methods::Foo::::used_substs + //~ MONO_ITEM fn methods::Foo::::used_substs + //~ MONO_ITEM fn methods::Foo::::used_substs // Function has an unused type parameter from impl and fn. pub fn closure_unused_all() -> u32 { @@ -210,8 +205,8 @@ mod methods { add_one(3) } -//~ MONO_ITEM fn methods::Foo::::closure_unused_all::::{closure#0} -//~ MONO_ITEM fn methods::Foo::::closure_unused_all:: + //~ MONO_ITEM fn methods::Foo::::closure_unused_all::::{closure#0} + //~ MONO_ITEM fn methods::Foo::::closure_unused_all:: // Function uses type parameter from impl and fn in closure. pub fn closure_used_both() -> u32 { @@ -224,10 +219,10 @@ mod methods { add_one(3) } -//~ MONO_ITEM fn methods::Foo::::closure_used_both::::{closure#0} -//~ MONO_ITEM fn methods::Foo::::closure_used_both::::{closure#0} -//~ MONO_ITEM fn methods::Foo::::closure_used_both:: -//~ MONO_ITEM fn methods::Foo::::closure_used_both:: + //~ MONO_ITEM fn methods::Foo::::closure_used_both::::{closure#0} + //~ MONO_ITEM fn methods::Foo::::closure_used_both::::{closure#0} + //~ MONO_ITEM fn methods::Foo::::closure_used_both:: + //~ MONO_ITEM fn methods::Foo::::closure_used_both:: // Function uses type parameter from fn in closure. pub fn closure_used_fn() -> u32 { @@ -239,10 +234,10 @@ mod methods { add_one(3) } -//~ MONO_ITEM fn methods::Foo::::closure_used_fn::::{closure#0} -//~ MONO_ITEM fn methods::Foo::::closure_used_fn::::{closure#0} -//~ MONO_ITEM fn methods::Foo::::closure_used_fn:: -//~ MONO_ITEM fn methods::Foo::::closure_used_fn:: + //~ MONO_ITEM fn methods::Foo::::closure_used_fn::::{closure#0} + //~ MONO_ITEM fn methods::Foo::::closure_used_fn::::{closure#0} + //~ MONO_ITEM fn methods::Foo::::closure_used_fn:: + //~ MONO_ITEM fn methods::Foo::::closure_used_fn:: // Function uses type parameter from impl in closure. pub fn closure_used_impl() -> u32 { @@ -254,10 +249,10 @@ mod methods { add_one(3) } -//~ MONO_ITEM fn methods::Foo::::closure_used_impl::::{closure#0} -//~ MONO_ITEM fn methods::Foo::::closure_used_impl::::{closure#0} -//~ MONO_ITEM fn methods::Foo::::closure_used_impl:: -//~ MONO_ITEM fn methods::Foo::::closure_used_impl:: + //~ MONO_ITEM fn methods::Foo::::closure_used_impl::::{closure#0} + //~ MONO_ITEM fn methods::Foo::::closure_used_impl::::{closure#0} + //~ MONO_ITEM fn methods::Foo::::closure_used_impl:: + //~ MONO_ITEM fn methods::Foo::::closure_used_impl:: // Closure uses type parameter in substitutions to another function. pub fn closure_used_substs() { @@ -265,15 +260,13 @@ mod methods { x() } -//~ MONO_ITEM fn methods::Foo::::closure_used_substs::{closure#0} -//~ MONO_ITEM fn methods::Foo::::closure_used_substs::{closure#0} -//~ MONO_ITEM fn methods::Foo::::closure_used_substs -//~ MONO_ITEM fn methods::Foo::::closure_used_substs + //~ MONO_ITEM fn methods::Foo::::closure_used_substs::{closure#0} + //~ MONO_ITEM fn methods::Foo::::closure_used_substs::{closure#0} + //~ MONO_ITEM fn methods::Foo::::closure_used_substs + //~ MONO_ITEM fn methods::Foo::::closure_used_substs } } - - fn dispatch() { functions::no_parameters(); functions::unused::(); diff --git a/tests/codegen/aarch64-struct-align-128.rs b/tests/codegen/aarch64-struct-align-128.rs index 0a30a2527da6..d1b4132d501f 100644 --- a/tests/codegen/aarch64-struct-align-128.rs +++ b/tests/codegen/aarch64-struct-align-128.rs @@ -12,14 +12,12 @@ #![crate_type = "lib"] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="freeze"] -trait Freeze { } -#[lang="copy"] -trait Copy { } - - +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} // Passed as `[i64 x 2]`, since it's an aggregate with size <= 128 bits, align < 128 bits. #[repr(C)] @@ -31,7 +29,7 @@ pub struct Align8 { // repr(transparent), so same as above. #[repr(transparent)] pub struct Transparent8 { - a: Align8 + a: Align8, } // Passed as `[i64 x 2]`, since it's an aggregate with size <= 128 bits, align < 128 bits. @@ -47,8 +45,6 @@ extern "C" { fn test_8(a: Align8, b: Transparent8, c: Wrapped8); } - - // Passed as `i128`, since it's an aggregate with size <= 128 bits, align = 128 bits. // EXCEPT on Linux, where there's a special case to use its unadjusted alignment, // making it the same as `Align8`, so it's be passed as `[i64 x 2]`. @@ -62,7 +58,7 @@ pub struct Align16 { // repr(transparent), so same as above. #[repr(transparent)] pub struct Transparent16 { - a: Align16 + a: Align16, } // Passed as `i128`, since it's an aggregate with size <= 128 bits, align = 128 bits. @@ -79,8 +75,6 @@ extern "C" { fn test_16(a: Align16, b: Transparent16, c: Wrapped16); } - - // Passed as `i128`, since it's an aggregate with size <= 128 bits, align = 128 bits. #[repr(C)] pub struct I128 { @@ -90,13 +84,13 @@ pub struct I128 { // repr(transparent), so same as above. #[repr(transparent)] pub struct TransparentI128 { - a: I128 + a: I128, } // Passed as `i128`, since it's an aggregate with size <= 128 bits, align = 128 bits. #[repr(C)] pub struct WrappedI128 { - pub a: I128 + pub a: I128, } extern "C" { @@ -106,8 +100,6 @@ extern "C" { fn test_i128(a: I128, b: TransparentI128, c: WrappedI128); } - - // Passed as `[2 x i64]`, since it's an aggregate with size <= 128 bits, align < 128 bits. // Note that the Linux special case does not apply, because packing is not considered "adjustment". #[repr(C)] @@ -119,13 +111,13 @@ pub struct Packed { // repr(transparent), so same as above. #[repr(transparent)] pub struct TransparentPacked { - a: Packed + a: Packed, } // Passed as `[2 x i64]`, since it's an aggregate with size <= 128 bits, align < 128 bits. #[repr(C)] pub struct WrappedPacked { - pub a: Packed + pub a: Packed, } extern "C" { @@ -135,13 +127,19 @@ extern "C" { fn test_packed(a: Packed, b: TransparentPacked, c: WrappedPacked); } - - pub unsafe fn main( - a1: Align8, a2: Transparent8, a3: Wrapped8, - b1: Align16, b2: Transparent16, b3: Wrapped16, - c1: I128, c2: TransparentI128, c3: WrappedI128, - d1: Packed, d2: TransparentPacked, d3: WrappedPacked, + a1: Align8, + a2: Transparent8, + a3: Wrapped8, + b1: Align16, + b2: Transparent16, + b3: Wrapped16, + c1: I128, + c2: TransparentI128, + c3: WrappedI128, + d1: Packed, + d2: TransparentPacked, + d3: WrappedPacked, ) { test_8(a1, a2, a3); test_16(b1, b2, b3); diff --git a/tests/codegen/abi-efiapi.rs b/tests/codegen/abi-efiapi.rs index fa73f649ed87..986d042268aa 100644 --- a/tests/codegen/abi-efiapi.rs +++ b/tests/codegen/abi-efiapi.rs @@ -17,12 +17,12 @@ #![feature(no_core, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="freeze"] -trait Freeze { } -#[lang="copy"] -trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} //x86_64: define win64cc void @has_efiapi //i686: define void @has_efiapi diff --git a/tests/codegen/abi-main-signature-16bit-c-int.rs b/tests/codegen/abi-main-signature-16bit-c-int.rs index 9832088ab338..d44b80475e47 100644 --- a/tests/codegen/abi-main-signature-16bit-c-int.rs +++ b/tests/codegen/abi-main-signature-16bit-c-int.rs @@ -6,8 +6,6 @@ //@[avr] only-avr //@[msp] only-msp430 - -fn main() { -} +fn main() {} // CHECK: define i16 @main(i16, i8**) diff --git a/tests/codegen/abi-main-signature-32bit-c-int.rs b/tests/codegen/abi-main-signature-32bit-c-int.rs index 7684024a2e38..ce475adde44c 100644 --- a/tests/codegen/abi-main-signature-32bit-c-int.rs +++ b/tests/codegen/abi-main-signature-32bit-c-int.rs @@ -6,7 +6,6 @@ //@ ignore-avr //@ ignore-wasi wasi codegens the main symbol differently -fn main() { -} +fn main() {} // CHECK: define{{( hidden| noundef)*}} i32 @main(i32{{( %0)?}}, ptr{{( %1)?}}) diff --git a/tests/codegen/abi-repr-ext.rs b/tests/codegen/abi-repr-ext.rs index 2e100a372355..a42f73566961 100644 --- a/tests/codegen/abi-repr-ext.rs +++ b/tests/codegen/abi-repr-ext.rs @@ -24,14 +24,17 @@ #![no_std] #![no_core] -#[lang="sized"] trait Sized { } -#[lang="freeze"] trait Freeze { } -#[lang="copy"] trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} #[repr(i8)] pub enum Type { Type1 = 0, - Type2 = 1 + Type2 = 1, } // To accommodate rust#97800, one might consider writing the below as: @@ -50,7 +53,6 @@ pub enum Type { // riscv-SAME: signext // CHECK-SAME: i8 @test() - #[no_mangle] pub extern "C" fn test() -> Type { Type::Type1 diff --git a/tests/codegen/abi-x86_64_sysv.rs b/tests/codegen/abi-x86_64_sysv.rs index 659c1d93e208..09909f994d65 100644 --- a/tests/codegen/abi-x86_64_sysv.rs +++ b/tests/codegen/abi-x86_64_sysv.rs @@ -5,25 +5,25 @@ #![crate_type = "lib"] pub struct S24 { - a: i8, - b: i8, - c: i8, + a: i8, + b: i8, + c: i8, } pub struct S48 { - a: i16, - b: i16, - c: i8, + a: i16, + b: i16, + c: i8, } // CHECK: i24 @struct_24_bits(i24 #[no_mangle] pub extern "sysv64" fn struct_24_bits(a: S24) -> S24 { - a + a } // CHECK: i48 @struct_48_bits(i48 #[no_mangle] pub extern "sysv64" fn struct_48_bits(a: S48) -> S48 { - a + a } diff --git a/tests/codegen/adjustments.rs b/tests/codegen/adjustments.rs index 549a9737eb15..7f7831def083 100644 --- a/tests/codegen/adjustments.rs +++ b/tests/codegen/adjustments.rs @@ -5,17 +5,16 @@ // Hack to get the correct size for the length part in slices // CHECK: @helper([[USIZE:i[0-9]+]] %_1) #[no_mangle] -pub fn helper(_: usize) { -} +pub fn helper(_: usize) {} // CHECK-LABEL: @no_op_slice_adjustment #[no_mangle] pub fn no_op_slice_adjustment(x: &[u8]) -> &[u8] { // We used to generate an extra alloca and memcpy for the block's trailing expression value, so // check that we copy directly to the return value slot -// CHECK: %0 = insertvalue { ptr, [[USIZE]] } poison, ptr %x.0, 0 -// CHECK: %1 = insertvalue { ptr, [[USIZE]] } %0, [[USIZE]] %x.1, 1 -// CHECK: ret { ptr, [[USIZE]] } %1 + // CHECK: %0 = insertvalue { ptr, [[USIZE]] } poison, ptr %x.0, 0 + // CHECK: %1 = insertvalue { ptr, [[USIZE]] } %0, [[USIZE]] %x.1, 1 + // CHECK: ret { ptr, [[USIZE]] } %1 { x } } @@ -24,6 +23,6 @@ pub fn no_op_slice_adjustment(x: &[u8]) -> &[u8] { pub fn no_op_slice_adjustment2(x: &[u8]) -> &[u8] { // We used to generate an extra alloca and memcpy for the function's return value, so check // that there's no memcpy (the slice is written to sret_slot element-wise) -// CHECK-NOT: call void @llvm.memcpy. + // CHECK-NOT: call void @llvm.memcpy. no_op_slice_adjustment(x) } diff --git a/tests/codegen/align-byval.rs b/tests/codegen/align-byval.rs index 3a2be2b2b9c3..223696229cb1 100644 --- a/tests/codegen/align-byval.rs +++ b/tests/codegen/align-byval.rs @@ -23,9 +23,12 @@ #![no_std] #![no_core] -#[lang="sized"] trait Sized { } -#[lang="freeze"] trait Freeze { } -#[lang="copy"] trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} impl Copy for i32 {} impl Copy for i64 {} @@ -58,7 +61,7 @@ pub struct ForceAlign4 { pub struct NaturalAlign8 { a: i64, b: i64, - c: i64 + c: i64, } // On i686-windows, this is passed by reference (because alignment is >4 and requested/forced), @@ -68,7 +71,7 @@ pub struct NaturalAlign8 { pub struct ForceAlign8 { a: i64, b: i64, - c: i64 + c: i64, } // On i686-windows, this is passed on stack, because requested alignment is <=4. @@ -77,28 +80,28 @@ pub struct ForceAlign8 { pub struct LowerFA8 { a: i64, b: i64, - c: i64 + c: i64, } // On i686-windows, this is passed by reference, because it contains a field with // requested/forced alignment. #[repr(C)] pub struct WrappedFA8 { - a: ForceAlign8 + a: ForceAlign8, } // On i686-windows, this has the same ABI as ForceAlign8, i.e. passed by reference. #[repr(transparent)] pub struct TransparentFA8 { _0: (), - a: ForceAlign8 + a: ForceAlign8, } #[repr(C)] #[repr(align(16))] pub struct ForceAlign16 { a: [i32; 16], - b: i8 + b: i8, } // CHECK-LABEL: @call_na1 diff --git a/tests/codegen/align-enum.rs b/tests/codegen/align-enum.rs index 93d5a87fb309..e8dd95d3afb0 100644 --- a/tests/codegen/align-enum.rs +++ b/tests/codegen/align-enum.rs @@ -18,8 +18,8 @@ pub struct Nested64 { // CHECK-LABEL: @align64 #[no_mangle] pub fn align64(a: u32) -> Align64 { -// CHECK: %a64 = alloca [64 x i8], align 64 -// CHECK: call void @llvm.memcpy.{{.*}}(ptr align 64 %{{.*}}, ptr align 64 %{{.*}}, i{{[0-9]+}} 64, i1 false) + // CHECK: %a64 = alloca [64 x i8], align 64 + // CHECK: call void @llvm.memcpy.{{.*}}(ptr align 64 %{{.*}}, ptr align 64 %{{.*}}, i{{[0-9]+}} 64, i1 false) let a64 = Align64::A(a); a64 } @@ -27,7 +27,7 @@ pub fn align64(a: u32) -> Align64 { // CHECK-LABEL: @nested64 #[no_mangle] pub fn nested64(a: u8, b: u32, c: u16) -> Nested64 { -// CHECK: %n64 = alloca [128 x i8], align 64 + // CHECK: %n64 = alloca [128 x i8], align 64 let n64 = Nested64 { a, b: Align64::B(b), c }; n64 } diff --git a/tests/codegen/align-offset.rs b/tests/codegen/align-offset.rs index 15b11f413cb5..aeac230f718f 100644 --- a/tests/codegen/align-offset.rs +++ b/tests/codegen/align-offset.rs @@ -53,7 +53,6 @@ pub fn align_offset_word_slice(slice: &[Align4]) -> usize { slice.as_ptr().align_offset(32) } - // CHECK-LABEL: @align_offset_word_ptr(ptr{{.+}}%ptr #[no_mangle] pub fn align_offset_word_ptr(ptr: *const Align4) -> usize { diff --git a/tests/codegen/align-struct.rs b/tests/codegen/align-struct.rs index e70b42b47db8..cc65b08a9223 100644 --- a/tests/codegen/align-struct.rs +++ b/tests/codegen/align-struct.rs @@ -25,9 +25,9 @@ pub enum Enum64 { // CHECK-LABEL: @align64 #[no_mangle] -pub fn align64(i : i32) -> Align64 { -// CHECK: %a64 = alloca [64 x i8], align 64 -// CHECK: call void @llvm.memcpy.{{.*}}(ptr align 64 %{{.*}}, ptr align 64 %{{.*}}, i{{[0-9]+}} 64, i1 false) +pub fn align64(i: i32) -> Align64 { + // CHECK: %a64 = alloca [64 x i8], align 64 + // CHECK: call void @llvm.memcpy.{{.*}}(ptr align 64 %{{.*}}, ptr align 64 %{{.*}}, i{{[0-9]+}} 64, i1 false) let a64 = Align64(i); a64 } @@ -37,14 +37,14 @@ pub fn align64(i : i32) -> Align64 { // CHECK-LABEL: @align64_load #[no_mangle] pub fn align64_load(a: Align64) -> i32 { -// CHECK: {{%.*}} = load i32, ptr {{%.*}}, align 64 + // CHECK: {{%.*}} = load i32, ptr {{%.*}}, align 64 a.0 } // CHECK-LABEL: @nested64 #[no_mangle] pub fn nested64(a: Align64, b: i32, c: i32, d: i8) -> Nested64 { -// CHECK: %n64 = alloca [128 x i8], align 64 + // CHECK: %n64 = alloca [128 x i8], align 64 let n64 = Nested64 { a, b, c, d }; n64 } @@ -52,7 +52,7 @@ pub fn nested64(a: Align64, b: i32, c: i32, d: i8) -> Nested64 { // CHECK-LABEL: @enum4 #[no_mangle] pub fn enum4(a: i32) -> Enum4 { -// CHECK: %e4 = alloca [8 x i8], align 4 + // CHECK: %e4 = alloca [8 x i8], align 4 let e4 = Enum4::A(a); e4 } @@ -60,7 +60,7 @@ pub fn enum4(a: i32) -> Enum4 { // CHECK-LABEL: @enum64 #[no_mangle] pub fn enum64(a: Align64) -> Enum64 { -// CHECK: %e64 = alloca [128 x i8], align 64 + // CHECK: %e64 = alloca [128 x i8], align 64 let e64 = Enum64::A(a); e64 } diff --git a/tests/codegen/array-cmp.rs b/tests/codegen/array-cmp.rs new file mode 100644 index 000000000000..2565a385b61b --- /dev/null +++ b/tests/codegen/array-cmp.rs @@ -0,0 +1,19 @@ +// Ensure the asm for array comparisons is properly optimized. + +//@ compile-flags: -C opt-level=2 + +#![crate_type = "lib"] + +// CHECK-LABEL: @compare +// CHECK: start: +// CHECK-NEXT: ret i1 true +#[no_mangle] +pub fn compare() -> bool { + let bytes = 12.5f32.to_ne_bytes(); + bytes + == if cfg!(target_endian = "big") { + [0x41, 0x48, 0x00, 0x00] + } else { + [0x00, 0x00, 0x48, 0x41] + } +} diff --git a/tests/codegen/array-repeat.rs b/tests/codegen/array-repeat.rs new file mode 100644 index 000000000000..b6f3b2e83d3e --- /dev/null +++ b/tests/codegen/array-repeat.rs @@ -0,0 +1,15 @@ +//@ compile-flags: -O + +#![crate_type = "lib"] +#![feature(array_repeat)] + +use std::array::repeat; + +// CHECK-LABEL: @byte_repeat +#[no_mangle] +fn byte_repeat(b: u8) -> [u8; 1024] { + // CHECK-NOT: alloca + // CHECK-NOT: store + // CHECK: memset + repeat(b) +} diff --git a/tests/codegen/asm-maybe-uninit.rs b/tests/codegen/asm-maybe-uninit.rs index f9bf280b3846..55813c35a468 100644 --- a/tests/codegen/asm-maybe-uninit.rs +++ b/tests/codegen/asm-maybe-uninit.rs @@ -4,8 +4,8 @@ #![crate_type = "rlib"] #![allow(asm_sub_register)] -use std::mem::MaybeUninit; use std::arch::asm; +use std::mem::MaybeUninit; // CHECK-LABEL: @int #[no_mangle] diff --git a/tests/codegen/asm-sanitize-llvm.rs b/tests/codegen/asm-sanitize-llvm.rs index 8638ed2236ac..fb332f9a0f3e 100644 --- a/tests/codegen/asm-sanitize-llvm.rs +++ b/tests/codegen/asm-sanitize-llvm.rs @@ -21,14 +21,10 @@ trait Copy {} pub unsafe fn we_escape_dollar_signs() { // CHECK: call void asm sideeffect alignstack inteldialect "banana$$:" - asm!( - r"banana$:", - ) + asm!(r"banana$:",) } pub unsafe fn we_escape_escapes_too() { // CHECK: call void asm sideeffect alignstack inteldialect "banana\{{(\\|5C)}}36:" - asm!( - r"banana\36:", - ) + asm!(r"banana\36:",) } diff --git a/tests/codegen/atomicptr.rs b/tests/codegen/atomicptr.rs index cbbd56155123..ea8b382c8fcc 100644 --- a/tests/codegen/atomicptr.rs +++ b/tests/codegen/atomicptr.rs @@ -6,13 +6,12 @@ //@ compile-flags: -O -Cno-prepopulate-passes #![crate_type = "lib"] - #![feature(strict_provenance)] #![feature(strict_provenance_atomic_ptr)] +use std::ptr::without_provenance_mut; use std::sync::atomic::AtomicPtr; use std::sync::atomic::Ordering::Relaxed; -use std::ptr::without_provenance_mut; // Portability hack so that we can say [[USIZE]] instead of i64/i32/i16 for usize. // CHECK: @helper([[USIZE:i[0-9]+]] noundef %_1) diff --git a/tests/codegen/autovectorize-f32x4.rs b/tests/codegen/autovectorize-f32x4.rs index 90c9f3691045..254362842f91 100644 --- a/tests/codegen/autovectorize-f32x4.rs +++ b/tests/codegen/autovectorize-f32x4.rs @@ -5,25 +5,20 @@ // CHECK-LABEL: @auto_vectorize_direct #[no_mangle] pub fn auto_vectorize_direct(a: [f32; 4], b: [f32; 4]) -> [f32; 4] { -// CHECK: load <4 x float> -// CHECK: load <4 x float> -// CHECK: fadd <4 x float> -// CHECK: store <4 x float> - [ - a[0] + b[0], - a[1] + b[1], - a[2] + b[2], - a[3] + b[3], - ] + // CHECK: load <4 x float> + // CHECK: load <4 x float> + // CHECK: fadd <4 x float> + // CHECK: store <4 x float> + [a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]] } // CHECK-LABEL: @auto_vectorize_loop #[no_mangle] pub fn auto_vectorize_loop(a: [f32; 4], b: [f32; 4]) -> [f32; 4] { -// CHECK: load <4 x float> -// CHECK: load <4 x float> -// CHECK: fadd <4 x float> -// CHECK: store <4 x float> + // CHECK: load <4 x float> + // CHECK: load <4 x float> + // CHECK: fadd <4 x float> + // CHECK: store <4 x float> let mut c = [0.0; 4]; for i in 0..4 { c[i] = a[i] + b[i]; @@ -34,9 +29,9 @@ pub fn auto_vectorize_loop(a: [f32; 4], b: [f32; 4]) -> [f32; 4] { // CHECK-LABEL: @auto_vectorize_array_from_fn #[no_mangle] pub fn auto_vectorize_array_from_fn(a: [f32; 4], b: [f32; 4]) -> [f32; 4] { -// CHECK: load <4 x float> -// CHECK: load <4 x float> -// CHECK: fadd <4 x float> -// CHECK: store <4 x float> + // CHECK: load <4 x float> + // CHECK: load <4 x float> + // CHECK: fadd <4 x float> + // CHECK: store <4 x float> std::array::from_fn(|i| a[i] + b[i]) } diff --git a/tests/codegen/auxiliary/extern_decl.rs b/tests/codegen/auxiliary/extern_decl.rs index edc48351869a..d17e77b14448 100644 --- a/tests/codegen/auxiliary/extern_decl.rs +++ b/tests/codegen/auxiliary/extern_decl.rs @@ -5,7 +5,9 @@ #![crate_type = "lib"] #[no_mangle] -pub fn extern_fn() -> u8 { unsafe { extern_static } } +pub fn extern_fn() -> u8 { + unsafe { extern_static } +} #[no_mangle] pub static mut extern_static: u8 = 71; diff --git a/tests/codegen/auxiliary/nounwind.rs b/tests/codegen/auxiliary/nounwind.rs index 73c5aee33878..40f66442c6ed 100644 --- a/tests/codegen/auxiliary/nounwind.rs +++ b/tests/codegen/auxiliary/nounwind.rs @@ -1,3 +1,2 @@ #[no_mangle] -pub fn bar() { -} +pub fn bar() {} diff --git a/tests/codegen/avr/avr-func-addrspace.rs b/tests/codegen/avr/avr-func-addrspace.rs index fb53abecfdb2..2d9efb52c7cf 100644 --- a/tests/codegen/avr/avr-func-addrspace.rs +++ b/tests/codegen/avr/avr-func-addrspace.rs @@ -14,15 +14,18 @@ #![no_core] #[lang = "sized"] -pub trait Sized { } +pub trait Sized {} #[lang = "copy"] -pub trait Copy { } +pub trait Copy {} #[lang = "receiver"] -pub trait Receiver { } +pub trait Receiver {} #[lang = "tuple_trait"] -pub trait Tuple { } +pub trait Tuple {} -pub struct Result { _a: T, _b: E } +pub struct Result { + _a: T, + _b: E, +} impl Copy for usize {} impl Copy for &usize {} @@ -39,7 +42,7 @@ pub trait FnOnce { } #[lang = "fn_mut"] -pub trait FnMut : FnOnce { +pub trait FnMut: FnOnce { extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; } @@ -64,7 +67,7 @@ fn arbitrary_black_box(ptr: &usize, _: &mut u32) -> Result<(), ()> { #[inline(never)] #[no_mangle] -fn call_through_fn_trait(a: &mut impl Fn<(), Output=()>) { +fn call_through_fn_trait(a: &mut impl Fn<(), Output = ()>) { (*a)() } @@ -110,7 +113,10 @@ pub unsafe fn transmute_fn_ptr_to_data(x: fn()) -> *const () { transmute(x) } -pub enum Either { A(T), B(U) } +pub enum Either { + A(T), + B(U), +} // Previously, we would codegen this as passing/returning a scalar pair of `{ i8, ptr }`, // with the `ptr` field representing both `&i32` and `fn()` depending on the variant. diff --git a/tests/codegen/binary-search-index-no-bound-check.rs b/tests/codegen/binary-search-index-no-bound-check.rs index 96f6bb54b3fe..a213c015a40b 100644 --- a/tests/codegen/binary-search-index-no-bound-check.rs +++ b/tests/codegen/binary-search-index-no-bound-check.rs @@ -11,11 +11,7 @@ pub fn binary_search_index_no_bounds_check(s: &[u8]) -> u8 { // CHECK-NOT: slice_start_index_len_fail // CHECK-NOT: slice_end_index_len_fail // CHECK-NOT: panic_bounds_check - if let Ok(idx) = s.binary_search(&b'\\') { - s[idx] - } else { - 42 - } + if let Ok(idx) = s.binary_search(&b'\\') { s[idx] } else { 42 } } // Similarly, check that `partition_point` is known to return a valid fencepost. diff --git a/tests/codegen/bool-cmp.rs b/tests/codegen/bool-cmp.rs index 29ee3e0627bc..71d3411689f4 100644 --- a/tests/codegen/bool-cmp.rs +++ b/tests/codegen/bool-cmp.rs @@ -10,9 +10,9 @@ use std::cmp::Ordering; // CHECK-LABEL: @cmp_bool #[no_mangle] pub fn cmp_bool(a: bool, b: bool) -> Ordering { -// LLVM 10 produces (zext a) + (sext b), but the final lowering is (zext a) - (zext b). -// CHECK: zext i1 -// CHECK: {{z|s}}ext i1 -// CHECK: {{sub|add}} nsw + // LLVM 10 produces (zext a) + (sext b), but the final lowering is (zext a) - (zext b). + // CHECK: zext i1 + // CHECK: {{z|s}}ext i1 + // CHECK: {{sub|add}} nsw a.cmp(&b) } diff --git a/tests/codegen/branch-protection.rs b/tests/codegen/branch-protection.rs index 0961b1b9f522..a29ec67d578b 100644 --- a/tests/codegen/branch-protection.rs +++ b/tests/codegen/branch-protection.rs @@ -12,12 +12,11 @@ #![feature(no_core, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } +#[lang = "sized"] +trait Sized {} // A basic test function. -pub fn test() { -} +pub fn test() {} // BTI: !"branch-target-enforcement", i32 1 // BTI: !"sign-return-address", i32 0 diff --git a/tests/codegen/cast-target-abi.rs b/tests/codegen/cast-target-abi.rs index 9c31acc9bb7c..c6a8b7bbf378 100644 --- a/tests/codegen/cast-target-abi.rs +++ b/tests/codegen/cast-target-abi.rs @@ -18,9 +18,12 @@ #![no_std] #![no_core] -#[lang="sized"] trait Sized { } -#[lang="freeze"] trait Freeze { } -#[lang="copy"] trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} // This struct will be passed as a single `i64` or `i32`. // This may be (if `i64)) larger than the Rust layout, which is just `{ i16, i16 }`. @@ -104,7 +107,6 @@ pub unsafe fn return_twou16s() -> TwoU16s { // powerpc64: [[RETVAL:%.+]] = alloca [4 x i8], align 2 // powerpc64: call void @returns_twou16s(ptr {{.+}} [[RETVAL]]) - // The other targets copy the cast ABI type to an alloca. // aarch64: [[ABI_ALLOCA:%.+]] = alloca [8 x i8], align [[ABI_ALIGN:8]] @@ -151,7 +153,6 @@ pub unsafe fn return_fiveu16s() -> FiveU16s { // powerpc64: call void @returns_fiveu16s(ptr {{.+}} [[RET_PTR]]) - // The other targets copy the cast ABI type to the sret pointer. // aarch64: [[ABI_ALLOCA:%.+]] = alloca [16 x i8], align [[ABI_ALIGN:8]] @@ -199,7 +200,6 @@ pub unsafe fn return_doubledouble() -> DoubleDouble { // powerpc64: [[RETVAL:%.+]] = alloca [16 x i8], align 8 // powerpc64: call void @returns_doubledouble(ptr {{.+}} [[RETVAL]]) - // The other targets copy the cast ABI type to an alloca. // aarch64: [[ABI_ALLOCA:%.+]] = alloca [16 x i8], align [[ABI_ALIGN:8]] @@ -266,7 +266,6 @@ pub unsafe fn return_doublefloat() -> DoubleFloat { // powerpc64: [[RETVAL:%.+]] = alloca [16 x i8], align 8 // powerpc64: call void @returns_doublefloat(ptr {{.+}} [[RETVAL]]) - // The other targets copy the cast ABI type to an alloca. // aarch64: [[ABI_ALLOCA:%.+]] = alloca [16 x i8], align [[ABI_ALIGN:8]] diff --git a/tests/codegen/cf-protection.rs b/tests/codegen/cf-protection.rs index 5120bbf114d9..244d1eb25442 100644 --- a/tests/codegen/cf-protection.rs +++ b/tests/codegen/cf-protection.rs @@ -13,12 +13,11 @@ #![feature(no_core, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } +#[lang = "sized"] +trait Sized {} // A basic test function. -pub fn test() { -} +pub fn test() {} // undefined-NOT: !"cf-protection-branch" // undefined-NOT: !"cf-protection-return" diff --git a/tests/codegen/cffi/ffi-const.rs b/tests/codegen/cffi/ffi-const.rs index 8044ad105d5d..564b8f7f8d8d 100644 --- a/tests/codegen/cffi/ffi-const.rs +++ b/tests/codegen/cffi/ffi-const.rs @@ -2,12 +2,15 @@ #![crate_type = "lib"] #![feature(ffi_const)] -pub fn bar() { unsafe { foo() } } +pub fn bar() { + unsafe { foo() } +} extern "C" { // CHECK-LABEL: declare{{.*}}void @foo() // CHECK-SAME: [[ATTRS:#[0-9]+]] // The attribute changed from `readnone` to `memory(none)` with LLVM 16.0. // CHECK-DAG: attributes [[ATTRS]] = { {{.*}}{{readnone|memory\(none\)}}{{.*}} } - #[ffi_const] pub fn foo(); + #[ffi_const] + pub fn foo(); } diff --git a/tests/codegen/cffi/ffi-out-of-bounds-loads.rs b/tests/codegen/cffi/ffi-out-of-bounds-loads.rs index 35bf00f8f3c3..614d5d94f620 100644 --- a/tests/codegen/cffi/ffi-out-of-bounds-loads.rs +++ b/tests/codegen/cffi/ffi-out-of-bounds-loads.rs @@ -13,9 +13,12 @@ #![no_std] #![no_core] -#[lang="sized"] trait Sized { } -#[lang="freeze"] trait Freeze { } -#[lang="copy"] trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} #[repr(C)] struct S { diff --git a/tests/codegen/cffi/ffi-pure.rs b/tests/codegen/cffi/ffi-pure.rs index 51135fd37535..601509d5c90f 100644 --- a/tests/codegen/cffi/ffi-pure.rs +++ b/tests/codegen/cffi/ffi-pure.rs @@ -2,12 +2,15 @@ #![crate_type = "lib"] #![feature(ffi_pure)] -pub fn bar() { unsafe { foo() } } +pub fn bar() { + unsafe { foo() } +} extern "C" { // CHECK-LABEL: declare{{.*}}void @foo() // CHECK-SAME: [[ATTRS:#[0-9]+]] // The attribute changed from `readonly` to `memory(read)` with LLVM 16.0. // CHECK-DAG: attributes [[ATTRS]] = { {{.*}}{{readonly|memory\(read\)}}{{.*}} } - #[ffi_pure] pub fn foo(); + #[ffi_pure] + pub fn foo(); } diff --git a/tests/codegen/cfguard-checks.rs b/tests/codegen/cfguard-checks.rs index 2b09a5fe12c2..cdf6406ad61b 100644 --- a/tests/codegen/cfguard-checks.rs +++ b/tests/codegen/cfguard-checks.rs @@ -4,8 +4,7 @@ #![crate_type = "lib"] // A basic test function. -pub fn test() { -} +pub fn test() {} // Ensure the module flag cfguard=2 is present // CHECK: !"cfguard", i32 2 diff --git a/tests/codegen/cfguard-disabled.rs b/tests/codegen/cfguard-disabled.rs index 105e02072615..90915c0f0c6b 100644 --- a/tests/codegen/cfguard-disabled.rs +++ b/tests/codegen/cfguard-disabled.rs @@ -4,8 +4,7 @@ #![crate_type = "lib"] // A basic test function. -pub fn test() { -} +pub fn test() {} // Ensure the module flag cfguard is not present // CHECK-NOT: !"cfguard" diff --git a/tests/codegen/cfguard-nochecks.rs b/tests/codegen/cfguard-nochecks.rs index 0443880d72da..5f386533ec1a 100644 --- a/tests/codegen/cfguard-nochecks.rs +++ b/tests/codegen/cfguard-nochecks.rs @@ -4,8 +4,7 @@ #![crate_type = "lib"] // A basic test function. -pub fn test() { -} +pub fn test() {} // Ensure the module flag cfguard=1 is present // CHECK: !"cfguard", i32 1 diff --git a/tests/codegen/cfguard-non-msvc.rs b/tests/codegen/cfguard-non-msvc.rs index 5d266de8a94d..1e6559aaf5d2 100644 --- a/tests/codegen/cfguard-non-msvc.rs +++ b/tests/codegen/cfguard-non-msvc.rs @@ -4,8 +4,7 @@ #![crate_type = "lib"] // A basic test function. -pub fn test() { -} +pub fn test() {} // Ensure the cfguard module flag is not added for non-MSVC targets. // CHECK-NOT: !"cfguard" diff --git a/tests/codegen/checked_ilog.rs b/tests/codegen/checked_ilog.rs new file mode 100644 index 000000000000..8f3c07119fee --- /dev/null +++ b/tests/codegen/checked_ilog.rs @@ -0,0 +1,20 @@ +//@ compile-flags: -O + +#![crate_type = "lib"] + +// Ensure that when val < base, we do not divide or multiply. + +// CHECK-LABEL: @checked_ilog +// CHECK-SAME: (i16 noundef %val, i16 noundef %base) +#[no_mangle] +pub fn checked_ilog(val: u16, base: u16) -> Option { + // CHECK-NOT: udiv + // CHECK-NOT: mul + // CHECK: %[[IS_LESS:.+]] = icmp ult i16 %val, %base + // CHECK-NEXT: br i1 %[[IS_LESS]], label %[[TRUE:.+]], label %[[FALSE:.+]] + // CHECK: [[TRUE]]: + // CHECK-NOT: udiv + // CHECK-NOT: mul + // CHECK: ret { i32, i32 } + val.checked_ilog(base) +} diff --git a/tests/codegen/coercions.rs b/tests/codegen/coercions.rs index a205e541df10..63c1742c6399 100644 --- a/tests/codegen/coercions.rs +++ b/tests/codegen/coercions.rs @@ -7,7 +7,7 @@ static X: i32 = 5; // CHECK-LABEL: @raw_ptr_to_raw_ptr_noop // CHECK-NOT: alloca #[no_mangle] -pub fn raw_ptr_to_raw_ptr_noop() -> *const i32{ +pub fn raw_ptr_to_raw_ptr_noop() -> *const i32 { &X as *const i32 } diff --git a/tests/codegen/constant-branch.rs b/tests/codegen/constant-branch.rs index 3328b1eb4a88..a2710cc4b258 100644 --- a/tests/codegen/constant-branch.rs +++ b/tests/codegen/constant-branch.rs @@ -8,18 +8,10 @@ #[no_mangle] pub fn if_bool() { // CHECK: br label %{{.+}} - _ = if true { - 0 - } else { - 1 - }; + _ = if true { 0 } else { 1 }; // CHECK: br label %{{.+}} - _ = if false { - 0 - } else { - 1 - }; + _ = if false { 0 } else { 1 }; } // CHECK-LABEL: @if_constant_int_eq @@ -27,18 +19,10 @@ pub fn if_bool() { pub fn if_constant_int_eq() { let val = 0; // CHECK: br label %{{.+}} - _ = if val == 0 { - 0 - } else { - 1 - }; + _ = if val == 0 { 0 } else { 1 }; // CHECK: br label %{{.+}} - _ = if val == 1 { - 0 - } else { - 1 - }; + _ = if val == 1 { 0 } else { 1 }; } // CHECK-LABEL: @if_constant_match @@ -48,19 +32,19 @@ pub fn if_constant_match() { _ = match 1 { 1 => 2, 2 => 3, - _ => 4 + _ => 4, }; // CHECK: br label %{{.+}} _ = match 1 { 2 => 3, - _ => 4 + _ => 4, }; // CHECK: br label %[[MINUS1:.+]] _ = match -1 { - // CHECK: [[MINUS1]]: - // CHECK: store i32 1 + // CHECK: [[MINUS1]]: + // CHECK: store i32 1 -1 => 1, _ => 0, } diff --git a/tests/codegen/consts.rs b/tests/codegen/consts.rs index 93c58c37c287..42ce7679d1ac 100644 --- a/tests/codegen/consts.rs +++ b/tests/codegen/consts.rs @@ -9,11 +9,11 @@ // CHECK: @STATIC = {{.*}}, align 4 // This checks the constants from inline_enum_const -// CHECK: @alloc_af1f8e8e6f4b341431a1d405e652df2d = {{.*}}, align 2 +// CHECK: @alloc_[[INLINE_ENUM_HASH:[a-f0-9]{32}]] = {{.*}}, align 2 // This checks the constants from {low,high}_align_const, they share the same // constant, but the alignment differs, so the higher one should be used -// CHECK: [[LOW_HIGH:@alloc_[a-f0-9]+]] = {{.*}}, align 4 +// CHECK: [[LOW_HIGH:@alloc_[a-f0-9]{32}]] = {{.*}}, align 4 #[derive(Copy, Clone)] // repr(i16) is required for the {low,high}_align_const test diff --git a/tests/codegen/coroutine-debug-msvc.rs b/tests/codegen/coroutine-debug-msvc.rs index e2296db1d594..9e2ec3ea28ac 100644 --- a/tests/codegen/coroutine-debug-msvc.rs +++ b/tests/codegen/coroutine-debug-msvc.rs @@ -11,7 +11,8 @@ use std::ops::Coroutine; fn coroutine_test() -> impl Coroutine { - #[coroutine] || { + #[coroutine] + || { yield 0; let s = String::from("foo"); yield 1; @@ -23,23 +24,23 @@ fn coroutine_test() -> impl Coroutine { // CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$" // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]], // For brevity, we only check the struct name and members of the last variant. -// CHECK-SAME: file: [[FILE:![0-9]*]], line: 14, +// CHECK-SAME: file: [[FILE:![0-9]*]], line: 15, // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant1", scope: [[GEN]], -// CHECK-SAME: file: [[FILE]], line: 18, +// CHECK-SAME: file: [[FILE]], line: 19, // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant2", scope: [[GEN]], -// CHECK-SAME: file: [[FILE]], line: 18, +// CHECK-SAME: file: [[FILE]], line: 19, // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant3", scope: [[GEN]], -// CHECK-SAME: file: [[FILE]], line: 15, +// CHECK-SAME: file: [[FILE]], line: 16, // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant4", scope: [[GEN]], -// CHECK-SAME: file: [[FILE]], line: 17, +// CHECK-SAME: file: [[FILE]], line: 18, // CHECK-SAME: baseType: [[VARIANT_WRAPPER:![0-9]*]] // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) diff --git a/tests/codegen/coroutine-debug.rs b/tests/codegen/coroutine-debug.rs index 914515f58b85..d00667a37d5e 100644 --- a/tests/codegen/coroutine-debug.rs +++ b/tests/codegen/coroutine-debug.rs @@ -11,7 +11,8 @@ use std::ops::Coroutine; fn coroutine_test() -> impl Coroutine { - #[coroutine] || { + #[coroutine] + || { yield 0; let s = String::from("foo"); yield 1; @@ -26,26 +27,26 @@ fn coroutine_test() -> impl Coroutine { // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: discriminator: [[DISC:![0-9]*]] // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "0", scope: [[VARIANT]], -// CHECK-SAME: file: [[FILE:![0-9]*]], line: 14, +// CHECK-SAME: file: [[FILE:![0-9]*]], line: 15, // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) // CHECK: {{!.*}} = !DICompositeType(tag: DW_TAG_structure_type, name: "Unresumed", scope: [[GEN]], // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "1", scope: [[VARIANT]], -// CHECK-SAME: file: [[FILE]], line: 18, +// CHECK-SAME: file: [[FILE]], line: 19, // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "2", scope: [[VARIANT]], -// CHECK-SAME: file: [[FILE]], line: 18, +// CHECK-SAME: file: [[FILE]], line: 19, // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "3", scope: [[VARIANT]], -// CHECK-SAME: file: [[FILE]], line: 15, +// CHECK-SAME: file: [[FILE]], line: 16, // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "4", scope: [[VARIANT]], -// CHECK-SAME: file: [[FILE]], line: 17, +// CHECK-SAME: file: [[FILE]], line: 18, // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) // CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]], diff --git a/tests/codegen/dealloc-no-unwind.rs b/tests/codegen/dealloc-no-unwind.rs index 667f6fea1857..ead26da610e2 100644 --- a/tests/codegen/dealloc-no-unwind.rs +++ b/tests/codegen/dealloc-no-unwind.rs @@ -1,13 +1,17 @@ //@ compile-flags: -O -#![crate_type="lib"] +#![crate_type = "lib"] struct A; impl Drop for A { fn drop(&mut self) { - extern "C" { fn foo(); } - unsafe { foo(); } + extern "C" { + fn foo(); + } + unsafe { + foo(); + } } } diff --git a/tests/codegen/debug-column.rs b/tests/codegen/debug-column.rs index ff25fbe1b132..d14a5c29142e 100644 --- a/tests/codegen/debug-column.rs +++ b/tests/codegen/debug-column.rs @@ -3,6 +3,7 @@ //@ ignore-windows //@ compile-flags: -C debuginfo=2 +#[rustfmt::skip] fn main() { unsafe { // Column numbers are 1-based. Regression test for #65437. @@ -13,8 +14,8 @@ fn main() { // CHECK: call void @turtle(){{( #[0-9]+)?}}, !dbg [[B:!.*]] /* ż */ turtle(); - // CHECK: [[A]] = !DILocation(line: 10, column: 9, - // CHECK: [[B]] = !DILocation(line: 14, column: 10, + // CHECK: [[A]] = !DILocation(line: 11, column: 9, + // CHECK: [[B]] = !DILocation(line: 15, column: 10, } } diff --git a/tests/codegen/debug-compile-unit-path.rs b/tests/codegen/debug-compile-unit-path.rs index 4be418d66103..6131d9d7351a 100644 --- a/tests/codegen/debug-compile-unit-path.rs +++ b/tests/codegen/debug-compile-unit-path.rs @@ -3,7 +3,7 @@ // // Ensure that we remap the compile unit directory and that we set it to the compilers current // working directory and not something else. -#![crate_type="rlib"] +#![crate_type = "rlib"] // CHECK-DAG: [[FILE:![0-9]*]] = !DIFile(filename: "/base/debug-compile-unit-path.rs{{.*}}", directory: "/cwd/") // CHECK-DAG: {{![0-9]*}} = distinct !DICompileUnit({{.*}}file: [[FILE]] diff --git a/tests/codegen/debuginfo-generic-closure-env-names.rs b/tests/codegen/debuginfo-generic-closure-env-names.rs index 04ff7fff4399..6d56fbc40abe 100644 --- a/tests/codegen/debuginfo-generic-closure-env-names.rs +++ b/tests/codegen/debuginfo-generic-closure-env-names.rs @@ -46,7 +46,6 @@ // NONMSVC-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}", scope: ![[function_containing_closure_NAMESPACE]] // MSVC-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0", scope: ![[function_containing_closure_NAMESPACE]] - #![crate_type = "lib"] use std::future::Future; @@ -70,11 +69,9 @@ async fn generic_async_function(x: T) -> T { x } -fn generic_async_block(x: T) -> impl Future { +fn generic_async_block(x: T) -> impl Future { static _X: u8 = 0; // Same as above - async move { - x - } + async move { x } } pub fn instantiate_generics() { diff --git a/tests/codegen/dllimports/auxiliary/wrapper.rs b/tests/codegen/dllimports/auxiliary/wrapper.rs index 7d1f6ab70d5f..00a29f7ee7e5 100644 --- a/tests/codegen/dllimports/auxiliary/wrapper.rs +++ b/tests/codegen/dllimports/auxiliary/wrapper.rs @@ -1,13 +1,13 @@ //@ no-prefer-dynamic #![crate_type = "rlib"] -#[link(name = "dummy", kind="dylib")] +#[link(name = "dummy", kind = "dylib")] extern "C" { pub fn dylib_func2(x: i32) -> i32; pub static dylib_global2: i32; } -#[link(name = "dummy", kind="static")] +#[link(name = "dummy", kind = "static")] extern "C" { pub fn static_func2(x: i32) -> i32; pub static static_global2: i32; diff --git a/tests/codegen/dllimports/main.rs b/tests/codegen/dllimports/main.rs index c1626853b16d..93d350a22380 100644 --- a/tests/codegen/dllimports/main.rs +++ b/tests/codegen/dllimports/main.rs @@ -1,4 +1,4 @@ - // This test is for *-windows-msvc only. +// This test is for *-windows-msvc only. //@ only-windows //@ ignore-gnu @@ -20,13 +20,13 @@ extern crate wrapper; // CHECK: declare noundef i32 @static_func1(i32 noundef) // CHECK: declare noundef i32 @static_func2(i32 noundef) -#[link(name = "dummy", kind="dylib")] +#[link(name = "dummy", kind = "dylib")] extern "C" { pub fn dylib_func1(x: i32) -> i32; pub static dylib_global1: i32; } -#[link(name = "dummy", kind="static")] +#[link(name = "dummy", kind = "static")] extern "C" { pub fn static_func1(x: i32) -> i32; pub static static_global1: i32; diff --git a/tests/codegen/dont_codegen_private_const_fn_only_used_in_const_eval.rs b/tests/codegen/dont_codegen_private_const_fn_only_used_in_const_eval.rs index eeeaebe52dd1..df50b4af8098 100644 --- a/tests/codegen/dont_codegen_private_const_fn_only_used_in_const_eval.rs +++ b/tests/codegen/dont_codegen_private_const_fn_only_used_in_const_eval.rs @@ -3,8 +3,25 @@ //@compile-flags: --crate-type=lib -Copt-level=0 +#![feature(generic_const_items)] + const fn foo() {} pub static FOO: () = foo(); // CHECK-NOT: define{{.*}}foo{{.*}} + +const fn bar() {} + +pub const BAR: () = bar(); + +// CHECK-NOT: define{{.*}}bar{{.*}} + +const fn baz() {} + +#[rustfmt::skip] +pub const BAZ: () = if C { + baz() +}; + +// CHECK: define{{.*}}baz{{.*}} diff --git a/tests/codegen/drop-in-place-noalias.rs b/tests/codegen/drop-in-place-noalias.rs index 36532ea8f532..2dc769df1c92 100644 --- a/tests/codegen/drop-in-place-noalias.rs +++ b/tests/codegen/drop-in-place-noalias.rs @@ -3,7 +3,7 @@ // Tests that the compiler can apply `noalias` and other &mut attributes to `drop_in_place`. // Note that non-Unpin types should not get `noalias`, matching &mut behavior. -#![crate_type="lib"] +#![crate_type = "lib"] use std::marker::PhantomPinned; diff --git a/tests/codegen/drop.rs b/tests/codegen/drop.rs index 1e80247ba8aa..b22a8ef27d23 100644 --- a/tests/codegen/drop.rs +++ b/tests/codegen/drop.rs @@ -7,28 +7,26 @@ struct SomeUniqueName; impl Drop for SomeUniqueName { #[inline(never)] - fn drop(&mut self) { - } + fn drop(&mut self) {} } #[inline(never)] -pub fn possibly_unwinding() { -} +pub fn possibly_unwinding() {} // CHECK-LABEL: @droppy #[no_mangle] pub fn droppy() { -// Check that there are exactly 6 drop calls. The cleanups for the unwinding should be reused, so -// that's one new drop call per call to possibly_unwinding(), and finally 3 drop calls for the -// regular function exit. We used to have problems with quadratic growths of drop calls in such -// functions. -// FIXME(eddyb) the `void @` forces a match on the instruction, instead of the -// comment, that's `; call core::ptr::drop_in_place::` -// for the `v0` mangling, should switch to matching on that once `legacy` is gone. -// CHECK-COUNT-6: {{(call|invoke) void @.*}}drop_in_place{{.*}}SomeUniqueName -// CHECK-NOT: {{(call|invoke) void @.*}}drop_in_place{{.*}}SomeUniqueName -// The next line checks for the } that ends the function definition -// CHECK-LABEL: {{^[}]}} + // Check that there are exactly 6 drop calls. The cleanups for the unwinding should be reused, + // so that's one new drop call per call to possibly_unwinding(), and finally 3 drop calls for + // the regular function exit. We used to have problems with quadratic growths of drop calls in + // such functions. + // FIXME(eddyb) the `void @` forces a match on the instruction, instead of the + // comment, that's `; call core::ptr::drop_in_place::` + // for the `v0` mangling, should switch to matching on that once `legacy` is gone. + // CHECK-COUNT-6: {{(call|invoke) void @.*}}drop_in_place{{.*}}SomeUniqueName + // CHECK-NOT: {{(call|invoke) void @.*}}drop_in_place{{.*}}SomeUniqueName + // The next line checks for the } that ends the function definition + // CHECK-LABEL: {{^[}]}} let _s = SomeUniqueName; possibly_unwinding(); let _s = SomeUniqueName; diff --git a/tests/codegen/dst-offset.rs b/tests/codegen/dst-offset.rs index ce735baeb6a9..7177a960432a 100644 --- a/tests/codegen/dst-offset.rs +++ b/tests/codegen/dst-offset.rs @@ -3,7 +3,6 @@ //@ compile-flags: -C no-prepopulate-passes -Copt-level=0 #![crate_type = "lib"] - #![feature(extern_types)] use std::ptr::addr_of; @@ -11,8 +10,7 @@ use std::ptr::addr_of; // Hack to get the correct type for usize // CHECK: @helper([[USIZE:i[0-9]+]] %_1) #[no_mangle] -pub fn helper(_: usize) { -} +pub fn helper(_: usize) {} struct Dst { x: u32, @@ -23,30 +21,31 @@ struct Dst { // CHECK: @dst_dyn_trait_offset(ptr align {{[0-9]+}} [[DATA_PTR:%.+]], ptr align {{[0-9]+}} [[VTABLE_PTR:%.+]]) #[no_mangle] pub fn dst_dyn_trait_offset(s: &Dst) -> &dyn Drop { -// The alignment of dyn trait is unknown, so we compute the offset based on align from the vtable. + // The alignment of dyn trait is unknown, so we compute the offset based on align from the + // vtable. -// CHECK: [[SIZE_PTR:%[0-9]+]] = getelementptr inbounds i8, ptr [[VTABLE_PTR]] -// CHECK: load [[USIZE]], ptr [[SIZE_PTR]] -// CHECK: [[ALIGN_PTR:%[0-9]+]] = getelementptr inbounds i8, ptr [[VTABLE_PTR]] -// CHECK: load [[USIZE]], ptr [[ALIGN_PTR]] + // CHECK: [[SIZE_PTR:%[0-9]+]] = getelementptr inbounds i8, ptr [[VTABLE_PTR]] + // CHECK: load [[USIZE]], ptr [[SIZE_PTR]] + // CHECK: [[ALIGN_PTR:%[0-9]+]] = getelementptr inbounds i8, ptr [[VTABLE_PTR]] + // CHECK: load [[USIZE]], ptr [[ALIGN_PTR]] -// CHECK: getelementptr inbounds i8, ptr [[DATA_PTR]] -// CHECK-NEXT: insertvalue -// CHECK-NEXT: insertvalue -// CHECK-NEXT: ret + // CHECK: getelementptr inbounds i8, ptr [[DATA_PTR]] + // CHECK-NEXT: insertvalue + // CHECK-NEXT: insertvalue + // CHECK-NEXT: ret &s.z } // CHECK-LABEL: @dst_slice_offset #[no_mangle] pub fn dst_slice_offset(s: &Dst<[u16]>) -> &[u16] { -// The alignment of [u16] is known, so we generate a GEP directly. + // The alignment of [u16] is known, so we generate a GEP directly. -// CHECK: start: -// CHECK-NEXT: getelementptr inbounds i8, {{.+}}, [[USIZE]] 6 -// CHECK-NEXT: insertvalue -// CHECK-NEXT: insertvalue -// CHECK-NEXT: ret + // CHECK: start: + // CHECK-NEXT: getelementptr inbounds i8, {{.+}}, [[USIZE]] 6 + // CHECK-NEXT: insertvalue + // CHECK-NEXT: insertvalue + // CHECK-NEXT: ret &s.z } @@ -60,25 +59,25 @@ struct PackedDstSlice { // CHECK-LABEL: @packed_dst_slice_offset #[no_mangle] pub fn packed_dst_slice_offset(s: &PackedDstSlice) -> *const [u16] { -// The alignment of [u16] is known, so we generate a GEP directly. + // The alignment of [u16] is known, so we generate a GEP directly. -// CHECK: start: -// CHECK-NEXT: getelementptr inbounds i8, {{.+}}, [[USIZE]] 5 -// CHECK-NEXT: insertvalue -// CHECK-NEXT: insertvalue -// CHECK-NEXT: ret + // CHECK: start: + // CHECK-NEXT: getelementptr inbounds i8, {{.+}}, [[USIZE]] 5 + // CHECK-NEXT: insertvalue + // CHECK-NEXT: insertvalue + // CHECK-NEXT: ret addr_of!(s.z) } -extern { +extern "C" { pub type Extern; } // CHECK-LABEL: @dst_extern #[no_mangle] pub fn dst_extern(s: &Dst) -> &Extern { -// Computing the alignment of an extern type is currently unsupported and just panics. + // Computing the alignment of an extern type is currently unsupported and just panics. -// CHECK: call void @{{.+}}panic + // CHECK: call void @{{.+}}panic &s.z } diff --git a/tests/codegen/dst-vtable-align-nonzero.rs b/tests/codegen/dst-vtable-align-nonzero.rs index b0507f4c217d..cb07e43238c1 100644 --- a/tests/codegen/dst-vtable-align-nonzero.rs +++ b/tests/codegen/dst-vtable-align-nonzero.rs @@ -10,9 +10,15 @@ pub trait Trait { fn f(&self); } -pub struct WrapperWithAlign1 { x: u8, y: T } +pub struct WrapperWithAlign1 { + x: u8, + y: T, +} -pub struct WrapperWithAlign2 { x: u16, y: T } +pub struct WrapperWithAlign2 { + x: u16, + y: T, +} pub struct Struct { _field: i8, @@ -22,7 +28,7 @@ pub struct Struct { // CHECK-LABEL: @eliminates_runtime_check_when_align_1 #[no_mangle] pub fn eliminates_runtime_check_when_align_1( - x: &Struct> + x: &Struct>, ) -> &WrapperWithAlign1 { // CHECK: load [[USIZE:i[0-9]+]], {{.+}} !range [[RANGE_META:![0-9]+]] // CHECK-NOT: llvm.umax @@ -35,7 +41,7 @@ pub fn eliminates_runtime_check_when_align_1( // CHECK-LABEL: @does_not_eliminate_runtime_check_when_align_2 #[no_mangle] pub fn does_not_eliminate_runtime_check_when_align_2( - x: &Struct> + x: &Struct>, ) -> &WrapperWithAlign2 { // CHECK: [[X0:%[0-9]+]] = load [[USIZE]], {{.+}} !range [[RANGE_META]] // CHECK: {{icmp|llvm.umax}} diff --git a/tests/codegen/ehcontguard_disabled.rs b/tests/codegen/ehcontguard_disabled.rs index dc4b5eb430bf..9efb2721b3e8 100644 --- a/tests/codegen/ehcontguard_disabled.rs +++ b/tests/codegen/ehcontguard_disabled.rs @@ -3,8 +3,7 @@ #![crate_type = "lib"] // A basic test function. -pub fn test() { -} +pub fn test() {} // Ensure the module flag ehcontguard is not present // CHECK-NOT: !"ehcontguard" diff --git a/tests/codegen/ehcontguard_enabled.rs b/tests/codegen/ehcontguard_enabled.rs index fde66f1c148e..ecc5512fd5de 100644 --- a/tests/codegen/ehcontguard_enabled.rs +++ b/tests/codegen/ehcontguard_enabled.rs @@ -3,8 +3,7 @@ #![crate_type = "lib"] // A basic test function. -pub fn test() { -} +pub fn test() {} // Ensure the module flag ehcontguard=1 is present // CHECK: !"ehcontguard", i32 1 diff --git a/tests/codegen/emcripten-catch-unwind.rs b/tests/codegen/emcripten-catch-unwind.rs index 7de7bd81b5c0..6cda8c6799f4 100644 --- a/tests/codegen/emcripten-catch-unwind.rs +++ b/tests/codegen/emcripten-catch-unwind.rs @@ -9,18 +9,23 @@ #![no_std] #![no_core] -#[lang="sized"] trait Sized { } -#[lang="freeze"] trait Freeze { } -#[lang="copy"] trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} #[rustc_intrinsic] -fn size_of() -> usize { loop {} } +fn size_of() -> usize { + loop {} +} extern "rust-intrinsic" { fn catch_unwind( try_fn: fn(_: *mut u8), data: *mut u8, - catch_fn: fn(_: *mut u8, _: *mut u8) + catch_fn: fn(_: *mut u8, _: *mut u8), ) -> i32; } @@ -36,7 +41,7 @@ pub fn ptr_size() -> usize { pub unsafe fn test_catch_unwind( try_fn: fn(_: *mut u8), data: *mut u8, - catch_fn: fn(_: *mut u8, _: *mut u8) + catch_fn: fn(_: *mut u8, _: *mut u8), ) -> i32 { // CHECK: start: // CHECK: [[ALLOCA:%.*]] = alloca diff --git a/tests/codegen/enable-lto-unit-splitting.rs b/tests/codegen/enable-lto-unit-splitting.rs index e46980944778..51c2671bc4eb 100644 --- a/tests/codegen/enable-lto-unit-splitting.rs +++ b/tests/codegen/enable-lto-unit-splitting.rs @@ -2,9 +2,8 @@ // //@ compile-flags: -Clto -Ctarget-feature=-crt-static -Zsplit-lto-unit -#![crate_type="lib"] +#![crate_type = "lib"] -pub fn foo() { -} +pub fn foo() {} // CHECK: !{{[0-9]+}} = !{i32 4, !"EnableSplitLTOUnit", i32 1} diff --git a/tests/codegen/enum/enum-bounds-check.rs b/tests/codegen/enum/enum-bounds-check.rs index a1b32ec9295b..c44c007ed6a9 100644 --- a/tests/codegen/enum/enum-bounds-check.rs +++ b/tests/codegen/enum/enum-bounds-check.rs @@ -3,7 +3,8 @@ #![crate_type = "lib"] pub enum Foo { - A, B + A, + B, } // CHECK-LABEL: @lookup @@ -15,7 +16,7 @@ pub fn lookup(buf: &[u8; 2], f: Foo) -> u8 { pub enum Bar { A = 2, - B = 3 + B = 3, } // CHECK-LABEL: @lookup_unmodified diff --git a/tests/codegen/enum/enum-debug-clike.rs b/tests/codegen/enum/enum-debug-clike.rs index 59ad58784439..89c803cce5e7 100644 --- a/tests/codegen/enum/enum-debug-clike.rs +++ b/tests/codegen/enum/enum-debug-clike.rs @@ -17,7 +17,11 @@ #![allow(unused_variables)] #![allow(unused_assignments)] -enum E { A, B, C } +enum E { + A, + B, + C, +} pub fn main() { let e = E::C; diff --git a/tests/codegen/enum/enum-debug-niche.rs b/tests/codegen/enum/enum-debug-niche.rs index 90de928bced6..59e8b8a78b43 100644 --- a/tests/codegen/enum/enum-debug-niche.rs +++ b/tests/codegen/enum/enum-debug-niche.rs @@ -23,7 +23,12 @@ #![allow(unused_variables)] #![allow(unused_assignments)] -enum E { A, B, C, D(bool) } +enum E { + A, + B, + C, + D(bool), +} pub fn main() { let e = E::D(true); diff --git a/tests/codegen/enum/enum-debug-tagged.rs b/tests/codegen/enum/enum-debug-tagged.rs index f13922ee33b8..e8f147665b08 100644 --- a/tests/codegen/enum/enum-debug-tagged.rs +++ b/tests/codegen/enum/enum-debug-tagged.rs @@ -21,7 +21,10 @@ #![allow(unused_variables)] #![allow(unused_assignments)] -enum E { A(u32), B(u32) } +enum E { + A(u32), + B(u32), +} pub fn main() { let e = E::A(23); diff --git a/tests/codegen/enum/enum-match.rs b/tests/codegen/enum/enum-match.rs index ced26c0a4340..8da5de63e67d 100644 --- a/tests/codegen/enum/enum-match.rs +++ b/tests/codegen/enum/enum-match.rs @@ -50,6 +50,7 @@ pub fn match1(e: Enum1) -> u8 { } // Case 2: Special cases don't apply. +#[rustfmt::skip] pub enum X { _2=2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, diff --git a/tests/codegen/enum/unreachable_enum_default_branch.rs b/tests/codegen/enum/unreachable_enum_default_branch.rs index dae01cfb055e..81a258f27220 100644 --- a/tests/codegen/enum/unreachable_enum_default_branch.rs +++ b/tests/codegen/enum/unreachable_enum_default_branch.rs @@ -22,8 +22,7 @@ const C: Int = Int(153); // CHECK-NEXT: ret i1 [[SPEC_SELECT]] #[no_mangle] pub fn implicit_match(x: Int) -> bool { - (x >= A && x <= B) - || x == C + (x >= A && x <= B) || x == C } // The code is from https://github.com/rust-lang/rust/issues/110097. @@ -35,9 +34,5 @@ pub fn implicit_match(x: Int) -> bool { // CHECK-NEXT: ret #[no_mangle] pub fn if_let(val: Result) -> Result { - if let Ok(x) = val { - Ok(x) - } else { - Err(()) - } + if let Ok(x) = val { Ok(x) } else { Err(()) } } diff --git a/tests/codegen/error-provide.rs b/tests/codegen/error-provide.rs new file mode 100644 index 000000000000..68dd383e5cce --- /dev/null +++ b/tests/codegen/error-provide.rs @@ -0,0 +1,50 @@ +// Codegen test for #126242 + +//@ compile-flags: -O +#![crate_type = "lib"] +#![feature(error_generic_member_access)] +use std::error::Request; +use std::fmt; + +#[derive(Debug)] +struct MyBacktrace1 {} + +#[derive(Debug)] +struct MyBacktrace2 {} + +#[derive(Debug)] +struct MyBacktrace3 {} + +#[derive(Debug)] +struct MyError { + backtrace1: MyBacktrace1, + backtrace2: MyBacktrace2, + backtrace3: MyBacktrace3, + other: MyBacktrace3, +} + +impl fmt::Display for MyError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Example Error") + } +} + +impl std::error::Error for MyError { + // CHECK-LABEL: @provide + #[no_mangle] + fn provide<'a>(&'a self, request: &mut Request<'a>) { + // LLVM should be able to optimize multiple .provide_* calls into a switch table + // and eliminate redundant ones, rather than compare one-by-one. + + // CHECK-NEXT: start: + // CHECK-NEXT: %[[SCRUTINEE:[^ ]+]] = load i64, ptr + // CHECK-NEXT: switch i64 %[[SCRUTINEE]], label %{{.*}} [ + // CHECK-COUNT-3: i64 {{.*}}, label %{{.*}} + // CHECK-NEXT: ] + request + .provide_ref::(&self.backtrace1) + .provide_ref::(&self.other) + .provide_ref::(&self.backtrace2) + .provide_ref::(&self.backtrace3); + } +} diff --git a/tests/codegen/fatptr.rs b/tests/codegen/fatptr.rs index 0f13e66fbad0..041807202b84 100644 --- a/tests/codegen/fatptr.rs +++ b/tests/codegen/fatptr.rs @@ -7,6 +7,6 @@ pub trait T {} // CHECK-LABEL: @copy_fat_ptr #[no_mangle] pub fn copy_fat_ptr(x: &T) { -// CHECK-NOT: extractvalue + // CHECK-NOT: extractvalue let x2 = x; } diff --git a/tests/codegen/fixed-x18.rs b/tests/codegen/fixed-x18.rs new file mode 100644 index 000000000000..4997a39a7263 --- /dev/null +++ b/tests/codegen/fixed-x18.rs @@ -0,0 +1,22 @@ +// Test that the `reserve-x18` target feature is (not) emitted when +// the `-Zfixed-x18` flag is (not) set. + +//@ revisions: unset set +//@ needs-llvm-components: aarch64 +//@ compile-flags: --target aarch64-unknown-none +//@ [set] compile-flags: -Zfixed-x18 + +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +#[no_mangle] +pub fn foo() { + // CHECK: @foo() unnamed_addr #0 + + // unset-NOT: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+reserve-x18{{.*}} } + // set: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+reserve-x18{{.*}} } +} diff --git a/tests/codegen/float_math.rs b/tests/codegen/float_math.rs index dcca51c2f5eb..31387ec82b92 100644 --- a/tests/codegen/float_math.rs +++ b/tests/codegen/float_math.rs @@ -3,48 +3,40 @@ #![crate_type = "lib"] #![feature(core_intrinsics)] -use std::intrinsics::{fadd_fast, fsub_fast, fmul_fast, fdiv_fast, frem_fast}; +use std::intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast}; // CHECK-LABEL: @add #[no_mangle] pub fn add(x: f32, y: f32) -> f32 { -// CHECK: fadd float -// CHECK-NOT: fast + // CHECK: fadd float + // CHECK-NOT: fast x + y } // CHECK-LABEL: @addition #[no_mangle] pub fn addition(x: f32, y: f32) -> f32 { -// CHECK: fadd fast float - unsafe { - fadd_fast(x, y) - } + // CHECK: fadd fast float + unsafe { fadd_fast(x, y) } } // CHECK-LABEL: @subtraction #[no_mangle] pub fn subtraction(x: f32, y: f32) -> f32 { -// CHECK: fsub fast float - unsafe { - fsub_fast(x, y) - } + // CHECK: fsub fast float + unsafe { fsub_fast(x, y) } } // CHECK-LABEL: @multiplication #[no_mangle] pub fn multiplication(x: f32, y: f32) -> f32 { -// CHECK: fmul fast float - unsafe { - fmul_fast(x, y) - } + // CHECK: fmul fast float + unsafe { fmul_fast(x, y) } } // CHECK-LABEL: @division #[no_mangle] pub fn division(x: f32, y: f32) -> f32 { -// CHECK: fdiv fast float - unsafe { - fdiv_fast(x, y) - } + // CHECK: fdiv fast float + unsafe { fdiv_fast(x, y) } } diff --git a/tests/codegen/force-frame-pointers.rs b/tests/codegen/force-frame-pointers.rs index c41824e024fd..84e0bcb39add 100644 --- a/tests/codegen/force-frame-pointers.rs +++ b/tests/codegen/force-frame-pointers.rs @@ -1,6 +1,6 @@ //@ compile-flags: -C no-prepopulate-passes -C force-frame-pointers=y -Copt-level=0 -#![crate_type="lib"] +#![crate_type = "lib"] // CHECK: attributes #{{.*}} "frame-pointer"="all" pub fn foo() {} diff --git a/tests/codegen/force-no-unwind-tables.rs b/tests/codegen/force-no-unwind-tables.rs index 0189ae7c0a68..e823bedac0f6 100644 --- a/tests/codegen/force-no-unwind-tables.rs +++ b/tests/codegen/force-no-unwind-tables.rs @@ -1,7 +1,7 @@ //@ compile-flags: -C no-prepopulate-passes -C panic=abort -C force-unwind-tables=n //@ ignore-windows -#![crate_type="lib"] +#![crate_type = "lib"] // CHECK-LABEL: define{{.*}}void @foo // CHECK-NOT: attributes #{{.*}} uwtable diff --git a/tests/codegen/force-unwind-tables.rs b/tests/codegen/force-unwind-tables.rs index 33fdf7653f49..a2ef8a104543 100644 --- a/tests/codegen/force-unwind-tables.rs +++ b/tests/codegen/force-unwind-tables.rs @@ -1,6 +1,6 @@ //@ compile-flags: -C no-prepopulate-passes -C force-unwind-tables=y -Copt-level=0 -#![crate_type="lib"] +#![crate_type = "lib"] // CHECK: attributes #{{.*}} uwtable pub fn foo() {} diff --git a/tests/codegen/frame-pointer.rs b/tests/codegen/frame-pointer.rs index 879535bcc368..da81c2e9cd9a 100644 --- a/tests/codegen/frame-pointer.rs +++ b/tests/codegen/frame-pointer.rs @@ -13,13 +13,12 @@ #![feature(no_core, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="copy"] -trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} impl Copy for u32 {} - // CHECK: define i32 @peach{{.*}}[[PEACH_ATTRS:\#[0-9]+]] { #[no_mangle] pub fn peach(x: u32) -> u32 { diff --git a/tests/codegen/function-arguments-noopt.rs b/tests/codegen/function-arguments-noopt.rs index 0d42915893bc..c80f119696df 100644 --- a/tests/codegen/function-arguments-noopt.rs +++ b/tests/codegen/function-arguments-noopt.rs @@ -7,63 +7,63 @@ #![feature(rustc_attrs)] pub struct S { - _field: [i32; 8], + _field: [i32; 8], } // CHECK: zeroext i1 @boolean(i1 zeroext %x) #[no_mangle] pub fn boolean(x: bool) -> bool { - x + x } // CHECK-LABEL: @boolean_call #[no_mangle] pub fn boolean_call(x: bool, f: fn(bool) -> bool) -> bool { -// CHECK: call zeroext i1 %f(i1 zeroext %x) - f(x) + // CHECK: call zeroext i1 %f(i1 zeroext %x) + f(x) } // CHECK: align 4 ptr @borrow(ptr align 4 %x) #[no_mangle] pub fn borrow(x: &i32) -> &i32 { - x + x } // CHECK: align 4 ptr @borrow_mut(ptr align 4 %x) #[no_mangle] pub fn borrow_mut(x: &mut i32) -> &mut i32 { - x + x } // CHECK-LABEL: @borrow_call #[no_mangle] pub fn borrow_call(x: &i32, f: fn(&i32) -> &i32) -> &i32 { - // CHECK: call align 4 ptr %f(ptr align 4 %x) - f(x) + // CHECK: call align 4 ptr %f(ptr align 4 %x) + f(x) } // CHECK: void @struct_(ptr sret([32 x i8]) align 4{{( %_0)?}}, ptr align 4 %x) #[no_mangle] pub fn struct_(x: S) -> S { - x + x } // CHECK-LABEL: @struct_call #[no_mangle] pub fn struct_call(x: S, f: fn(S) -> S) -> S { - // CHECK: call void %f(ptr sret([32 x i8]) align 4{{( %_0)?}}, ptr align 4 %{{.+}}) - f(x) + // CHECK: call void %f(ptr sret([32 x i8]) align 4{{( %_0)?}}, ptr align 4 %{{.+}}) + f(x) } // CHECK: { i1, i8 } @enum_(i1 zeroext %x.0, i8 %x.1) #[no_mangle] pub fn enum_(x: Option) -> Option { - x + x } // CHECK-LABEL: @enum_call #[no_mangle] pub fn enum_call(x: Option, f: fn(Option) -> Option) -> Option { - // CHECK: call { i1, i8 } %f(i1 zeroext %x.0, i8 %x.1) - f(x) + // CHECK: call { i1, i8 } %f(i1 zeroext %x.0, i8 %x.1) + f(x) } diff --git a/tests/codegen/function-arguments.rs b/tests/codegen/function-arguments.rs index ebcbcae8563c..56504df40347 100644 --- a/tests/codegen/function-arguments.rs +++ b/tests/codegen/function-arguments.rs @@ -3,129 +3,123 @@ #![feature(dyn_star)] #![feature(allocator_api)] +use std::marker::PhantomPinned; use std::mem::MaybeUninit; use std::num::NonZero; -use std::marker::PhantomPinned; use std::ptr::NonNull; pub struct S { - _field: [i32; 8], + _field: [i32; 8], } pub struct UnsafeInner { - _field: std::cell::UnsafeCell, + _field: std::cell::UnsafeCell, } pub struct NotUnpin { - _field: i32, - _marker: PhantomPinned, + _field: i32, + _marker: PhantomPinned, } pub enum MyBool { - True, - False, + True, + False, } // CHECK: noundef zeroext i1 @boolean(i1 noundef zeroext %x) #[no_mangle] pub fn boolean(x: bool) -> bool { - x + x } // CHECK: i8 @maybeuninit_boolean(i8 %x) #[no_mangle] pub fn maybeuninit_boolean(x: MaybeUninit) -> MaybeUninit { - x + x } // CHECK: noundef zeroext i1 @enum_bool(i1 noundef zeroext %x) #[no_mangle] pub fn enum_bool(x: MyBool) -> MyBool { - x + x } // CHECK: i8 @maybeuninit_enum_bool(i8 %x) #[no_mangle] pub fn maybeuninit_enum_bool(x: MaybeUninit) -> MaybeUninit { - x + x } // CHECK: noundef i32 @char(i32 noundef %x) #[no_mangle] pub fn char(x: char) -> char { - x + x } // CHECK: i32 @maybeuninit_char(i32 %x) #[no_mangle] pub fn maybeuninit_char(x: MaybeUninit) -> MaybeUninit { - x + x } // CHECK: noundef i64 @int(i64 noundef %x) #[no_mangle] pub fn int(x: u64) -> u64 { - x + x } // CHECK: noundef i64 @nonzero_int(i64 noundef %x) #[no_mangle] pub fn nonzero_int(x: NonZero) -> NonZero { - x + x } // CHECK: noundef i64 @option_nonzero_int(i64 noundef %x) #[no_mangle] pub fn option_nonzero_int(x: Option>) -> Option> { - x + x } // CHECK: @readonly_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1) // FIXME #25759 This should also have `nocapture` #[no_mangle] -pub fn readonly_borrow(_: &i32) { -} +pub fn readonly_borrow(_: &i32) {} // CHECK: noundef align 4 dereferenceable(4) ptr @readonly_borrow_ret() #[no_mangle] pub fn readonly_borrow_ret() -> &'static i32 { - loop {} + loop {} } // CHECK: @static_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1) // static borrow may be captured #[no_mangle] -pub fn static_borrow(_: &'static i32) { -} +pub fn static_borrow(_: &'static i32) {} // CHECK: @named_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1) // borrow with named lifetime may be captured #[no_mangle] -pub fn named_borrow<'r>(_: &'r i32) { -} +pub fn named_borrow<'r>(_: &'r i32) {} // CHECK: @unsafe_borrow(ptr noundef nonnull align 2 %_1) // unsafe interior means this isn't actually readonly and there may be aliases ... #[no_mangle] -pub fn unsafe_borrow(_: &UnsafeInner) { -} +pub fn unsafe_borrow(_: &UnsafeInner) {} // CHECK: @mutable_unsafe_borrow(ptr noalias noundef align 2 dereferenceable(2) %_1) // ... unless this is a mutable borrow, those never alias #[no_mangle] -pub fn mutable_unsafe_borrow(_: &mut UnsafeInner) { -} +pub fn mutable_unsafe_borrow(_: &mut UnsafeInner) {} // CHECK: @mutable_borrow(ptr noalias noundef align 4 dereferenceable(4) %_1) // FIXME #25759 This should also have `nocapture` #[no_mangle] -pub fn mutable_borrow(_: &mut i32) { -} +pub fn mutable_borrow(_: &mut i32) {} // CHECK: noundef align 4 dereferenceable(4) ptr @mutable_borrow_ret() #[no_mangle] pub fn mutable_borrow_ret() -> &'static mut i32 { - loop {} + loop {} } #[no_mangle] @@ -133,53 +127,44 @@ pub fn mutable_borrow_ret() -> &'static mut i32 { // This one is *not* `noalias` because it might be self-referential. // It is also not `dereferenceable` due to // . -pub fn mutable_notunpin_borrow(_: &mut NotUnpin) { -} +pub fn mutable_notunpin_borrow(_: &mut NotUnpin) {} // CHECK: @notunpin_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1) // But `&NotUnpin` behaves perfectly normal. #[no_mangle] -pub fn notunpin_borrow(_: &NotUnpin) { -} +pub fn notunpin_borrow(_: &NotUnpin) {} // CHECK: @indirect_struct(ptr noalias nocapture noundef readonly align 4 dereferenceable(32) %_1) #[no_mangle] -pub fn indirect_struct(_: S) { -} +pub fn indirect_struct(_: S) {} // CHECK: @borrowed_struct(ptr noalias noundef readonly align 4 dereferenceable(32) %_1) // FIXME #25759 This should also have `nocapture` #[no_mangle] -pub fn borrowed_struct(_: &S) { -} +pub fn borrowed_struct(_: &S) {} // CHECK: @option_borrow(ptr noalias noundef readonly align 4 dereferenceable_or_null(4) %x) #[no_mangle] -pub fn option_borrow(x: Option<&i32>) { -} +pub fn option_borrow(x: Option<&i32>) {} // CHECK: @option_borrow_mut(ptr noalias noundef align 4 dereferenceable_or_null(4) %x) #[no_mangle] -pub fn option_borrow_mut(x: Option<&mut i32>) { -} +pub fn option_borrow_mut(x: Option<&mut i32>) {} // CHECK: @raw_struct(ptr noundef %_1) #[no_mangle] -pub fn raw_struct(_: *const S) { -} +pub fn raw_struct(_: *const S) {} // CHECK: @raw_option_nonnull_struct(ptr noundef %_1) #[no_mangle] -pub fn raw_option_nonnull_struct(_: Option>) { -} - +pub fn raw_option_nonnull_struct(_: Option>) {} // `Box` can get deallocated during execution of the function, so it should // not get `dereferenceable`. // CHECK: noundef nonnull align 4 ptr @_box(ptr noalias noundef nonnull align 4 %x) #[no_mangle] pub fn _box(x: Box) -> Box { - x + x } // With a custom allocator, it should *not* have `noalias`. (See @@ -188,106 +173,93 @@ pub fn _box(x: Box) -> Box { // CHECK: @_box_custom(ptr noundef nonnull align 4 %x.0, ptr noalias noundef nonnull readonly align 1 %x.1) #[no_mangle] pub fn _box_custom(x: Box) { - drop(x) + drop(x) } // CHECK: noundef nonnull align 4 ptr @notunpin_box(ptr noundef nonnull align 4 %x) #[no_mangle] pub fn notunpin_box(x: Box) -> Box { - x + x } // CHECK: @struct_return(ptr{{( dead_on_unwind)?}} noalias nocapture noundef{{( writable)?}} sret([32 x i8]) align 4 dereferenceable(32){{( %_0)?}}) #[no_mangle] pub fn struct_return() -> S { - S { - _field: [0, 0, 0, 0, 0, 0, 0, 0] - } + S { _field: [0, 0, 0, 0, 0, 0, 0, 0] } } // Hack to get the correct size for the length part in slices // CHECK: @helper([[USIZE:i[0-9]+]] noundef %_1) #[no_mangle] -pub fn helper(_: usize) { -} +pub fn helper(_: usize) {} // CHECK: @slice(ptr noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] -pub fn slice(_: &[u8]) { -} +pub fn slice(_: &[u8]) {} // CHECK: @mutable_slice(ptr noalias noundef nonnull align 1 %_1.0, [[USIZE]] noundef %_1.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] -pub fn mutable_slice(_: &mut [u8]) { -} +pub fn mutable_slice(_: &mut [u8]) {} // CHECK: @unsafe_slice(ptr noundef nonnull align 2 %_1.0, [[USIZE]] noundef %_1.1) // unsafe interior means this isn't actually readonly and there may be aliases ... #[no_mangle] -pub fn unsafe_slice(_: &[UnsafeInner]) { -} +pub fn unsafe_slice(_: &[UnsafeInner]) {} // CHECK: @raw_slice(ptr noundef %_1.0, [[USIZE]] noundef %_1.1) #[no_mangle] -pub fn raw_slice(_: *const [u8]) { -} +pub fn raw_slice(_: *const [u8]) {} // CHECK: @str(ptr noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] -pub fn str(_: &[u8]) { -} +pub fn str(_: &[u8]) {} // CHECK: @trait_borrow(ptr noundef nonnull align 1 %_1.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] -pub fn trait_borrow(_: &dyn Drop) { -} +pub fn trait_borrow(_: &dyn Drop) {} // CHECK: @option_trait_borrow(ptr noundef align 1 %x.0, ptr %x.1) #[no_mangle] -pub fn option_trait_borrow(x: Option<&dyn Drop>) { -} +pub fn option_trait_borrow(x: Option<&dyn Drop>) {} // CHECK: @option_trait_borrow_mut(ptr noundef align 1 %x.0, ptr %x.1) #[no_mangle] -pub fn option_trait_borrow_mut(x: Option<&mut dyn Drop>) { -} +pub fn option_trait_borrow_mut(x: Option<&mut dyn Drop>) {} // CHECK: @trait_raw(ptr noundef %_1.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1) #[no_mangle] -pub fn trait_raw(_: *const dyn Drop) { -} +pub fn trait_raw(_: *const dyn Drop) {} // CHECK: @trait_box(ptr noalias noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}}) #[no_mangle] -pub fn trait_box(_: Box) { -} +pub fn trait_box(_: Box) {} // CHECK: { ptr, ptr } @trait_option(ptr noalias noundef align 1 %x.0, ptr %x.1) #[no_mangle] pub fn trait_option(x: Option>) -> Option> { - x + x } // CHECK: { ptr, [[USIZE]] } @return_slice(ptr noalias noundef nonnull readonly align 2 %x.0, [[USIZE]] noundef %x.1) #[no_mangle] pub fn return_slice(x: &[u16]) -> &[u16] { - x + x } // CHECK: { i16, i16 } @enum_id_1(i16 noundef %x.0, i16 %x.1) #[no_mangle] pub fn enum_id_1(x: Option>) -> Option> { - x + x } // CHECK: { i1, i8 } @enum_id_2(i1 noundef zeroext %x.0, i8 %x.1) #[no_mangle] pub fn enum_id_2(x: Option) -> Option { - x + x } // CHECK: { ptr, {{.+}} } @dyn_star(ptr noundef %x.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %x.1) @@ -295,5 +267,5 @@ pub fn enum_id_2(x: Option) -> Option { // so do like the `trait_box` test and just match on `{{.+}}` for the vtable. #[no_mangle] pub fn dyn_star(x: dyn* Drop) -> dyn* Drop { - x + x } diff --git a/tests/codegen/gdb_debug_script_load.rs b/tests/codegen/gdb_debug_script_load.rs index f15defeaca8b..30d518c0bcb8 100644 --- a/tests/codegen/gdb_debug_script_load.rs +++ b/tests/codegen/gdb_debug_script_load.rs @@ -1,6 +1,6 @@ // //@ ignore-windows -//@ ignore-macos +//@ ignore-apple //@ ignore-wasm //@ ignore-emscripten diff --git a/tests/codegen/generic-debug.rs b/tests/codegen/generic-debug.rs index 0f289026396f..3423abe7187b 100644 --- a/tests/codegen/generic-debug.rs +++ b/tests/codegen/generic-debug.rs @@ -13,6 +13,6 @@ pub struct Generic(Type); -fn main () { +fn main() { let generic = Generic(10); } diff --git a/tests/codegen/inline-always-works-always.rs b/tests/codegen/inline-always-works-always.rs index e9ca05d17569..07200fd9e373 100644 --- a/tests/codegen/inline-always-works-always.rs +++ b/tests/codegen/inline-always-works-always.rs @@ -3,7 +3,7 @@ //@[SIZE-OPT] compile-flags: -Copt-level=s //@[SPEED-OPT] compile-flags: -Copt-level=3 -#![crate_type="rlib"] +#![crate_type = "rlib"] #[no_mangle] #[inline(always)] diff --git a/tests/codegen/inline-debuginfo.rs b/tests/codegen/inline-debuginfo.rs index f327180560de..1e1c9037f5c9 100644 --- a/tests/codegen/inline-debuginfo.rs +++ b/tests/codegen/inline-debuginfo.rs @@ -1,4 +1,4 @@ -#![crate_type="rlib"] +#![crate_type = "rlib"] //@ compile-flags: -Copt-level=3 -g // diff --git a/tests/codegen/instrument-coverage/instrument-coverage-off.rs b/tests/codegen/instrument-coverage/instrument-coverage-off.rs index 616e3295e5b1..e44d6c658748 100644 --- a/tests/codegen/instrument-coverage/instrument-coverage-off.rs +++ b/tests/codegen/instrument-coverage/instrument-coverage-off.rs @@ -1,5 +1,6 @@ // Test that `-Cinstrument-coverage=off` does not add coverage instrumentation to LLVM IR. +//@ compile-flags: -Zno-profiler-runtime //@ revisions: n no off false_ zero //@ [n] compile-flags: -Cinstrument-coverage=n //@ [no] compile-flags: -Cinstrument-coverage=no diff --git a/tests/codegen/instrument-coverage/instrument-coverage.rs b/tests/codegen/instrument-coverage/instrument-coverage.rs index d638a544d5a5..23d23651c72f 100644 --- a/tests/codegen/instrument-coverage/instrument-coverage.rs +++ b/tests/codegen/instrument-coverage/instrument-coverage.rs @@ -1,6 +1,6 @@ // Test that `-Cinstrument-coverage` creates expected __llvm_profile_filename symbol in LLVM IR. -//@ needs-profiler-support +//@ compile-flags: -Zno-profiler-runtime //@ revisions: default y yes on true_ all //@ [default] compile-flags: -Cinstrument-coverage //@ [y] compile-flags: -Cinstrument-coverage=y @@ -9,15 +9,13 @@ //@ [true_] compile-flags: -Cinstrument-coverage=true //@ [all] compile-flags: -Cinstrument-coverage=all -// CHECK: @__llvm_profile_filename = {{.*}}"default_%m_%p.profraw\00"{{.*}} -// CHECK: @__llvm_coverage_mapping +// CHECK-DAG: @__llvm_coverage_mapping +// CHECK-DAG: @__llvm_profile_filename = {{.*}}"default_%m_%p.profraw\00"{{.*}} -#![crate_type="lib"] +#![crate_type = "lib"] #[inline(never)] -fn some_function() { - -} +fn some_function() {} pub fn some_other_function() { some_function(); diff --git a/tests/codegen/instrument-coverage/testprog.rs b/tests/codegen/instrument-coverage/testprog.rs index b352cbdb7557..eea4d9cb3cf0 100644 --- a/tests/codegen/instrument-coverage/testprog.rs +++ b/tests/codegen/instrument-coverage/testprog.rs @@ -1,5 +1,5 @@ //@ edition: 2021 -//@ needs-profiler-support +//@ compile-flags: -Zno-profiler-runtime //@ compile-flags: -Cinstrument-coverage -Copt-level=0 //@ revisions: LINUX DARWIN WINDOWS @@ -11,7 +11,7 @@ //@ [LINUX] filecheck-flags: -DINSTR_PROF_COVFUN=__llvm_covfun //@ [LINUX] filecheck-flags: '-DCOMDAT_IF_SUPPORTED=, comdat' -//@ [DARWIN] only-macos +//@ [DARWIN] only-apple //@ [DARWIN] filecheck-flags: -DINSTR_PROF_DATA=__DATA,__llvm_prf_data,regular,live_support //@ [DARWIN] filecheck-flags: -DINSTR_PROF_NAME=__DATA,__llvm_prf_names //@ [DARWIN] filecheck-flags: -DINSTR_PROF_CNTS=__DATA,__llvm_prf_cnts @@ -49,7 +49,7 @@ where pub fn wrap_with(inner: T, should_wrap: bool, wrapper: F) where - F: FnOnce(&T) + F: FnOnce(&T), { if should_wrap { wrapper(&inner) diff --git a/tests/codegen/integer-cmp.rs b/tests/codegen/integer-cmp.rs index 46972878da50..bba112b246f3 100644 --- a/tests/codegen/integer-cmp.rs +++ b/tests/codegen/integer-cmp.rs @@ -10,19 +10,19 @@ use std::cmp::Ordering; // CHECK-LABEL: @cmp_signed #[no_mangle] pub fn cmp_signed(a: i64, b: i64) -> Ordering { -// CHECK: icmp slt -// CHECK: icmp ne -// CHECK: zext i1 -// CHECK: select i1 + // CHECK: icmp slt + // CHECK: icmp ne + // CHECK: zext i1 + // CHECK: select i1 a.cmp(&b) } // CHECK-LABEL: @cmp_unsigned #[no_mangle] pub fn cmp_unsigned(a: u32, b: u32) -> Ordering { -// CHECK: icmp ult -// CHECK: icmp ne -// CHECK: zext i1 -// CHECK: select i1 + // CHECK: icmp ult + // CHECK: icmp ne + // CHECK: zext i1 + // CHECK: select i1 a.cmp(&b) } diff --git a/tests/codegen/integer-overflow.rs b/tests/codegen/integer-overflow.rs index 00780251bbc1..a6407476fc20 100644 --- a/tests/codegen/integer-overflow.rs +++ b/tests/codegen/integer-overflow.rs @@ -2,7 +2,6 @@ #![crate_type = "lib"] - pub struct S1<'a> { data: &'a [u8], position: usize, @@ -12,7 +11,7 @@ pub struct S1<'a> { #[no_mangle] pub fn slice_no_index_order<'a>(s: &'a mut S1, n: usize) -> &'a [u8] { // CHECK-NOT: slice_index_order_fail - let d = &s.data[s.position..s.position+n]; + let d = &s.data[s.position..s.position + n]; s.position += n; return d; } diff --git a/tests/codegen/internalize-closures.rs b/tests/codegen/internalize-closures.rs index d37aa5fee8da..f226ea6faac8 100644 --- a/tests/codegen/internalize-closures.rs +++ b/tests/codegen/internalize-closures.rs @@ -1,7 +1,6 @@ //@ compile-flags: -C no-prepopulate-passes -Zmir-opt-level=0 pub fn main() { - // We want to make sure that closures get 'internal' linkage instead of // 'weak_odr' when they are not shared between codegen units // FIXME(eddyb) `legacy` mangling uses `{{closure}}`, while `v0` @@ -9,6 +8,6 @@ pub fn main() { // CHECK-LABEL: ; internalize_closures::main::{{.*}}closure // CHECK-NEXT: ; Function Attrs: // CHECK-NEXT: define internal - let c = |x:i32| { x + 1 }; + let c = |x: i32| x + 1; let _ = c(1); } diff --git a/tests/codegen/intrinsic-no-unnamed-attr.rs b/tests/codegen/intrinsic-no-unnamed-attr.rs index 45d06c70a6d7..fce0de80d7b6 100644 --- a/tests/codegen/intrinsic-no-unnamed-attr.rs +++ b/tests/codegen/intrinsic-no-unnamed-attr.rs @@ -8,5 +8,7 @@ extern "rust-intrinsic" { // CHECK: @llvm.sqrt.f32(float) #{{[0-9]*}} fn main() { - unsafe { sqrtf32(0.0f32); } + unsafe { + sqrtf32(0.0f32); + } } diff --git a/tests/codegen/intrinsics/const_eval_select.rs b/tests/codegen/intrinsics/const_eval_select.rs index c8debe8d7113..baa985b00cd0 100644 --- a/tests/codegen/intrinsics/const_eval_select.rs +++ b/tests/codegen/intrinsics/const_eval_select.rs @@ -6,10 +6,14 @@ use std::intrinsics::const_eval_select; -const fn foo(_: i32) -> i32 { 1 } +const fn foo(_: i32) -> i32 { + 1 +} #[no_mangle] -pub fn hi(n: i32) -> i32 { n } +pub fn hi(n: i32) -> i32 { + n +} #[no_mangle] pub unsafe fn hey() { diff --git a/tests/codegen/intrinsics/likely.rs b/tests/codegen/intrinsics/likely.rs index c5904085fc08..098fd9936ce1 100644 --- a/tests/codegen/intrinsics/likely.rs +++ b/tests/codegen/intrinsics/likely.rs @@ -3,17 +3,13 @@ #![crate_type = "lib"] #![feature(core_intrinsics)] -use std::intrinsics::{likely,unlikely}; +use std::intrinsics::{likely, unlikely}; #[no_mangle] pub fn check_likely(x: i32, y: i32) -> Option { unsafe { // CHECK: call i1 @llvm.expect.i1(i1 %{{.*}}, i1 true) - if likely(x == y) { - None - } else { - Some(x + y) - } + if likely(x == y) { None } else { Some(x + y) } } } @@ -21,10 +17,6 @@ pub fn check_likely(x: i32, y: i32) -> Option { pub fn check_unlikely(x: i32, y: i32) -> Option { unsafe { // CHECK: call i1 @llvm.expect.i1(i1 %{{.*}}, i1 false) - if unlikely(x == y) { - None - } else { - Some(x + y) - } + if unlikely(x == y) { None } else { Some(x + y) } } } diff --git a/tests/codegen/intrinsics/prefetch.rs b/tests/codegen/intrinsics/prefetch.rs index edd8c20b38f1..3f9f21c85cbf 100644 --- a/tests/codegen/intrinsics/prefetch.rs +++ b/tests/codegen/intrinsics/prefetch.rs @@ -3,8 +3,9 @@ #![crate_type = "lib"] #![feature(core_intrinsics)] -use std::intrinsics::{prefetch_read_data, prefetch_write_data, - prefetch_read_instruction, prefetch_write_instruction}; +use std::intrinsics::{ + prefetch_read_data, prefetch_read_instruction, prefetch_write_data, prefetch_write_instruction, +}; #[no_mangle] pub fn check_prefetch_read_data(data: &[i8]) { diff --git a/tests/codegen/intrinsics/ptr_metadata.rs b/tests/codegen/intrinsics/ptr_metadata.rs new file mode 100644 index 000000000000..f4bf5a1f5f17 --- /dev/null +++ b/tests/codegen/intrinsics/ptr_metadata.rs @@ -0,0 +1,36 @@ +//@ compile-flags: -O -C no-prepopulate-passes -Z inline-mir +//@ only-64bit (so I don't need to worry about usize) + +#![crate_type = "lib"] +#![feature(core_intrinsics)] + +use std::intrinsics::ptr_metadata; + +// CHECK-LABEL: @thin_metadata( +#[no_mangle] +pub fn thin_metadata(p: *const ()) { + // CHECK: start + // CHECK-NEXT: ret void + ptr_metadata(p) +} + +// CHECK-LABEL: @slice_metadata( +#[no_mangle] +pub fn slice_metadata(p: *const [u8]) -> usize { + // CHECK: start + // CHECK-NEXT: ret i64 %p.1 + ptr_metadata(p) +} + +// CHECK-LABEL: @dyn_byte_offset( +#[no_mangle] +pub unsafe fn dyn_byte_offset( + p: *const dyn std::fmt::Debug, + n: usize, +) -> *const dyn std::fmt::Debug { + // CHECK: %[[Q:.+]] = getelementptr inbounds i8, ptr %p.0, i64 %n + // CHECK: %[[TEMP1:.+]] = insertvalue { ptr, ptr } poison, ptr %[[Q]], 0 + // CHECK: %[[TEMP2:.+]] = insertvalue { ptr, ptr } %[[TEMP1]], ptr %p.1, 1 + // CHECK: ret { ptr, ptr } %[[TEMP2]] + p.byte_add(n) +} diff --git a/tests/codegen/is_val_statically_known.rs b/tests/codegen/is_val_statically_known.rs index 255d8950a974..6af4f353a481 100644 --- a/tests/codegen/is_val_statically_known.rs +++ b/tests/codegen/is_val_statically_known.rs @@ -75,7 +75,7 @@ pub fn _slice_ref(a: &[u8]) -> i32 { #[no_mangle] pub fn _slice_ref_borrow() -> i32 { // CHECK: ret i32 6 - _slice_ref(&[0;3]) + _slice_ref(&[0; 3]) } // CHECK-LABEL: @_slice_ref_arg( diff --git a/tests/codegen/issue-97217.rs b/tests/codegen/issue-97217.rs index a0dfff2ef2eb..ecf1fa1ddb3e 100644 --- a/tests/codegen/issue-97217.rs +++ b/tests/codegen/issue-97217.rs @@ -16,6 +16,6 @@ pub fn issue97217() -> i32 { let v1 = vec![5, 6, 7]; let v1_iter = v1.iter(); let total: i32 = v1_iter.sum(); - println!("{}",total); + println!("{}", total); total } diff --git a/tests/codegen/issues/issue-103327.rs b/tests/codegen/issues/issue-103327.rs index 398b1f376b78..f8cf273e4a6c 100644 --- a/tests/codegen/issues/issue-103327.rs +++ b/tests/codegen/issues/issue-103327.rs @@ -9,9 +9,5 @@ pub fn test(a: i32, b: i32) -> bool { let c1 = (a >= 0) && (a <= 10); let c2 = (b >= 0) && (b <= 20); - if c1 & c2 { - a + 100 != b - } else { - true - } + if c1 & c2 { a + 100 != b } else { true } } diff --git a/tests/codegen/issues/issue-105386-ub-in-debuginfo.rs b/tests/codegen/issues/issue-105386-ub-in-debuginfo.rs index 56b4330b1a6c..db9eeda19a6c 100644 --- a/tests/codegen/issues/issue-105386-ub-in-debuginfo.rs +++ b/tests/codegen/issues/issue-105386-ub-in-debuginfo.rs @@ -6,7 +6,8 @@ pub struct S([usize; 8]); #[no_mangle] pub fn outer_function(x: S, y: S) -> usize { - (#[inline(always)]|| { + (#[inline(always)] + || { let _z = x; y.0[0] })() diff --git a/tests/codegen/issues/issue-109328-split_first.rs b/tests/codegen/issues/issue-109328-split_first.rs new file mode 100644 index 000000000000..7f7957593d2d --- /dev/null +++ b/tests/codegen/issues/issue-109328-split_first.rs @@ -0,0 +1,16 @@ +//@ compile-flags: -O + +#![crate_type = "lib"] + +// CHECK-LABEL: @foo +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: getelementptr inbounds +// CHECK-NEXT: load [[TYPE:i(32|64)]] +// CHECK-NEXT: icmp eq [[TYPE]] +// CHECK-NEXT: br i1 +#[no_mangle] +pub fn foo(input: &mut &[u64]) -> Option { + let (first, rest) = input.split_first()?; + *input = rest; + Some(*first) +} diff --git a/tests/codegen/issues/issue-110797-enum-jump-same.rs b/tests/codegen/issues/issue-110797-enum-jump-same.rs new file mode 100644 index 000000000000..f34b191ac709 --- /dev/null +++ b/tests/codegen/issues/issue-110797-enum-jump-same.rs @@ -0,0 +1,26 @@ +//@ compile-flags: -O +//@ min-llvm-version: 18 + +#![crate_type = "lib"] + +pub enum K { + A(Box<[i32]>), + B(Box<[u8]>), + C(Box<[String]>), + D(Box<[u16]>), +} + +// CHECK-LABEL: @get_len +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: getelementptr inbounds +// CHECK-NEXT: load [[TYPE:i(32|64)]] +// CHECK-NEXT: ret [[TYPE]] +#[no_mangle] +pub fn get_len(arg: &K) -> usize { + match arg { + K::A(ref lst) => lst.len(), + K::B(ref lst) => lst.len(), + K::C(ref lst) => lst.len(), + K::D(ref lst) => lst.len(), + } +} diff --git a/tests/codegen/issues/issue-111508-vec-tryinto-array.rs b/tests/codegen/issues/issue-111508-vec-tryinto-array.rs new file mode 100644 index 000000000000..6415724b40ad --- /dev/null +++ b/tests/codegen/issues/issue-111508-vec-tryinto-array.rs @@ -0,0 +1,22 @@ +//@ compile-flags: -O +// This regress since Rust version 1.72. +//@ min-llvm-version: 18.1.4 + +#![crate_type = "lib"] + +use std::convert::TryInto; + +const N: usize = 24; + +// CHECK-LABEL: @example +// CHECK-NOT: unwrap_failed +#[no_mangle] +pub fn example(a: Vec) -> u8 { + if a.len() != 32 { + return 0; + } + + let a: [u8; 32] = a.try_into().unwrap(); + + a[15] + a[N] +} diff --git a/tests/codegen/issues/issue-112509-slice-get-andthen-get.rs b/tests/codegen/issues/issue-112509-slice-get-andthen-get.rs new file mode 100644 index 000000000000..ae02c3fb79e7 --- /dev/null +++ b/tests/codegen/issues/issue-112509-slice-get-andthen-get.rs @@ -0,0 +1,12 @@ +//@ compile-flags: -O +#![crate_type = "lib"] + +// CHECK-LABEL: @write_u8_variant_a +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: getelementptr +// CHECK-NEXT: icmp ugt +#[no_mangle] +pub fn write_u8_variant_a(bytes: &mut [u8], buf: u8, offset: usize) -> Option<&mut [u8]> { + let buf = buf.to_le_bytes(); + bytes.get_mut(offset..).and_then(|bytes| bytes.get_mut(..buf.len())) +} diff --git a/tests/codegen/issues/issue-113757-bounds-check-after-cmp-max.rs b/tests/codegen/issues/issue-113757-bounds-check-after-cmp-max.rs new file mode 100644 index 000000000000..d495adf99804 --- /dev/null +++ b/tests/codegen/issues/issue-113757-bounds-check-after-cmp-max.rs @@ -0,0 +1,18 @@ +// in Rust 1.73, -O and opt-level=3 optimizes differently +//@ compile-flags: -C opt-level=3 +#![crate_type = "lib"] + +use std::cmp::max; + +// CHECK-LABEL: @foo +// CHECK-NOT: slice_start_index_len_fail +// CHECK-NOT: unreachable +#[no_mangle] +pub fn foo(v: &mut Vec, size: usize) -> Option<&mut [u8]> { + if v.len() > max(1, size) { + let start = v.len() - size; + Some(&mut v[start..]) + } else { + None + } +} diff --git a/tests/codegen/issues/issue-118392.rs b/tests/codegen/issues/issue-118392.rs new file mode 100644 index 000000000000..2cbb1f8b2043 --- /dev/null +++ b/tests/codegen/issues/issue-118392.rs @@ -0,0 +1,11 @@ +//@ compile-flags: -O +//@ min-llvm-version: 18 +#![crate_type = "lib"] + +// CHECK-LABEL: @div2 +// CHECK: ashr i32 %a, 1 +// CHECK-NEXT: ret i32 +#[no_mangle] +pub fn div2(a: i32) -> i32 { + a.div_euclid(2) +} diff --git a/tests/codegen/issues/issue-119422.rs b/tests/codegen/issues/issue-119422.rs index aa56bfe79acb..682430a79f4a 100644 --- a/tests/codegen/issues/issue-119422.rs +++ b/tests/codegen/issues/issue-119422.rs @@ -5,8 +5,8 @@ //@ only-64bit (because the LLVM type of i64 for usize shows up) #![crate_type = "lib"] -use core::ptr::NonNull; use core::num::NonZero; +use core::ptr::NonNull; // CHECK-LABEL: @check_non_null #[no_mangle] @@ -73,7 +73,7 @@ pub fn isize_try_from_i32(x: NonZero) -> NonZero { // CHECK-LABEL: @u64_from_nonzero_is_not_zero #[no_mangle] -pub fn u64_from_nonzero_is_not_zero(x: NonZero)->bool { +pub fn u64_from_nonzero_is_not_zero(x: NonZero) -> bool { // CHECK-NOT: br // CHECK: ret i1 false // CHECK-NOT: br diff --git a/tests/codegen/issues/issue-122805.rs b/tests/codegen/issues/issue-122805.rs index 6d108ada6dd2..8e03c6c8884a 100644 --- a/tests/codegen/issues/issue-122805.rs +++ b/tests/codegen/issues/issue-122805.rs @@ -39,17 +39,20 @@ // OPT3WINX64-NEXT: store <8 x i16> // CHECK-NEXT: ret void #[no_mangle] -#[cfg(target_endian = "little")] pub fn convert(value: [u16; 8]) -> [u8; 16] { + #[cfg(target_endian = "little")] + let bswap = u16::to_be; + #[cfg(target_endian = "big")] + let bswap = u16::to_le; let addr16 = [ - value[0].to_be(), - value[1].to_be(), - value[2].to_be(), - value[3].to_be(), - value[4].to_be(), - value[5].to_be(), - value[6].to_be(), - value[7].to_be(), + bswap(value[0]), + bswap(value[1]), + bswap(value[2]), + bswap(value[3]), + bswap(value[4]), + bswap(value[5]), + bswap(value[6]), + bswap(value[7]), ]; unsafe { core::mem::transmute::<_, [u8; 16]>(addr16) } } diff --git a/tests/codegen/issues/issue-13018.rs b/tests/codegen/issues/issue-13018.rs index d0a8ce159118..66282dc42749 100644 --- a/tests/codegen/issues/issue-13018.rs +++ b/tests/codegen/issues/issue-13018.rs @@ -6,6 +6,6 @@ use std::rc::Rc; pub fn foo(t: &Rc>) { -// CHECK-NOT: __rust_dealloc + // CHECK-NOT: __rust_dealloc drop(t.clone()); } diff --git a/tests/codegen/issues/issue-32364.rs b/tests/codegen/issues/issue-32364.rs index 50006e3f2181..016981d1947a 100644 --- a/tests/codegen/issues/issue-32364.rs +++ b/tests/codegen/issues/issue-32364.rs @@ -7,10 +7,9 @@ struct Foo; impl Foo { -// CHECK: define internal x86_stdcallcc void @{{.*}}foo{{.*}}() + // CHECK: define internal x86_stdcallcc void @{{.*}}foo{{.*}}() #[inline(never)] - pub extern "stdcall" fn foo() { - } + pub extern "stdcall" fn foo() {} } fn main() { diff --git a/tests/codegen/issues/issue-36010-some-box-is_some.rs b/tests/codegen/issues/issue-36010-some-box-is_some.rs new file mode 100644 index 000000000000..44c01096f15a --- /dev/null +++ b/tests/codegen/issues/issue-36010-some-box-is_some.rs @@ -0,0 +1,28 @@ +#![crate_type = "lib"] + +//@ compile-flags: -O + +use std::mem; + +fn foo(a: &mut T, b: T) -> bool { + let b = Some(mem::replace(a, b)); + let ret = b.is_some(); + mem::forget(b); + return ret; +} + +// CHECK-LABEL: @foo_u32 +// CHECK: store i32 +// CHECK-NEXT: ret i1 true +#[no_mangle] +pub fn foo_u32(a: &mut u32, b: u32) -> bool { + foo(a, b) +} + +// CHECK-LABEL: @foo_box +// CHECK: store ptr +// CHECK-NEXT: ret i1 true +#[no_mangle] +pub fn foo_box(a: &mut Box, b: Box) -> bool { + foo(a, b) +} diff --git a/tests/codegen/issues/issue-37945.rs b/tests/codegen/issues/issue-37945.rs index 756a75e2f0ed..01d1c694ec7c 100644 --- a/tests/codegen/issues/issue-37945.rs +++ b/tests/codegen/issues/issue-37945.rs @@ -9,26 +9,26 @@ use std::slice::Iter; #[no_mangle] pub fn is_empty_1(xs: Iter) -> bool { -// CHECK-LABEL: @is_empty_1( -// CHECK-NEXT: start: -// CHECK-NEXT: [[A:%.*]] = icmp ne ptr {{%xs.0|%xs.1}}, null -// CHECK-NEXT: tail call void @llvm.assume(i1 [[A]]) -// The order between %xs.0 and %xs.1 on the next line doesn't matter -// and different LLVM versions produce different order. -// CHECK-NEXT: [[B:%.*]] = icmp eq ptr {{%xs.0, %xs.1|%xs.1, %xs.0}} -// CHECK-NEXT: ret i1 [[B:%.*]] - {xs}.next().is_none() + // CHECK-LABEL: @is_empty_1( + // CHECK-NEXT: start: + // CHECK-NEXT: [[A:%.*]] = icmp ne ptr {{%xs.0|%xs.1}}, null + // CHECK-NEXT: tail call void @llvm.assume(i1 [[A]]) + // The order between %xs.0 and %xs.1 on the next line doesn't matter + // and different LLVM versions produce different order. + // CHECK-NEXT: [[B:%.*]] = icmp eq ptr {{%xs.0, %xs.1|%xs.1, %xs.0}} + // CHECK-NEXT: ret i1 [[B:%.*]] + { xs }.next().is_none() } #[no_mangle] pub fn is_empty_2(xs: Iter) -> bool { -// CHECK-LABEL: @is_empty_2 -// CHECK-NEXT: start: -// CHECK-NEXT: [[C:%.*]] = icmp ne ptr {{%xs.0|%xs.1}}, null -// CHECK-NEXT: tail call void @llvm.assume(i1 [[C]]) -// The order between %xs.0 and %xs.1 on the next line doesn't matter -// and different LLVM versions produce different order. -// CHECK-NEXT: [[D:%.*]] = icmp eq ptr {{%xs.0, %xs.1|%xs.1, %xs.0}} -// CHECK-NEXT: ret i1 [[D:%.*]] + // CHECK-LABEL: @is_empty_2 + // CHECK-NEXT: start: + // CHECK-NEXT: [[C:%.*]] = icmp ne ptr {{%xs.0|%xs.1}}, null + // CHECK-NEXT: tail call void @llvm.assume(i1 [[C]]) + // The order between %xs.0 and %xs.1 on the next line doesn't matter + // and different LLVM versions produce different order. + // CHECK-NEXT: [[D:%.*]] = icmp eq ptr {{%xs.0, %xs.1|%xs.1, %xs.0}} + // CHECK-NEXT: ret i1 [[D:%.*]] xs.map(|&x| x).next().is_none() } diff --git a/tests/codegen/issues/issue-44056-macos-tls-align.rs b/tests/codegen/issues/issue-44056-macos-tls-align.rs index c99f0b73038b..972b8490d18f 100644 --- a/tests/codegen/issues/issue-44056-macos-tls-align.rs +++ b/tests/codegen/issues/issue-44056-macos-tls-align.rs @@ -1,5 +1,5 @@ // -//@ only-macos +//@ only-apple //@ compile-flags: -O #![crate_type = "rlib"] diff --git a/tests/codegen/issues/issue-45466.rs b/tests/codegen/issues/issue-45466.rs index fc714247dfb6..8a324fa555bb 100644 --- a/tests/codegen/issues/issue-45466.rs +++ b/tests/codegen/issues/issue-45466.rs @@ -1,6 +1,6 @@ //@ compile-flags: -O -#![crate_type="rlib"] +#![crate_type = "rlib"] // CHECK-LABEL: @memzero // CHECK-NOT: store diff --git a/tests/codegen/issues/issue-45964-bounds-check-slice-pos.rs b/tests/codegen/issues/issue-45964-bounds-check-slice-pos.rs index b7568bea4d0a..ea9288564e9d 100644 --- a/tests/codegen/issues/issue-45964-bounds-check-slice-pos.rs +++ b/tests/codegen/issues/issue-45964-bounds-check-slice-pos.rs @@ -3,7 +3,7 @@ //@ compile-flags: -O -#![crate_type="rlib"] +#![crate_type = "rlib"] // CHECK-LABEL: @test #[no_mangle] diff --git a/tests/codegen/issues/issue-47278.rs b/tests/codegen/issues/issue-47278.rs index 9076274f45e9..4f0a5bdf36f4 100644 --- a/tests/codegen/issues/issue-47278.rs +++ b/tests/codegen/issues/issue-47278.rs @@ -1,9 +1,11 @@ // -C no-prepopulate-passes -#![crate_type="staticlib"] +#![crate_type = "staticlib"] #[repr(C)] pub struct Foo(u64); // CHECK: define {{.*}} @foo( #[no_mangle] -pub extern "C" fn foo(_: Foo) -> Foo { loop {} } +pub extern "C" fn foo(_: Foo) -> Foo { + loop {} +} diff --git a/tests/codegen/issues/issue-47442.rs b/tests/codegen/issues/issue-47442.rs index 6944336d3356..445234e55add 100644 --- a/tests/codegen/issues/issue-47442.rs +++ b/tests/codegen/issues/issue-47442.rs @@ -4,7 +4,7 @@ // CHECK-NOT: Unwind #![feature(test)] -#![crate_type="rlib"] +#![crate_type = "rlib"] extern crate test; diff --git a/tests/codegen/issues/issue-56267-2.rs b/tests/codegen/issues/issue-56267-2.rs index ced0d2d63bb5..98e3732777e3 100644 --- a/tests/codegen/issues/issue-56267-2.rs +++ b/tests/codegen/issues/issue-56267-2.rs @@ -1,6 +1,6 @@ //@ compile-flags: -C no-prepopulate-passes -#![crate_type="rlib"] +#![crate_type = "rlib"] #[allow(dead_code)] pub struct Foo { diff --git a/tests/codegen/issues/issue-56267.rs b/tests/codegen/issues/issue-56267.rs index fc3754f2d990..cabcc298482d 100644 --- a/tests/codegen/issues/issue-56267.rs +++ b/tests/codegen/issues/issue-56267.rs @@ -1,6 +1,6 @@ //@ compile-flags: -C no-prepopulate-passes -#![crate_type="rlib"] +#![crate_type = "rlib"] #[allow(dead_code)] pub struct Foo { diff --git a/tests/codegen/issues/issue-56927.rs b/tests/codegen/issues/issue-56927.rs index e4a0a1791418..a40718689b3e 100644 --- a/tests/codegen/issues/issue-56927.rs +++ b/tests/codegen/issues/issue-56927.rs @@ -1,6 +1,6 @@ //@ compile-flags: -C no-prepopulate-passes -#![crate_type="rlib"] +#![crate_type = "rlib"] #[repr(align(16))] pub struct S { diff --git a/tests/codegen/issues/issue-68667-unwrap-combinators.rs b/tests/codegen/issues/issue-68667-unwrap-combinators.rs new file mode 100644 index 000000000000..6bd4c566a0c2 --- /dev/null +++ b/tests/codegen/issues/issue-68667-unwrap-combinators.rs @@ -0,0 +1,15 @@ +#![crate_type = "lib"] + +//@ compile-flags: -O + +// MIR inlining now optimizes this code. + +// CHECK-LABEL: @unwrap_combinators +// CHECK: icmp +// CHECK-NEXT: icmp +// CHECK-NEXT: select i1 +// CHECK-NEXT: ret i1 +#[no_mangle] +pub fn unwrap_combinators(a: Option, b: i32) -> bool { + a.map(|t| t >= b).unwrap_or(false) +} diff --git a/tests/codegen/issues/issue-73031.rs b/tests/codegen/issues/issue-73031.rs index 61a269999e90..db9c6d6db233 100644 --- a/tests/codegen/issues/issue-73031.rs +++ b/tests/codegen/issues/issue-73031.rs @@ -12,11 +12,7 @@ pub enum All { // CHECK-LABEL: @issue_73031 #[no_mangle] pub fn issue_73031(a: &mut All, q: i32) -> i32 { - *a = if q == 5 { - All::Foo - } else { - All::Bar - }; + *a = if q == 5 { All::Foo } else { All::Bar }; match *a { // CHECK-NOT: panic All::None => panic!(), diff --git a/tests/codegen/issues/issue-73258.rs b/tests/codegen/issues/issue-73258.rs index 48f14fe2dfe3..e5c622b5656b 100644 --- a/tests/codegen/issues/issue-73258.rs +++ b/tests/codegen/issues/issue-73258.rs @@ -7,7 +7,10 @@ #[derive(Clone, Copy)] #[repr(u8)] pub enum Foo { - A, B, C, D, + A, + B, + C, + D, } // CHECK-LABEL: @issue_73258( diff --git a/tests/codegen/issues/issue-73338-effecient-cmp.rs b/tests/codegen/issues/issue-73338-effecient-cmp.rs index a64eb56f9030..71641a5457b1 100644 --- a/tests/codegen/issues/issue-73338-effecient-cmp.rs +++ b/tests/codegen/issues/issue-73338-effecient-cmp.rs @@ -4,7 +4,7 @@ //@ compile-flags: -Copt-level=3 -#![crate_type="lib"] +#![crate_type = "lib"] #[repr(u32)] #[derive(Copy, Clone, Eq, PartialEq, PartialOrd)] @@ -15,25 +15,25 @@ pub enum Foo { } #[no_mangle] -pub fn compare_less(a: Foo, b: Foo)->bool{ +pub fn compare_less(a: Foo, b: Foo) -> bool { // CHECK-NOT: br {{.*}} a < b } #[no_mangle] -pub fn compare_le(a: Foo, b: Foo)->bool{ +pub fn compare_le(a: Foo, b: Foo) -> bool { // CHECK-NOT: br {{.*}} a <= b } #[no_mangle] -pub fn compare_ge(a: Foo, b: Foo)->bool{ +pub fn compare_ge(a: Foo, b: Foo) -> bool { // CHECK-NOT: br {{.*}} a >= b } #[no_mangle] -pub fn compare_greater(a: Foo, b: Foo)->bool{ +pub fn compare_greater(a: Foo, b: Foo) -> bool { // CHECK-NOT: br {{.*}} a > b } diff --git a/tests/codegen/issues/issue-73396-bounds-check-after-position.rs b/tests/codegen/issues/issue-73396-bounds-check-after-position.rs index ef4538ac84e1..9b3b1318ced1 100644 --- a/tests/codegen/issues/issue-73396-bounds-check-after-position.rs +++ b/tests/codegen/issues/issue-73396-bounds-check-after-position.rs @@ -12,11 +12,7 @@ pub fn position_slice_to_no_bounds_check(s: &[u8]) -> &[u8] { // CHECK-NOT: slice_end_index_len_fail // CHECK-NOT: panic_bounds_check // CHECK-NOT: unreachable - if let Some(idx) = s.iter().position(|b| *b == b'\\') { - &s[..idx] - } else { - s - } + if let Some(idx) = s.iter().position(|b| *b == b'\\') { &s[..idx] } else { s } } // CHECK-LABEL: @position_slice_from_no_bounds_check @@ -27,11 +23,7 @@ pub fn position_slice_from_no_bounds_check(s: &[u8]) -> &[u8] { // CHECK-NOT: slice_end_index_len_fail // CHECK-NOT: panic_bounds_check // CHECK-NOT: unreachable - if let Some(idx) = s.iter().position(|b| *b == b'\\') { - &s[idx..] - } else { - s - } + if let Some(idx) = s.iter().position(|b| *b == b'\\') { &s[idx..] } else { s } } // CHECK-LABEL: @position_index_no_bounds_check @@ -42,11 +34,7 @@ pub fn position_index_no_bounds_check(s: &[u8]) -> u8 { // CHECK-NOT: slice_end_index_len_fail // CHECK-NOT: panic_bounds_check // CHECK-NOT: unreachable - if let Some(idx) = s.iter().position(|b| *b == b'\\') { - s[idx] - } else { - 42 - } + if let Some(idx) = s.iter().position(|b| *b == b'\\') { s[idx] } else { 42 } } // CHECK-LABEL: @rposition_slice_to_no_bounds_check #[no_mangle] @@ -56,11 +44,7 @@ pub fn rposition_slice_to_no_bounds_check(s: &[u8]) -> &[u8] { // CHECK-NOT: slice_end_index_len_fail // CHECK-NOT: panic_bounds_check // CHECK-NOT: unreachable - if let Some(idx) = s.iter().rposition(|b| *b == b'\\') { - &s[..idx] - } else { - s - } + if let Some(idx) = s.iter().rposition(|b| *b == b'\\') { &s[..idx] } else { s } } // CHECK-LABEL: @rposition_slice_from_no_bounds_check @@ -71,11 +55,7 @@ pub fn rposition_slice_from_no_bounds_check(s: &[u8]) -> &[u8] { // CHECK-NOT: slice_end_index_len_fail // CHECK-NOT: panic_bounds_check // CHECK-NOT: unreachable - if let Some(idx) = s.iter().rposition(|b| *b == b'\\') { - &s[idx..] - } else { - s - } + if let Some(idx) = s.iter().rposition(|b| *b == b'\\') { &s[idx..] } else { s } } // CHECK-LABEL: @rposition_index_no_bounds_check @@ -86,9 +66,5 @@ pub fn rposition_index_no_bounds_check(s: &[u8]) -> u8 { // CHECK-NOT: slice_end_index_len_fail // CHECK-NOT: panic_bounds_check // CHECK-NOT: unreachable - if let Some(idx) = s.iter().rposition(|b| *b == b'\\') { - s[idx] - } else { - 42 - } + if let Some(idx) = s.iter().rposition(|b| *b == b'\\') { s[idx] } else { 42 } } diff --git a/tests/codegen/issues/issue-74938-array-split-at.rs b/tests/codegen/issues/issue-74938-array-split-at.rs new file mode 100644 index 000000000000..2675e404ced3 --- /dev/null +++ b/tests/codegen/issues/issue-74938-array-split-at.rs @@ -0,0 +1,14 @@ +//@ compile-flags: -O + +#![crate_type = "lib"] + +const N: usize = 3; +pub type T = u8; + +// CHECK-LABEL: @split_multiple +// CHECK-NOT: unreachable +#[no_mangle] +pub fn split_multiple(slice: &[T]) -> (&[T], &[T]) { + let len = slice.len() / N; + slice.split_at(len * N) +} diff --git a/tests/codegen/issues/issue-75546.rs b/tests/codegen/issues/issue-75546.rs index 992ef97d6242..1132c8ab5093 100644 --- a/tests/codegen/issues/issue-75546.rs +++ b/tests/codegen/issues/issue-75546.rs @@ -9,7 +9,9 @@ pub fn issue_75546() { let mut i = 1u32; while i < u32::MAX { // CHECK-NOT: panic - if i == 0 { panic!(); } + if i == 0 { + panic!(); + } i += 1; } } diff --git a/tests/codegen/issues/issue-77812.rs b/tests/codegen/issues/issue-77812.rs index b9ce0a4f7db2..bf84ac21b16d 100644 --- a/tests/codegen/issues/issue-77812.rs +++ b/tests/codegen/issues/issue-77812.rs @@ -10,7 +10,7 @@ pub enum Variant { Two, } -extern { +extern "C" { fn exf1(); fn exf2(); } diff --git a/tests/codegen/issues/issue-84268.rs b/tests/codegen/issues/issue-84268.rs index 1e3950609b33..5e852133ed3d 100644 --- a/tests/codegen/issues/issue-84268.rs +++ b/tests/codegen/issues/issue-84268.rs @@ -17,7 +17,5 @@ pub struct M([i32; 4]); pub fn is_infinite(v: V) -> M { // CHECK: fabs // CHECK: cmp oeq - unsafe { - simd_eq(simd_fabs(v), V([f32::INFINITY; 4])) - } + unsafe { simd_eq(simd_fabs(v), V([f32::INFINITY; 4])) } } diff --git a/tests/codegen/issues/issue-93036-assert-index.rs b/tests/codegen/issues/issue-93036-assert-index.rs new file mode 100644 index 000000000000..7a2ea0872668 --- /dev/null +++ b/tests/codegen/issues/issue-93036-assert-index.rs @@ -0,0 +1,14 @@ +//@ compile-flags: -O + +#![crate_type = "lib"] + +#[no_mangle] +// CHECK-LABEL: @foo +// CHECK-NOT: unreachable +pub fn foo(arr: &mut [u32]) { + for i in 0..arr.len() { + for j in 0..i { + assert!(j < arr.len()); + } + } +} diff --git a/tests/codegen/issues/issue-96497-slice-size-nowrap.rs b/tests/codegen/issues/issue-96497-slice-size-nowrap.rs index c2b262b3334c..f922462cc279 100644 --- a/tests/codegen/issues/issue-96497-slice-size-nowrap.rs +++ b/tests/codegen/issues/issue-96497-slice-size-nowrap.rs @@ -4,7 +4,7 @@ //@ compile-flags: -O -#![crate_type="lib"] +#![crate_type = "lib"] // CHECK-LABEL: @simple_size_of_nowrap #[no_mangle] diff --git a/tests/codegen/issues/issue-99960.rs b/tests/codegen/issues/issue-99960.rs index 075495061750..9029121d35f4 100644 --- a/tests/codegen/issues/issue-99960.rs +++ b/tests/codegen/issues/issue-99960.rs @@ -6,9 +6,5 @@ pub fn test(dividend: i64, divisor: i64) -> Option { // CHECK-LABEL: @test( // CHECK-NOT: panic - if dividend > i64::min_value() && divisor != 0 { - Some(dividend / divisor) - } else { - None - } + if dividend > i64::min_value() && divisor != 0 { Some(dividend / divisor) } else { None } } diff --git a/tests/codegen/lib-optimizations/iter-sum.rs b/tests/codegen/lib-optimizations/iter-sum.rs index b563a6debb52..ea8c916bfc1b 100644 --- a/tests/codegen/lib-optimizations/iter-sum.rs +++ b/tests/codegen/lib-optimizations/iter-sum.rs @@ -2,7 +2,6 @@ //@ only-x86_64 (vectorization varies between architectures) #![crate_type = "lib"] - // Ensure that slice + take + sum gets vectorized. // Currently this relies on the slice::Iter::try_fold implementation // CHECK-LABEL: @slice_take_sum diff --git a/tests/codegen/lifetime_start_end.rs b/tests/codegen/lifetime_start_end.rs index 38e878451588..99d37c25dcac 100644 --- a/tests/codegen/lifetime_start_end.rs +++ b/tests/codegen/lifetime_start_end.rs @@ -8,27 +8,27 @@ pub fn test() { let a = 0u8; &a; // keep variable in an alloca -// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, ptr %a) + // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, ptr %a) { let b = &Some(a); &b; // keep variable in an alloca -// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, {{.*}}) + // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, {{.*}}) -// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, {{.*}}) + // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, {{.*}}) -// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, {{.*}}) + // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, {{.*}}) -// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, {{.*}}) + // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, {{.*}}) } let c = 1u8; &c; // keep variable in an alloca -// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, ptr %c) + // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, ptr %c) -// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, ptr %c) + // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, ptr %c) -// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, ptr %a) + // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, ptr %a) } diff --git a/tests/codegen/link-dead-code.rs b/tests/codegen/link-dead-code.rs index 7769622233f6..93e1d84d9c70 100644 --- a/tests/codegen/link-dead-code.rs +++ b/tests/codegen/link-dead-code.rs @@ -8,15 +8,21 @@ // CHECK-LABEL: ; link_dead_code::const_fn // CHECK-NEXT: ; Function Attrs: // CHECK-NEXT: define hidden -const fn const_fn() -> i32 { 1 } +const fn const_fn() -> i32 { + 1 +} // CHECK-LABEL: ; link_dead_code::inline_fn // CHECK-NEXT: ; Function Attrs: // CHECK-NEXT: define hidden #[inline] -fn inline_fn() -> i32 { 2 } +fn inline_fn() -> i32 { + 2 +} // CHECK-LABEL: ; link_dead_code::private_fn // CHECK-NEXT: ; Function Attrs: // CHECK-NEXT: define hidden -fn private_fn() -> i32 { 3 } +fn private_fn() -> i32 { + 3 +} diff --git a/tests/codegen/link_section.rs b/tests/codegen/link_section.rs index 281d3fb99d46..196f5edb7d63 100644 --- a/tests/codegen/link_section.rs +++ b/tests/codegen/link_section.rs @@ -16,7 +16,7 @@ pub static VAR1: u32 = 0x01000000; pub enum E { A(u32), - B(f32) + B(f32), } // CHECK: @VAR2 = {{(dso_local )?}}constant {{.*}}, section ".test_two" diff --git a/tests/codegen/loongarch-abi/loongarch64-lp64d-abi.rs b/tests/codegen/loongarch-abi/loongarch64-lp64d-abi.rs index d978f2d25254..cba1a980caa7 100644 --- a/tests/codegen/loongarch-abi/loongarch64-lp64d-abi.rs +++ b/tests/codegen/loongarch-abi/loongarch64-lp64d-abi.rs @@ -6,9 +6,12 @@ #![no_std] #![no_core] -#[lang="sized"] trait Sized { } -#[lang="freeze"] trait Freeze { } -#[lang="copy"] trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} // CHECK: define void @f_fpr_tracking(double %0, double %1, double %2, double %3, double %4, double %5, double %6, double %7, i8 noundef zeroext %i) #[no_mangle] diff --git a/tests/codegen/lto-removes-invokes.rs b/tests/codegen/lto-removes-invokes.rs index f0102c25d5bd..3217c239bf78 100644 --- a/tests/codegen/lto-removes-invokes.rs +++ b/tests/codegen/lto-removes-invokes.rs @@ -10,8 +10,8 @@ fn main() { fn foo() { let _a = Box::new(3); bar(); -// CHECK-LABEL: define dso_local void @foo -// CHECK: call void @bar + // CHECK-LABEL: define dso_local void @foo + // CHECK: call void @bar } #[inline(never)] diff --git a/tests/codegen/macos/i686-macosx-deployment-target.rs b/tests/codegen/macos/i686-macosx-deployment-target.rs index b854476de418..389434da1f67 100644 --- a/tests/codegen/macos/i686-macosx-deployment-target.rs +++ b/tests/codegen/macos/i686-macosx-deployment-target.rs @@ -8,12 +8,12 @@ #![feature(no_core, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="freeze"] -trait Freeze { } -#[lang="copy"] -trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} #[repr(C)] pub struct Bool { diff --git a/tests/codegen/macos/i686-no-macosx-deployment-target.rs b/tests/codegen/macos/i686-no-macosx-deployment-target.rs index a49a3467e7a0..4c6b7656e593 100644 --- a/tests/codegen/macos/i686-no-macosx-deployment-target.rs +++ b/tests/codegen/macos/i686-no-macosx-deployment-target.rs @@ -8,12 +8,12 @@ #![feature(no_core, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="freeze"] -trait Freeze { } -#[lang="copy"] -trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} #[repr(C)] pub struct Bool { diff --git a/tests/codegen/macos/x86_64-macosx-deployment-target.rs b/tests/codegen/macos/x86_64-macosx-deployment-target.rs index eac989c29541..a40deca24bbe 100644 --- a/tests/codegen/macos/x86_64-macosx-deployment-target.rs +++ b/tests/codegen/macos/x86_64-macosx-deployment-target.rs @@ -8,12 +8,12 @@ #![feature(no_core, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="freeze"] -trait Freeze { } -#[lang="copy"] -trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} #[repr(C)] pub struct Bool { diff --git a/tests/codegen/macos/x86_64-no-macosx-deployment-target.rs b/tests/codegen/macos/x86_64-no-macosx-deployment-target.rs index ed294cf4e3dc..26d519ef1a6d 100644 --- a/tests/codegen/macos/x86_64-no-macosx-deployment-target.rs +++ b/tests/codegen/macos/x86_64-no-macosx-deployment-target.rs @@ -8,12 +8,12 @@ #![feature(no_core, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="freeze"] -trait Freeze { } -#[lang="copy"] -trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} #[repr(C)] pub struct Bool { diff --git a/tests/codegen/mainsubprogram.rs b/tests/codegen/mainsubprogram.rs index c1933b2b3904..12b24c90229e 100644 --- a/tests/codegen/mainsubprogram.rs +++ b/tests/codegen/mainsubprogram.rs @@ -2,7 +2,7 @@ // before 4.0, formerly backported to the Rust LLVM fork. //@ ignore-windows -//@ ignore-macos +//@ ignore-apple //@ ignore-wasi //@ compile-flags: -g -C no-prepopulate-passes @@ -10,5 +10,4 @@ // CHECK-LABEL: @main // CHECK: {{.*}}DISubprogram{{.*}}name: "main",{{.*}}DI{{(SP)?}}FlagMainSubprogram{{.*}} -pub fn main() { -} +pub fn main() {} diff --git a/tests/codegen/mainsubprogramstart.rs b/tests/codegen/mainsubprogramstart.rs index 84d680b9bff9..20741791db5c 100644 --- a/tests/codegen/mainsubprogramstart.rs +++ b/tests/codegen/mainsubprogramstart.rs @@ -1,5 +1,5 @@ //@ ignore-windows -//@ ignore-macos +//@ ignore-apple //@ ignore-wasi wasi codegens the main symbol differently //@ compile-flags: -g -C no-prepopulate-passes diff --git a/tests/codegen/match-optimized.rs b/tests/codegen/match-optimized.rs index 5cecafb9f29c..d6893be0b7be 100644 --- a/tests/codegen/match-optimized.rs +++ b/tests/codegen/match-optimized.rs @@ -11,23 +11,23 @@ pub enum E { // CHECK-LABEL: @exhaustive_match #[no_mangle] pub fn exhaustive_match(e: E) -> u8 { -// CHECK: switch{{.*}}, label %[[OTHERWISE:[a-zA-Z0-9_]+]] [ -// CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[A:[a-zA-Z0-9_]+]] -// CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[B:[a-zA-Z0-9_]+]] -// CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[C:[a-zA-Z0-9_]+]] -// CHECK-NEXT: ] -// CHECK: [[OTHERWISE]]: -// CHECK-NEXT: unreachable -// -// CHECK: [[A]]: -// CHECK-NEXT: store i8 0, ptr %_0, align 1 -// CHECK-NEXT: br label %[[EXIT:[a-zA-Z0-9_]+]] -// CHECK: [[B]]: -// CHECK-NEXT: store i8 1, ptr %_0, align 1 -// CHECK-NEXT: br label %[[EXIT]] -// CHECK: [[C]]: -// CHECK-NEXT: store i8 3, ptr %_0, align 1 -// CHECK-NEXT: br label %[[EXIT]] + // CHECK: switch{{.*}}, label %[[OTHERWISE:[a-zA-Z0-9_]+]] [ + // CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[A:[a-zA-Z0-9_]+]] + // CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[B:[a-zA-Z0-9_]+]] + // CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[C:[a-zA-Z0-9_]+]] + // CHECK-NEXT: ] + // CHECK: [[OTHERWISE]]: + // CHECK-NEXT: unreachable + // + // CHECK: [[A]]: + // CHECK-NEXT: store i8 0, ptr %_0, align 1 + // CHECK-NEXT: br label %[[EXIT:[a-zA-Z0-9_]+]] + // CHECK: [[B]]: + // CHECK-NEXT: store i8 1, ptr %_0, align 1 + // CHECK-NEXT: br label %[[EXIT]] + // CHECK: [[C]]: + // CHECK-NEXT: store i8 3, ptr %_0, align 1 + // CHECK-NEXT: br label %[[EXIT]] match e { E::A => 0, E::B => 1, diff --git a/tests/codegen/match-optimizes-away.rs b/tests/codegen/match-optimizes-away.rs index 55ece89cec21..82ab5718b375 100644 --- a/tests/codegen/match-optimizes-away.rs +++ b/tests/codegen/match-optimizes-away.rs @@ -1,11 +1,20 @@ // //@ compile-flags: -O -#![crate_type="lib"] +#![crate_type = "lib"] -pub enum Three { A, B, C } +pub enum Three { + A, + B, + C, +} #[repr(u16)] -pub enum Four { A, B, C, D } +pub enum Four { + A, + B, + C, + D, +} #[no_mangle] pub fn three_valued(x: Three) -> Three { diff --git a/tests/codegen/mir_zst_stores.rs b/tests/codegen/mir_zst_stores.rs index 667273c2f0f7..ff1d429cffdb 100644 --- a/tests/codegen/mir_zst_stores.rs +++ b/tests/codegen/mir_zst_stores.rs @@ -4,7 +4,9 @@ use std::marker::PhantomData; #[derive(Copy, Clone)] -struct Zst { phantom: PhantomData } +struct Zst { + phantom: PhantomData, +} // CHECK-LABEL: @mir // CHECK-NOT: store{{.*}}undef diff --git a/tests/codegen/naked-fn/naked-functions.rs b/tests/codegen/naked-fn/naked-functions.rs index 3c426825537b..307745a921cf 100644 --- a/tests/codegen/naked-fn/naked-functions.rs +++ b/tests/codegen/naked-fn/naked-functions.rs @@ -14,8 +14,7 @@ pub unsafe extern "C" fn naked_empty() { // CHECK-NEXT: {{.+}}: // CHECK-NEXT: call void asm // CHECK-NEXT: unreachable - asm!("ret", - options(noreturn)); + asm!("ret", options(noreturn)); } // CHECK: Function Attrs: naked @@ -26,7 +25,5 @@ pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize // CHECK-NEXT: {{.+}}: // CHECK-NEXT: call void asm // CHECK-NEXT: unreachable - asm!("lea rax, [rdi + rsi]", - "ret", - options(noreturn)); + asm!("lea rax, [rdi + rsi]", "ret", options(noreturn)); } diff --git a/tests/codegen/naked-fn/naked-nocoverage.rs b/tests/codegen/naked-fn/naked-nocoverage.rs index e8d3b5834fa3..d73c5b7fd26d 100644 --- a/tests/codegen/naked-fn/naked-nocoverage.rs +++ b/tests/codegen/naked-fn/naked-nocoverage.rs @@ -2,7 +2,7 @@ // Regression test for issue #105170. // //@ needs-asm-support -//@ needs-profiler-support +//@ compile-flags: -Zno-profiler-runtime //@ compile-flags: -Cinstrument-coverage #![crate_type = "lib"] #![feature(naked_functions)] diff --git a/tests/codegen/no-assumes-on-casts.rs b/tests/codegen/no-assumes-on-casts.rs index b9c264daa2d1..9c00dc2c0155 100644 --- a/tests/codegen/no-assumes-on-casts.rs +++ b/tests/codegen/no-assumes-on-casts.rs @@ -6,14 +6,14 @@ #[no_mangle] pub fn fna(a: i16) -> i32 { a as i32 -// CHECK-NOT: assume -// CHECK: sext + // CHECK-NOT: assume + // CHECK: sext } // CHECK-LABEL: fnb #[no_mangle] pub fn fnb(a: u16) -> u32 { a as u32 -// CHECK-NOT: assume -// CHECK: zext + // CHECK-NOT: assume + // CHECK: zext } diff --git a/tests/codegen/noalias-freeze.rs b/tests/codegen/noalias-freeze.rs new file mode 100644 index 000000000000..32c840140260 --- /dev/null +++ b/tests/codegen/noalias-freeze.rs @@ -0,0 +1,23 @@ +//@ compile-flags: -Copt-level=1 + +// References returned by a Frozen pointer type +// could be marked as "noalias", which caused miscompilation errors. +// This test runs the most minimal possible code that can reproduce this bug, +// and checks that noalias does not appear. +// See https://github.com/rust-lang/rust/issues/46239 + +#![crate_type = "lib"] + +fn project(x: &(T,)) -> &T { + &x.0 +} + +fn dummy() {} + +// CHECK-LABEL: @foo( +// CHECK-NOT: noalias +#[no_mangle] +pub fn foo() { + let f = (dummy as fn(),); + (*project(&f))(); +} diff --git a/tests/codegen/noalias-unpin.rs b/tests/codegen/noalias-unpin.rs index 546b1edb7b61..630a62020c11 100644 --- a/tests/codegen/noalias-unpin.rs +++ b/tests/codegen/noalias-unpin.rs @@ -4,7 +4,7 @@ pub struct SelfRef { self_ref: *mut SelfRef, - _pin: std::marker::PhantomPinned + _pin: std::marker::PhantomPinned, } // CHECK-LABEL: @test_self_ref( diff --git a/tests/codegen/non-terminate/infinite-recursion.rs b/tests/codegen/non-terminate/infinite-recursion.rs index 804704c0292e..19123639896b 100644 --- a/tests/codegen/non-terminate/infinite-recursion.rs +++ b/tests/codegen/non-terminate/infinite-recursion.rs @@ -1,7 +1,6 @@ //@ compile-flags: -C opt-level=3 #![crate_type = "lib"] - #![allow(unconditional_recursion)] // CHECK-LABEL: @infinite_recursion diff --git a/tests/codegen/noreturnflag.rs b/tests/codegen/noreturnflag.rs index a8f08628986a..d9bb30b2703a 100644 --- a/tests/codegen/noreturnflag.rs +++ b/tests/codegen/noreturnflag.rs @@ -4,7 +4,7 @@ #[no_mangle] pub fn foo() -> ! { -// CHECK: @foo() unnamed_addr #0 + // CHECK: @foo() unnamed_addr #0 loop {} } @@ -12,7 +12,7 @@ pub enum EmptyEnum {} #[no_mangle] pub fn bar() -> EmptyEnum { -// CHECK: @bar() unnamed_addr #0 + // CHECK: @bar() unnamed_addr #0 loop {} } diff --git a/tests/codegen/nounwind.rs b/tests/codegen/nounwind.rs index 2b237ef01208..464bc2535c2b 100644 --- a/tests/codegen/nounwind.rs +++ b/tests/codegen/nounwind.rs @@ -10,7 +10,7 @@ extern crate nounwind; #[no_mangle] pub fn foo() { nounwind::bar(); -// CHECK: @foo() unnamed_addr #0 -// CHECK: @bar() unnamed_addr #0 -// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} } + // CHECK: @foo() unnamed_addr #0 + // CHECK: @bar() unnamed_addr #0 + // CHECK: attributes #0 = { {{.*}}nounwind{{.*}} } } diff --git a/tests/codegen/optimize-attr-1.rs b/tests/codegen/optimize-attr-1.rs index c8e68779aae6..3aee44791e0e 100644 --- a/tests/codegen/optimize-attr-1.rs +++ b/tests/codegen/optimize-attr-1.rs @@ -4,7 +4,7 @@ //@[SPEED-OPT] compile-flags: -Copt-level=3 -Ccodegen-units=1 #![feature(optimize_attribute)] -#![crate_type="rlib"] +#![crate_type = "rlib"] // CHECK-LABEL: define{{.*}}i32 @nothing // CHECK-SAME: [[NOTHING_ATTRS:#[0-9]+]] diff --git a/tests/codegen/option-niche-eq.rs b/tests/codegen/option-niche-eq.rs index 25ea5dd595c3..4d3a7ce3764b 100644 --- a/tests/codegen/option-niche-eq.rs +++ b/tests/codegen/option-niche-eq.rs @@ -4,8 +4,8 @@ extern crate core; use core::cmp::Ordering; -use core::ptr::NonNull; use core::num::NonZero; +use core::ptr::NonNull; // CHECK-LABEL: @non_zero_eq #[no_mangle] diff --git a/tests/codegen/packed.rs b/tests/codegen/packed.rs index 5142df9c4881..790d618b2ead 100644 --- a/tests/codegen/packed.rs +++ b/tests/codegen/packed.rs @@ -6,20 +6,20 @@ #[repr(packed)] pub struct Packed1 { dealign: u8, - data: u32 + data: u32, } #[repr(packed(2))] pub struct Packed2 { dealign: u8, - data: u32 + data: u32, } // CHECK-LABEL: @write_pkd1 #[no_mangle] pub fn write_pkd1(pkd: &mut Packed1) -> u32 { -// CHECK: %{{.*}} = load i32, ptr %{{.*}}, align 1 -// CHECK: store i32 42, ptr %{{.*}}, align 1 + // CHECK: %{{.*}} = load i32, ptr %{{.*}}, align 1 + // CHECK: store i32 42, ptr %{{.*}}, align 1 let result = pkd.data; pkd.data = 42; result @@ -28,8 +28,8 @@ pub fn write_pkd1(pkd: &mut Packed1) -> u32 { // CHECK-LABEL: @write_pkd2 #[no_mangle] pub fn write_pkd2(pkd: &mut Packed2) -> u32 { -// CHECK: %{{.*}} = load i32, ptr %{{.*}}, align 2 -// CHECK: store i32 42, ptr %{{.*}}, align 2 + // CHECK: %{{.*}} = load i32, ptr %{{.*}}, align 2 + // CHECK: store i32 42, ptr %{{.*}}, align 2 let result = pkd.data; pkd.data = 42; result @@ -39,21 +39,21 @@ pub struct Array([i32; 8]); #[repr(packed)] pub struct BigPacked1 { dealign: u8, - data: Array + data: Array, } #[repr(packed(2))] pub struct BigPacked2 { dealign: u8, - data: Array + data: Array, } // CHECK-LABEL: @call_pkd1 #[no_mangle] pub fn call_pkd1(f: fn() -> Array) -> BigPacked1 { -// CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca [32 x i8] -// CHECK: call void %{{.*}}(ptr noalias nocapture noundef sret{{.*}} dereferenceable(32) [[ALLOCA]]) -// CHECK: call void @llvm.memcpy.{{.*}}(ptr align 1 %{{.*}}, ptr align 4 %{{.*}}, i{{[0-9]+}} 32, i1 false) + // CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca [32 x i8] + // CHECK: call void %{{.*}}(ptr noalias nocapture noundef sret{{.*}} dereferenceable(32) [[ALLOCA]]) + // CHECK: call void @llvm.memcpy.{{.*}}(ptr align 1 %{{.*}}, ptr align 4 %{{.*}}, i{{[0-9]+}} 32, i1 false) // check that calls whose destination is a field of a packed struct // go through an alloca rather than calling the function with an // unaligned destination. @@ -63,9 +63,9 @@ pub fn call_pkd1(f: fn() -> Array) -> BigPacked1 { // CHECK-LABEL: @call_pkd2 #[no_mangle] pub fn call_pkd2(f: fn() -> Array) -> BigPacked2 { -// CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca [32 x i8] -// CHECK: call void %{{.*}}(ptr noalias nocapture noundef sret{{.*}} dereferenceable(32) [[ALLOCA]]) -// CHECK: call void @llvm.memcpy.{{.*}}(ptr align 2 %{{.*}}, ptr align 4 %{{.*}}, i{{[0-9]+}} 32, i1 false) + // CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca [32 x i8] + // CHECK: call void %{{.*}}(ptr noalias nocapture noundef sret{{.*}} dereferenceable(32) [[ALLOCA]]) + // CHECK: call void @llvm.memcpy.{{.*}}(ptr align 2 %{{.*}}, ptr align 4 %{{.*}}, i{{[0-9]+}} 32, i1 false) // check that calls whose destination is a field of a packed struct // go through an alloca rather than calling the function with an // unaligned destination. @@ -119,14 +119,14 @@ pub struct Packed2Pair(u8, u32); // CHECK-LABEL: @pkd1_pair #[no_mangle] pub fn pkd1_pair(pair1: &mut Packed1Pair, pair2: &mut Packed1Pair) { -// CHECK: call void @llvm.memcpy.{{.*}}(ptr align 1 %{{.*}}, ptr align 1 %{{.*}}, i{{[0-9]+}} 5, i1 false) + // CHECK: call void @llvm.memcpy.{{.*}}(ptr align 1 %{{.*}}, ptr align 1 %{{.*}}, i{{[0-9]+}} 5, i1 false) *pair2 = *pair1; } // CHECK-LABEL: @pkd2_pair #[no_mangle] pub fn pkd2_pair(pair1: &mut Packed2Pair, pair2: &mut Packed2Pair) { -// CHECK: call void @llvm.memcpy.{{.*}}(ptr align 2 %{{.*}}, ptr align 2 %{{.*}}, i{{[0-9]+}} 6, i1 false) + // CHECK: call void @llvm.memcpy.{{.*}}(ptr align 2 %{{.*}}, ptr align 2 %{{.*}}, i{{[0-9]+}} 6, i1 false) *pair2 = *pair1; } @@ -141,13 +141,13 @@ pub struct Packed2NestedPair((u32, u32)); // CHECK-LABEL: @pkd1_nested_pair #[no_mangle] pub fn pkd1_nested_pair(pair1: &mut Packed1NestedPair, pair2: &mut Packed1NestedPair) { -// CHECK: call void @llvm.memcpy.{{.*}}(ptr align 1 %{{.*}}, ptr align 1 %{{.*}}, i{{[0-9]+}} 8, i1 false) + // CHECK: call void @llvm.memcpy.{{.*}}(ptr align 1 %{{.*}}, ptr align 1 %{{.*}}, i{{[0-9]+}} 8, i1 false) *pair2 = *pair1; } // CHECK-LABEL: @pkd2_nested_pair #[no_mangle] pub fn pkd2_nested_pair(pair1: &mut Packed2NestedPair, pair2: &mut Packed2NestedPair) { -// CHECK: call void @llvm.memcpy.{{.*}}(ptr align 2 %{{.*}}, ptr align 2 %{{.*}}, i{{[0-9]+}} 8, i1 false) + // CHECK: call void @llvm.memcpy.{{.*}}(ptr align 2 %{{.*}}, ptr align 2 %{{.*}}, i{{[0-9]+}} 8, i1 false) *pair2 = *pair1; } diff --git a/tests/codegen/panic-abort-windows.rs b/tests/codegen/panic-abort-windows.rs index 71caa1b3d2af..eb61e649f04a 100644 --- a/tests/codegen/panic-abort-windows.rs +++ b/tests/codegen/panic-abort-windows.rs @@ -8,11 +8,9 @@ // CHECK: Function Attrs: nounwind uwtable // CHECK-NEXT: define void @normal_uwtable() #[no_mangle] -pub fn normal_uwtable() { -} +pub fn normal_uwtable() {} // CHECK: Function Attrs: nounwind uwtable // CHECK-NEXT: define void @extern_uwtable() #[no_mangle] -pub extern fn extern_uwtable() { -} +pub extern "C" fn extern_uwtable() {} diff --git a/tests/codegen/pattern_type_symbols.rs b/tests/codegen/pattern_type_symbols.rs index dfe834433487..a99b3efca415 100644 --- a/tests/codegen/pattern_type_symbols.rs +++ b/tests/codegen/pattern_type_symbols.rs @@ -15,9 +15,9 @@ fn foo() {} pub fn bar() { // CHECK: call pattern_type_symbols::foo:: - // CHECK: call void @_RINvCs3QvG2ESzx2Q_20pattern_type_symbols3foomEB2_ + // CHECK: call void @_RINvC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_20pattern_type_symbols3foomEB2_ foo::(); // CHECK: call pattern_type_symbols::foo::<(u32, [(); 0], [(); 999999999], [(); true])> - // CHECK: call void @_RINvCs3QvG2ESzx2Q_20pattern_type_symbols3fooTmAum0_Aum3b9ac9ff_Aub1_EEB2_ + // CHECK: call void @_RINvC[[CRATE_IDENT]]_20pattern_type_symbols3fooTmAum0_Aum3b9ac9ff_Aub1_EEB2_ foo::(); } diff --git a/tests/codegen/personality_lifetimes.rs b/tests/codegen/personality_lifetimes.rs index 0ef4aa424d83..828af05436b4 100644 --- a/tests/codegen/personality_lifetimes.rs +++ b/tests/codegen/personality_lifetimes.rs @@ -3,19 +3,17 @@ //@ compile-flags: -O -C no-prepopulate-passes -#![crate_type="lib"] +#![crate_type = "lib"] struct S; impl Drop for S { #[inline(never)] - fn drop(&mut self) { - } + fn drop(&mut self) {} } #[inline(never)] -fn might_unwind() { -} +fn might_unwind() {} // CHECK-LABEL: @test #[no_mangle] diff --git a/tests/codegen/pgo-counter-bias.rs b/tests/codegen/pgo-counter-bias.rs index 1263eaf206f7..48e815dda048 100644 --- a/tests/codegen/pgo-counter-bias.rs +++ b/tests/codegen/pgo-counter-bias.rs @@ -1,8 +1,8 @@ // Test that __llvm_profile_counter_bias does not get internalized by lto. -//@ ignore-macos -runtime-counter-relocation not honored on Mach-O +//@ ignore-apple -runtime-counter-relocation not honored on Mach-O //@ compile-flags: -Cprofile-generate -Cllvm-args=-runtime-counter-relocation -Clto=fat -//@ needs-profiler-support +//@ compile-flags: -Zno-profiler-runtime //@ no-prefer-dynamic // CHECK: @__llvm_profile_counter_bias = {{.*}}global diff --git a/tests/codegen/pgo-instrumentation.rs b/tests/codegen/pgo-instrumentation.rs index e2c348edf822..a8f12ccce1cb 100644 --- a/tests/codegen/pgo-instrumentation.rs +++ b/tests/codegen/pgo-instrumentation.rs @@ -1,6 +1,6 @@ // Test that `-Cprofile-generate` creates expected instrumentation artifacts in LLVM IR. -//@ needs-profiler-support +//@ compile-flags: -Zno-profiler-runtime //@ compile-flags: -Cprofile-generate -Ccodegen-units=1 // CHECK: @__llvm_profile_raw_version = @@ -10,12 +10,10 @@ // CHECK-DAG: @__profd_{{.*}}pgo_instrumentation{{.*}}some_other_function{{.*}} = {{.*}}global // CHECK: @__llvm_profile_filename = {{.*}}"default_%m.profraw\00"{{.*}} -#![crate_type="lib"] +#![crate_type = "lib"] #[inline(never)] -fn some_function() { - -} +fn some_function() {} pub fn some_other_function() { some_function(); diff --git a/tests/codegen/pic-relocation-model.rs b/tests/codegen/pic-relocation-model.rs index 10ade847133a..a1d1678a6bda 100644 --- a/tests/codegen/pic-relocation-model.rs +++ b/tests/codegen/pic-relocation-model.rs @@ -5,15 +5,15 @@ // CHECK: define i8 @call_foreign_fn() #[no_mangle] pub fn call_foreign_fn() -> u8 { - unsafe { - foreign_fn() - } + unsafe { foreign_fn() } } // (Allow but do not require `zeroext` here, because it is not worth effort to // spell out which targets have it and which ones do not; see rust#97800.) // CHECK: declare{{( zeroext)?}} i8 @foreign_fn() -extern "C" {fn foreign_fn() -> u8;} +extern "C" { + fn foreign_fn() -> u8; +} // CHECK: !{i32 {{[78]}}, !"PIC Level", i32 2} diff --git a/tests/codegen/pie-relocation-model.rs b/tests/codegen/pie-relocation-model.rs index 20bf8919ac10..b10af6934522 100644 --- a/tests/codegen/pie-relocation-model.rs +++ b/tests/codegen/pie-relocation-model.rs @@ -8,15 +8,15 @@ // CHECK: define dso_local i8 @call_foreign_fn() #[no_mangle] pub fn call_foreign_fn() -> u8 { - unsafe { - foreign_fn() - } + unsafe { foreign_fn() } } // External functions are still marked as non-dso_local, since we don't know if the symbol // is defined in the binary or in the shared library. // CHECK: declare zeroext i8 @foreign_fn() -extern "C" {fn foreign_fn() -> u8;} +extern "C" { + fn foreign_fn() -> u8; +} // CHECK: !{i32 {{[78]}}, !"PIC Level", i32 2} // CHECK: !{i32 7, !"PIE Level", i32 2} diff --git a/tests/codegen/powerpc64le-struct-align-128.rs b/tests/codegen/powerpc64le-struct-align-128.rs index 0096c6d3138b..3981cd121296 100644 --- a/tests/codegen/powerpc64le-struct-align-128.rs +++ b/tests/codegen/powerpc64le-struct-align-128.rs @@ -8,12 +8,12 @@ #![crate_type = "lib"] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="freeze"] -trait Freeze { } -#[lang="copy"] -trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} #[repr(C)] pub struct Align8 { @@ -23,7 +23,7 @@ pub struct Align8 { #[repr(transparent)] pub struct Transparent8 { - a: Align8 + a: Align8, } #[repr(C)] @@ -45,7 +45,7 @@ pub struct Align16 { #[repr(transparent)] pub struct Transparent16 { - a: Align16 + a: Align16, } #[repr(C)] @@ -69,7 +69,7 @@ pub struct Align32 { #[repr(transparent)] pub struct Transparent32 { - a: Align32 + a: Align32, } #[repr(C)] @@ -83,9 +83,15 @@ extern "C" { } pub unsafe fn main( - a1: Align8, a2: Transparent8, a3: Wrapped8, - b1: Align16, b2: Transparent16, b3: Wrapped16, - c1: Align32, c2: Transparent32, c3: Wrapped32, + a1: Align8, + a2: Transparent8, + a3: Wrapped8, + b1: Align16, + b2: Transparent16, + b3: Wrapped16, + c1: Align32, + c2: Transparent32, + c3: Wrapped32, ) { test_8(a1, a2, a3); test_16(b1, b2, b3); diff --git a/tests/codegen/precondition-checks.rs b/tests/codegen/precondition-checks.rs index 191494450037..16812ca17207 100644 --- a/tests/codegen/precondition-checks.rs +++ b/tests/codegen/precondition-checks.rs @@ -21,7 +21,5 @@ use std::ptr::NonNull; #[no_mangle] pub unsafe fn nonnull_new(ptr: *mut u8) -> NonNull { // CHECK: ; call core::ptr::non_null::NonNull::new_unchecked - unsafe { - NonNull::new_unchecked(ptr) - } + unsafe { NonNull::new_unchecked(ptr) } } diff --git a/tests/codegen/refs.rs b/tests/codegen/refs.rs index 40ce04c07a11..97c362950855 100644 --- a/tests/codegen/refs.rs +++ b/tests/codegen/refs.rs @@ -5,17 +5,16 @@ // Hack to get the correct size for the length part in slices // CHECK: @helper([[USIZE:i[0-9]+]] %_1) #[no_mangle] -pub fn helper(_: usize) { -} +pub fn helper(_: usize) {} // CHECK-LABEL: @ref_dst #[no_mangle] pub fn ref_dst(s: &[u8]) { // We used to generate an extra alloca and memcpy to ref the dst, so check that we copy // directly to the alloca for "x" -// CHECK: store ptr %s.0, {{.*}} %x -// CHECK: [[X1:%[0-9]+]] = getelementptr inbounds i8, {{.*}} %x, {{i32 4|i64 8}} -// CHECK: store [[USIZE]] %s.1, {{.*}} [[X1]] + // CHECK: store ptr %s.0, {{.*}} %x + // CHECK: [[X1:%[0-9]+]] = getelementptr inbounds i8, {{.*}} %x, {{i32 4|i64 8}} + // CHECK: store [[USIZE]] %s.1, {{.*}} [[X1]] let x = &*s; &x; // keep variable in an alloca diff --git a/tests/codegen/repr/transparent-imm-array.rs b/tests/codegen/repr/transparent-imm-array.rs index 917a2c6ef527..1acd4742d356 100644 --- a/tests/codegen/repr/transparent-imm-array.rs +++ b/tests/codegen/repr/transparent-imm-array.rs @@ -26,15 +26,17 @@ #![no_std] #![no_core] -#[lang="sized"] trait Sized { } -#[lang="freeze"] trait Freeze { } -#[lang="copy"] trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} impl Copy for [u32; 16] {} impl Copy for BigS {} impl Copy for BigU {} - #[repr(C)] pub struct BigS([u32; 16]); @@ -53,20 +55,27 @@ pub enum TeBigS { // CHECK: define void @test_BigS(ptr [[BIGS_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGS_RET_ATTRS2:.*]], [16 x i32] #[no_mangle] -pub extern fn test_BigS(_: BigS) -> BigS { loop {} } +pub extern "C" fn test_BigS(_: BigS) -> BigS { + loop {} +} // CHECK: define void @test_TsBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], [16 x i32] #[no_mangle] -pub extern fn test_TsBigS(_: TsBigS) -> TsBigS { loop {} } +pub extern "C" fn test_TsBigS(_: TsBigS) -> TsBigS { + loop {} +} // CHECK: define void @test_TuBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], [16 x i32] #[no_mangle] -pub extern fn test_TuBigS(_: TuBigS) -> TuBigS { loop {} } +pub extern "C" fn test_TuBigS(_: TuBigS) -> TuBigS { + loop {} +} // CHECK: define void @test_TeBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], [16 x i32] #[no_mangle] -pub extern fn test_TeBigS(_: TeBigS) -> TeBigS { loop {} } - +pub extern "C" fn test_TeBigS(_: TeBigS) -> TeBigS { + loop {} +} #[repr(C)] pub union BigU { @@ -88,16 +97,24 @@ pub enum TeBigU { // CHECK: define void @test_BigU(ptr [[BIGU_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], [16 x i32] #[no_mangle] -pub extern fn test_BigU(_: BigU) -> BigU { loop {} } +pub extern "C" fn test_BigU(_: BigU) -> BigU { + loop {} +} // CHECK: define void @test_TsBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2]], [16 x i32] #[no_mangle] -pub extern fn test_TsBigU(_: TsBigU) -> TsBigU { loop {} } +pub extern "C" fn test_TsBigU(_: TsBigU) -> TsBigU { + loop {} +} // CHECK: define void @test_TuBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2]], [16 x i32] #[no_mangle] -pub extern fn test_TuBigU(_: TuBigU) -> TuBigU { loop {} } +pub extern "C" fn test_TuBigU(_: TuBigU) -> TuBigU { + loop {} +} // CHECK: define void @test_TeBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2]], [16 x i32] #[no_mangle] -pub extern fn test_TeBigU(_: TeBigU) -> TeBigU { loop {} } +pub extern "C" fn test_TeBigU(_: TeBigU) -> TeBigU { + loop {} +} diff --git a/tests/codegen/repr/transparent-mips64.rs b/tests/codegen/repr/transparent-mips64.rs index ebed8d7989fd..588d440b4d7b 100644 --- a/tests/codegen/repr/transparent-mips64.rs +++ b/tests/codegen/repr/transparent-mips64.rs @@ -13,9 +13,12 @@ #![no_std] #![no_core] -#[lang="sized"] trait Sized { } -#[lang="freeze"] trait Freeze { } -#[lang="copy"] trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} impl Copy for [u32; 16] {} impl Copy for BigS {} @@ -39,20 +42,27 @@ pub enum TeBigS { // CHECK: define void @test_BigS(ptr [[BIGS_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGS_RET_ATTRS2:.*]], [8 x i64] #[no_mangle] -pub extern fn test_BigS(_: BigS) -> BigS { loop {} } +pub extern "C" fn test_BigS(_: BigS) -> BigS { + loop {} +} // CHECK: define void @test_TsBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], [8 x i64] #[no_mangle] -pub extern fn test_TsBigS(_: TsBigS) -> TsBigS { loop {} } +pub extern "C" fn test_TsBigS(_: TsBigS) -> TsBigS { + loop {} +} // CHECK: define void @test_TuBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], [8 x i64] #[no_mangle] -pub extern fn test_TuBigS(_: TuBigS) -> TuBigS { loop {} } +pub extern "C" fn test_TuBigS(_: TuBigS) -> TuBigS { + loop {} +} // CHECK: define void @test_TeBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], [8 x i64] #[no_mangle] -pub extern fn test_TeBigS(_: TeBigS) -> TeBigS { loop {} } - +pub extern "C" fn test_TeBigS(_: TeBigS) -> TeBigS { + loop {} +} #[repr(C)] pub union BigU { @@ -74,16 +84,24 @@ pub enum TeBigU { // CHECK: define void @test_BigU(ptr [[BIGU_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], [8 x i64] #[no_mangle] -pub extern fn test_BigU(_: BigU) -> BigU { loop {} } +pub extern "C" fn test_BigU(_: BigU) -> BigU { + loop {} +} // CHECK: define void @test_TsBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2]], [8 x i64] #[no_mangle] -pub extern fn test_TsBigU(_: TsBigU) -> TsBigU { loop {} } +pub extern "C" fn test_TsBigU(_: TsBigU) -> TsBigU { + loop {} +} // CHECK: define void @test_TuBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2]], [8 x i64] #[no_mangle] -pub extern fn test_TuBigU(_: TuBigU) -> TuBigU { loop {} } +pub extern "C" fn test_TuBigU(_: TuBigU) -> TuBigU { + loop {} +} // CHECK: define void @test_TeBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2]], [8 x i64] #[no_mangle] -pub extern fn test_TeBigU(_: TeBigU) -> TeBigU { loop {} } +pub extern "C" fn test_TeBigU(_: TeBigU) -> TeBigU { + loop {} +} diff --git a/tests/codegen/repr/transparent-sparc64.rs b/tests/codegen/repr/transparent-sparc64.rs index 7005a07f554f..8e4c8ce2ee9b 100644 --- a/tests/codegen/repr/transparent-sparc64.rs +++ b/tests/codegen/repr/transparent-sparc64.rs @@ -8,15 +8,17 @@ #![no_std] #![no_core] -#[lang="sized"] trait Sized { } -#[lang="freeze"] trait Freeze { } -#[lang="copy"] trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} impl Copy for [u32; 16] {} impl Copy for BigS {} impl Copy for BigU {} - #[repr(C)] pub struct BigS([u32; 16]); @@ -37,26 +39,33 @@ pub enum TeBigS { // CHECK-NOT: byval // CHECK-SAME: %{{[0-9a-z_]+}}) #[no_mangle] -pub extern "C" fn test_BigS(_: BigS) -> BigS { loop {} } +pub extern "C" fn test_BigS(_: BigS) -> BigS { + loop {} +} // CHECK: define{{.*}}void @test_TsBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr // CHECK-NOT: byval // CHECK-SAME: %{{[0-9a-z_]+}}) #[no_mangle] -pub extern "C" fn test_TsBigS(_: TsBigS) -> TsBigS { loop {} } +pub extern "C" fn test_TsBigS(_: TsBigS) -> TsBigS { + loop {} +} // CHECK: define{{.*}}void @test_TuBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr // CHECK-NOT: byval // CHECK-SAME: %{{[0-9a-z_]+}}) #[no_mangle] -pub extern "C" fn test_TuBigS(_: TuBigS) -> TuBigS { loop {} } +pub extern "C" fn test_TuBigS(_: TuBigS) -> TuBigS { + loop {} +} // CHECK: define{{.*}}void @test_TeBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr // CHECK-NOT: byval // CHECK-SAME: %{{[0-9a-z_]+}}) #[no_mangle] -pub extern "C" fn test_TeBigS(_: TeBigS) -> TeBigS { loop {} } - +pub extern "C" fn test_TeBigS(_: TeBigS) -> TeBigS { + loop {} +} #[repr(C)] pub union BigU { @@ -80,22 +89,30 @@ pub enum TeBigU { // CHECK-NOT: byval // CHECK-SAME: %{{[0-9a-z_]+}}) #[no_mangle] -pub extern "C" fn test_BigU(_: BigU) -> BigU { loop {} } +pub extern "C" fn test_BigU(_: BigU) -> BigU { + loop {} +} // CHECK: define{{.*}}void @test_TsBigU(ptr [[BIGU_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr // CHECK-NOT: byval // CHECK-SAME: %{{[0-9a-z_]+}}) #[no_mangle] -pub extern "C" fn test_TsBigU(_: TsBigU) -> TsBigU { loop {} } +pub extern "C" fn test_TsBigU(_: TsBigU) -> TsBigU { + loop {} +} // CHECK: define{{.*}}void @test_TuBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr // CHECK-NOT: byval // CHECK-SAME: %{{[0-9a-z_]+}}) #[no_mangle] -pub extern "C" fn test_TuBigU(_: TuBigU) -> TuBigU { loop {} } +pub extern "C" fn test_TuBigU(_: TuBigU) -> TuBigU { + loop {} +} // CHECK: define{{.*}}void @test_TeBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr // CHECK-NOT: byval // CHECK-SAME: %{{[0-9a-z_]+}}) #[no_mangle] -pub extern "C" fn test_TeBigU(_: TeBigU) -> TeBigU { loop {} } +pub extern "C" fn test_TeBigU(_: TeBigU) -> TeBigU { + loop {} +} diff --git a/tests/codegen/repr/transparent-struct-ptr.rs b/tests/codegen/repr/transparent-struct-ptr.rs index 26f0120752f2..9cffd6c7f732 100644 --- a/tests/codegen/repr/transparent-struct-ptr.rs +++ b/tests/codegen/repr/transparent-struct-ptr.rs @@ -22,15 +22,17 @@ #![no_std] #![no_core] -#[lang="sized"] trait Sized { } -#[lang="freeze"] trait Freeze { } -#[lang="copy"] trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} impl Copy for [u32; 16] {} impl Copy for BigS {} impl Copy for BigU {} - #[repr(C)] pub struct BigS([u32; 16]); @@ -49,20 +51,27 @@ pub enum TeBigS { // CHECK: define{{.*}}void @test_BigS(ptr [[BIGS_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGS_RET_ATTRS2:.*]], ptr [[BIGS_ARG_ATTRS1:.*]] byval([64 x i8]) [[BIGS_ARG_ATTRS2:.*]]) #[no_mangle] -pub extern "C" fn test_BigS(_: BigS) -> BigS { loop {} } +pub extern "C" fn test_BigS(_: BigS) -> BigS { + loop {} +} // CHECK: define{{.*}}void @test_TsBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr [[BIGS_ARG_ATTRS1]] byval([64 x i8]) [[BIGS_ARG_ATTRS2:.*]]) #[no_mangle] -pub extern "C" fn test_TsBigS(_: TsBigS) -> TsBigS { loop {} } +pub extern "C" fn test_TsBigS(_: TsBigS) -> TsBigS { + loop {} +} // CHECK: define{{.*}}void @test_TuBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr [[BIGS_ARG_ATTRS1]] byval([64 x i8]) [[BIGS_ARG_ATTRS2:.*]]) #[no_mangle] -pub extern "C" fn test_TuBigS(_: TuBigS) -> TuBigS { loop {} } +pub extern "C" fn test_TuBigS(_: TuBigS) -> TuBigS { + loop {} +} // CHECK: define{{.*}}void @test_TeBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr [[BIGS_ARG_ATTRS1]] byval([64 x i8]) [[BIGS_ARG_ATTRS2]]) #[no_mangle] -pub extern "C" fn test_TeBigS(_: TeBigS) -> TeBigS { loop {} } - +pub extern "C" fn test_TeBigS(_: TeBigS) -> TeBigS { + loop {} +} #[repr(C)] pub union BigU { @@ -84,16 +93,24 @@ pub enum TeBigU { // CHECK: define{{.*}}void @test_BigU(ptr [[BIGU_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr [[BIGU_ARG_ATTRS1:.*]] byval([64 x i8]) [[BIGU_ARG_ATTRS2:.*]]) #[no_mangle] -pub extern "C" fn test_BigU(_: BigU) -> BigU { loop {} } +pub extern "C" fn test_BigU(_: BigU) -> BigU { + loop {} +} // CHECK: define{{.*}}void @test_TsBigU(ptr [[BIGU_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr [[BIGU_ARG_ATTRS1]] byval([64 x i8]) [[BIGU_ARG_ATTRS2]]) #[no_mangle] -pub extern "C" fn test_TsBigU(_: TsBigU) -> TsBigU { loop {} } +pub extern "C" fn test_TsBigU(_: TsBigU) -> TsBigU { + loop {} +} // CHECK: define{{.*}}void @test_TuBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr [[BIGU_ARG_ATTRS1]] byval([64 x i8]) [[BIGU_ARG_ATTRS2]]) #[no_mangle] -pub extern "C" fn test_TuBigU(_: TuBigU) -> TuBigU { loop {} } +pub extern "C" fn test_TuBigU(_: TuBigU) -> TuBigU { + loop {} +} // CHECK: define{{.*}}void @test_TeBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr [[BIGU_ARG_ATTRS1]] byval([64 x i8]) [[BIGU_ARG_ATTRS2]]) #[no_mangle] -pub extern "C" fn test_TeBigU(_: TeBigU) -> TeBigU { loop {} } +pub extern "C" fn test_TeBigU(_: TeBigU) -> TeBigU { + loop {} +} diff --git a/tests/codegen/repr/transparent-sysv64.rs b/tests/codegen/repr/transparent-sysv64.rs index 1c7bca7ee406..afb06dcc1bdf 100644 --- a/tests/codegen/repr/transparent-sysv64.rs +++ b/tests/codegen/repr/transparent-sysv64.rs @@ -13,19 +13,28 @@ #![no_std] #![no_core] -#[lang="sized"] trait Sized { } -#[lang="freeze"] trait Freeze { } -#[lang="copy"] trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} #[repr(C)] -pub struct Rgb8 { r: u8, g: u8, b: u8 } +pub struct Rgb8 { + r: u8, + g: u8, + b: u8, +} #[repr(transparent)] pub struct Rgb8Wrap(Rgb8); // CHECK: i24 @test_Rgb8Wrap(i24{{( %0)?}}) #[no_mangle] -pub extern "sysv64" fn test_Rgb8Wrap(_: Rgb8Wrap) -> Rgb8Wrap { loop {} } +pub extern "sysv64" fn test_Rgb8Wrap(_: Rgb8Wrap) -> Rgb8Wrap { + loop {} +} #[repr(C)] pub union FloatBits { @@ -38,4 +47,6 @@ pub struct SmallUnion(FloatBits); // CHECK: i32 @test_SmallUnion(i32{{( %0)?}}) #[no_mangle] -pub extern "sysv64" fn test_SmallUnion(_: SmallUnion) -> SmallUnion { loop {} } +pub extern "sysv64" fn test_SmallUnion(_: SmallUnion) -> SmallUnion { + loop {} +} diff --git a/tests/codegen/repr/transparent.rs b/tests/codegen/repr/transparent.rs index 17ec476035f2..4b41332db451 100644 --- a/tests/codegen/repr/transparent.rs +++ b/tests/codegen/repr/transparent.rs @@ -8,7 +8,7 @@ // For RISCV: see codegen/riscv-abi // For LoongArch: see codegen/loongarch-abi -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(repr_simd, transparent_unions)] use std::marker::PhantomData; @@ -24,83 +24,112 @@ pub struct F32(f32); // CHECK: define{{.*}}float @test_F32(float noundef %_1) #[no_mangle] -pub extern "C" fn test_F32(_: F32) -> F32 { loop {} } +pub extern "C" fn test_F32(_: F32) -> F32 { + loop {} +} #[repr(transparent)] pub struct Ptr(*mut u8); // CHECK: define{{.*}}ptr @test_Ptr(ptr noundef %_1) #[no_mangle] -pub extern "C" fn test_Ptr(_: Ptr) -> Ptr { loop {} } +pub extern "C" fn test_Ptr(_: Ptr) -> Ptr { + loop {} +} #[repr(transparent)] pub struct WithZst(u64, Zst1); // CHECK: define{{.*}}i64 @test_WithZst(i64 noundef %_1) #[no_mangle] -pub extern "C" fn test_WithZst(_: WithZst) -> WithZst { loop {} } +pub extern "C" fn test_WithZst(_: WithZst) -> WithZst { + loop {} +} #[repr(transparent)] pub struct WithZeroSizedArray(*const f32, [i8; 0]); // CHECK: define{{.*}}ptr @test_WithZeroSizedArray(ptr noundef %_1) #[no_mangle] -pub extern "C" fn test_WithZeroSizedArray(_: WithZeroSizedArray) -> WithZeroSizedArray { loop {} } +pub extern "C" fn test_WithZeroSizedArray(_: WithZeroSizedArray) -> WithZeroSizedArray { + loop {} +} #[repr(transparent)] pub struct Generic(T); // CHECK: define{{.*}}double @test_Generic(double noundef %_1) #[no_mangle] -pub extern "C" fn test_Generic(_: Generic) -> Generic { loop {} } +pub extern "C" fn test_Generic(_: Generic) -> Generic { + loop {} +} #[repr(transparent)] pub struct GenericPlusZst(T, Zst2); #[repr(u8)] -pub enum Bool { True, False, FileNotFound } +pub enum Bool { + True, + False, + FileNotFound, +} // CHECK: define{{( dso_local)?}} noundef{{( zeroext)?}} i8 @test_Gpz(i8 noundef{{( zeroext)?}} %_1) #[no_mangle] -pub extern "C" fn test_Gpz(_: GenericPlusZst) -> GenericPlusZst { loop {} } +pub extern "C" fn test_Gpz(_: GenericPlusZst) -> GenericPlusZst { + loop {} +} #[repr(transparent)] pub struct LifetimePhantom<'a, T: 'a>(*const T, PhantomData<&'a T>); // CHECK: define{{.*}}ptr @test_LifetimePhantom(ptr noundef %_1) #[no_mangle] -pub extern "C" fn test_LifetimePhantom(_: LifetimePhantom) -> LifetimePhantom { loop {} } +pub extern "C" fn test_LifetimePhantom(_: LifetimePhantom) -> LifetimePhantom { + loop {} +} // This works despite current alignment resrictions because PhantomData is always align(1) #[repr(transparent)] -pub struct UnitPhantom { val: T, unit: PhantomData } +pub struct UnitPhantom { + val: T, + unit: PhantomData, +} pub struct Px; // CHECK: define{{.*}}float @test_UnitPhantom(float noundef %_1) #[no_mangle] -pub extern "C" fn test_UnitPhantom(_: UnitPhantom) -> UnitPhantom { loop {} } +pub extern "C" fn test_UnitPhantom(_: UnitPhantom) -> UnitPhantom { + loop {} +} #[repr(transparent)] pub struct TwoZsts(Zst1, i8, Zst2); // CHECK: define{{( dso_local)?}} noundef{{( signext)?}} i8 @test_TwoZsts(i8 noundef{{( signext)?}} %_1) #[no_mangle] -pub extern "C" fn test_TwoZsts(_: TwoZsts) -> TwoZsts { loop {} } +pub extern "C" fn test_TwoZsts(_: TwoZsts) -> TwoZsts { + loop {} +} #[repr(transparent)] pub struct Nested1(Zst2, Generic); // CHECK: define{{.*}}double @test_Nested1(double noundef %_1) #[no_mangle] -pub extern "C" fn test_Nested1(_: Nested1) -> Nested1 { loop {} } +pub extern "C" fn test_Nested1(_: Nested1) -> Nested1 { + loop {} +} #[repr(transparent)] pub struct Nested2(Nested1, Zst1); // CHECK: define{{.*}}double @test_Nested2(double noundef %_1) #[no_mangle] -pub extern "C" fn test_Nested2(_: Nested2) -> Nested2 { loop {} } +pub extern "C" fn test_Nested2(_: Nested2) -> Nested2 { + loop {} +} #[repr(simd)] struct f32x4(f32, f32, f32, f32); @@ -110,35 +139,47 @@ pub struct Vector(f32x4); // CHECK: define{{.*}}<4 x float> @test_Vector(<4 x float> %_1) #[no_mangle] -pub extern "C" fn test_Vector(_: Vector) -> Vector { loop {} } +pub extern "C" fn test_Vector(_: Vector) -> Vector { + loop {} +} -trait Mirror { type It: ?Sized; } -impl Mirror for T { type It = Self; } +trait Mirror { + type It: ?Sized; +} +impl Mirror for T { + type It = Self; +} #[repr(transparent)] pub struct StructWithProjection(::It); // CHECK: define{{.*}}float @test_Projection(float noundef %_1) #[no_mangle] -pub extern "C" fn test_Projection(_: StructWithProjection) -> StructWithProjection { loop {} } +pub extern "C" fn test_Projection(_: StructWithProjection) -> StructWithProjection { + loop {} +} #[repr(transparent)] pub enum EnumF32 { - Variant(F32) + Variant(F32), } // CHECK: define{{.*}}float @test_EnumF32(float noundef %_1) #[no_mangle] -pub extern "C" fn test_EnumF32(_: EnumF32) -> EnumF32 { loop {} } +pub extern "C" fn test_EnumF32(_: EnumF32) -> EnumF32 { + loop {} +} #[repr(transparent)] pub enum EnumF32WithZsts { - Variant(Zst1, F32, Zst2) + Variant(Zst1, F32, Zst2), } // CHECK: define{{.*}}float @test_EnumF32WithZsts(float noundef %_1) #[no_mangle] -pub extern "C" fn test_EnumF32WithZsts(_: EnumF32WithZsts) -> EnumF32WithZsts { loop {} } +pub extern "C" fn test_EnumF32WithZsts(_: EnumF32WithZsts) -> EnumF32WithZsts { + loop {} +} #[repr(transparent)] pub union UnionF32 { @@ -147,7 +188,9 @@ pub union UnionF32 { // CHECK: define{{.*}} float @test_UnionF32(float %_1) #[no_mangle] -pub extern "C" fn test_UnionF32(_: UnionF32) -> UnionF32 { loop {} } +pub extern "C" fn test_UnionF32(_: UnionF32) -> UnionF32 { + loop {} +} #[repr(transparent)] pub union UnionF32WithZsts { @@ -158,8 +201,9 @@ pub union UnionF32WithZsts { // CHECK: define{{.*}}float @test_UnionF32WithZsts(float %_1) #[no_mangle] -pub extern "C" fn test_UnionF32WithZsts(_: UnionF32WithZsts) -> UnionF32WithZsts { loop {} } - +pub extern "C" fn test_UnionF32WithZsts(_: UnionF32WithZsts) -> UnionF32WithZsts { + loop {} +} // All that remains to be tested are aggregates. They are tested in separate files called // transparent-*.rs with `only-*` or `ignore-*` directives, because the expected LLVM IR diff --git a/tests/codegen/riscv-abi/call-llvm-intrinsics.rs b/tests/codegen/riscv-abi/call-llvm-intrinsics.rs index c3f795e88576..e72a649a530a 100644 --- a/tests/codegen/riscv-abi/call-llvm-intrinsics.rs +++ b/tests/codegen/riscv-abi/call-llvm-intrinsics.rs @@ -23,7 +23,7 @@ pub fn do_call() { unsafe { // Ensure that we `call` LLVM intrinsics instead of trying to `invoke` them - // CHECK: store float 4.000000e+00, float* %{{.}}, align 4 + // CHECK: store float 4.000000e+00, ptr %{{.}}, align 4 // CHECK: call float @llvm.sqrt.f32(float %{{.}} sqrt(4.0); } diff --git a/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs b/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs index 95c936ece730..977749ad578a 100644 --- a/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs +++ b/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs @@ -83,8 +83,7 @@ pub struct Tiny { // CHECK: define void @f_agg_tiny(i64 %0) #[no_mangle] -pub extern "C" fn f_agg_tiny(mut e: Tiny) { -} +pub extern "C" fn f_agg_tiny(mut e: Tiny) {} // CHECK: define i64 @f_agg_tiny_ret() #[no_mangle] @@ -100,8 +99,7 @@ pub struct Small { // CHECK: define void @f_agg_small([2 x i64] %0) #[no_mangle] -pub extern "C" fn f_agg_small(mut x: Small) { -} +pub extern "C" fn f_agg_small(mut x: Small) {} // CHECK: define [2 x i64] @f_agg_small_ret() #[no_mangle] @@ -116,8 +114,7 @@ pub struct SmallAligned { // CHECK: define void @f_agg_small_aligned(i128 %0) #[no_mangle] -pub extern "C" fn f_agg_small_aligned(mut x: SmallAligned) { -} +pub extern "C" fn f_agg_small_aligned(mut x: SmallAligned) {} #[repr(C)] pub struct Large { @@ -129,8 +126,7 @@ pub struct Large { // CHECK: define void @f_agg_large(ptr {{.*}}%x) #[no_mangle] -pub extern "C" fn f_agg_large(mut x: Large) { -} +pub extern "C" fn f_agg_large(mut x: Large) {} // CHECK: define void @f_agg_large_ret(ptr {{.*}}sret{{.*}}, i32 noundef signext %i, i8 noundef signext %j) #[no_mangle] diff --git a/tests/codegen/riscv-abi/riscv64-lp64d-abi.rs b/tests/codegen/riscv-abi/riscv64-lp64d-abi.rs index 060d91a2696a..bcd9b0eae71d 100644 --- a/tests/codegen/riscv-abi/riscv64-lp64d-abi.rs +++ b/tests/codegen/riscv-abi/riscv64-lp64d-abi.rs @@ -1,10 +1,19 @@ -// -//@ compile-flags: -C no-prepopulate-passes -//@ only-riscv64 -//@ only-linux -#![crate_type = "lib"] +//@ compile-flags: -O -C no-prepopulate-passes --target riscv64gc-unknown-linux-gnu +//@ needs-llvm-components: riscv -// CHECK: define void @f_fpr_tracking(double %0, double %1, double %2, double %3, double %4, double %5, double %6, double %7, i8 zeroext %i) +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_std] +#![no_core] + +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} + +// CHECK: define void @f_fpr_tracking(double %0, double %1, double %2, double %3, double %4, double %5, double %6, double %7, i8 noundef zeroext %i) #[no_mangle] pub extern "C" fn f_fpr_tracking( a: f64, @@ -144,7 +153,7 @@ pub extern "C" fn f_ret_double_int64_s() -> DoubleInt64 { DoubleInt64 { f: 1., i: 2 } } -// CHECK: define void @f_double_int8_s_arg_insufficient_gprs(i32 signext %a, i32 signext %b, i32 signext %c, i32 signext %d, i32 signext %e, i32 signext %f, i32 signext %g, i32 signext %h, [2 x i64] %0) +// CHECK: define void @f_double_int8_s_arg_insufficient_gprs(i32 noundef signext %a, i32 noundef signext %b, i32 noundef signext %c, i32 noundef signext %d, i32 noundef signext %e, i32 noundef signext %f, i32 noundef signext %g, i32 noundef signext %h, [2 x i64] %0) #[no_mangle] pub extern "C" fn f_double_int8_s_arg_insufficient_gprs( a: i32, @@ -250,11 +259,11 @@ pub struct IntDoubleInt { c: i32, } -// CHECK: define void @f_int_double_int_s_arg(%IntDoubleInt* {{.*}}%a) +// CHECK: define void @f_int_double_int_s_arg(ptr {{.*}} %a) #[no_mangle] pub extern "C" fn f_int_double_int_s_arg(a: IntDoubleInt) {} -// CHECK: define void @f_ret_int_double_int_s(%IntDoubleInt* {{.*}}sret +// CHECK: define void @f_ret_int_double_int_s(ptr {{.*}} sret([24 x i8]) align 8 dereferenceable(24) %_0) #[no_mangle] pub extern "C" fn f_ret_int_double_int_s() -> IntDoubleInt { IntDoubleInt { a: 1, b: 2., c: 3 } diff --git a/tests/codegen/riscv-abi/riscv64-lp64f-lp64d-abi.rs b/tests/codegen/riscv-abi/riscv64-lp64f-lp64d-abi.rs index 3d0512817f7c..27018d2e6d20 100644 --- a/tests/codegen/riscv-abi/riscv64-lp64f-lp64d-abi.rs +++ b/tests/codegen/riscv-abi/riscv64-lp64f-lp64d-abi.rs @@ -1,10 +1,19 @@ -// -//@ compile-flags: -C no-prepopulate-passes -//@ only-riscv64 -//@ only-linux -#![crate_type = "lib"] +//@ compile-flags: -O -C no-prepopulate-passes --target riscv64gc-unknown-linux-gnu +//@ needs-llvm-components: riscv -// CHECK: define void @f_fpr_tracking(float %0, float %1, float %2, float %3, float %4, float %5, float %6, float %7, i8 zeroext %i) +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_std] +#![no_core] + +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} + +// CHECK: define void @f_fpr_tracking(float %0, float %1, float %2, float %3, float %4, float %5, float %6, float %7, i8 noundef zeroext %i) #[no_mangle] pub extern "C" fn f_fpr_tracking( a: f32, @@ -128,7 +137,7 @@ pub extern "C" fn f_ret_float_int64_s() -> FloatInt64 { FloatInt64 { f: 1., i: 2 } } -// CHECK: define void @f_float_int8_s_arg_insufficient_gprs(i32 signext %a, i32 signext %b, i32 signext %c, i32 signext %d, i32 signext %e, i32 signext %f, i32 signext %g, i32 signext %h, i64 %0) +// CHECK: define void @f_float_int8_s_arg_insufficient_gprs(i32 noundef signext %a, i32 noundef signext %b, i32 noundef signext %c, i32 noundef signext %d, i32 noundef signext %e, i32 noundef signext %f, i32 noundef signext %g, i32 noundef signext %h, i64 %0) #[no_mangle] pub extern "C" fn f_float_int8_s_arg_insufficient_gprs( a: i32, diff --git a/tests/codegen/sanitizer/address-sanitizer-globals-tracking.rs b/tests/codegen/sanitizer/address-sanitizer-globals-tracking.rs index 8ffa235c18f1..f319306f93fd 100644 --- a/tests/codegen/sanitizer/address-sanitizer-globals-tracking.rs +++ b/tests/codegen/sanitizer/address-sanitizer-globals-tracking.rs @@ -23,7 +23,7 @@ //@[ASAN] compile-flags: //@[ASAN-FAT-LTO] compile-flags: -Cprefer-dynamic=false -Clto=fat -#![crate_type="staticlib"] +#![crate_type = "staticlib"] // The test below mimics `CACHED_POW10` from `library/core/src/num/flt2dec/strategy/grisu.rs` which // (because of incorrect handling of `___asan_globals_registered` during LTO) was incorrectly diff --git a/tests/codegen/sanitizer/cfi/add-canonical-jump-tables-flag.rs b/tests/codegen/sanitizer/cfi/add-canonical-jump-tables-flag.rs index f122fbdc0866..22577e2a3c46 100644 --- a/tests/codegen/sanitizer/cfi/add-canonical-jump-tables-flag.rs +++ b/tests/codegen/sanitizer/cfi/add-canonical-jump-tables-flag.rs @@ -3,9 +3,8 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi -#![crate_type="lib"] +#![crate_type = "lib"] -pub fn foo() { -} +pub fn foo() {} // CHECK: !{{[0-9]+}} = !{i32 4, !"CFI Canonical Jump Tables", i32 1} diff --git a/tests/codegen/sanitizer/cfi/add-enable-split-lto-unit-flag.rs b/tests/codegen/sanitizer/cfi/add-enable-split-lto-unit-flag.rs index 05eea13c6ee0..283b8f261029 100644 --- a/tests/codegen/sanitizer/cfi/add-enable-split-lto-unit-flag.rs +++ b/tests/codegen/sanitizer/cfi/add-enable-split-lto-unit-flag.rs @@ -3,9 +3,8 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi -#![crate_type="lib"] +#![crate_type = "lib"] -pub fn foo() { -} +pub fn foo() {} // CHECK: !{{[0-9]+}} = !{i32 4, !"EnableSplitLTOUnit", i32 1} diff --git a/tests/codegen/sanitizer/cfi/emit-type-checks-attr-no-sanitize.rs b/tests/codegen/sanitizer/cfi/emit-type-checks-attr-no-sanitize.rs index 9f72de2ebcc9..259967e89181 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-checks-attr-no-sanitize.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-checks-attr-no-sanitize.rs @@ -3,7 +3,7 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(no_sanitize)] #[no_sanitize(cfi)] diff --git a/tests/codegen/sanitizer/cfi/emit-type-checks.rs b/tests/codegen/sanitizer/cfi/emit-type-checks.rs index 6ec6f0e5476d..37edbefee56a 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-checks.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-checks.rs @@ -3,7 +3,7 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 -#![crate_type="lib"] +#![crate_type = "lib"] pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { // CHECK-LABEL: define{{.*}}foo{{.*}}!type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-attr-cfi-encoding.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-attr-cfi-encoding.rs index be4af9b7962d..9bc2e42db0f6 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-attr-cfi-encoding.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-attr-cfi-encoding.rs @@ -3,13 +3,13 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(cfi_encoding, extern_types)] #[cfi_encoding = "3Foo"] pub struct Type1(i32); -extern { +extern "C" { #[cfi_encoding = "3Bar"] type Type2; } @@ -25,35 +25,35 @@ pub struct Type4(i32); #[repr(transparent)] pub struct Type5(u32); -pub fn foo0(_: Type1) { } +pub fn foo0(_: Type1) {} // CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo1(_: Type1, _: Type1) { } +pub fn foo1(_: Type1, _: Type1) {} // CHECK: define{{.*}}foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo2(_: Type1, _: Type1, _: Type1) { } +pub fn foo2(_: Type1, _: Type1, _: Type1) {} // CHECK: define{{.*}}foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo3(_: *mut Type2) { } +pub fn foo3(_: *mut Type2) {} // CHECK: define{{.*}}foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo4(_: *mut Type2, _: *mut Type2) { } +pub fn foo4(_: *mut Type2, _: *mut Type2) {} // CHECK: define{{.*}}foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo5(_: *mut Type2, _: *mut Type2, _: *mut Type2) { } +pub fn foo5(_: *mut Type2, _: *mut Type2, _: *mut Type2) {} // CHECK: define{{.*}}foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo6(_: *mut Type3) { } +pub fn foo6(_: *mut Type3) {} // CHECK: define{{.*}}foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo7(_: *mut Type3, _: *mut Type3) { } +pub fn foo7(_: *mut Type3, _: *mut Type3) {} // CHECK: define{{.*}}foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo8(_: *mut Type3, _: *mut Type3, _: *mut Type3) { } +pub fn foo8(_: *mut Type3, _: *mut Type3, _: *mut Type3) {} // CHECK: define{{.*}}foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo9(_: Type4) { } +pub fn foo9(_: Type4) {} // CHECK: define{{.*}}foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo10(_: Type4, _: Type4) { } +pub fn foo10(_: Type4, _: Type4) {} // CHECK: define{{.*}}foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo11(_: Type4, _: Type4, _: Type4) { } +pub fn foo11(_: Type4, _: Type4, _: Type4) {} // CHECK: define{{.*}}foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo12(_: Type5) { } +pub fn foo12(_: Type5) {} // CHECK: define{{.*}}foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo13(_: Type5, _: Type5) { } +pub fn foo13(_: Type5, _: Type5) {} // CHECK: define{{.*}}foo13{{.*}}!type ![[TYPE13:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo14(_: Type5, _: Type5, _: Type5) { } +pub fn foo14(_: Type5, _: Type5, _: Type5) {} // CHECK: define{{.*}}foo14{{.*}}!type ![[TYPE14:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} // CHECK: ![[TYPE0]] = !{i64 0, !"_ZTSFv3FooE"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs index 6608caca8af8..3edc68e13474 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs @@ -4,27 +4,31 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(type_alias_impl_trait)] extern crate core; -pub type Type1 = impl Send; +mod defining_module { + pub type Type1 = impl Send; -pub fn foo() where - Type1: 'static, -{ - pub struct Foo([T; N]); - let _: Type1 = Foo([0; 32]); + pub fn foo() + where + Type1: 'static, + { + pub struct Foo([T; N]); + let _: Type1 = Foo([0; 32]); + } } +use defining_module::*; -pub fn foo1(_: Type1) { } +pub fn foo1(_: Type1) {} // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo2(_: Type1, _: Type1) { } +pub fn foo2(_: Type1, _: Type1) {} // CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo3(_: Type1, _: Type1, _: Type1) { } +pub fn foo3(_: Type1, _: Type1, _: Type1) {} // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooIu3i32Lu5usize32EEE"} -// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooIu3i32Lu5usize32EES2_E"} -// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooIu3i32Lu5usize32EES2_S2_E"} +// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvNtC{{[[:print:]]+}}_{{[[:print:]]+}}15defining_module3foo3FooIu3i32Lu5usize32EEE"} +// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvNtC{{[[:print:]]+}}_{{[[:print:]]+}}15defining_module3foo3FooIu3i32Lu5usize32EES2_E"} +// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvNtC{{[[:print:]]+}}_{{[[:print:]]+}}15defining_module3foo3FooIu3i32Lu5usize32EES2_S2_E"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-drop-in-place.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-drop-in-place.rs index 3ec1988edd6e..2a7eca6fc196 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-drop-in-place.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-drop-in-place.rs @@ -3,7 +3,7 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -#![crate_type="lib"] +#![crate_type = "lib"] // CHECK-LABEL: define{{.*}}4core3ptr47drop_in_place$LT$dyn$u20$core..marker..Send$GT$ // CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs index c5dadf70de95..7e60aafff680 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs @@ -4,29 +4,29 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -#![crate_type="lib"] +#![crate_type = "lib"] -pub fn foo1(_: fn(i32) -> i32) { } +pub fn foo1(_: fn(i32) -> i32) {} // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo2(_: fn(i32) -> i32, _: fn(i32) -> i32) { } +pub fn foo2(_: fn(i32) -> i32, _: fn(i32) -> i32) {} // CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo3(_: fn(i32) -> i32, _: fn(i32) -> i32, _: fn(i32) -> i32) { } +pub fn foo3(_: fn(i32) -> i32, _: fn(i32) -> i32, _: fn(i32) -> i32) {} // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo4(_: &dyn Fn(i32) -> i32) { } +pub fn foo4(_: &dyn Fn(i32) -> i32) {} // CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo5(_: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32) { } +pub fn foo5(_: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32) {} // CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo6(_: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32) { } +pub fn foo6(_: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32) {} // CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo7(_: &dyn FnMut(i32) -> i32) { } +pub fn foo7(_: &dyn FnMut(i32) -> i32) {} // CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo8(_: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32) { } +pub fn foo8(_: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32) {} // CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo9(_: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32) { } +pub fn foo9(_: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32) {} // CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo10(_: &dyn FnOnce(i32) -> i32) { } +pub fn foo10(_: &dyn FnOnce(i32) -> i32) {} // CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo11(_: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32) { } +pub fn foo11(_: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32) {} // CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo12(_: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32) {} // CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-lifetimes.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-lifetimes.rs index 4d08c0c3039a..09cfd2e10d6e 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-lifetimes.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-lifetimes.rs @@ -4,24 +4,30 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(type_alias_impl_trait)] extern crate core; -pub type Type1 = impl Send; +mod defining_module { -pub fn foo<'a>() where - Type1: 'static, -{ - pub struct Foo<'a>(&'a i32); - pub struct Bar<'a, 'b>(&'a i32, &'b Foo<'b>); - let _: Type1 = Bar; + pub type Type1 = impl Send; + + pub fn foo<'a>() + where + Type1: 'static, + { + pub struct Foo<'a>(&'a i32); + pub struct Bar<'a, 'b>(&'a i32, &'b Foo<'b>); + let _: Type1 = Bar; + } } -pub fn foo1(_: Type1) { } +use defining_module::*; + +pub fn foo1(_: Type1) {} // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo2(_: Type1, _: Type1) { } +pub fn foo2(_: Type1, _: Type1) {} // CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo3(_: Type1, _: Type1, _: Type1) { } +pub fn foo3(_: Type1, _: Type1, _: Type1) {} // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs index 671db563dde7..9d611777ff0b 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs @@ -4,7 +4,7 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -#![crate_type="lib"] +#![crate_type = "lib"] trait Trait1 { fn foo(&self); @@ -17,6 +17,5 @@ impl Trait1 for Type1 { // CHECK: define{{.*}}3foo{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} } - // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u6regionEEE"} // CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}5Type1EE"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs index 6c3d991af9f1..ffbfe021ba3e 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs @@ -4,85 +4,89 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(type_alias_impl_trait)] extern crate core; -pub type Type1 = impl Send; -pub type Type2 = impl Send; -pub type Type3 = impl Send; -pub type Type4 = impl Send; +mod defining_module { + pub type Type1 = impl Send; + pub type Type2 = impl Send; + pub type Type3 = impl Send; + pub type Type4 = impl Send; -pub fn foo() where - Type1: 'static, - Type2: 'static, - Type3: 'static, - Type4: 'static, -{ - // Type in extern path - extern { - fn bar(); + pub fn foo() + where + Type1: 'static, + Type2: 'static, + Type4: 'static, + { + // Type in extern path + extern "C" { + fn bar(); + } + let _: Type1 = bar; + + // Type in closure path + || { + pub struct Foo; + let _: Type2 = Foo; + }; + + // Type in const path + const { + pub struct Foo; + fn bar() -> Type3 { + Foo + } + }; + + // Type in impl path + struct Foo; + impl Foo { + fn bar(&self) {} + } + let _: Type4 = ::bar; } - let _: Type1 = bar; - - // Type in closure path - || { - pub struct Foo; - let _: Type2 = Foo; - }; - - // Type in const path - const { - pub struct Foo; - fn bar() -> Type3 { Foo } - }; - - - // Type in impl path - struct Foo; - impl Foo { - fn bar(&self) { } - } - let _: Type4 = ::bar; } +use defining_module::*; // Force arguments to be passed by using a reference. Otherwise, they may end up PassMode::Ignore -pub fn foo1(_: &Type1) { } +pub fn foo1(_: &Type1) {} // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo2(_: &Type1, _: &Type1) { } +pub fn foo2(_: &Type1, _: &Type1) {} // CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo3(_: &Type1, _: &Type1, _: &Type1) { } +pub fn foo3(_: &Type1, _: &Type1, _: &Type1) {} // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo4(_: &Type2) { } +pub fn foo4(_: &Type2) {} // CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo5(_: &Type2, _: &Type2) { } +pub fn foo5(_: &Type2, _: &Type2) {} // CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo6(_: &Type2, _: &Type2, _: &Type2) { } +pub fn foo6(_: &Type2, _: &Type2, _: &Type2) {} // CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo7(_: &Type3) { } +pub fn foo7(_: &Type3) {} // CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo8(_: &Type3, _: &Type3) { } +pub fn foo8(_: &Type3, _: &Type3) {} // CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo9(_: &Type3, _: &Type3, _: &Type3) { } +pub fn foo9(_: &Type3, _: &Type3, _: &Type3) {} // CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo10(_: &Type4) { } +pub fn foo10(_: &Type4) {} // CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo11(_: &Type4, _: &Type4) { } +pub fn foo11(_: &Type4, _: &Type4) {} // CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo12(_: &Type4, _: &Type4, _: &Type4) { } +pub fn foo12(_: &Type4, _: &Type4, _: &Type4) {} // CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo10{{[{}][{}]}}extern{{[}][}]}}3barEE"} -// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo10{{[{}][{}]}}extern{{[}][}]}}3barES0_E"} -// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo10{{[{}][{}]}}extern{{[}][}]}}3barES0_S0_E"} -// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooEE"} -// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_E"} -// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_S0_E"} -// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooEE"} -// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_E"} -// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_S0_E"} -// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barEE"} -// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_E"} -// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_S0_E"} +// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNFNvNtC{{[[:print:]]+}}_{{[[:print:]]+}}15defining_module3foo10{{[{}][{}]}}extern{{[}][}]}}3barEE"} +// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNFNvNtC{{[[:print:]]+}}_{{[[:print:]]+}}15defining_module3foo10{{[{}][{}]}}extern{{[}][}]}}3barES0_E"} +// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNFNvNtC{{[[:print:]]+}}_{{[[:print:]]+}}15defining_module3foo10{{[{}][{}]}}extern{{[}][}]}}3barES0_S0_E"} +// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvNtC{{[[:print:]]+}}_{{[[:print:]]+}}15defining_module3foo11{{[{}][{}]}}closure{{[}][}]}}3FooEE"} +// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvNtC{{[[:print:]]+}}_{{[[:print:]]+}}15defining_module3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_E"} +// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvNtC{{[[:print:]]+}}_{{[[:print:]]+}}15defining_module3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_S0_E"} +// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvNtC{{[[:print:]]+}}_{{[[:print:]]+}}15defining_module3foo12{{[{}][{}]}}constant{{[}][}]}}3FooEE"} +// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvNtC{{[[:print:]]+}}_{{[[:print:]]+}}15defining_module3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_E"} +// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvNtC{{[[:print:]]+}}_{{[[:print:]]+}}15defining_module3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_S0_E"} +// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvNtC{{[[:print:]]+}}_{{[[:print:]]+}}15defining_module3foo8{{[{}][{}]}}impl{{[}][}]}}3barEE"} +// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvNtC{{[[:print:]]+}}_{{[[:print:]]+}}15defining_module3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_E"} +// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvNtC{{[[:print:]]+}}_{{[[:print:]]+}}15defining_module3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_S0_E"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-pointer-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-pointer-types.rs index 6ad6f3ac3489..d37bb740f550 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-pointer-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-pointer-types.rs @@ -4,37 +4,37 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -#![crate_type="lib"] +#![crate_type = "lib"] -pub fn foo1(_: &mut i32) { } +pub fn foo1(_: &mut i32) {} // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo2(_: &mut i32, _: &i32) { } +pub fn foo2(_: &mut i32, _: &i32) {} // CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo3(_: &mut i32, _: &i32, _: &i32) { } +pub fn foo3(_: &mut i32, _: &i32, _: &i32) {} // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo4(_: &i32) { } +pub fn foo4(_: &i32) {} // CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo5(_: &i32, _: &mut i32) { } +pub fn foo5(_: &i32, _: &mut i32) {} // CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo6(_: &i32, _: &mut i32, _: &mut i32) { } +pub fn foo6(_: &i32, _: &mut i32, _: &mut i32) {} // CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo7(_: *mut i32) { } +pub fn foo7(_: *mut i32) {} // CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo8(_: *mut i32, _: *const i32) { } +pub fn foo8(_: *mut i32, _: *const i32) {} // CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo9(_: *mut i32, _: *const i32, _: *const i32) { } +pub fn foo9(_: *mut i32, _: *const i32, _: *const i32) {} // CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo10(_: *const i32) { } +pub fn foo10(_: *const i32) {} // CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo11(_: *const i32, _: *mut i32) { } +pub fn foo11(_: *const i32, _: *mut i32) {} // CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo12(_: *const i32, _: *mut i32, _: *mut i32) { } +pub fn foo12(_: *const i32, _: *mut i32, _: *mut i32) {} // CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo13(_: fn(i32) -> i32) { } +pub fn foo13(_: fn(i32) -> i32) {} // CHECK: define{{.*}}5foo13{{.*}}!type ![[TYPE13:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo14(_: fn(i32) -> i32, _: fn(i32) -> i32) { } +pub fn foo14(_: fn(i32) -> i32, _: fn(i32) -> i32) {} // CHECK: define{{.*}}5foo14{{.*}}!type ![[TYPE14:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo15(_: fn(i32) -> i32, _: fn(i32) -> i32, _: fn(i32) -> i32) { } +pub fn foo15(_: fn(i32) -> i32, _: fn(i32) -> i32, _: fn(i32) -> i32) {} // CHECK: define{{.*}}5foo15{{.*}}!type ![[TYPE15:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvU3mutu3refIu3i32EE"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs index 38f507856bde..7d9e4d058727 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs @@ -4,130 +4,130 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -#![crate_type="lib"] +#![crate_type = "lib"] extern crate core; use core::ffi::*; -pub fn foo1(_: ()) { } +pub fn foo1(_: ()) {} // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo2(_: (), _: c_void) { } +pub fn foo2(_: (), _: c_void) {} // CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo3(_: (), _: c_void, _: c_void) { } +pub fn foo3(_: (), _: c_void, _: c_void) {} // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo4(_: *mut ()) { } +pub fn foo4(_: *mut ()) {} // CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo5(_: *mut (), _: *mut c_void) { } +pub fn foo5(_: *mut (), _: *mut c_void) {} // CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo6(_: *mut (), _: *mut c_void, _: *mut c_void) { } +pub fn foo6(_: *mut (), _: *mut c_void, _: *mut c_void) {} // CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo7(_: *const ()) { } +pub fn foo7(_: *const ()) {} // CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo8(_: *const (), _: *const c_void) { } +pub fn foo8(_: *const (), _: *const c_void) {} // CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo9(_: *const (), _: *const c_void, _: *const c_void) { } +pub fn foo9(_: *const (), _: *const c_void, _: *const c_void) {} // CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo10(_: bool) { } +pub fn foo10(_: bool) {} // CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo11(_: bool, _: bool) { } +pub fn foo11(_: bool, _: bool) {} // CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo12(_: bool, _: bool, _: bool) { } +pub fn foo12(_: bool, _: bool, _: bool) {} // CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo13(_: i8) { } +pub fn foo13(_: i8) {} // CHECK: define{{.*}}5foo13{{.*}}!type ![[TYPE13:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo14(_: i8, _: i8) { } +pub fn foo14(_: i8, _: i8) {} // CHECK: define{{.*}}5foo14{{.*}}!type ![[TYPE14:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo15(_: i8, _: i8, _: i8) { } +pub fn foo15(_: i8, _: i8, _: i8) {} // CHECK: define{{.*}}5foo15{{.*}}!type ![[TYPE15:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo16(_: i16) { } +pub fn foo16(_: i16) {} // CHECK: define{{.*}}5foo16{{.*}}!type ![[TYPE16:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo17(_: i16, _: i16) { } +pub fn foo17(_: i16, _: i16) {} // CHECK: define{{.*}}5foo17{{.*}}!type ![[TYPE17:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo18(_: i16, _: i16, _: i16) { } +pub fn foo18(_: i16, _: i16, _: i16) {} // CHECK: define{{.*}}5foo18{{.*}}!type ![[TYPE18:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo19(_: i32) { } +pub fn foo19(_: i32) {} // CHECK: define{{.*}}5foo19{{.*}}!type ![[TYPE19:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo20(_: i32, _: i32) { } +pub fn foo20(_: i32, _: i32) {} // CHECK: define{{.*}}5foo20{{.*}}!type ![[TYPE20:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo21(_: i32, _: i32, _: i32) { } +pub fn foo21(_: i32, _: i32, _: i32) {} // CHECK: define{{.*}}5foo21{{.*}}!type ![[TYPE21:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo22(_: i64) { } +pub fn foo22(_: i64) {} // CHECK: define{{.*}}5foo22{{.*}}!type ![[TYPE22:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo23(_: i64, _: i64) { } +pub fn foo23(_: i64, _: i64) {} // CHECK: define{{.*}}5foo23{{.*}}!type ![[TYPE23:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo24(_: i64, _: i64, _: i64) { } +pub fn foo24(_: i64, _: i64, _: i64) {} // CHECK: define{{.*}}5foo24{{.*}}!type ![[TYPE24:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo25(_: i128) { } +pub fn foo25(_: i128) {} // CHECK: define{{.*}}5foo25{{.*}}!type ![[TYPE25:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo26(_: i128, _: i128) { } +pub fn foo26(_: i128, _: i128) {} // CHECK: define{{.*}}5foo26{{.*}}!type ![[TYPE26:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo27(_: i128, _: i128, _: i128) { } +pub fn foo27(_: i128, _: i128, _: i128) {} // CHECK: define{{.*}}5foo27{{.*}}!type ![[TYPE27:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo28(_: isize) { } +pub fn foo28(_: isize) {} // CHECK: define{{.*}}5foo28{{.*}}!type ![[TYPE28:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo29(_: isize, _: isize) { } +pub fn foo29(_: isize, _: isize) {} // CHECK: define{{.*}}5foo29{{.*}}!type ![[TYPE29:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo30(_: isize, _: isize, _: isize) { } +pub fn foo30(_: isize, _: isize, _: isize) {} // CHECK: define{{.*}}5foo30{{.*}}!type ![[TYPE30:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo31(_: u8) { } +pub fn foo31(_: u8) {} // CHECK: define{{.*}}5foo31{{.*}}!type ![[TYPE31:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo32(_: u8, _: u8) { } +pub fn foo32(_: u8, _: u8) {} // CHECK: define{{.*}}5foo32{{.*}}!type ![[TYPE32:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo33(_: u8, _: u8, _: u8) { } +pub fn foo33(_: u8, _: u8, _: u8) {} // CHECK: define{{.*}}5foo33{{.*}}!type ![[TYPE33:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo34(_: u16) { } +pub fn foo34(_: u16) {} // CHECK: define{{.*}}5foo34{{.*}}!type ![[TYPE34:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo35(_: u16, _: u16) { } +pub fn foo35(_: u16, _: u16) {} // CHECK: define{{.*}}5foo35{{.*}}!type ![[TYPE35:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo36(_: u16, _: u16, _: u16) { } +pub fn foo36(_: u16, _: u16, _: u16) {} // CHECK: define{{.*}}5foo36{{.*}}!type ![[TYPE36:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo37(_: u32) { } +pub fn foo37(_: u32) {} // CHECK: define{{.*}}5foo37{{.*}}!type ![[TYPE37:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo38(_: u32, _: u32) { } +pub fn foo38(_: u32, _: u32) {} // CHECK: define{{.*}}5foo38{{.*}}!type ![[TYPE38:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo39(_: u32, _: u32, _: u32) { } +pub fn foo39(_: u32, _: u32, _: u32) {} // CHECK: define{{.*}}5foo39{{.*}}!type ![[TYPE39:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo40(_: u64) { } +pub fn foo40(_: u64) {} // CHECK: define{{.*}}5foo40{{.*}}!type ![[TYPE40:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo41(_: u64, _: u64) { } +pub fn foo41(_: u64, _: u64) {} // CHECK: define{{.*}}5foo41{{.*}}!type ![[TYPE41:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo42(_: u64, _: u64, _: u64) { } +pub fn foo42(_: u64, _: u64, _: u64) {} // CHECK: define{{.*}}5foo42{{.*}}!type ![[TYPE42:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo43(_: u128) { } +pub fn foo43(_: u128) {} // CHECK: define{{.*}}5foo43{{.*}}!type ![[TYPE43:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo44(_: u128, _: u128) { } +pub fn foo44(_: u128, _: u128) {} // CHECK: define{{.*}}5foo44{{.*}}!type ![[TYPE44:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo45(_: u128, _: u128, _: u128) { } +pub fn foo45(_: u128, _: u128, _: u128) {} // CHECK: define{{.*}}5foo45{{.*}}!type ![[TYPE45:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo46(_: usize) { } +pub fn foo46(_: usize) {} // CHECK: define{{.*}}5foo46{{.*}}!type ![[TYPE46:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo47(_: usize, _: usize) { } +pub fn foo47(_: usize, _: usize) {} // CHECK: define{{.*}}5foo47{{.*}}!type ![[TYPE47:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo48(_: usize, _: usize, _: usize) { } +pub fn foo48(_: usize, _: usize, _: usize) {} // CHECK: define{{.*}}5foo48{{.*}}!type ![[TYPE48:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo49(_: f32) { } +pub fn foo49(_: f32) {} // CHECK: define{{.*}}5foo49{{.*}}!type ![[TYPE49:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo50(_: f32, _: f32) { } +pub fn foo50(_: f32, _: f32) {} // CHECK: define{{.*}}5foo50{{.*}}!type ![[TYPE50:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo51(_: f32, _: f32, _: f32) { } +pub fn foo51(_: f32, _: f32, _: f32) {} // CHECK: define{{.*}}5foo51{{.*}}!type ![[TYPE51:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo52(_: f64) { } +pub fn foo52(_: f64) {} // CHECK: define{{.*}}5foo52{{.*}}!type ![[TYPE52:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo53(_: f64, _: f64) { } +pub fn foo53(_: f64, _: f64) {} // CHECK: define{{.*}}5foo53{{.*}}!type ![[TYPE53:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo54(_: f64, _: f64, _: f64) { } +pub fn foo54(_: f64, _: f64, _: f64) {} // CHECK: define{{.*}}5foo54{{.*}}!type ![[TYPE54:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo55(_: char) { } +pub fn foo55(_: char) {} // CHECK: define{{.*}}5foo55{{.*}}!type ![[TYPE55:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo56(_: char, _: char) { } +pub fn foo56(_: char, _: char) {} // CHECK: define{{.*}}5foo56{{.*}}!type ![[TYPE56:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo57(_: char, _: char, _: char) { } +pub fn foo57(_: char, _: char, _: char) {} // CHECK: define{{.*}}5foo57{{.*}}!type ![[TYPE57:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo58(_: &str) { } +pub fn foo58(_: &str) {} // CHECK: define{{.*}}5foo58{{.*}}!type ![[TYPE58:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo59(_: &str, _: &str) { } +pub fn foo59(_: &str, _: &str) {} // CHECK: define{{.*}}5foo59{{.*}}!type ![[TYPE59:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo60(_: &str, _: &str, _: &str) { } +pub fn foo60(_: &str, _: &str, _: &str) {} // CHECK: define{{.*}}5foo60{{.*}}!type ![[TYPE60:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvvE"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs index 1332338b26ab..0f97c70f3f92 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs @@ -4,7 +4,7 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -#![crate_type="lib"] +#![crate_type = "lib"] extern crate core; use core::ffi::*; @@ -40,32 +40,31 @@ pub struct Type4(Type4Helper); #[repr(transparent)] pub struct Type4Helper(*mut T); -pub fn foo1(_: Type1) { } +pub fn foo1(_: Type1) {} // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo2(_: Type1, _: Type1) { } +pub fn foo2(_: Type1, _: Type1) {} // CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo3(_: Type1, _: Type1, _: Type1) { } +pub fn foo3(_: Type1, _: Type1, _: Type1) {} // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo4(_: Type2) { } +pub fn foo4(_: Type2) {} // CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo5(_: Type2, _: Type2) { } +pub fn foo5(_: Type2, _: Type2) {} // CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo6(_: Type2, _: Type2, _: Type2) { } +pub fn foo6(_: Type2, _: Type2, _: Type2) {} // CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo7(_: Type3) { } +pub fn foo7(_: Type3) {} // CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo8(_: Type3, _: Type3) { } +pub fn foo8(_: Type3, _: Type3) {} // CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo9(_: Type3, _: Type3, _: Type3) { } +pub fn foo9(_: Type3, _: Type3, _: Type3) {} // CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo10(_: Type4) { } +pub fn foo10(_: Type4) {} // CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo11(_: Type4, _: Type4) { } +pub fn foo11(_: Type4, _: Type4) {} // CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo12(_: Type4, _: Type4, _: Type4) { } +pub fn foo12(_: Type4, _: Type4, _: Type4) {} // CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} - // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3FooE"} // CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3FooS_E"} // CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3FooS_S_E"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-sequence-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-sequence-types.rs index dd2e05dd2ec6..bdee3f47a837 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-sequence-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-sequence-types.rs @@ -4,25 +4,25 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -#![crate_type="lib"] +#![crate_type = "lib"] -pub fn foo1(_: (i32, i32)) { } +pub fn foo1(_: (i32, i32)) {} // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo2(_: (i32, i32), _: (i32, i32)) { } +pub fn foo2(_: (i32, i32), _: (i32, i32)) {} // CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo3(_: (i32, i32), _: (i32, i32), _: (i32, i32)) { } +pub fn foo3(_: (i32, i32), _: (i32, i32), _: (i32, i32)) {} // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo4(_: [i32; 32]) { } +pub fn foo4(_: [i32; 32]) {} // CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo5(_: [i32; 32], _: [i32; 32]) { } +pub fn foo5(_: [i32; 32], _: [i32; 32]) {} // CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo6(_: [i32; 32], _: [i32; 32], _: [i32; 32]) { } +pub fn foo6(_: [i32; 32], _: [i32; 32], _: [i32; 32]) {} // CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo7(_: &[i32]) { } +pub fn foo7(_: &[i32]) {} // CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo8(_: &[i32], _: &[i32]) { } +pub fn foo8(_: &[i32], _: &[i32]) {} // CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo9(_: &[i32], _: &[i32], _: &[i32]) { } +pub fn foo9(_: &[i32], _: &[i32], _: &[i32]) {} // CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu5tupleIu3i32S_EE"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs index 6d9fe7040f70..55e816178f8f 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs @@ -4,7 +4,7 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -#![crate_type="lib"] +#![crate_type = "lib"] extern crate core; @@ -16,8 +16,7 @@ pub trait Trait1 { pub struct Type1; impl Trait1 for Type1 { - fn foo(&self) { - } + fn foo(&self) {} } pub trait Trait2 { @@ -27,8 +26,7 @@ pub trait Trait2 { pub struct Type2; impl Trait2 for Type2 { - fn bar(&self) { - } + fn bar(&self) {} } pub trait Trait3 { @@ -38,8 +36,7 @@ pub trait Trait3 { pub struct Type3; impl Trait3 for T { - fn baz(&self, _: &U) { - } + fn baz(&self, _: &U) {} } pub trait Trait4<'a, T> { @@ -64,71 +61,88 @@ pub trait Trait5 { pub struct Type5; impl Trait5 for T { - fn quux(&self, _: &[U; N]) { - } + fn quux(&self, _: &[U; N]) {} } -pub fn foo1(_: &dyn Send) { } +pub fn foo1(_: &dyn Send) {} // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo2(_: &dyn Send, _: &dyn Send) { } +pub fn foo2(_: &dyn Send, _: &dyn Send) {} // CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo3(_: &dyn Send, _: &dyn Send, _: &dyn Send) { } +pub fn foo3(_: &dyn Send, _: &dyn Send, _: &dyn Send) {} // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo4(_: &(dyn Send + Sync)) { } +pub fn foo4(_: &(dyn Send + Sync)) {} // CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo5(_: &(dyn Send + Sync), _: &(dyn Sync + Send)) { } +pub fn foo5(_: &(dyn Send + Sync), _: &(dyn Sync + Send)) {} // CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo6(_: &(dyn Send + Sync), _: &(dyn Sync + Send), _: &(dyn Sync + Send)) { } +pub fn foo6(_: &(dyn Send + Sync), _: &(dyn Sync + Send), _: &(dyn Sync + Send)) {} // CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo7(_: &(dyn Trait1 + Send)) { } +pub fn foo7(_: &(dyn Trait1 + Send)) {} // CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo8(_: &(dyn Trait1 + Send), _: &(dyn Trait1 + Send)) { } +pub fn foo8(_: &(dyn Trait1 + Send), _: &(dyn Trait1 + Send)) {} // CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo9(_: &(dyn Trait1 + Send), _: &(dyn Trait1 + Send), _: &(dyn Trait1 + Send)) { } +pub fn foo9(_: &(dyn Trait1 + Send), _: &(dyn Trait1 + Send), _: &(dyn Trait1 + Send)) {} // CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo10(_: &(dyn Trait1 + Send + Sync)) { } +pub fn foo10(_: &(dyn Trait1 + Send + Sync)) {} // CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo11(_: &(dyn Trait1 + Send + Sync), _: &(dyn Trait1 + Sync + Send)) { } +pub fn foo11(_: &(dyn Trait1 + Send + Sync), _: &(dyn Trait1 + Sync + Send)) {} // CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo12(_: &(dyn Trait1 + Send + Sync), - _: &(dyn Trait1 + Sync + Send), - _: &(dyn Trait1 + Sync + Send)) { } +pub fn foo12( + _: &(dyn Trait1 + Send + Sync), + _: &(dyn Trait1 + Sync + Send), + _: &(dyn Trait1 + Sync + Send), +) { +} // CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo13(_: &dyn Trait1) { } +pub fn foo13(_: &dyn Trait1) {} // CHECK: define{{.*}}5foo13{{.*}}!type ![[TYPE13:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo14(_: &dyn Trait1, _: &dyn Trait1) { } +pub fn foo14(_: &dyn Trait1, _: &dyn Trait1) {} // CHECK: define{{.*}}5foo14{{.*}}!type ![[TYPE14:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo15(_: &dyn Trait1, _: &dyn Trait1, _: &dyn Trait1) { } +pub fn foo15(_: &dyn Trait1, _: &dyn Trait1, _: &dyn Trait1) {} // CHECK: define{{.*}}5foo15{{.*}}!type ![[TYPE15:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo16(_: &dyn Trait2) { } -pub fn bar16() { let a = Type2; foo16(&a); } +pub fn foo16(_: &dyn Trait2) {} +pub fn bar16() { + let a = Type2; + foo16(&a); +} // CHECK: define{{.*}}5foo16{{.*}}!type ![[TYPE16:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo17(_: &dyn Trait2, _: &dyn Trait2) { } -pub fn bar17() { let a = Type2; foo17(&a, &a); } +pub fn foo17(_: &dyn Trait2, _: &dyn Trait2) {} +pub fn bar17() { + let a = Type2; + foo17(&a, &a); +} // CHECK: define{{.*}}5foo17{{.*}}!type ![[TYPE17:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo18(_: &dyn Trait2, _: &dyn Trait2, _: &dyn Trait2) { } -pub fn bar18() { let a = Type2; foo18(&a, &a, &a); } +pub fn foo18(_: &dyn Trait2, _: &dyn Trait2, _: &dyn Trait2) {} +pub fn bar18() { + let a = Type2; + foo18(&a, &a, &a); +} // CHECK: define{{.*}}5foo18{{.*}}!type ![[TYPE18:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo19(_: &dyn Trait3) { } +pub fn foo19(_: &dyn Trait3) {} // CHECK: define{{.*}}5foo19{{.*}}!type ![[TYPE19:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo20(_: &dyn Trait3, _: &dyn Trait3) { } +pub fn foo20(_: &dyn Trait3, _: &dyn Trait3) {} // CHECK: define{{.*}}5foo20{{.*}}!type ![[TYPE20:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo21(_: &dyn Trait3, _: &dyn Trait3, _: &dyn Trait3) { } +pub fn foo21(_: &dyn Trait3, _: &dyn Trait3, _: &dyn Trait3) {} // CHECK: define{{.*}}5foo21{{.*}}!type ![[TYPE21:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo22<'a>(_: &dyn Trait4<'a, Type4, Output = &'a i32>) { } +pub fn foo22<'a>(_: &dyn Trait4<'a, Type4, Output = &'a i32>) {} // CHECK: define{{.*}}5foo22{{.*}}!type ![[TYPE22:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo23<'a>(_: &dyn Trait4<'a, Type4, Output = &'a i32>, - _: &dyn Trait4<'a, Type4, Output = &'a i32>) { } +pub fn foo23<'a>( + _: &dyn Trait4<'a, Type4, Output = &'a i32>, + _: &dyn Trait4<'a, Type4, Output = &'a i32>, +) { +} // CHECK: define{{.*}}5foo23{{.*}}!type ![[TYPE23:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo24<'a>(_: &dyn Trait4<'a, Type4, Output = &'a i32>, - _: &dyn Trait4<'a, Type4, Output = &'a i32>, - _: &dyn Trait4<'a, Type4, Output = &'a i32>) { } +pub fn foo24<'a>( + _: &dyn Trait4<'a, Type4, Output = &'a i32>, + _: &dyn Trait4<'a, Type4, Output = &'a i32>, + _: &dyn Trait4<'a, Type4, Output = &'a i32>, +) { +} // CHECK: define{{.*}}5foo24{{.*}}!type ![[TYPE24:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo25(_: &dyn Trait5) { } +pub fn foo25(_: &dyn Trait5) {} // CHECK: define{{.*}}5foo25{{.*}}!type ![[TYPE25:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo26(_: &dyn Trait5, _: &dyn Trait5) { } +pub fn foo26(_: &dyn Trait5, _: &dyn Trait5) {} // CHECK: define{{.*}}5foo26{{.*}}!type ![[TYPE26:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo27(_: &dyn Trait5, _: &dyn Trait5, _: &dyn Trait5) { } +pub fn foo27(_: &dyn Trait5, _: &dyn Trait5, _: &dyn Trait5) {} // CHECK: define{{.*}}5foo27{{.*}}!type ![[TYPE27:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} // CHECK: ![[TYPE13]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u6regionEEE"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-user-defined-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-user-defined-types.rs index 4eaf42bf87d1..c1f3ca61afeb 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-user-defined-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-user-defined-types.rs @@ -4,7 +4,7 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(extern_types)] pub struct Struct1 { @@ -19,33 +19,33 @@ pub union Union1 { member1: std::mem::ManuallyDrop, } -extern { +extern "C" { pub type type1; } -pub fn foo1(_: &Struct1::) { } +pub fn foo1(_: &Struct1) {} // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo2(_: &Struct1::, _: &Struct1::) { } +pub fn foo2(_: &Struct1, _: &Struct1) {} // CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo3(_: &Struct1::, _: &Struct1::, _: &Struct1::) { } +pub fn foo3(_: &Struct1, _: &Struct1, _: &Struct1) {} // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo4(_: &Enum1::) { } +pub fn foo4(_: &Enum1) {} // CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo5(_: &Enum1::, _: &Enum1::) { } +pub fn foo5(_: &Enum1, _: &Enum1) {} // CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo6(_: &Enum1::, _: &Enum1::, _: &Enum1::) { } +pub fn foo6(_: &Enum1, _: &Enum1, _: &Enum1) {} // CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo7(_: &Union1::) { } +pub fn foo7(_: &Union1) {} // CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo8(_: &Union1::, _: &Union1::) { } +pub fn foo8(_: &Union1, _: &Union1) {} // CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo9(_: &Union1::, _: &Union1::, _: &Union1::) { } +pub fn foo9(_: &Union1, _: &Union1, _: &Union1) {} // CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo10(_: *mut type1) { } +pub fn foo10(_: *mut type1) {} // CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo11(_: *mut type1, _: *mut type1) { } +pub fn foo11(_: *mut type1, _: *mut type1) {} // CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo12(_: *mut type1, _: *mut type1, _: *mut type1) { } +pub fn foo12(_: *mut type1, _: *mut type1, _: *mut type1) {} // CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}7Struct1Iu3i32EEE"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-generalized.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-generalized.rs index ccd7ee93ca1f..32637b64b3ea 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-generalized.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-generalized.rs @@ -3,7 +3,7 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-generalize-pointers -#![crate_type="lib"] +#![crate_type = "lib"] pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { // CHECK-LABEL: define{{.*}}foo diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs index d41300341780..51121b0aef1a 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized-generalized.rs @@ -3,7 +3,7 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers -Zsanitizer-cfi-generalize-pointers -#![crate_type="lib"] +#![crate_type = "lib"] pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { // CHECK-LABEL: define{{.*}}foo diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized.rs index ac18379165de..1cfdd23006e3 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi-normalized.rs @@ -3,7 +3,7 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers -#![crate_type="lib"] +#![crate_type = "lib"] pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { // CHECK-LABEL: define{{.*}}foo diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi.rs index 526ba62c264d..56ab1ce4b358 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-itanium-cxx-abi.rs @@ -3,7 +3,7 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -#![crate_type="lib"] +#![crate_type = "lib"] pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { // CHECK-LABEL: define{{.*}}foo diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-trait-objects.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-trait-objects.rs index 318aad9291c4..0e57ce322d12 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-trait-objects.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-trait-objects.rs @@ -3,7 +3,7 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Ctarget-feature=-crt-static -Zsanitizer=cfi -#![crate_type="lib"] +#![crate_type = "lib"] pub trait Trait1 { fn foo(&self); @@ -13,8 +13,7 @@ pub trait Trait1 { pub struct Type1; impl Trait1 for Type1 { - fn foo(&self) { - } + fn foo(&self) {} } pub trait Trait2 { @@ -24,8 +23,7 @@ pub trait Trait2 { pub struct Type2; impl Trait2 for Type2 { - fn bar(&self) { - } + fn bar(&self) {} } pub trait Trait3 { @@ -35,8 +33,7 @@ pub trait Trait3 { pub struct Type3; impl Trait3 for T { - fn baz(&self, _: &U) { - } + fn baz(&self, _: &U) {} } pub trait Trait4<'a, T> { @@ -61,8 +58,7 @@ pub trait Trait5 { pub struct Type5; impl Trait5 for T { - fn quux(&self, _: &[U; N]) { - } + fn quux(&self, _: &[U; N]) {} } pub fn foo1(a: &dyn Trait1) { diff --git a/tests/codegen/sanitizer/cfi/generalize-pointers.rs b/tests/codegen/sanitizer/cfi/generalize-pointers.rs index eaf3dad19092..57004da6f8e0 100644 --- a/tests/codegen/sanitizer/cfi/generalize-pointers.rs +++ b/tests/codegen/sanitizer/cfi/generalize-pointers.rs @@ -3,33 +3,33 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-generalize-pointers -Copt-level=0 -#![crate_type="lib"] +#![crate_type = "lib"] extern crate core; -pub fn foo0(_: &mut i32) { } +pub fn foo0(_: &mut i32) {} // CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo1(_: &mut i32, _: &mut i32) { } +pub fn foo1(_: &mut i32, _: &mut i32) {} // CHECK: define{{.*}}foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo2(_: &mut i32, _: &mut i32, _: &mut i32) { } +pub fn foo2(_: &mut i32, _: &mut i32, _: &mut i32) {} // CHECK: define{{.*}}foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo3(_: &i32) { } +pub fn foo3(_: &i32) {} // CHECK: define{{.*}}foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo4(_: &i32, _: &i32) { } +pub fn foo4(_: &i32, _: &i32) {} // CHECK: define{{.*}}foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo5(_: &i32, _: &i32, _: &i32) { } +pub fn foo5(_: &i32, _: &i32, _: &i32) {} // CHECK: define{{.*}}foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo6(_: *mut i32) { } +pub fn foo6(_: *mut i32) {} // CHECK: define{{.*}}foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo7(_: *mut i32, _: *mut i32) { } +pub fn foo7(_: *mut i32, _: *mut i32) {} // CHECK: define{{.*}}foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo8(_: *mut i32, _: *mut i32, _: *mut i32) { } +pub fn foo8(_: *mut i32, _: *mut i32, _: *mut i32) {} // CHECK: define{{.*}}foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo9(_: *const i32) { } +pub fn foo9(_: *const i32) {} // CHECK: define{{.*}}foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo10(_: *const i32, _: *const i32) { } +pub fn foo10(_: *const i32, _: *const i32) {} // CHECK: define{{.*}}foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo11(_: *const i32, _: *const i32, _: *const i32) { } +pub fn foo11(_: *const i32, _: *const i32, _: *const i32) {} // CHECK: define{{.*}}foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} // CHECK: ![[TYPE0]] = !{i64 0, !"_ZTSFvU3mutu3refIvEE.generalized"} diff --git a/tests/codegen/sanitizer/cfi/normalize-integers.rs b/tests/codegen/sanitizer/cfi/normalize-integers.rs index 801ed312be5b..770ee4e64e08 100644 --- a/tests/codegen/sanitizer/cfi/normalize-integers.rs +++ b/tests/codegen/sanitizer/cfi/normalize-integers.rs @@ -3,33 +3,33 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-normalize-integers -Copt-level=0 -#![crate_type="lib"] +#![crate_type = "lib"] extern crate core; -pub fn foo0(_: bool) { } +pub fn foo0(_: bool) {} // CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]] !type !{{[0-9]+}} -pub fn foo1(_: bool, _: bool) { } +pub fn foo1(_: bool, _: bool) {} // CHECK: define{{.*}}foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} -pub fn foo2(_: bool, _: bool, _: bool) { } +pub fn foo2(_: bool, _: bool, _: bool) {} // CHECK: define{{.*}}foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} -pub fn foo3(_: char) { } +pub fn foo3(_: char) {} // CHECK: define{{.*}}foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} -pub fn foo4(_: char, _: char) { } +pub fn foo4(_: char, _: char) {} // CHECK: define{{.*}}foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} -pub fn foo5(_: char, _: char, _: char) { } +pub fn foo5(_: char, _: char, _: char) {} // CHECK: define{{.*}}foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} -pub fn foo6(_: isize) { } +pub fn foo6(_: isize) {} // CHECK: define{{.*}}foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} -pub fn foo7(_: isize, _: isize) { } +pub fn foo7(_: isize, _: isize) {} // CHECK: define{{.*}}foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} -pub fn foo8(_: isize, _: isize, _: isize) { } +pub fn foo8(_: isize, _: isize, _: isize) {} // CHECK: define{{.*}}foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} -pub fn foo9(_: (), _: usize) { } +pub fn foo9(_: (), _: usize) {} // CHECK: define{{.*}}foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} -pub fn foo10(_: (), _: usize, _: usize) { } +pub fn foo10(_: (), _: usize, _: usize) {} // CHECK: define{{.*}}foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} -pub fn foo11(_: (), _: usize, _: usize, _: usize) { } +pub fn foo11(_: (), _: usize, _: usize, _: usize) {} // CHECK: define{{.*}}foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} // CHECK: ![[TYPE0]] = !{i64 0, !"_ZTSFvu2u8E.normalized"} diff --git a/tests/codegen/sanitizer/dataflow-instrument-functions.rs b/tests/codegen/sanitizer/dataflow-instrument-functions.rs index 69c3560882c1..a2d0d63cc175 100644 --- a/tests/codegen/sanitizer/dataflow-instrument-functions.rs +++ b/tests/codegen/sanitizer/dataflow-instrument-functions.rs @@ -3,8 +3,7 @@ //@ needs-sanitizer-dataflow //@ compile-flags: -Copt-level=0 -Zsanitizer=dataflow -#![crate_type="lib"] +#![crate_type = "lib"] -pub fn foo() { -} +pub fn foo() {} // CHECK: define{{.*}}foo{{.*}}.dfsan diff --git a/tests/codegen/sanitizer/kcfi/add-kcfi-flag.rs b/tests/codegen/sanitizer/kcfi/add-kcfi-flag.rs index 7751d3baf79a..013de74f8d68 100644 --- a/tests/codegen/sanitizer/kcfi/add-kcfi-flag.rs +++ b/tests/codegen/sanitizer/kcfi/add-kcfi-flag.rs @@ -8,15 +8,14 @@ //@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi #![feature(no_core, lang_items)] -#![crate_type="lib"] +#![crate_type = "lib"] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="copy"] -trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} -pub fn foo() { -} +pub fn foo() {} // CHECK: !{{[0-9]+}} = !{i32 4, !"kcfi", i32 1} diff --git a/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-no-sanitize.rs b/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-no-sanitize.rs index 8055c63a2f8f..ba2e397f6daa 100644 --- a/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-no-sanitize.rs +++ b/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-no-sanitize.rs @@ -7,14 +7,14 @@ //@ [x86_64] needs-llvm-components: //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(no_core, no_sanitize, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="copy"] -trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} impl Copy for i32 {} diff --git a/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs b/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs index bd1dfc4c413e..4e95bdf4d7c4 100644 --- a/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs +++ b/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs @@ -7,14 +7,14 @@ //@ [x86_64] needs-llvm-components: //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Zsanitizer-cfi-generalize-pointers -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(no_core, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="copy"] -trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} impl Copy for i32 {} diff --git a/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs b/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs index b8275f44fac5..31b43b509880 100644 --- a/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs +++ b/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs @@ -7,14 +7,14 @@ //@ [x86_64] needs-llvm-components: //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers -Zsanitizer-cfi-generalize-pointers -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(no_core, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="copy"] -trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} impl Copy for i32 {} diff --git a/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs b/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs index cd1b0c5efb0a..4755f6062aaa 100644 --- a/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs +++ b/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs @@ -7,14 +7,14 @@ //@ [x86_64] needs-llvm-components: //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(no_core, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="copy"] -trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} impl Copy for i32 {} diff --git a/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs b/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs index 12690577da7a..be9760bd9afa 100644 --- a/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs +++ b/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs @@ -7,14 +7,14 @@ //@ [x86_64] needs-llvm-components: //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(no_core, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="copy"] -trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} impl Copy for i32 {} diff --git a/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle.rs b/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle.rs index f4b3e48638e4..c9c94cdb3292 100644 --- a/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle.rs +++ b/tests/codegen/sanitizer/kcfi/emit-kcfi-operand-bundle.rs @@ -7,14 +7,14 @@ //@ [x86_64] needs-llvm-components: //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(no_core, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="copy"] -trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} impl Copy for i32 {} diff --git a/tests/codegen/sanitizer/kcfi/emit-type-metadata-trait-objects.rs b/tests/codegen/sanitizer/kcfi/emit-type-metadata-trait-objects.rs index f9c7cca39898..fc4d570dc2eb 100644 --- a/tests/codegen/sanitizer/kcfi/emit-type-metadata-trait-objects.rs +++ b/tests/codegen/sanitizer/kcfi/emit-type-metadata-trait-objects.rs @@ -7,30 +7,32 @@ //@ [x86_64] needs-llvm-components: //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(arbitrary_self_types, no_core, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="copy"] -trait Copy { } -#[lang="receiver"] -trait Receiver { } -#[lang="dispatch_from_dyn"] -trait DispatchFromDyn { } +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} +#[lang = "receiver"] +trait Receiver {} +#[lang = "dispatch_from_dyn"] +trait DispatchFromDyn {} impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} #[lang = "unsize"] -trait Unsize { } +trait Unsize {} #[lang = "coerce_unsized"] -pub trait CoerceUnsized { } +pub trait CoerceUnsized {} impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} -#[lang="freeze"] -trait Freeze { } -#[lang="drop_in_place"] -fn drop_in_place_fn() { } -#[lang="drop"] -trait Drop { fn drop(&mut self); } +#[lang = "freeze"] +trait Freeze {} +#[lang = "drop_in_place"] +fn drop_in_place_fn() {} +#[lang = "drop"] +trait Drop { + fn drop(&mut self); +} pub trait Trait1 { fn foo(&self); @@ -39,8 +41,7 @@ pub trait Trait1 { pub struct Type1; impl Trait1 for Type1 { - fn foo(&self) { - } + fn foo(&self) {} } pub trait Trait2 { @@ -50,8 +51,7 @@ pub trait Trait2 { pub struct Type2; impl Trait2 for Type2 { - fn bar(&self) { - } + fn bar(&self) {} } pub trait Trait3 { @@ -61,8 +61,7 @@ pub trait Trait3 { pub struct Type3; impl Trait3 for T { - fn baz(&self, _: &U) { - } + fn baz(&self, _: &U) {} } pub trait Trait4<'a, T> { @@ -88,8 +87,7 @@ pub struct Type5; impl Copy for Type5 {} impl Trait5 for T { - fn quux(&self, _: &[U; N]) { - } + fn quux(&self, _: &[U; N]) {} } pub fn foo1(a: &dyn Trait1) { diff --git a/tests/codegen/sanitizer/memory-track-origins.rs b/tests/codegen/sanitizer/memory-track-origins.rs index 956053ec42c7..318c277e10cb 100644 --- a/tests/codegen/sanitizer/memory-track-origins.rs +++ b/tests/codegen/sanitizer/memory-track-origins.rs @@ -11,7 +11,7 @@ //@[MSAN-1-LTO] compile-flags: -Zsanitizer-memory-track-origins=1 -C lto=fat //@[MSAN-2-LTO] compile-flags: -Zsanitizer-memory-track-origins -C lto=fat -#![crate_type="lib"] +#![crate_type = "lib"] // MSAN-0-NOT: @__msan_track_origins // MSAN-1: @__msan_track_origins = weak_odr {{.*}}constant i32 1 diff --git a/tests/codegen/sanitizer/no-sanitize-inlining.rs b/tests/codegen/sanitizer/no-sanitize-inlining.rs index d5e47884f858..4bd832d2ab19 100644 --- a/tests/codegen/sanitizer/no-sanitize-inlining.rs +++ b/tests/codegen/sanitizer/no-sanitize-inlining.rs @@ -8,7 +8,7 @@ //@[ASAN] compile-flags: -Zsanitizer=address //@[LSAN] compile-flags: -Zsanitizer=leak -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(no_sanitize)] // ASAN-LABEL: define void @test diff --git a/tests/codegen/sanitizer/no-sanitize.rs b/tests/codegen/sanitizer/no-sanitize.rs index 2614416210ff..47d3fd83f112 100644 --- a/tests/codegen/sanitizer/no-sanitize.rs +++ b/tests/codegen/sanitizer/no-sanitize.rs @@ -4,7 +4,7 @@ //@ needs-sanitizer-address //@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static -Copt-level=0 -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(no_sanitize)] // CHECK-LABEL: ; no_sanitize::unsanitized diff --git a/tests/codegen/set-discriminant-invalid.rs b/tests/codegen/set-discriminant-invalid.rs index 593da8cf80d0..0b7cb14880c9 100644 --- a/tests/codegen/set-discriminant-invalid.rs +++ b/tests/codegen/set-discriminant-invalid.rs @@ -7,17 +7,12 @@ pub struct TokioError { b: bool, } pub enum Error { - Api { - source: ApiError, - }, + Api { source: ApiError }, Ethereum, - Tokio { - source: TokioError, - }, + Tokio { source: TokioError }, } struct Api; -impl IntoError for Api -{ +impl IntoError for Api { type Source = ApiError; // CHECK-LABEL: @into_error // CHECK: llvm.trap() @@ -27,14 +22,11 @@ impl IntoError for Api // CHECK-NEXT: ret #[no_mangle] fn into_error(self, error: Self::Source) -> Error { - Error::Api { - source: error, - } + Error::Api { source: error } } } -pub trait IntoError -{ +pub trait IntoError { /// The underlying error type Source; diff --git a/tests/codegen/simd/issue-120720-reduce-nan.rs b/tests/codegen/simd/issue-120720-reduce-nan.rs index 0372582bf7f3..13af0bb076e6 100644 --- a/tests/codegen/simd/issue-120720-reduce-nan.rs +++ b/tests/codegen/simd/issue-120720-reduce-nan.rs @@ -15,8 +15,7 @@ pub unsafe fn demo() -> bool { // CHECK: %0 = tail call reassoc double @llvm.vector.reduce.fadd.v8f64( // CHECK: %_0.i = fcmp uno double %0, 0.000000e+00 // CHECK: ret i1 %_0.i - let res = unsafe { - _mm512_reduce_add_pd(_mm512_set_pd(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, f64::NAN)) - }; + let res = + unsafe { _mm512_reduce_add_pd(_mm512_set_pd(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, f64::NAN)) }; res.is_nan() } diff --git a/tests/codegen/simd/packed-simd-alignment.rs b/tests/codegen/simd/packed-simd-alignment.rs new file mode 100644 index 000000000000..53e88d8e5cf1 --- /dev/null +++ b/tests/codegen/simd/packed-simd-alignment.rs @@ -0,0 +1,44 @@ +//@ compile-flags: -Cno-prepopulate-passes + +#![crate_type = "lib"] +#![feature(repr_simd, core_intrinsics)] +// make sure that codegen emits correctly-aligned loads and stores for repr(packed, simd) types +// the alignment of a load should be no less than T, and no more than the size of the vector type +use std::intrinsics::simd as intrinsics; + +#[derive(Copy, Clone)] +#[repr(packed, simd)] +struct f32x3([f32; 3]); + +#[derive(Copy, Clone)] +#[repr(packed, simd)] +struct f32x4([f32; 4]); + +// CHECK-LABEL: load_f32x3 +#[no_mangle] +pub fn load_f32x3(floats: &f32x3) -> f32x3 { + // FIXME: Is a memcpy really the best we can do? + // CHECK: @llvm.memcpy.{{.*}}ptr align 4 {{.*}}ptr align 4 + *floats +} + +// CHECK-LABEL: load_f32x4 +#[no_mangle] +pub fn load_f32x4(floats: &f32x4) -> f32x4 { + // CHECK: load <4 x float>, ptr %{{[a-z0-9_]*}}, align {{4|8|16}} + *floats +} + +// CHECK-LABEL: add_f32x3 +#[no_mangle] +pub fn add_f32x3(x: f32x3, y: f32x3) -> f32x3 { + // CHECK: load <3 x float>, ptr %{{[a-z0-9_]*}}, align 4 + unsafe { intrinsics::simd_add(x, y) } +} + +// CHECK-LABEL: add_f32x4 +#[no_mangle] +pub fn add_f32x4(x: f32x4, y: f32x4) -> f32x4 { + // CHECK: load <4 x float>, ptr %{{[a-z0-9_]*}}, align {{4|8|16}} + unsafe { intrinsics::simd_add(x, y) } +} diff --git a/tests/codegen/simd/packed-simd.rs b/tests/codegen/simd/packed-simd.rs new file mode 100644 index 000000000000..1df09c96e6cc --- /dev/null +++ b/tests/codegen/simd/packed-simd.rs @@ -0,0 +1,59 @@ +//@ revisions:opt3 noopt +//@[opt3] compile-flags: -Copt-level=3 +//@[noopt] compile-flags: -Cno-prepopulate-passes + +#![crate_type = "lib"] +#![no_std] +#![feature(repr_simd, core_intrinsics)] +use core::intrinsics::simd as intrinsics; +use core::{mem, ptr}; + +// Test codegen for not only "packed" but also "fully aligned" SIMD types, and conversion between +// them. A repr(packed,simd) type with 3 elements can't exceed its element alignment, whereas the +// same type as repr(simd) will instead have padding. + +#[repr(simd, packed)] +#[derive(Copy, Clone)] +pub struct Simd([T; N]); + +#[repr(simd)] +#[derive(Copy, Clone)] +pub struct FullSimd([T; N]); + +// non-powers-of-two have padding and need to be expanded to full vectors +fn load(v: Simd) -> FullSimd { + unsafe { + let mut tmp = mem::MaybeUninit::>::uninit(); + ptr::copy_nonoverlapping(&v as *const _, tmp.as_mut_ptr().cast(), 1); + tmp.assume_init() + } +} + +// CHECK-LABEL: square_packed_full +// CHECK-SAME: ptr{{[a-z_ ]*}} sret([[RET_TYPE:[^)]+]]) [[RET_ALIGN:align (8|16)]]{{[^%]*}} [[RET_VREG:%[_0-9]*]] +// CHECK-SAME: ptr{{[a-z_ ]*}} align 4 +#[no_mangle] +pub fn square_packed_full(x: Simd) -> FullSimd { + // CHECK-NEXT: start + // noopt: alloca [[RET_TYPE]], [[RET_ALIGN]] + // CHECK: load <3 x float> + let x = load(x); + // CHECK: [[VREG:%[a-z0-9_]+]] = fmul <3 x float> + // CHECK-NEXT: store <3 x float> [[VREG]], ptr [[RET_VREG]], [[RET_ALIGN]] + // CHECK-NEXT: ret void + unsafe { intrinsics::simd_mul(x, x) } +} + +// CHECK-LABEL: square_packed +// CHECK-SAME: ptr{{[a-z_ ]*}} sret([[RET_TYPE:[^)]+]]) [[RET_ALIGN:align 4]]{{[^%]*}} [[RET_VREG:%[_0-9]*]] +// CHECK-SAME: ptr{{[a-z_ ]*}} align 4 +#[no_mangle] +pub fn square_packed(x: Simd) -> Simd { + // CHECK-NEXT: start + // CHECK-NEXT: load <3 x float> + // noopt-NEXT: load <3 x float> + // CHECK-NEXT: [[VREG:%[a-z0-9_]+]] = fmul <3 x float> + // CHECK-NEXT: store <3 x float> [[VREG]], ptr [[RET_VREG]], [[RET_ALIGN]] + // CHECK-NEXT: ret void + unsafe { intrinsics::simd_mul(x, x) } +} diff --git a/tests/codegen/simd/swap-simd-types.rs b/tests/codegen/simd/swap-simd-types.rs index 32e75220d696..cd6e84286e1c 100644 --- a/tests/codegen/simd/swap-simd-types.rs +++ b/tests/codegen/simd/swap-simd-types.rs @@ -13,18 +13,18 @@ use core::arch::x86_64::__m256; // CHECK-LABEL: @swap_single_m256 #[no_mangle] pub fn swap_single_m256(x: &mut __m256, y: &mut __m256) { -// CHECK-NOT: alloca -// CHECK: load <8 x float>{{.+}}align 32 -// CHECK: store <8 x float>{{.+}}align 32 + // CHECK-NOT: alloca + // CHECK: load <8 x float>{{.+}}align 32 + // CHECK: store <8 x float>{{.+}}align 32 swap(x, y) } // CHECK-LABEL: @swap_m256_slice #[no_mangle] pub fn swap_m256_slice(x: &mut [__m256], y: &mut [__m256]) { -// CHECK-NOT: alloca -// CHECK: load <8 x float>{{.+}}align 32 -// CHECK: store <8 x float>{{.+}}align 32 + // CHECK-NOT: alloca + // CHECK: load <8 x float>{{.+}}align 32 + // CHECK: store <8 x float>{{.+}}align 32 if x.len() == y.len() { x.swap_with_slice(y); } @@ -33,8 +33,8 @@ pub fn swap_m256_slice(x: &mut [__m256], y: &mut [__m256]) { // CHECK-LABEL: @swap_bytes32 #[no_mangle] pub fn swap_bytes32(x: &mut [u8; 32], y: &mut [u8; 32]) { -// CHECK-NOT: alloca -// CHECK: load <32 x i8>{{.+}}align 1 -// CHECK: store <32 x i8>{{.+}}align 1 + // CHECK-NOT: alloca + // CHECK: load <32 x i8>{{.+}}align 1 + // CHECK: store <32 x i8>{{.+}}align 1 swap(x, y) } diff --git a/tests/codegen/slice-pointer-nonnull-unwrap.rs b/tests/codegen/slice-pointer-nonnull-unwrap.rs new file mode 100644 index 000000000000..2c4a959685f9 --- /dev/null +++ b/tests/codegen/slice-pointer-nonnull-unwrap.rs @@ -0,0 +1,19 @@ +//@ compile-flags: -O +//@ min-llvm-version: 18 +#![crate_type = "lib"] + +use std::ptr::NonNull; + +// CHECK-LABEL: @slice_ptr_len_1 +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret {{i(32|64)}} %ptr.1 +#[no_mangle] +pub fn slice_ptr_len_1(ptr: *const [u8]) -> usize { + let ptr = ptr.cast_mut(); + if let Some(ptr) = NonNull::new(ptr) { + ptr.len() + } else { + // We know ptr is null, so we know ptr.wrapping_byte_add(1) is not null. + NonNull::new(ptr.wrapping_byte_add(1)).unwrap().len() + } +} diff --git a/tests/codegen/slice-position-bounds-check.rs b/tests/codegen/slice-position-bounds-check.rs index 301895883ee3..f83e2f2ec440 100644 --- a/tests/codegen/slice-position-bounds-check.rs +++ b/tests/codegen/slice-position-bounds-check.rs @@ -3,9 +3,7 @@ fn search(arr: &mut [T], a: &T) -> Result { match arr.iter().position(|x| x == a) { - Some(p) => { - Ok(p) - }, + Some(p) => Ok(p), None => Err(()), } } @@ -15,11 +13,7 @@ fn search(arr: &mut [T], a: &T) -> Result { pub fn position_no_bounds_check(y: &mut [u32], x: &u32, z: &u32) -> bool { // This contains "call assume" so we cannot just rule out all calls // CHECK-NOT: panic_bounds_check - if let Ok(p) = search(y, x) { - y[p] == *z - } else { - false - } + if let Ok(p) = search(y, x) { y[p] == *z } else { false } } // just to make sure that panicking really emits "panic_bounds_check" somewhere in the IR diff --git a/tests/codegen/slice-windows-no-bounds-check.rs b/tests/codegen/slice-windows-no-bounds-check.rs index 188011ebe7fd..db3211c8defd 100644 --- a/tests/codegen/slice-windows-no-bounds-check.rs +++ b/tests/codegen/slice-windows-no-bounds-check.rs @@ -12,10 +12,7 @@ pub fn naive_string_search(haystack: &str, needle: &str) -> Option { } // CHECK-NOT: panic // CHECK-NOT: fail - haystack - .as_bytes() - .windows(needle.len()) - .position(|sub| sub == needle.as_bytes()) + haystack.as_bytes().windows(needle.len()).position(|sub| sub == needle.as_bytes()) } // CHECK-LABEL: @next diff --git a/tests/codegen/some-abis-do-extend-params-to-32-bits.rs b/tests/codegen/some-abis-do-extend-params-to-32-bits.rs index a75a239e2ab8..10970cacdcf0 100644 --- a/tests/codegen/some-abis-do-extend-params-to-32-bits.rs +++ b/tests/codegen/some-abis-do-extend-params-to-32-bits.rs @@ -24,9 +24,12 @@ #![no_std] #![no_core] -#[lang="sized"] trait Sized { } -#[lang="freeze"] trait Freeze { } -#[lang="copy"] trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} // The patterns in this file are written in the style of a table to make the // uniformities and distinctions more apparent. @@ -40,7 +43,8 @@ // aarch64-linux: void @c_arg_u8(i8 %_a) // arm: void @c_arg_u8(i8 zeroext %_a) // riscv: void @c_arg_u8(i8 zeroext %_a) -#[no_mangle] pub extern "C" fn c_arg_u8(_a: u8) { } +#[no_mangle] +pub extern "C" fn c_arg_u8(_a: u8) {} // x86_64: void @c_arg_u16(i16 zeroext %_a) // i686: void @c_arg_u16(i16 zeroext %_a) @@ -49,7 +53,8 @@ // aarch64-linux: void @c_arg_u16(i16 %_a) // arm: void @c_arg_u16(i16 zeroext %_a) // riscv: void @c_arg_u16(i16 zeroext %_a) -#[no_mangle] pub extern "C" fn c_arg_u16(_a: u16) { } +#[no_mangle] +pub extern "C" fn c_arg_u16(_a: u16) {} // x86_64: void @c_arg_u32(i32 %_a) // i686: void @c_arg_u32(i32 %_a) @@ -58,7 +63,8 @@ // aarch64-linux: void @c_arg_u32(i32 %_a) // arm: void @c_arg_u32(i32 %_a) // riscv: void @c_arg_u32(i32 signext %_a) -#[no_mangle] pub extern "C" fn c_arg_u32(_a: u32) { } +#[no_mangle] +pub extern "C" fn c_arg_u32(_a: u32) {} // x86_64: void @c_arg_u64(i64 %_a) // i686: void @c_arg_u64(i64 %_a) @@ -67,7 +73,8 @@ // aarch64-linux: void @c_arg_u64(i64 %_a) // arm: void @c_arg_u64(i64 %_a) // riscv: void @c_arg_u64(i64 %_a) -#[no_mangle] pub extern "C" fn c_arg_u64(_a: u64) { } +#[no_mangle] +pub extern "C" fn c_arg_u64(_a: u64) {} // x86_64: void @c_arg_i8(i8 signext %_a) // i686: void @c_arg_i8(i8 signext %_a) @@ -76,7 +83,8 @@ // aarch64-linux: void @c_arg_i8(i8 %_a) // arm: void @c_arg_i8(i8 signext %_a) // riscv: void @c_arg_i8(i8 signext %_a) -#[no_mangle] pub extern "C" fn c_arg_i8(_a: i8) { } +#[no_mangle] +pub extern "C" fn c_arg_i8(_a: i8) {} // x86_64: void @c_arg_i16(i16 signext %_a) // i686: void @c_arg_i16(i16 signext %_a) @@ -85,7 +93,8 @@ // aarch64-linux: void @c_arg_i16(i16 %_a) // arm: void @c_arg_i16(i16 signext %_a) // riscv: void @c_arg_i16(i16 signext %_a) -#[no_mangle] pub extern "C" fn c_arg_i16(_a: i16) { } +#[no_mangle] +pub extern "C" fn c_arg_i16(_a: i16) {} // x86_64: void @c_arg_i32(i32 %_a) // i686: void @c_arg_i32(i32 %_a) @@ -94,7 +103,8 @@ // aarch64-linux: void @c_arg_i32(i32 %_a) // arm: void @c_arg_i32(i32 %_a) // riscv: void @c_arg_i32(i32 signext %_a) -#[no_mangle] pub extern "C" fn c_arg_i32(_a: i32) { } +#[no_mangle] +pub extern "C" fn c_arg_i32(_a: i32) {} // x86_64: void @c_arg_i64(i64 %_a) // i686: void @c_arg_i64(i64 %_a) @@ -103,7 +113,8 @@ // aarch64-linux: void @c_arg_i64(i64 %_a) // arm: void @c_arg_i64(i64 %_a) // riscv: void @c_arg_i64(i64 %_a) -#[no_mangle] pub extern "C" fn c_arg_i64(_a: i64) { } +#[no_mangle] +pub extern "C" fn c_arg_i64(_a: i64) {} // x86_64: zeroext i8 @c_ret_u8() // i686: zeroext i8 @c_ret_u8() @@ -112,7 +123,10 @@ // aarch64-linux: i8 @c_ret_u8() // arm: zeroext i8 @c_ret_u8() // riscv: zeroext i8 @c_ret_u8() -#[no_mangle] pub extern "C" fn c_ret_u8() -> u8 { 0 } +#[no_mangle] +pub extern "C" fn c_ret_u8() -> u8 { + 0 +} // x86_64: zeroext i16 @c_ret_u16() // i686: zeroext i16 @c_ret_u16() @@ -121,7 +135,10 @@ // aarch64-linux: i16 @c_ret_u16() // arm: zeroext i16 @c_ret_u16() // riscv: zeroext i16 @c_ret_u16() -#[no_mangle] pub extern "C" fn c_ret_u16() -> u16 { 0 } +#[no_mangle] +pub extern "C" fn c_ret_u16() -> u16 { + 0 +} // x86_64: i32 @c_ret_u32() // i686: i32 @c_ret_u32() @@ -130,7 +147,10 @@ // aarch64-linux: i32 @c_ret_u32() // arm: i32 @c_ret_u32() // riscv: signext i32 @c_ret_u32() -#[no_mangle] pub extern "C" fn c_ret_u32() -> u32 { 0 } +#[no_mangle] +pub extern "C" fn c_ret_u32() -> u32 { + 0 +} // x86_64: i64 @c_ret_u64() // i686: i64 @c_ret_u64() @@ -139,7 +159,10 @@ // aarch64-linux: i64 @c_ret_u64() // arm: i64 @c_ret_u64() // riscv: i64 @c_ret_u64() -#[no_mangle] pub extern "C" fn c_ret_u64() -> u64 { 0 } +#[no_mangle] +pub extern "C" fn c_ret_u64() -> u64 { + 0 +} // x86_64: signext i8 @c_ret_i8() // i686: signext i8 @c_ret_i8() @@ -148,7 +171,10 @@ // aarch64-linux: i8 @c_ret_i8() // arm: signext i8 @c_ret_i8() // riscv: signext i8 @c_ret_i8() -#[no_mangle] pub extern "C" fn c_ret_i8() -> i8 { 0 } +#[no_mangle] +pub extern "C" fn c_ret_i8() -> i8 { + 0 +} // x86_64: signext i16 @c_ret_i16() // i686: signext i16 @c_ret_i16() @@ -157,7 +183,10 @@ // aarch64-linux: i16 @c_ret_i16() // arm: signext i16 @c_ret_i16() // riscv: signext i16 @c_ret_i16() -#[no_mangle] pub extern "C" fn c_ret_i16() -> i16 { 0 } +#[no_mangle] +pub extern "C" fn c_ret_i16() -> i16 { + 0 +} // x86_64: i32 @c_ret_i32() // i686: i32 @c_ret_i32() @@ -166,7 +195,10 @@ // aarch64-linux: i32 @c_ret_i32() // arm: i32 @c_ret_i32() // riscv: signext i32 @c_ret_i32() -#[no_mangle] pub extern "C" fn c_ret_i32() -> i32 { 0 } +#[no_mangle] +pub extern "C" fn c_ret_i32() -> i32 { + 0 +} // x86_64: i64 @c_ret_i64() // i686: i64 @c_ret_i64() @@ -175,7 +207,10 @@ // aarch64-linux: i64 @c_ret_i64() // arm: i64 @c_ret_i64() // riscv: i64 @c_ret_i64() -#[no_mangle] pub extern "C" fn c_ret_i64() -> i64 { 0 } +#[no_mangle] +pub extern "C" fn c_ret_i64() -> i64 { + 0 +} const C_SOURCE_FILE: &'static str = r##" #include diff --git a/tests/codegen/sparc-struct-abi.rs b/tests/codegen/sparc-struct-abi.rs index 0850e9e15eaf..5d9781663570 100644 --- a/tests/codegen/sparc-struct-abi.rs +++ b/tests/codegen/sparc-struct-abi.rs @@ -6,12 +6,12 @@ #![feature(no_core, lang_items)] #![no_core] -#[lang="sized"] -trait Sized { } -#[lang="freeze"] -trait Freeze { } -#[lang="copy"] -trait Copy { } +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} #[repr(C)] pub struct Bool { @@ -26,7 +26,6 @@ pub extern "C" fn structbool() -> Bool { Bool { b: true } } - #[repr(C)] pub struct BoolFloat { b: bool, @@ -44,8 +43,7 @@ pub extern "C" fn structboolfloat() -> BoolFloat { // CHECK: define void @structboolfloat_input({ i32, float } inreg %0) // CHECK-NEXT: start: #[no_mangle] -pub extern "C" fn structboolfloat_input(a: BoolFloat) { } - +pub extern "C" fn structboolfloat_input(a: BoolFloat) {} #[repr(C)] pub struct ShortDouble { @@ -64,8 +62,7 @@ pub extern "C" fn structshortdouble() -> ShortDouble { // CHECK: define void @structshortdouble_input({ i64, double } %0) // CHECK-NEXT: start: #[no_mangle] -pub extern "C" fn structshortdouble_input(a: ShortDouble) { } - +pub extern "C" fn structshortdouble_input(a: ShortDouble) {} #[repr(C)] pub struct FloatLongFloat { diff --git a/tests/codegen/split-lto-unit.rs b/tests/codegen/split-lto-unit.rs index 6cea9a2d8d5f..7858a0e7b796 100644 --- a/tests/codegen/split-lto-unit.rs +++ b/tests/codegen/split-lto-unit.rs @@ -3,9 +3,8 @@ //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Ctarget-feature=-crt-static -Zsplit-lto-unit -#![crate_type="lib"] +#![crate_type = "lib"] -pub fn foo() { -} +pub fn foo() {} // CHECK: !{{[0-9]+}} = !{i32 4, !"EnableSplitLTOUnit", i32 1} diff --git a/tests/codegen/sroa-fragment-debuginfo.rs b/tests/codegen/sroa-fragment-debuginfo.rs index 670ddb56540e..0413cf968942 100644 --- a/tests/codegen/sroa-fragment-debuginfo.rs +++ b/tests/codegen/sroa-fragment-debuginfo.rs @@ -13,13 +13,13 @@ pub struct ExtraSlice<'input> { #[no_mangle] pub fn extra(s: &[u8]) { -// CHECK: void @extra( -// CHECK: %slice.dbg.spill1 = alloca [4 x i8], -// CHECK: %slice.dbg.spill = alloca [16 x i8], -// CHECK: %s.dbg.spill = alloca [16 x i8], -// CHECK: dbg{{.}}declare({{(metadata )?}}ptr %s.dbg.spill, {{(metadata )?}}![[S_EXTRA:.*]], {{(metadata )?}}!DIExpression() -// CHECK: dbg{{.}}declare({{(metadata )?}}ptr %slice.dbg.spill, {{(metadata )?}}![[SLICE_EXTRA:.*]], {{(metadata )?}}!DIExpression(DW_OP_LLVM_fragment, 0, 128) -// CHECK: dbg{{.}}declare({{(metadata )?}}ptr %slice.dbg.spill1, {{(metadata )?}}![[SLICE_EXTRA]], {{(metadata )?}}!DIExpression(DW_OP_LLVM_fragment, 128, 32) + // CHECK: void @extra( + // CHECK: %slice.dbg.spill1 = alloca [4 x i8], + // CHECK: %slice.dbg.spill = alloca [16 x i8], + // CHECK: %s.dbg.spill = alloca [16 x i8], + // CHECK: dbg{{.}}declare({{(metadata )?}}ptr %s.dbg.spill, {{(metadata )?}}![[S_EXTRA:.*]], {{(metadata )?}}!DIExpression() + // CHECK: dbg{{.}}declare({{(metadata )?}}ptr %slice.dbg.spill, {{(metadata )?}}![[SLICE_EXTRA:.*]], {{(metadata )?}}!DIExpression(DW_OP_LLVM_fragment, 0, 128) + // CHECK: dbg{{.}}declare({{(metadata )?}}ptr %slice.dbg.spill1, {{(metadata )?}}![[SLICE_EXTRA]], {{(metadata )?}}!DIExpression(DW_OP_LLVM_fragment, 128, 32) let slice = ExtraSlice { slice: s, extra: s.len() as u32 }; } @@ -35,10 +35,10 @@ pub fn zst(s: &[u8]) { // The field `extra` is a ZST. The fragment for the field `slice` encompasses the whole // variable, so is not a fragment. In that case, the variable must have no fragment. -// CHECK: void @zst( -// CHECK-NOT: dbg{{.}}declare({{(metadata )?}}ptr %slice.dbg.spill, {{(metadata )?}}!{}, {{(metadata )?}}!DIExpression(DW_OP_LLVM_fragment, -// CHECK: dbg{{.}}declare({{(metadata )?}}ptr %{{.*}}, {{(metadata )?}}![[SLICE_ZST:.*]], {{(metadata )?}}!DIExpression() -// CHECK-NOT: dbg{{.}}declare({{(metadata )?}}ptr %{{.*}}, {{(metadata )?}}![[SLICE_ZST]], + // CHECK: void @zst( + // CHECK-NOT: dbg{{.}}declare({{(metadata )?}}ptr %slice.dbg.spill, {{(metadata )?}}!{}, {{(metadata )?}}!DIExpression(DW_OP_LLVM_fragment, + // CHECK: dbg{{.}}declare({{(metadata )?}}ptr %{{.*}}, {{(metadata )?}}![[SLICE_ZST:.*]], {{(metadata )?}}!DIExpression() + // CHECK-NOT: dbg{{.}}declare({{(metadata )?}}ptr %{{.*}}, {{(metadata )?}}![[SLICE_ZST]], let slice = ZstSlice { slice: s, extra: Zst }; } diff --git a/tests/codegen/stack-probes-inline.rs b/tests/codegen/stack-probes-inline.rs index 6bd6b0cb354c..c5073b9cc221 100644 --- a/tests/codegen/stack-probes-inline.rs +++ b/tests/codegen/stack-probes-inline.rs @@ -27,6 +27,6 @@ trait Sized {} #[no_mangle] pub fn foo() { -// CHECK: @foo() unnamed_addr #0 -// CHECK: attributes #0 = { {{.*}}"probe-stack"="inline-asm"{{.*}} } + // CHECK: @foo() unnamed_addr #0 + // CHECK: attributes #0 = { {{.*}}"probe-stack"="inline-asm"{{.*}} } } diff --git a/tests/codegen/static-relocation-model-msvc.rs b/tests/codegen/static-relocation-model-msvc.rs index 5501827498cc..8ed8331466c7 100644 --- a/tests/codegen/static-relocation-model-msvc.rs +++ b/tests/codegen/static-relocation-model-msvc.rs @@ -20,7 +20,5 @@ extern crate extern_decl; #[no_mangle] pub fn access_extern() -> u8 { - unsafe { - extern_decl::extern_fn() + extern_decl::extern_static - } + unsafe { extern_decl::extern_fn() + extern_decl::extern_static } } diff --git a/tests/codegen/stores.rs b/tests/codegen/stores.rs index 86ec52fa1015..aa3090db6d31 100644 --- a/tests/codegen/stores.rs +++ b/tests/codegen/stores.rs @@ -4,10 +4,10 @@ #![crate_type = "lib"] pub struct Bytes { - a: u8, - b: u8, - c: u8, - d: u8, + a: u8, + b: u8, + c: u8, + d: u8, } // CHECK-LABEL: small_array_alignment @@ -15,10 +15,10 @@ pub struct Bytes { // dependent alignment #[no_mangle] pub fn small_array_alignment(x: &mut [i8; 4], y: [i8; 4]) { -// CHECK: [[TMP:%.+]] = alloca [4 x i8], align 4 -// CHECK: %y = alloca [4 x i8], align 1 -// CHECK: store i32 %0, ptr [[TMP]] -// CHECK: call void @llvm.memcpy.{{.*}}(ptr align 1 {{.+}}, ptr align 4 {{.+}}, i{{[0-9]+}} 4, i1 false) + // CHECK: [[TMP:%.+]] = alloca [4 x i8], align 4 + // CHECK: %y = alloca [4 x i8], align 1 + // CHECK: store i32 %0, ptr [[TMP]] + // CHECK: call void @llvm.memcpy.{{.*}}(ptr align 1 {{.+}}, ptr align 4 {{.+}}, i{{[0-9]+}} 4, i1 false) *x = y; } @@ -27,9 +27,9 @@ pub fn small_array_alignment(x: &mut [i8; 4], y: [i8; 4]) { // dependent alignment #[no_mangle] pub fn small_struct_alignment(x: &mut Bytes, y: Bytes) { -// CHECK: [[TMP:%.+]] = alloca [4 x i8], align 4 -// CHECK: %y = alloca [4 x i8], align 1 -// CHECK: store i32 %0, ptr [[TMP]] -// CHECK: call void @llvm.memcpy.{{.*}}(ptr align 1 {{.+}}, ptr align 4 {{.+}}, i{{[0-9]+}} 4, i1 false) + // CHECK: [[TMP:%.+]] = alloca [4 x i8], align 4 + // CHECK: %y = alloca [4 x i8], align 1 + // CHECK: store i32 %0, ptr [[TMP]] + // CHECK: call void @llvm.memcpy.{{.*}}(ptr align 1 {{.+}}, ptr align 4 {{.+}}, i{{[0-9]+}} 4, i1 false) *x = y; } diff --git a/tests/codegen/swap-large-types.rs b/tests/codegen/swap-large-types.rs index b976f6fe207b..761d48969dad 100644 --- a/tests/codegen/swap-large-types.rs +++ b/tests/codegen/swap-large-types.rs @@ -4,7 +4,7 @@ #![crate_type = "lib"] use std::mem::swap; -use std::ptr::{read, copy_nonoverlapping, write}; +use std::ptr::{copy_nonoverlapping, read, write}; type KeccakBuffer = [[u64; 5]; 5]; @@ -15,7 +15,7 @@ type KeccakBuffer = [[u64; 5]; 5]; // CHECK-LABEL: @swap_basic #[no_mangle] pub fn swap_basic(x: &mut KeccakBuffer, y: &mut KeccakBuffer) { -// CHECK: alloca [200 x i8] + // CHECK: alloca [200 x i8] // SAFETY: exclusive references are always valid to read/write, // are non-overlapping, and nothing here panics so it's drop-safe. @@ -32,9 +32,9 @@ pub fn swap_basic(x: &mut KeccakBuffer, y: &mut KeccakBuffer) { // CHECK-LABEL: @swap_std #[no_mangle] pub fn swap_std(x: &mut KeccakBuffer, y: &mut KeccakBuffer) { -// CHECK-NOT: alloca -// CHECK: load <{{[0-9]+}} x i64> -// CHECK: store <{{[0-9]+}} x i64> + // CHECK-NOT: alloca + // CHECK: load <{{[0-9]+}} x i64> + // CHECK: store <{{[0-9]+}} x i64> swap(x, y) } @@ -44,9 +44,9 @@ pub fn swap_std(x: &mut KeccakBuffer, y: &mut KeccakBuffer) { // CHECK-LABEL: @swap_slice #[no_mangle] pub fn swap_slice(x: &mut [KeccakBuffer], y: &mut [KeccakBuffer]) { -// CHECK-NOT: alloca -// CHECK: load <{{[0-9]+}} x i64> -// CHECK: store <{{[0-9]+}} x i64> + // CHECK-NOT: alloca + // CHECK: load <{{[0-9]+}} x i64> + // CHECK: store <{{[0-9]+}} x i64> if x.len() == y.len() { x.swap_with_slice(y); } @@ -59,9 +59,9 @@ type OneKilobyteBuffer = [u8; 1024]; // CHECK-LABEL: @swap_1kb_slices #[no_mangle] pub fn swap_1kb_slices(x: &mut [OneKilobyteBuffer], y: &mut [OneKilobyteBuffer]) { -// CHECK-NOT: alloca -// CHECK: load <{{[0-9]+}} x i8> -// CHECK: store <{{[0-9]+}} x i8> + // CHECK-NOT: alloca + // CHECK: load <{{[0-9]+}} x i8> + // CHECK: store <{{[0-9]+}} x i8> if x.len() == y.len() { x.swap_with_slice(y); } @@ -81,10 +81,10 @@ pub struct BigButHighlyAligned([u8; 64 * 3]); // CHECK-LABEL: @swap_big_aligned #[no_mangle] pub fn swap_big_aligned(x: &mut BigButHighlyAligned, y: &mut BigButHighlyAligned) { -// CHECK-NOT: call void @llvm.memcpy -// CHECK: call void @llvm.memcpy.{{.+}}(ptr noundef nonnull align 64 dereferenceable(192) -// CHECK: call void @llvm.memcpy.{{.+}}(ptr noundef nonnull align 64 dereferenceable(192) -// CHECK: call void @llvm.memcpy.{{.+}}(ptr noundef nonnull align 64 dereferenceable(192) -// CHECK-NOT: call void @llvm.memcpy + // CHECK-NOT: call void @llvm.memcpy + // CHECK: call void @llvm.memcpy.{{.+}}(ptr noundef nonnull align 64 dereferenceable(192) + // CHECK: call void @llvm.memcpy.{{.+}}(ptr noundef nonnull align 64 dereferenceable(192) + // CHECK: call void @llvm.memcpy.{{.+}}(ptr noundef nonnull align 64 dereferenceable(192) + // CHECK-NOT: call void @llvm.memcpy swap(x, y) } diff --git a/tests/codegen/target-feature-overrides.rs b/tests/codegen/target-feature-overrides.rs index 61b1b3fd2578..1e2c364dbbc9 100644 --- a/tests/codegen/target-feature-overrides.rs +++ b/tests/codegen/target-feature-overrides.rs @@ -9,7 +9,6 @@ #![crate_type = "lib"] #![no_core] - #[lang = "sized"] trait Sized {} #[lang = "copy"] @@ -23,19 +22,19 @@ extern "C" { #[target_feature(enable = "avx")] #[no_mangle] pub unsafe fn apple() -> u32 { -// CHECK-LABEL: @apple() -// CHECK-SAME: [[APPLEATTRS:#[0-9]+]] { -// CHECK: {{.*}}call{{.*}}@peach + // CHECK-LABEL: @apple() + // CHECK-SAME: [[APPLEATTRS:#[0-9]+]] { + // CHECK: {{.*}}call{{.*}}@peach peach() } // target features same as global #[no_mangle] pub unsafe fn banana() -> u32 { -// CHECK-LABEL: @banana() -// CHECK-SAME: [[BANANAATTRS:#[0-9]+]] { -// COMPAT: {{.*}}call{{.*}}@peach -// INCOMPAT: {{.*}}call{{.*}}@apple + // CHECK-LABEL: @banana() + // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] { + // COMPAT: {{.*}}call{{.*}}@peach + // INCOMPAT: {{.*}}call{{.*}}@apple apple() // Compatible for inline in COMPAT revision and can't be inlined in INCOMPAT } diff --git a/tests/codegen/thin-lto.rs b/tests/codegen/thin-lto.rs index c75f9841a773..4339d20532ea 100644 --- a/tests/codegen/thin-lto.rs +++ b/tests/codegen/thin-lto.rs @@ -3,5 +3,4 @@ // CHECK: main -pub fn main() { -} +pub fn main() {} diff --git a/tests/codegen/tied-features-strength.rs b/tests/codegen/tied-features-strength.rs index b97865295a23..7f0805bc1b43 100644 --- a/tests/codegen/tied-features-strength.rs +++ b/tests/codegen/tied-features-strength.rs @@ -19,7 +19,6 @@ //@ [ENABLE_NEON] compile-flags: -C target-feature=+neon -Copt-level=0 // ENABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(\+fp-armv8,?)|(\+neon,?))*}}" } - #![feature(no_core, lang_items)] #![no_core] diff --git a/tests/codegen/to_vec.rs b/tests/codegen/to_vec.rs index 651084d811ca..4666f8d6f153 100644 --- a/tests/codegen/to_vec.rs +++ b/tests/codegen/to_vec.rs @@ -5,6 +5,6 @@ // CHECK-LABEL: @copy_to_vec #[no_mangle] fn copy_to_vec(s: &[u64]) -> Vec { - s.to_vec() - // CHECK: call void @llvm.memcpy + s.to_vec() + // CHECK: call void @llvm.memcpy } diff --git a/tests/codegen/transmute-optimized.rs b/tests/codegen/transmute-optimized.rs index 8e5bcb2340e4..11bd0523788a 100644 --- a/tests/codegen/transmute-optimized.rs +++ b/tests/codegen/transmute-optimized.rs @@ -5,7 +5,9 @@ // destination types for transmutes. #[repr(u32)] -pub enum AlwaysZero32 { X = 0 } +pub enum AlwaysZero32 { + X = 0, +} // CHECK-LABEL: i32 @issue_109958(i32 #[no_mangle] @@ -81,7 +83,11 @@ pub fn div_transmute_nonzero(a: u32, b: std::num::NonZero) -> u32 { } #[repr(i8)] -pub enum OneTwoThree { One = 1, Two = 2, Three = 3 } +pub enum OneTwoThree { + One = 1, + Two = 2, + Three = 3, +} // CHECK-LABEL: i8 @ordering_transmute_onetwothree(i8 #[no_mangle] diff --git a/tests/codegen/try_identity.rs b/tests/codegen/try_identity.rs index 87b7d0727d08..6a3a8a06e82b 100644 --- a/tests/codegen/try_identity.rs +++ b/tests/codegen/try_identity.rs @@ -13,9 +13,9 @@ type R = Result; // optimization that picks up the `?` desugaring, as `SimplifyArmIdentity` does not. #[no_mangle] pub fn try_identity(x: R) -> R { -// CHECK: start: -// FIXME(JakobDegen): Broken by deaggregation change CHECK-NOT\: br {{.*}} -// CHECK ret void + // CHECK: start: + // FIXME(JakobDegen): Broken by deaggregation change CHECK-NOT\: br {{.*}} + // CHECK ret void let y = match into_result(x) { Err(e) => return from_error(From::from(e)), Ok(v) => v, diff --git a/tests/codegen/try_question_mark_nop.rs b/tests/codegen/try_question_mark_nop.rs index f6cdf9552097..c23f41f54671 100644 --- a/tests/codegen/try_question_mark_nop.rs +++ b/tests/codegen/try_question_mark_nop.rs @@ -4,7 +4,7 @@ #![crate_type = "lib"] #![feature(try_blocks)] -use std::ops::ControlFlow::{self, Continue, Break}; +use std::ops::ControlFlow::{self, Break, Continue}; use std::ptr::NonNull; // CHECK-LABEL: @option_nop_match_32 @@ -27,9 +27,7 @@ pub fn option_nop_traits_32(x: Option) -> Option { // CHECK-NEXT: insertvalue { i32, i32 } // CHECK-NEXT: insertvalue { i32, i32 } // CHECK-NEXT: ret { i32, i32 } - try { - x? - } + try { x? } } // CHECK-LABEL: @result_nop_match_32 @@ -52,9 +50,7 @@ pub fn result_nop_traits_32(x: Result) -> Result { // CHECK-NEXT: insertvalue { i32, i32 } // CHECK-NEXT: insertvalue { i32, i32 } // CHECK-NEXT: ret { i32, i32 } - try { - x? - } + try { x? } } // CHECK-LABEL: @result_nop_match_64 @@ -77,9 +73,7 @@ pub fn result_nop_traits_64(x: Result) -> Result { // CHECK-NEXT: insertvalue { i64, i64 } // CHECK-NEXT: insertvalue { i64, i64 } // CHECK-NEXT: ret { i64, i64 } - try { - x? - } + try { x? } } // CHECK-LABEL: @result_nop_match_ptr @@ -102,9 +96,7 @@ pub fn result_nop_traits_ptr(x: Result>) -> Result) -> ControlFlow ScalarZstLast { loop {} } +pub fn test_ScalarZstLast(_: ScalarZstLast) -> ScalarZstLast { + loop {} +} type ScalarZstFirst = ((), u128); // CHECK: define {{(dso_local )?}}i128 @test_ScalarZstFirst(i128 %_1) #[no_mangle] -pub fn test_ScalarZstFirst(_: ScalarZstFirst) -> ScalarZstFirst { loop {} } +pub fn test_ScalarZstFirst(_: ScalarZstFirst) -> ScalarZstFirst { + loop {} +} type ScalarPairZstLast = (u8, u128, ()); // CHECK: define {{(dso_local )?}}{ i128, i8 } @test_ScalarPairZstLast(i128 %_1.0, i8 %_1.1) #[no_mangle] -pub fn test_ScalarPairZstLast(_: ScalarPairZstLast) -> ScalarPairZstLast { loop {} } +pub fn test_ScalarPairZstLast(_: ScalarPairZstLast) -> ScalarPairZstLast { + loop {} +} type ScalarPairZstFirst = ((), u8, u128); // CHECK: define {{(dso_local )?}}{ i8, i128 } @test_ScalarPairZstFirst(i8 %_1.0, i128 %_1.1) #[no_mangle] -pub fn test_ScalarPairZstFirst(_: ScalarPairZstFirst) -> ScalarPairZstFirst { loop {} } +pub fn test_ScalarPairZstFirst(_: ScalarPairZstFirst) -> ScalarPairZstFirst { + loop {} +} type ScalarPairLotsOfZsts = ((), u8, (), u128, ()); // CHECK: define {{(dso_local )?}}{ i128, i8 } @test_ScalarPairLotsOfZsts(i128 %_1.0, i8 %_1.1) #[no_mangle] -pub fn test_ScalarPairLotsOfZsts(_: ScalarPairLotsOfZsts) -> ScalarPairLotsOfZsts { loop {} } +pub fn test_ScalarPairLotsOfZsts(_: ScalarPairLotsOfZsts) -> ScalarPairLotsOfZsts { + loop {} +} type ScalarPairLottaNesting = (((), ((), u8, (), u128, ())), ()); // CHECK: define {{(dso_local )?}}{ i128, i8 } @test_ScalarPairLottaNesting(i128 %_1.0, i8 %_1.1) #[no_mangle] -pub fn test_ScalarPairLottaNesting(_: ScalarPairLottaNesting) -> ScalarPairLottaNesting { loop {} } +pub fn test_ScalarPairLottaNesting(_: ScalarPairLottaNesting) -> ScalarPairLottaNesting { + loop {} +} diff --git a/tests/codegen/union-abi.rs b/tests/codegen/union-abi.rs index b1b0daa13b64..9e02fa9ff359 100644 --- a/tests/codegen/union-abi.rs +++ b/tests/codegen/union-abi.rs @@ -4,7 +4,7 @@ // This test that using union forward the abi of the inner type, as // discussed in #54668 -#![crate_type="lib"] +#![crate_type = "lib"] #![feature(repr_simd)] #[derive(Copy, Clone)] @@ -15,62 +15,111 @@ pub enum Unhab {} pub struct i64x4(i64, i64, i64, i64); #[derive(Copy, Clone)] -pub union UnionI64x4{ a:(), b: i64x4 } +pub union UnionI64x4 { + a: (), + b: i64x4, +} // CHECK: define {{(dso_local )?}}void @test_UnionI64x4(ptr {{.*}} %_1) #[no_mangle] -pub fn test_UnionI64x4(_: UnionI64x4) { loop {} } +pub fn test_UnionI64x4(_: UnionI64x4) { + loop {} +} -pub union UnionI64x4_{ a: i64x4, b: (), c:i64x4, d: Unhab, e: ((),()), f: UnionI64x4 } +pub union UnionI64x4_ { + a: i64x4, + b: (), + c: i64x4, + d: Unhab, + e: ((), ()), + f: UnionI64x4, +} // CHECK: define {{(dso_local )?}}void @test_UnionI64x4_(ptr {{.*}} %_1) #[no_mangle] -pub fn test_UnionI64x4_(_: UnionI64x4_) { loop {} } +pub fn test_UnionI64x4_(_: UnionI64x4_) { + loop {} +} -pub union UnionI64x4I64{ a: i64x4, b: i64 } +pub union UnionI64x4I64 { + a: i64x4, + b: i64, +} // CHECK: define {{(dso_local )?}}void @test_UnionI64x4I64(ptr {{.*}} %_1) #[no_mangle] -pub fn test_UnionI64x4I64(_: UnionI64x4I64) { loop {} } +pub fn test_UnionI64x4I64(_: UnionI64x4I64) { + loop {} +} -pub union UnionI64x4Tuple{ a: i64x4, b: (i64, i64, i64, i64) } +pub union UnionI64x4Tuple { + a: i64x4, + b: (i64, i64, i64, i64), +} // CHECK: define {{(dso_local )?}}void @test_UnionI64x4Tuple(ptr {{.*}} %_1) #[no_mangle] -pub fn test_UnionI64x4Tuple(_: UnionI64x4Tuple) { loop {} } +pub fn test_UnionI64x4Tuple(_: UnionI64x4Tuple) { + loop {} +} - -pub union UnionF32{a:f32} +pub union UnionF32 { + a: f32, +} // CHECK: define {{(dso_local )?}}float @test_UnionF32(float %_1) #[no_mangle] -pub fn test_UnionF32(_: UnionF32) -> UnionF32 { loop {} } +pub fn test_UnionF32(_: UnionF32) -> UnionF32 { + loop {} +} -pub union UnionF32F32{a:f32, b:f32} +pub union UnionF32F32 { + a: f32, + b: f32, +} // CHECK: define {{(dso_local )?}}float @test_UnionF32F32(float %_1) #[no_mangle] -pub fn test_UnionF32F32(_: UnionF32F32) -> UnionF32F32 { loop {} } +pub fn test_UnionF32F32(_: UnionF32F32) -> UnionF32F32 { + loop {} +} -pub union UnionF32U32{a:f32, b:u32} +pub union UnionF32U32 { + a: f32, + b: u32, +} // CHECK: define {{(dso_local )?}}i32 @test_UnionF32U32(i32{{( %0)?}}) #[no_mangle] -pub fn test_UnionF32U32(_: UnionF32U32) -> UnionF32U32 { loop {} } +pub fn test_UnionF32U32(_: UnionF32U32) -> UnionF32U32 { + loop {} +} -pub union UnionU128{a:u128} +pub union UnionU128 { + a: u128, +} // CHECK: define {{(dso_local )?}}i128 @test_UnionU128(i128 %_1) #[no_mangle] -pub fn test_UnionU128(_: UnionU128) -> UnionU128 { loop {} } +pub fn test_UnionU128(_: UnionU128) -> UnionU128 { + loop {} +} #[repr(C)] -pub union CUnionU128{a:u128} +pub union CUnionU128 { + a: u128, +} // CHECK: define {{(dso_local )?}}void @test_CUnionU128(ptr {{.*}} %_1) #[no_mangle] -pub fn test_CUnionU128(_: CUnionU128) { loop {} } +pub fn test_CUnionU128(_: CUnionU128) { + loop {} +} -pub union UnionBool { b:bool } +pub union UnionBool { + b: bool, +} // CHECK: define {{(dso_local )?}}noundef zeroext i1 @test_UnionBool(i8 %b) #[no_mangle] -pub fn test_UnionBool(b: UnionBool) -> bool { unsafe { b.b } } +pub fn test_UnionBool(b: UnionBool) -> bool { + unsafe { b.b } +} // CHECK: %_0 = trunc i8 %b to i1 diff --git a/tests/codegen/unwind-abis/aapcs-unwind-abi.rs b/tests/codegen/unwind-abis/aapcs-unwind-abi.rs index 484af78105f1..48c7ecf78242 100644 --- a/tests/codegen/unwind-abis/aapcs-unwind-abi.rs +++ b/tests/codegen/unwind-abis/aapcs-unwind-abi.rs @@ -2,8 +2,8 @@ //@ compile-flags: --target=armv7-unknown-linux-gnueabihf --crate-type=rlib -Cno-prepopulate-passes #![no_core] #![feature(no_core, lang_items, c_unwind)] -#[lang="sized"] -trait Sized { } +#[lang = "sized"] +trait Sized {} // Test that `nounwind` attributes are correctly applied to exported `aapcs` and // `aapcs-unwind` extern functions. `aapcs-unwind` functions MUST NOT have this attribute. We @@ -11,13 +11,11 @@ trait Sized { } // CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] -pub extern "aapcs" fn rust_item_that_cannot_unwind() { -} +pub extern "aapcs" fn rust_item_that_cannot_unwind() {} // CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] -pub extern "aapcs-unwind" fn rust_item_that_can_unwind() { -} +pub extern "aapcs-unwind" fn rust_item_that_can_unwind() {} // Now, make some assertions that the LLVM attributes for these functions are correct. First, make // sure that the first item is correctly marked with the `nounwind` attribute: diff --git a/tests/codegen/unwind-abis/c-unwind-abi.rs b/tests/codegen/unwind-abis/c-unwind-abi.rs index 140c18e6bb33..99763943a6ac 100644 --- a/tests/codegen/unwind-abis/c-unwind-abi.rs +++ b/tests/codegen/unwind-abis/c-unwind-abi.rs @@ -10,13 +10,11 @@ // CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] -pub extern "C" fn rust_item_that_cannot_unwind() { -} +pub extern "C" fn rust_item_that_cannot_unwind() {} // CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] -pub extern "C-unwind" fn rust_item_that_can_unwind() { -} +pub extern "C-unwind" fn rust_item_that_can_unwind() {} // Now, make some assertions that the LLVM attributes for these functions are correct. First, make // sure that the first item is correctly marked with the `nounwind` attribute: diff --git a/tests/codegen/unwind-abis/cdecl-unwind-abi.rs b/tests/codegen/unwind-abis/cdecl-unwind-abi.rs index 5604b8d5765a..78dbb55b35a2 100644 --- a/tests/codegen/unwind-abis/cdecl-unwind-abi.rs +++ b/tests/codegen/unwind-abis/cdecl-unwind-abi.rs @@ -10,13 +10,11 @@ // CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] -pub extern "cdecl" fn rust_item_that_cannot_unwind() { -} +pub extern "cdecl" fn rust_item_that_cannot_unwind() {} // CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] -pub extern "cdecl-unwind" fn rust_item_that_can_unwind() { -} +pub extern "cdecl-unwind" fn rust_item_that_can_unwind() {} // Now, make some assertions that the LLVM attributes for these functions are correct. First, make // sure that the first item is correctly marked with the `nounwind` attribute: diff --git a/tests/codegen/unwind-abis/fastcall-unwind-abi.rs b/tests/codegen/unwind-abis/fastcall-unwind-abi.rs index 18e2cd22c8aa..3cbeaf51d960 100644 --- a/tests/codegen/unwind-abis/fastcall-unwind-abi.rs +++ b/tests/codegen/unwind-abis/fastcall-unwind-abi.rs @@ -2,8 +2,8 @@ //@ compile-flags: --target=i686-pc-windows-msvc --crate-type=rlib -Cno-prepopulate-passes #![no_core] #![feature(no_core, lang_items, c_unwind)] -#[lang="sized"] -trait Sized { } +#[lang = "sized"] +trait Sized {} // Test that `nounwind` attributes are correctly applied to exported `fastcall` and // `fastcall-unwind` extern functions. `fastcall-unwind` functions MUST NOT have this attribute. We @@ -11,13 +11,11 @@ trait Sized { } // CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] -pub extern "fastcall" fn rust_item_that_cannot_unwind() { -} +pub extern "fastcall" fn rust_item_that_cannot_unwind() {} // CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] -pub extern "fastcall-unwind" fn rust_item_that_can_unwind() { -} +pub extern "fastcall-unwind" fn rust_item_that_can_unwind() {} // Now, make some assertions that the LLVM attributes for these functions are correct. First, make // sure that the first item is correctly marked with the `nounwind` attribute: diff --git a/tests/codegen/unwind-abis/stdcall-unwind-abi.rs b/tests/codegen/unwind-abis/stdcall-unwind-abi.rs index a5e600fe076f..ffb235cd1168 100644 --- a/tests/codegen/unwind-abis/stdcall-unwind-abi.rs +++ b/tests/codegen/unwind-abis/stdcall-unwind-abi.rs @@ -2,8 +2,8 @@ //@ compile-flags: --target=i686-pc-windows-msvc --crate-type=rlib -Cno-prepopulate-passes #![no_core] #![feature(no_core, lang_items, c_unwind)] -#[lang="sized"] -trait Sized { } +#[lang = "sized"] +trait Sized {} // Test that `nounwind` attributes are correctly applied to exported `stdcall` and `stdcall-unwind` // extern functions. `stdcall-unwind` functions MUST NOT have this attribute. We disable @@ -11,13 +11,11 @@ trait Sized { } // CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] -pub extern "stdcall" fn rust_item_that_cannot_unwind() { -} +pub extern "stdcall" fn rust_item_that_cannot_unwind() {} // CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] -pub extern "stdcall-unwind" fn rust_item_that_can_unwind() { -} +pub extern "stdcall-unwind" fn rust_item_that_can_unwind() {} // Now, make some assertions that the LLVM attributes for these functions are correct. First, make // sure that the first item is correctly marked with the `nounwind` attribute: diff --git a/tests/codegen/unwind-abis/system-unwind-abi.rs b/tests/codegen/unwind-abis/system-unwind-abi.rs index b001c1f951c4..1dd0e9bbbec7 100644 --- a/tests/codegen/unwind-abis/system-unwind-abi.rs +++ b/tests/codegen/unwind-abis/system-unwind-abi.rs @@ -10,13 +10,11 @@ // CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] -pub extern "system" fn rust_item_that_cannot_unwind() { -} +pub extern "system" fn rust_item_that_cannot_unwind() {} // CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] -pub extern "system-unwind" fn rust_item_that_can_unwind() { -} +pub extern "system-unwind" fn rust_item_that_can_unwind() {} // Now, make some assertions that the LLVM attributes for these functions are correct. First, make // sure that the first item is correctly marked with the `nounwind` attribute: diff --git a/tests/codegen/unwind-abis/sysv64-unwind-abi.rs b/tests/codegen/unwind-abis/sysv64-unwind-abi.rs index 49407f748bc3..9824009dc559 100644 --- a/tests/codegen/unwind-abis/sysv64-unwind-abi.rs +++ b/tests/codegen/unwind-abis/sysv64-unwind-abi.rs @@ -2,8 +2,8 @@ //@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib -Cno-prepopulate-passes #![no_core] #![feature(no_core, lang_items, c_unwind)] -#[lang="sized"] -trait Sized { } +#[lang = "sized"] +trait Sized {} // Test that `nounwind` attributes are correctly applied to exported `sysv64` and // `sysv64-unwind` extern functions. `sysv64-unwind` functions MUST NOT have this attribute. We @@ -11,13 +11,11 @@ trait Sized { } // CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] -pub extern "sysv64" fn rust_item_that_cannot_unwind() { -} +pub extern "sysv64" fn rust_item_that_cannot_unwind() {} // CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] -pub extern "sysv64-unwind" fn rust_item_that_can_unwind() { -} +pub extern "sysv64-unwind" fn rust_item_that_can_unwind() {} // Now, make some assertions that the LLVM attributes for these functions are correct. First, make // sure that the first item is correctly marked with the `nounwind` attribute: diff --git a/tests/codegen/unwind-abis/thiscall-unwind-abi.rs b/tests/codegen/unwind-abis/thiscall-unwind-abi.rs index d07e9b81d783..a96f4d5a350c 100644 --- a/tests/codegen/unwind-abis/thiscall-unwind-abi.rs +++ b/tests/codegen/unwind-abis/thiscall-unwind-abi.rs @@ -2,8 +2,8 @@ //@ compile-flags: --target=i686-pc-windows-msvc --crate-type=rlib -Cno-prepopulate-passes #![no_core] #![feature(no_core, lang_items, c_unwind)] -#[lang="sized"] -trait Sized { } +#[lang = "sized"] +trait Sized {} // Test that `nounwind` attributes are correctly applied to exported `thiscall` and // `thiscall-unwind` extern functions. `thiscall-unwind` functions MUST NOT have this attribute. We @@ -11,13 +11,11 @@ trait Sized { } // CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] -pub extern "thiscall" fn rust_item_that_cannot_unwind() { -} +pub extern "thiscall" fn rust_item_that_cannot_unwind() {} // CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] -pub extern "thiscall-unwind" fn rust_item_that_can_unwind() { -} +pub extern "thiscall-unwind" fn rust_item_that_can_unwind() {} // Now, make some assertions that the LLVM attributes for these functions are correct. First, make // sure that the first item is correctly marked with the `nounwind` attribute: diff --git a/tests/codegen/unwind-abis/vectorcall-unwind-abi.rs b/tests/codegen/unwind-abis/vectorcall-unwind-abi.rs index a89794fbf6e3..9929e3e3703d 100644 --- a/tests/codegen/unwind-abis/vectorcall-unwind-abi.rs +++ b/tests/codegen/unwind-abis/vectorcall-unwind-abi.rs @@ -2,8 +2,8 @@ //@ compile-flags: --target=i686-pc-windows-msvc --crate-type=rlib -Cno-prepopulate-passes #![no_core] #![feature(no_core, lang_items, c_unwind, abi_vectorcall)] -#[lang="sized"] -trait Sized { } +#[lang = "sized"] +trait Sized {} // Test that `nounwind` attributes are correctly applied to exported `vectorcall` and // `vectorcall-unwind` extern functions. `vectorcall-unwind` functions MUST NOT have this attribute. @@ -11,13 +11,11 @@ trait Sized { } // CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] -pub extern "vectorcall" fn rust_item_that_cannot_unwind() { -} +pub extern "vectorcall" fn rust_item_that_cannot_unwind() {} // CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] -pub extern "vectorcall-unwind" fn rust_item_that_can_unwind() { -} +pub extern "vectorcall-unwind" fn rust_item_that_can_unwind() {} // Now, make some assertions that the LLVM attributes for these functions are correct. First, make // sure that the first item is correctly marked with the `nounwind` attribute: diff --git a/tests/codegen/unwind-abis/win64-unwind-abi.rs b/tests/codegen/unwind-abis/win64-unwind-abi.rs index aa466469c4de..a45a94f628d1 100644 --- a/tests/codegen/unwind-abis/win64-unwind-abi.rs +++ b/tests/codegen/unwind-abis/win64-unwind-abi.rs @@ -2,8 +2,8 @@ //@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib -Cno-prepopulate-passes #![no_core] #![feature(no_core, lang_items, c_unwind)] -#[lang="sized"] -trait Sized { } +#[lang = "sized"] +trait Sized {} // Test that `nounwind` attributes are correctly applied to exported `win64` and // `win64-unwind` extern functions. `win64-unwind` functions MUST NOT have this attribute. We @@ -11,13 +11,11 @@ trait Sized { } // CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 { #[no_mangle] -pub extern "win64" fn rust_item_that_cannot_unwind() { -} +pub extern "win64" fn rust_item_that_cannot_unwind() {} // CHECK: @rust_item_that_can_unwind() unnamed_addr #1 { #[no_mangle] -pub extern "win64-unwind" fn rust_item_that_can_unwind() { -} +pub extern "win64-unwind" fn rust_item_that_can_unwind() {} // Now, make some assertions that the LLVM attributes for these functions are correct. First, make // sure that the first item is correctly marked with the `nounwind` attribute: diff --git a/tests/codegen/vec-in-place.rs b/tests/codegen/vec-in-place.rs index 7a175dc4f7e1..c6b77363a4e5 100644 --- a/tests/codegen/vec-in-place.rs +++ b/tests/codegen/vec-in-place.rs @@ -90,3 +90,25 @@ pub fn vec_iterator_cast_deaggregate_fold(vec: Vec) -> Vec<[u64; 4]> { // correct. vec.into_iter().map(|e| unsafe { std::mem::transmute(e) }).collect() } + +// CHECK-LABEL: @vec_iterator_cast_unwrap_drop +#[no_mangle] +pub fn vec_iterator_cast_unwrap_drop(vec: Vec>) -> Vec { + // CHECK-NOT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}} + // CHECK-NOT: call + // CHECK-NOT: %{{.*}} = mul + // CHECK-NOT: %{{.*}} = udiv + + vec.into_iter().map(|Wrapper(e)| e).collect() +} + +// CHECK-LABEL: @vec_iterator_cast_wrap_drop +#[no_mangle] +pub fn vec_iterator_cast_wrap_drop(vec: Vec) -> Vec> { + // CHECK-NOT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}} + // CHECK-NOT: call + // CHECK-NOT: %{{.*}} = mul + // CHECK-NOT: %{{.*}} = udiv + + vec.into_iter().map(Wrapper).collect() +} diff --git a/tests/codegen/vec-iter-collect-len.rs b/tests/codegen/vec-iter-collect-len.rs index e4242c574023..8c5d2f6f9a74 100644 --- a/tests/codegen/vec-iter-collect-len.rs +++ b/tests/codegen/vec-iter-collect-len.rs @@ -1,5 +1,5 @@ //@ compile-flags: -O -#![crate_type="lib"] +#![crate_type = "lib"] #[no_mangle] pub fn get_len() -> usize { diff --git a/tests/codegen/virtual-function-elimination.rs b/tests/codegen/virtual-function-elimination.rs index 6c391d9114b4..23d7657baa99 100644 --- a/tests/codegen/virtual-function-elimination.rs +++ b/tests/codegen/virtual-function-elimination.rs @@ -81,7 +81,7 @@ fn taking_u(u: &dyn U) -> i32 { } pub fn taking_v(v: &dyn V) -> i32 { - // CHECK: @llvm.type.checked.load({{.*}}, i32 24, metadata !"NtCs64ITQYi9761_28virtual_function_elimination1V") + // CHECK: @llvm.type.checked.load({{.*}}, i32 24, metadata !"NtC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_28virtual_function_elimination1V") v.public_function() } @@ -96,5 +96,5 @@ pub fn main() { // CHECK: ![[TYPE0]] = !{i64 0, !"[[MANGLED_TYPE0]]"} // CHECK: ![[VCALL_VIS0]] = !{i64 2} // CHECK: ![[TYPE1]] = !{i64 0, !"[[MANGLED_TYPE1]]"} -// CHECK: ![[TYPE2]] = !{i64 0, !"NtCs64ITQYi9761_28virtual_function_elimination1V"} +// CHECK: ![[TYPE2]] = !{i64 0, !"NtC[[CRATE_IDENT]]_28virtual_function_elimination1V"} // CHECK: ![[VCALL_VIS2]] = !{i64 1} diff --git a/tests/codegen/vtable-loads.rs b/tests/codegen/vtable-loads.rs new file mode 100644 index 000000000000..1dd6ca51063b --- /dev/null +++ b/tests/codegen/vtable-loads.rs @@ -0,0 +1,16 @@ +//@ compile-flags: -O + +#![crate_type = "lib"] + +// CHECK-LABEL: @loop_skips_vtable_load +#[no_mangle] +pub fn loop_skips_vtable_load(x: &dyn Fn()) { + // CHECK: load ptr, ptr %0{{.*}}, !invariant.load + // CHECK-NEXT: tail call void %1 + // CHECK-NOT: load ptr + x(); + for _ in 0..100 { + // CHECK: tail call void %1 + x(); + } +} diff --git a/tests/codegen/vtable-upcast.rs b/tests/codegen/vtable-upcast.rs index 41a4be26cb44..ae7b4bf7aee9 100644 --- a/tests/codegen/vtable-upcast.rs +++ b/tests/codegen/vtable-upcast.rs @@ -8,15 +8,15 @@ pub trait Base { fn base(&self); } -pub trait A : Base { +pub trait A: Base { fn a(&self); } -pub trait B : Base { +pub trait B: Base { fn b(&self); } -pub trait Diamond : A + B { +pub trait Diamond: A + B { fn diamond(&self); } diff --git a/tests/codegen/wasm_exceptions.rs b/tests/codegen/wasm_exceptions.rs index a53722f834c5..3910850e03a0 100644 --- a/tests/codegen/wasm_exceptions.rs +++ b/tests/codegen/wasm_exceptions.rs @@ -5,7 +5,7 @@ #![feature(core_intrinsics)] #![feature(rustc_attrs)] -extern { +extern "C" { fn may_panic(); #[rustc_nounwind] @@ -16,7 +16,9 @@ struct LogOnDrop; impl Drop for LogOnDrop { fn drop(&mut self) { - unsafe { log_number(0); } + unsafe { + log_number(0); + } } } @@ -24,7 +26,9 @@ impl Drop for LogOnDrop { #[no_mangle] pub fn test_cleanup() { let _log_on_drop = LogOnDrop; - unsafe { may_panic(); } + unsafe { + may_panic(); + } // CHECK-NOT: call // CHECK: invoke void @may_panic() @@ -35,12 +39,16 @@ pub fn test_cleanup() { #[no_mangle] pub fn test_rtry() { unsafe { - core::intrinsics::catch_unwind(|_| { - may_panic(); - }, core::ptr::null_mut(), |data, exception| { - log_number(data as usize); - log_number(exception as usize); - }); + core::intrinsics::catch_unwind( + |_| { + may_panic(); + }, + core::ptr::null_mut(), + |data, exception| { + log_number(data as usize); + log_number(exception as usize); + }, + ); } // CHECK-NOT: call diff --git a/tests/codegen/zip.rs b/tests/codegen/zip.rs index 5db0a93b78ef..ea8caba61f39 100644 --- a/tests/codegen/zip.rs +++ b/tests/codegen/zip.rs @@ -5,7 +5,7 @@ // CHECK-LABEL: @zip_copy #[no_mangle] pub fn zip_copy(xs: &[u8], ys: &mut [u8]) { -// CHECK: memcpy + // CHECK: memcpy for (x, y) in xs.iter().zip(ys) { *y = *x; } @@ -14,7 +14,7 @@ pub fn zip_copy(xs: &[u8], ys: &mut [u8]) { // CHECK-LABEL: @zip_copy_mapped #[no_mangle] pub fn zip_copy_mapped(xs: &[u8], ys: &mut [u8]) { -// CHECK: memcpy + // CHECK: memcpy for (x, y) in xs.iter().map(|&x| x).zip(ys) { *y = x; } diff --git a/tests/codegen/zst-offset.rs b/tests/codegen/zst-offset.rs index b623d492d9d6..14e97fd26ddf 100644 --- a/tests/codegen/zst-offset.rs +++ b/tests/codegen/zst-offset.rs @@ -6,14 +6,13 @@ // Hack to get the correct size for the length part in slices // CHECK: @helper([[USIZE:i[0-9]+]] %_1) #[no_mangle] -pub fn helper(_: usize) { -} +pub fn helper(_: usize) {} // Check that we correctly generate a GEP for a ZST that is not included in Scalar layout // CHECK-LABEL: @scalar_layout #[no_mangle] pub fn scalar_layout(s: &(u64, ())) { -// CHECK: getelementptr inbounds i8, {{.+}}, [[USIZE]] 8 + // CHECK: getelementptr inbounds i8, {{.+}}, [[USIZE]] 8 let x = &s.1; witness(&x); // keep variable in an alloca } @@ -22,7 +21,7 @@ pub fn scalar_layout(s: &(u64, ())) { // CHECK-LABEL: @scalarpair_layout #[no_mangle] pub fn scalarpair_layout(s: &(u64, u32, ())) { -// CHECK: getelementptr inbounds i8, {{.+}}, [[USIZE]] 12 + // CHECK: getelementptr inbounds i8, {{.+}}, [[USIZE]] 12 let x = &s.2; witness(&x); // keep variable in an alloca } @@ -34,7 +33,7 @@ pub struct U64x4(u64, u64, u64, u64); // CHECK-LABEL: @vector_layout #[no_mangle] pub fn vector_layout(s: &(U64x4, ())) { -// CHECK: getelementptr inbounds i8, {{.+}}, [[USIZE]] 32 + // CHECK: getelementptr inbounds i8, {{.+}}, [[USIZE]] 32 let x = &s.1; witness(&x); // keep variable in an alloca } diff --git a/tests/coverage/abort.cov-map b/tests/coverage/abort.cov-map index 1c36f2871dd1..5673fa98ca60 100644 --- a/tests/coverage/abort.cov-map +++ b/tests/coverage/abort.cov-map @@ -1,45 +1,37 @@ Function name: abort::main -Raw bytes (105): 0x[01, 01, 12, 01, 47, 05, 09, 03, 0d, 42, 11, 03, 0d, 11, 3e, 42, 11, 03, 0d, 3b, 15, 11, 3e, 42, 11, 03, 0d, 15, 36, 3b, 15, 11, 3e, 42, 11, 03, 0d, 05, 09, 0d, 01, 0e, 01, 01, 1b, 03, 02, 0b, 00, 18, 42, 01, 0c, 00, 19, 11, 00, 1a, 02, 0a, 3e, 02, 0a, 00, 0b, 3b, 02, 0c, 00, 19, 15, 00, 1a, 00, 31, 36, 00, 31, 00, 32, 33, 04, 0c, 00, 19, 05, 00, 1a, 00, 31, 09, 00, 31, 00, 32, 47, 01, 09, 00, 17, 0d, 02, 05, 01, 02] +Raw bytes (89): 0x[01, 01, 0a, 01, 27, 05, 09, 03, 0d, 22, 11, 03, 0d, 03, 0d, 22, 15, 03, 0d, 03, 0d, 05, 09, 0d, 01, 0e, 01, 01, 1b, 03, 02, 0b, 00, 18, 22, 01, 0c, 00, 19, 11, 00, 1a, 02, 0a, 0e, 02, 0a, 00, 0b, 22, 02, 0c, 00, 19, 15, 00, 1a, 00, 31, 1a, 00, 31, 00, 32, 22, 04, 0c, 00, 19, 05, 00, 1a, 00, 31, 09, 00, 31, 00, 32, 27, 01, 09, 00, 17, 0d, 02, 05, 01, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 18 -- expression 0 operands: lhs = Counter(0), rhs = Expression(17, Add) +Number of expressions: 10 +- expression 0 operands: lhs = Counter(0), rhs = Expression(9, Add) - expression 1 operands: lhs = Counter(1), rhs = Counter(2) - expression 2 operands: lhs = Expression(0, Add), rhs = Counter(3) -- expression 3 operands: lhs = Expression(16, Sub), rhs = Counter(4) +- expression 3 operands: lhs = Expression(8, Sub), rhs = Counter(4) - expression 4 operands: lhs = Expression(0, Add), rhs = Counter(3) -- expression 5 operands: lhs = Counter(4), rhs = Expression(15, Sub) -- expression 6 operands: lhs = Expression(16, Sub), rhs = Counter(4) +- expression 5 operands: lhs = Expression(0, Add), rhs = Counter(3) +- expression 6 operands: lhs = Expression(8, Sub), rhs = Counter(5) - expression 7 operands: lhs = Expression(0, Add), rhs = Counter(3) -- expression 8 operands: lhs = Expression(14, Add), rhs = Counter(5) -- expression 9 operands: lhs = Counter(4), rhs = Expression(15, Sub) -- expression 10 operands: lhs = Expression(16, Sub), rhs = Counter(4) -- expression 11 operands: lhs = Expression(0, Add), rhs = Counter(3) -- expression 12 operands: lhs = Counter(5), rhs = Expression(13, Sub) -- expression 13 operands: lhs = Expression(14, Add), rhs = Counter(5) -- expression 14 operands: lhs = Counter(4), rhs = Expression(15, Sub) -- expression 15 operands: lhs = Expression(16, Sub), rhs = Counter(4) -- expression 16 operands: lhs = Expression(0, Add), rhs = Counter(3) -- expression 17 operands: lhs = Counter(1), rhs = Counter(2) +- expression 8 operands: lhs = Expression(0, Add), rhs = Counter(3) +- expression 9 operands: lhs = Counter(1), rhs = Counter(2) Number of file 0 mappings: 13 - Code(Counter(0)) at (prev + 14, 1) to (start + 1, 27) - Code(Expression(0, Add)) at (prev + 2, 11) to (start + 0, 24) = (c0 + (c1 + c2)) -- Code(Expression(16, Sub)) at (prev + 1, 12) to (start + 0, 25) +- Code(Expression(8, Sub)) at (prev + 1, 12) to (start + 0, 25) = ((c0 + (c1 + c2)) - c3) - Code(Counter(4)) at (prev + 0, 26) to (start + 2, 10) -- Code(Expression(15, Sub)) at (prev + 2, 10) to (start + 0, 11) +- Code(Expression(3, Sub)) at (prev + 2, 10) to (start + 0, 11) = (((c0 + (c1 + c2)) - c3) - c4) -- Code(Expression(14, Add)) at (prev + 2, 12) to (start + 0, 25) - = (c4 + (((c0 + (c1 + c2)) - c3) - c4)) +- Code(Expression(8, Sub)) at (prev + 2, 12) to (start + 0, 25) + = ((c0 + (c1 + c2)) - c3) - Code(Counter(5)) at (prev + 0, 26) to (start + 0, 49) -- Code(Expression(13, Sub)) at (prev + 0, 49) to (start + 0, 50) - = ((c4 + (((c0 + (c1 + c2)) - c3) - c4)) - c5) -- Code(Expression(12, Add)) at (prev + 4, 12) to (start + 0, 25) - = (c5 + ((c4 + (((c0 + (c1 + c2)) - c3) - c4)) - c5)) +- Code(Expression(6, Sub)) at (prev + 0, 49) to (start + 0, 50) + = (((c0 + (c1 + c2)) - c3) - c5) +- Code(Expression(8, Sub)) at (prev + 4, 12) to (start + 0, 25) + = ((c0 + (c1 + c2)) - c3) - Code(Counter(1)) at (prev + 0, 26) to (start + 0, 49) - Code(Counter(2)) at (prev + 0, 49) to (start + 0, 50) -- Code(Expression(17, Add)) at (prev + 1, 9) to (start + 0, 23) +- Code(Expression(9, Add)) at (prev + 1, 9) to (start + 0, 23) = (c1 + c2) - Code(Counter(3)) at (prev + 2, 5) to (start + 1, 2) diff --git a/tests/coverage/assert-ne.cov-map b/tests/coverage/assert-ne.cov-map new file mode 100644 index 000000000000..6d9906fd7f57 --- /dev/null +++ b/tests/coverage/assert-ne.cov-map @@ -0,0 +1,13 @@ +Function name: assert_ne::main +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 08, 01, 03, 1c, 05, 04, 0d, 00, 13, 02, 02, 0d, 00, 13, 09, 03, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 1 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 8, 1) to (start + 3, 28) +- Code(Counter(1)) at (prev + 4, 13) to (start + 0, 19) +- Code(Expression(0, Sub)) at (prev + 2, 13) to (start + 0, 19) + = (c0 - c1) +- Code(Counter(2)) at (prev + 3, 5) to (start + 1, 2) + diff --git a/tests/coverage/assert-ne.coverage b/tests/coverage/assert-ne.coverage new file mode 100644 index 000000000000..236a8fd13858 --- /dev/null +++ b/tests/coverage/assert-ne.coverage @@ -0,0 +1,23 @@ + LL| |//@ edition: 2021 + LL| | + LL| |use core::hint::black_box; + LL| | + LL| |#[derive(Debug, PartialEq)] + LL| |struct Foo(u32); + LL| | + LL| 1|fn main() { + LL| 1| assert_ne!( + LL| 1| Foo(5), // Make sure this expression's span isn't lost. + LL| 1| if black_box(false) { + LL| 0| Foo(0) // + LL| | } else { + LL| 1| Foo(1) // + LL| | } + LL| | ); + LL| 1| () + LL| 1|} + LL| | + LL| |// This test is a short fragment extracted from `issue-84561.rs`, highlighting + LL| |// a particular span of code that can easily be lost if overlapping spans are + LL| |// processed incorrectly. + diff --git a/tests/coverage/assert-ne.rs b/tests/coverage/assert-ne.rs new file mode 100644 index 000000000000..8a8fe0898048 --- /dev/null +++ b/tests/coverage/assert-ne.rs @@ -0,0 +1,22 @@ +//@ edition: 2021 + +use core::hint::black_box; + +#[derive(Debug, PartialEq)] +struct Foo(u32); + +fn main() { + assert_ne!( + Foo(5), // Make sure this expression's span isn't lost. + if black_box(false) { + Foo(0) // + } else { + Foo(1) // + } + ); + () +} + +// This test is a short fragment extracted from `issue-84561.rs`, highlighting +// a particular span of code that can easily be lost if overlapping spans are +// processed incorrectly. diff --git a/tests/coverage/async.cov-map b/tests/coverage/async.cov-map index e06d1676358f..7d16372375a7 100644 --- a/tests/coverage/async.cov-map +++ b/tests/coverage/async.cov-map @@ -7,19 +7,17 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 9, 1) to (start + 0, 25) Function name: async::c::{closure#0} -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 09, 19, 01, 0e, 05, 02, 09, 00, 0a, 02, 02, 09, 00, 0a, 07, 02, 01, 00, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 09, 19, 01, 0e, 05, 02, 09, 00, 0a, 02, 02, 09, 00, 0a, 01, 02, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 9, 25) to (start + 1, 14) - Code(Counter(1)) at (prev + 2, 9) to (start + 0, 10) - Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 10) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 2, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 2, 1) to (start + 0, 2) Function name: async::d Raw bytes (9): 0x[01, 01, 00, 01, 01, 11, 01, 00, 14] @@ -188,19 +186,17 @@ Number of file 0 mappings: 9 = ((c1 + c2) + c3) Function name: async::j::c -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 37, 05, 01, 12, 05, 02, 0d, 00, 0e, 02, 0a, 0d, 00, 0e, 07, 02, 05, 00, 06] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 37, 05, 01, 12, 05, 02, 0d, 00, 0e, 02, 0a, 0d, 00, 0e, 01, 02, 05, 00, 06] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 55, 5) to (start + 1, 18) - Code(Counter(1)) at (prev + 2, 13) to (start + 0, 14) - Code(Expression(0, Sub)) at (prev + 10, 13) to (start + 0, 14) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 2, 5) to (start + 0, 6) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 2, 5) to (start + 0, 6) Function name: async::j::d Raw bytes (9): 0x[01, 01, 00, 01, 01, 46, 05, 00, 17] diff --git a/tests/coverage/async2.cov-map b/tests/coverage/async2.cov-map index 28f319bfb80b..e39a1d7dd2f9 100644 --- a/tests/coverage/async2.cov-map +++ b/tests/coverage/async2.cov-map @@ -7,17 +7,15 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 13, 1) to (start + 0, 23) Function name: async2::async_func::{closure#0} -Raw bytes (26): 0x[01, 01, 01, 05, 00, 04, 01, 0d, 17, 03, 09, 05, 03, 0a, 02, 06, 00, 02, 06, 00, 07, 03, 01, 01, 00, 02] +Raw bytes (24): 0x[01, 01, 00, 04, 01, 0d, 17, 03, 09, 05, 03, 0a, 02, 06, 00, 02, 06, 00, 07, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 1 -- expression 0 operands: lhs = Counter(1), rhs = Zero +Number of expressions: 0 Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 13, 23) to (start + 3, 9) - Code(Counter(1)) at (prev + 3, 10) to (start + 2, 6) - Code(Zero) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(0, Add)) at (prev + 1, 1) to (start + 0, 2) - = (c1 + Zero) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) Function name: async2::async_func_just_println Raw bytes (9): 0x[01, 01, 00, 01, 01, 15, 01, 00, 24] @@ -44,15 +42,13 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 25, 1) to (start + 7, 2) Function name: async2::non_async_func -Raw bytes (26): 0x[01, 01, 01, 05, 00, 04, 01, 05, 01, 03, 09, 05, 03, 0a, 02, 06, 00, 02, 06, 00, 07, 03, 01, 01, 00, 02] +Raw bytes (24): 0x[01, 01, 00, 04, 01, 05, 01, 03, 09, 05, 03, 0a, 02, 06, 00, 02, 06, 00, 07, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 1 -- expression 0 operands: lhs = Counter(1), rhs = Zero +Number of expressions: 0 Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 5, 1) to (start + 3, 9) - Code(Counter(1)) at (prev + 3, 10) to (start + 2, 6) - Code(Zero) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(0, Add)) at (prev + 1, 1) to (start + 0, 2) - = (c1 + Zero) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) diff --git a/tests/coverage/async_block.cov-map b/tests/coverage/async_block.cov-map index 104133f6e678..e54c15c04362 100644 --- a/tests/coverage/async_block.cov-map +++ b/tests/coverage/async_block.cov-map @@ -1,10 +1,9 @@ Function name: async_block::main -Raw bytes (38): 0x[01, 01, 02, 01, 05, 03, 05, 06, 01, 05, 01, 00, 0b, 05, 01, 09, 00, 0a, 03, 00, 0e, 00, 13, 05, 00, 14, 01, 16, 05, 07, 0a, 02, 06, 06, 03, 01, 00, 02] +Raw bytes (36): 0x[01, 01, 01, 01, 05, 06, 01, 05, 01, 00, 0b, 05, 01, 09, 00, 0a, 03, 00, 0e, 00, 13, 05, 00, 14, 01, 16, 05, 07, 0a, 02, 06, 01, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Expression(0, Add), rhs = Counter(1) Number of file 0 mappings: 6 - Code(Counter(0)) at (prev + 5, 1) to (start + 0, 11) - Code(Counter(1)) at (prev + 1, 9) to (start + 0, 10) @@ -12,21 +11,18 @@ Number of file 0 mappings: 6 = (c0 + c1) - Code(Counter(1)) at (prev + 0, 20) to (start + 1, 22) - Code(Counter(1)) at (prev + 7, 10) to (start + 2, 6) -- Code(Expression(1, Sub)) at (prev + 3, 1) to (start + 0, 2) - = ((c0 + c1) - c1) +- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) Function name: async_block::main::{closure#0} -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 07, 1c, 01, 17, 05, 01, 18, 02, 0e, 02, 02, 14, 02, 0e, 07, 03, 09, 00, 0a] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 07, 1c, 01, 17, 05, 01, 18, 02, 0e, 02, 02, 14, 02, 0e, 01, 03, 09, 00, 0a] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 7, 28) to (start + 1, 23) - Code(Counter(1)) at (prev + 1, 24) to (start + 2, 14) - Code(Expression(0, Sub)) at (prev + 2, 20) to (start + 2, 14) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 3, 9) to (start + 0, 10) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 3, 9) to (start + 0, 10) diff --git a/tests/coverage/attr/nested.cov-map b/tests/coverage/attr/nested.cov-map new file mode 100644 index 000000000000..a613bb7f8cdf --- /dev/null +++ b/tests/coverage/attr/nested.cov-map @@ -0,0 +1,100 @@ +Function name: <<::trait_method::MyMiddle as nested::MyTrait>::trait_method::MyInner as nested::MyTrait>::trait_method (unused) +Raw bytes (9): 0x[01, 01, 00, 01, 00, 39, 15, 02, 16] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 57, 21) to (start + 2, 22) + +Function name: <<::outer_method::MyMiddle>::middle_method::MyInner>::inner_method (unused) +Raw bytes (9): 0x[01, 01, 00, 01, 00, 23, 15, 02, 16] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 35, 21) to (start + 2, 22) + +Function name: <::trait_method::MyMiddle as nested::MyTrait>::trait_method (unused) +Raw bytes (9): 0x[01, 01, 00, 01, 00, 36, 0d, 08, 0e] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 54, 13) to (start + 8, 14) + +Function name: <::outer_method::MyMiddle>::middle_method (unused) +Raw bytes (9): 0x[01, 01, 00, 01, 00, 20, 0d, 08, 0e] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 32, 13) to (start + 8, 14) + +Function name: nested::closure_expr +Raw bytes (14): 0x[01, 01, 00, 02, 01, 44, 01, 01, 0f, 01, 0b, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 68, 1) to (start + 1, 15) +- Code(Counter(0)) at (prev + 11, 5) to (start + 1, 2) + +Function name: nested::closure_expr::{closure#0}::{closure#0} (unused) +Raw bytes (14): 0x[01, 01, 00, 02, 00, 47, 1a, 01, 17, 00, 04, 0d, 01, 0a] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Zero) at (prev + 71, 26) to (start + 1, 23) +- Code(Zero) at (prev + 4, 13) to (start + 1, 10) + +Function name: nested::closure_expr::{closure#0}::{closure#0}::{closure#0} (unused) +Raw bytes (9): 0x[01, 01, 00, 01, 00, 48, 1d, 02, 0e] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 72, 29) to (start + 2, 14) + +Function name: nested::closure_tail +Raw bytes (14): 0x[01, 01, 00, 02, 01, 53, 01, 01, 0f, 01, 11, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 83, 1) to (start + 1, 15) +- Code(Counter(0)) at (prev + 17, 5) to (start + 1, 2) + +Function name: nested::closure_tail::{closure#0}::{closure#0} (unused) +Raw bytes (14): 0x[01, 01, 00, 02, 00, 58, 14, 01, 1f, 00, 06, 15, 01, 12] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Zero) at (prev + 88, 20) to (start + 1, 31) +- Code(Zero) at (prev + 6, 21) to (start + 1, 18) + +Function name: nested::closure_tail::{closure#0}::{closure#0}::{closure#0} (unused) +Raw bytes (9): 0x[01, 01, 00, 01, 00, 5a, 1c, 02, 1a] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 90, 28) to (start + 2, 26) + +Function name: nested::outer_fn::middle_fn (unused) +Raw bytes (9): 0x[01, 01, 00, 01, 00, 11, 05, 05, 06] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 17, 5) to (start + 5, 6) + +Function name: nested::outer_fn::middle_fn::inner_fn (unused) +Raw bytes (9): 0x[01, 01, 00, 01, 00, 12, 09, 02, 0a] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 18, 9) to (start + 2, 10) + diff --git a/tests/coverage/attr/nested.coverage b/tests/coverage/attr/nested.coverage new file mode 100644 index 000000000000..13129572aec7 --- /dev/null +++ b/tests/coverage/attr/nested.coverage @@ -0,0 +1,111 @@ + LL| |#![feature(coverage_attribute, stmt_expr_attributes)] + LL| |//@ edition: 2021 + LL| | + LL| |// Demonstrates the interaction between #[coverage(off)] and various kinds of + LL| |// nested function. + LL| | + LL| |// FIXME(#126625): Coverage attributes should apply recursively to nested functions. + LL| |// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`, + LL| |// its lines can still be marked with misleading execution counts from its enclosing + LL| |// function. + LL| | + LL| |#[coverage(off)] + LL| |fn do_stuff() {} + LL| | + LL| |#[coverage(off)] + LL| |fn outer_fn() { + LL| 0| fn middle_fn() { + LL| 0| fn inner_fn() { + LL| 0| do_stuff(); + LL| 0| } + LL| 0| do_stuff(); + LL| 0| } + LL| | do_stuff(); + LL| |} + LL| | + LL| |struct MyOuter; + LL| |impl MyOuter { + LL| | #[coverage(off)] + LL| | fn outer_method(&self) { + LL| | struct MyMiddle; + LL| | impl MyMiddle { + LL| 0| fn middle_method(&self) { + LL| 0| struct MyInner; + LL| 0| impl MyInner { + LL| 0| fn inner_method(&self) { + LL| 0| do_stuff(); + LL| 0| } + LL| 0| } + LL| 0| do_stuff(); + LL| 0| } + LL| | } + LL| | do_stuff(); + LL| | } + LL| |} + LL| | + LL| |trait MyTrait { + LL| | fn trait_method(&self); + LL| |} + LL| |impl MyTrait for MyOuter { + LL| | #[coverage(off)] + LL| | fn trait_method(&self) { + LL| | struct MyMiddle; + LL| | impl MyTrait for MyMiddle { + LL| 0| fn trait_method(&self) { + LL| 0| struct MyInner; + LL| 0| impl MyTrait for MyInner { + LL| 0| fn trait_method(&self) { + LL| 0| do_stuff(); + LL| 0| } + LL| 0| } + LL| 0| do_stuff(); + LL| 0| } + LL| | } + LL| | do_stuff(); + LL| | } + LL| |} + LL| | + LL| 1|fn closure_expr() { + LL| 1| let _outer = #[coverage(off)] + LL| | || { + LL| 0| let _middle = || { + LL| 0| let _inner = || { + LL| 0| do_stuff(); + LL| 0| }; + LL| 0| do_stuff(); + LL| 0| }; + LL| | do_stuff(); + LL| | }; + LL| 1| do_stuff(); + LL| 1|} + LL| | + LL| |// This syntax is allowed, even without #![feature(stmt_expr_attributes)]. + LL| 1|fn closure_tail() { + LL| 1| let _outer = { + LL| | #[coverage(off)] + LL| | || { + LL| | let _middle = { + LL| 0| || { + LL| 0| let _inner = { + LL| 0| || { + LL| 0| do_stuff(); + LL| 0| } + LL| | }; + LL| 0| do_stuff(); + LL| 0| } + LL| | }; + LL| | do_stuff(); + LL| | } + LL| | }; + LL| 1| do_stuff(); + LL| 1|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | outer_fn(); + LL| | MyOuter.outer_method(); + LL| | MyOuter.trait_method(); + LL| | closure_expr(); + LL| | closure_tail(); + LL| |} + diff --git a/tests/coverage/attr/nested.rs b/tests/coverage/attr/nested.rs new file mode 100644 index 000000000000..c7ff835f44f3 --- /dev/null +++ b/tests/coverage/attr/nested.rs @@ -0,0 +1,110 @@ +#![feature(coverage_attribute, stmt_expr_attributes)] +//@ edition: 2021 + +// Demonstrates the interaction between #[coverage(off)] and various kinds of +// nested function. + +// FIXME(#126625): Coverage attributes should apply recursively to nested functions. +// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`, +// its lines can still be marked with misleading execution counts from its enclosing +// function. + +#[coverage(off)] +fn do_stuff() {} + +#[coverage(off)] +fn outer_fn() { + fn middle_fn() { + fn inner_fn() { + do_stuff(); + } + do_stuff(); + } + do_stuff(); +} + +struct MyOuter; +impl MyOuter { + #[coverage(off)] + fn outer_method(&self) { + struct MyMiddle; + impl MyMiddle { + fn middle_method(&self) { + struct MyInner; + impl MyInner { + fn inner_method(&self) { + do_stuff(); + } + } + do_stuff(); + } + } + do_stuff(); + } +} + +trait MyTrait { + fn trait_method(&self); +} +impl MyTrait for MyOuter { + #[coverage(off)] + fn trait_method(&self) { + struct MyMiddle; + impl MyTrait for MyMiddle { + fn trait_method(&self) { + struct MyInner; + impl MyTrait for MyInner { + fn trait_method(&self) { + do_stuff(); + } + } + do_stuff(); + } + } + do_stuff(); + } +} + +fn closure_expr() { + let _outer = #[coverage(off)] + || { + let _middle = || { + let _inner = || { + do_stuff(); + }; + do_stuff(); + }; + do_stuff(); + }; + do_stuff(); +} + +// This syntax is allowed, even without #![feature(stmt_expr_attributes)]. +fn closure_tail() { + let _outer = { + #[coverage(off)] + || { + let _middle = { + || { + let _inner = { + || { + do_stuff(); + } + }; + do_stuff(); + } + }; + do_stuff(); + } + }; + do_stuff(); +} + +#[coverage(off)] +fn main() { + outer_fn(); + MyOuter.outer_method(); + MyOuter.trait_method(); + closure_expr(); + closure_tail(); +} diff --git a/tests/coverage/attr/off-on-sandwich.cov-map b/tests/coverage/attr/off-on-sandwich.cov-map new file mode 100644 index 000000000000..72b96420cb56 --- /dev/null +++ b/tests/coverage/attr/off-on-sandwich.cov-map @@ -0,0 +1,32 @@ +Function name: off_on_sandwich::dense_a::dense_b +Raw bytes (9): 0x[01, 01, 00, 01, 01, 14, 05, 07, 06] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 20, 5) to (start + 7, 6) + +Function name: off_on_sandwich::sparse_a::sparse_b +Raw bytes (9): 0x[01, 01, 00, 01, 01, 22, 05, 10, 06] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 34, 5) to (start + 16, 6) + +Function name: off_on_sandwich::sparse_a::sparse_b::sparse_c +Raw bytes (9): 0x[01, 01, 00, 01, 01, 26, 09, 0b, 0a] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 38, 9) to (start + 11, 10) + +Function name: off_on_sandwich::sparse_a::sparse_b::sparse_c::sparse_d +Raw bytes (9): 0x[01, 01, 00, 01, 01, 29, 0d, 07, 0e] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 41, 13) to (start + 7, 14) + diff --git a/tests/coverage/attr/off-on-sandwich.coverage b/tests/coverage/attr/off-on-sandwich.coverage new file mode 100644 index 000000000000..e831b0e926e3 --- /dev/null +++ b/tests/coverage/attr/off-on-sandwich.coverage @@ -0,0 +1,58 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| | + LL| |// Demonstrates the interaction of `#[coverage(off)]` and `#[coverage(on)]` + LL| |// in nested functions. + LL| | + LL| |// FIXME(#126625): Coverage attributes should apply recursively to nested functions. + LL| |// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`, + LL| |// its lines can still be marked with misleading execution counts from its enclosing + LL| |// function. + LL| | + LL| |#[coverage(off)] + LL| |fn do_stuff() {} + LL| | + LL| |#[coverage(off)] + LL| |fn dense_a() { + LL| | dense_b(); + LL| | dense_b(); + LL| | #[coverage(on)] + LL| 2| fn dense_b() { + LL| 2| dense_c(); + LL| 2| dense_c(); + LL| 2| #[coverage(off)] + LL| 2| fn dense_c() { + LL| 2| do_stuff(); + LL| 2| } + LL| 2| } + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |fn sparse_a() { + LL| | sparse_b(); + LL| | sparse_b(); + LL| 2| fn sparse_b() { + LL| 2| sparse_c(); + LL| 2| sparse_c(); + LL| 2| #[coverage(on)] + LL| 4| fn sparse_c() { + LL| 4| sparse_d(); + LL| 4| sparse_d(); + LL| 8| fn sparse_d() { + LL| 8| sparse_e(); + LL| 8| sparse_e(); + LL| 8| #[coverage(off)] + LL| 8| fn sparse_e() { + LL| 8| do_stuff(); + LL| 8| } + LL| 8| } + LL| 4| } + LL| 2| } + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | dense_a(); + LL| | sparse_a(); + LL| |} + diff --git a/tests/coverage/attr/off-on-sandwich.rs b/tests/coverage/attr/off-on-sandwich.rs new file mode 100644 index 000000000000..6b21b180223e --- /dev/null +++ b/tests/coverage/attr/off-on-sandwich.rs @@ -0,0 +1,57 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 + +// Demonstrates the interaction of `#[coverage(off)]` and `#[coverage(on)]` +// in nested functions. + +// FIXME(#126625): Coverage attributes should apply recursively to nested functions. +// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`, +// its lines can still be marked with misleading execution counts from its enclosing +// function. + +#[coverage(off)] +fn do_stuff() {} + +#[coverage(off)] +fn dense_a() { + dense_b(); + dense_b(); + #[coverage(on)] + fn dense_b() { + dense_c(); + dense_c(); + #[coverage(off)] + fn dense_c() { + do_stuff(); + } + } +} + +#[coverage(off)] +fn sparse_a() { + sparse_b(); + sparse_b(); + fn sparse_b() { + sparse_c(); + sparse_c(); + #[coverage(on)] + fn sparse_c() { + sparse_d(); + sparse_d(); + fn sparse_d() { + sparse_e(); + sparse_e(); + #[coverage(off)] + fn sparse_e() { + do_stuff(); + } + } + } + } +} + +#[coverage(off)] +fn main() { + dense_a(); + sparse_a(); +} diff --git a/tests/coverage/auxiliary/used_crate.rs b/tests/coverage/auxiliary/used_crate.rs index 72d479c74a68..c0556a5bb4cd 100644 --- a/tests/coverage/auxiliary/used_crate.rs +++ b/tests/coverage/auxiliary/used_crate.rs @@ -17,23 +17,23 @@ pub fn used_function() { } pub fn used_only_from_bin_crate_generic_function(arg: T) { - println!("used_only_from_bin_crate_generic_function with {:?}", arg); + println!("used_only_from_bin_crate_generic_function with {arg:?}"); } // Expect for above function: `Unexecuted instantiation` (see below) pub fn used_only_from_this_lib_crate_generic_function(arg: T) { - println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + println!("used_only_from_this_lib_crate_generic_function with {arg:?}"); } pub fn used_from_bin_crate_and_lib_crate_generic_function(arg: T) { - println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + println!("used_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); } pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function(arg: T) { - println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); } pub fn unused_generic_function(arg: T) { - println!("unused_generic_function with {:?}", arg); + println!("unused_generic_function with {arg:?}"); } pub fn unused_function() { diff --git a/tests/coverage/auxiliary/used_inline_crate.rs b/tests/coverage/auxiliary/used_inline_crate.rs index d5fe7478aa41..165d5c1e3096 100644 --- a/tests/coverage/auxiliary/used_inline_crate.rs +++ b/tests/coverage/auxiliary/used_inline_crate.rs @@ -31,28 +31,28 @@ pub fn used_inline_function() { #[inline(always)] pub fn used_only_from_bin_crate_generic_function(arg: T) { - println!("used_only_from_bin_crate_generic_function with {:?}", arg); + println!("used_only_from_bin_crate_generic_function with {arg:?}"); } // Expect for above function: `Unexecuted instantiation` (see notes in `used_crate.rs`) #[inline(always)] pub fn used_only_from_this_lib_crate_generic_function(arg: T) { - println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + println!("used_only_from_this_lib_crate_generic_function with {arg:?}"); } #[inline(always)] pub fn used_from_bin_crate_and_lib_crate_generic_function(arg: T) { - println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + println!("used_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); } #[inline(always)] pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function(arg: T) { - println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); } #[inline(always)] pub fn unused_generic_function(arg: T) { - println!("unused_generic_function with {:?}", arg); + println!("unused_generic_function with {arg:?}"); } #[inline(always)] diff --git a/tests/coverage/branch/generics.cov-map b/tests/coverage/branch/generics.cov-map index d729b0c260a5..2e5668c8b568 100644 --- a/tests/coverage/branch/generics.cov-map +++ b/tests/coverage/branch/generics.cov-map @@ -1,10 +1,9 @@ Function name: generics::print_size::<()> -Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] +Raw bytes (33): 0x[01, 01, 01, 01, 05, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 01, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 6, 1) to (start + 1, 36) - Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 1, 8) to (start + 0, 36) @@ -13,16 +12,14 @@ Number of file 0 mappings: 5 - Code(Counter(1)) at (prev + 0, 37) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 12) to (start + 2, 6) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) Function name: generics::print_size:: -Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] +Raw bytes (33): 0x[01, 01, 01, 01, 05, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 01, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 6, 1) to (start + 1, 36) - Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 1, 8) to (start + 0, 36) @@ -31,16 +28,14 @@ Number of file 0 mappings: 5 - Code(Counter(1)) at (prev + 0, 37) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 12) to (start + 2, 6) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) Function name: generics::print_size:: -Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] +Raw bytes (33): 0x[01, 01, 01, 01, 05, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 01, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 6, 1) to (start + 1, 36) - Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 1, 8) to (start + 0, 36) @@ -49,6 +44,5 @@ Number of file 0 mappings: 5 - Code(Counter(1)) at (prev + 0, 37) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 12) to (start + 2, 6) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) diff --git a/tests/coverage/branch/if-let.cov-map b/tests/coverage/branch/if-let.cov-map index 0c7d986933e7..0b098bc6497c 100644 --- a/tests/coverage/branch/if-let.cov-map +++ b/tests/coverage/branch/if-let.cov-map @@ -1,10 +1,9 @@ Function name: if_let::if_let -Raw bytes (45): 0x[01, 01, 02, 05, 09, 09, 02, 07, 01, 0c, 01, 01, 10, 20, 02, 09, 03, 0c, 00, 13, 02, 00, 11, 00, 12, 05, 00, 16, 00, 1b, 02, 00, 1c, 02, 06, 09, 02, 0c, 02, 06, 07, 03, 05, 01, 02] +Raw bytes (43): 0x[01, 01, 01, 05, 09, 07, 01, 0c, 01, 01, 10, 20, 02, 09, 03, 0c, 00, 13, 02, 00, 11, 00, 12, 05, 00, 16, 00, 1b, 02, 00, 1c, 02, 06, 09, 02, 0c, 02, 06, 05, 03, 05, 01, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(1), rhs = Counter(2) -- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub) Number of file 0 mappings: 7 - Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) - Branch { true: Expression(0, Sub), false: Counter(2) } at (prev + 3, 12) to (start + 0, 19) @@ -16,8 +15,7 @@ Number of file 0 mappings: 7 - Code(Expression(0, Sub)) at (prev + 0, 28) to (start + 2, 6) = (c1 - c2) - Code(Counter(2)) at (prev + 2, 12) to (start + 2, 6) -- Code(Expression(1, Add)) at (prev + 3, 5) to (start + 1, 2) - = (c2 + (c1 - c2)) +- Code(Counter(1)) at (prev + 3, 5) to (start + 1, 2) Function name: if_let::if_let_chain Raw bytes (66): 0x[01, 01, 04, 01, 05, 05, 09, 0f, 0d, 05, 09, 0a, 01, 17, 01, 00, 33, 20, 02, 05, 01, 0c, 00, 13, 02, 00, 11, 00, 12, 01, 00, 16, 00, 17, 20, 0d, 09, 01, 10, 00, 17, 0d, 00, 15, 00, 16, 02, 00, 1a, 00, 1b, 0d, 01, 05, 03, 06, 0f, 03, 0c, 02, 06, 0b, 03, 05, 01, 02] diff --git a/tests/coverage/branch/if.cov-map b/tests/coverage/branch/if.cov-map index 50f6216e0698..7f4ee980e262 100644 --- a/tests/coverage/branch/if.cov-map +++ b/tests/coverage/branch/if.cov-map @@ -24,51 +24,17 @@ Number of file 0 mappings: 8 = (c4 + (c3 + (c1 - c2))) Function name: if::branch_not -Raw bytes (224): 0x[01, 01, 29, 05, 09, 09, 02, a3, 01, 0d, 09, 02, a3, 01, 0d, 09, 02, 0d, 9e, 01, a3, 01, 0d, 09, 02, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 93, 01, 15, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 93, 01, 15, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 15, 8e, 01, 93, 01, 15, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 12, 01, 0c, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 09, 01, 09, 00, 11, 02, 01, 06, 00, 07, a3, 01, 01, 08, 00, 0a, 20, 9e, 01, 0d, 00, 08, 00, 0a, 9e, 01, 00, 0b, 02, 06, 0d, 02, 06, 00, 07, 9b, 01, 01, 08, 00, 0b, 20, 11, 96, 01, 00, 08, 00, 0b, 11, 00, 0c, 02, 06, 96, 01, 02, 06, 00, 07, 93, 01, 01, 08, 00, 0c, 20, 8e, 01, 15, 00, 08, 00, 0c, 8e, 01, 00, 0d, 02, 06, 15, 02, 06, 00, 07, 8b, 01, 01, 01, 00, 02] +Raw bytes (116): 0x[01, 01, 07, 05, 09, 05, 0d, 05, 0d, 05, 11, 05, 11, 05, 15, 05, 15, 12, 01, 0c, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 09, 01, 09, 00, 11, 02, 01, 06, 00, 07, 05, 01, 08, 00, 0a, 20, 0a, 0d, 00, 08, 00, 0a, 0a, 00, 0b, 02, 06, 0d, 02, 06, 00, 07, 05, 01, 08, 00, 0b, 20, 11, 12, 00, 08, 00, 0b, 11, 00, 0c, 02, 06, 12, 02, 06, 00, 07, 05, 01, 08, 00, 0c, 20, 1a, 15, 00, 08, 00, 0c, 1a, 00, 0d, 02, 06, 15, 02, 06, 00, 07, 05, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 41 +Number of expressions: 7 - expression 0 operands: lhs = Counter(1), rhs = Counter(2) -- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 2 operands: lhs = Expression(40, Add), rhs = Counter(3) -- expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 4 operands: lhs = Expression(40, Add), rhs = Counter(3) -- expression 5 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 6 operands: lhs = Counter(3), rhs = Expression(39, Sub) -- expression 7 operands: lhs = Expression(40, Add), rhs = Counter(3) -- expression 8 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 9 operands: lhs = Expression(38, Add), rhs = Counter(4) -- expression 10 operands: lhs = Counter(3), rhs = Expression(39, Sub) -- expression 11 operands: lhs = Expression(40, Add), rhs = Counter(3) -- expression 12 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 13 operands: lhs = Expression(38, Add), rhs = Counter(4) -- expression 14 operands: lhs = Counter(3), rhs = Expression(39, Sub) -- expression 15 operands: lhs = Expression(40, Add), rhs = Counter(3) -- expression 16 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 17 operands: lhs = Counter(4), rhs = Expression(37, Sub) -- expression 18 operands: lhs = Expression(38, Add), rhs = Counter(4) -- expression 19 operands: lhs = Counter(3), rhs = Expression(39, Sub) -- expression 20 operands: lhs = Expression(40, Add), rhs = Counter(3) -- expression 21 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 22 operands: lhs = Expression(36, Add), rhs = Counter(5) -- expression 23 operands: lhs = Counter(4), rhs = Expression(37, Sub) -- expression 24 operands: lhs = Expression(38, Add), rhs = Counter(4) -- expression 25 operands: lhs = Counter(3), rhs = Expression(39, Sub) -- expression 26 operands: lhs = Expression(40, Add), rhs = Counter(3) -- expression 27 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 28 operands: lhs = Expression(36, Add), rhs = Counter(5) -- expression 29 operands: lhs = Counter(4), rhs = Expression(37, Sub) -- expression 30 operands: lhs = Expression(38, Add), rhs = Counter(4) -- expression 31 operands: lhs = Counter(3), rhs = Expression(39, Sub) -- expression 32 operands: lhs = Expression(40, Add), rhs = Counter(3) -- expression 33 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 34 operands: lhs = Counter(5), rhs = Expression(35, Sub) -- expression 35 operands: lhs = Expression(36, Add), rhs = Counter(5) -- expression 36 operands: lhs = Counter(4), rhs = Expression(37, Sub) -- expression 37 operands: lhs = Expression(38, Add), rhs = Counter(4) -- expression 38 operands: lhs = Counter(3), rhs = Expression(39, Sub) -- expression 39 operands: lhs = Expression(40, Add), rhs = Counter(3) -- expression 40 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 1 operands: lhs = Counter(1), rhs = Counter(3) +- expression 2 operands: lhs = Counter(1), rhs = Counter(3) +- expression 3 operands: lhs = Counter(1), rhs = Counter(4) +- expression 4 operands: lhs = Counter(1), rhs = Counter(4) +- expression 5 operands: lhs = Counter(1), rhs = Counter(5) +- expression 6 operands: lhs = Counter(1), rhs = Counter(5) Number of file 0 mappings: 18 - Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) - Code(Counter(1)) at (prev + 3, 8) to (start + 0, 9) @@ -78,60 +44,39 @@ Number of file 0 mappings: 18 - Code(Counter(2)) at (prev + 1, 9) to (start + 0, 17) - Code(Expression(0, Sub)) at (prev + 1, 6) to (start + 0, 7) = (c1 - c2) -- Code(Expression(40, Add)) at (prev + 1, 8) to (start + 0, 10) - = (c2 + (c1 - c2)) -- Branch { true: Expression(39, Sub), false: Counter(3) } at (prev + 0, 8) to (start + 0, 10) - true = ((c2 + (c1 - c2)) - c3) +- Code(Counter(1)) at (prev + 1, 8) to (start + 0, 10) +- Branch { true: Expression(2, Sub), false: Counter(3) } at (prev + 0, 8) to (start + 0, 10) + true = (c1 - c3) false = c3 -- Code(Expression(39, Sub)) at (prev + 0, 11) to (start + 2, 6) - = ((c2 + (c1 - c2)) - c3) +- Code(Expression(2, Sub)) at (prev + 0, 11) to (start + 2, 6) + = (c1 - c3) - Code(Counter(3)) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(38, Add)) at (prev + 1, 8) to (start + 0, 11) - = (c3 + ((c2 + (c1 - c2)) - c3)) -- Branch { true: Counter(4), false: Expression(37, Sub) } at (prev + 0, 8) to (start + 0, 11) +- Code(Counter(1)) at (prev + 1, 8) to (start + 0, 11) +- Branch { true: Counter(4), false: Expression(4, Sub) } at (prev + 0, 8) to (start + 0, 11) true = c4 - false = ((c3 + ((c2 + (c1 - c2)) - c3)) - c4) + false = (c1 - c4) - Code(Counter(4)) at (prev + 0, 12) to (start + 2, 6) -- Code(Expression(37, Sub)) at (prev + 2, 6) to (start + 0, 7) - = ((c3 + ((c2 + (c1 - c2)) - c3)) - c4) -- Code(Expression(36, Add)) at (prev + 1, 8) to (start + 0, 12) - = (c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) -- Branch { true: Expression(35, Sub), false: Counter(5) } at (prev + 0, 8) to (start + 0, 12) - true = ((c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) - c5) +- Code(Expression(4, Sub)) at (prev + 2, 6) to (start + 0, 7) + = (c1 - c4) +- Code(Counter(1)) at (prev + 1, 8) to (start + 0, 12) +- Branch { true: Expression(6, Sub), false: Counter(5) } at (prev + 0, 8) to (start + 0, 12) + true = (c1 - c5) false = c5 -- Code(Expression(35, Sub)) at (prev + 0, 13) to (start + 2, 6) - = ((c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) - c5) +- Code(Expression(6, Sub)) at (prev + 0, 13) to (start + 2, 6) + = (c1 - c5) - Code(Counter(5)) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(34, Add)) at (prev + 1, 1) to (start + 0, 2) - = (c5 + ((c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) - c5)) +- Code(Counter(1)) at (prev + 1, 1) to (start + 0, 2) Function name: if::branch_not_as -Raw bytes (124): 0x[01, 01, 16, 05, 09, 09, 02, 57, 0d, 09, 02, 57, 0d, 09, 02, 0d, 52, 57, 0d, 09, 02, 4f, 11, 0d, 52, 57, 0d, 09, 02, 4f, 11, 0d, 52, 57, 0d, 09, 02, 11, 4a, 4f, 11, 0d, 52, 57, 0d, 09, 02, 0e, 01, 1d, 01, 01, 10, 05, 03, 08, 00, 14, 20, 02, 09, 00, 08, 00, 14, 02, 00, 15, 02, 06, 09, 02, 06, 00, 07, 57, 01, 08, 00, 15, 20, 0d, 52, 00, 08, 00, 15, 0d, 00, 16, 02, 06, 52, 02, 06, 00, 07, 4f, 01, 08, 00, 16, 20, 4a, 11, 00, 08, 00, 16, 4a, 00, 17, 02, 06, 11, 02, 06, 00, 07, 47, 01, 01, 00, 02] +Raw bytes (90): 0x[01, 01, 05, 05, 09, 05, 0d, 05, 0d, 05, 11, 05, 11, 0e, 01, 1d, 01, 01, 10, 05, 03, 08, 00, 14, 20, 02, 09, 00, 08, 00, 14, 02, 00, 15, 02, 06, 09, 02, 06, 00, 07, 05, 01, 08, 00, 15, 20, 0d, 0a, 00, 08, 00, 15, 0d, 00, 16, 02, 06, 0a, 02, 06, 00, 07, 05, 01, 08, 00, 16, 20, 12, 11, 00, 08, 00, 16, 12, 00, 17, 02, 06, 11, 02, 06, 00, 07, 05, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 22 +Number of expressions: 5 - expression 0 operands: lhs = Counter(1), rhs = Counter(2) -- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 2 operands: lhs = Expression(21, Add), rhs = Counter(3) -- expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 4 operands: lhs = Expression(21, Add), rhs = Counter(3) -- expression 5 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 6 operands: lhs = Counter(3), rhs = Expression(20, Sub) -- expression 7 operands: lhs = Expression(21, Add), rhs = Counter(3) -- expression 8 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 9 operands: lhs = Expression(19, Add), rhs = Counter(4) -- expression 10 operands: lhs = Counter(3), rhs = Expression(20, Sub) -- expression 11 operands: lhs = Expression(21, Add), rhs = Counter(3) -- expression 12 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 13 operands: lhs = Expression(19, Add), rhs = Counter(4) -- expression 14 operands: lhs = Counter(3), rhs = Expression(20, Sub) -- expression 15 operands: lhs = Expression(21, Add), rhs = Counter(3) -- expression 16 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 17 operands: lhs = Counter(4), rhs = Expression(18, Sub) -- expression 18 operands: lhs = Expression(19, Add), rhs = Counter(4) -- expression 19 operands: lhs = Counter(3), rhs = Expression(20, Sub) -- expression 20 operands: lhs = Expression(21, Add), rhs = Counter(3) -- expression 21 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 1 operands: lhs = Counter(1), rhs = Counter(3) +- expression 2 operands: lhs = Counter(1), rhs = Counter(3) +- expression 3 operands: lhs = Counter(1), rhs = Counter(4) +- expression 4 operands: lhs = Counter(1), rhs = Counter(4) Number of file 0 mappings: 14 - Code(Counter(0)) at (prev + 29, 1) to (start + 1, 16) - Code(Counter(1)) at (prev + 3, 8) to (start + 0, 20) @@ -141,24 +86,21 @@ Number of file 0 mappings: 14 - Code(Expression(0, Sub)) at (prev + 0, 21) to (start + 2, 6) = (c1 - c2) - Code(Counter(2)) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(21, Add)) at (prev + 1, 8) to (start + 0, 21) - = (c2 + (c1 - c2)) -- Branch { true: Counter(3), false: Expression(20, Sub) } at (prev + 0, 8) to (start + 0, 21) +- Code(Counter(1)) at (prev + 1, 8) to (start + 0, 21) +- Branch { true: Counter(3), false: Expression(2, Sub) } at (prev + 0, 8) to (start + 0, 21) true = c3 - false = ((c2 + (c1 - c2)) - c3) + false = (c1 - c3) - Code(Counter(3)) at (prev + 0, 22) to (start + 2, 6) -- Code(Expression(20, Sub)) at (prev + 2, 6) to (start + 0, 7) - = ((c2 + (c1 - c2)) - c3) -- Code(Expression(19, Add)) at (prev + 1, 8) to (start + 0, 22) - = (c3 + ((c2 + (c1 - c2)) - c3)) -- Branch { true: Expression(18, Sub), false: Counter(4) } at (prev + 0, 8) to (start + 0, 22) - true = ((c3 + ((c2 + (c1 - c2)) - c3)) - c4) +- Code(Expression(2, Sub)) at (prev + 2, 6) to (start + 0, 7) + = (c1 - c3) +- Code(Counter(1)) at (prev + 1, 8) to (start + 0, 22) +- Branch { true: Expression(4, Sub), false: Counter(4) } at (prev + 0, 8) to (start + 0, 22) + true = (c1 - c4) false = c4 -- Code(Expression(18, Sub)) at (prev + 0, 23) to (start + 2, 6) - = ((c3 + ((c2 + (c1 - c2)) - c3)) - c4) +- Code(Expression(4, Sub)) at (prev + 0, 23) to (start + 2, 6) + = (c1 - c4) - Code(Counter(4)) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(17, Add)) at (prev + 1, 1) to (start + 0, 2) - = (c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) +- Code(Counter(1)) at (prev + 1, 1) to (start + 0, 2) Function name: if::branch_or Raw bytes (56): 0x[01, 01, 04, 05, 09, 09, 0d, 0f, 11, 09, 0d, 08, 01, 35, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 02, 00, 0d, 00, 0e, 20, 0d, 11, 00, 0d, 00, 0e, 0f, 00, 0f, 02, 06, 11, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] diff --git a/tests/coverage/branch/lazy-boolean.cov-map b/tests/coverage/branch/lazy-boolean.cov-map index e2d731022d7c..09ce91376733 100644 --- a/tests/coverage/branch/lazy-boolean.cov-map +++ b/tests/coverage/branch/lazy-boolean.cov-map @@ -1,44 +1,35 @@ Function name: lazy_boolean::branch_and -Raw bytes (42): 0x[01, 01, 03, 09, 0a, 05, 09, 05, 09, 06, 01, 13, 01, 01, 10, 03, 04, 09, 00, 0a, 05, 00, 0d, 00, 0e, 20, 09, 0a, 00, 0d, 00, 0e, 09, 00, 12, 00, 13, 03, 01, 05, 01, 02] +Raw bytes (38): 0x[01, 01, 01, 05, 09, 06, 01, 13, 01, 01, 10, 05, 04, 09, 00, 0a, 05, 00, 0d, 00, 0e, 20, 09, 02, 00, 0d, 00, 0e, 09, 00, 12, 00, 13, 05, 01, 05, 01, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 3 -- expression 0 operands: lhs = Counter(2), rhs = Expression(2, Sub) -- expression 1 operands: lhs = Counter(1), rhs = Counter(2) -- expression 2 operands: lhs = Counter(1), rhs = Counter(2) +Number of expressions: 1 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) Number of file 0 mappings: 6 - Code(Counter(0)) at (prev + 19, 1) to (start + 1, 16) -- Code(Expression(0, Add)) at (prev + 4, 9) to (start + 0, 10) - = (c2 + (c1 - c2)) +- Code(Counter(1)) at (prev + 4, 9) to (start + 0, 10) - Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14) -- Branch { true: Counter(2), false: Expression(2, Sub) } at (prev + 0, 13) to (start + 0, 14) +- Branch { true: Counter(2), false: Expression(0, Sub) } at (prev + 0, 13) to (start + 0, 14) true = c2 false = (c1 - c2) - Code(Counter(2)) at (prev + 0, 18) to (start + 0, 19) -- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) - = (c2 + (c1 - c2)) +- Code(Counter(1)) at (prev + 1, 5) to (start + 1, 2) Function name: lazy_boolean::branch_or -Raw bytes (44): 0x[01, 01, 04, 09, 0e, 05, 09, 05, 09, 05, 09, 06, 01, 1b, 01, 01, 10, 03, 04, 09, 00, 0a, 05, 00, 0d, 00, 0e, 20, 09, 0e, 00, 0d, 00, 0e, 0e, 00, 12, 00, 13, 03, 01, 05, 01, 02] +Raw bytes (38): 0x[01, 01, 01, 05, 09, 06, 01, 1b, 01, 01, 10, 05, 04, 09, 00, 0a, 05, 00, 0d, 00, 0e, 20, 09, 02, 00, 0d, 00, 0e, 02, 00, 12, 00, 13, 05, 01, 05, 01, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 4 -- expression 0 operands: lhs = Counter(2), rhs = Expression(3, Sub) -- expression 1 operands: lhs = Counter(1), rhs = Counter(2) -- expression 2 operands: lhs = Counter(1), rhs = Counter(2) -- expression 3 operands: lhs = Counter(1), rhs = Counter(2) +Number of expressions: 1 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) Number of file 0 mappings: 6 - Code(Counter(0)) at (prev + 27, 1) to (start + 1, 16) -- Code(Expression(0, Add)) at (prev + 4, 9) to (start + 0, 10) - = (c2 + (c1 - c2)) +- Code(Counter(1)) at (prev + 4, 9) to (start + 0, 10) - Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14) -- Branch { true: Counter(2), false: Expression(3, Sub) } at (prev + 0, 13) to (start + 0, 14) +- Branch { true: Counter(2), false: Expression(0, Sub) } at (prev + 0, 13) to (start + 0, 14) true = c2 false = (c1 - c2) -- Code(Expression(3, Sub)) at (prev + 0, 18) to (start + 0, 19) +- Code(Expression(0, Sub)) at (prev + 0, 18) to (start + 0, 19) = (c1 - c2) -- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) - = (c2 + (c1 - c2)) +- Code(Counter(1)) at (prev + 1, 5) to (start + 1, 2) Function name: lazy_boolean::chain Raw bytes (149): 0x[01, 01, 13, 11, 07, 0b, 16, 15, 1a, 09, 0d, 05, 09, 05, 09, 09, 0d, 47, 25, 4b, 21, 19, 1d, 03, 19, 03, 19, 3e, 1d, 03, 19, 3e, 1d, 03, 19, 47, 25, 4b, 21, 19, 1d, 13, 01, 24, 01, 01, 10, 03, 04, 09, 00, 0a, 05, 00, 0d, 00, 12, 20, 09, 16, 00, 0d, 00, 12, 09, 00, 16, 00, 1b, 20, 0d, 1a, 00, 16, 00, 1b, 0d, 00, 1f, 00, 24, 20, 11, 15, 00, 1f, 00, 24, 11, 00, 28, 00, 2d, 03, 01, 05, 00, 11, 43, 03, 09, 00, 0a, 03, 00, 0d, 00, 12, 20, 19, 3e, 00, 0d, 00, 12, 3e, 00, 16, 00, 1b, 20, 1d, 3a, 00, 16, 00, 1b, 3a, 00, 1f, 00, 24, 20, 21, 25, 00, 1f, 00, 24, 25, 00, 28, 00, 2d, 43, 01, 05, 01, 02] @@ -105,73 +96,71 @@ Number of file 0 mappings: 19 = (((c6 + c7) + c8) + c9) Function name: lazy_boolean::nested_mixed -Raw bytes (159): 0x[01, 01, 18, 07, 22, 11, 36, 3b, 11, 09, 0d, 26, 0d, 05, 09, 05, 09, 05, 09, 26, 0d, 05, 09, 09, 0d, 3b, 11, 09, 0d, 3b, 11, 09, 0d, 19, 5f, 1d, 21, 03, 15, 15, 19, 52, 56, 15, 19, 03, 15, 19, 5f, 1d, 21, 13, 01, 31, 01, 01, 10, 03, 04, 09, 00, 0a, 05, 00, 0e, 00, 13, 20, 09, 26, 00, 0e, 00, 13, 26, 00, 17, 00, 1d, 20, 0d, 22, 00, 17, 00, 1d, 3b, 00, 23, 00, 28, 20, 11, 36, 00, 23, 00, 28, 36, 00, 2c, 00, 33, 03, 01, 05, 00, 11, 5b, 03, 09, 00, 0a, 03, 00, 0e, 00, 13, 20, 15, 56, 00, 0e, 00, 13, 15, 00, 17, 00, 1c, 20, 19, 52, 00, 17, 00, 1c, 4f, 00, 22, 00, 28, 20, 1d, 21, 00, 22, 00, 28, 1d, 00, 2c, 00, 33, 5b, 01, 05, 01, 02] +Raw bytes (155): 0x[01, 01, 16, 33, 1a, 09, 0d, 1e, 0d, 05, 09, 05, 09, 05, 09, 1e, 0d, 05, 09, 09, 0d, 33, 11, 09, 0d, 33, 11, 09, 0d, 19, 57, 1d, 21, 03, 15, 15, 19, 4a, 4e, 15, 19, 03, 15, 19, 57, 1d, 21, 13, 01, 31, 01, 01, 10, 03, 04, 09, 00, 0a, 05, 00, 0e, 00, 13, 20, 09, 1e, 00, 0e, 00, 13, 1e, 00, 17, 00, 1d, 20, 0d, 1a, 00, 17, 00, 1d, 33, 00, 23, 00, 28, 20, 11, 2e, 00, 23, 00, 28, 2e, 00, 2c, 00, 33, 03, 01, 05, 00, 11, 53, 03, 09, 00, 0a, 03, 00, 0e, 00, 13, 20, 15, 4e, 00, 0e, 00, 13, 15, 00, 17, 00, 1c, 20, 19, 4a, 00, 17, 00, 1c, 47, 00, 22, 00, 28, 20, 1d, 21, 00, 22, 00, 28, 1d, 00, 2c, 00, 33, 53, 01, 05, 01, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 24 -- expression 0 operands: lhs = Expression(1, Add), rhs = Expression(8, Sub) -- expression 1 operands: lhs = Counter(4), rhs = Expression(13, Sub) -- expression 2 operands: lhs = Expression(14, Add), rhs = Counter(4) -- expression 3 operands: lhs = Counter(2), rhs = Counter(3) -- expression 4 operands: lhs = Expression(9, Sub), rhs = Counter(3) +Number of expressions: 22 +- expression 0 operands: lhs = Expression(12, Add), rhs = Expression(6, Sub) +- expression 1 operands: lhs = Counter(2), rhs = Counter(3) +- expression 2 operands: lhs = Expression(7, Sub), rhs = Counter(3) +- expression 3 operands: lhs = Counter(1), rhs = Counter(2) +- expression 4 operands: lhs = Counter(1), rhs = Counter(2) - expression 5 operands: lhs = Counter(1), rhs = Counter(2) -- expression 6 operands: lhs = Counter(1), rhs = Counter(2) +- expression 6 operands: lhs = Expression(7, Sub), rhs = Counter(3) - expression 7 operands: lhs = Counter(1), rhs = Counter(2) -- expression 8 operands: lhs = Expression(9, Sub), rhs = Counter(3) -- expression 9 operands: lhs = Counter(1), rhs = Counter(2) +- expression 8 operands: lhs = Counter(2), rhs = Counter(3) +- expression 9 operands: lhs = Expression(12, Add), rhs = Counter(4) - expression 10 operands: lhs = Counter(2), rhs = Counter(3) -- expression 11 operands: lhs = Expression(14, Add), rhs = Counter(4) +- expression 11 operands: lhs = Expression(12, Add), rhs = Counter(4) - expression 12 operands: lhs = Counter(2), rhs = Counter(3) -- expression 13 operands: lhs = Expression(14, Add), rhs = Counter(4) -- expression 14 operands: lhs = Counter(2), rhs = Counter(3) -- expression 15 operands: lhs = Counter(6), rhs = Expression(23, Add) -- expression 16 operands: lhs = Counter(7), rhs = Counter(8) -- expression 17 operands: lhs = Expression(0, Add), rhs = Counter(5) +- expression 13 operands: lhs = Counter(6), rhs = Expression(21, Add) +- expression 14 operands: lhs = Counter(7), rhs = Counter(8) +- expression 15 operands: lhs = Expression(0, Add), rhs = Counter(5) +- expression 16 operands: lhs = Counter(5), rhs = Counter(6) +- expression 17 operands: lhs = Expression(18, Sub), rhs = Expression(19, Sub) - expression 18 operands: lhs = Counter(5), rhs = Counter(6) -- expression 19 operands: lhs = Expression(20, Sub), rhs = Expression(21, Sub) -- expression 20 operands: lhs = Counter(5), rhs = Counter(6) -- expression 21 operands: lhs = Expression(0, Add), rhs = Counter(5) -- expression 22 operands: lhs = Counter(6), rhs = Expression(23, Add) -- expression 23 operands: lhs = Counter(7), rhs = Counter(8) +- expression 19 operands: lhs = Expression(0, Add), rhs = Counter(5) +- expression 20 operands: lhs = Counter(6), rhs = Expression(21, Add) +- expression 21 operands: lhs = Counter(7), rhs = Counter(8) Number of file 0 mappings: 19 - Code(Counter(0)) at (prev + 49, 1) to (start + 1, 16) - Code(Expression(0, Add)) at (prev + 4, 9) to (start + 0, 10) - = ((c4 + ((c2 + c3) - c4)) + ((c1 - c2) - c3)) + = ((c2 + c3) + ((c1 - c2) - c3)) - Code(Counter(1)) at (prev + 0, 14) to (start + 0, 19) -- Branch { true: Counter(2), false: Expression(9, Sub) } at (prev + 0, 14) to (start + 0, 19) +- Branch { true: Counter(2), false: Expression(7, Sub) } at (prev + 0, 14) to (start + 0, 19) true = c2 false = (c1 - c2) -- Code(Expression(9, Sub)) at (prev + 0, 23) to (start + 0, 29) +- Code(Expression(7, Sub)) at (prev + 0, 23) to (start + 0, 29) = (c1 - c2) -- Branch { true: Counter(3), false: Expression(8, Sub) } at (prev + 0, 23) to (start + 0, 29) +- Branch { true: Counter(3), false: Expression(6, Sub) } at (prev + 0, 23) to (start + 0, 29) true = c3 false = ((c1 - c2) - c3) -- Code(Expression(14, Add)) at (prev + 0, 35) to (start + 0, 40) +- Code(Expression(12, Add)) at (prev + 0, 35) to (start + 0, 40) = (c2 + c3) -- Branch { true: Counter(4), false: Expression(13, Sub) } at (prev + 0, 35) to (start + 0, 40) +- Branch { true: Counter(4), false: Expression(11, Sub) } at (prev + 0, 35) to (start + 0, 40) true = c4 false = ((c2 + c3) - c4) -- Code(Expression(13, Sub)) at (prev + 0, 44) to (start + 0, 51) +- Code(Expression(11, Sub)) at (prev + 0, 44) to (start + 0, 51) = ((c2 + c3) - c4) - Code(Expression(0, Add)) at (prev + 1, 5) to (start + 0, 17) - = ((c4 + ((c2 + c3) - c4)) + ((c1 - c2) - c3)) -- Code(Expression(22, Add)) at (prev + 3, 9) to (start + 0, 10) + = ((c2 + c3) + ((c1 - c2) - c3)) +- Code(Expression(20, Add)) at (prev + 3, 9) to (start + 0, 10) = (c6 + (c7 + c8)) - Code(Expression(0, Add)) at (prev + 0, 14) to (start + 0, 19) - = ((c4 + ((c2 + c3) - c4)) + ((c1 - c2) - c3)) -- Branch { true: Counter(5), false: Expression(21, Sub) } at (prev + 0, 14) to (start + 0, 19) + = ((c2 + c3) + ((c1 - c2) - c3)) +- Branch { true: Counter(5), false: Expression(19, Sub) } at (prev + 0, 14) to (start + 0, 19) true = c5 - false = (((c4 + ((c2 + c3) - c4)) + ((c1 - c2) - c3)) - c5) + false = (((c2 + c3) + ((c1 - c2) - c3)) - c5) - Code(Counter(5)) at (prev + 0, 23) to (start + 0, 28) -- Branch { true: Counter(6), false: Expression(20, Sub) } at (prev + 0, 23) to (start + 0, 28) +- Branch { true: Counter(6), false: Expression(18, Sub) } at (prev + 0, 23) to (start + 0, 28) true = c6 false = (c5 - c6) -- Code(Expression(19, Add)) at (prev + 0, 34) to (start + 0, 40) - = ((c5 - c6) + (((c4 + ((c2 + c3) - c4)) + ((c1 - c2) - c3)) - c5)) +- Code(Expression(17, Add)) at (prev + 0, 34) to (start + 0, 40) + = ((c5 - c6) + (((c2 + c3) + ((c1 - c2) - c3)) - c5)) - Branch { true: Counter(7), false: Counter(8) } at (prev + 0, 34) to (start + 0, 40) true = c7 false = c8 - Code(Counter(7)) at (prev + 0, 44) to (start + 0, 51) -- Code(Expression(22, Add)) at (prev + 1, 5) to (start + 1, 2) +- Code(Expression(20, Add)) at (prev + 1, 5) to (start + 1, 2) = (c6 + (c7 + c8)) diff --git a/tests/coverage/branch/let-else.cov-map b/tests/coverage/branch/let-else.cov-map index c7f7adddbc29..070799344647 100644 --- a/tests/coverage/branch/let-else.cov-map +++ b/tests/coverage/branch/let-else.cov-map @@ -1,10 +1,9 @@ Function name: let_else::let_else -Raw bytes (45): 0x[01, 01, 02, 05, 09, 09, 02, 07, 01, 0c, 01, 01, 10, 20, 02, 09, 03, 09, 00, 10, 02, 00, 0e, 00, 0f, 05, 00, 13, 00, 18, 09, 01, 09, 01, 0f, 02, 04, 05, 00, 0b, 07, 01, 01, 00, 02] +Raw bytes (43): 0x[01, 01, 01, 05, 09, 07, 01, 0c, 01, 01, 10, 20, 02, 09, 03, 09, 00, 10, 02, 00, 0e, 00, 0f, 05, 00, 13, 00, 18, 09, 01, 09, 01, 0f, 02, 04, 05, 00, 0b, 05, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(1), rhs = Counter(2) -- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub) Number of file 0 mappings: 7 - Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) - Branch { true: Expression(0, Sub), false: Counter(2) } at (prev + 3, 9) to (start + 0, 16) @@ -16,6 +15,5 @@ Number of file 0 mappings: 7 - Code(Counter(2)) at (prev + 1, 9) to (start + 1, 15) - Code(Expression(0, Sub)) at (prev + 4, 5) to (start + 0, 11) = (c1 - c2) -- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) - = (c2 + (c1 - c2)) +- Code(Counter(1)) at (prev + 1, 1) to (start + 0, 2) diff --git a/tests/coverage/branch/no-mir-spans.cov-map b/tests/coverage/branch/no-mir-spans.cov-map new file mode 100644 index 000000000000..cb19211913f8 --- /dev/null +++ b/tests/coverage/branch/no-mir-spans.cov-map @@ -0,0 +1,52 @@ +Function name: no_mir_spans::while_cond +Raw bytes (16): 0x[01, 01, 00, 02, 01, 10, 01, 00, 11, 20, 05, 09, 04, 0b, 00, 10] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 16, 1) to (start + 0, 17) +- Branch { true: Counter(1), false: Counter(2) } at (prev + 4, 11) to (start + 0, 16) + true = c1 + false = c2 + +Function name: no_mir_spans::while_cond_not +Raw bytes (16): 0x[01, 01, 00, 02, 01, 19, 01, 00, 15, 20, 09, 05, 04, 0b, 00, 14] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 25, 1) to (start + 0, 21) +- Branch { true: Counter(2), false: Counter(1) } at (prev + 4, 11) to (start + 0, 20) + true = c2 + false = c1 + +Function name: no_mir_spans::while_op_and +Raw bytes (25): 0x[01, 01, 01, 09, 0d, 03, 01, 22, 01, 00, 13, 20, 09, 05, 05, 0b, 00, 10, 20, 02, 0d, 00, 14, 00, 19] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 1 +- expression 0 operands: lhs = Counter(2), rhs = Counter(3) +Number of file 0 mappings: 3 +- Code(Counter(0)) at (prev + 34, 1) to (start + 0, 19) +- Branch { true: Counter(2), false: Counter(1) } at (prev + 5, 11) to (start + 0, 16) + true = c2 + false = c1 +- Branch { true: Expression(0, Sub), false: Counter(3) } at (prev + 0, 20) to (start + 0, 25) + true = (c2 - c3) + false = c3 + +Function name: no_mir_spans::while_op_or +Raw bytes (25): 0x[01, 01, 01, 09, 0d, 03, 01, 2d, 01, 00, 12, 20, 05, 09, 05, 0b, 00, 10, 20, 0d, 02, 00, 14, 00, 19] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 1 +- expression 0 operands: lhs = Counter(2), rhs = Counter(3) +Number of file 0 mappings: 3 +- Code(Counter(0)) at (prev + 45, 1) to (start + 0, 18) +- Branch { true: Counter(1), false: Counter(2) } at (prev + 5, 11) to (start + 0, 16) + true = c1 + false = c2 +- Branch { true: Counter(3), false: Expression(0, Sub) } at (prev + 0, 20) to (start + 0, 25) + true = c3 + false = (c2 - c3) + diff --git a/tests/coverage/branch/no-mir-spans.coverage b/tests/coverage/branch/no-mir-spans.coverage new file mode 100644 index 000000000000..2cae98ed3ff4 --- /dev/null +++ b/tests/coverage/branch/no-mir-spans.coverage @@ -0,0 +1,77 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ compile-flags: -Zcoverage-options=branch,no-mir-spans + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| |// Tests the behaviour of the `-Zcoverage-options=no-mir-spans` debugging flag. + LL| |// The actual code below is just some non-trivial code copied from another test + LL| |// (`while.rs`), and has no particular significance. + LL| | + LL| |macro_rules! no_merge { + LL| | () => { + LL| | for _ in 0..1 {} + LL| | }; + LL| |} + LL| | + LL| 1|fn while_cond() { + LL| | no_merge!(); + LL| | + LL| | let mut a = 8; + LL| | while a > 0 { + ------------------ + | Branch (LL:11): [True: 8, False: 1] + ------------------ + LL| | a -= 1; + LL| | } + LL| |} + LL| | + LL| 1|fn while_cond_not() { + LL| | no_merge!(); + LL| | + LL| | let mut a = 8; + LL| | while !(a == 0) { + ------------------ + | Branch (LL:11): [True: 8, False: 1] + ------------------ + LL| | a -= 1; + LL| | } + LL| |} + LL| | + LL| 1|fn while_op_and() { + LL| | no_merge!(); + LL| | + LL| | let mut a = 8; + LL| | let mut b = 4; + LL| | while a > 0 && b > 0 { + ------------------ + | Branch (LL:11): [True: 5, False: 0] + | Branch (LL:20): [True: 4, False: 1] + ------------------ + LL| | a -= 1; + LL| | b -= 1; + LL| | } + LL| |} + LL| | + LL| 1|fn while_op_or() { + LL| | no_merge!(); + LL| | + LL| | let mut a = 4; + LL| | let mut b = 8; + LL| | while a > 0 || b > 0 { + ------------------ + | Branch (LL:11): [True: 4, False: 5] + | Branch (LL:20): [True: 4, False: 1] + ------------------ + LL| | a -= 1; + LL| | b -= 1; + LL| | } + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | while_cond(); + LL| | while_cond_not(); + LL| | while_op_and(); + LL| | while_op_or(); + LL| |} + diff --git a/tests/coverage/branch/no-mir-spans.rs b/tests/coverage/branch/no-mir-spans.rs new file mode 100644 index 000000000000..acb268f2d455 --- /dev/null +++ b/tests/coverage/branch/no-mir-spans.rs @@ -0,0 +1,62 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ compile-flags: -Zcoverage-options=branch,no-mir-spans +//@ llvm-cov-flags: --show-branches=count + +// Tests the behaviour of the `-Zcoverage-options=no-mir-spans` debugging flag. +// The actual code below is just some non-trivial code copied from another test +// (`while.rs`), and has no particular significance. + +macro_rules! no_merge { + () => { + for _ in 0..1 {} + }; +} + +fn while_cond() { + no_merge!(); + + let mut a = 8; + while a > 0 { + a -= 1; + } +} + +fn while_cond_not() { + no_merge!(); + + let mut a = 8; + while !(a == 0) { + a -= 1; + } +} + +fn while_op_and() { + no_merge!(); + + let mut a = 8; + let mut b = 4; + while a > 0 && b > 0 { + a -= 1; + b -= 1; + } +} + +fn while_op_or() { + no_merge!(); + + let mut a = 4; + let mut b = 8; + while a > 0 || b > 0 { + a -= 1; + b -= 1; + } +} + +#[coverage(off)] +fn main() { + while_cond(); + while_cond_not(); + while_op_and(); + while_op_or(); +} diff --git a/tests/coverage/branch/while.cov-map b/tests/coverage/branch/while.cov-map index 5a3ef096bedd..fd05bbb69a55 100644 --- a/tests/coverage/branch/while.cov-map +++ b/tests/coverage/branch/while.cov-map @@ -1,42 +1,36 @@ Function name: while::while_cond -Raw bytes (42): 0x[01, 01, 03, 05, 09, 03, 09, 03, 09, 06, 01, 0c, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 10, 20, 09, 0a, 00, 0b, 00, 10, 09, 00, 11, 02, 06, 0a, 03, 01, 00, 02] +Raw bytes (38): 0x[01, 01, 01, 05, 09, 06, 01, 0c, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 10, 20, 09, 05, 00, 0b, 00, 10, 09, 00, 11, 02, 06, 05, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 3 +Number of expressions: 1 - expression 0 operands: lhs = Counter(1), rhs = Counter(2) -- expression 1 operands: lhs = Expression(0, Add), rhs = Counter(2) -- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(2) Number of file 0 mappings: 6 - Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) - Code(Counter(1)) at (prev + 3, 9) to (start + 0, 18) - Code(Expression(0, Add)) at (prev + 1, 11) to (start + 0, 16) = (c1 + c2) -- Branch { true: Counter(2), false: Expression(2, Sub) } at (prev + 0, 11) to (start + 0, 16) +- Branch { true: Counter(2), false: Counter(1) } at (prev + 0, 11) to (start + 0, 16) true = c2 - false = ((c1 + c2) - c2) + false = c1 - Code(Counter(2)) at (prev + 0, 17) to (start + 2, 6) -- Code(Expression(2, Sub)) at (prev + 3, 1) to (start + 0, 2) - = ((c1 + c2) - c2) +- Code(Counter(1)) at (prev + 3, 1) to (start + 0, 2) Function name: while::while_cond_not -Raw bytes (42): 0x[01, 01, 03, 05, 09, 03, 09, 03, 09, 06, 01, 15, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 14, 20, 09, 0a, 00, 0b, 00, 14, 09, 00, 15, 02, 06, 0a, 03, 01, 00, 02] +Raw bytes (38): 0x[01, 01, 01, 05, 09, 06, 01, 15, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 14, 20, 09, 05, 00, 0b, 00, 14, 09, 00, 15, 02, 06, 05, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 3 +Number of expressions: 1 - expression 0 operands: lhs = Counter(1), rhs = Counter(2) -- expression 1 operands: lhs = Expression(0, Add), rhs = Counter(2) -- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(2) Number of file 0 mappings: 6 - Code(Counter(0)) at (prev + 21, 1) to (start + 1, 16) - Code(Counter(1)) at (prev + 3, 9) to (start + 0, 18) - Code(Expression(0, Add)) at (prev + 1, 11) to (start + 0, 20) = (c1 + c2) -- Branch { true: Counter(2), false: Expression(2, Sub) } at (prev + 0, 11) to (start + 0, 20) +- Branch { true: Counter(2), false: Counter(1) } at (prev + 0, 11) to (start + 0, 20) true = c2 - false = ((c1 + c2) - c2) + false = c1 - Code(Counter(2)) at (prev + 0, 21) to (start + 2, 6) -- Code(Expression(2, Sub)) at (prev + 3, 1) to (start + 0, 2) - = ((c1 + c2) - c2) +- Code(Counter(1)) at (prev + 3, 1) to (start + 0, 2) Function name: while::while_op_and Raw bytes (56): 0x[01, 01, 04, 05, 09, 03, 0d, 03, 0d, 11, 0d, 08, 01, 1e, 01, 01, 10, 05, 03, 09, 01, 12, 03, 02, 0b, 00, 10, 20, 0a, 0d, 00, 0b, 00, 10, 0a, 00, 14, 00, 19, 20, 09, 11, 00, 14, 00, 19, 09, 00, 1a, 03, 06, 0f, 04, 01, 00, 02] diff --git a/tests/coverage/closure.cov-map b/tests/coverage/closure.cov-map index 9f0d33745bcb..f36ef7af7ac3 100644 --- a/tests/coverage/closure.cov-map +++ b/tests/coverage/closure.cov-map @@ -1,10 +1,9 @@ Function name: closure::main -Raw bytes (128): 0x[01, 01, 02, 01, 05, 05, 02, 18, 01, 09, 01, 0f, 0d, 01, 16, 0e, 06, 0a, 01, 10, 05, 13, 0d, 01, 1a, 0e, 06, 0a, 01, 10, 05, 0c, 16, 01, 16, 05, 0d, 18, 01, 19, 09, 01, 1e, 01, 04, 09, 00, 29, 01, 01, 09, 00, 2d, 01, 01, 09, 00, 24, 01, 05, 09, 00, 24, 01, 02, 09, 00, 21, 01, 04, 09, 00, 21, 01, 04, 09, 00, 28, 01, 09, 09, 00, 32, 01, 04, 09, 00, 33, 01, 07, 09, 00, 4b, 01, 08, 09, 00, 48, 01, 0a, 09, 00, 47, 01, 08, 09, 00, 44, 01, 0a, 08, 00, 10, 05, 00, 11, 04, 06, 02, 04, 06, 00, 07, 07, 01, 05, 03, 02] +Raw bytes (126): 0x[01, 01, 01, 01, 05, 18, 01, 09, 01, 0f, 0d, 01, 16, 0e, 06, 0a, 01, 10, 05, 13, 0d, 01, 1a, 0e, 06, 0a, 01, 10, 05, 0c, 16, 01, 16, 05, 0d, 18, 01, 19, 09, 01, 1e, 01, 04, 09, 00, 29, 01, 01, 09, 00, 2d, 01, 01, 09, 00, 24, 01, 05, 09, 00, 24, 01, 02, 09, 00, 21, 01, 04, 09, 00, 21, 01, 04, 09, 00, 28, 01, 09, 09, 00, 32, 01, 04, 09, 00, 33, 01, 07, 09, 00, 4b, 01, 08, 09, 00, 48, 01, 0a, 09, 00, 47, 01, 08, 09, 00, 44, 01, 0a, 08, 00, 10, 05, 00, 11, 04, 06, 02, 04, 06, 00, 07, 01, 01, 05, 03, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 24 - Code(Counter(0)) at (prev + 9, 1) to (start + 15, 13) - Code(Counter(0)) at (prev + 22, 14) to (start + 6, 10) @@ -30,8 +29,7 @@ Number of file 0 mappings: 24 - Code(Counter(1)) at (prev + 0, 17) to (start + 4, 6) - Code(Expression(0, Sub)) at (prev + 4, 6) to (start + 0, 7) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 5) to (start + 3, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 5) to (start + 3, 2) Function name: closure::main::{closure#0} (unused) Raw bytes (24): 0x[01, 01, 00, 04, 00, 28, 05, 02, 14, 00, 02, 15, 02, 0a, 00, 02, 0a, 00, 0b, 00, 01, 09, 01, 06] @@ -77,72 +75,60 @@ Number of file 0 mappings: 1 - Code(Zero) at (prev + 172, 13) to (start + 2, 14) Function name: closure::main::{closure#14} -Raw bytes (29): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, b3, 01, 0d, 02, 1b, 05, 02, 1e, 00, 25, 02, 00, 2f, 00, 33, 07, 01, 0d, 00, 0e] +Raw bytes (27): 0x[01, 01, 01, 01, 05, 04, 01, b3, 01, 0d, 02, 1b, 05, 02, 1e, 00, 25, 02, 00, 2f, 00, 33, 01, 01, 0d, 00, 0e] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 179, 13) to (start + 2, 27) - Code(Counter(1)) at (prev + 2, 30) to (start + 0, 37) - Code(Expression(0, Sub)) at (prev + 0, 47) to (start + 0, 51) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 13) to (start + 0, 14) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 14) Function name: closure::main::{closure#15} -Raw bytes (41): 0x[01, 01, 03, 05, 0a, 01, 05, 01, 05, 06, 01, bb, 01, 09, 00, 0a, 03, 01, 0d, 00, 15, 01, 01, 11, 01, 1b, 05, 01, 1e, 00, 25, 0a, 00, 2f, 00, 33, 03, 02, 09, 00, 0a] +Raw bytes (37): 0x[01, 01, 01, 01, 05, 06, 01, bb, 01, 09, 00, 0a, 01, 01, 0d, 00, 15, 01, 01, 11, 01, 1b, 05, 01, 1e, 00, 25, 02, 00, 2f, 00, 33, 01, 02, 09, 00, 0a] Number of files: 1 - file 0 => global file 1 -Number of expressions: 3 -- expression 0 operands: lhs = Counter(1), rhs = Expression(2, Sub) -- expression 1 operands: lhs = Counter(0), rhs = Counter(1) -- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +Number of expressions: 1 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) Number of file 0 mappings: 6 - Code(Counter(0)) at (prev + 187, 9) to (start + 0, 10) -- Code(Expression(0, Add)) at (prev + 1, 13) to (start + 0, 21) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 21) - Code(Counter(0)) at (prev + 1, 17) to (start + 1, 27) - Code(Counter(1)) at (prev + 1, 30) to (start + 0, 37) -- Code(Expression(2, Sub)) at (prev + 0, 47) to (start + 0, 51) +- Code(Expression(0, Sub)) at (prev + 0, 47) to (start + 0, 51) = (c0 - c1) -- Code(Expression(0, Add)) at (prev + 2, 9) to (start + 0, 10) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 2, 9) to (start + 0, 10) Function name: closure::main::{closure#16} -Raw bytes (29): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, c5, 01, 0d, 02, 1b, 05, 02, 1e, 00, 25, 02, 00, 2f, 00, 33, 07, 01, 0d, 00, 0e] +Raw bytes (27): 0x[01, 01, 01, 01, 05, 04, 01, c5, 01, 0d, 02, 1b, 05, 02, 1e, 00, 25, 02, 00, 2f, 00, 33, 01, 01, 0d, 00, 0e] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 197, 13) to (start + 2, 27) - Code(Counter(1)) at (prev + 2, 30) to (start + 0, 37) - Code(Expression(0, Sub)) at (prev + 0, 47) to (start + 0, 51) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 13) to (start + 0, 14) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 14) Function name: closure::main::{closure#17} -Raw bytes (41): 0x[01, 01, 03, 05, 0a, 01, 05, 01, 05, 06, 01, cd, 01, 09, 00, 0a, 03, 01, 0d, 00, 15, 01, 01, 11, 01, 1b, 05, 01, 1e, 00, 25, 0a, 00, 2f, 00, 33, 03, 02, 09, 00, 0a] +Raw bytes (37): 0x[01, 01, 01, 01, 05, 06, 01, cd, 01, 09, 00, 0a, 01, 01, 0d, 00, 15, 01, 01, 11, 01, 1b, 05, 01, 1e, 00, 25, 02, 00, 2f, 00, 33, 01, 02, 09, 00, 0a] Number of files: 1 - file 0 => global file 1 -Number of expressions: 3 -- expression 0 operands: lhs = Counter(1), rhs = Expression(2, Sub) -- expression 1 operands: lhs = Counter(0), rhs = Counter(1) -- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +Number of expressions: 1 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) Number of file 0 mappings: 6 - Code(Counter(0)) at (prev + 205, 9) to (start + 0, 10) -- Code(Expression(0, Add)) at (prev + 1, 13) to (start + 0, 21) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 21) - Code(Counter(0)) at (prev + 1, 17) to (start + 1, 27) - Code(Counter(1)) at (prev + 1, 30) to (start + 0, 37) -- Code(Expression(2, Sub)) at (prev + 0, 47) to (start + 0, 51) +- Code(Expression(0, Sub)) at (prev + 0, 47) to (start + 0, 51) = (c0 - c1) -- Code(Expression(0, Add)) at (prev + 2, 9) to (start + 0, 10) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 2, 9) to (start + 0, 10) Function name: closure::main::{closure#18} (unused) Raw bytes (24): 0x[01, 01, 00, 04, 00, 19, 0d, 02, 1c, 00, 02, 1d, 02, 12, 00, 02, 12, 00, 13, 00, 01, 11, 01, 0e] @@ -156,49 +142,43 @@ Number of file 0 mappings: 4 - Code(Zero) at (prev + 1, 17) to (start + 1, 14) Function name: closure::main::{closure#19} -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 43, 0d, 02, 1c, 05, 02, 1d, 02, 12, 02, 02, 12, 00, 13, 07, 01, 11, 01, 0e] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 43, 0d, 02, 1c, 05, 02, 1d, 02, 12, 02, 02, 12, 00, 13, 01, 01, 11, 01, 0e] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 67, 13) to (start + 2, 28) - Code(Counter(1)) at (prev + 2, 29) to (start + 2, 18) - Code(Expression(0, Sub)) at (prev + 2, 18) to (start + 0, 19) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 17) to (start + 1, 14) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 17) to (start + 1, 14) Function name: closure::main::{closure#1} -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 52, 05, 02, 14, 05, 02, 15, 02, 0a, 02, 02, 0a, 00, 0b, 07, 01, 09, 01, 06] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 52, 05, 02, 14, 05, 02, 15, 02, 0a, 02, 02, 0a, 00, 0b, 01, 01, 09, 01, 06] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 82, 5) to (start + 2, 20) - Code(Counter(1)) at (prev + 2, 21) to (start + 2, 10) - Code(Expression(0, Sub)) at (prev + 2, 10) to (start + 0, 11) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 9) to (start + 1, 6) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 9) to (start + 1, 6) Function name: closure::main::{closure#2} -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 68, 05, 02, 14, 05, 02, 15, 02, 0a, 02, 02, 0a, 00, 0b, 07, 01, 09, 01, 06] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 68, 05, 02, 14, 05, 02, 15, 02, 0a, 02, 02, 0a, 00, 0b, 01, 01, 09, 01, 06] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 104, 5) to (start + 2, 20) - Code(Counter(1)) at (prev + 2, 21) to (start + 2, 10) - Code(Expression(0, Sub)) at (prev + 2, 10) to (start + 0, 11) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 9) to (start + 1, 6) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 9) to (start + 1, 6) Function name: closure::main::{closure#3} (unused) Raw bytes (25): 0x[01, 01, 00, 04, 00, 81, 01, 05, 01, 14, 00, 01, 15, 02, 0a, 00, 02, 0a, 00, 0b, 00, 01, 09, 01, 06] diff --git a/tests/coverage/closure_bug.cov-map b/tests/coverage/closure_bug.cov-map index 160b348bd632..32e8db5fb2f4 100644 --- a/tests/coverage/closure_bug.cov-map +++ b/tests/coverage/closure_bug.cov-map @@ -1,133 +1,84 @@ Function name: closure_bug::main -Raw bytes (201): 0x[01, 01, 26, 01, 05, 05, 02, 05, 02, 97, 01, 09, 05, 02, 09, 92, 01, 97, 01, 09, 05, 02, 09, 92, 01, 97, 01, 09, 05, 02, 8f, 01, 0d, 09, 92, 01, 97, 01, 09, 05, 02, 0d, 8a, 01, 8f, 01, 0d, 09, 92, 01, 97, 01, 09, 05, 02, 0d, 8a, 01, 8f, 01, 0d, 09, 92, 01, 97, 01, 09, 05, 02, 87, 01, 11, 0d, 8a, 01, 8f, 01, 0d, 09, 92, 01, 97, 01, 09, 05, 02, 11, 82, 01, 87, 01, 11, 0d, 8a, 01, 8f, 01, 0d, 09, 92, 01, 97, 01, 09, 05, 02, 11, 01, 07, 01, 03, 0a, 01, 09, 05, 01, 0e, 05, 01, 0f, 00, 17, 02, 00, 17, 00, 18, 97, 01, 02, 09, 00, 0a, 97, 01, 06, 05, 01, 0e, 09, 01, 0f, 00, 17, 92, 01, 00, 17, 00, 18, 8f, 01, 02, 09, 00, 0a, 8f, 01, 06, 05, 01, 0e, 0d, 01, 0f, 00, 17, 8a, 01, 00, 17, 00, 18, 87, 01, 02, 09, 00, 0a, 87, 01, 06, 05, 01, 0e, 11, 01, 0f, 00, 17, 82, 01, 00, 17, 00, 18, 7f, 01, 01, 00, 02] +Raw bytes (97): 0x[01, 01, 04, 01, 05, 01, 09, 01, 0d, 01, 11, 11, 01, 07, 01, 03, 0a, 01, 09, 05, 01, 0e, 05, 01, 0f, 00, 17, 02, 00, 17, 00, 18, 01, 02, 09, 00, 0a, 01, 06, 05, 01, 0e, 09, 01, 0f, 00, 17, 06, 00, 17, 00, 18, 01, 02, 09, 00, 0a, 01, 06, 05, 01, 0e, 0d, 01, 0f, 00, 17, 0a, 00, 17, 00, 18, 01, 02, 09, 00, 0a, 01, 06, 05, 01, 0e, 11, 01, 0f, 00, 17, 0e, 00, 17, 00, 18, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 38 +Number of expressions: 4 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 2 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 3 operands: lhs = Expression(37, Add), rhs = Counter(2) -- expression 4 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 5 operands: lhs = Counter(2), rhs = Expression(36, Sub) -- expression 6 operands: lhs = Expression(37, Add), rhs = Counter(2) -- expression 7 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 8 operands: lhs = Counter(2), rhs = Expression(36, Sub) -- expression 9 operands: lhs = Expression(37, Add), rhs = Counter(2) -- expression 10 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 11 operands: lhs = Expression(35, Add), rhs = Counter(3) -- expression 12 operands: lhs = Counter(2), rhs = Expression(36, Sub) -- expression 13 operands: lhs = Expression(37, Add), rhs = Counter(2) -- expression 14 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 15 operands: lhs = Counter(3), rhs = Expression(34, Sub) -- expression 16 operands: lhs = Expression(35, Add), rhs = Counter(3) -- expression 17 operands: lhs = Counter(2), rhs = Expression(36, Sub) -- expression 18 operands: lhs = Expression(37, Add), rhs = Counter(2) -- expression 19 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 20 operands: lhs = Counter(3), rhs = Expression(34, Sub) -- expression 21 operands: lhs = Expression(35, Add), rhs = Counter(3) -- expression 22 operands: lhs = Counter(2), rhs = Expression(36, Sub) -- expression 23 operands: lhs = Expression(37, Add), rhs = Counter(2) -- expression 24 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 25 operands: lhs = Expression(33, Add), rhs = Counter(4) -- expression 26 operands: lhs = Counter(3), rhs = Expression(34, Sub) -- expression 27 operands: lhs = Expression(35, Add), rhs = Counter(3) -- expression 28 operands: lhs = Counter(2), rhs = Expression(36, Sub) -- expression 29 operands: lhs = Expression(37, Add), rhs = Counter(2) -- expression 30 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 31 operands: lhs = Counter(4), rhs = Expression(32, Sub) -- expression 32 operands: lhs = Expression(33, Add), rhs = Counter(4) -- expression 33 operands: lhs = Counter(3), rhs = Expression(34, Sub) -- expression 34 operands: lhs = Expression(35, Add), rhs = Counter(3) -- expression 35 operands: lhs = Counter(2), rhs = Expression(36, Sub) -- expression 36 operands: lhs = Expression(37, Add), rhs = Counter(2) -- expression 37 operands: lhs = Counter(1), rhs = Expression(0, Sub) +- expression 1 operands: lhs = Counter(0), rhs = Counter(2) +- expression 2 operands: lhs = Counter(0), rhs = Counter(3) +- expression 3 operands: lhs = Counter(0), rhs = Counter(4) Number of file 0 mappings: 17 - Code(Counter(0)) at (prev + 7, 1) to (start + 3, 10) - Code(Counter(0)) at (prev + 9, 5) to (start + 1, 14) - Code(Counter(1)) at (prev + 1, 15) to (start + 0, 23) - Code(Expression(0, Sub)) at (prev + 0, 23) to (start + 0, 24) = (c0 - c1) -- Code(Expression(37, Add)) at (prev + 2, 9) to (start + 0, 10) - = (c1 + (c0 - c1)) -- Code(Expression(37, Add)) at (prev + 6, 5) to (start + 1, 14) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 2, 9) to (start + 0, 10) +- Code(Counter(0)) at (prev + 6, 5) to (start + 1, 14) - Code(Counter(2)) at (prev + 1, 15) to (start + 0, 23) -- Code(Expression(36, Sub)) at (prev + 0, 23) to (start + 0, 24) - = ((c1 + (c0 - c1)) - c2) -- Code(Expression(35, Add)) at (prev + 2, 9) to (start + 0, 10) - = (c2 + ((c1 + (c0 - c1)) - c2)) -- Code(Expression(35, Add)) at (prev + 6, 5) to (start + 1, 14) - = (c2 + ((c1 + (c0 - c1)) - c2)) +- Code(Expression(1, Sub)) at (prev + 0, 23) to (start + 0, 24) + = (c0 - c2) +- Code(Counter(0)) at (prev + 2, 9) to (start + 0, 10) +- Code(Counter(0)) at (prev + 6, 5) to (start + 1, 14) - Code(Counter(3)) at (prev + 1, 15) to (start + 0, 23) -- Code(Expression(34, Sub)) at (prev + 0, 23) to (start + 0, 24) - = ((c2 + ((c1 + (c0 - c1)) - c2)) - c3) -- Code(Expression(33, Add)) at (prev + 2, 9) to (start + 0, 10) - = (c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) -- Code(Expression(33, Add)) at (prev + 6, 5) to (start + 1, 14) - = (c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) +- Code(Expression(2, Sub)) at (prev + 0, 23) to (start + 0, 24) + = (c0 - c3) +- Code(Counter(0)) at (prev + 2, 9) to (start + 0, 10) +- Code(Counter(0)) at (prev + 6, 5) to (start + 1, 14) - Code(Counter(4)) at (prev + 1, 15) to (start + 0, 23) -- Code(Expression(32, Sub)) at (prev + 0, 23) to (start + 0, 24) - = ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4) -- Code(Expression(31, Add)) at (prev + 1, 1) to (start + 0, 2) - = (c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)) +- Code(Expression(3, Sub)) at (prev + 0, 23) to (start + 0, 24) + = (c0 - c4) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) Function name: closure_bug::main::{closure#0} -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 0e, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 07, 00, 29, 00, 2a] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 0e, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 01, 00, 29, 00, 2a] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 14, 9) to (start + 0, 18) - Code(Counter(1)) at (prev + 0, 21) to (start + 0, 25) - Code(Expression(0, Sub)) at (prev + 0, 35) to (start + 0, 40) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 0, 41) to (start + 0, 42) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 0, 41) to (start + 0, 42) Function name: closure_bug::main::{closure#1} -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 17, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 07, 00, 29, 00, 2a] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 17, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 01, 00, 29, 00, 2a] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 23, 9) to (start + 0, 18) - Code(Counter(1)) at (prev + 0, 21) to (start + 0, 25) - Code(Expression(0, Sub)) at (prev + 0, 35) to (start + 0, 40) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 0, 41) to (start + 0, 42) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 0, 41) to (start + 0, 42) Function name: closure_bug::main::{closure#2} -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 20, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 07, 00, 29, 00, 2a] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 20, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 01, 00, 29, 00, 2a] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 32, 9) to (start + 0, 18) - Code(Counter(1)) at (prev + 0, 21) to (start + 0, 25) - Code(Expression(0, Sub)) at (prev + 0, 35) to (start + 0, 40) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 0, 41) to (start + 0, 42) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 0, 41) to (start + 0, 42) Function name: closure_bug::main::{closure#3} -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 29, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 07, 00, 29, 00, 2a] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 29, 09, 00, 12, 05, 00, 15, 00, 19, 02, 00, 23, 00, 28, 01, 00, 29, 00, 2a] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 41, 9) to (start + 0, 18) - Code(Counter(1)) at (prev + 0, 21) to (start + 0, 25) - Code(Expression(0, Sub)) at (prev + 0, 35) to (start + 0, 40) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 0, 41) to (start + 0, 42) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 0, 41) to (start + 0, 42) diff --git a/tests/coverage/closure_macro.cov-map b/tests/coverage/closure_macro.cov-map index e43ed1f76f36..156947f4e21c 100644 --- a/tests/coverage/closure_macro.cov-map +++ b/tests/coverage/closure_macro.cov-map @@ -7,23 +7,19 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 29, 1) to (start + 2, 2) Function name: closure_macro::main -Raw bytes (38): 0x[01, 01, 02, 01, 05, 05, 02, 06, 01, 21, 01, 01, 21, 02, 02, 09, 00, 12, 02, 00, 0f, 00, 54, 05, 00, 54, 00, 55, 02, 02, 09, 02, 0b, 07, 03, 01, 00, 02] +Raw bytes (31): 0x[01, 01, 01, 01, 05, 05, 01, 21, 01, 01, 21, 02, 02, 09, 00, 0f, 05, 00, 54, 00, 55, 02, 02, 09, 02, 0b, 01, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) -Number of file 0 mappings: 6 +Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 33, 1) to (start + 1, 33) -- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 18) - = (c0 - c1) -- Code(Expression(0, Sub)) at (prev + 0, 15) to (start + 0, 84) +- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 15) = (c0 - c1) - Code(Counter(1)) at (prev + 0, 84) to (start + 0, 85) - Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 2, 11) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) Function name: closure_macro::main::{closure#0} Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 00, 05, 01, 10, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 00, 00, 17, 00, 1e, 07, 02, 09, 00, 0a] diff --git a/tests/coverage/closure_macro_async.cov-map b/tests/coverage/closure_macro_async.cov-map index 212b67a8a3e1..0f2b4e017483 100644 --- a/tests/coverage/closure_macro_async.cov-map +++ b/tests/coverage/closure_macro_async.cov-map @@ -15,23 +15,19 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 35, 1) to (start + 0, 43) Function name: closure_macro_async::test::{closure#0} -Raw bytes (38): 0x[01, 01, 02, 01, 05, 05, 02, 06, 01, 23, 2b, 01, 21, 02, 02, 09, 00, 12, 02, 00, 0f, 00, 54, 05, 00, 54, 00, 55, 02, 02, 09, 02, 0b, 07, 03, 01, 00, 02] +Raw bytes (31): 0x[01, 01, 01, 01, 05, 05, 01, 23, 2b, 01, 21, 02, 02, 09, 00, 0f, 05, 00, 54, 00, 55, 02, 02, 09, 02, 0b, 01, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) -Number of file 0 mappings: 6 +Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 35, 43) to (start + 1, 33) -- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 18) - = (c0 - c1) -- Code(Expression(0, Sub)) at (prev + 0, 15) to (start + 0, 84) +- Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 15) = (c0 - c1) - Code(Counter(1)) at (prev + 0, 84) to (start + 0, 85) - Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 2, 11) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) Function name: closure_macro_async::test::{closure#0}::{closure#0} Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 00, 05, 01, 12, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 00, 00, 17, 00, 1e, 07, 02, 09, 00, 0a] diff --git a/tests/coverage/condition/conditions.cov-map b/tests/coverage/condition/conditions.cov-map new file mode 100644 index 000000000000..74726e269fc6 --- /dev/null +++ b/tests/coverage/condition/conditions.cov-map @@ -0,0 +1,152 @@ +Function name: conditions::assign_3_and_or +Raw bytes (69): 0x[01, 01, 07, 07, 11, 09, 0d, 01, 05, 05, 09, 16, 1a, 05, 09, 01, 05, 09, 01, 1c, 01, 00, 2f, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 1a, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 20, 09, 16, 00, 12, 00, 13, 13, 00, 17, 00, 18, 20, 0d, 11, 00, 17, 00, 18, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 7 +- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(4) +- expression 1 operands: lhs = Counter(2), rhs = Counter(3) +- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +- expression 3 operands: lhs = Counter(1), rhs = Counter(2) +- expression 4 operands: lhs = Expression(5, Sub), rhs = Expression(6, Sub) +- expression 5 operands: lhs = Counter(1), rhs = Counter(2) +- expression 6 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 9 +- Code(Counter(0)) at (prev + 28, 1) to (start + 0, 47) +- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) + = ((c2 + c3) + c4) +- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) +- Branch { true: Counter(1), false: Expression(6, Sub) } at (prev + 0, 13) to (start + 0, 14) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 18) to (start + 0, 19) +- Branch { true: Counter(2), false: Expression(5, Sub) } at (prev + 0, 18) to (start + 0, 19) + true = c2 + false = (c1 - c2) +- Code(Expression(4, Add)) at (prev + 0, 23) to (start + 0, 24) + = ((c1 - c2) + (c0 - c1)) +- Branch { true: Counter(3), false: Counter(4) } at (prev + 0, 23) to (start + 0, 24) + true = c3 + false = c4 +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = ((c2 + c3) + c4) + +Function name: conditions::assign_3_or_and +Raw bytes (73): 0x[01, 01, 09, 05, 07, 0b, 11, 09, 0d, 01, 05, 01, 05, 22, 11, 01, 05, 22, 11, 01, 05, 09, 01, 17, 01, 00, 2f, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 22, 00, 0d, 00, 0e, 22, 00, 12, 00, 13, 20, 1e, 11, 00, 12, 00, 13, 1e, 00, 17, 00, 18, 20, 09, 0d, 00, 17, 00, 18, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 9 +- expression 0 operands: lhs = Counter(1), rhs = Expression(1, Add) +- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(4) +- expression 2 operands: lhs = Counter(2), rhs = Counter(3) +- expression 3 operands: lhs = Counter(0), rhs = Counter(1) +- expression 4 operands: lhs = Counter(0), rhs = Counter(1) +- expression 5 operands: lhs = Expression(8, Sub), rhs = Counter(4) +- expression 6 operands: lhs = Counter(0), rhs = Counter(1) +- expression 7 operands: lhs = Expression(8, Sub), rhs = Counter(4) +- expression 8 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 9 +- Code(Counter(0)) at (prev + 23, 1) to (start + 0, 47) +- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) + = (c1 + ((c2 + c3) + c4)) +- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) +- Branch { true: Counter(1), false: Expression(8, Sub) } at (prev + 0, 13) to (start + 0, 14) + true = c1 + false = (c0 - c1) +- Code(Expression(8, Sub)) at (prev + 0, 18) to (start + 0, 19) + = (c0 - c1) +- Branch { true: Expression(7, Sub), false: Counter(4) } at (prev + 0, 18) to (start + 0, 19) + true = ((c0 - c1) - c4) + false = c4 +- Code(Expression(7, Sub)) at (prev + 0, 23) to (start + 0, 24) + = ((c0 - c1) - c4) +- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 23) to (start + 0, 24) + true = c2 + false = c3 +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = (c1 + ((c2 + c3) + c4)) + +Function name: conditions::assign_and +Raw bytes (51): 0x[01, 01, 04, 07, 0e, 09, 0d, 01, 05, 01, 05, 07, 01, 0d, 01, 00, 21, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 0e, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 20, 09, 0d, 00, 12, 00, 13, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Expression(1, Add), rhs = Expression(3, Sub) +- expression 1 operands: lhs = Counter(2), rhs = Counter(3) +- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +- expression 3 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 7 +- Code(Counter(0)) at (prev + 13, 1) to (start + 0, 33) +- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) + = ((c2 + c3) + (c0 - c1)) +- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) +- Branch { true: Counter(1), false: Expression(3, Sub) } at (prev + 0, 13) to (start + 0, 14) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 18) to (start + 0, 19) +- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 18) to (start + 0, 19) + true = c2 + false = c3 +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = ((c2 + c3) + (c0 - c1)) + +Function name: conditions::assign_or +Raw bytes (51): 0x[01, 01, 04, 07, 0d, 05, 09, 01, 05, 01, 05, 07, 01, 12, 01, 00, 20, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 20, 05, 0e, 00, 0d, 00, 0e, 0e, 00, 12, 00, 13, 20, 09, 0d, 00, 12, 00, 13, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(3) +- expression 1 operands: lhs = Counter(1), rhs = Counter(2) +- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +- expression 3 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 7 +- Code(Counter(0)) at (prev + 18, 1) to (start + 0, 32) +- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) + = ((c1 + c2) + c3) +- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) +- Branch { true: Counter(1), false: Expression(3, Sub) } at (prev + 0, 13) to (start + 0, 14) + true = c1 + false = (c0 - c1) +- Code(Expression(3, Sub)) at (prev + 0, 18) to (start + 0, 19) + = (c0 - c1) +- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 18) to (start + 0, 19) + true = c2 + false = c3 +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = ((c1 + c2) + c3) + +Function name: conditions::foo +Raw bytes (9): 0x[01, 01, 00, 01, 01, 21, 01, 02, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 33, 1) to (start + 2, 2) + +Function name: conditions::func_call +Raw bytes (39): 0x[01, 01, 03, 01, 05, 0b, 02, 09, 0d, 05, 01, 25, 01, 01, 0a, 20, 05, 02, 01, 09, 00, 0a, 05, 00, 0e, 00, 0f, 20, 09, 0d, 00, 0e, 00, 0f, 07, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 3 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Expression(2, Add), rhs = Expression(0, Sub) +- expression 2 operands: lhs = Counter(2), rhs = Counter(3) +Number of file 0 mappings: 5 +- Code(Counter(0)) at (prev + 37, 1) to (start + 1, 10) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 1, 9) to (start + 0, 10) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 14) to (start + 0, 15) +- Branch { true: Counter(2), false: Counter(3) } at (prev + 0, 14) to (start + 0, 15) + true = c2 + false = c3 +- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) + = ((c2 + c3) + (c0 - c1)) + +Function name: conditions::simple_assign +Raw bytes (9): 0x[01, 01, 00, 01, 01, 08, 01, 03, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 8, 1) to (start + 3, 2) + diff --git a/tests/coverage/condition/conditions.coverage b/tests/coverage/condition/conditions.coverage new file mode 100644 index 000000000000..3215b391d622 --- /dev/null +++ b/tests/coverage/condition/conditions.coverage @@ -0,0 +1,95 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ compile-flags: -Zcoverage-options=condition + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| |use core::hint::black_box; + LL| | + LL| 2|fn simple_assign(a: bool) { + LL| 2| let x = a; + LL| 2| black_box(x); + LL| 2|} + LL| | + LL| 3|fn assign_and(a: bool, b: bool) { + LL| 3| let x = a && b; + ^2 + ------------------ + | Branch (LL:13): [True: 2, False: 1] + | Branch (LL:18): [True: 1, False: 1] + ------------------ + LL| 3| black_box(x); + LL| 3|} + LL| | + LL| 3|fn assign_or(a: bool, b: bool) { + LL| 3| let x = a || b; + ^1 + ------------------ + | Branch (LL:13): [True: 2, False: 1] + | Branch (LL:18): [True: 0, False: 1] + ------------------ + LL| 3| black_box(x); + LL| 3|} + LL| | + LL| 4|fn assign_3_or_and(a: bool, b: bool, c: bool) { + LL| 4| let x = a || b && c; + ^2 ^1 + ------------------ + | Branch (LL:13): [True: 2, False: 2] + | Branch (LL:18): [True: 1, False: 1] + | Branch (LL:23): [True: 1, False: 0] + ------------------ + LL| 4| black_box(x); + LL| 4|} + LL| | + LL| 4|fn assign_3_and_or(a: bool, b: bool, c: bool) { + LL| 4| let x = a && b || c; + ^2 ^3 + ------------------ + | Branch (LL:13): [True: 2, False: 2] + | Branch (LL:18): [True: 1, False: 1] + | Branch (LL:23): [True: 2, False: 1] + ------------------ + LL| 4| black_box(x); + LL| 4|} + LL| | + LL| 3|fn foo(a: bool) -> bool { + LL| 3| black_box(a) + LL| 3|} + LL| | + LL| 3|fn func_call(a: bool, b: bool) { + LL| 3| foo(a && b); + ^2 + ------------------ + | Branch (LL:9): [True: 2, False: 1] + | Branch (LL:14): [True: 1, False: 1] + ------------------ + LL| 3|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | simple_assign(true); + LL| | simple_assign(false); + LL| | + LL| | assign_and(true, false); + LL| | assign_and(true, true); + LL| | assign_and(false, false); + LL| | + LL| | assign_or(true, false); + LL| | assign_or(true, true); + LL| | assign_or(false, false); + LL| | + LL| | assign_3_or_and(true, false, false); + LL| | assign_3_or_and(true, true, false); + LL| | assign_3_or_and(false, false, true); + LL| | assign_3_or_and(false, true, true); + LL| | + LL| | assign_3_and_or(true, false, false); + LL| | assign_3_and_or(true, true, false); + LL| | assign_3_and_or(false, false, true); + LL| | assign_3_and_or(false, true, true); + LL| | + LL| | func_call(true, false); + LL| | func_call(true, true); + LL| | func_call(false, false); + LL| |} + diff --git a/tests/coverage/condition/conditions.rs b/tests/coverage/condition/conditions.rs new file mode 100644 index 000000000000..3d658dc93e0c --- /dev/null +++ b/tests/coverage/condition/conditions.rs @@ -0,0 +1,67 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ compile-flags: -Zcoverage-options=condition +//@ llvm-cov-flags: --show-branches=count + +use core::hint::black_box; + +fn simple_assign(a: bool) { + let x = a; + black_box(x); +} + +fn assign_and(a: bool, b: bool) { + let x = a && b; + black_box(x); +} + +fn assign_or(a: bool, b: bool) { + let x = a || b; + black_box(x); +} + +fn assign_3_or_and(a: bool, b: bool, c: bool) { + let x = a || b && c; + black_box(x); +} + +fn assign_3_and_or(a: bool, b: bool, c: bool) { + let x = a && b || c; + black_box(x); +} + +fn foo(a: bool) -> bool { + black_box(a) +} + +fn func_call(a: bool, b: bool) { + foo(a && b); +} + +#[coverage(off)] +fn main() { + simple_assign(true); + simple_assign(false); + + assign_and(true, false); + assign_and(true, true); + assign_and(false, false); + + assign_or(true, false); + assign_or(true, true); + assign_or(false, false); + + assign_3_or_and(true, false, false); + assign_3_or_and(true, true, false); + assign_3_or_and(false, false, true); + assign_3_or_and(false, true, true); + + assign_3_and_or(true, false, false); + assign_3_and_or(true, true, false); + assign_3_and_or(false, false, true); + assign_3_and_or(false, true, true); + + func_call(true, false); + func_call(true, true); + func_call(false, false); +} diff --git a/tests/coverage/conditions.cov-map b/tests/coverage/conditions.cov-map index a6a427aca007..17143775b7b4 100644 --- a/tests/coverage/conditions.cov-map +++ b/tests/coverage/conditions.cov-map @@ -1,259 +1,263 @@ Function name: conditions::main -Raw bytes (784): 0x[01, 01, 8e, 01, 09, 33, 37, 41, 3b, 3d, 35, 39, 05, 00, b7, 04, 09, 05, 00, 0d, 35, 26, 39, 0d, 35, 3b, 3d, 35, 39, 37, 41, 3b, 3d, 35, 39, b2, 04, 0d, b7, 04, 09, 05, 00, 45, 00, 83, 01, 49, 45, 00, 7e, 31, 83, 01, 49, 45, 00, 7a, 4d, 7e, 31, 83, 01, 49, 45, 00, 76, 51, 7a, 4d, 7e, 31, 83, 01, 49, 45, 00, a7, 01, 55, 4d, 51, a3, 01, 59, a7, 01, 55, 4d, 51, 49, 9f, 01, a3, 01, 59, a7, 01, 55, 4d, 51, 61, 00, e3, 01, 65, 61, 00, de, 01, 2d, e3, 01, 65, 61, 00, da, 01, 69, de, 01, 2d, e3, 01, 65, 61, 00, d6, 01, 6d, da, 01, 69, de, 01, 2d, e3, 01, 65, 61, 00, 8b, 02, 71, 69, 6d, 87, 02, 75, 8b, 02, 71, 69, 6d, ff, 01, 00, 65, 83, 02, 87, 02, 75, 8b, 02, 71, 69, 6d, 7d, eb, 03, ef, 03, 8d, 01, f3, 03, 89, 01, 81, 01, 85, 01, 79, 00, d7, 02, 7d, 79, 00, d2, 02, 29, d7, 02, 7d, 79, 00, ce, 02, 81, 01, d2, 02, 29, d7, 02, 7d, 79, 00, ca, 02, 85, 01, ce, 02, 81, 01, d2, 02, 29, d7, 02, 7d, 79, 00, f3, 03, 89, 01, 81, 01, 85, 01, ef, 03, 8d, 01, f3, 03, 89, 01, 81, 01, 85, 01, 11, 93, 04, 97, 04, 21, 9b, 04, 1d, 15, 19, 7d, eb, 03, ef, 03, 8d, 01, f3, 03, 89, 01, 81, 01, 85, 01, e7, 03, 11, 7d, eb, 03, ef, 03, 8d, 01, f3, 03, 89, 01, 81, 01, 85, 01, e2, 03, 25, e7, 03, 11, 7d, eb, 03, ef, 03, 8d, 01, f3, 03, 89, 01, 81, 01, 85, 01, de, 03, 15, e2, 03, 25, e7, 03, 11, 7d, eb, 03, ef, 03, 8d, 01, f3, 03, 89, 01, 81, 01, 85, 01, da, 03, 19, de, 03, 15, e2, 03, 25, e7, 03, 11, 7d, eb, 03, ef, 03, 8d, 01, f3, 03, 89, 01, 81, 01, 85, 01, 9b, 04, 1d, 15, 19, 97, 04, 21, 9b, 04, 1d, 15, 19, 8f, 04, 9f, 04, 11, 93, 04, 97, 04, 21, 9b, 04, 1d, 15, 19, a3, 04, ae, 04, a7, 04, 31, ab, 04, 2d, 25, 29, b2, 04, 0d, b7, 04, 09, 05, 00, 44, 01, 03, 01, 02, 0c, 05, 02, 0d, 02, 06, 00, 02, 06, 00, 07, 03, 03, 09, 00, 0a, b7, 04, 00, 10, 00, 1d, 09, 01, 09, 01, 0a, b2, 04, 02, 0f, 00, 1c, 0d, 01, 0c, 00, 19, 26, 00, 1d, 00, 2a, 22, 00, 2e, 00, 3c, 37, 00, 3d, 02, 0a, 41, 02, 0a, 00, 0b, 33, 01, 09, 01, 12, ae, 04, 03, 09, 00, 0f, 03, 03, 09, 01, 0c, 45, 01, 0d, 02, 06, 00, 02, 06, 00, 07, 83, 01, 02, 08, 00, 15, 49, 00, 16, 02, 06, 7e, 02, 0f, 00, 1c, 7a, 01, 0c, 00, 19, 76, 00, 1d, 00, 2a, 72, 00, 2e, 00, 3c, a3, 01, 00, 3d, 02, 0a, 59, 02, 0a, 00, 0b, 9f, 01, 01, 09, 00, 17, 31, 02, 09, 00, 0f, 9b, 01, 03, 08, 00, 0c, 5d, 01, 0d, 01, 10, 61, 01, 11, 02, 0a, 00, 02, 0a, 00, 0b, e3, 01, 02, 0c, 00, 19, 65, 00, 1a, 02, 0a, de, 01, 04, 11, 00, 1e, da, 01, 01, 10, 00, 1d, d6, 01, 00, 21, 00, 2e, d2, 01, 00, 32, 00, 40, 87, 02, 00, 41, 02, 0e, 75, 02, 0e, 00, 0f, 83, 02, 01, 0d, 00, 1b, 2d, 02, 0d, 00, 13, 00, 02, 06, 00, 07, fb, 01, 02, 09, 01, 0c, 79, 01, 0d, 02, 06, 00, 02, 06, 00, 07, e7, 03, 02, 09, 00, 0a, d7, 02, 00, 10, 00, 1d, 7d, 00, 1e, 02, 06, d2, 02, 02, 0f, 00, 1c, ce, 02, 01, 0c, 00, 19, ca, 02, 00, 1d, 00, 2a, c6, 02, 00, 2e, 00, 3c, ef, 03, 00, 3d, 02, 0a, 8d, 01, 02, 0a, 00, 0b, eb, 03, 01, 09, 00, 17, 29, 02, 0d, 02, 0f, 8f, 04, 05, 09, 00, 0a, e7, 03, 00, 10, 00, 1d, 11, 00, 1e, 02, 06, e2, 03, 02, 0f, 00, 1c, de, 03, 01, 0c, 00, 19, da, 03, 00, 1d, 00, 2a, d6, 03, 00, 2e, 00, 3c, 97, 04, 00, 3d, 02, 0a, 21, 02, 0a, 00, 0b, 93, 04, 01, 09, 00, 17, 25, 02, 09, 00, 0f, 8b, 04, 02, 01, 00, 02] +Raw bytes (799): 0x[01, 01, 94, 01, 09, 2b, 2f, 41, 33, 3d, 35, 39, 01, 09, 0d, 35, 1e, 39, 0d, 35, 33, 3d, 35, 39, 2f, 41, 33, 3d, 35, 39, ce, 04, 0d, 01, 09, 03, 49, 62, 31, 03, 49, 5e, 4d, 62, 31, 03, 49, 5a, 51, 5e, 4d, 62, 31, 03, 49, 87, 01, 55, 4d, 51, 83, 01, 59, 87, 01, 55, 4d, 51, 49, 7f, 83, 01, 59, 87, 01, 55, 4d, 51, 5d, 65, ae, 01, 2d, 5d, 65, aa, 01, 69, ae, 01, 2d, 5d, 65, a6, 01, 6d, aa, 01, 69, ae, 01, 2d, 5d, 65, f3, 02, 71, 69, 6d, ef, 02, 75, f3, 02, 71, 69, 6d, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, e3, 02, 7d, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, de, 02, 29, e3, 02, 7d, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, da, 02, 81, 01, de, 02, 29, e3, 02, 7d, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, d6, 02, 85, 01, da, 02, 81, 01, de, 02, 29, e3, 02, 7d, e7, 02, 00, 65, eb, 02, ef, 02, 75, f3, 02, 71, 69, 6d, 8f, 04, 89, 01, 81, 01, 85, 01, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, 11, af, 04, b3, 04, 21, b7, 04, 1d, 15, 19, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, 83, 04, 11, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, fe, 03, 25, 83, 04, 11, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, fa, 03, 15, fe, 03, 25, 83, 04, 11, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, f6, 03, 19, fa, 03, 15, fe, 03, 25, 83, 04, 11, 7d, 87, 04, 8b, 04, 8d, 01, 8f, 04, 89, 01, 81, 01, 85, 01, b7, 04, 1d, 15, 19, b3, 04, 21, b7, 04, 1d, 15, 19, ab, 04, bb, 04, 11, af, 04, b3, 04, 21, b7, 04, 1d, 15, 19, bf, 04, ca, 04, c3, 04, 31, c7, 04, 2d, 25, 29, ce, 04, 0d, 01, 09, 44, 01, 03, 01, 02, 0c, 05, 02, 0d, 02, 06, 00, 02, 06, 00, 07, 03, 03, 09, 00, 0a, 01, 00, 10, 00, 1d, 09, 01, 09, 01, 0a, ce, 04, 02, 0f, 00, 1c, 0d, 01, 0c, 00, 19, 1e, 00, 1d, 00, 2a, 1a, 00, 2e, 00, 3c, 2f, 00, 3d, 02, 0a, 41, 02, 0a, 00, 0b, 2b, 01, 09, 01, 12, ca, 04, 03, 09, 00, 0f, 03, 03, 09, 01, 0c, 45, 01, 0d, 02, 06, 00, 02, 06, 00, 07, 03, 02, 08, 00, 15, 49, 00, 16, 02, 06, 62, 02, 0f, 00, 1c, 5e, 01, 0c, 00, 19, 5a, 00, 1d, 00, 2a, 56, 00, 2e, 00, 3c, 83, 01, 00, 3d, 02, 0a, 59, 02, 0a, 00, 0b, 7f, 01, 09, 00, 17, 31, 02, 09, 00, 0f, 7b, 03, 08, 00, 0c, 5d, 01, 0d, 01, 10, 61, 01, 11, 02, 0a, 00, 02, 0a, 00, 0b, 5d, 02, 0c, 00, 19, 65, 00, 1a, 02, 0a, ae, 01, 04, 11, 00, 1e, aa, 01, 01, 10, 00, 1d, a6, 01, 00, 21, 00, 2e, a2, 01, 00, 32, 00, 40, ef, 02, 00, 41, 02, 0e, 75, 02, 0e, 00, 0f, eb, 02, 01, 0d, 00, 1b, 2d, 02, 0d, 00, 13, 00, 02, 06, 00, 07, e3, 02, 02, 09, 01, 0c, 79, 01, 0d, 02, 06, 00, 02, 06, 00, 07, 83, 04, 02, 09, 00, 0a, e3, 02, 00, 10, 00, 1d, 7d, 00, 1e, 02, 06, de, 02, 02, 0f, 00, 1c, da, 02, 01, 0c, 00, 19, d6, 02, 00, 1d, 00, 2a, d2, 02, 00, 2e, 00, 3c, 8b, 04, 00, 3d, 02, 0a, 8d, 01, 02, 0a, 00, 0b, 87, 04, 01, 09, 00, 17, 29, 02, 0d, 02, 0f, ab, 04, 05, 09, 00, 0a, 83, 04, 00, 10, 00, 1d, 11, 00, 1e, 02, 06, fe, 03, 02, 0f, 00, 1c, fa, 03, 01, 0c, 00, 19, f6, 03, 00, 1d, 00, 2a, f2, 03, 00, 2e, 00, 3c, b3, 04, 00, 3d, 02, 0a, 21, 02, 0a, 00, 0b, af, 04, 01, 09, 00, 17, 25, 02, 09, 00, 0f, a7, 04, 02, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 142 -- expression 0 operands: lhs = Counter(2), rhs = Expression(12, Add) -- expression 1 operands: lhs = Expression(13, Add), rhs = Counter(16) -- expression 2 operands: lhs = Expression(14, Add), rhs = Counter(15) +Number of expressions: 148 +- expression 0 operands: lhs = Counter(2), rhs = Expression(10, Add) +- expression 1 operands: lhs = Expression(11, Add), rhs = Counter(16) +- expression 2 operands: lhs = Expression(12, Add), rhs = Counter(15) - expression 3 operands: lhs = Counter(13), rhs = Counter(14) -- expression 4 operands: lhs = Counter(1), rhs = Zero -- expression 5 operands: lhs = Expression(141, Add), rhs = Counter(2) -- expression 6 operands: lhs = Counter(1), rhs = Zero +- expression 4 operands: lhs = Counter(0), rhs = Counter(2) +- expression 5 operands: lhs = Counter(3), rhs = Counter(13) +- expression 6 operands: lhs = Expression(7, Sub), rhs = Counter(14) - expression 7 operands: lhs = Counter(3), rhs = Counter(13) -- expression 8 operands: lhs = Expression(9, Sub), rhs = Counter(14) -- expression 9 operands: lhs = Counter(3), rhs = Counter(13) -- expression 10 operands: lhs = Expression(14, Add), rhs = Counter(15) -- expression 11 operands: lhs = Counter(13), rhs = Counter(14) -- expression 12 operands: lhs = Expression(13, Add), rhs = Counter(16) -- expression 13 operands: lhs = Expression(14, Add), rhs = Counter(15) -- expression 14 operands: lhs = Counter(13), rhs = Counter(14) -- expression 15 operands: lhs = Expression(140, Sub), rhs = Counter(3) -- expression 16 operands: lhs = Expression(141, Add), rhs = Counter(2) -- expression 17 operands: lhs = Counter(1), rhs = Zero -- expression 18 operands: lhs = Counter(17), rhs = Zero -- expression 19 operands: lhs = Expression(32, Add), rhs = Counter(18) -- expression 20 operands: lhs = Counter(17), rhs = Zero -- expression 21 operands: lhs = Expression(31, Sub), rhs = Counter(12) -- expression 22 operands: lhs = Expression(32, Add), rhs = Counter(18) -- expression 23 operands: lhs = Counter(17), rhs = Zero -- expression 24 operands: lhs = Expression(30, Sub), rhs = Counter(19) -- expression 25 operands: lhs = Expression(31, Sub), rhs = Counter(12) -- expression 26 operands: lhs = Expression(32, Add), rhs = Counter(18) -- expression 27 operands: lhs = Counter(17), rhs = Zero -- expression 28 operands: lhs = Expression(29, Sub), rhs = Counter(20) -- expression 29 operands: lhs = Expression(30, Sub), rhs = Counter(19) -- expression 30 operands: lhs = Expression(31, Sub), rhs = Counter(12) -- expression 31 operands: lhs = Expression(32, Add), rhs = Counter(18) -- expression 32 operands: lhs = Counter(17), rhs = Zero -- expression 33 operands: lhs = Expression(41, Add), rhs = Counter(21) -- expression 34 operands: lhs = Counter(19), rhs = Counter(20) -- expression 35 operands: lhs = Expression(40, Add), rhs = Counter(22) -- expression 36 operands: lhs = Expression(41, Add), rhs = Counter(21) -- expression 37 operands: lhs = Counter(19), rhs = Counter(20) -- expression 38 operands: lhs = Counter(18), rhs = Expression(39, Add) -- expression 39 operands: lhs = Expression(40, Add), rhs = Counter(22) -- expression 40 operands: lhs = Expression(41, Add), rhs = Counter(21) -- expression 41 operands: lhs = Counter(19), rhs = Counter(20) -- expression 42 operands: lhs = Counter(24), rhs = Zero -- expression 43 operands: lhs = Expression(56, Add), rhs = Counter(25) -- expression 44 operands: lhs = Counter(24), rhs = Zero -- expression 45 operands: lhs = Expression(55, Sub), rhs = Counter(11) -- expression 46 operands: lhs = Expression(56, Add), rhs = Counter(25) -- expression 47 operands: lhs = Counter(24), rhs = Zero -- expression 48 operands: lhs = Expression(54, Sub), rhs = Counter(26) -- expression 49 operands: lhs = Expression(55, Sub), rhs = Counter(11) -- expression 50 operands: lhs = Expression(56, Add), rhs = Counter(25) -- expression 51 operands: lhs = Counter(24), rhs = Zero -- expression 52 operands: lhs = Expression(53, Sub), rhs = Counter(27) -- expression 53 operands: lhs = Expression(54, Sub), rhs = Counter(26) -- expression 54 operands: lhs = Expression(55, Sub), rhs = Counter(11) -- expression 55 operands: lhs = Expression(56, Add), rhs = Counter(25) -- expression 56 operands: lhs = Counter(24), rhs = Zero -- expression 57 operands: lhs = Expression(66, Add), rhs = Counter(28) -- expression 58 operands: lhs = Counter(26), rhs = Counter(27) -- expression 59 operands: lhs = Expression(65, Add), rhs = Counter(29) -- expression 60 operands: lhs = Expression(66, Add), rhs = Counter(28) -- expression 61 operands: lhs = Counter(26), rhs = Counter(27) -- expression 62 operands: lhs = Expression(63, Add), rhs = Zero -- expression 63 operands: lhs = Counter(25), rhs = Expression(64, Add) -- expression 64 operands: lhs = Expression(65, Add), rhs = Counter(29) -- expression 65 operands: lhs = Expression(66, Add), rhs = Counter(28) -- expression 66 operands: lhs = Counter(26), rhs = Counter(27) -- expression 67 operands: lhs = Counter(31), rhs = Expression(122, Add) -- expression 68 operands: lhs = Expression(123, Add), rhs = Counter(35) -- expression 69 operands: lhs = Expression(124, Add), rhs = Counter(34) -- expression 70 operands: lhs = Counter(32), rhs = Counter(33) -- expression 71 operands: lhs = Counter(30), rhs = Zero -- expression 72 operands: lhs = Expression(85, Add), rhs = Counter(31) -- expression 73 operands: lhs = Counter(30), rhs = Zero -- expression 74 operands: lhs = Expression(84, Sub), rhs = Counter(10) -- expression 75 operands: lhs = Expression(85, Add), rhs = Counter(31) -- expression 76 operands: lhs = Counter(30), rhs = Zero -- expression 77 operands: lhs = Expression(83, Sub), rhs = Counter(32) -- expression 78 operands: lhs = Expression(84, Sub), rhs = Counter(10) -- expression 79 operands: lhs = Expression(85, Add), rhs = Counter(31) -- expression 80 operands: lhs = Counter(30), rhs = Zero -- expression 81 operands: lhs = Expression(82, Sub), rhs = Counter(33) -- expression 82 operands: lhs = Expression(83, Sub), rhs = Counter(32) -- expression 83 operands: lhs = Expression(84, Sub), rhs = Counter(10) -- expression 84 operands: lhs = Expression(85, Add), rhs = Counter(31) -- expression 85 operands: lhs = Counter(30), rhs = Zero -- expression 86 operands: lhs = Expression(124, Add), rhs = Counter(34) -- expression 87 operands: lhs = Counter(32), rhs = Counter(33) -- expression 88 operands: lhs = Expression(123, Add), rhs = Counter(35) -- expression 89 operands: lhs = Expression(124, Add), rhs = Counter(34) -- expression 90 operands: lhs = Counter(32), rhs = Counter(33) -- expression 91 operands: lhs = Counter(4), rhs = Expression(132, Add) -- expression 92 operands: lhs = Expression(133, Add), rhs = Counter(8) -- expression 93 operands: lhs = Expression(134, Add), rhs = Counter(7) -- expression 94 operands: lhs = Counter(5), rhs = Counter(6) -- expression 95 operands: lhs = Counter(31), rhs = Expression(122, Add) -- expression 96 operands: lhs = Expression(123, Add), rhs = Counter(35) -- expression 97 operands: lhs = Expression(124, Add), rhs = Counter(34) -- expression 98 operands: lhs = Counter(32), rhs = Counter(33) -- expression 99 operands: lhs = Expression(121, Add), rhs = Counter(4) -- expression 100 operands: lhs = Counter(31), rhs = Expression(122, Add) -- expression 101 operands: lhs = Expression(123, Add), rhs = Counter(35) -- expression 102 operands: lhs = Expression(124, Add), rhs = Counter(34) -- expression 103 operands: lhs = Counter(32), rhs = Counter(33) -- expression 104 operands: lhs = Expression(120, Sub), rhs = Counter(9) -- expression 105 operands: lhs = Expression(121, Add), rhs = Counter(4) -- expression 106 operands: lhs = Counter(31), rhs = Expression(122, Add) -- expression 107 operands: lhs = Expression(123, Add), rhs = Counter(35) -- expression 108 operands: lhs = Expression(124, Add), rhs = Counter(34) -- expression 109 operands: lhs = Counter(32), rhs = Counter(33) -- expression 110 operands: lhs = Expression(119, Sub), rhs = Counter(5) -- expression 111 operands: lhs = Expression(120, Sub), rhs = Counter(9) -- expression 112 operands: lhs = Expression(121, Add), rhs = Counter(4) -- expression 113 operands: lhs = Counter(31), rhs = Expression(122, Add) -- expression 114 operands: lhs = Expression(123, Add), rhs = Counter(35) -- expression 115 operands: lhs = Expression(124, Add), rhs = Counter(34) +- expression 8 operands: lhs = Expression(12, Add), rhs = Counter(15) +- expression 9 operands: lhs = Counter(13), rhs = Counter(14) +- expression 10 operands: lhs = Expression(11, Add), rhs = Counter(16) +- expression 11 operands: lhs = Expression(12, Add), rhs = Counter(15) +- expression 12 operands: lhs = Counter(13), rhs = Counter(14) +- expression 13 operands: lhs = Expression(147, Sub), rhs = Counter(3) +- expression 14 operands: lhs = Counter(0), rhs = Counter(2) +- expression 15 operands: lhs = Expression(0, Add), rhs = Counter(18) +- expression 16 operands: lhs = Expression(24, Sub), rhs = Counter(12) +- expression 17 operands: lhs = Expression(0, Add), rhs = Counter(18) +- expression 18 operands: lhs = Expression(23, Sub), rhs = Counter(19) +- expression 19 operands: lhs = Expression(24, Sub), rhs = Counter(12) +- expression 20 operands: lhs = Expression(0, Add), rhs = Counter(18) +- expression 21 operands: lhs = Expression(22, Sub), rhs = Counter(20) +- expression 22 operands: lhs = Expression(23, Sub), rhs = Counter(19) +- expression 23 operands: lhs = Expression(24, Sub), rhs = Counter(12) +- expression 24 operands: lhs = Expression(0, Add), rhs = Counter(18) +- expression 25 operands: lhs = Expression(33, Add), rhs = Counter(21) +- expression 26 operands: lhs = Counter(19), rhs = Counter(20) +- expression 27 operands: lhs = Expression(32, Add), rhs = Counter(22) +- expression 28 operands: lhs = Expression(33, Add), rhs = Counter(21) +- expression 29 operands: lhs = Counter(19), rhs = Counter(20) +- expression 30 operands: lhs = Counter(18), rhs = Expression(31, Add) +- expression 31 operands: lhs = Expression(32, Add), rhs = Counter(22) +- expression 32 operands: lhs = Expression(33, Add), rhs = Counter(21) +- expression 33 operands: lhs = Counter(19), rhs = Counter(20) +- expression 34 operands: lhs = Counter(23), rhs = Counter(25) +- expression 35 operands: lhs = Expression(43, Sub), rhs = Counter(11) +- expression 36 operands: lhs = Counter(23), rhs = Counter(25) +- expression 37 operands: lhs = Expression(42, Sub), rhs = Counter(26) +- expression 38 operands: lhs = Expression(43, Sub), rhs = Counter(11) +- expression 39 operands: lhs = Counter(23), rhs = Counter(25) +- expression 40 operands: lhs = Expression(41, Sub), rhs = Counter(27) +- expression 41 operands: lhs = Expression(42, Sub), rhs = Counter(26) +- expression 42 operands: lhs = Expression(43, Sub), rhs = Counter(11) +- expression 43 operands: lhs = Counter(23), rhs = Counter(25) +- expression 44 operands: lhs = Expression(92, Add), rhs = Counter(28) +- expression 45 operands: lhs = Counter(26), rhs = Counter(27) +- expression 46 operands: lhs = Expression(91, Add), rhs = Counter(29) +- expression 47 operands: lhs = Expression(92, Add), rhs = Counter(28) +- expression 48 operands: lhs = Counter(26), rhs = Counter(27) +- expression 49 operands: lhs = Expression(89, Add), rhs = Zero +- expression 50 operands: lhs = Counter(25), rhs = Expression(90, Add) +- expression 51 operands: lhs = Expression(91, Add), rhs = Counter(29) +- expression 52 operands: lhs = Expression(92, Add), rhs = Counter(28) +- expression 53 operands: lhs = Counter(26), rhs = Counter(27) +- expression 54 operands: lhs = Counter(31), rhs = Expression(129, Add) +- expression 55 operands: lhs = Expression(130, Add), rhs = Counter(35) +- expression 56 operands: lhs = Expression(131, Add), rhs = Counter(34) +- expression 57 operands: lhs = Counter(32), rhs = Counter(33) +- expression 58 operands: lhs = Expression(89, Add), rhs = Zero +- expression 59 operands: lhs = Counter(25), rhs = Expression(90, Add) +- expression 60 operands: lhs = Expression(91, Add), rhs = Counter(29) +- expression 61 operands: lhs = Expression(92, Add), rhs = Counter(28) +- expression 62 operands: lhs = Counter(26), rhs = Counter(27) +- expression 63 operands: lhs = Expression(88, Add), rhs = Counter(31) +- expression 64 operands: lhs = Expression(89, Add), rhs = Zero +- expression 65 operands: lhs = Counter(25), rhs = Expression(90, Add) +- expression 66 operands: lhs = Expression(91, Add), rhs = Counter(29) +- expression 67 operands: lhs = Expression(92, Add), rhs = Counter(28) +- expression 68 operands: lhs = Counter(26), rhs = Counter(27) +- expression 69 operands: lhs = Expression(87, Sub), rhs = Counter(10) +- expression 70 operands: lhs = Expression(88, Add), rhs = Counter(31) +- expression 71 operands: lhs = Expression(89, Add), rhs = Zero +- expression 72 operands: lhs = Counter(25), rhs = Expression(90, Add) +- expression 73 operands: lhs = Expression(91, Add), rhs = Counter(29) +- expression 74 operands: lhs = Expression(92, Add), rhs = Counter(28) +- expression 75 operands: lhs = Counter(26), rhs = Counter(27) +- expression 76 operands: lhs = Expression(86, Sub), rhs = Counter(32) +- expression 77 operands: lhs = Expression(87, Sub), rhs = Counter(10) +- expression 78 operands: lhs = Expression(88, Add), rhs = Counter(31) +- expression 79 operands: lhs = Expression(89, Add), rhs = Zero +- expression 80 operands: lhs = Counter(25), rhs = Expression(90, Add) +- expression 81 operands: lhs = Expression(91, Add), rhs = Counter(29) +- expression 82 operands: lhs = Expression(92, Add), rhs = Counter(28) +- expression 83 operands: lhs = Counter(26), rhs = Counter(27) +- expression 84 operands: lhs = Expression(85, Sub), rhs = Counter(33) +- expression 85 operands: lhs = Expression(86, Sub), rhs = Counter(32) +- expression 86 operands: lhs = Expression(87, Sub), rhs = Counter(10) +- expression 87 operands: lhs = Expression(88, Add), rhs = Counter(31) +- expression 88 operands: lhs = Expression(89, Add), rhs = Zero +- expression 89 operands: lhs = Counter(25), rhs = Expression(90, Add) +- expression 90 operands: lhs = Expression(91, Add), rhs = Counter(29) +- expression 91 operands: lhs = Expression(92, Add), rhs = Counter(28) +- expression 92 operands: lhs = Counter(26), rhs = Counter(27) +- expression 93 operands: lhs = Expression(131, Add), rhs = Counter(34) +- expression 94 operands: lhs = Counter(32), rhs = Counter(33) +- expression 95 operands: lhs = Expression(130, Add), rhs = Counter(35) +- expression 96 operands: lhs = Expression(131, Add), rhs = Counter(34) +- expression 97 operands: lhs = Counter(32), rhs = Counter(33) +- expression 98 operands: lhs = Counter(4), rhs = Expression(139, Add) +- expression 99 operands: lhs = Expression(140, Add), rhs = Counter(8) +- expression 100 operands: lhs = Expression(141, Add), rhs = Counter(7) +- expression 101 operands: lhs = Counter(5), rhs = Counter(6) +- expression 102 operands: lhs = Counter(31), rhs = Expression(129, Add) +- expression 103 operands: lhs = Expression(130, Add), rhs = Counter(35) +- expression 104 operands: lhs = Expression(131, Add), rhs = Counter(34) +- expression 105 operands: lhs = Counter(32), rhs = Counter(33) +- expression 106 operands: lhs = Expression(128, Add), rhs = Counter(4) +- expression 107 operands: lhs = Counter(31), rhs = Expression(129, Add) +- expression 108 operands: lhs = Expression(130, Add), rhs = Counter(35) +- expression 109 operands: lhs = Expression(131, Add), rhs = Counter(34) +- expression 110 operands: lhs = Counter(32), rhs = Counter(33) +- expression 111 operands: lhs = Expression(127, Sub), rhs = Counter(9) +- expression 112 operands: lhs = Expression(128, Add), rhs = Counter(4) +- expression 113 operands: lhs = Counter(31), rhs = Expression(129, Add) +- expression 114 operands: lhs = Expression(130, Add), rhs = Counter(35) +- expression 115 operands: lhs = Expression(131, Add), rhs = Counter(34) - expression 116 operands: lhs = Counter(32), rhs = Counter(33) -- expression 117 operands: lhs = Expression(118, Sub), rhs = Counter(6) -- expression 118 operands: lhs = Expression(119, Sub), rhs = Counter(5) -- expression 119 operands: lhs = Expression(120, Sub), rhs = Counter(9) -- expression 120 operands: lhs = Expression(121, Add), rhs = Counter(4) -- expression 121 operands: lhs = Counter(31), rhs = Expression(122, Add) -- expression 122 operands: lhs = Expression(123, Add), rhs = Counter(35) -- expression 123 operands: lhs = Expression(124, Add), rhs = Counter(34) -- expression 124 operands: lhs = Counter(32), rhs = Counter(33) -- expression 125 operands: lhs = Expression(134, Add), rhs = Counter(7) -- expression 126 operands: lhs = Counter(5), rhs = Counter(6) -- expression 127 operands: lhs = Expression(133, Add), rhs = Counter(8) -- expression 128 operands: lhs = Expression(134, Add), rhs = Counter(7) -- expression 129 operands: lhs = Counter(5), rhs = Counter(6) -- expression 130 operands: lhs = Expression(131, Add), rhs = Expression(135, Add) -- expression 131 operands: lhs = Counter(4), rhs = Expression(132, Add) -- expression 132 operands: lhs = Expression(133, Add), rhs = Counter(8) -- expression 133 operands: lhs = Expression(134, Add), rhs = Counter(7) -- expression 134 operands: lhs = Counter(5), rhs = Counter(6) -- expression 135 operands: lhs = Expression(136, Add), rhs = Expression(139, Sub) -- expression 136 operands: lhs = Expression(137, Add), rhs = Counter(12) -- expression 137 operands: lhs = Expression(138, Add), rhs = Counter(11) -- expression 138 operands: lhs = Counter(9), rhs = Counter(10) -- expression 139 operands: lhs = Expression(140, Sub), rhs = Counter(3) -- expression 140 operands: lhs = Expression(141, Add), rhs = Counter(2) -- expression 141 operands: lhs = Counter(1), rhs = Zero +- expression 117 operands: lhs = Expression(126, Sub), rhs = Counter(5) +- expression 118 operands: lhs = Expression(127, Sub), rhs = Counter(9) +- expression 119 operands: lhs = Expression(128, Add), rhs = Counter(4) +- expression 120 operands: lhs = Counter(31), rhs = Expression(129, Add) +- expression 121 operands: lhs = Expression(130, Add), rhs = Counter(35) +- expression 122 operands: lhs = Expression(131, Add), rhs = Counter(34) +- expression 123 operands: lhs = Counter(32), rhs = Counter(33) +- expression 124 operands: lhs = Expression(125, Sub), rhs = Counter(6) +- expression 125 operands: lhs = Expression(126, Sub), rhs = Counter(5) +- expression 126 operands: lhs = Expression(127, Sub), rhs = Counter(9) +- expression 127 operands: lhs = Expression(128, Add), rhs = Counter(4) +- expression 128 operands: lhs = Counter(31), rhs = Expression(129, Add) +- expression 129 operands: lhs = Expression(130, Add), rhs = Counter(35) +- expression 130 operands: lhs = Expression(131, Add), rhs = Counter(34) +- expression 131 operands: lhs = Counter(32), rhs = Counter(33) +- expression 132 operands: lhs = Expression(141, Add), rhs = Counter(7) +- expression 133 operands: lhs = Counter(5), rhs = Counter(6) +- expression 134 operands: lhs = Expression(140, Add), rhs = Counter(8) +- expression 135 operands: lhs = Expression(141, Add), rhs = Counter(7) +- expression 136 operands: lhs = Counter(5), rhs = Counter(6) +- expression 137 operands: lhs = Expression(138, Add), rhs = Expression(142, Add) +- expression 138 operands: lhs = Counter(4), rhs = Expression(139, Add) +- expression 139 operands: lhs = Expression(140, Add), rhs = Counter(8) +- expression 140 operands: lhs = Expression(141, Add), rhs = Counter(7) +- expression 141 operands: lhs = Counter(5), rhs = Counter(6) +- expression 142 operands: lhs = Expression(143, Add), rhs = Expression(146, Sub) +- expression 143 operands: lhs = Expression(144, Add), rhs = Counter(12) +- expression 144 operands: lhs = Expression(145, Add), rhs = Counter(11) +- expression 145 operands: lhs = Counter(9), rhs = Counter(10) +- expression 146 operands: lhs = Expression(147, Sub), rhs = Counter(3) +- expression 147 operands: lhs = Counter(0), rhs = Counter(2) Number of file 0 mappings: 68 - Code(Counter(0)) at (prev + 3, 1) to (start + 2, 12) - Code(Counter(1)) at (prev + 2, 13) to (start + 2, 6) - Code(Zero) at (prev + 2, 6) to (start + 0, 7) - Code(Expression(0, Add)) at (prev + 3, 9) to (start + 0, 10) = (c2 + (((c13 + c14) + c15) + c16)) -- Code(Expression(141, Add)) at (prev + 0, 16) to (start + 0, 29) - = (c1 + Zero) +- Code(Counter(0)) at (prev + 0, 16) to (start + 0, 29) - Code(Counter(2)) at (prev + 1, 9) to (start + 1, 10) -- Code(Expression(140, Sub)) at (prev + 2, 15) to (start + 0, 28) - = ((c1 + Zero) - c2) +- Code(Expression(147, Sub)) at (prev + 2, 15) to (start + 0, 28) + = (c0 - c2) - Code(Counter(3)) at (prev + 1, 12) to (start + 0, 25) -- Code(Expression(9, Sub)) at (prev + 0, 29) to (start + 0, 42) +- Code(Expression(7, Sub)) at (prev + 0, 29) to (start + 0, 42) = (c3 - c13) -- Code(Expression(8, Sub)) at (prev + 0, 46) to (start + 0, 60) +- Code(Expression(6, Sub)) at (prev + 0, 46) to (start + 0, 60) = ((c3 - c13) - c14) -- Code(Expression(13, Add)) at (prev + 0, 61) to (start + 2, 10) +- Code(Expression(11, Add)) at (prev + 0, 61) to (start + 2, 10) = ((c13 + c14) + c15) - Code(Counter(16)) at (prev + 2, 10) to (start + 0, 11) -- Code(Expression(12, Add)) at (prev + 1, 9) to (start + 1, 18) +- Code(Expression(10, Add)) at (prev + 1, 9) to (start + 1, 18) = (((c13 + c14) + c15) + c16) -- Code(Expression(139, Sub)) at (prev + 3, 9) to (start + 0, 15) - = (((c1 + Zero) - c2) - c3) +- Code(Expression(146, Sub)) at (prev + 3, 9) to (start + 0, 15) + = ((c0 - c2) - c3) - Code(Expression(0, Add)) at (prev + 3, 9) to (start + 1, 12) = (c2 + (((c13 + c14) + c15) + c16)) - Code(Counter(17)) at (prev + 1, 13) to (start + 2, 6) - Code(Zero) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(32, Add)) at (prev + 2, 8) to (start + 0, 21) - = (c17 + Zero) +- Code(Expression(0, Add)) at (prev + 2, 8) to (start + 0, 21) + = (c2 + (((c13 + c14) + c15) + c16)) - Code(Counter(18)) at (prev + 0, 22) to (start + 2, 6) -- Code(Expression(31, Sub)) at (prev + 2, 15) to (start + 0, 28) - = ((c17 + Zero) - c18) -- Code(Expression(30, Sub)) at (prev + 1, 12) to (start + 0, 25) - = (((c17 + Zero) - c18) - c12) -- Code(Expression(29, Sub)) at (prev + 0, 29) to (start + 0, 42) - = ((((c17 + Zero) - c18) - c12) - c19) -- Code(Expression(28, Sub)) at (prev + 0, 46) to (start + 0, 60) - = (((((c17 + Zero) - c18) - c12) - c19) - c20) -- Code(Expression(40, Add)) at (prev + 0, 61) to (start + 2, 10) +- Code(Expression(24, Sub)) at (prev + 2, 15) to (start + 0, 28) + = ((c2 + (((c13 + c14) + c15) + c16)) - c18) +- Code(Expression(23, Sub)) at (prev + 1, 12) to (start + 0, 25) + = (((c2 + (((c13 + c14) + c15) + c16)) - c18) - c12) +- Code(Expression(22, Sub)) at (prev + 0, 29) to (start + 0, 42) + = ((((c2 + (((c13 + c14) + c15) + c16)) - c18) - c12) - c19) +- Code(Expression(21, Sub)) at (prev + 0, 46) to (start + 0, 60) + = (((((c2 + (((c13 + c14) + c15) + c16)) - c18) - c12) - c19) - c20) +- Code(Expression(32, Add)) at (prev + 0, 61) to (start + 2, 10) = ((c19 + c20) + c21) - Code(Counter(22)) at (prev + 2, 10) to (start + 0, 11) -- Code(Expression(39, Add)) at (prev + 1, 9) to (start + 0, 23) +- Code(Expression(31, Add)) at (prev + 1, 9) to (start + 0, 23) = (((c19 + c20) + c21) + c22) - Code(Counter(12)) at (prev + 2, 9) to (start + 0, 15) -- Code(Expression(38, Add)) at (prev + 3, 8) to (start + 0, 12) +- Code(Expression(30, Add)) at (prev + 3, 8) to (start + 0, 12) = (c18 + (((c19 + c20) + c21) + c22)) - Code(Counter(23)) at (prev + 1, 13) to (start + 1, 16) - Code(Counter(24)) at (prev + 1, 17) to (start + 2, 10) - Code(Zero) at (prev + 2, 10) to (start + 0, 11) -- Code(Expression(56, Add)) at (prev + 2, 12) to (start + 0, 25) - = (c24 + Zero) +- Code(Counter(23)) at (prev + 2, 12) to (start + 0, 25) - Code(Counter(25)) at (prev + 0, 26) to (start + 2, 10) -- Code(Expression(55, Sub)) at (prev + 4, 17) to (start + 0, 30) - = ((c24 + Zero) - c25) -- Code(Expression(54, Sub)) at (prev + 1, 16) to (start + 0, 29) - = (((c24 + Zero) - c25) - c11) -- Code(Expression(53, Sub)) at (prev + 0, 33) to (start + 0, 46) - = ((((c24 + Zero) - c25) - c11) - c26) -- Code(Expression(52, Sub)) at (prev + 0, 50) to (start + 0, 64) - = (((((c24 + Zero) - c25) - c11) - c26) - c27) -- Code(Expression(65, Add)) at (prev + 0, 65) to (start + 2, 14) +- Code(Expression(43, Sub)) at (prev + 4, 17) to (start + 0, 30) + = (c23 - c25) +- Code(Expression(42, Sub)) at (prev + 1, 16) to (start + 0, 29) + = ((c23 - c25) - c11) +- Code(Expression(41, Sub)) at (prev + 0, 33) to (start + 0, 46) + = (((c23 - c25) - c11) - c26) +- Code(Expression(40, Sub)) at (prev + 0, 50) to (start + 0, 64) + = ((((c23 - c25) - c11) - c26) - c27) +- Code(Expression(91, Add)) at (prev + 0, 65) to (start + 2, 14) = ((c26 + c27) + c28) - Code(Counter(29)) at (prev + 2, 14) to (start + 0, 15) -- Code(Expression(64, Add)) at (prev + 1, 13) to (start + 0, 27) +- Code(Expression(90, Add)) at (prev + 1, 13) to (start + 0, 27) = (((c26 + c27) + c28) + c29) - Code(Counter(11)) at (prev + 2, 13) to (start + 0, 19) - Code(Zero) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(62, Add)) at (prev + 2, 9) to (start + 1, 12) +- Code(Expression(88, Add)) at (prev + 2, 9) to (start + 1, 12) = ((c25 + (((c26 + c27) + c28) + c29)) + Zero) - Code(Counter(30)) at (prev + 1, 13) to (start + 2, 6) - Code(Zero) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(121, Add)) at (prev + 2, 9) to (start + 0, 10) +- Code(Expression(128, Add)) at (prev + 2, 9) to (start + 0, 10) = (c31 + (((c32 + c33) + c34) + c35)) -- Code(Expression(85, Add)) at (prev + 0, 16) to (start + 0, 29) - = (c30 + Zero) +- Code(Expression(88, Add)) at (prev + 0, 16) to (start + 0, 29) + = ((c25 + (((c26 + c27) + c28) + c29)) + Zero) - Code(Counter(31)) at (prev + 0, 30) to (start + 2, 6) -- Code(Expression(84, Sub)) at (prev + 2, 15) to (start + 0, 28) - = ((c30 + Zero) - c31) -- Code(Expression(83, Sub)) at (prev + 1, 12) to (start + 0, 25) - = (((c30 + Zero) - c31) - c10) -- Code(Expression(82, Sub)) at (prev + 0, 29) to (start + 0, 42) - = ((((c30 + Zero) - c31) - c10) - c32) -- Code(Expression(81, Sub)) at (prev + 0, 46) to (start + 0, 60) - = (((((c30 + Zero) - c31) - c10) - c32) - c33) -- Code(Expression(123, Add)) at (prev + 0, 61) to (start + 2, 10) +- Code(Expression(87, Sub)) at (prev + 2, 15) to (start + 0, 28) + = (((c25 + (((c26 + c27) + c28) + c29)) + Zero) - c31) +- Code(Expression(86, Sub)) at (prev + 1, 12) to (start + 0, 25) + = ((((c25 + (((c26 + c27) + c28) + c29)) + Zero) - c31) - c10) +- Code(Expression(85, Sub)) at (prev + 0, 29) to (start + 0, 42) + = (((((c25 + (((c26 + c27) + c28) + c29)) + Zero) - c31) - c10) - c32) +- Code(Expression(84, Sub)) at (prev + 0, 46) to (start + 0, 60) + = ((((((c25 + (((c26 + c27) + c28) + c29)) + Zero) - c31) - c10) - c32) - c33) +- Code(Expression(130, Add)) at (prev + 0, 61) to (start + 2, 10) = ((c32 + c33) + c34) - Code(Counter(35)) at (prev + 2, 10) to (start + 0, 11) -- Code(Expression(122, Add)) at (prev + 1, 9) to (start + 0, 23) +- Code(Expression(129, Add)) at (prev + 1, 9) to (start + 0, 23) = (((c32 + c33) + c34) + c35) - Code(Counter(10)) at (prev + 2, 13) to (start + 2, 15) -- Code(Expression(131, Add)) at (prev + 5, 9) to (start + 0, 10) +- Code(Expression(138, Add)) at (prev + 5, 9) to (start + 0, 10) = (c4 + (((c5 + c6) + c7) + c8)) -- Code(Expression(121, Add)) at (prev + 0, 16) to (start + 0, 29) +- Code(Expression(128, Add)) at (prev + 0, 16) to (start + 0, 29) = (c31 + (((c32 + c33) + c34) + c35)) - Code(Counter(4)) at (prev + 0, 30) to (start + 2, 6) -- Code(Expression(120, Sub)) at (prev + 2, 15) to (start + 0, 28) +- Code(Expression(127, Sub)) at (prev + 2, 15) to (start + 0, 28) = ((c31 + (((c32 + c33) + c34) + c35)) - c4) -- Code(Expression(119, Sub)) at (prev + 1, 12) to (start + 0, 25) +- Code(Expression(126, Sub)) at (prev + 1, 12) to (start + 0, 25) = (((c31 + (((c32 + c33) + c34) + c35)) - c4) - c9) -- Code(Expression(118, Sub)) at (prev + 0, 29) to (start + 0, 42) +- Code(Expression(125, Sub)) at (prev + 0, 29) to (start + 0, 42) = ((((c31 + (((c32 + c33) + c34) + c35)) - c4) - c9) - c5) -- Code(Expression(117, Sub)) at (prev + 0, 46) to (start + 0, 60) +- Code(Expression(124, Sub)) at (prev + 0, 46) to (start + 0, 60) = (((((c31 + (((c32 + c33) + c34) + c35)) - c4) - c9) - c5) - c6) -- Code(Expression(133, Add)) at (prev + 0, 61) to (start + 2, 10) +- Code(Expression(140, Add)) at (prev + 0, 61) to (start + 2, 10) = ((c5 + c6) + c7) - Code(Counter(8)) at (prev + 2, 10) to (start + 0, 11) -- Code(Expression(132, Add)) at (prev + 1, 9) to (start + 0, 23) +- Code(Expression(139, Add)) at (prev + 1, 9) to (start + 0, 23) = (((c5 + c6) + c7) + c8) - Code(Counter(9)) at (prev + 2, 9) to (start + 0, 15) -- Code(Expression(130, Add)) at (prev + 2, 1) to (start + 0, 2) - = ((c4 + (((c5 + c6) + c7) + c8)) + ((((c9 + c10) + c11) + c12) + (((c1 + Zero) - c2) - c3))) +- Code(Expression(137, Add)) at (prev + 2, 1) to (start + 0, 2) + = ((c4 + (((c5 + c6) + c7) + c8)) + ((((c9 + c10) + c11) + c12) + ((c0 - c2) - c3))) diff --git a/tests/coverage/coroutine.cov-map b/tests/coverage/coroutine.cov-map index 255708d365ee..6ff5ed74a96d 100644 --- a/tests/coverage/coroutine.cov-map +++ b/tests/coverage/coroutine.cov-map @@ -1,20 +1,18 @@ Function name: coroutine::get_u32 -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 0b, 01, 01, 0b, 05, 02, 09, 00, 0e, 02, 02, 09, 00, 28, 07, 02, 01, 00, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 0b, 01, 01, 0b, 05, 02, 09, 00, 0e, 02, 02, 09, 00, 28, 01, 02, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 11, 1) to (start + 1, 11) - Code(Counter(1)) at (prev + 2, 9) to (start + 0, 14) - Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 40) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 2, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 2, 1) to (start + 0, 2) Function name: coroutine::main -Raw bytes (65): 0x[01, 01, 08, 07, 0d, 05, 09, 11, 15, 1e, 19, 11, 15, 15, 19, 1e, 19, 11, 15, 09, 01, 13, 01, 02, 16, 01, 07, 0b, 00, 2e, 11, 01, 2b, 00, 2d, 03, 01, 0e, 00, 35, 11, 02, 0b, 00, 2e, 1e, 01, 22, 00, 27, 1a, 00, 2c, 00, 2e, 17, 01, 0e, 00, 35, 1a, 02, 01, 00, 02] +Raw bytes (65): 0x[01, 01, 08, 07, 0d, 05, 09, 11, 15, 1e, 19, 11, 15, 15, 19, 1e, 19, 11, 15, 09, 01, 13, 01, 02, 16, 01, 08, 0b, 00, 2e, 11, 01, 2b, 00, 2d, 03, 01, 0e, 00, 35, 11, 02, 0b, 00, 2e, 1e, 01, 22, 00, 27, 1a, 00, 2c, 00, 2e, 17, 01, 0e, 00, 35, 1a, 02, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 8 @@ -28,7 +26,7 @@ Number of expressions: 8 - expression 7 operands: lhs = Counter(4), rhs = Counter(5) Number of file 0 mappings: 9 - Code(Counter(0)) at (prev + 19, 1) to (start + 2, 22) -- Code(Counter(0)) at (prev + 7, 11) to (start + 0, 46) +- Code(Counter(0)) at (prev + 8, 11) to (start + 0, 46) - Code(Counter(4)) at (prev + 1, 43) to (start + 0, 45) - Code(Expression(0, Add)) at (prev + 1, 14) to (start + 0, 53) = ((c1 + c2) + c3) @@ -43,11 +41,11 @@ Number of file 0 mappings: 9 = ((c4 - c5) - c6) Function name: coroutine::main::{closure#0} -Raw bytes (14): 0x[01, 01, 00, 02, 01, 15, 29, 01, 1f, 05, 02, 10, 01, 06] +Raw bytes (14): 0x[01, 01, 00, 02, 01, 16, 08, 01, 1f, 05, 02, 10, 01, 06] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 2 -- Code(Counter(0)) at (prev + 21, 41) to (start + 1, 31) +- Code(Counter(0)) at (prev + 22, 8) to (start + 1, 31) - Code(Counter(1)) at (prev + 2, 16) to (start + 1, 6) diff --git a/tests/coverage/coroutine.coverage b/tests/coverage/coroutine.coverage index 68b52d198316..611470c57738 100644 --- a/tests/coverage/coroutine.coverage +++ b/tests/coverage/coroutine.coverage @@ -10,15 +10,16 @@ LL| |// to handle this condition, and still report dead block coverage. LL| 1|fn get_u32(val: bool) -> Result { LL| 1| if val { - LL| 1| Ok(1) + LL| 1| Ok(1) // LL| | } else { - LL| 0| Err(String::from("some error")) + LL| 0| Err(String::from("some error")) // LL| | } LL| 1|} LL| | LL| 1|fn main() { LL| 1| let is_true = std::env::args().len() == 1; - LL| 1| let mut coroutine = #[coroutine] || { + LL| 1| let mut coroutine = #[coroutine] + LL| 1| || { LL| 1| yield get_u32(is_true); LL| 1| return "foo"; LL| 1| }; diff --git a/tests/coverage/coroutine.rs b/tests/coverage/coroutine.rs index 7f72e0d8bd42..bd149764b373 100644 --- a/tests/coverage/coroutine.rs +++ b/tests/coverage/coroutine.rs @@ -10,15 +10,16 @@ use std::pin::Pin; // to handle this condition, and still report dead block coverage. fn get_u32(val: bool) -> Result { if val { - Ok(1) + Ok(1) // } else { - Err(String::from("some error")) + Err(String::from("some error")) // } } fn main() { let is_true = std::env::args().len() == 1; - let mut coroutine = #[coroutine] || { + let mut coroutine = #[coroutine] + || { yield get_u32(is_true); return "foo"; }; diff --git a/tests/coverage/dead_code.cov-map b/tests/coverage/dead_code.cov-map index 0b8a40a8cde0..31599d9072c9 100644 --- a/tests/coverage/dead_code.cov-map +++ b/tests/coverage/dead_code.cov-map @@ -1,17 +1,15 @@ Function name: dead_code::main -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 1b, 01, 07, 0f, 05, 07, 10, 02, 06, 02, 02, 06, 00, 07, 07, 01, 01, 00, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 1b, 01, 07, 0f, 05, 07, 10, 02, 06, 02, 02, 06, 00, 07, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 27, 1) to (start + 7, 15) - Code(Counter(1)) at (prev + 7, 16) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) Function name: dead_code::unused_fn (unused) Raw bytes (24): 0x[01, 01, 00, 04, 00, 0f, 01, 07, 0f, 00, 07, 10, 02, 06, 00, 02, 06, 00, 07, 00, 01, 01, 00, 02] diff --git a/tests/coverage/drop_trait.cov-map b/tests/coverage/drop_trait.cov-map index 203d1048b054..eb9d7d7acfdf 100644 --- a/tests/coverage/drop_trait.cov-map +++ b/tests/coverage/drop_trait.cov-map @@ -7,15 +7,13 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 9, 5) to (start + 2, 6) Function name: drop_trait::main -Raw bytes (26): 0x[01, 01, 01, 05, 00, 04, 01, 0e, 01, 05, 0c, 05, 06, 09, 01, 16, 00, 02, 06, 04, 0b, 03, 05, 01, 00, 02] +Raw bytes (24): 0x[01, 01, 00, 04, 01, 0e, 01, 05, 0c, 05, 06, 09, 01, 16, 00, 02, 06, 04, 0b, 01, 05, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 1 -- expression 0 operands: lhs = Counter(1), rhs = Zero +Number of expressions: 0 Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 14, 1) to (start + 5, 12) - Code(Counter(1)) at (prev + 6, 9) to (start + 1, 22) - Code(Zero) at (prev + 2, 6) to (start + 4, 11) -- Code(Expression(0, Add)) at (prev + 5, 1) to (start + 0, 2) - = (c1 + Zero) +- Code(Counter(0)) at (prev + 5, 1) to (start + 0, 2) diff --git a/tests/coverage/fn_sig_into_try.cov-map b/tests/coverage/fn_sig_into_try.cov-map index c3969f8ce999..49758954831a 100644 --- a/tests/coverage/fn_sig_into_try.cov-map +++ b/tests/coverage/fn_sig_into_try.cov-map @@ -7,47 +7,41 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 10, 1) to (start + 5, 2) Function name: fn_sig_into_try::b -Raw bytes (28): 0x[01, 01, 02, 01, 00, 00, 02, 04, 01, 11, 01, 03, 0f, 00, 03, 0f, 00, 10, 02, 01, 05, 00, 0c, 07, 01, 01, 00, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 00, 04, 01, 11, 01, 03, 0f, 00, 03, 0f, 00, 10, 02, 01, 05, 00, 0c, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Zero -- expression 1 operands: lhs = Zero, rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 17, 1) to (start + 3, 15) - Code(Zero) at (prev + 3, 15) to (start + 0, 16) - Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 12) = (c0 - Zero) -- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) - = (Zero + (c0 - Zero)) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) Function name: fn_sig_into_try::c -Raw bytes (28): 0x[01, 01, 02, 01, 00, 00, 02, 04, 01, 18, 01, 03, 17, 00, 03, 17, 00, 18, 02, 01, 05, 00, 0c, 07, 01, 01, 00, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 00, 04, 01, 18, 01, 03, 17, 00, 03, 17, 00, 18, 02, 01, 05, 00, 0c, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Zero -- expression 1 operands: lhs = Zero, rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 24, 1) to (start + 3, 23) - Code(Zero) at (prev + 3, 23) to (start + 0, 24) - Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 12) = (c0 - Zero) -- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) - = (Zero + (c0 - Zero)) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) Function name: fn_sig_into_try::d -Raw bytes (28): 0x[01, 01, 02, 01, 00, 00, 02, 04, 01, 1f, 01, 04, 0f, 00, 04, 0f, 00, 10, 02, 01, 05, 00, 0c, 07, 01, 01, 00, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 00, 04, 01, 1f, 01, 04, 0f, 00, 04, 0f, 00, 10, 02, 01, 05, 00, 0c, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Zero -- expression 1 operands: lhs = Zero, rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 31, 1) to (start + 4, 15) - Code(Zero) at (prev + 4, 15) to (start + 0, 16) - Code(Expression(0, Sub)) at (prev + 1, 5) to (start + 0, 12) = (c0 - Zero) -- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) - = (Zero + (c0 - Zero)) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) diff --git a/tests/coverage/generics.cov-map b/tests/coverage/generics.cov-map index 6079a433cd04..48e191c1156f 100644 --- a/tests/coverage/generics.cov-map +++ b/tests/coverage/generics.cov-map @@ -31,15 +31,13 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 10, 5) to (start + 2, 6) Function name: generics::main -Raw bytes (26): 0x[01, 01, 01, 05, 00, 04, 01, 16, 01, 08, 0c, 05, 09, 09, 01, 16, 00, 02, 06, 04, 0b, 03, 05, 01, 00, 02] +Raw bytes (24): 0x[01, 01, 00, 04, 01, 16, 01, 08, 0c, 05, 09, 09, 01, 16, 00, 02, 06, 04, 0b, 01, 05, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 1 -- expression 0 operands: lhs = Counter(1), rhs = Zero +Number of expressions: 0 Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 22, 1) to (start + 8, 12) - Code(Counter(1)) at (prev + 9, 9) to (start + 1, 22) - Code(Zero) at (prev + 2, 6) to (start + 4, 11) -- Code(Expression(0, Add)) at (prev + 5, 1) to (start + 0, 2) - = (c1 + Zero) +- Code(Counter(0)) at (prev + 5, 1) to (start + 0, 2) diff --git a/tests/coverage/if.cov-map b/tests/coverage/if.cov-map index d7122f4b1a03..2b5ae97b406d 100644 --- a/tests/coverage/if.cov-map +++ b/tests/coverage/if.cov-map @@ -1,15 +1,13 @@ Function name: if::main -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 04, 01, 12, 10, 05, 13, 05, 05, 06, 02, 05, 06, 00, 07, 07, 01, 01, 00, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 04, 01, 12, 10, 05, 13, 05, 05, 06, 02, 05, 06, 00, 07, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 4, 1) to (start + 18, 16) - Code(Counter(1)) at (prev + 19, 5) to (start + 5, 6) - Code(Expression(0, Sub)) at (prev + 5, 6) to (start + 0, 7) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) diff --git a/tests/coverage/if_else.cov-map b/tests/coverage/if_else.cov-map index 7163681d3a04..c2111917e559 100644 --- a/tests/coverage/if_else.cov-map +++ b/tests/coverage/if_else.cov-map @@ -1,25 +1,18 @@ Function name: if_else::main -Raw bytes (53): 0x[01, 01, 07, 01, 05, 05, 02, 1b, 09, 05, 02, 09, 16, 1b, 09, 05, 02, 07, 01, 04, 01, 08, 10, 05, 09, 05, 05, 06, 02, 08, 09, 02, 10, 1b, 06, 09, 00, 10, 09, 01, 05, 05, 06, 16, 07, 05, 05, 06, 13, 06, 01, 00, 02] +Raw bytes (43): 0x[01, 01, 02, 01, 05, 01, 09, 07, 01, 04, 01, 08, 10, 05, 09, 05, 05, 06, 02, 08, 09, 02, 10, 01, 06, 09, 00, 10, 09, 01, 05, 05, 06, 06, 07, 05, 05, 06, 01, 06, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 7 +Number of expressions: 2 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 2 operands: lhs = Expression(6, Add), rhs = Counter(2) -- expression 3 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 4 operands: lhs = Counter(2), rhs = Expression(5, Sub) -- expression 5 operands: lhs = Expression(6, Add), rhs = Counter(2) -- expression 6 operands: lhs = Counter(1), rhs = Expression(0, Sub) +- expression 1 operands: lhs = Counter(0), rhs = Counter(2) Number of file 0 mappings: 7 - Code(Counter(0)) at (prev + 4, 1) to (start + 8, 16) - Code(Counter(1)) at (prev + 9, 5) to (start + 5, 6) - Code(Expression(0, Sub)) at (prev + 8, 9) to (start + 2, 16) = (c0 - c1) -- Code(Expression(6, Add)) at (prev + 6, 9) to (start + 0, 16) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 6, 9) to (start + 0, 16) - Code(Counter(2)) at (prev + 1, 5) to (start + 5, 6) -- Code(Expression(5, Sub)) at (prev + 7, 5) to (start + 5, 6) - = ((c1 + (c0 - c1)) - c2) -- Code(Expression(4, Add)) at (prev + 6, 1) to (start + 0, 2) - = (c2 + ((c1 + (c0 - c1)) - c2)) +- Code(Expression(1, Sub)) at (prev + 7, 5) to (start + 5, 6) + = (c0 - c2) +- Code(Counter(0)) at (prev + 6, 1) to (start + 0, 2) diff --git a/tests/coverage/if_not.cov-map b/tests/coverage/if_not.cov-map index 3c660551dea1..9e11ed787a88 100644 --- a/tests/coverage/if_not.cov-map +++ b/tests/coverage/if_not.cov-map @@ -1,39 +1,23 @@ Function name: if_not::if_not -Raw bytes (86): 0x[01, 01, 10, 01, 05, 05, 02, 3f, 09, 05, 02, 09, 3a, 3f, 09, 05, 02, 37, 0d, 09, 3a, 3f, 09, 05, 02, 0d, 32, 37, 0d, 09, 3a, 3f, 09, 05, 02, 0a, 01, 05, 01, 03, 0d, 02, 04, 05, 02, 06, 05, 02, 06, 00, 07, 3f, 03, 09, 01, 0d, 3a, 02, 05, 02, 06, 09, 02, 06, 00, 07, 37, 03, 09, 01, 0d, 32, 02, 05, 02, 06, 0d, 02, 0c, 02, 06, 2f, 03, 01, 00, 02] +Raw bytes (60): 0x[01, 01, 03, 01, 05, 01, 09, 01, 0d, 0a, 01, 05, 01, 03, 0d, 02, 04, 05, 02, 06, 05, 02, 06, 00, 07, 01, 03, 09, 01, 0d, 06, 02, 05, 02, 06, 09, 02, 06, 00, 07, 01, 03, 09, 01, 0d, 0a, 02, 05, 02, 06, 0d, 02, 0c, 02, 06, 01, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 16 +Number of expressions: 3 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 2 operands: lhs = Expression(15, Add), rhs = Counter(2) -- expression 3 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 4 operands: lhs = Counter(2), rhs = Expression(14, Sub) -- expression 5 operands: lhs = Expression(15, Add), rhs = Counter(2) -- expression 6 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 7 operands: lhs = Expression(13, Add), rhs = Counter(3) -- expression 8 operands: lhs = Counter(2), rhs = Expression(14, Sub) -- expression 9 operands: lhs = Expression(15, Add), rhs = Counter(2) -- expression 10 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 11 operands: lhs = Counter(3), rhs = Expression(12, Sub) -- expression 12 operands: lhs = Expression(13, Add), rhs = Counter(3) -- expression 13 operands: lhs = Counter(2), rhs = Expression(14, Sub) -- expression 14 operands: lhs = Expression(15, Add), rhs = Counter(2) -- expression 15 operands: lhs = Counter(1), rhs = Expression(0, Sub) +- expression 1 operands: lhs = Counter(0), rhs = Counter(2) +- expression 2 operands: lhs = Counter(0), rhs = Counter(3) Number of file 0 mappings: 10 - Code(Counter(0)) at (prev + 5, 1) to (start + 3, 13) - Code(Expression(0, Sub)) at (prev + 4, 5) to (start + 2, 6) = (c0 - c1) - Code(Counter(1)) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(15, Add)) at (prev + 3, 9) to (start + 1, 13) - = (c1 + (c0 - c1)) -- Code(Expression(14, Sub)) at (prev + 2, 5) to (start + 2, 6) - = ((c1 + (c0 - c1)) - c2) +- Code(Counter(0)) at (prev + 3, 9) to (start + 1, 13) +- Code(Expression(1, Sub)) at (prev + 2, 5) to (start + 2, 6) + = (c0 - c2) - Code(Counter(2)) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(13, Add)) at (prev + 3, 9) to (start + 1, 13) - = (c2 + ((c1 + (c0 - c1)) - c2)) -- Code(Expression(12, Sub)) at (prev + 2, 5) to (start + 2, 6) - = ((c2 + ((c1 + (c0 - c1)) - c2)) - c3) +- Code(Counter(0)) at (prev + 3, 9) to (start + 1, 13) +- Code(Expression(2, Sub)) at (prev + 2, 5) to (start + 2, 6) + = (c0 - c3) - Code(Counter(3)) at (prev + 2, 12) to (start + 2, 6) -- Code(Expression(11, Add)) at (prev + 3, 1) to (start + 0, 2) - = (c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) +- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) diff --git a/tests/coverage/inline-dead.cov-map b/tests/coverage/inline-dead.cov-map index f77781ca0283..96b907cbe153 100644 --- a/tests/coverage/inline-dead.cov-map +++ b/tests/coverage/inline-dead.cov-map @@ -7,19 +7,17 @@ Number of file 0 mappings: 1 - Code(Zero) at (prev + 23, 1) to (start + 2, 2) Function name: inline_dead::live:: -Raw bytes (28): 0x[01, 01, 02, 01, 00, 00, 02, 04, 01, 0e, 01, 01, 09, 00, 02, 09, 00, 0f, 02, 02, 09, 00, 0a, 07, 02, 01, 00, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 00, 04, 01, 0e, 01, 01, 09, 00, 02, 09, 00, 0f, 02, 02, 09, 00, 0a, 01, 02, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Zero -- expression 1 operands: lhs = Zero, rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 14, 1) to (start + 1, 9) - Code(Zero) at (prev + 2, 9) to (start + 0, 15) - Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 10) = (c0 - Zero) -- Code(Expression(1, Add)) at (prev + 2, 1) to (start + 0, 2) - = (Zero + (c0 - Zero)) +- Code(Counter(0)) at (prev + 2, 1) to (start + 0, 2) Function name: inline_dead::main Raw bytes (14): 0x[01, 01, 00, 02, 01, 04, 01, 03, 0a, 01, 06, 05, 01, 02] diff --git a/tests/coverage/inline-dead.coverage b/tests/coverage/inline-dead.coverage index a2d24fd2f6a6..c12668ce89a2 100644 --- a/tests/coverage/inline-dead.coverage +++ b/tests/coverage/inline-dead.coverage @@ -14,7 +14,7 @@ LL| |#[inline] LL| 1|fn live() -> u32 { LL| 1| if B { - LL| 0| dead() + LL| 0| dead() // LL| | } else { LL| 1| 0 LL| | } diff --git a/tests/coverage/inline-dead.rs b/tests/coverage/inline-dead.rs index 327e68c60bb7..4eb397a43fc1 100644 --- a/tests/coverage/inline-dead.rs +++ b/tests/coverage/inline-dead.rs @@ -13,7 +13,7 @@ fn main() { #[inline] fn live() -> u32 { if B { - dead() + dead() // } else { 0 } diff --git a/tests/coverage/inline.cov-map b/tests/coverage/inline.cov-map index 001c333ae6d9..2366a3bc5345 100644 --- a/tests/coverage/inline.cov-map +++ b/tests/coverage/inline.cov-map @@ -1,18 +1,16 @@ Function name: inline::display:: -Raw bytes (33): 0x[01, 01, 02, 01, 05, 03, 05, 05, 01, 29, 01, 00, 22, 05, 01, 09, 00, 0a, 03, 00, 0e, 00, 10, 05, 00, 11, 02, 06, 06, 03, 05, 01, 02] +Raw bytes (31): 0x[01, 01, 01, 01, 05, 05, 01, 29, 01, 00, 22, 05, 01, 09, 00, 0a, 03, 00, 0e, 00, 10, 05, 00, 11, 02, 06, 01, 03, 05, 01, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Expression(0, Add), rhs = Counter(1) Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 41, 1) to (start + 0, 34) - Code(Counter(1)) at (prev + 1, 9) to (start + 0, 10) - Code(Expression(0, Add)) at (prev + 0, 14) to (start + 0, 16) = (c0 + c1) - Code(Counter(1)) at (prev + 0, 17) to (start + 2, 6) -- Code(Expression(1, Sub)) at (prev + 3, 5) to (start + 1, 2) - = ((c0 + c1) - c1) +- Code(Counter(0)) at (prev + 3, 5) to (start + 1, 2) Function name: inline::error Raw bytes (9): 0x[01, 01, 00, 01, 01, 31, 01, 01, 14] diff --git a/tests/coverage/inner_items.cov-map b/tests/coverage/inner_items.cov-map index 3f39d74efbac..6ccc3f0bafc8 100644 --- a/tests/coverage/inner_items.cov-map +++ b/tests/coverage/inner_items.cov-map @@ -15,29 +15,22 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 40, 9) to (start + 3, 10) Function name: inner_items::main -Raw bytes (53): 0x[01, 01, 07, 01, 05, 05, 02, 1b, 09, 05, 02, 09, 16, 1b, 09, 05, 02, 07, 01, 03, 01, 07, 0f, 05, 07, 10, 02, 06, 02, 02, 06, 00, 07, 1b, 24, 08, 00, 0f, 09, 00, 10, 02, 06, 16, 02, 06, 00, 07, 13, 02, 09, 05, 02] +Raw bytes (43): 0x[01, 01, 02, 01, 05, 01, 09, 07, 01, 03, 01, 07, 0f, 05, 07, 10, 02, 06, 02, 02, 06, 00, 07, 01, 24, 08, 00, 0f, 09, 00, 10, 02, 06, 06, 02, 06, 00, 07, 01, 02, 09, 05, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 7 +Number of expressions: 2 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 2 operands: lhs = Expression(6, Add), rhs = Counter(2) -- expression 3 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 4 operands: lhs = Counter(2), rhs = Expression(5, Sub) -- expression 5 operands: lhs = Expression(6, Add), rhs = Counter(2) -- expression 6 operands: lhs = Counter(1), rhs = Expression(0, Sub) +- expression 1 operands: lhs = Counter(0), rhs = Counter(2) Number of file 0 mappings: 7 - Code(Counter(0)) at (prev + 3, 1) to (start + 7, 15) - Code(Counter(1)) at (prev + 7, 16) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7) = (c0 - c1) -- Code(Expression(6, Add)) at (prev + 36, 8) to (start + 0, 15) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 36, 8) to (start + 0, 15) - Code(Counter(2)) at (prev + 0, 16) to (start + 2, 6) -- Code(Expression(5, Sub)) at (prev + 2, 6) to (start + 0, 7) - = ((c1 + (c0 - c1)) - c2) -- Code(Expression(4, Add)) at (prev + 2, 9) to (start + 5, 2) - = (c2 + ((c1 + (c0 - c1)) - c2)) +- Code(Expression(1, Sub)) at (prev + 2, 6) to (start + 0, 7) + = (c0 - c2) +- Code(Counter(0)) at (prev + 2, 9) to (start + 5, 2) Function name: inner_items::main::in_func Raw bytes (9): 0x[01, 01, 00, 01, 01, 12, 05, 04, 06] diff --git a/tests/coverage/inner_items.coverage b/tests/coverage/inner_items.coverage index 65493bcd9db4..152f3da1a221 100644 --- a/tests/coverage/inner_items.coverage +++ b/tests/coverage/inner_items.coverage @@ -52,7 +52,7 @@ ^0 LL| | LL| 1| let mut val = InStruct { - LL| 1| in_struct_field: 101, + LL| 1| in_struct_field: 101, // LL| 1| }; LL| 1| LL| 1| val.default_trait_func(); diff --git a/tests/coverage/inner_items.rs b/tests/coverage/inner_items.rs index bcb62b3031cd..4d513e612f06 100644 --- a/tests/coverage/inner_items.rs +++ b/tests/coverage/inner_items.rs @@ -50,7 +50,7 @@ fn main() { } let mut val = InStruct { - in_struct_field: 101, + in_struct_field: 101, // }; val.default_trait_func(); diff --git a/tests/coverage/issue-84561.cov-map b/tests/coverage/issue-84561.cov-map index ab66a2fffce1..c4087d9369d8 100644 --- a/tests/coverage/issue-84561.cov-map +++ b/tests/coverage/issue-84561.cov-map @@ -1,17 +1,15 @@ Function name: ::fmt -Raw bytes (29): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 8a, 01, 05, 01, 25, 05, 01, 25, 00, 26, 02, 01, 09, 00, 0f, 07, 01, 05, 00, 06] +Raw bytes (27): 0x[01, 01, 01, 01, 05, 04, 01, 8a, 01, 05, 01, 25, 05, 01, 25, 00, 26, 02, 01, 09, 00, 0f, 01, 01, 05, 00, 06] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 138, 5) to (start + 1, 37) - Code(Counter(1)) at (prev + 1, 37) to (start + 0, 38) - Code(Expression(0, Sub)) at (prev + 1, 9) to (start + 0, 15) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 5) to (start + 0, 6) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 6) Function name: issue_84561::main Raw bytes (10): 0x[01, 01, 00, 01, 01, b4, 01, 01, 04, 02] @@ -22,51 +20,30 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 180, 1) to (start + 4, 2) Function name: issue_84561::test1 -Raw bytes (78): 0x[01, 01, 0e, 05, 06, 01, 05, 09, 36, 03, 09, 0d, 2e, 33, 0d, 09, 36, 03, 09, 11, 26, 2b, 11, 0d, 2e, 33, 0d, 09, 36, 03, 09, 09, 01, 9a, 01, 01, 01, 0b, 05, 01, 0c, 00, 1e, 03, 01, 05, 00, 0b, 09, 00, 0c, 00, 1e, 33, 01, 0d, 01, 0b, 0d, 01, 0c, 00, 1e, 2b, 01, 05, 03, 0b, 11, 03, 0c, 00, 1e, 23, 01, 01, 00, 02] +Raw bytes (50): 0x[01, 01, 00, 09, 01, 9a, 01, 01, 01, 0b, 05, 01, 0c, 00, 1e, 01, 01, 05, 00, 0b, 09, 00, 0c, 00, 1e, 01, 01, 0d, 01, 0b, 0d, 01, 0c, 00, 1e, 01, 01, 05, 03, 0b, 11, 03, 0c, 00, 1e, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 14 -- expression 0 operands: lhs = Counter(1), rhs = Expression(1, Sub) -- expression 1 operands: lhs = Counter(0), rhs = Counter(1) -- expression 2 operands: lhs = Counter(2), rhs = Expression(13, Sub) -- expression 3 operands: lhs = Expression(0, Add), rhs = Counter(2) -- expression 4 operands: lhs = Counter(3), rhs = Expression(11, Sub) -- expression 5 operands: lhs = Expression(12, Add), rhs = Counter(3) -- expression 6 operands: lhs = Counter(2), rhs = Expression(13, Sub) -- expression 7 operands: lhs = Expression(0, Add), rhs = Counter(2) -- expression 8 operands: lhs = Counter(4), rhs = Expression(9, Sub) -- expression 9 operands: lhs = Expression(10, Add), rhs = Counter(4) -- expression 10 operands: lhs = Counter(3), rhs = Expression(11, Sub) -- expression 11 operands: lhs = Expression(12, Add), rhs = Counter(3) -- expression 12 operands: lhs = Counter(2), rhs = Expression(13, Sub) -- expression 13 operands: lhs = Expression(0, Add), rhs = Counter(2) +Number of expressions: 0 Number of file 0 mappings: 9 - Code(Counter(0)) at (prev + 154, 1) to (start + 1, 11) - Code(Counter(1)) at (prev + 1, 12) to (start + 0, 30) -- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 0, 11) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 11) - Code(Counter(2)) at (prev + 0, 12) to (start + 0, 30) -- Code(Expression(12, Add)) at (prev + 1, 13) to (start + 1, 11) - = (c2 + ((c1 + (c0 - c1)) - c2)) +- Code(Counter(0)) at (prev + 1, 13) to (start + 1, 11) - Code(Counter(3)) at (prev + 1, 12) to (start + 0, 30) -- Code(Expression(10, Add)) at (prev + 1, 5) to (start + 3, 11) - = (c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) +- Code(Counter(0)) at (prev + 1, 5) to (start + 3, 11) - Code(Counter(4)) at (prev + 3, 12) to (start + 0, 30) -- Code(Expression(8, Add)) at (prev + 1, 1) to (start + 0, 2) - = (c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) Function name: issue_84561::test2 -Raw bytes (24): 0x[01, 01, 02, 05, 06, 01, 05, 03, 01, b0, 01, 01, 01, 10, 05, 01, 11, 00, 23, 03, 01, 01, 00, 02] +Raw bytes (20): 0x[01, 01, 00, 03, 01, b0, 01, 01, 01, 10, 05, 01, 11, 00, 23, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 -- expression 0 operands: lhs = Counter(1), rhs = Expression(1, Sub) -- expression 1 operands: lhs = Counter(0), rhs = Counter(1) +Number of expressions: 0 Number of file 0 mappings: 3 - Code(Counter(0)) at (prev + 176, 1) to (start + 1, 16) - Code(Counter(1)) at (prev + 1, 17) to (start + 0, 35) -- Code(Expression(0, Add)) at (prev + 1, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) Function name: issue_84561::test2::call_print Raw bytes (10): 0x[01, 01, 00, 01, 01, a7, 01, 09, 02, 0a] @@ -77,10 +54,10 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 167, 9) to (start + 2, 10) Function name: issue_84561::test3 -Raw bytes (436): 0x[01, 01, 41, 05, 00, 0d, 00, 15, 00, 12, 00, 15, 00, 21, 00, 1e, 00, 21, 00, 31, 00, 3d, 00, 2e, 45, 3d, 00, 42, 49, 45, 00, 3f, 51, 42, 49, 45, 00, 5d, 8a, 01, 8f, 01, 5d, 92, 01, 55, 51, 00, 92, 01, 55, 51, 00, 8f, 01, 5d, 92, 01, 55, 51, 00, 87, 01, 61, 5d, 8a, 01, 8f, 01, 5d, 92, 01, 55, 51, 00, 82, 01, 65, 87, 01, 61, 5d, 8a, 01, 8f, 01, 5d, 92, 01, 55, 51, 00, 75, f6, 01, fb, 01, 79, 00, fe, 01, 82, 02, 00, 69, 6d, 00, fe, 01, 82, 02, 00, 69, 6d, 69, 6d, 82, 02, 00, 69, 6d, fb, 01, 79, 00, fe, 01, 82, 02, 00, 69, 6d, f3, 01, 7d, 75, f6, 01, fb, 01, 79, 00, fe, 01, 82, 02, 00, 69, 6d, ee, 01, 00, f3, 01, 7d, 75, f6, 01, fb, 01, 79, 00, fe, 01, 82, 02, 00, 69, 6d, 33, 01, 08, 01, 03, 1c, 05, 04, 09, 01, 1c, 02, 02, 05, 04, 1f, 0d, 05, 05, 00, 1f, 06, 01, 05, 00, 1f, 15, 01, 09, 01, 1c, 12, 02, 05, 00, 1f, 0e, 01, 05, 00, 0f, 00, 00, 20, 00, 30, 21, 01, 05, 03, 0f, 00, 03, 20, 00, 30, 00, 00, 33, 00, 41, 00, 00, 4b, 00, 5a, 1e, 01, 05, 00, 0f, 00, 05, 09, 03, 10, 00, 05, 0d, 00, 1b, 00, 02, 0d, 00, 1c, 1a, 04, 09, 05, 06, 31, 06, 05, 03, 06, 22, 04, 05, 03, 06, 3d, 04, 09, 04, 06, 2e, 05, 08, 00, 0f, 45, 01, 09, 03, 0a, 2a, 05, 09, 03, 0a, 3f, 05, 08, 00, 0f, 51, 01, 09, 00, 13, 00, 03, 0d, 00, 1d, 3a, 03, 09, 00, 13, 00, 03, 0d, 00, 1d, 87, 01, 03, 05, 00, 0f, 8f, 01, 01, 0c, 00, 13, 5d, 01, 0d, 00, 13, 8a, 01, 02, 0d, 00, 13, 82, 01, 04, 05, 02, 13, 65, 03, 0d, 00, 13, 7e, 02, 0d, 00, 13, f3, 01, 03, 05, 00, 0f, 69, 01, 0c, 00, 13, 6d, 01, 0d, 03, 0e, 75, 04, 0d, 00, 13, fb, 01, 02, 0d, 00, 17, 82, 02, 01, 14, 00, 1b, 00, 01, 15, 00, 1b, fe, 01, 02, 15, 00, 1b, f6, 01, 04, 0d, 00, 13, 7d, 03, 09, 00, 19, ee, 01, 02, 05, 00, 0f, ea, 01, 03, 09, 00, 22, 00, 02, 05, 00, 0f, 00, 03, 09, 00, 2c, 00, 02, 01, 00, 02] +Raw bytes (375): 0x[01, 01, 31, 05, 00, 0d, 00, 15, 00, 12, 00, 15, 00, 21, 00, 1e, 00, 21, 00, 31, 00, 3d, 00, 2e, 45, 3d, 00, 42, 49, 45, 00, 3f, 51, 42, 49, 45, 00, 7a, 55, 51, 00, 7a, 55, 51, 00, 77, 5d, 7a, 55, 51, 00, 77, 61, 7a, 55, 51, 00, 72, 65, 77, 61, 7a, 55, 51, 00, 75, be, 01, c2, 01, 79, 69, 6d, 69, 6d, 69, 6d, c2, 01, 00, 69, 6d, c2, 01, 79, 69, 6d, bb, 01, 7d, 75, be, 01, c2, 01, 79, 69, 6d, b6, 01, 00, bb, 01, 7d, 75, be, 01, c2, 01, 79, 69, 6d, 33, 01, 08, 01, 03, 1c, 05, 04, 09, 01, 1c, 02, 02, 05, 04, 1f, 0d, 05, 05, 00, 1f, 06, 01, 05, 00, 1f, 15, 01, 09, 01, 1c, 12, 02, 05, 00, 1f, 0e, 01, 05, 00, 0f, 00, 00, 20, 00, 30, 21, 01, 05, 03, 0f, 00, 03, 20, 00, 30, 00, 00, 33, 00, 41, 00, 00, 4b, 00, 5a, 1e, 01, 05, 00, 0f, 00, 05, 09, 03, 10, 00, 05, 0d, 00, 1b, 00, 02, 0d, 00, 1c, 1a, 04, 09, 05, 06, 31, 06, 05, 03, 06, 22, 04, 05, 03, 06, 3d, 04, 09, 04, 06, 2e, 05, 08, 00, 0f, 45, 01, 09, 03, 0a, 2a, 05, 09, 03, 0a, 3f, 05, 08, 00, 0f, 51, 01, 09, 00, 13, 00, 03, 0d, 00, 1d, 3a, 03, 09, 00, 13, 00, 03, 0d, 00, 1d, 77, 03, 05, 00, 0f, 77, 01, 0c, 00, 13, 5d, 01, 0d, 00, 13, 56, 02, 0d, 00, 13, 72, 04, 05, 02, 13, 65, 03, 0d, 00, 13, 6e, 02, 0d, 00, 13, bb, 01, 03, 05, 00, 0f, 69, 01, 0c, 00, 13, 6d, 01, 0d, 03, 0e, 75, 04, 0d, 00, 13, c2, 01, 02, 0d, 00, 17, c2, 01, 01, 14, 00, 1b, 00, 01, 15, 00, 1b, 92, 01, 02, 15, 00, 1b, be, 01, 04, 0d, 00, 13, 7d, 03, 09, 00, 19, b6, 01, 02, 05, 00, 0f, b2, 01, 03, 09, 00, 22, 00, 02, 05, 00, 0f, 00, 03, 09, 00, 2c, 00, 02, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 65 +Number of expressions: 49 - expression 0 operands: lhs = Counter(1), rhs = Zero - expression 1 operands: lhs = Counter(3), rhs = Zero - expression 2 operands: lhs = Counter(5), rhs = Zero @@ -98,54 +75,38 @@ Number of expressions: 65 - expression 14 operands: lhs = Expression(15, Add), rhs = Counter(20) - expression 15 operands: lhs = Expression(16, Sub), rhs = Counter(18) - expression 16 operands: lhs = Counter(17), rhs = Zero -- expression 17 operands: lhs = Counter(23), rhs = Expression(34, Sub) -- expression 18 operands: lhs = Expression(35, Add), rhs = Counter(23) -- expression 19 operands: lhs = Expression(36, Sub), rhs = Counter(21) +- expression 17 operands: lhs = Expression(30, Sub), rhs = Counter(21) +- expression 18 operands: lhs = Counter(20), rhs = Zero +- expression 19 operands: lhs = Expression(30, Sub), rhs = Counter(21) - expression 20 operands: lhs = Counter(20), rhs = Zero -- expression 21 operands: lhs = Expression(36, Sub), rhs = Counter(21) -- expression 22 operands: lhs = Counter(20), rhs = Zero -- expression 23 operands: lhs = Expression(35, Add), rhs = Counter(23) -- expression 24 operands: lhs = Expression(36, Sub), rhs = Counter(21) -- expression 25 operands: lhs = Counter(20), rhs = Zero -- expression 26 operands: lhs = Expression(33, Add), rhs = Counter(24) -- expression 27 operands: lhs = Counter(23), rhs = Expression(34, Sub) -- expression 28 operands: lhs = Expression(35, Add), rhs = Counter(23) -- expression 29 operands: lhs = Expression(36, Sub), rhs = Counter(21) +- expression 21 operands: lhs = Expression(29, Add), rhs = Counter(23) +- expression 22 operands: lhs = Expression(30, Sub), rhs = Counter(21) +- expression 23 operands: lhs = Counter(20), rhs = Zero +- expression 24 operands: lhs = Expression(29, Add), rhs = Counter(24) +- expression 25 operands: lhs = Expression(30, Sub), rhs = Counter(21) +- expression 26 operands: lhs = Counter(20), rhs = Zero +- expression 27 operands: lhs = Expression(28, Sub), rhs = Counter(25) +- expression 28 operands: lhs = Expression(29, Add), rhs = Counter(24) +- expression 29 operands: lhs = Expression(30, Sub), rhs = Counter(21) - expression 30 operands: lhs = Counter(20), rhs = Zero -- expression 31 operands: lhs = Expression(32, Sub), rhs = Counter(25) -- expression 32 operands: lhs = Expression(33, Add), rhs = Counter(24) -- expression 33 operands: lhs = Counter(23), rhs = Expression(34, Sub) -- expression 34 operands: lhs = Expression(35, Add), rhs = Counter(23) -- expression 35 operands: lhs = Expression(36, Sub), rhs = Counter(21) -- expression 36 operands: lhs = Counter(20), rhs = Zero -- expression 37 operands: lhs = Counter(29), rhs = Expression(61, Sub) -- expression 38 operands: lhs = Expression(62, Add), rhs = Counter(30) -- expression 39 operands: lhs = Zero, rhs = Expression(63, Sub) -- expression 40 operands: lhs = Expression(64, Sub), rhs = Zero -- expression 41 operands: lhs = Counter(26), rhs = Counter(27) -- expression 42 operands: lhs = Zero, rhs = Expression(63, Sub) -- expression 43 operands: lhs = Expression(64, Sub), rhs = Zero -- expression 44 operands: lhs = Counter(26), rhs = Counter(27) -- expression 45 operands: lhs = Counter(26), rhs = Counter(27) -- expression 46 operands: lhs = Expression(64, Sub), rhs = Zero -- expression 47 operands: lhs = Counter(26), rhs = Counter(27) -- expression 48 operands: lhs = Expression(62, Add), rhs = Counter(30) -- expression 49 operands: lhs = Zero, rhs = Expression(63, Sub) -- expression 50 operands: lhs = Expression(64, Sub), rhs = Zero -- expression 51 operands: lhs = Counter(26), rhs = Counter(27) -- expression 52 operands: lhs = Expression(60, Add), rhs = Counter(31) -- expression 53 operands: lhs = Counter(29), rhs = Expression(61, Sub) -- expression 54 operands: lhs = Expression(62, Add), rhs = Counter(30) -- expression 55 operands: lhs = Zero, rhs = Expression(63, Sub) -- expression 56 operands: lhs = Expression(64, Sub), rhs = Zero -- expression 57 operands: lhs = Counter(26), rhs = Counter(27) -- expression 58 operands: lhs = Expression(59, Sub), rhs = Zero -- expression 59 operands: lhs = Expression(60, Add), rhs = Counter(31) -- expression 60 operands: lhs = Counter(29), rhs = Expression(61, Sub) -- expression 61 operands: lhs = Expression(62, Add), rhs = Counter(30) -- expression 62 operands: lhs = Zero, rhs = Expression(63, Sub) -- expression 63 operands: lhs = Expression(64, Sub), rhs = Zero -- expression 64 operands: lhs = Counter(26), rhs = Counter(27) +- expression 31 operands: lhs = Counter(29), rhs = Expression(47, Sub) +- expression 32 operands: lhs = Expression(48, Sub), rhs = Counter(30) +- expression 33 operands: lhs = Counter(26), rhs = Counter(27) +- expression 34 operands: lhs = Counter(26), rhs = Counter(27) +- expression 35 operands: lhs = Counter(26), rhs = Counter(27) +- expression 36 operands: lhs = Expression(48, Sub), rhs = Zero +- expression 37 operands: lhs = Counter(26), rhs = Counter(27) +- expression 38 operands: lhs = Expression(48, Sub), rhs = Counter(30) +- expression 39 operands: lhs = Counter(26), rhs = Counter(27) +- expression 40 operands: lhs = Expression(46, Add), rhs = Counter(31) +- expression 41 operands: lhs = Counter(29), rhs = Expression(47, Sub) +- expression 42 operands: lhs = Expression(48, Sub), rhs = Counter(30) +- expression 43 operands: lhs = Counter(26), rhs = Counter(27) +- expression 44 operands: lhs = Expression(45, Sub), rhs = Zero +- expression 45 operands: lhs = Expression(46, Add), rhs = Counter(31) +- expression 46 operands: lhs = Counter(29), rhs = Expression(47, Sub) +- expression 47 operands: lhs = Expression(48, Sub), rhs = Counter(30) +- expression 48 operands: lhs = Counter(26), rhs = Counter(27) Number of file 0 mappings: 51 - Code(Counter(0)) at (prev + 8, 1) to (start + 3, 28) - Code(Counter(1)) at (prev + 4, 9) to (start + 1, 28) @@ -187,37 +148,37 @@ Number of file 0 mappings: 51 - Code(Expression(14, Sub)) at (prev + 3, 9) to (start + 0, 19) = (((c17 - Zero) + c18) - c20) - Code(Zero) at (prev + 3, 13) to (start + 0, 29) -- Code(Expression(33, Add)) at (prev + 3, 5) to (start + 0, 15) - = (c23 + (((c20 - Zero) + c21) - c23)) -- Code(Expression(35, Add)) at (prev + 1, 12) to (start + 0, 19) +- Code(Expression(29, Add)) at (prev + 3, 5) to (start + 0, 15) + = ((c20 - Zero) + c21) +- Code(Expression(29, Add)) at (prev + 1, 12) to (start + 0, 19) = ((c20 - Zero) + c21) - Code(Counter(23)) at (prev + 1, 13) to (start + 0, 19) -- Code(Expression(34, Sub)) at (prev + 2, 13) to (start + 0, 19) +- Code(Expression(21, Sub)) at (prev + 2, 13) to (start + 0, 19) = (((c20 - Zero) + c21) - c23) -- Code(Expression(32, Sub)) at (prev + 4, 5) to (start + 2, 19) - = ((c23 + (((c20 - Zero) + c21) - c23)) - c24) +- Code(Expression(28, Sub)) at (prev + 4, 5) to (start + 2, 19) + = (((c20 - Zero) + c21) - c24) - Code(Counter(25)) at (prev + 3, 13) to (start + 0, 19) -- Code(Expression(31, Sub)) at (prev + 2, 13) to (start + 0, 19) - = (((c23 + (((c20 - Zero) + c21) - c23)) - c24) - c25) -- Code(Expression(60, Add)) at (prev + 3, 5) to (start + 0, 15) - = (c29 + ((Zero + ((c26 - c27) - Zero)) - c30)) +- Code(Expression(27, Sub)) at (prev + 2, 13) to (start + 0, 19) + = ((((c20 - Zero) + c21) - c24) - c25) +- Code(Expression(46, Add)) at (prev + 3, 5) to (start + 0, 15) + = (c29 + ((c26 - c27) - c30)) - Code(Counter(26)) at (prev + 1, 12) to (start + 0, 19) - Code(Counter(27)) at (prev + 1, 13) to (start + 3, 14) - Code(Counter(29)) at (prev + 4, 13) to (start + 0, 19) -- Code(Expression(62, Add)) at (prev + 2, 13) to (start + 0, 23) - = (Zero + ((c26 - c27) - Zero)) -- Code(Expression(64, Sub)) at (prev + 1, 20) to (start + 0, 27) +- Code(Expression(48, Sub)) at (prev + 2, 13) to (start + 0, 23) + = (c26 - c27) +- Code(Expression(48, Sub)) at (prev + 1, 20) to (start + 0, 27) = (c26 - c27) - Code(Zero) at (prev + 1, 21) to (start + 0, 27) -- Code(Expression(63, Sub)) at (prev + 2, 21) to (start + 0, 27) +- Code(Expression(36, Sub)) at (prev + 2, 21) to (start + 0, 27) = ((c26 - c27) - Zero) -- Code(Expression(61, Sub)) at (prev + 4, 13) to (start + 0, 19) - = ((Zero + ((c26 - c27) - Zero)) - c30) +- Code(Expression(47, Sub)) at (prev + 4, 13) to (start + 0, 19) + = ((c26 - c27) - c30) - Code(Counter(31)) at (prev + 3, 9) to (start + 0, 25) -- Code(Expression(59, Sub)) at (prev + 2, 5) to (start + 0, 15) - = ((c29 + ((Zero + ((c26 - c27) - Zero)) - c30)) - c31) -- Code(Expression(58, Sub)) at (prev + 3, 9) to (start + 0, 34) - = (((c29 + ((Zero + ((c26 - c27) - Zero)) - c30)) - c31) - Zero) +- Code(Expression(45, Sub)) at (prev + 2, 5) to (start + 0, 15) + = ((c29 + ((c26 - c27) - c30)) - c31) +- Code(Expression(44, Sub)) at (prev + 3, 9) to (start + 0, 34) + = (((c29 + ((c26 - c27) - c30)) - c31) - Zero) - Code(Zero) at (prev + 2, 5) to (start + 0, 15) - Code(Zero) at (prev + 3, 9) to (start + 0, 44) - Code(Zero) at (prev + 2, 1) to (start + 0, 2) diff --git a/tests/coverage/lazy_boolean.cov-map b/tests/coverage/lazy_boolean.cov-map index 03dbb59d26b5..8dca205d33f2 100644 --- a/tests/coverage/lazy_boolean.cov-map +++ b/tests/coverage/lazy_boolean.cov-map @@ -1,219 +1,49 @@ Function name: lazy_boolean::main -Raw bytes (636): 0x[01, 01, a4, 01, 01, 05, 09, 8a, 05, 8f, 05, 09, 05, 02, 05, 02, 8f, 05, 09, 05, 02, 0d, 82, 05, 87, 05, 0d, 09, 8a, 05, 8f, 05, 09, 05, 02, 09, 8a, 05, 8f, 05, 09, 05, 02, 87, 05, 0d, 09, 8a, 05, 8f, 05, 09, 05, 02, 11, fa, 04, ff, 04, 11, 0d, 82, 05, 87, 05, 0d, 09, 8a, 05, 8f, 05, 09, 05, 02, 0d, 82, 05, 87, 05, 0d, 09, 8a, 05, 8f, 05, 09, 05, 02, 15, f2, 04, f7, 04, 15, 11, fa, 04, ff, 04, 11, 0d, 82, 05, 87, 05, 0d, 09, 8a, 05, 8f, 05, 09, 05, 02, 11, fa, 04, ff, 04, 11, 0d, 82, 05, 87, 05, 0d, 09, 8a, 05, 8f, 05, 09, 05, 02, 15, f2, 04, f7, 04, 15, 11, fa, 04, ff, 04, 11, 0d, 82, 05, 87, 05, 0d, 09, 8a, 05, 8f, 05, 09, 05, 02, ef, 04, 19, 15, f2, 04, f7, 04, 15, 11, fa, 04, ff, 04, 11, 0d, 82, 05, 87, 05, 0d, 09, 8a, 05, 8f, 05, 09, 05, 02, 19, ea, 04, ef, 04, 19, 15, f2, 04, f7, 04, 15, 11, fa, 04, ff, 04, 11, 0d, 82, 05, 87, 05, 0d, 09, 8a, 05, 8f, 05, 09, 05, 02, e7, 04, 1d, 19, ea, 04, ef, 04, 19, 15, f2, 04, f7, 04, 15, 11, fa, 04, ff, 04, 11, 0d, 82, 05, 87, 05, 0d, 09, 8a, 05, 8f, 05, 09, 05, 02, 1d, e2, 04, e7, 04, 1d, 19, ea, 04, ef, 04, 19, 15, f2, 04, f7, 04, 15, 11, fa, 04, ff, 04, 11, 0d, 82, 05, 87, 05, 0d, 09, 8a, 05, 8f, 05, 09, 05, 02, df, 04, 21, 1d, e2, 04, e7, 04, 1d, 19, ea, 04, ef, 04, 19, 15, f2, 04, f7, 04, 15, 11, fa, 04, ff, 04, 11, 0d, 82, 05, 87, 05, 0d, 09, 8a, 05, 8f, 05, 09, 05, 02, 21, da, 04, df, 04, 21, 1d, e2, 04, e7, 04, 1d, 19, ea, 04, ef, 04, 19, 15, f2, 04, f7, 04, 15, 11, fa, 04, ff, 04, 11, 0d, 82, 05, 87, 05, 0d, 09, 8a, 05, 8f, 05, 09, 05, 02, d7, 04, 25, 21, da, 04, df, 04, 21, 1d, e2, 04, e7, 04, 1d, 19, ea, 04, ef, 04, 19, 15, f2, 04, f7, 04, 15, 11, fa, 04, ff, 04, 11, 0d, 82, 05, 87, 05, 0d, 09, 8a, 05, 8f, 05, 09, 05, 02, 25, d2, 04, d7, 04, 25, 21, da, 04, df, 04, 21, 1d, e2, 04, e7, 04, 1d, 19, ea, 04, ef, 04, 19, 15, f2, 04, f7, 04, 15, 11, fa, 04, ff, 04, 11, 0d, 82, 05, 87, 05, 0d, 09, 8a, 05, 8f, 05, 09, 05, 02, 1c, 01, 04, 01, 07, 0f, 05, 07, 10, 04, 06, 02, 04, 06, 00, 07, 87, 05, 02, 09, 00, 11, 8f, 05, 02, 0d, 00, 12, 8a, 05, 02, 0d, 00, 12, ff, 04, 03, 09, 00, 11, 87, 05, 02, 0d, 00, 12, 82, 05, 02, 0d, 00, 12, f7, 04, 02, 09, 00, 11, ff, 04, 00, 14, 00, 19, 11, 00, 1d, 00, 22, ef, 04, 01, 09, 00, 11, f7, 04, 00, 14, 00, 19, 15, 00, 1d, 00, 22, ef, 04, 03, 09, 01, 10, ea, 04, 02, 05, 03, 06, 19, 03, 06, 00, 07, e7, 04, 03, 09, 00, 10, 1d, 01, 05, 03, 06, e2, 04, 05, 05, 03, 06, df, 04, 05, 08, 00, 10, da, 04, 00, 11, 02, 06, 21, 02, 06, 00, 07, d7, 04, 02, 08, 00, 0f, 25, 00, 10, 02, 06, d2, 04, 02, 0c, 02, 06, cf, 04, 03, 01, 00, 02] +Raw bytes (158): 0x[01, 01, 07, 01, 05, 01, 09, 01, 0d, 01, 19, 01, 1d, 01, 21, 01, 25, 1c, 01, 04, 01, 07, 0f, 05, 07, 10, 04, 06, 02, 04, 06, 00, 07, 01, 02, 09, 00, 11, 01, 02, 0d, 00, 12, 06, 02, 0d, 00, 12, 01, 03, 09, 00, 11, 01, 02, 0d, 00, 12, 0a, 02, 0d, 00, 12, 01, 02, 09, 00, 11, 01, 00, 14, 00, 19, 11, 00, 1d, 00, 22, 01, 01, 09, 00, 11, 01, 00, 14, 00, 19, 15, 00, 1d, 00, 22, 01, 03, 09, 01, 10, 0e, 02, 05, 03, 06, 19, 03, 06, 00, 07, 01, 03, 09, 00, 10, 1d, 01, 05, 03, 06, 12, 05, 05, 03, 06, 01, 05, 08, 00, 10, 16, 00, 11, 02, 06, 21, 02, 06, 00, 07, 01, 02, 08, 00, 0f, 25, 00, 10, 02, 06, 1a, 02, 0c, 02, 06, 01, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 164 +Number of expressions: 7 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 2 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 3 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 4 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 5 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 6 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 7 operands: lhs = Counter(3), rhs = Expression(160, Sub) -- expression 8 operands: lhs = Expression(161, Add), rhs = Counter(3) -- expression 9 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 10 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 11 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 12 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 13 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 14 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 15 operands: lhs = Expression(161, Add), rhs = Counter(3) -- expression 16 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 17 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 18 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 19 operands: lhs = Counter(4), rhs = Expression(158, Sub) -- expression 20 operands: lhs = Expression(159, Add), rhs = Counter(4) -- expression 21 operands: lhs = Counter(3), rhs = Expression(160, Sub) -- expression 22 operands: lhs = Expression(161, Add), rhs = Counter(3) -- expression 23 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 24 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 25 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 26 operands: lhs = Counter(3), rhs = Expression(160, Sub) -- expression 27 operands: lhs = Expression(161, Add), rhs = Counter(3) -- expression 28 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 29 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 30 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 31 operands: lhs = Counter(5), rhs = Expression(156, Sub) -- expression 32 operands: lhs = Expression(157, Add), rhs = Counter(5) -- expression 33 operands: lhs = Counter(4), rhs = Expression(158, Sub) -- expression 34 operands: lhs = Expression(159, Add), rhs = Counter(4) -- expression 35 operands: lhs = Counter(3), rhs = Expression(160, Sub) -- expression 36 operands: lhs = Expression(161, Add), rhs = Counter(3) -- expression 37 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 38 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 39 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 40 operands: lhs = Counter(4), rhs = Expression(158, Sub) -- expression 41 operands: lhs = Expression(159, Add), rhs = Counter(4) -- expression 42 operands: lhs = Counter(3), rhs = Expression(160, Sub) -- expression 43 operands: lhs = Expression(161, Add), rhs = Counter(3) -- expression 44 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 45 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 46 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 47 operands: lhs = Counter(5), rhs = Expression(156, Sub) -- expression 48 operands: lhs = Expression(157, Add), rhs = Counter(5) -- expression 49 operands: lhs = Counter(4), rhs = Expression(158, Sub) -- expression 50 operands: lhs = Expression(159, Add), rhs = Counter(4) -- expression 51 operands: lhs = Counter(3), rhs = Expression(160, Sub) -- expression 52 operands: lhs = Expression(161, Add), rhs = Counter(3) -- expression 53 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 54 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 55 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 56 operands: lhs = Expression(155, Add), rhs = Counter(6) -- expression 57 operands: lhs = Counter(5), rhs = Expression(156, Sub) -- expression 58 operands: lhs = Expression(157, Add), rhs = Counter(5) -- expression 59 operands: lhs = Counter(4), rhs = Expression(158, Sub) -- expression 60 operands: lhs = Expression(159, Add), rhs = Counter(4) -- expression 61 operands: lhs = Counter(3), rhs = Expression(160, Sub) -- expression 62 operands: lhs = Expression(161, Add), rhs = Counter(3) -- expression 63 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 64 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 65 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 66 operands: lhs = Counter(6), rhs = Expression(154, Sub) -- expression 67 operands: lhs = Expression(155, Add), rhs = Counter(6) -- expression 68 operands: lhs = Counter(5), rhs = Expression(156, Sub) -- expression 69 operands: lhs = Expression(157, Add), rhs = Counter(5) -- expression 70 operands: lhs = Counter(4), rhs = Expression(158, Sub) -- expression 71 operands: lhs = Expression(159, Add), rhs = Counter(4) -- expression 72 operands: lhs = Counter(3), rhs = Expression(160, Sub) -- expression 73 operands: lhs = Expression(161, Add), rhs = Counter(3) -- expression 74 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 75 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 76 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 77 operands: lhs = Expression(153, Add), rhs = Counter(7) -- expression 78 operands: lhs = Counter(6), rhs = Expression(154, Sub) -- expression 79 operands: lhs = Expression(155, Add), rhs = Counter(6) -- expression 80 operands: lhs = Counter(5), rhs = Expression(156, Sub) -- expression 81 operands: lhs = Expression(157, Add), rhs = Counter(5) -- expression 82 operands: lhs = Counter(4), rhs = Expression(158, Sub) -- expression 83 operands: lhs = Expression(159, Add), rhs = Counter(4) -- expression 84 operands: lhs = Counter(3), rhs = Expression(160, Sub) -- expression 85 operands: lhs = Expression(161, Add), rhs = Counter(3) -- expression 86 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 87 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 88 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 89 operands: lhs = Counter(7), rhs = Expression(152, Sub) -- expression 90 operands: lhs = Expression(153, Add), rhs = Counter(7) -- expression 91 operands: lhs = Counter(6), rhs = Expression(154, Sub) -- expression 92 operands: lhs = Expression(155, Add), rhs = Counter(6) -- expression 93 operands: lhs = Counter(5), rhs = Expression(156, Sub) -- expression 94 operands: lhs = Expression(157, Add), rhs = Counter(5) -- expression 95 operands: lhs = Counter(4), rhs = Expression(158, Sub) -- expression 96 operands: lhs = Expression(159, Add), rhs = Counter(4) -- expression 97 operands: lhs = Counter(3), rhs = Expression(160, Sub) -- expression 98 operands: lhs = Expression(161, Add), rhs = Counter(3) -- expression 99 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 100 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 101 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 102 operands: lhs = Expression(151, Add), rhs = Counter(8) -- expression 103 operands: lhs = Counter(7), rhs = Expression(152, Sub) -- expression 104 operands: lhs = Expression(153, Add), rhs = Counter(7) -- expression 105 operands: lhs = Counter(6), rhs = Expression(154, Sub) -- expression 106 operands: lhs = Expression(155, Add), rhs = Counter(6) -- expression 107 operands: lhs = Counter(5), rhs = Expression(156, Sub) -- expression 108 operands: lhs = Expression(157, Add), rhs = Counter(5) -- expression 109 operands: lhs = Counter(4), rhs = Expression(158, Sub) -- expression 110 operands: lhs = Expression(159, Add), rhs = Counter(4) -- expression 111 operands: lhs = Counter(3), rhs = Expression(160, Sub) -- expression 112 operands: lhs = Expression(161, Add), rhs = Counter(3) -- expression 113 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 114 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 115 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 116 operands: lhs = Counter(8), rhs = Expression(150, Sub) -- expression 117 operands: lhs = Expression(151, Add), rhs = Counter(8) -- expression 118 operands: lhs = Counter(7), rhs = Expression(152, Sub) -- expression 119 operands: lhs = Expression(153, Add), rhs = Counter(7) -- expression 120 operands: lhs = Counter(6), rhs = Expression(154, Sub) -- expression 121 operands: lhs = Expression(155, Add), rhs = Counter(6) -- expression 122 operands: lhs = Counter(5), rhs = Expression(156, Sub) -- expression 123 operands: lhs = Expression(157, Add), rhs = Counter(5) -- expression 124 operands: lhs = Counter(4), rhs = Expression(158, Sub) -- expression 125 operands: lhs = Expression(159, Add), rhs = Counter(4) -- expression 126 operands: lhs = Counter(3), rhs = Expression(160, Sub) -- expression 127 operands: lhs = Expression(161, Add), rhs = Counter(3) -- expression 128 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 129 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 130 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 131 operands: lhs = Expression(149, Add), rhs = Counter(9) -- expression 132 operands: lhs = Counter(8), rhs = Expression(150, Sub) -- expression 133 operands: lhs = Expression(151, Add), rhs = Counter(8) -- expression 134 operands: lhs = Counter(7), rhs = Expression(152, Sub) -- expression 135 operands: lhs = Expression(153, Add), rhs = Counter(7) -- expression 136 operands: lhs = Counter(6), rhs = Expression(154, Sub) -- expression 137 operands: lhs = Expression(155, Add), rhs = Counter(6) -- expression 138 operands: lhs = Counter(5), rhs = Expression(156, Sub) -- expression 139 operands: lhs = Expression(157, Add), rhs = Counter(5) -- expression 140 operands: lhs = Counter(4), rhs = Expression(158, Sub) -- expression 141 operands: lhs = Expression(159, Add), rhs = Counter(4) -- expression 142 operands: lhs = Counter(3), rhs = Expression(160, Sub) -- expression 143 operands: lhs = Expression(161, Add), rhs = Counter(3) -- expression 144 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 145 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 146 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 147 operands: lhs = Counter(9), rhs = Expression(148, Sub) -- expression 148 operands: lhs = Expression(149, Add), rhs = Counter(9) -- expression 149 operands: lhs = Counter(8), rhs = Expression(150, Sub) -- expression 150 operands: lhs = Expression(151, Add), rhs = Counter(8) -- expression 151 operands: lhs = Counter(7), rhs = Expression(152, Sub) -- expression 152 operands: lhs = Expression(153, Add), rhs = Counter(7) -- expression 153 operands: lhs = Counter(6), rhs = Expression(154, Sub) -- expression 154 operands: lhs = Expression(155, Add), rhs = Counter(6) -- expression 155 operands: lhs = Counter(5), rhs = Expression(156, Sub) -- expression 156 operands: lhs = Expression(157, Add), rhs = Counter(5) -- expression 157 operands: lhs = Counter(4), rhs = Expression(158, Sub) -- expression 158 operands: lhs = Expression(159, Add), rhs = Counter(4) -- expression 159 operands: lhs = Counter(3), rhs = Expression(160, Sub) -- expression 160 operands: lhs = Expression(161, Add), rhs = Counter(3) -- expression 161 operands: lhs = Counter(2), rhs = Expression(162, Sub) -- expression 162 operands: lhs = Expression(163, Add), rhs = Counter(2) -- expression 163 operands: lhs = Counter(1), rhs = Expression(0, Sub) +- expression 1 operands: lhs = Counter(0), rhs = Counter(2) +- expression 2 operands: lhs = Counter(0), rhs = Counter(3) +- expression 3 operands: lhs = Counter(0), rhs = Counter(6) +- expression 4 operands: lhs = Counter(0), rhs = Counter(7) +- expression 5 operands: lhs = Counter(0), rhs = Counter(8) +- expression 6 operands: lhs = Counter(0), rhs = Counter(9) Number of file 0 mappings: 28 - Code(Counter(0)) at (prev + 4, 1) to (start + 7, 15) - Code(Counter(1)) at (prev + 7, 16) to (start + 4, 6) - Code(Expression(0, Sub)) at (prev + 4, 6) to (start + 0, 7) = (c0 - c1) -- Code(Expression(161, Add)) at (prev + 2, 9) to (start + 0, 17) - = (c2 + ((c1 + (c0 - c1)) - c2)) -- Code(Expression(163, Add)) at (prev + 2, 13) to (start + 0, 18) - = (c1 + (c0 - c1)) -- Code(Expression(162, Sub)) at (prev + 2, 13) to (start + 0, 18) - = ((c1 + (c0 - c1)) - c2) -- Code(Expression(159, Add)) at (prev + 3, 9) to (start + 0, 17) - = (c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) -- Code(Expression(161, Add)) at (prev + 2, 13) to (start + 0, 18) - = (c2 + ((c1 + (c0 - c1)) - c2)) -- Code(Expression(160, Sub)) at (prev + 2, 13) to (start + 0, 18) - = ((c2 + ((c1 + (c0 - c1)) - c2)) - c3) -- Code(Expression(157, Add)) at (prev + 2, 9) to (start + 0, 17) - = (c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)) -- Code(Expression(159, Add)) at (prev + 0, 20) to (start + 0, 25) - = (c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) +- Code(Counter(0)) at (prev + 2, 9) to (start + 0, 17) +- Code(Counter(0)) at (prev + 2, 13) to (start + 0, 18) +- Code(Expression(1, Sub)) at (prev + 2, 13) to (start + 0, 18) + = (c0 - c2) +- Code(Counter(0)) at (prev + 3, 9) to (start + 0, 17) +- Code(Counter(0)) at (prev + 2, 13) to (start + 0, 18) +- Code(Expression(2, Sub)) at (prev + 2, 13) to (start + 0, 18) + = (c0 - c3) +- Code(Counter(0)) at (prev + 2, 9) to (start + 0, 17) +- Code(Counter(0)) at (prev + 0, 20) to (start + 0, 25) - Code(Counter(4)) at (prev + 0, 29) to (start + 0, 34) -- Code(Expression(155, Add)) at (prev + 1, 9) to (start + 0, 17) - = (c5 + ((c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)) - c5)) -- Code(Expression(157, Add)) at (prev + 0, 20) to (start + 0, 25) - = (c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 17) +- Code(Counter(0)) at (prev + 0, 20) to (start + 0, 25) - Code(Counter(5)) at (prev + 0, 29) to (start + 0, 34) -- Code(Expression(155, Add)) at (prev + 3, 9) to (start + 1, 16) - = (c5 + ((c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)) - c5)) -- Code(Expression(154, Sub)) at (prev + 2, 5) to (start + 3, 6) - = ((c5 + ((c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)) - c5)) - c6) +- Code(Counter(0)) at (prev + 3, 9) to (start + 1, 16) +- Code(Expression(3, Sub)) at (prev + 2, 5) to (start + 3, 6) + = (c0 - c6) - Code(Counter(6)) at (prev + 3, 6) to (start + 0, 7) -- Code(Expression(153, Add)) at (prev + 3, 9) to (start + 0, 16) - = (c6 + ((c5 + ((c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)) - c5)) - c6)) +- Code(Counter(0)) at (prev + 3, 9) to (start + 0, 16) - Code(Counter(7)) at (prev + 1, 5) to (start + 3, 6) -- Code(Expression(152, Sub)) at (prev + 5, 5) to (start + 3, 6) - = ((c6 + ((c5 + ((c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)) - c5)) - c6)) - c7) -- Code(Expression(151, Add)) at (prev + 5, 8) to (start + 0, 16) - = (c7 + ((c6 + ((c5 + ((c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)) - c5)) - c6)) - c7)) -- Code(Expression(150, Sub)) at (prev + 0, 17) to (start + 2, 6) - = ((c7 + ((c6 + ((c5 + ((c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)) - c5)) - c6)) - c7)) - c8) +- Code(Expression(4, Sub)) at (prev + 5, 5) to (start + 3, 6) + = (c0 - c7) +- Code(Counter(0)) at (prev + 5, 8) to (start + 0, 16) +- Code(Expression(5, Sub)) at (prev + 0, 17) to (start + 2, 6) + = (c0 - c8) - Code(Counter(8)) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(149, Add)) at (prev + 2, 8) to (start + 0, 15) - = (c8 + ((c7 + ((c6 + ((c5 + ((c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)) - c5)) - c6)) - c7)) - c8)) +- Code(Counter(0)) at (prev + 2, 8) to (start + 0, 15) - Code(Counter(9)) at (prev + 0, 16) to (start + 2, 6) -- Code(Expression(148, Sub)) at (prev + 2, 12) to (start + 2, 6) - = ((c8 + ((c7 + ((c6 + ((c5 + ((c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)) - c5)) - c6)) - c7)) - c8)) - c9) -- Code(Expression(147, Add)) at (prev + 3, 1) to (start + 0, 2) - = (c9 + ((c8 + ((c7 + ((c6 + ((c5 + ((c4 + ((c3 + ((c2 + ((c1 + (c0 - c1)) - c2)) - c3)) - c4)) - c5)) - c6)) - c7)) - c8)) - c9)) +- Code(Expression(6, Sub)) at (prev + 2, 12) to (start + 2, 6) + = (c0 - c9) +- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) diff --git a/tests/coverage/let_else_loop.cov-map b/tests/coverage/let_else_loop.cov-map index b0cee3005220..13d0d08adb11 100644 --- a/tests/coverage/let_else_loop.cov-map +++ b/tests/coverage/let_else_loop.cov-map @@ -1,12 +1,12 @@ Function name: let_else_loop::_if (unused) -Raw bytes (19): 0x[01, 01, 00, 03, 00, 16, 01, 01, 0c, 00, 02, 09, 00, 10, 00, 02, 09, 00, 10] +Raw bytes (19): 0x[01, 01, 00, 03, 00, 16, 01, 01, 0c, 00, 01, 0f, 00, 16, 00, 00, 20, 00, 27] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 3 - Code(Zero) at (prev + 22, 1) to (start + 1, 12) -- Code(Zero) at (prev + 2, 9) to (start + 0, 16) -- Code(Zero) at (prev + 2, 9) to (start + 0, 16) +- Code(Zero) at (prev + 1, 15) to (start + 0, 22) +- Code(Zero) at (prev + 0, 32) to (start + 0, 39) Function name: let_else_loop::_loop_either_way (unused) Raw bytes (19): 0x[01, 01, 00, 03, 00, 0f, 01, 01, 14, 00, 01, 1c, 00, 23, 00, 01, 05, 00, 0c] diff --git a/tests/coverage/let_else_loop.coverage b/tests/coverage/let_else_loop.coverage index d193c8ca1b51..bd13f6e56501 100644 --- a/tests/coverage/let_else_loop.coverage +++ b/tests/coverage/let_else_loop.coverage @@ -21,11 +21,7 @@ LL| |// Variant using regular `if` instead of let-else. LL| |// This doesn't trigger the original ICE, but might help detect regressions. LL| 0|fn _if(cond: bool) { - LL| 0| if cond { - LL| 0| loop {} - LL| | } else { - LL| 0| loop {} - LL| | } + LL| 0| if cond { loop {} } else { loop {} } LL| |} LL| | LL| |#[coverage(off)] diff --git a/tests/coverage/let_else_loop.rs b/tests/coverage/let_else_loop.rs index 12e0aeabcab9..8217c0d072a6 100644 --- a/tests/coverage/let_else_loop.rs +++ b/tests/coverage/let_else_loop.rs @@ -20,11 +20,7 @@ fn _loop_either_way(cond: bool) { // Variant using regular `if` instead of let-else. // This doesn't trigger the original ICE, but might help detect regressions. fn _if(cond: bool) { - if cond { - loop {} - } else { - loop {} - } + if cond { loop {} } else { loop {} } } #[coverage(off)] diff --git a/tests/coverage/loop-break.cov-map b/tests/coverage/loop-break.cov-map new file mode 100644 index 000000000000..890d5d84539e --- /dev/null +++ b/tests/coverage/loop-break.cov-map @@ -0,0 +1,14 @@ +Function name: loop_break::main +Raw bytes (31): 0x[01, 01, 01, 01, 05, 05, 01, 03, 01, 00, 0b, 03, 02, 0c, 00, 27, 01, 01, 0d, 00, 12, 05, 01, 0a, 00, 0b, 01, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 1 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 5 +- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 11) +- Code(Expression(0, Add)) at (prev + 2, 12) to (start + 0, 39) + = (c0 + c1) +- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 18) +- Code(Counter(1)) at (prev + 1, 10) to (start + 0, 11) +- Code(Counter(0)) at (prev + 2, 1) to (start + 0, 2) + diff --git a/tests/coverage/loop-break.coverage b/tests/coverage/loop-break.coverage new file mode 100644 index 000000000000..1b7c64fb68d5 --- /dev/null +++ b/tests/coverage/loop-break.coverage @@ -0,0 +1,14 @@ + LL| |//@ edition: 2021 + LL| | + LL| 1|fn main() { + LL| | loop { + LL| 1| if core::hint::black_box(true) { + LL| 1| break; + LL| 0| } + LL| | } + LL| 1|} + LL| | + LL| |// This test is a lightly-modified version of `tests/mir-opt/coverage/instrument_coverage.rs`. + LL| |// If this test needs to be blessed, then the mir-opt version probably needs to + LL| |// be blessed too! + diff --git a/tests/coverage/loop-break.rs b/tests/coverage/loop-break.rs new file mode 100644 index 000000000000..9a842225e833 --- /dev/null +++ b/tests/coverage/loop-break.rs @@ -0,0 +1,13 @@ +//@ edition: 2021 + +fn main() { + loop { + if core::hint::black_box(true) { + break; + } + } +} + +// This test is a lightly-modified version of `tests/mir-opt/coverage/instrument_coverage.rs`. +// If this test needs to be blessed, then the mir-opt version probably needs to +// be blessed too! diff --git a/tests/coverage/loops_branches.cov-map b/tests/coverage/loops_branches.cov-map index 8dc35321133b..9187dcbd8651 100644 --- a/tests/coverage/loops_branches.cov-map +++ b/tests/coverage/loops_branches.cov-map @@ -1,57 +1,50 @@ Function name: ::fmt -Raw bytes (249): 0x[01, 01, 31, 05, 00, 00, 02, bb, 01, 19, bf, 01, c3, 01, 0d, 00, 11, 00, bf, 01, c3, 01, 0d, 00, 11, 00, bb, 01, 19, bf, 01, c3, 01, 0d, 00, 11, 00, b6, 01, 00, bb, 01, 19, bf, 01, c3, 01, 0d, 00, 11, 00, b2, 01, 00, b6, 01, 00, bb, 01, 19, bf, 01, c3, 01, 0d, 00, 11, 00, 00, ae, 01, b2, 01, 00, b6, 01, 00, bb, 01, 19, bf, 01, c3, 01, 0d, 00, 11, 00, ab, 01, 11, 00, ae, 01, b2, 01, 00, b6, 01, 00, bb, 01, 19, bf, 01, c3, 01, 0d, 00, 11, 00, a3, 01, 19, 25, a6, 01, ab, 01, 11, 00, ae, 01, b2, 01, 00, b6, 01, 00, bb, 01, 19, bf, 01, c3, 01, 0d, 00, 11, 00, 14, 01, 09, 05, 01, 10, 05, 02, 10, 00, 15, 00, 01, 17, 00, 1b, 00, 00, 1c, 00, 1e, 02, 01, 0e, 00, 0f, 07, 01, 0d, 00, 1e, 25, 00, 1e, 00, 1f, 00, 01, 10, 01, 0a, b6, 01, 03, 0d, 00, 0e, bb, 01, 00, 12, 00, 17, b6, 01, 01, 10, 00, 14, b2, 01, 01, 14, 00, 19, 00, 01, 1b, 00, 1f, 00, 00, 20, 00, 22, ae, 01, 01, 12, 00, 13, ab, 01, 01, 11, 00, 22, a6, 01, 00, 22, 00, 23, 00, 01, 14, 01, 0e, 19, 03, 09, 00, 0f, 9f, 01, 01, 05, 00, 06] +Raw bytes (228): 0x[01, 01, 2a, 05, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, a3, 01, a7, 01, 0d, 00, 11, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 9a, 01, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 96, 01, 00, 9a, 01, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 9a, 01, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 96, 01, 11, 9a, 01, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 8f, 01, 19, 25, 92, 01, 96, 01, 11, 9a, 01, 00, 9f, 01, 19, a3, 01, a7, 01, 0d, 00, 11, 00, 14, 01, 09, 05, 01, 10, 05, 02, 10, 00, 15, 00, 01, 17, 00, 1b, 00, 00, 1c, 00, 1e, 02, 01, 0e, 00, 0f, 05, 01, 0d, 00, 1e, 25, 00, 1e, 00, 1f, 00, 01, 10, 01, 0a, 9a, 01, 03, 0d, 00, 0e, 9f, 01, 00, 12, 00, 17, 9a, 01, 01, 10, 00, 14, 96, 01, 01, 14, 00, 19, 00, 01, 1b, 00, 1f, 00, 00, 20, 00, 22, 46, 01, 12, 00, 13, 96, 01, 01, 11, 00, 22, 92, 01, 00, 22, 00, 23, 00, 01, 14, 01, 0e, 19, 03, 09, 00, 0f, 8b, 01, 01, 05, 00, 06] Number of files: 1 - file 0 => global file 1 -Number of expressions: 49 +Number of expressions: 42 - expression 0 operands: lhs = Counter(1), rhs = Zero -- expression 1 operands: lhs = Zero, rhs = Expression(0, Sub) -- expression 2 operands: lhs = Expression(46, Add), rhs = Counter(6) -- expression 3 operands: lhs = Expression(47, Add), rhs = Expression(48, Add) -- expression 4 operands: lhs = Counter(3), rhs = Zero -- expression 5 operands: lhs = Counter(4), rhs = Zero -- expression 6 operands: lhs = Expression(47, Add), rhs = Expression(48, Add) -- expression 7 operands: lhs = Counter(3), rhs = Zero -- expression 8 operands: lhs = Counter(4), rhs = Zero -- expression 9 operands: lhs = Expression(46, Add), rhs = Counter(6) -- expression 10 operands: lhs = Expression(47, Add), rhs = Expression(48, Add) -- expression 11 operands: lhs = Counter(3), rhs = Zero -- expression 12 operands: lhs = Counter(4), rhs = Zero -- expression 13 operands: lhs = Expression(45, Sub), rhs = Zero -- expression 14 operands: lhs = Expression(46, Add), rhs = Counter(6) -- expression 15 operands: lhs = Expression(47, Add), rhs = Expression(48, Add) -- expression 16 operands: lhs = Counter(3), rhs = Zero -- expression 17 operands: lhs = Counter(4), rhs = Zero -- expression 18 operands: lhs = Expression(44, Sub), rhs = Zero -- expression 19 operands: lhs = Expression(45, Sub), rhs = Zero -- expression 20 operands: lhs = Expression(46, Add), rhs = Counter(6) -- expression 21 operands: lhs = Expression(47, Add), rhs = Expression(48, Add) -- expression 22 operands: lhs = Counter(3), rhs = Zero -- expression 23 operands: lhs = Counter(4), rhs = Zero -- expression 24 operands: lhs = Zero, rhs = Expression(43, Sub) -- expression 25 operands: lhs = Expression(44, Sub), rhs = Zero -- expression 26 operands: lhs = Expression(45, Sub), rhs = Zero -- expression 27 operands: lhs = Expression(46, Add), rhs = Counter(6) -- expression 28 operands: lhs = Expression(47, Add), rhs = Expression(48, Add) -- expression 29 operands: lhs = Counter(3), rhs = Zero -- expression 30 operands: lhs = Counter(4), rhs = Zero -- expression 31 operands: lhs = Expression(42, Add), rhs = Counter(4) -- expression 32 operands: lhs = Zero, rhs = Expression(43, Sub) -- expression 33 operands: lhs = Expression(44, Sub), rhs = Zero -- expression 34 operands: lhs = Expression(45, Sub), rhs = Zero -- expression 35 operands: lhs = Expression(46, Add), rhs = Counter(6) -- expression 36 operands: lhs = Expression(47, Add), rhs = Expression(48, Add) -- expression 37 operands: lhs = Counter(3), rhs = Zero -- expression 38 operands: lhs = Counter(4), rhs = Zero -- expression 39 operands: lhs = Expression(40, Add), rhs = Counter(6) -- expression 40 operands: lhs = Counter(9), rhs = Expression(41, Sub) -- expression 41 operands: lhs = Expression(42, Add), rhs = Counter(4) -- expression 42 operands: lhs = Zero, rhs = Expression(43, Sub) -- expression 43 operands: lhs = Expression(44, Sub), rhs = Zero -- expression 44 operands: lhs = Expression(45, Sub), rhs = Zero -- expression 45 operands: lhs = Expression(46, Add), rhs = Counter(6) -- expression 46 operands: lhs = Expression(47, Add), rhs = Expression(48, Add) -- expression 47 operands: lhs = Counter(3), rhs = Zero -- expression 48 operands: lhs = Counter(4), rhs = Zero +- expression 1 operands: lhs = Expression(39, Add), rhs = Counter(6) +- expression 2 operands: lhs = Expression(40, Add), rhs = Expression(41, Add) +- expression 3 operands: lhs = Counter(3), rhs = Zero +- expression 4 operands: lhs = Counter(4), rhs = Zero +- expression 5 operands: lhs = Expression(40, Add), rhs = Expression(41, Add) +- expression 6 operands: lhs = Counter(3), rhs = Zero +- expression 7 operands: lhs = Counter(4), rhs = Zero +- expression 8 operands: lhs = Expression(39, Add), rhs = Counter(6) +- expression 9 operands: lhs = Expression(40, Add), rhs = Expression(41, Add) +- expression 10 operands: lhs = Counter(3), rhs = Zero +- expression 11 operands: lhs = Counter(4), rhs = Zero +- expression 12 operands: lhs = Expression(38, Sub), rhs = Zero +- expression 13 operands: lhs = Expression(39, Add), rhs = Counter(6) +- expression 14 operands: lhs = Expression(40, Add), rhs = Expression(41, Add) +- expression 15 operands: lhs = Counter(3), rhs = Zero +- expression 16 operands: lhs = Counter(4), rhs = Zero +- expression 17 operands: lhs = Expression(37, Sub), rhs = Zero +- expression 18 operands: lhs = Expression(38, Sub), rhs = Zero +- expression 19 operands: lhs = Expression(39, Add), rhs = Counter(6) +- expression 20 operands: lhs = Expression(40, Add), rhs = Expression(41, Add) +- expression 21 operands: lhs = Counter(3), rhs = Zero +- expression 22 operands: lhs = Counter(4), rhs = Zero +- expression 23 operands: lhs = Expression(38, Sub), rhs = Zero +- expression 24 operands: lhs = Expression(39, Add), rhs = Counter(6) +- expression 25 operands: lhs = Expression(40, Add), rhs = Expression(41, Add) +- expression 26 operands: lhs = Counter(3), rhs = Zero +- expression 27 operands: lhs = Counter(4), rhs = Zero +- expression 28 operands: lhs = Expression(37, Sub), rhs = Counter(4) +- expression 29 operands: lhs = Expression(38, Sub), rhs = Zero +- expression 30 operands: lhs = Expression(39, Add), rhs = Counter(6) +- expression 31 operands: lhs = Expression(40, Add), rhs = Expression(41, Add) +- expression 32 operands: lhs = Counter(3), rhs = Zero +- expression 33 operands: lhs = Counter(4), rhs = Zero +- expression 34 operands: lhs = Expression(35, Add), rhs = Counter(6) +- expression 35 operands: lhs = Counter(9), rhs = Expression(36, Sub) +- expression 36 operands: lhs = Expression(37, Sub), rhs = Counter(4) +- expression 37 operands: lhs = Expression(38, Sub), rhs = Zero +- expression 38 operands: lhs = Expression(39, Add), rhs = Counter(6) +- expression 39 operands: lhs = Expression(40, Add), rhs = Expression(41, Add) +- expression 40 operands: lhs = Counter(3), rhs = Zero +- expression 41 operands: lhs = Counter(4), rhs = Zero Number of file 0 mappings: 20 - Code(Counter(0)) at (prev + 9, 5) to (start + 1, 16) - Code(Counter(1)) at (prev + 2, 16) to (start + 0, 21) @@ -59,87 +52,78 @@ Number of file 0 mappings: 20 - Code(Zero) at (prev + 0, 28) to (start + 0, 30) - Code(Expression(0, Sub)) at (prev + 1, 14) to (start + 0, 15) = (c1 - Zero) -- Code(Expression(1, Add)) at (prev + 1, 13) to (start + 0, 30) - = (Zero + (c1 - Zero)) +- Code(Counter(1)) at (prev + 1, 13) to (start + 0, 30) - Code(Counter(9)) at (prev + 0, 30) to (start + 0, 31) - Code(Zero) at (prev + 1, 16) to (start + 1, 10) -- Code(Expression(45, Sub)) at (prev + 3, 13) to (start + 0, 14) +- Code(Expression(38, Sub)) at (prev + 3, 13) to (start + 0, 14) = (((c3 + Zero) + (c4 + Zero)) - c6) -- Code(Expression(46, Add)) at (prev + 0, 18) to (start + 0, 23) +- Code(Expression(39, Add)) at (prev + 0, 18) to (start + 0, 23) = ((c3 + Zero) + (c4 + Zero)) -- Code(Expression(45, Sub)) at (prev + 1, 16) to (start + 0, 20) +- Code(Expression(38, Sub)) at (prev + 1, 16) to (start + 0, 20) = (((c3 + Zero) + (c4 + Zero)) - c6) -- Code(Expression(44, Sub)) at (prev + 1, 20) to (start + 0, 25) +- Code(Expression(37, Sub)) at (prev + 1, 20) to (start + 0, 25) = ((((c3 + Zero) + (c4 + Zero)) - c6) - Zero) - Code(Zero) at (prev + 1, 27) to (start + 0, 31) - Code(Zero) at (prev + 0, 32) to (start + 0, 34) -- Code(Expression(43, Sub)) at (prev + 1, 18) to (start + 0, 19) +- Code(Expression(17, Sub)) at (prev + 1, 18) to (start + 0, 19) = (((((c3 + Zero) + (c4 + Zero)) - c6) - Zero) - Zero) -- Code(Expression(42, Add)) at (prev + 1, 17) to (start + 0, 34) - = (Zero + (((((c3 + Zero) + (c4 + Zero)) - c6) - Zero) - Zero)) -- Code(Expression(41, Sub)) at (prev + 0, 34) to (start + 0, 35) - = ((Zero + (((((c3 + Zero) + (c4 + Zero)) - c6) - Zero) - Zero)) - c4) +- Code(Expression(37, Sub)) at (prev + 1, 17) to (start + 0, 34) + = ((((c3 + Zero) + (c4 + Zero)) - c6) - Zero) +- Code(Expression(36, Sub)) at (prev + 0, 34) to (start + 0, 35) + = (((((c3 + Zero) + (c4 + Zero)) - c6) - Zero) - c4) - Code(Zero) at (prev + 1, 20) to (start + 1, 14) - Code(Counter(6)) at (prev + 3, 9) to (start + 0, 15) -- Code(Expression(39, Add)) at (prev + 1, 5) to (start + 0, 6) - = ((c9 + ((Zero + (((((c3 + Zero) + (c4 + Zero)) - c6) - Zero) - Zero)) - c4)) + c6) +- Code(Expression(34, Add)) at (prev + 1, 5) to (start + 0, 6) + = ((c9 + (((((c3 + Zero) + (c4 + Zero)) - c6) - Zero) - c4)) + c6) Function name: ::fmt -Raw bytes (253): 0x[01, 01, 33, 01, 00, 02, 00, 00, 0e, 02, 00, c3, 01, 19, c7, 01, cb, 01, 00, 0d, 00, 15, c7, 01, cb, 01, 00, 0d, 00, 15, c3, 01, 19, c7, 01, cb, 01, 00, 0d, 00, 15, be, 01, 00, c3, 01, 19, c7, 01, cb, 01, 00, 0d, 00, 15, ba, 01, 00, be, 01, 00, c3, 01, 19, c7, 01, cb, 01, 00, 0d, 00, 15, 00, b6, 01, ba, 01, 00, be, 01, 00, c3, 01, 19, c7, 01, cb, 01, 00, 0d, 00, 15, b3, 01, 15, 00, b6, 01, ba, 01, 00, be, 01, 00, c3, 01, 19, c7, 01, cb, 01, 00, 0d, 00, 15, ab, 01, 25, ae, 01, 19, b3, 01, 15, 00, b6, 01, ba, 01, 00, be, 01, 00, c3, 01, 19, c7, 01, cb, 01, 00, 0d, 00, 15, 14, 01, 22, 05, 01, 11, 00, 01, 12, 01, 0a, 02, 02, 10, 00, 15, 00, 01, 17, 00, 1b, 00, 00, 1c, 00, 1e, 0e, 01, 0e, 00, 0f, 0b, 01, 0d, 00, 1e, 25, 00, 1e, 00, 1f, be, 01, 02, 0d, 00, 0e, c3, 01, 00, 12, 00, 17, be, 01, 01, 10, 00, 15, 00, 00, 16, 01, 0e, ba, 01, 02, 14, 00, 19, 00, 01, 1b, 00, 1f, 00, 00, 20, 00, 22, b6, 01, 01, 12, 00, 13, b3, 01, 01, 11, 00, 22, ae, 01, 00, 22, 00, 23, 19, 03, 09, 00, 0f, a7, 01, 01, 05, 00, 06] +Raw bytes (230): 0x[01, 01, 2b, 01, 00, 02, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, a7, 01, ab, 01, 00, 0d, 00, 15, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 9e, 01, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 9a, 01, 00, 9e, 01, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 9e, 01, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 9a, 01, 15, 9e, 01, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 93, 01, 25, 96, 01, 19, 9a, 01, 15, 9e, 01, 00, a3, 01, 19, a7, 01, ab, 01, 00, 0d, 00, 15, 14, 01, 22, 05, 01, 11, 00, 01, 12, 01, 0a, 02, 02, 10, 00, 15, 00, 01, 17, 00, 1b, 00, 00, 1c, 00, 1e, 06, 01, 0e, 00, 0f, 02, 01, 0d, 00, 1e, 25, 00, 1e, 00, 1f, 9e, 01, 02, 0d, 00, 0e, a3, 01, 00, 12, 00, 17, 9e, 01, 01, 10, 00, 15, 00, 00, 16, 01, 0e, 9a, 01, 02, 14, 00, 19, 00, 01, 1b, 00, 1f, 00, 00, 20, 00, 22, 4a, 01, 12, 00, 13, 9a, 01, 01, 11, 00, 22, 96, 01, 00, 22, 00, 23, 19, 03, 09, 00, 0f, 8f, 01, 01, 05, 00, 06] Number of files: 1 - file 0 => global file 1 -Number of expressions: 51 +Number of expressions: 43 - expression 0 operands: lhs = Counter(0), rhs = Zero - expression 1 operands: lhs = Expression(0, Sub), rhs = Zero -- expression 2 operands: lhs = Zero, rhs = Expression(3, Sub) -- expression 3 operands: lhs = Expression(0, Sub), rhs = Zero -- expression 4 operands: lhs = Expression(48, Add), rhs = Counter(6) -- expression 5 operands: lhs = Expression(49, Add), rhs = Expression(50, Add) -- expression 6 operands: lhs = Zero, rhs = Counter(3) -- expression 7 operands: lhs = Zero, rhs = Counter(5) -- expression 8 operands: lhs = Expression(49, Add), rhs = Expression(50, Add) -- expression 9 operands: lhs = Zero, rhs = Counter(3) -- expression 10 operands: lhs = Zero, rhs = Counter(5) -- expression 11 operands: lhs = Expression(48, Add), rhs = Counter(6) -- expression 12 operands: lhs = Expression(49, Add), rhs = Expression(50, Add) -- expression 13 operands: lhs = Zero, rhs = Counter(3) -- expression 14 operands: lhs = Zero, rhs = Counter(5) -- expression 15 operands: lhs = Expression(47, Sub), rhs = Zero -- expression 16 operands: lhs = Expression(48, Add), rhs = Counter(6) -- expression 17 operands: lhs = Expression(49, Add), rhs = Expression(50, Add) -- expression 18 operands: lhs = Zero, rhs = Counter(3) -- expression 19 operands: lhs = Zero, rhs = Counter(5) -- expression 20 operands: lhs = Expression(46, Sub), rhs = Zero -- expression 21 operands: lhs = Expression(47, Sub), rhs = Zero -- expression 22 operands: lhs = Expression(48, Add), rhs = Counter(6) -- expression 23 operands: lhs = Expression(49, Add), rhs = Expression(50, Add) -- expression 24 operands: lhs = Zero, rhs = Counter(3) -- expression 25 operands: lhs = Zero, rhs = Counter(5) -- expression 26 operands: lhs = Zero, rhs = Expression(45, Sub) -- expression 27 operands: lhs = Expression(46, Sub), rhs = Zero -- expression 28 operands: lhs = Expression(47, Sub), rhs = Zero -- expression 29 operands: lhs = Expression(48, Add), rhs = Counter(6) -- expression 30 operands: lhs = Expression(49, Add), rhs = Expression(50, Add) -- expression 31 operands: lhs = Zero, rhs = Counter(3) -- expression 32 operands: lhs = Zero, rhs = Counter(5) -- expression 33 operands: lhs = Expression(44, Add), rhs = Counter(5) -- expression 34 operands: lhs = Zero, rhs = Expression(45, Sub) -- expression 35 operands: lhs = Expression(46, Sub), rhs = Zero -- expression 36 operands: lhs = Expression(47, Sub), rhs = Zero -- expression 37 operands: lhs = Expression(48, Add), rhs = Counter(6) -- expression 38 operands: lhs = Expression(49, Add), rhs = Expression(50, Add) -- expression 39 operands: lhs = Zero, rhs = Counter(3) -- expression 40 operands: lhs = Zero, rhs = Counter(5) -- expression 41 operands: lhs = Expression(42, Add), rhs = Counter(9) -- expression 42 operands: lhs = Expression(43, Sub), rhs = Counter(6) -- expression 43 operands: lhs = Expression(44, Add), rhs = Counter(5) -- expression 44 operands: lhs = Zero, rhs = Expression(45, Sub) -- expression 45 operands: lhs = Expression(46, Sub), rhs = Zero -- expression 46 operands: lhs = Expression(47, Sub), rhs = Zero -- expression 47 operands: lhs = Expression(48, Add), rhs = Counter(6) -- expression 48 operands: lhs = Expression(49, Add), rhs = Expression(50, Add) -- expression 49 operands: lhs = Zero, rhs = Counter(3) -- expression 50 operands: lhs = Zero, rhs = Counter(5) +- expression 2 operands: lhs = Expression(40, Add), rhs = Counter(6) +- expression 3 operands: lhs = Expression(41, Add), rhs = Expression(42, Add) +- expression 4 operands: lhs = Zero, rhs = Counter(3) +- expression 5 operands: lhs = Zero, rhs = Counter(5) +- expression 6 operands: lhs = Expression(41, Add), rhs = Expression(42, Add) +- expression 7 operands: lhs = Zero, rhs = Counter(3) +- expression 8 operands: lhs = Zero, rhs = Counter(5) +- expression 9 operands: lhs = Expression(40, Add), rhs = Counter(6) +- expression 10 operands: lhs = Expression(41, Add), rhs = Expression(42, Add) +- expression 11 operands: lhs = Zero, rhs = Counter(3) +- expression 12 operands: lhs = Zero, rhs = Counter(5) +- expression 13 operands: lhs = Expression(39, Sub), rhs = Zero +- expression 14 operands: lhs = Expression(40, Add), rhs = Counter(6) +- expression 15 operands: lhs = Expression(41, Add), rhs = Expression(42, Add) +- expression 16 operands: lhs = Zero, rhs = Counter(3) +- expression 17 operands: lhs = Zero, rhs = Counter(5) +- expression 18 operands: lhs = Expression(38, Sub), rhs = Zero +- expression 19 operands: lhs = Expression(39, Sub), rhs = Zero +- expression 20 operands: lhs = Expression(40, Add), rhs = Counter(6) +- expression 21 operands: lhs = Expression(41, Add), rhs = Expression(42, Add) +- expression 22 operands: lhs = Zero, rhs = Counter(3) +- expression 23 operands: lhs = Zero, rhs = Counter(5) +- expression 24 operands: lhs = Expression(39, Sub), rhs = Zero +- expression 25 operands: lhs = Expression(40, Add), rhs = Counter(6) +- expression 26 operands: lhs = Expression(41, Add), rhs = Expression(42, Add) +- expression 27 operands: lhs = Zero, rhs = Counter(3) +- expression 28 operands: lhs = Zero, rhs = Counter(5) +- expression 29 operands: lhs = Expression(38, Sub), rhs = Counter(5) +- expression 30 operands: lhs = Expression(39, Sub), rhs = Zero +- expression 31 operands: lhs = Expression(40, Add), rhs = Counter(6) +- expression 32 operands: lhs = Expression(41, Add), rhs = Expression(42, Add) +- expression 33 operands: lhs = Zero, rhs = Counter(3) +- expression 34 operands: lhs = Zero, rhs = Counter(5) +- expression 35 operands: lhs = Expression(36, Add), rhs = Counter(9) +- expression 36 operands: lhs = Expression(37, Sub), rhs = Counter(6) +- expression 37 operands: lhs = Expression(38, Sub), rhs = Counter(5) +- expression 38 operands: lhs = Expression(39, Sub), rhs = Zero +- expression 39 operands: lhs = Expression(40, Add), rhs = Counter(6) +- expression 40 operands: lhs = Expression(41, Add), rhs = Expression(42, Add) +- expression 41 operands: lhs = Zero, rhs = Counter(3) +- expression 42 operands: lhs = Zero, rhs = Counter(5) Number of file 0 mappings: 20 - Code(Counter(0)) at (prev + 34, 5) to (start + 1, 17) - Code(Zero) at (prev + 1, 18) to (start + 1, 10) @@ -147,31 +131,31 @@ Number of file 0 mappings: 20 = (c0 - Zero) - Code(Zero) at (prev + 1, 23) to (start + 0, 27) - Code(Zero) at (prev + 0, 28) to (start + 0, 30) -- Code(Expression(3, Sub)) at (prev + 1, 14) to (start + 0, 15) +- Code(Expression(1, Sub)) at (prev + 1, 14) to (start + 0, 15) = ((c0 - Zero) - Zero) -- Code(Expression(2, Add)) at (prev + 1, 13) to (start + 0, 30) - = (Zero + ((c0 - Zero) - Zero)) +- Code(Expression(0, Sub)) at (prev + 1, 13) to (start + 0, 30) + = (c0 - Zero) - Code(Counter(9)) at (prev + 0, 30) to (start + 0, 31) -- Code(Expression(47, Sub)) at (prev + 2, 13) to (start + 0, 14) +- Code(Expression(39, Sub)) at (prev + 2, 13) to (start + 0, 14) = (((Zero + c3) + (Zero + c5)) - c6) -- Code(Expression(48, Add)) at (prev + 0, 18) to (start + 0, 23) +- Code(Expression(40, Add)) at (prev + 0, 18) to (start + 0, 23) = ((Zero + c3) + (Zero + c5)) -- Code(Expression(47, Sub)) at (prev + 1, 16) to (start + 0, 21) +- Code(Expression(39, Sub)) at (prev + 1, 16) to (start + 0, 21) = (((Zero + c3) + (Zero + c5)) - c6) - Code(Zero) at (prev + 0, 22) to (start + 1, 14) -- Code(Expression(46, Sub)) at (prev + 2, 20) to (start + 0, 25) +- Code(Expression(38, Sub)) at (prev + 2, 20) to (start + 0, 25) = ((((Zero + c3) + (Zero + c5)) - c6) - Zero) - Code(Zero) at (prev + 1, 27) to (start + 0, 31) - Code(Zero) at (prev + 0, 32) to (start + 0, 34) -- Code(Expression(45, Sub)) at (prev + 1, 18) to (start + 0, 19) +- Code(Expression(18, Sub)) at (prev + 1, 18) to (start + 0, 19) = (((((Zero + c3) + (Zero + c5)) - c6) - Zero) - Zero) -- Code(Expression(44, Add)) at (prev + 1, 17) to (start + 0, 34) - = (Zero + (((((Zero + c3) + (Zero + c5)) - c6) - Zero) - Zero)) -- Code(Expression(43, Sub)) at (prev + 0, 34) to (start + 0, 35) - = ((Zero + (((((Zero + c3) + (Zero + c5)) - c6) - Zero) - Zero)) - c5) +- Code(Expression(38, Sub)) at (prev + 1, 17) to (start + 0, 34) + = ((((Zero + c3) + (Zero + c5)) - c6) - Zero) +- Code(Expression(37, Sub)) at (prev + 0, 34) to (start + 0, 35) + = (((((Zero + c3) + (Zero + c5)) - c6) - Zero) - c5) - Code(Counter(6)) at (prev + 3, 9) to (start + 0, 15) -- Code(Expression(41, Add)) at (prev + 1, 5) to (start + 0, 6) - = ((((Zero + (((((Zero + c3) + (Zero + c5)) - c6) - Zero) - Zero)) - c5) + c6) + c9) +- Code(Expression(35, Add)) at (prev + 1, 5) to (start + 0, 6) + = (((((((Zero + c3) + (Zero + c5)) - c6) - Zero) - c5) + c6) + c9) Function name: loops_branches::main Raw bytes (9): 0x[01, 01, 00, 01, 01, 37, 01, 05, 02] diff --git a/tests/coverage/match_or_pattern.cov-map b/tests/coverage/match_or_pattern.cov-map index d63407a99c35..60b7024533d2 100644 --- a/tests/coverage/match_or_pattern.cov-map +++ b/tests/coverage/match_or_pattern.cov-map @@ -1,83 +1,75 @@ Function name: match_or_pattern::main -Raw bytes (202): 0x[01, 01, 23, 01, 05, 05, 02, 09, 0d, 2f, 11, 09, 0d, 2b, 15, 2f, 11, 09, 0d, 15, 26, 2b, 15, 2f, 11, 09, 0d, 19, 1d, 57, 21, 19, 1d, 53, 25, 57, 21, 19, 1d, 25, 4e, 53, 25, 57, 21, 19, 1d, 29, 2d, 7f, 31, 29, 2d, 7b, 35, 7f, 31, 29, 2d, 35, 76, 7b, 35, 7f, 31, 29, 2d, 39, 3d, 8b, 01, 41, 39, 3d, 19, 01, 01, 01, 08, 0f, 05, 08, 10, 03, 06, 02, 03, 06, 00, 07, 07, 01, 0b, 00, 11, 11, 03, 1b, 00, 1d, 2f, 01, 0e, 00, 10, 2b, 02, 08, 00, 0f, 15, 00, 10, 03, 06, 26, 03, 06, 00, 07, 23, 01, 0b, 00, 11, 21, 01, 1b, 00, 1d, 57, 01, 0e, 00, 10, 53, 02, 08, 00, 0f, 25, 00, 10, 03, 06, 4e, 03, 06, 00, 07, 4b, 01, 0b, 00, 11, 31, 01, 1b, 00, 1d, 7f, 01, 0e, 00, 10, 7b, 02, 08, 00, 0f, 35, 00, 10, 03, 06, 76, 03, 06, 00, 07, 73, 01, 0b, 00, 11, 41, 01, 1b, 00, 1d, 8b, 01, 01, 0e, 00, 10, 87, 01, 02, 01, 00, 02] +Raw bytes (185): 0x[01, 01, 1c, 01, 05, 09, 0d, 23, 11, 09, 0d, 1f, 15, 23, 11, 09, 0d, 23, 11, 09, 0d, 19, 1d, 43, 21, 19, 1d, 3f, 25, 43, 21, 19, 1d, 43, 21, 19, 1d, 29, 2d, 63, 31, 29, 2d, 5f, 35, 63, 31, 29, 2d, 63, 31, 29, 2d, 39, 3d, 6f, 41, 39, 3d, 19, 01, 01, 01, 08, 0f, 05, 08, 10, 03, 06, 02, 03, 06, 00, 07, 01, 01, 0b, 00, 11, 11, 03, 1b, 00, 1d, 23, 01, 0e, 00, 10, 1f, 02, 08, 00, 0f, 15, 00, 10, 03, 06, 12, 03, 06, 00, 07, 1f, 01, 0b, 00, 11, 21, 01, 1b, 00, 1d, 43, 01, 0e, 00, 10, 3f, 02, 08, 00, 0f, 25, 00, 10, 03, 06, 32, 03, 06, 00, 07, 3f, 01, 0b, 00, 11, 31, 01, 1b, 00, 1d, 63, 01, 0e, 00, 10, 5f, 02, 08, 00, 0f, 35, 00, 10, 03, 06, 52, 03, 06, 00, 07, 5f, 01, 0b, 00, 11, 41, 01, 1b, 00, 1d, 6f, 01, 0e, 00, 10, 6b, 02, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 35 +Number of expressions: 28 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 2 operands: lhs = Counter(2), rhs = Counter(3) -- expression 3 operands: lhs = Expression(11, Add), rhs = Counter(4) -- expression 4 operands: lhs = Counter(2), rhs = Counter(3) -- expression 5 operands: lhs = Expression(10, Add), rhs = Counter(5) -- expression 6 operands: lhs = Expression(11, Add), rhs = Counter(4) -- expression 7 operands: lhs = Counter(2), rhs = Counter(3) -- expression 8 operands: lhs = Counter(5), rhs = Expression(9, Sub) -- expression 9 operands: lhs = Expression(10, Add), rhs = Counter(5) -- expression 10 operands: lhs = Expression(11, Add), rhs = Counter(4) -- expression 11 operands: lhs = Counter(2), rhs = Counter(3) -- expression 12 operands: lhs = Counter(6), rhs = Counter(7) -- expression 13 operands: lhs = Expression(21, Add), rhs = Counter(8) +- expression 1 operands: lhs = Counter(2), rhs = Counter(3) +- expression 2 operands: lhs = Expression(8, Add), rhs = Counter(4) +- expression 3 operands: lhs = Counter(2), rhs = Counter(3) +- expression 4 operands: lhs = Expression(7, Add), rhs = Counter(5) +- expression 5 operands: lhs = Expression(8, Add), rhs = Counter(4) +- expression 6 operands: lhs = Counter(2), rhs = Counter(3) +- expression 7 operands: lhs = Expression(8, Add), rhs = Counter(4) +- expression 8 operands: lhs = Counter(2), rhs = Counter(3) +- expression 9 operands: lhs = Counter(6), rhs = Counter(7) +- expression 10 operands: lhs = Expression(16, Add), rhs = Counter(8) +- expression 11 operands: lhs = Counter(6), rhs = Counter(7) +- expression 12 operands: lhs = Expression(15, Add), rhs = Counter(9) +- expression 13 operands: lhs = Expression(16, Add), rhs = Counter(8) - expression 14 operands: lhs = Counter(6), rhs = Counter(7) -- expression 15 operands: lhs = Expression(20, Add), rhs = Counter(9) -- expression 16 operands: lhs = Expression(21, Add), rhs = Counter(8) -- expression 17 operands: lhs = Counter(6), rhs = Counter(7) -- expression 18 operands: lhs = Counter(9), rhs = Expression(19, Sub) -- expression 19 operands: lhs = Expression(20, Add), rhs = Counter(9) -- expression 20 operands: lhs = Expression(21, Add), rhs = Counter(8) -- expression 21 operands: lhs = Counter(6), rhs = Counter(7) +- expression 15 operands: lhs = Expression(16, Add), rhs = Counter(8) +- expression 16 operands: lhs = Counter(6), rhs = Counter(7) +- expression 17 operands: lhs = Counter(10), rhs = Counter(11) +- expression 18 operands: lhs = Expression(24, Add), rhs = Counter(12) +- expression 19 operands: lhs = Counter(10), rhs = Counter(11) +- expression 20 operands: lhs = Expression(23, Add), rhs = Counter(13) +- expression 21 operands: lhs = Expression(24, Add), rhs = Counter(12) - expression 22 operands: lhs = Counter(10), rhs = Counter(11) -- expression 23 operands: lhs = Expression(31, Add), rhs = Counter(12) +- expression 23 operands: lhs = Expression(24, Add), rhs = Counter(12) - expression 24 operands: lhs = Counter(10), rhs = Counter(11) -- expression 25 operands: lhs = Expression(30, Add), rhs = Counter(13) -- expression 26 operands: lhs = Expression(31, Add), rhs = Counter(12) -- expression 27 operands: lhs = Counter(10), rhs = Counter(11) -- expression 28 operands: lhs = Counter(13), rhs = Expression(29, Sub) -- expression 29 operands: lhs = Expression(30, Add), rhs = Counter(13) -- expression 30 operands: lhs = Expression(31, Add), rhs = Counter(12) -- expression 31 operands: lhs = Counter(10), rhs = Counter(11) -- expression 32 operands: lhs = Counter(14), rhs = Counter(15) -- expression 33 operands: lhs = Expression(34, Add), rhs = Counter(16) -- expression 34 operands: lhs = Counter(14), rhs = Counter(15) +- expression 25 operands: lhs = Counter(14), rhs = Counter(15) +- expression 26 operands: lhs = Expression(27, Add), rhs = Counter(16) +- expression 27 operands: lhs = Counter(14), rhs = Counter(15) Number of file 0 mappings: 25 - Code(Counter(0)) at (prev + 1, 1) to (start + 8, 15) - Code(Counter(1)) at (prev + 8, 16) to (start + 3, 6) - Code(Expression(0, Sub)) at (prev + 3, 6) to (start + 0, 7) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 11) to (start + 0, 17) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 11) to (start + 0, 17) - Code(Counter(4)) at (prev + 3, 27) to (start + 0, 29) -- Code(Expression(11, Add)) at (prev + 1, 14) to (start + 0, 16) +- Code(Expression(8, Add)) at (prev + 1, 14) to (start + 0, 16) = (c2 + c3) -- Code(Expression(10, Add)) at (prev + 2, 8) to (start + 0, 15) +- Code(Expression(7, Add)) at (prev + 2, 8) to (start + 0, 15) = ((c2 + c3) + c4) - Code(Counter(5)) at (prev + 0, 16) to (start + 3, 6) -- Code(Expression(9, Sub)) at (prev + 3, 6) to (start + 0, 7) +- Code(Expression(4, Sub)) at (prev + 3, 6) to (start + 0, 7) = (((c2 + c3) + c4) - c5) -- Code(Expression(8, Add)) at (prev + 1, 11) to (start + 0, 17) - = (c5 + (((c2 + c3) + c4) - c5)) +- Code(Expression(7, Add)) at (prev + 1, 11) to (start + 0, 17) + = ((c2 + c3) + c4) - Code(Counter(8)) at (prev + 1, 27) to (start + 0, 29) -- Code(Expression(21, Add)) at (prev + 1, 14) to (start + 0, 16) +- Code(Expression(16, Add)) at (prev + 1, 14) to (start + 0, 16) = (c6 + c7) -- Code(Expression(20, Add)) at (prev + 2, 8) to (start + 0, 15) +- Code(Expression(15, Add)) at (prev + 2, 8) to (start + 0, 15) = ((c6 + c7) + c8) - Code(Counter(9)) at (prev + 0, 16) to (start + 3, 6) -- Code(Expression(19, Sub)) at (prev + 3, 6) to (start + 0, 7) +- Code(Expression(12, Sub)) at (prev + 3, 6) to (start + 0, 7) = (((c6 + c7) + c8) - c9) -- Code(Expression(18, Add)) at (prev + 1, 11) to (start + 0, 17) - = (c9 + (((c6 + c7) + c8) - c9)) +- Code(Expression(15, Add)) at (prev + 1, 11) to (start + 0, 17) + = ((c6 + c7) + c8) - Code(Counter(12)) at (prev + 1, 27) to (start + 0, 29) -- Code(Expression(31, Add)) at (prev + 1, 14) to (start + 0, 16) +- Code(Expression(24, Add)) at (prev + 1, 14) to (start + 0, 16) = (c10 + c11) -- Code(Expression(30, Add)) at (prev + 2, 8) to (start + 0, 15) +- Code(Expression(23, Add)) at (prev + 2, 8) to (start + 0, 15) = ((c10 + c11) + c12) - Code(Counter(13)) at (prev + 0, 16) to (start + 3, 6) -- Code(Expression(29, Sub)) at (prev + 3, 6) to (start + 0, 7) +- Code(Expression(20, Sub)) at (prev + 3, 6) to (start + 0, 7) = (((c10 + c11) + c12) - c13) -- Code(Expression(28, Add)) at (prev + 1, 11) to (start + 0, 17) - = (c13 + (((c10 + c11) + c12) - c13)) +- Code(Expression(23, Add)) at (prev + 1, 11) to (start + 0, 17) + = ((c10 + c11) + c12) - Code(Counter(16)) at (prev + 1, 27) to (start + 0, 29) -- Code(Expression(34, Add)) at (prev + 1, 14) to (start + 0, 16) +- Code(Expression(27, Add)) at (prev + 1, 14) to (start + 0, 16) = (c14 + c15) -- Code(Expression(33, Add)) at (prev + 2, 1) to (start + 0, 2) +- Code(Expression(26, Add)) at (prev + 2, 1) to (start + 0, 2) = ((c14 + c15) + c16) diff --git a/tests/coverage/mcdc/condition-limit.cov-map b/tests/coverage/mcdc/condition-limit.cov-map new file mode 100644 index 000000000000..b4447a33691a --- /dev/null +++ b/tests/coverage/mcdc/condition-limit.cov-map @@ -0,0 +1,162 @@ +Function name: condition_limit::bad +Raw bytes (204): 0x[01, 01, 2c, 01, 05, 05, 1d, 05, 1d, 7a, 19, 05, 1d, 7a, 19, 05, 1d, 76, 15, 7a, 19, 05, 1d, 76, 15, 7a, 19, 05, 1d, 72, 11, 76, 15, 7a, 19, 05, 1d, 72, 11, 76, 15, 7a, 19, 05, 1d, 6e, 0d, 72, 11, 76, 15, 7a, 19, 05, 1d, 6e, 0d, 72, 11, 76, 15, 7a, 19, 05, 1d, 9f, 01, 02, a3, 01, 1d, a7, 01, 19, ab, 01, 15, af, 01, 11, 09, 0d, 21, 9b, 01, 9f, 01, 02, a3, 01, 1d, a7, 01, 19, ab, 01, 15, af, 01, 11, 09, 0d, 11, 01, 14, 01, 03, 09, 20, 05, 02, 03, 08, 00, 09, 05, 00, 0d, 00, 0e, 20, 7a, 1d, 00, 0d, 00, 0e, 7a, 00, 12, 00, 13, 20, 76, 19, 00, 12, 00, 13, 76, 00, 17, 00, 18, 20, 72, 15, 00, 17, 00, 18, 72, 00, 1c, 00, 1d, 20, 6e, 11, 00, 1c, 00, 1d, 6e, 00, 21, 00, 22, 20, 6a, 0d, 00, 21, 00, 22, 6a, 00, 26, 00, 27, 20, 21, 09, 00, 26, 00, 27, 21, 00, 28, 02, 06, 9b, 01, 02, 06, 00, 07, 97, 01, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 44 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Counter(7) +- expression 2 operands: lhs = Counter(1), rhs = Counter(7) +- expression 3 operands: lhs = Expression(30, Sub), rhs = Counter(6) +- expression 4 operands: lhs = Counter(1), rhs = Counter(7) +- expression 5 operands: lhs = Expression(30, Sub), rhs = Counter(6) +- expression 6 operands: lhs = Counter(1), rhs = Counter(7) +- expression 7 operands: lhs = Expression(29, Sub), rhs = Counter(5) +- expression 8 operands: lhs = Expression(30, Sub), rhs = Counter(6) +- expression 9 operands: lhs = Counter(1), rhs = Counter(7) +- expression 10 operands: lhs = Expression(29, Sub), rhs = Counter(5) +- expression 11 operands: lhs = Expression(30, Sub), rhs = Counter(6) +- expression 12 operands: lhs = Counter(1), rhs = Counter(7) +- expression 13 operands: lhs = Expression(28, Sub), rhs = Counter(4) +- expression 14 operands: lhs = Expression(29, Sub), rhs = Counter(5) +- expression 15 operands: lhs = Expression(30, Sub), rhs = Counter(6) +- expression 16 operands: lhs = Counter(1), rhs = Counter(7) +- expression 17 operands: lhs = Expression(28, Sub), rhs = Counter(4) +- expression 18 operands: lhs = Expression(29, Sub), rhs = Counter(5) +- expression 19 operands: lhs = Expression(30, Sub), rhs = Counter(6) +- expression 20 operands: lhs = Counter(1), rhs = Counter(7) +- expression 21 operands: lhs = Expression(27, Sub), rhs = Counter(3) +- expression 22 operands: lhs = Expression(28, Sub), rhs = Counter(4) +- expression 23 operands: lhs = Expression(29, Sub), rhs = Counter(5) +- expression 24 operands: lhs = Expression(30, Sub), rhs = Counter(6) +- expression 25 operands: lhs = Counter(1), rhs = Counter(7) +- expression 26 operands: lhs = Expression(27, Sub), rhs = Counter(3) +- expression 27 operands: lhs = Expression(28, Sub), rhs = Counter(4) +- expression 28 operands: lhs = Expression(29, Sub), rhs = Counter(5) +- expression 29 operands: lhs = Expression(30, Sub), rhs = Counter(6) +- expression 30 operands: lhs = Counter(1), rhs = Counter(7) +- expression 31 operands: lhs = Expression(39, Add), rhs = Expression(0, Sub) +- expression 32 operands: lhs = Expression(40, Add), rhs = Counter(7) +- expression 33 operands: lhs = Expression(41, Add), rhs = Counter(6) +- expression 34 operands: lhs = Expression(42, Add), rhs = Counter(5) +- expression 35 operands: lhs = Expression(43, Add), rhs = Counter(4) +- expression 36 operands: lhs = Counter(2), rhs = Counter(3) +- expression 37 operands: lhs = Counter(8), rhs = Expression(38, Add) +- expression 38 operands: lhs = Expression(39, Add), rhs = Expression(0, Sub) +- expression 39 operands: lhs = Expression(40, Add), rhs = Counter(7) +- expression 40 operands: lhs = Expression(41, Add), rhs = Counter(6) +- expression 41 operands: lhs = Expression(42, Add), rhs = Counter(5) +- expression 42 operands: lhs = Expression(43, Add), rhs = Counter(4) +- expression 43 operands: lhs = Counter(2), rhs = Counter(3) +Number of file 0 mappings: 17 +- Code(Counter(0)) at (prev + 20, 1) to (start + 3, 9) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 3, 8) to (start + 0, 9) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14) +- Branch { true: Expression(30, Sub), false: Counter(7) } at (prev + 0, 13) to (start + 0, 14) + true = (c1 - c7) + false = c7 +- Code(Expression(30, Sub)) at (prev + 0, 18) to (start + 0, 19) + = (c1 - c7) +- Branch { true: Expression(29, Sub), false: Counter(6) } at (prev + 0, 18) to (start + 0, 19) + true = ((c1 - c7) - c6) + false = c6 +- Code(Expression(29, Sub)) at (prev + 0, 23) to (start + 0, 24) + = ((c1 - c7) - c6) +- Branch { true: Expression(28, Sub), false: Counter(5) } at (prev + 0, 23) to (start + 0, 24) + true = (((c1 - c7) - c6) - c5) + false = c5 +- Code(Expression(28, Sub)) at (prev + 0, 28) to (start + 0, 29) + = (((c1 - c7) - c6) - c5) +- Branch { true: Expression(27, Sub), false: Counter(4) } at (prev + 0, 28) to (start + 0, 29) + true = ((((c1 - c7) - c6) - c5) - c4) + false = c4 +- Code(Expression(27, Sub)) at (prev + 0, 33) to (start + 0, 34) + = ((((c1 - c7) - c6) - c5) - c4) +- Branch { true: Expression(26, Sub), false: Counter(3) } at (prev + 0, 33) to (start + 0, 34) + true = (((((c1 - c7) - c6) - c5) - c4) - c3) + false = c3 +- Code(Expression(26, Sub)) at (prev + 0, 38) to (start + 0, 39) + = (((((c1 - c7) - c6) - c5) - c4) - c3) +- Branch { true: Counter(8), false: Counter(2) } at (prev + 0, 38) to (start + 0, 39) + true = c8 + false = c2 +- Code(Counter(8)) at (prev + 0, 40) to (start + 2, 6) +- Code(Expression(38, Add)) at (prev + 2, 6) to (start + 0, 7) + = ((((((c2 + c3) + c4) + c5) + c6) + c7) + (c0 - c1)) +- Code(Expression(37, Add)) at (prev + 1, 1) to (start + 0, 2) + = (c8 + ((((((c2 + c3) + c4) + c5) + c6) + c7) + (c0 - c1))) + +Function name: condition_limit::good +Raw bytes (180): 0x[01, 01, 20, 01, 05, 05, 19, 05, 19, 52, 15, 05, 19, 52, 15, 05, 19, 4e, 11, 52, 15, 05, 19, 4e, 11, 52, 15, 05, 19, 4a, 0d, 4e, 11, 52, 15, 05, 19, 4a, 0d, 4e, 11, 52, 15, 05, 19, 73, 02, 77, 19, 7b, 15, 7f, 11, 09, 0d, 1d, 6f, 73, 02, 77, 19, 7b, 15, 7f, 11, 09, 0d, 10, 01, 0c, 01, 03, 09, 28, 00, 06, 03, 08, 00, 22, 30, 05, 02, 01, 06, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 52, 19, 06, 05, 00, 00, 0d, 00, 0e, 52, 00, 12, 00, 13, 30, 4e, 15, 05, 04, 00, 00, 12, 00, 13, 4e, 00, 17, 00, 18, 30, 4a, 11, 04, 03, 00, 00, 17, 00, 18, 4a, 00, 1c, 00, 1d, 30, 46, 0d, 03, 02, 00, 00, 1c, 00, 1d, 46, 00, 21, 00, 22, 30, 1d, 09, 02, 00, 00, 00, 21, 00, 22, 1d, 00, 23, 02, 06, 6f, 02, 06, 00, 07, 6b, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 32 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Counter(6) +- expression 2 operands: lhs = Counter(1), rhs = Counter(6) +- expression 3 operands: lhs = Expression(20, Sub), rhs = Counter(5) +- expression 4 operands: lhs = Counter(1), rhs = Counter(6) +- expression 5 operands: lhs = Expression(20, Sub), rhs = Counter(5) +- expression 6 operands: lhs = Counter(1), rhs = Counter(6) +- expression 7 operands: lhs = Expression(19, Sub), rhs = Counter(4) +- expression 8 operands: lhs = Expression(20, Sub), rhs = Counter(5) +- expression 9 operands: lhs = Counter(1), rhs = Counter(6) +- expression 10 operands: lhs = Expression(19, Sub), rhs = Counter(4) +- expression 11 operands: lhs = Expression(20, Sub), rhs = Counter(5) +- expression 12 operands: lhs = Counter(1), rhs = Counter(6) +- expression 13 operands: lhs = Expression(18, Sub), rhs = Counter(3) +- expression 14 operands: lhs = Expression(19, Sub), rhs = Counter(4) +- expression 15 operands: lhs = Expression(20, Sub), rhs = Counter(5) +- expression 16 operands: lhs = Counter(1), rhs = Counter(6) +- expression 17 operands: lhs = Expression(18, Sub), rhs = Counter(3) +- expression 18 operands: lhs = Expression(19, Sub), rhs = Counter(4) +- expression 19 operands: lhs = Expression(20, Sub), rhs = Counter(5) +- expression 20 operands: lhs = Counter(1), rhs = Counter(6) +- expression 21 operands: lhs = Expression(28, Add), rhs = Expression(0, Sub) +- expression 22 operands: lhs = Expression(29, Add), rhs = Counter(6) +- expression 23 operands: lhs = Expression(30, Add), rhs = Counter(5) +- expression 24 operands: lhs = Expression(31, Add), rhs = Counter(4) +- expression 25 operands: lhs = Counter(2), rhs = Counter(3) +- expression 26 operands: lhs = Counter(7), rhs = Expression(27, Add) +- expression 27 operands: lhs = Expression(28, Add), rhs = Expression(0, Sub) +- expression 28 operands: lhs = Expression(29, Add), rhs = Counter(6) +- expression 29 operands: lhs = Expression(30, Add), rhs = Counter(5) +- expression 30 operands: lhs = Expression(31, Add), rhs = Counter(4) +- expression 31 operands: lhs = Counter(2), rhs = Counter(3) +Number of file 0 mappings: 16 +- Code(Counter(0)) at (prev + 12, 1) to (start + 3, 9) +- MCDCDecision { bitmap_idx: 0, conditions_num: 6 } at (prev + 3, 8) to (start + 0, 34) +- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 6, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14) +- MCDCBranch { true: Expression(20, Sub), false: Counter(6), condition_id: 6, true_next_id: 5, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 14) + true = (c1 - c6) + false = c6 +- Code(Expression(20, Sub)) at (prev + 0, 18) to (start + 0, 19) + = (c1 - c6) +- MCDCBranch { true: Expression(19, Sub), false: Counter(5), condition_id: 5, true_next_id: 4, false_next_id: 0 } at (prev + 0, 18) to (start + 0, 19) + true = ((c1 - c6) - c5) + false = c5 +- Code(Expression(19, Sub)) at (prev + 0, 23) to (start + 0, 24) + = ((c1 - c6) - c5) +- MCDCBranch { true: Expression(18, Sub), false: Counter(4), condition_id: 4, true_next_id: 3, false_next_id: 0 } at (prev + 0, 23) to (start + 0, 24) + true = (((c1 - c6) - c5) - c4) + false = c4 +- Code(Expression(18, Sub)) at (prev + 0, 28) to (start + 0, 29) + = (((c1 - c6) - c5) - c4) +- MCDCBranch { true: Expression(17, Sub), false: Counter(3), condition_id: 3, true_next_id: 2, false_next_id: 0 } at (prev + 0, 28) to (start + 0, 29) + true = ((((c1 - c6) - c5) - c4) - c3) + false = c3 +- Code(Expression(17, Sub)) at (prev + 0, 33) to (start + 0, 34) + = ((((c1 - c6) - c5) - c4) - c3) +- MCDCBranch { true: Counter(7), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 33) to (start + 0, 34) + true = c7 + false = c2 +- Code(Counter(7)) at (prev + 0, 35) to (start + 2, 6) +- Code(Expression(27, Add)) at (prev + 2, 6) to (start + 0, 7) + = (((((c2 + c3) + c4) + c5) + c6) + (c0 - c1)) +- Code(Expression(26, Add)) at (prev + 1, 1) to (start + 0, 2) + = (c7 + (((((c2 + c3) + c4) + c5) + c6) + (c0 - c1))) + diff --git a/tests/coverage/mcdc/condition-limit.coverage b/tests/coverage/mcdc/condition-limit.coverage new file mode 100644 index 000000000000..4eb87432fab4 --- /dev/null +++ b/tests/coverage/mcdc/condition-limit.coverage @@ -0,0 +1,76 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ min-llvm-version: 18 + LL| |//@ compile-flags: -Zcoverage-options=mcdc + LL| |//@ llvm-cov-flags: --show-branches=count --show-mcdc + LL| | + LL| |// Check that MC/DC instrumentation can gracefully handle conditions that + LL| |// exceed LLVM's limit of 6 conditions per decision. + LL| |// + LL| |// (The limit is enforced in `compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs`.) + LL| | + LL| 1|fn good() { + LL| 1| // With only 6 conditions, perform full MC/DC instrumentation. + LL| 1| let [a, b, c, d, e, f] = <[bool; 6]>::default(); + LL| 1| if a && b && c && d && e && f { + ^0 ^0 ^0 ^0 ^0 + ------------------ + | Branch (LL:8): [True: 0, False: 1] + | Branch (LL:13): [True: 0, False: 0] + | Branch (LL:18): [True: 0, False: 0] + | Branch (LL:23): [True: 0, False: 0] + | Branch (LL:28): [True: 0, False: 0] + | Branch (LL:33): [True: 0, False: 0] + ------------------ + |---> MC/DC Decision Region (LL:8) to (LL:34) + | + | Number of Conditions: 6 + | Condition C1 --> (LL:8) + | Condition C2 --> (LL:13) + | Condition C3 --> (LL:18) + | Condition C4 --> (LL:23) + | Condition C5 --> (LL:28) + | Condition C6 --> (LL:33) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3, C4, C5, C6 Result + | 1 { F, -, -, -, -, - = F } + | + | C1-Pair: not covered + | C2-Pair: not covered + | C3-Pair: not covered + | C4-Pair: not covered + | C5-Pair: not covered + | C6-Pair: not covered + | MC/DC Coverage for Decision: 0.00% + | + ------------------ + LL| 0| core::hint::black_box("hello"); + LL| 1| } + LL| 1|} + LL| | + LL| 1|fn bad() { + LL| 1| // With 7 conditions, fall back to branch instrumentation only. + LL| 1| let [a, b, c, d, e, f, g] = <[bool; 7]>::default(); + LL| 1| if a && b && c && d && e && f && g { + ^0 ^0 ^0 ^0 ^0 ^0 + ------------------ + | Branch (LL:8): [True: 0, False: 1] + | Branch (LL:13): [True: 0, False: 0] + | Branch (LL:18): [True: 0, False: 0] + | Branch (LL:23): [True: 0, False: 0] + | Branch (LL:28): [True: 0, False: 0] + | Branch (LL:33): [True: 0, False: 0] + | Branch (LL:38): [True: 0, False: 0] + ------------------ + LL| 0| core::hint::black_box("hello"); + LL| 1| } + LL| 1|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | good(); + LL| | bad(); + LL| |} + diff --git a/tests/coverage/mcdc/condition-limit.rs b/tests/coverage/mcdc/condition-limit.rs new file mode 100644 index 000000000000..571c600ebd09 --- /dev/null +++ b/tests/coverage/mcdc/condition-limit.rs @@ -0,0 +1,32 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ min-llvm-version: 18 +//@ compile-flags: -Zcoverage-options=mcdc +//@ llvm-cov-flags: --show-branches=count --show-mcdc + +// Check that MC/DC instrumentation can gracefully handle conditions that +// exceed LLVM's limit of 6 conditions per decision. +// +// (The limit is enforced in `compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs`.) + +fn good() { + // With only 6 conditions, perform full MC/DC instrumentation. + let [a, b, c, d, e, f] = <[bool; 6]>::default(); + if a && b && c && d && e && f { + core::hint::black_box("hello"); + } +} + +fn bad() { + // With 7 conditions, fall back to branch instrumentation only. + let [a, b, c, d, e, f, g] = <[bool; 7]>::default(); + if a && b && c && d && e && f && g { + core::hint::black_box("hello"); + } +} + +#[coverage(off)] +fn main() { + good(); + bad(); +} diff --git a/tests/coverage/mcdc_non_control_flow.cov-map b/tests/coverage/mcdc_non_control_flow.cov-map new file mode 100644 index 000000000000..937c36e1f16c --- /dev/null +++ b/tests/coverage/mcdc_non_control_flow.cov-map @@ -0,0 +1,204 @@ +Function name: mcdc_non_control_flow::assign_3 +Raw bytes (89): 0x[01, 01, 09, 05, 07, 0b, 11, 09, 0d, 01, 05, 01, 05, 22, 11, 01, 05, 22, 11, 01, 05, 0a, 01, 16, 01, 00, 28, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 03, 00, 0d, 00, 18, 30, 05, 22, 01, 00, 02, 00, 0d, 00, 0e, 22, 00, 12, 00, 13, 30, 1e, 11, 02, 03, 00, 00, 12, 00, 13, 1e, 00, 17, 00, 18, 30, 09, 0d, 03, 00, 00, 00, 17, 00, 18, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 9 +- expression 0 operands: lhs = Counter(1), rhs = Expression(1, Add) +- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(4) +- expression 2 operands: lhs = Counter(2), rhs = Counter(3) +- expression 3 operands: lhs = Counter(0), rhs = Counter(1) +- expression 4 operands: lhs = Counter(0), rhs = Counter(1) +- expression 5 operands: lhs = Expression(8, Sub), rhs = Counter(4) +- expression 6 operands: lhs = Counter(0), rhs = Counter(1) +- expression 7 operands: lhs = Expression(8, Sub), rhs = Counter(4) +- expression 8 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 10 +- Code(Counter(0)) at (prev + 22, 1) to (start + 0, 40) +- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) + = (c1 + ((c2 + c3) + c4)) +- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) +- MCDCDecision { bitmap_idx: 0, conditions_num: 3 } at (prev + 0, 13) to (start + 0, 24) +- MCDCBranch { true: Counter(1), false: Expression(8, Sub), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 13) to (start + 0, 14) + true = c1 + false = (c0 - c1) +- Code(Expression(8, Sub)) at (prev + 0, 18) to (start + 0, 19) + = (c0 - c1) +- MCDCBranch { true: Expression(7, Sub), false: Counter(4), condition_id: 2, true_next_id: 3, false_next_id: 0 } at (prev + 0, 18) to (start + 0, 19) + true = ((c0 - c1) - c4) + false = c4 +- Code(Expression(7, Sub)) at (prev + 0, 23) to (start + 0, 24) + = ((c0 - c1) - c4) +- MCDCBranch { true: Counter(2), false: Counter(3), condition_id: 3, true_next_id: 0, false_next_id: 0 } at (prev + 0, 23) to (start + 0, 24) + true = c2 + false = c3 +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = (c1 + ((c2 + c3) + c4)) + +Function name: mcdc_non_control_flow::assign_3_bis +Raw bytes (85): 0x[01, 01, 07, 07, 11, 09, 0d, 01, 05, 05, 09, 16, 1a, 05, 09, 01, 05, 0a, 01, 1b, 01, 00, 2c, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 03, 00, 0d, 00, 18, 30, 05, 1a, 01, 03, 02, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 30, 09, 16, 03, 00, 02, 00, 12, 00, 13, 13, 00, 17, 00, 18, 30, 0d, 11, 02, 00, 00, 00, 17, 00, 18, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 7 +- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(4) +- expression 1 operands: lhs = Counter(2), rhs = Counter(3) +- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +- expression 3 operands: lhs = Counter(1), rhs = Counter(2) +- expression 4 operands: lhs = Expression(5, Sub), rhs = Expression(6, Sub) +- expression 5 operands: lhs = Counter(1), rhs = Counter(2) +- expression 6 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 10 +- Code(Counter(0)) at (prev + 27, 1) to (start + 0, 44) +- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) + = ((c2 + c3) + c4) +- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) +- MCDCDecision { bitmap_idx: 0, conditions_num: 3 } at (prev + 0, 13) to (start + 0, 24) +- MCDCBranch { true: Counter(1), false: Expression(6, Sub), condition_id: 1, true_next_id: 3, false_next_id: 2 } at (prev + 0, 13) to (start + 0, 14) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 18) to (start + 0, 19) +- MCDCBranch { true: Counter(2), false: Expression(5, Sub), condition_id: 3, true_next_id: 0, false_next_id: 2 } at (prev + 0, 18) to (start + 0, 19) + true = c2 + false = (c1 - c2) +- Code(Expression(4, Add)) at (prev + 0, 23) to (start + 0, 24) + = ((c1 - c2) + (c0 - c1)) +- MCDCBranch { true: Counter(3), false: Counter(4), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 23) to (start + 0, 24) + true = c3 + false = c4 +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = ((c2 + c3) + c4) + +Function name: mcdc_non_control_flow::assign_and +Raw bytes (64): 0x[01, 01, 04, 07, 0e, 09, 0d, 01, 05, 01, 05, 08, 01, 0c, 01, 00, 21, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 02, 00, 0d, 00, 13, 30, 05, 0e, 01, 02, 00, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 30, 09, 0d, 02, 00, 00, 00, 12, 00, 13, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Expression(1, Add), rhs = Expression(3, Sub) +- expression 1 operands: lhs = Counter(2), rhs = Counter(3) +- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +- expression 3 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 8 +- Code(Counter(0)) at (prev + 12, 1) to (start + 0, 33) +- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) + = ((c2 + c3) + (c0 - c1)) +- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) +- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 0, 13) to (start + 0, 19) +- MCDCBranch { true: Counter(1), false: Expression(3, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 14) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 18) to (start + 0, 19) +- MCDCBranch { true: Counter(2), false: Counter(3), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 18) to (start + 0, 19) + true = c2 + false = c3 +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = ((c2 + c3) + (c0 - c1)) + +Function name: mcdc_non_control_flow::assign_or +Raw bytes (64): 0x[01, 01, 04, 07, 0d, 05, 09, 01, 05, 01, 05, 08, 01, 11, 01, 00, 20, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 02, 00, 0d, 00, 13, 30, 05, 0e, 01, 00, 02, 00, 0d, 00, 0e, 0e, 00, 12, 00, 13, 30, 09, 0d, 02, 00, 00, 00, 12, 00, 13, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(3) +- expression 1 operands: lhs = Counter(1), rhs = Counter(2) +- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +- expression 3 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 8 +- Code(Counter(0)) at (prev + 17, 1) to (start + 0, 32) +- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) + = ((c1 + c2) + c3) +- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) +- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 0, 13) to (start + 0, 19) +- MCDCBranch { true: Counter(1), false: Expression(3, Sub), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 13) to (start + 0, 14) + true = c1 + false = (c0 - c1) +- Code(Expression(3, Sub)) at (prev + 0, 18) to (start + 0, 19) + = (c0 - c1) +- MCDCBranch { true: Counter(2), false: Counter(3), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 18) to (start + 0, 19) + true = c2 + false = c3 +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = ((c1 + c2) + c3) + +Function name: mcdc_non_control_flow::foo +Raw bytes (9): 0x[01, 01, 00, 01, 01, 25, 01, 02, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 37, 1) to (start + 2, 2) + +Function name: mcdc_non_control_flow::func_call +Raw bytes (52): 0x[01, 01, 03, 01, 05, 0b, 02, 09, 0d, 06, 01, 29, 01, 01, 0a, 28, 00, 02, 01, 09, 00, 0f, 30, 05, 02, 01, 02, 00, 00, 09, 00, 0a, 05, 00, 0e, 00, 0f, 30, 09, 0d, 02, 00, 00, 00, 0e, 00, 0f, 07, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 3 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Expression(2, Add), rhs = Expression(0, Sub) +- expression 2 operands: lhs = Counter(2), rhs = Counter(3) +Number of file 0 mappings: 6 +- Code(Counter(0)) at (prev + 41, 1) to (start + 1, 10) +- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 1, 9) to (start + 0, 15) +- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 9) to (start + 0, 10) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 14) to (start + 0, 15) +- MCDCBranch { true: Counter(2), false: Counter(3), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 14) to (start + 0, 15) + true = c2 + false = c3 +- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) + = ((c2 + c3) + (c0 - c1)) + +Function name: mcdc_non_control_flow::right_comb_tree +Raw bytes (139): 0x[01, 01, 13, 07, 1a, 0b, 19, 0f, 15, 13, 11, 09, 0d, 01, 05, 01, 05, 05, 19, 05, 19, 4a, 15, 05, 19, 4a, 15, 05, 19, 46, 11, 4a, 15, 05, 19, 46, 11, 4a, 15, 05, 19, 0e, 01, 20, 01, 00, 41, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 05, 00, 0d, 00, 2a, 30, 05, 1a, 01, 02, 00, 00, 0d, 00, 0e, 05, 00, 13, 00, 14, 30, 4a, 19, 02, 03, 00, 00, 13, 00, 14, 4a, 00, 19, 00, 1a, 30, 46, 15, 03, 04, 00, 00, 19, 00, 1a, 46, 00, 1f, 00, 20, 30, 42, 11, 04, 05, 00, 00, 1f, 00, 20, 42, 00, 24, 00, 27, 30, 09, 0d, 05, 00, 00, 00, 24, 00, 27, 03, 01, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 19 +- expression 0 operands: lhs = Expression(1, Add), rhs = Expression(6, Sub) +- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(6) +- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(5) +- expression 3 operands: lhs = Expression(4, Add), rhs = Counter(4) +- expression 4 operands: lhs = Counter(2), rhs = Counter(3) +- expression 5 operands: lhs = Counter(0), rhs = Counter(1) +- expression 6 operands: lhs = Counter(0), rhs = Counter(1) +- expression 7 operands: lhs = Counter(1), rhs = Counter(6) +- expression 8 operands: lhs = Counter(1), rhs = Counter(6) +- expression 9 operands: lhs = Expression(18, Sub), rhs = Counter(5) +- expression 10 operands: lhs = Counter(1), rhs = Counter(6) +- expression 11 operands: lhs = Expression(18, Sub), rhs = Counter(5) +- expression 12 operands: lhs = Counter(1), rhs = Counter(6) +- expression 13 operands: lhs = Expression(17, Sub), rhs = Counter(4) +- expression 14 operands: lhs = Expression(18, Sub), rhs = Counter(5) +- expression 15 operands: lhs = Counter(1), rhs = Counter(6) +- expression 16 operands: lhs = Expression(17, Sub), rhs = Counter(4) +- expression 17 operands: lhs = Expression(18, Sub), rhs = Counter(5) +- expression 18 operands: lhs = Counter(1), rhs = Counter(6) +Number of file 0 mappings: 14 +- Code(Counter(0)) at (prev + 32, 1) to (start + 0, 65) +- Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) + = (((((c2 + c3) + c4) + c5) + c6) + (c0 - c1)) +- Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) +- MCDCDecision { bitmap_idx: 0, conditions_num: 5 } at (prev + 0, 13) to (start + 0, 42) +- MCDCBranch { true: Counter(1), false: Expression(6, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 14) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 19) to (start + 0, 20) +- MCDCBranch { true: Expression(18, Sub), false: Counter(6), condition_id: 2, true_next_id: 3, false_next_id: 0 } at (prev + 0, 19) to (start + 0, 20) + true = (c1 - c6) + false = c6 +- Code(Expression(18, Sub)) at (prev + 0, 25) to (start + 0, 26) + = (c1 - c6) +- MCDCBranch { true: Expression(17, Sub), false: Counter(5), condition_id: 3, true_next_id: 4, false_next_id: 0 } at (prev + 0, 25) to (start + 0, 26) + true = ((c1 - c6) - c5) + false = c5 +- Code(Expression(17, Sub)) at (prev + 0, 31) to (start + 0, 32) + = ((c1 - c6) - c5) +- MCDCBranch { true: Expression(16, Sub), false: Counter(4), condition_id: 4, true_next_id: 5, false_next_id: 0 } at (prev + 0, 31) to (start + 0, 32) + true = (((c1 - c6) - c5) - c4) + false = c4 +- Code(Expression(16, Sub)) at (prev + 0, 36) to (start + 0, 39) + = (((c1 - c6) - c5) - c4) +- MCDCBranch { true: Counter(2), false: Counter(3), condition_id: 5, true_next_id: 0, false_next_id: 0 } at (prev + 0, 36) to (start + 0, 39) + true = c2 + false = c3 +- Code(Expression(0, Add)) at (prev + 1, 5) to (start + 1, 2) + = (((((c2 + c3) + c4) + c5) + c6) + (c0 - c1)) + diff --git a/tests/coverage/mcdc_non_control_flow.coverage b/tests/coverage/mcdc_non_control_flow.coverage new file mode 100644 index 000000000000..cd733885a98b --- /dev/null +++ b/tests/coverage/mcdc_non_control_flow.coverage @@ -0,0 +1,202 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ min-llvm-version: 18 + LL| |//@ compile-flags: -Zcoverage-options=mcdc + LL| |//@ llvm-cov-flags: --show-mcdc + LL| | + LL| |// This test ensures that boolean expressions that are not inside control flow + LL| |// decisions are correctly instrumented. + LL| | + LL| |use core::hint::black_box; + LL| | + LL| 3|fn assign_and(a: bool, b: bool) { + LL| 3| let x = a && b; + ^2 + ------------------ + |---> MC/DC Decision Region (LL:13) to (LL:19) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:13) + | Condition C2 --> (LL:18) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, F = F } + | 3 { T, T = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: covered: (2,3) + | MC/DC Coverage for Decision: 100.00% + | + ------------------ + LL| 3| black_box(x); + LL| 3|} + LL| | + LL| 3|fn assign_or(a: bool, b: bool) { + LL| 3| let x = a || b; + ^1 + ------------------ + |---> MC/DC Decision Region (LL:13) to (LL:19) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:13) + | Condition C2 --> (LL:18) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, F = F } + | 2 { T, - = T } + | + | C1-Pair: covered: (1,2) + | C2-Pair: not covered + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 3| black_box(x); + LL| 3|} + LL| | + LL| 4|fn assign_3(a: bool, b: bool, c: bool) { + LL| 4| let x = a || b && c; + ^2 ^1 + ------------------ + |---> MC/DC Decision Region (LL:13) to (LL:24) + | + | Number of Conditions: 3 + | Condition C1 --> (LL:13) + | Condition C2 --> (LL:18) + | Condition C3 --> (LL:23) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3 Result + | 1 { F, F, - = F } + | 2 { T, -, - = T } + | 3 { F, T, T = T } + | + | C1-Pair: covered: (1,2) + | C2-Pair: covered: (1,3) + | C3-Pair: not covered + | MC/DC Coverage for Decision: 66.67% + | + ------------------ + LL| 4| black_box(x); + LL| 4|} + LL| | + LL| 4|fn assign_3_bis(a: bool, b: bool, c: bool) { + LL| 4| let x = a && b || c; + ^2 ^3 + ------------------ + |---> MC/DC Decision Region (LL:13) to (LL:24) + | + | Number of Conditions: 3 + | Condition C1 --> (LL:13) + | Condition C2 --> (LL:18) + | Condition C3 --> (LL:23) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3 Result + | 1 { T, F, F = F } + | 2 { F, -, T = T } + | 3 { T, T, - = T } + | + | C1-Pair: not covered + | C2-Pair: covered: (1,3) + | C3-Pair: not covered + | MC/DC Coverage for Decision: 33.33% + | + ------------------ + LL| 4| black_box(x); + LL| 4|} + LL| | + LL| 3|fn right_comb_tree(a: bool, b: bool, c: bool, d: bool, e: bool) { + LL| 3| let x = a && (b && (c && (d && (e)))); + ^2 ^1 ^1 ^1 + ------------------ + |---> MC/DC Decision Region (LL:13) to (LL:42) + | + | Number of Conditions: 5 + | Condition C1 --> (LL:13) + | Condition C2 --> (LL:19) + | Condition C3 --> (LL:25) + | Condition C4 --> (LL:31) + | Condition C5 --> (LL:36) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3, C4, C5 Result + | 1 { F, -, -, -, - = F } + | 2 { T, F, -, -, - = F } + | 3 { T, T, T, T, T = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: covered: (2,3) + | C3-Pair: not covered + | C4-Pair: not covered + | C5-Pair: not covered + | MC/DC Coverage for Decision: 40.00% + | + ------------------ + LL| 3| black_box(x); + LL| 3|} + LL| | + LL| 3|fn foo(a: bool) -> bool { + LL| 3| black_box(a) + LL| 3|} + LL| | + LL| 3|fn func_call(a: bool, b: bool) { + LL| 3| foo(a && b); + ^2 + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:15) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:9) + | Condition C2 --> (LL:14) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, F = F } + | 3 { T, T = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: covered: (2,3) + | MC/DC Coverage for Decision: 100.00% + | + ------------------ + LL| 3|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | assign_and(true, false); + LL| | assign_and(true, true); + LL| | assign_and(false, false); + LL| | + LL| | assign_or(true, false); + LL| | assign_or(true, true); + LL| | assign_or(false, false); + LL| | + LL| | assign_3(true, false, false); + LL| | assign_3(true, true, false); + LL| | assign_3(false, false, true); + LL| | assign_3(false, true, true); + LL| | + LL| | assign_3_bis(true, false, false); + LL| | assign_3_bis(true, true, false); + LL| | assign_3_bis(false, false, true); + LL| | assign_3_bis(false, true, true); + LL| | + LL| | right_comb_tree(false, false, false, true, true); + LL| | right_comb_tree(true, false, false, true, true); + LL| | right_comb_tree(true, true, true, true, true); + LL| | + LL| | func_call(true, false); + LL| | func_call(true, true); + LL| | func_call(false, false); + LL| |} + diff --git a/tests/coverage/mcdc_non_control_flow.rs b/tests/coverage/mcdc_non_control_flow.rs new file mode 100644 index 000000000000..85c0a6c6ae58 --- /dev/null +++ b/tests/coverage/mcdc_non_control_flow.rs @@ -0,0 +1,72 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ min-llvm-version: 18 +//@ compile-flags: -Zcoverage-options=mcdc +//@ llvm-cov-flags: --show-mcdc + +// This test ensures that boolean expressions that are not inside control flow +// decisions are correctly instrumented. + +use core::hint::black_box; + +fn assign_and(a: bool, b: bool) { + let x = a && b; + black_box(x); +} + +fn assign_or(a: bool, b: bool) { + let x = a || b; + black_box(x); +} + +fn assign_3(a: bool, b: bool, c: bool) { + let x = a || b && c; + black_box(x); +} + +fn assign_3_bis(a: bool, b: bool, c: bool) { + let x = a && b || c; + black_box(x); +} + +fn right_comb_tree(a: bool, b: bool, c: bool, d: bool, e: bool) { + let x = a && (b && (c && (d && (e)))); + black_box(x); +} + +fn foo(a: bool) -> bool { + black_box(a) +} + +fn func_call(a: bool, b: bool) { + foo(a && b); +} + +#[coverage(off)] +fn main() { + assign_and(true, false); + assign_and(true, true); + assign_and(false, false); + + assign_or(true, false); + assign_or(true, true); + assign_or(false, false); + + assign_3(true, false, false); + assign_3(true, true, false); + assign_3(false, false, true); + assign_3(false, true, true); + + assign_3_bis(true, false, false); + assign_3_bis(true, true, false); + assign_3_bis(false, false, true); + assign_3_bis(false, true, true); + + right_comb_tree(false, false, false, true, true); + right_comb_tree(true, false, false, true, true); + right_comb_tree(true, true, true, true, true); + + func_call(true, false); + func_call(true, true); + func_call(false, false); +} diff --git a/tests/coverage/no_cov_crate.cov-map b/tests/coverage/no_cov_crate.cov-map index 05b6448bbd24..e623f6480b90 100644 --- a/tests/coverage/no_cov_crate.cov-map +++ b/tests/coverage/no_cov_crate.cov-map @@ -47,32 +47,28 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 63, 5) to (start + 11, 6) Function name: no_cov_crate::nested_fns::outer_both_covered::inner -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 43, 09, 01, 17, 05, 01, 18, 02, 0e, 02, 02, 14, 02, 0e, 07, 03, 09, 00, 0a] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 43, 09, 01, 17, 05, 01, 18, 02, 0e, 02, 02, 14, 02, 0e, 01, 03, 09, 00, 0a] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 67, 9) to (start + 1, 23) - Code(Counter(1)) at (prev + 1, 24) to (start + 2, 14) - Code(Expression(0, Sub)) at (prev + 2, 20) to (start + 2, 14) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 3, 9) to (start + 0, 10) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 3, 9) to (start + 0, 10) Function name: no_cov_crate::nested_fns::outer_not_covered::inner -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 26, 09, 01, 17, 05, 01, 18, 02, 0e, 02, 02, 14, 02, 0e, 07, 03, 09, 00, 0a] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 26, 09, 01, 17, 05, 01, 18, 02, 0e, 02, 02, 14, 02, 0e, 01, 03, 09, 00, 0a] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 38, 9) to (start + 1, 23) - Code(Counter(1)) at (prev + 1, 24) to (start + 2, 14) - Code(Expression(0, Sub)) at (prev + 2, 20) to (start + 2, 14) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 3, 9) to (start + 0, 10) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 3, 9) to (start + 0, 10) diff --git a/tests/coverage/overflow.cov-map b/tests/coverage/overflow.cov-map index 39a5c05f879a..cb31f3d1f3e4 100644 --- a/tests/coverage/overflow.cov-map +++ b/tests/coverage/overflow.cov-map @@ -27,17 +27,15 @@ Number of file 0 mappings: 9 - Code(Counter(4)) at (prev + 2, 5) to (start + 1, 2) Function name: overflow::might_overflow -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 05, 01, 01, 12, 05, 01, 13, 02, 06, 02, 02, 06, 00, 07, 07, 01, 09, 05, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 05, 01, 01, 12, 05, 01, 13, 02, 06, 02, 02, 06, 00, 07, 01, 01, 09, 05, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 5, 1) to (start + 1, 18) - Code(Counter(1)) at (prev + 1, 19) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 9) to (start + 5, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 9) to (start + 5, 2) diff --git a/tests/coverage/partial_eq.cov-map b/tests/coverage/partial_eq.cov-map index 80670fbfa5a7..6c39ac0e378b 100644 --- a/tests/coverage/partial_eq.cov-map +++ b/tests/coverage/partial_eq.cov-map @@ -1,16 +1,16 @@ Function name: ::new -Raw bytes (9): 0x[01, 01, 00, 01, 01, 0c, 05, 06, 06] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 0c, 05, 02, 06] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 12, 5) to (start + 6, 6) +- Code(Counter(0)) at (prev + 12, 5) to (start + 2, 6) Function name: partial_eq::main -Raw bytes (9): 0x[01, 01, 00, 01, 01, 15, 01, 0a, 02] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 11, 01, 0a, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 21, 1) to (start + 10, 2) +- Code(Counter(0)) at (prev + 17, 1) to (start + 10, 2) diff --git a/tests/coverage/partial_eq.coverage b/tests/coverage/partial_eq.coverage index 9de1c933570c..dc5b82b3c5f1 100644 --- a/tests/coverage/partial_eq.coverage +++ b/tests/coverage/partial_eq.coverage @@ -10,11 +10,7 @@ LL| | LL| |impl Version { LL| 2| pub fn new(major: usize, minor: usize, patch: usize) -> Self { - LL| 2| Self { - LL| 2| major, - LL| 2| minor, - LL| 2| patch, - LL| 2| } + LL| 2| Self { major, minor, patch } LL| 2| } LL| |} LL| | @@ -26,7 +22,7 @@ LL| 1| "{:?} < {:?} = {}", LL| 1| version_3_2_1, LL| 1| version_3_3_0, - LL| 1| version_3_2_1 < version_3_3_0 + LL| 1| version_3_2_1 < version_3_3_0, // LL| 1| ); LL| 1|} LL| | diff --git a/tests/coverage/partial_eq.rs b/tests/coverage/partial_eq.rs index 825e266f111f..081502d4a9d6 100644 --- a/tests/coverage/partial_eq.rs +++ b/tests/coverage/partial_eq.rs @@ -10,11 +10,7 @@ pub struct Version { impl Version { pub fn new(major: usize, minor: usize, patch: usize) -> Self { - Self { - major, - minor, - patch, - } + Self { major, minor, patch } } } @@ -26,7 +22,7 @@ fn main() { "{:?} < {:?} = {}", version_3_2_1, version_3_3_0, - version_3_2_1 < version_3_3_0 + version_3_2_1 < version_3_3_0, // ); } diff --git a/tests/coverage/simple_loop.cov-map b/tests/coverage/simple_loop.cov-map index 0a342cb3673a..541f7bf8fbdc 100644 --- a/tests/coverage/simple_loop.cov-map +++ b/tests/coverage/simple_loop.cov-map @@ -1,27 +1,18 @@ Function name: simple_loop::main -Raw bytes (57): 0x[01, 01, 09, 01, 05, 23, 09, 05, 02, 1f, 09, 23, 09, 05, 02, 1f, 09, 23, 09, 05, 02, 07, 01, 04, 01, 09, 10, 05, 0a, 05, 05, 06, 02, 05, 06, 00, 07, 1f, 05, 0d, 02, 0e, 1a, 04, 0d, 00, 12, 09, 02, 0a, 03, 0a, 1a, 06, 01, 00, 02] +Raw bytes (43): 0x[01, 01, 02, 01, 05, 01, 09, 07, 01, 04, 01, 09, 10, 05, 0a, 05, 05, 06, 02, 05, 06, 00, 07, 07, 05, 0d, 02, 0e, 01, 04, 0d, 00, 12, 09, 02, 0a, 03, 0a, 01, 06, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 9 +Number of expressions: 2 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Expression(8, Add), rhs = Counter(2) -- expression 2 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 3 operands: lhs = Expression(7, Add), rhs = Counter(2) -- expression 4 operands: lhs = Expression(8, Add), rhs = Counter(2) -- expression 5 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 6 operands: lhs = Expression(7, Add), rhs = Counter(2) -- expression 7 operands: lhs = Expression(8, Add), rhs = Counter(2) -- expression 8 operands: lhs = Counter(1), rhs = Expression(0, Sub) +- expression 1 operands: lhs = Counter(0), rhs = Counter(2) Number of file 0 mappings: 7 - Code(Counter(0)) at (prev + 4, 1) to (start + 9, 16) - Code(Counter(1)) at (prev + 10, 5) to (start + 5, 6) - Code(Expression(0, Sub)) at (prev + 5, 6) to (start + 0, 7) = (c0 - c1) -- Code(Expression(7, Add)) at (prev + 5, 13) to (start + 2, 14) - = ((c1 + (c0 - c1)) + c2) -- Code(Expression(6, Sub)) at (prev + 4, 13) to (start + 0, 18) - = (((c1 + (c0 - c1)) + c2) - c2) +- Code(Expression(1, Add)) at (prev + 5, 13) to (start + 2, 14) + = (c0 + c2) +- Code(Counter(0)) at (prev + 4, 13) to (start + 0, 18) - Code(Counter(2)) at (prev + 2, 10) to (start + 3, 10) -- Code(Expression(6, Sub)) at (prev + 6, 1) to (start + 0, 2) - = (((c1 + (c0 - c1)) + c2) - c2) +- Code(Counter(0)) at (prev + 6, 1) to (start + 0, 2) diff --git a/tests/coverage/simple_match.cov-map b/tests/coverage/simple_match.cov-map index 7c242e2c328d..49819b23482f 100644 --- a/tests/coverage/simple_match.cov-map +++ b/tests/coverage/simple_match.cov-map @@ -1,32 +1,29 @@ Function name: simple_match::main -Raw bytes (78): 0x[01, 01, 0c, 01, 05, 2b, 2f, 05, 02, 09, 0d, 27, 11, 2b, 2f, 05, 02, 09, 0d, 27, 11, 2b, 2f, 05, 02, 09, 0d, 0a, 01, 04, 01, 07, 0f, 05, 07, 10, 02, 06, 02, 02, 06, 00, 07, 27, 05, 09, 00, 0d, 22, 05, 0d, 00, 16, 09, 02, 0d, 00, 0e, 22, 02, 11, 02, 12, 09, 04, 0d, 07, 0e, 0d, 0a, 0d, 00, 0f, 11, 03, 01, 00, 02] +Raw bytes (72): 0x[01, 01, 09, 01, 05, 01, 23, 09, 0d, 1f, 11, 01, 23, 09, 0d, 1f, 11, 01, 23, 09, 0d, 0a, 01, 04, 01, 07, 0f, 05, 07, 10, 02, 06, 02, 02, 06, 00, 07, 1f, 05, 09, 00, 0d, 1a, 05, 0d, 00, 16, 09, 02, 0d, 00, 0e, 1a, 02, 11, 02, 12, 09, 04, 0d, 07, 0e, 0d, 0a, 0d, 00, 0f, 11, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 12 +Number of expressions: 9 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Expression(10, Add), rhs = Expression(11, Add) -- expression 2 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 3 operands: lhs = Counter(2), rhs = Counter(3) -- expression 4 operands: lhs = Expression(9, Add), rhs = Counter(4) -- expression 5 operands: lhs = Expression(10, Add), rhs = Expression(11, Add) -- expression 6 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 7 operands: lhs = Counter(2), rhs = Counter(3) -- expression 8 operands: lhs = Expression(9, Add), rhs = Counter(4) -- expression 9 operands: lhs = Expression(10, Add), rhs = Expression(11, Add) -- expression 10 operands: lhs = Counter(1), rhs = Expression(0, Sub) -- expression 11 operands: lhs = Counter(2), rhs = Counter(3) +- expression 1 operands: lhs = Counter(0), rhs = Expression(8, Add) +- expression 2 operands: lhs = Counter(2), rhs = Counter(3) +- expression 3 operands: lhs = Expression(7, Add), rhs = Counter(4) +- expression 4 operands: lhs = Counter(0), rhs = Expression(8, Add) +- expression 5 operands: lhs = Counter(2), rhs = Counter(3) +- expression 6 operands: lhs = Expression(7, Add), rhs = Counter(4) +- expression 7 operands: lhs = Counter(0), rhs = Expression(8, Add) +- expression 8 operands: lhs = Counter(2), rhs = Counter(3) Number of file 0 mappings: 10 - Code(Counter(0)) at (prev + 4, 1) to (start + 7, 15) - Code(Counter(1)) at (prev + 7, 16) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7) = (c0 - c1) -- Code(Expression(9, Add)) at (prev + 5, 9) to (start + 0, 13) - = ((c1 + (c0 - c1)) + (c2 + c3)) -- Code(Expression(8, Sub)) at (prev + 5, 13) to (start + 0, 22) - = (((c1 + (c0 - c1)) + (c2 + c3)) - c4) +- Code(Expression(7, Add)) at (prev + 5, 9) to (start + 0, 13) + = (c0 + (c2 + c3)) +- Code(Expression(6, Sub)) at (prev + 5, 13) to (start + 0, 22) + = ((c0 + (c2 + c3)) - c4) - Code(Counter(2)) at (prev + 2, 13) to (start + 0, 14) -- Code(Expression(8, Sub)) at (prev + 2, 17) to (start + 2, 18) - = (((c1 + (c0 - c1)) + (c2 + c3)) - c4) +- Code(Expression(6, Sub)) at (prev + 2, 17) to (start + 2, 18) + = ((c0 + (c2 + c3)) - c4) - Code(Counter(2)) at (prev + 4, 13) to (start + 7, 14) - Code(Counter(3)) at (prev + 10, 13) to (start + 0, 15) - Code(Counter(4)) at (prev + 3, 1) to (start + 0, 2) diff --git a/tests/coverage/sort_groups.cov-map b/tests/coverage/sort_groups.cov-map index 3cbda6fbe1ab..361b70fb74f7 100644 --- a/tests/coverage/sort_groups.cov-map +++ b/tests/coverage/sort_groups.cov-map @@ -1,77 +1,67 @@ Function name: sort_groups::generic_fn::<&str> -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 06, 00, 07, 07, 01, 01, 00, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 06, 00, 07, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 17, 1) to (start + 1, 12) - Code(Counter(1)) at (prev + 1, 13) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) Function name: sort_groups::generic_fn::<()> -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 06, 00, 07, 07, 01, 01, 00, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 06, 00, 07, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 17, 1) to (start + 1, 12) - Code(Counter(1)) at (prev + 1, 13) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) Function name: sort_groups::generic_fn:: -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 06, 00, 07, 07, 01, 01, 00, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 06, 00, 07, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 17, 1) to (start + 1, 12) - Code(Counter(1)) at (prev + 1, 13) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) Function name: sort_groups::generic_fn:: -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 06, 00, 07, 07, 01, 01, 00, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 11, 01, 01, 0c, 05, 01, 0d, 02, 06, 02, 02, 06, 00, 07, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 17, 1) to (start + 1, 12) - Code(Counter(1)) at (prev + 1, 13) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) Function name: sort_groups::main -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 06, 01, 04, 23, 05, 04, 24, 02, 06, 02, 02, 06, 00, 07, 07, 01, 05, 02, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 06, 01, 04, 23, 05, 04, 24, 02, 06, 02, 02, 06, 00, 07, 01, 01, 05, 02, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 6, 1) to (start + 4, 35) - Code(Counter(1)) at (prev + 4, 36) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 5) to (start + 2, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 5) to (start + 2, 2) Function name: sort_groups::other_fn Raw bytes (9): 0x[01, 01, 00, 01, 01, 17, 01, 00, 11] diff --git a/tests/coverage/try_error_result.cov-map b/tests/coverage/try_error_result.cov-map index 83f1869a31e6..9c18827d8e68 100644 --- a/tests/coverage/try_error_result.cov-map +++ b/tests/coverage/try_error_result.cov-map @@ -1,62 +1,54 @@ Function name: ::get_thing_2 -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 29, 05, 01, 18, 05, 02, 0d, 00, 14, 02, 02, 0d, 00, 1a, 07, 02, 05, 00, 06] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 29, 05, 01, 18, 05, 02, 0d, 00, 14, 02, 02, 0d, 00, 1a, 01, 02, 05, 00, 06] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 41, 5) to (start + 1, 24) - Code(Counter(1)) at (prev + 2, 13) to (start + 0, 20) - Code(Expression(0, Sub)) at (prev + 2, 13) to (start + 0, 26) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 2, 5) to (start + 0, 6) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 2, 5) to (start + 0, 6) Function name: ::call -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 34, 05, 01, 18, 05, 02, 0d, 00, 14, 02, 02, 0d, 00, 13, 07, 02, 05, 00, 06] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 34, 05, 01, 18, 05, 02, 0d, 00, 14, 02, 02, 0d, 00, 13, 01, 02, 05, 00, 06] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 52, 5) to (start + 1, 24) - Code(Counter(1)) at (prev + 2, 13) to (start + 0, 20) - Code(Expression(0, Sub)) at (prev + 2, 13) to (start + 0, 19) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 2, 5) to (start + 0, 6) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 2, 5) to (start + 0, 6) Function name: try_error_result::call -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 04, 01, 01, 14, 05, 02, 09, 00, 10, 02, 02, 09, 00, 0f, 07, 02, 01, 00, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 05, 01, 01, 14, 05, 02, 09, 00, 10, 02, 02, 09, 00, 0f, 01, 02, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 -- Code(Counter(0)) at (prev + 4, 1) to (start + 1, 20) +- Code(Counter(0)) at (prev + 5, 1) to (start + 1, 20) - Code(Counter(1)) at (prev + 2, 9) to (start + 0, 16) - Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 15) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 2, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 2, 1) to (start + 0, 2) Function name: try_error_result::main -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 73, 01, 02, 0c, 05, 03, 05, 00, 06, 02, 02, 05, 00, 0b, 07, 01, 01, 00, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 71, 01, 02, 0c, 05, 03, 05, 00, 06, 02, 02, 05, 00, 0b, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 -- Code(Counter(0)) at (prev + 115, 1) to (start + 2, 12) +- Code(Counter(0)) at (prev + 113, 1) to (start + 2, 12) - Code(Counter(1)) at (prev + 3, 5) to (start + 0, 6) - Code(Expression(0, Sub)) at (prev + 2, 5) to (start + 0, 11) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) Function name: try_error_result::test1 Raw bytes (75): 0x[01, 01, 08, 01, 07, 00, 09, 03, 0d, 12, 1d, 03, 0d, 1b, 0d, 1f, 00, 11, 00, 0b, 01, 0d, 01, 02, 17, 03, 07, 09, 00, 0e, 12, 02, 09, 04, 1a, 1d, 06, 0d, 00, 29, 11, 00, 29, 00, 2a, 00, 01, 0d, 00, 2a, 00, 00, 2a, 00, 2b, 0e, 04, 0d, 00, 2a, 00, 00, 2a, 00, 2b, 0d, 03, 05, 00, 0b, 17, 01, 01, 00, 02] @@ -89,7 +81,7 @@ Number of file 0 mappings: 11 = (((c4 + Zero) + Zero) + c3) Function name: try_error_result::test2 -Raw bytes (280): 0x[01, 01, 24, 01, 07, 00, 09, 03, 0d, 41, 00, 1e, 00, 41, 00, 1e, 00, 41, 00, 4a, 00, 4e, 00, 52, 41, 03, 0d, 52, 41, 03, 0d, 4e, 00, 52, 41, 03, 0d, 4a, 00, 4e, 00, 52, 41, 03, 0d, 66, 00, 45, 00, 45, 00, 66, 00, 45, 00, 7a, 00, 4d, 00, 4d, 00, 7a, 00, 4d, 00, 83, 01, 0d, 87, 01, 00, 00, 8b, 01, 8f, 01, 00, 19, 00, 28, 01, 3e, 01, 03, 17, 03, 08, 09, 00, 0e, 52, 02, 09, 04, 1a, 41, 06, 0d, 00, 2f, 00, 00, 2f, 00, 30, 1e, 00, 31, 03, 35, 00, 04, 11, 00, 12, 1a, 02, 11, 04, 12, 00, 05, 11, 00, 14, 1a, 00, 17, 00, 41, 19, 00, 41, 00, 42, 00, 00, 43, 00, 5f, 00, 00, 5f, 00, 60, 00, 01, 0d, 00, 20, 00, 01, 11, 00, 14, 00, 00, 17, 00, 41, 00, 00, 41, 00, 42, 00, 00, 43, 00, 60, 00, 00, 60, 00, 61, 00, 01, 0d, 00, 20, 46, 04, 11, 00, 14, 4e, 00, 17, 00, 42, 00, 00, 42, 00, 43, 4a, 00, 44, 00, 61, 00, 00, 61, 00, 62, 46, 01, 0d, 00, 20, 62, 01, 11, 00, 14, 45, 00, 17, 01, 36, 00, 01, 36, 00, 37, 66, 01, 12, 00, 2f, 00, 00, 2f, 00, 30, 62, 01, 0d, 00, 20, 76, 01, 11, 00, 14, 4d, 00, 17, 01, 36, 00, 02, 11, 00, 12, 7a, 01, 12, 00, 2f, 00, 01, 11, 00, 12, 76, 02, 0d, 00, 20, 0d, 03, 05, 00, 0b, 7f, 01, 01, 00, 02] +Raw bytes (280): 0x[01, 01, 24, 01, 07, 00, 09, 03, 0d, 41, 00, 1e, 00, 41, 00, 1e, 00, 41, 00, 4a, 00, 4e, 00, 52, 41, 03, 0d, 52, 41, 03, 0d, 4e, 00, 52, 41, 03, 0d, 4a, 00, 4e, 00, 52, 41, 03, 0d, 66, 00, 45, 00, 45, 00, 66, 00, 45, 00, 7a, 00, 4d, 00, 4d, 00, 7a, 00, 4d, 00, 83, 01, 0d, 87, 01, 00, 00, 8b, 01, 8f, 01, 00, 19, 00, 28, 01, 3d, 01, 03, 17, 03, 08, 09, 00, 0e, 52, 02, 09, 04, 1a, 41, 06, 0d, 00, 2f, 00, 00, 2f, 00, 30, 1e, 00, 31, 03, 35, 00, 04, 11, 00, 12, 1a, 02, 11, 04, 12, 00, 05, 11, 00, 14, 1a, 00, 17, 00, 41, 19, 00, 41, 00, 42, 00, 00, 43, 00, 5f, 00, 00, 5f, 00, 60, 00, 01, 0d, 00, 20, 00, 01, 11, 00, 14, 00, 00, 17, 00, 41, 00, 00, 41, 00, 42, 00, 00, 43, 00, 60, 00, 00, 60, 00, 61, 00, 01, 0d, 00, 20, 46, 04, 11, 00, 14, 4e, 00, 17, 00, 42, 00, 00, 42, 00, 43, 4a, 00, 44, 00, 61, 00, 00, 61, 00, 62, 46, 01, 0d, 00, 20, 62, 01, 11, 00, 14, 45, 00, 17, 01, 36, 00, 01, 36, 00, 37, 66, 01, 12, 00, 2f, 00, 00, 2f, 00, 30, 62, 01, 0d, 00, 20, 76, 01, 11, 00, 14, 4d, 00, 17, 01, 36, 00, 02, 11, 00, 12, 7a, 01, 12, 00, 2f, 00, 01, 11, 00, 12, 76, 02, 0d, 00, 20, 0d, 03, 05, 00, 0b, 7f, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 36 @@ -130,7 +122,7 @@ Number of expressions: 36 - expression 34 operands: lhs = Expression(35, Add), rhs = Zero - expression 35 operands: lhs = Counter(6), rhs = Zero Number of file 0 mappings: 40 -- Code(Counter(0)) at (prev + 62, 1) to (start + 3, 23) +- Code(Counter(0)) at (prev + 61, 1) to (start + 3, 23) - Code(Expression(0, Add)) at (prev + 8, 9) to (start + 0, 14) = (c0 + (Zero + c2)) - Code(Expression(20, Sub)) at (prev + 2, 9) to (start + 4, 26) diff --git a/tests/coverage/try_error_result.coverage b/tests/coverage/try_error_result.coverage index 6fbe8b8db13f..7100248f7df8 100644 --- a/tests/coverage/try_error_result.coverage +++ b/tests/coverage/try_error_result.coverage @@ -1,4 +1,5 @@ LL| |#![allow(unused_assignments)] + LL| |#![cfg_attr(rustfmt, rustfmt::skip)] LL| |//@ failure-status: 1 LL| | LL| 6|fn call(return_error: bool) -> Result<(), ()> { @@ -9,7 +10,6 @@ LL| | } LL| 6|} LL| | - LL| |#[rustfmt::skip] LL| 1|fn test1() -> Result<(), ()> { LL| 1| let mut LL| 1| countdown = 10 @@ -59,7 +59,6 @@ LL| 17| } LL| |} LL| | - LL| |#[rustfmt::skip] LL| 1|fn test2() -> Result<(), ()> { LL| 1| let thing1 = Thing1{}; LL| 1| let mut @@ -117,7 +116,6 @@ LL| 0| Ok(()) LL| 1|} LL| | - LL| |#[rustfmt::skip] LL| 1|fn main() -> Result<(), ()> { LL| 1| test1().expect_err("test1 should fail"); LL| 1| test2() diff --git a/tests/coverage/try_error_result.rs b/tests/coverage/try_error_result.rs index f36283c34c8b..1fd176c353d9 100644 --- a/tests/coverage/try_error_result.rs +++ b/tests/coverage/try_error_result.rs @@ -1,4 +1,5 @@ #![allow(unused_assignments)] +#![cfg_attr(rustfmt, rustfmt::skip)] //@ failure-status: 1 fn call(return_error: bool) -> Result<(), ()> { @@ -9,7 +10,6 @@ fn call(return_error: bool) -> Result<(), ()> { } } -#[rustfmt::skip] fn test1() -> Result<(), ()> { let mut countdown = 10 @@ -58,7 +58,6 @@ impl Thing2 { } } -#[rustfmt::skip] fn test2() -> Result<(), ()> { let thing1 = Thing1{}; let mut @@ -111,7 +110,6 @@ fn test2() -> Result<(), ()> { Ok(()) } -#[rustfmt::skip] fn main() -> Result<(), ()> { test1().expect_err("test1 should fail"); test2() diff --git a/tests/coverage/unicode.cov-map b/tests/coverage/unicode.cov-map index aedfb2071c14..06a2c930498b 100644 --- a/tests/coverage/unicode.cov-map +++ b/tests/coverage/unicode.cov-map @@ -1,31 +1,27 @@ Function name: unicode::main -Raw bytes (67): 0x[01, 01, 09, 01, 05, 03, 05, 1e, 0d, 22, 09, 03, 05, 11, 1b, 1e, 0d, 22, 09, 03, 05, 09, 01, 0e, 01, 00, 0b, 05, 01, 09, 00, 0c, 03, 00, 10, 00, 1b, 05, 00, 1c, 00, 28, 22, 02, 08, 00, 25, 09, 00, 29, 00, 46, 11, 00, 47, 02, 06, 1b, 02, 06, 00, 07, 17, 02, 05, 01, 02] +Raw bytes (61): 0x[01, 01, 06, 01, 05, 16, 0d, 01, 09, 11, 13, 16, 0d, 01, 09, 09, 01, 0e, 01, 00, 0b, 05, 01, 09, 00, 0c, 03, 00, 10, 00, 1b, 05, 00, 1c, 00, 28, 01, 02, 08, 00, 25, 09, 00, 29, 00, 46, 11, 00, 47, 02, 06, 13, 02, 06, 00, 07, 0f, 02, 05, 01, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 9 +Number of expressions: 6 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Expression(0, Add), rhs = Counter(1) -- expression 2 operands: lhs = Expression(7, Sub), rhs = Counter(3) -- expression 3 operands: lhs = Expression(8, Sub), rhs = Counter(2) -- expression 4 operands: lhs = Expression(0, Add), rhs = Counter(1) -- expression 5 operands: lhs = Counter(4), rhs = Expression(6, Add) -- expression 6 operands: lhs = Expression(7, Sub), rhs = Counter(3) -- expression 7 operands: lhs = Expression(8, Sub), rhs = Counter(2) -- expression 8 operands: lhs = Expression(0, Add), rhs = Counter(1) +- expression 1 operands: lhs = Expression(5, Sub), rhs = Counter(3) +- expression 2 operands: lhs = Counter(0), rhs = Counter(2) +- expression 3 operands: lhs = Counter(4), rhs = Expression(4, Add) +- expression 4 operands: lhs = Expression(5, Sub), rhs = Counter(3) +- expression 5 operands: lhs = Counter(0), rhs = Counter(2) Number of file 0 mappings: 9 - Code(Counter(0)) at (prev + 14, 1) to (start + 0, 11) - Code(Counter(1)) at (prev + 1, 9) to (start + 0, 12) - Code(Expression(0, Add)) at (prev + 0, 16) to (start + 0, 27) = (c0 + c1) - Code(Counter(1)) at (prev + 0, 28) to (start + 0, 40) -- Code(Expression(8, Sub)) at (prev + 2, 8) to (start + 0, 37) - = ((c0 + c1) - c1) +- Code(Counter(0)) at (prev + 2, 8) to (start + 0, 37) - Code(Counter(2)) at (prev + 0, 41) to (start + 0, 70) - Code(Counter(4)) at (prev + 0, 71) to (start + 2, 6) -- Code(Expression(6, Add)) at (prev + 2, 6) to (start + 0, 7) - = ((((c0 + c1) - c1) - c2) + c3) -- Code(Expression(5, Add)) at (prev + 2, 5) to (start + 1, 2) - = (c4 + ((((c0 + c1) - c1) - c2) + c3)) +- Code(Expression(4, Add)) at (prev + 2, 6) to (start + 0, 7) + = ((c0 - c2) + c3) +- Code(Expression(3, Add)) at (prev + 2, 5) to (start + 1, 2) + = (c4 + ((c0 - c2) + c3)) Function name: unicode::他 (unused) Raw bytes (9): 0x[01, 01, 00, 01, 00, 1e, 19, 00, 25] diff --git a/tests/coverage/uses_crate.coverage b/tests/coverage/uses_crate.coverage index a6a570a08502..d001eeffd863 100644 --- a/tests/coverage/uses_crate.coverage +++ b/tests/coverage/uses_crate.coverage @@ -19,69 +19,69 @@ $DIR/auxiliary/used_crate.rs: LL| 1|} LL| | LL| 2|pub fn used_only_from_bin_crate_generic_function(arg: T) { - LL| 2| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + LL| 2| println!("used_only_from_bin_crate_generic_function with {arg:?}"); LL| 2|} ------------------ | Unexecuted instantiation: used_crate::used_only_from_bin_crate_generic_function::<_> ------------------ | used_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec>: | LL| 1|pub fn used_only_from_bin_crate_generic_function(arg: T) { - | LL| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + | LL| 1| println!("used_only_from_bin_crate_generic_function with {arg:?}"); | LL| 1|} ------------------ | used_crate::used_only_from_bin_crate_generic_function::<&str>: | LL| 1|pub fn used_only_from_bin_crate_generic_function(arg: T) { - | LL| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + | LL| 1| println!("used_only_from_bin_crate_generic_function with {arg:?}"); | LL| 1|} ------------------ LL| |// Expect for above function: `Unexecuted instantiation` (see below) LL| 2|pub fn used_only_from_this_lib_crate_generic_function(arg: T) { - LL| 2| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + LL| 2| println!("used_only_from_this_lib_crate_generic_function with {arg:?}"); LL| 2|} ------------------ | used_crate::used_only_from_this_lib_crate_generic_function::<&str>: | LL| 1|pub fn used_only_from_this_lib_crate_generic_function(arg: T) { - | LL| 1| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + | LL| 1| println!("used_only_from_this_lib_crate_generic_function with {arg:?}"); | LL| 1|} ------------------ | used_crate::used_only_from_this_lib_crate_generic_function::>: | LL| 1|pub fn used_only_from_this_lib_crate_generic_function(arg: T) { - | LL| 1| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + | LL| 1| println!("used_only_from_this_lib_crate_generic_function with {arg:?}"); | LL| 1|} ------------------ LL| | LL| 2|pub fn used_from_bin_crate_and_lib_crate_generic_function(arg: T) { - LL| 2| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + LL| 2| println!("used_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); LL| 2|} ------------------ | used_crate::used_from_bin_crate_and_lib_crate_generic_function::<&str>: | LL| 1|pub fn used_from_bin_crate_and_lib_crate_generic_function(arg: T) { - | LL| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | LL| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); | LL| 1|} ------------------ | used_crate::used_from_bin_crate_and_lib_crate_generic_function::>: | LL| 1|pub fn used_from_bin_crate_and_lib_crate_generic_function(arg: T) { - | LL| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | LL| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); | LL| 1|} ------------------ LL| | LL| 2|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function(arg: T) { - LL| 2| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + LL| 2| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); LL| 2|} ------------------ | used_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function::<&str>: | LL| 1|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function(arg: T) { - | LL| 1| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | LL| 1| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); | LL| 1|} ------------------ | used_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function::<&str>: | LL| 1|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function(arg: T) { - | LL| 1| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | LL| 1| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); | LL| 1|} ------------------ LL| | LL| 0|pub fn unused_generic_function(arg: T) { - LL| 0| println!("unused_generic_function with {:?}", arg); + LL| 0| println!("unused_generic_function with {arg:?}"); LL| 0|} LL| | LL| 0|pub fn unused_function() { diff --git a/tests/coverage/uses_inline_crate.cov-map b/tests/coverage/uses_inline_crate.cov-map index 6b621825c886..55ceb46b0602 100644 --- a/tests/coverage/uses_inline_crate.cov-map +++ b/tests/coverage/uses_inline_crate.cov-map @@ -7,19 +7,17 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 44, 1) to (start + 2, 2) Function name: used_inline_crate::used_inline_function -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 14, 01, 06, 0f, 05, 06, 10, 02, 06, 02, 02, 06, 00, 07, 07, 01, 05, 01, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 14, 01, 06, 0f, 05, 06, 10, 02, 06, 02, 02, 06, 00, 07, 01, 01, 05, 01, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 20, 1) to (start + 6, 15) - Code(Counter(1)) at (prev + 6, 16) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 6) to (start + 0, 7) = (c0 - c1) -- Code(Expression(1, Add)) at (prev + 1, 5) to (start + 1, 2) - = (c1 + (c0 - c1)) +- Code(Counter(0)) at (prev + 1, 5) to (start + 1, 2) Function name: used_inline_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec> Raw bytes (9): 0x[01, 01, 00, 01, 01, 21, 01, 02, 02] diff --git a/tests/coverage/uses_inline_crate.coverage b/tests/coverage/uses_inline_crate.coverage index 0c9735990c7e..832a5a6a62b4 100644 --- a/tests/coverage/uses_inline_crate.coverage +++ b/tests/coverage/uses_inline_crate.coverage @@ -34,74 +34,74 @@ $DIR/auxiliary/used_inline_crate.rs: LL| | LL| |#[inline(always)] LL| 2|pub fn used_only_from_bin_crate_generic_function(arg: T) { - LL| 2| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + LL| 2| println!("used_only_from_bin_crate_generic_function with {arg:?}"); LL| 2|} ------------------ | Unexecuted instantiation: used_inline_crate::used_only_from_bin_crate_generic_function::<_> ------------------ | used_inline_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec>: | LL| 1|pub fn used_only_from_bin_crate_generic_function(arg: T) { - | LL| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + | LL| 1| println!("used_only_from_bin_crate_generic_function with {arg:?}"); | LL| 1|} ------------------ | used_inline_crate::used_only_from_bin_crate_generic_function::<&str>: | LL| 1|pub fn used_only_from_bin_crate_generic_function(arg: T) { - | LL| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + | LL| 1| println!("used_only_from_bin_crate_generic_function with {arg:?}"); | LL| 1|} ------------------ LL| |// Expect for above function: `Unexecuted instantiation` (see notes in `used_crate.rs`) LL| | LL| |#[inline(always)] LL| 4|pub fn used_only_from_this_lib_crate_generic_function(arg: T) { - LL| 4| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + LL| 4| println!("used_only_from_this_lib_crate_generic_function with {arg:?}"); LL| 4|} ------------------ | used_inline_crate::used_only_from_this_lib_crate_generic_function::<&str>: | LL| 2|pub fn used_only_from_this_lib_crate_generic_function(arg: T) { - | LL| 2| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + | LL| 2| println!("used_only_from_this_lib_crate_generic_function with {arg:?}"); | LL| 2|} ------------------ | used_inline_crate::used_only_from_this_lib_crate_generic_function::>: | LL| 2|pub fn used_only_from_this_lib_crate_generic_function(arg: T) { - | LL| 2| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + | LL| 2| println!("used_only_from_this_lib_crate_generic_function with {arg:?}"); | LL| 2|} ------------------ LL| | LL| |#[inline(always)] LL| 3|pub fn used_from_bin_crate_and_lib_crate_generic_function(arg: T) { - LL| 3| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + LL| 3| println!("used_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); LL| 3|} ------------------ | used_inline_crate::used_from_bin_crate_and_lib_crate_generic_function::<&str>: | LL| 2|pub fn used_from_bin_crate_and_lib_crate_generic_function(arg: T) { - | LL| 2| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | LL| 2| println!("used_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); | LL| 2|} ------------------ | used_inline_crate::used_from_bin_crate_and_lib_crate_generic_function::>: | LL| 1|pub fn used_from_bin_crate_and_lib_crate_generic_function(arg: T) { - | LL| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | LL| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); | LL| 1|} ------------------ LL| | LL| |#[inline(always)] LL| 3|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function(arg: T) { - LL| 3| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + LL| 3| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); LL| 3|} ------------------ | used_inline_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function::<&str>: | LL| 1|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function(arg: T) { - | LL| 1| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | LL| 1| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); | LL| 1|} ------------------ | used_inline_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function::<&str>: | LL| 2|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function(arg: T) { - | LL| 2| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | LL| 2| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {arg:?}"); | LL| 2|} ------------------ LL| | LL| |#[inline(always)] LL| 0|pub fn unused_generic_function(arg: T) { - LL| 0| println!("unused_generic_function with {:?}", arg); + LL| 0| println!("unused_generic_function with {arg:?}"); LL| 0|} LL| | LL| |#[inline(always)] diff --git a/tests/coverage/while.cov-map b/tests/coverage/while.cov-map index c6557b48e27d..4d813a935a05 100644 --- a/tests/coverage/while.cov-map +++ b/tests/coverage/while.cov-map @@ -1,15 +1,13 @@ Function name: while::main -Raw bytes (28): 0x[01, 01, 02, 01, 00, 03, 00, 04, 01, 01, 01, 01, 10, 03, 02, 0b, 00, 14, 00, 00, 15, 02, 06, 06, 03, 01, 00, 02] +Raw bytes (26): 0x[01, 01, 01, 01, 00, 04, 01, 01, 01, 01, 10, 03, 02, 0b, 00, 14, 00, 00, 15, 02, 06, 01, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Zero -- expression 1 operands: lhs = Expression(0, Add), rhs = Zero Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 1, 1) to (start + 1, 16) - Code(Expression(0, Add)) at (prev + 2, 11) to (start + 0, 20) = (c0 + Zero) - Code(Zero) at (prev + 0, 21) to (start + 2, 6) -- Code(Expression(1, Sub)) at (prev + 3, 1) to (start + 0, 2) - = ((c0 + Zero) - Zero) +- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) diff --git a/tests/coverage/yield.cov-map b/tests/coverage/yield.cov-map index 0347aaaa3676..a273c688e248 100644 --- a/tests/coverage/yield.cov-map +++ b/tests/coverage/yield.cov-map @@ -1,5 +1,5 @@ Function name: yield::main -Raw bytes (106): 0x[01, 01, 0b, 05, 00, 0d, 11, 22, 15, 0d, 11, 11, 15, 22, 15, 0d, 11, 22, 15, 0d, 11, 19, 1d, 25, 29, 10, 01, 07, 01, 01, 16, 01, 06, 0b, 00, 2e, 0d, 01, 27, 00, 29, 03, 01, 0e, 00, 34, 0d, 02, 0b, 00, 2e, 22, 01, 22, 00, 27, 1e, 00, 2c, 00, 2e, 13, 01, 0e, 00, 34, 1e, 03, 09, 00, 16, 1e, 07, 0b, 00, 2e, 21, 01, 27, 00, 29, 27, 01, 0e, 00, 34, 21, 02, 0b, 00, 2e, 2d, 01, 27, 00, 29, 2b, 01, 0e, 00, 34, 2d, 02, 01, 00, 02] +Raw bytes (106): 0x[01, 01, 0b, 05, 00, 0d, 11, 22, 15, 0d, 11, 11, 15, 22, 15, 0d, 11, 22, 15, 0d, 11, 19, 1d, 25, 29, 10, 01, 07, 01, 01, 16, 01, 07, 0b, 00, 2e, 0d, 01, 27, 00, 29, 03, 01, 0e, 00, 34, 0d, 02, 0b, 00, 2e, 22, 01, 22, 00, 27, 1e, 00, 2c, 00, 2e, 13, 01, 0e, 00, 34, 1e, 03, 09, 00, 16, 1e, 08, 0b, 00, 2e, 21, 01, 27, 00, 29, 27, 01, 0e, 00, 34, 21, 02, 0b, 00, 2e, 2d, 01, 27, 00, 29, 2b, 01, 0e, 00, 34, 2d, 02, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 11 @@ -16,7 +16,7 @@ Number of expressions: 11 - expression 10 operands: lhs = Counter(9), rhs = Counter(10) Number of file 0 mappings: 16 - Code(Counter(0)) at (prev + 7, 1) to (start + 1, 22) -- Code(Counter(0)) at (prev + 6, 11) to (start + 0, 46) +- Code(Counter(0)) at (prev + 7, 11) to (start + 0, 46) - Code(Counter(3)) at (prev + 1, 39) to (start + 0, 41) - Code(Expression(0, Add)) at (prev + 1, 14) to (start + 0, 52) = (c1 + Zero) @@ -29,7 +29,7 @@ Number of file 0 mappings: 16 = (c4 + c5) - Code(Expression(7, Sub)) at (prev + 3, 9) to (start + 0, 22) = ((c3 - c4) - c5) -- Code(Expression(7, Sub)) at (prev + 7, 11) to (start + 0, 46) +- Code(Expression(7, Sub)) at (prev + 8, 11) to (start + 0, 46) = ((c3 - c4) - c5) - Code(Counter(8)) at (prev + 1, 39) to (start + 0, 41) - Code(Expression(9, Add)) at (prev + 1, 14) to (start + 0, 52) @@ -41,21 +41,21 @@ Number of file 0 mappings: 16 - Code(Counter(11)) at (prev + 2, 1) to (start + 0, 2) Function name: yield::main::{closure#0} -Raw bytes (14): 0x[01, 01, 00, 02, 01, 08, 29, 01, 10, 05, 02, 10, 01, 06] +Raw bytes (14): 0x[01, 01, 00, 02, 01, 09, 08, 01, 10, 05, 02, 10, 01, 06] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 2 -- Code(Counter(0)) at (prev + 8, 41) to (start + 1, 16) +- Code(Counter(0)) at (prev + 9, 8) to (start + 1, 16) - Code(Counter(1)) at (prev + 2, 16) to (start + 1, 6) Function name: yield::main::{closure#1} -Raw bytes (24): 0x[01, 01, 00, 04, 01, 16, 29, 01, 10, 05, 02, 09, 00, 10, 09, 01, 09, 00, 10, 0d, 01, 10, 01, 06] +Raw bytes (24): 0x[01, 01, 00, 04, 01, 18, 08, 01, 10, 05, 02, 09, 00, 10, 09, 01, 09, 00, 10, 0d, 01, 10, 01, 06] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 4 -- Code(Counter(0)) at (prev + 22, 41) to (start + 1, 16) +- Code(Counter(0)) at (prev + 24, 8) to (start + 1, 16) - Code(Counter(1)) at (prev + 2, 9) to (start + 0, 16) - Code(Counter(2)) at (prev + 1, 9) to (start + 0, 16) - Code(Counter(3)) at (prev + 1, 16) to (start + 1, 6) diff --git a/tests/coverage/yield.coverage b/tests/coverage/yield.coverage index e2fc9196d244..2c133cbec549 100644 --- a/tests/coverage/yield.coverage +++ b/tests/coverage/yield.coverage @@ -5,7 +5,8 @@ LL| |use std::pin::Pin; LL| | LL| 1|fn main() { - LL| 1| let mut coroutine = #[coroutine] || { + LL| 1| let mut coroutine = #[coroutine] + LL| 1| || { LL| 1| yield 1; LL| 1| return "foo"; LL| 1| }; @@ -19,7 +20,8 @@ LL| 0| _ => panic!("unexpected value from resume"), LL| | } LL| | - LL| 1| let mut coroutine = #[coroutine] || { + LL| 1| let mut coroutine = #[coroutine] + LL| 1| || { LL| 1| yield 1; LL| 1| yield 2; LL| 0| yield 3; diff --git a/tests/coverage/yield.rs b/tests/coverage/yield.rs index 64ea27066047..e02e3d356124 100644 --- a/tests/coverage/yield.rs +++ b/tests/coverage/yield.rs @@ -5,7 +5,8 @@ use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; fn main() { - let mut coroutine = #[coroutine] || { + let mut coroutine = #[coroutine] + || { yield 1; return "foo"; }; @@ -19,7 +20,8 @@ fn main() { _ => panic!("unexpected value from resume"), } - let mut coroutine = #[coroutine] || { + let mut coroutine = #[coroutine] + || { yield 1; yield 2; yield 3; diff --git a/tests/crashes/109812.rs b/tests/crashes/109812.rs deleted file mode 100644 index c29b87465215..000000000000 --- a/tests/crashes/109812.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ known-bug: #109812 - -#![warn(rust_2021_incompatible_closure_captures)] - -enum Either { - One(X), - Two(X), -} - -struct X(Y); - -struct Y; - -fn move_into_fnmut() { - let x = X(Y); - - consume_fnmut(|| { - let Either::Two(ref mut _t) = x; - - let X(mut _t) = x; - }); -} diff --git a/tests/crashes/118185.rs b/tests/crashes/118185.rs deleted file mode 100644 index c3a29c3a3f54..000000000000 --- a/tests/crashes/118185.rs +++ /dev/null @@ -1,26 +0,0 @@ -//@ known-bug: #118185 - -fn main() { - let target: Target = create_target(); - target.get(0); // correct arguments work - target.get(10.0); // CRASH HERE -} - -// must be generic -fn create_target() -> T { - unimplemented!() -} - -// unimplemented trait, but contains function with the same name -pub trait RandomTrait { - fn get(&mut self); // but less arguments -} - -struct Target; - -impl Target { - // correct function with arguments - pub fn get(&self, data: i32) { - unimplemented!() - } -} diff --git a/tests/crashes/118403.rs b/tests/crashes/118403.rs deleted file mode 100644 index 21ab15f9ffd0..000000000000 --- a/tests/crashes/118403.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ known-bug: #118403 -#![feature(generic_const_exprs)] -pub struct X {} -impl X { - pub fn y<'a, U: 'a>(&'a self) -> impl Iterator + '_> { - (0..1).map(move |_| (0..1).map(move |_| loop {})) - } -} diff --git a/tests/crashes/119381.rs b/tests/crashes/119381.rs deleted file mode 100644 index 51d1d084ba2b..000000000000 --- a/tests/crashes/119381.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ known-bug: #119381 - -#![feature(with_negative_coherence)] -trait Trait {} -impl Trait for [(); N] {} -impl Trait for [(); N] {} diff --git a/tests/crashes/119830.rs b/tests/crashes/119830.rs deleted file mode 100644 index 71becc04e169..000000000000 --- a/tests/crashes/119830.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ known-bug: #119830 -#![feature(effects)] -#![feature(min_specialization)] - -trait Specialize {} - -trait Foo {} - -impl const Foo for T {} - -impl const Foo for T where T: const Specialize {} diff --git a/tests/crashes/121126.rs b/tests/crashes/121126.rs deleted file mode 100644 index 2ebe91f02ded..000000000000 --- a/tests/crashes/121126.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ known-bug: #121126 -fn main() { - let _n = 1i64 >> [64][4_294_967_295]; -} diff --git a/tests/crashes/121134.rs b/tests/crashes/121134.rs deleted file mode 100644 index 36397d4ec3c0..000000000000 --- a/tests/crashes/121134.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ known-bug: #121134 -trait Output<'a> { - type Type; -} - -struct Wrapper; - -impl Wrapper { - fn do_something_wrapper(&mut self, do_something_wrapper: F) - where - FnOnce:, - F: for<'a> FnOnce(>::Type), - { - } -} - -fn main() { - let mut wrapper = Wrapper; - wrapper.do_something_wrapper::(|value| ()); -} diff --git a/tests/crashes/121574-2.rs b/tests/crashes/121574-2.rs deleted file mode 100644 index a08f3f063974..000000000000 --- a/tests/crashes/121574-2.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ known-bug: #121574 -#![feature(generic_const_exprs)] -pub struct DimName {} -impl X { - pub fn y<'a, U: 'a>(&'a self) -> impl Iterator + '_> { - "0".as_bytes(move |_| (0..1).map(move |_| loop {})) - } -} diff --git a/tests/crashes/121574.rs b/tests/crashes/121574.rs deleted file mode 100644 index 53eec829c5f3..000000000000 --- a/tests/crashes/121574.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ known-bug: #121574 -#![feature(generic_const_exprs)] - -impl X { - pub fn y<'a, U: 'a>(&'a self) -> impl Iterator + '_> {} -} diff --git a/tests/crashes/121585-2.rs b/tests/crashes/121585-2.rs deleted file mode 100644 index 99cc8f791955..000000000000 --- a/tests/crashes/121585-2.rs +++ /dev/null @@ -1,30 +0,0 @@ -//@ known-bug: #121585 -//@ check-pass -#![feature(generic_const_exprs)] -#![allow(incomplete_features)] - -trait Trait {} -pub struct EvaluatableU128; - -struct HasCastInTraitImpl; -impl Trait for HasCastInTraitImpl {} - -pub fn use_trait_impl() where EvaluatableU128<{N as u128}>:, { - fn assert_impl() {} - - assert_impl::>(); - assert_impl::>(); - assert_impl::>(); - assert_impl::>(); -} -pub fn use_trait_impl_2() where EvaluatableU128<{N as _}>:, { - fn assert_impl() {} - - assert_impl::>(); - assert_impl::>(); - assert_impl::>()const NUM: u8 = xyz(); - assert_impl::>(); -} - - -fn main() {} diff --git a/tests/crashes/121858-2.rs b/tests/crashes/121858-2.rs deleted file mode 100644 index cb80c081cffa..000000000000 --- a/tests/crashes/121858-2.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ known-bug: #121858 -#![allow(named_arguments_used_positionally)] -#![feature(generic_const_exprs)] -struct Inner; -impl Inner where [(); N + M]: { - fn i() -> Self { - Self - } -} - -struct Outer(Inner) where [(); A + (B * 2)]:; -impl Outer where [(); A + (B * 2)]: { - fn o() -> Union { - Self(Inner::i()) - } -} - -fn main() { - Outer::<1, 1>::o(); -} diff --git a/tests/crashes/122638.rs b/tests/crashes/122638.rs deleted file mode 100644 index af0fc5bbd503..000000000000 --- a/tests/crashes/122638.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ known-bug: #122638 -#![feature(min_specialization)] - -impl<'a, T: std::fmt::Debug, const N: usize> Iterator for ConstChunksExact<'a, T, { N }> { - fn next(&mut self) -> Option {} -} - -struct ConstChunksExact<'a, T: '_, const assert: usize> {} - -impl<'a, T: std::fmt::Debug, const N: usize> Iterator for ConstChunksExact<'a, T, {}> { - type Item = &'a [T; N]; -} diff --git a/tests/crashes/122908.rs b/tests/crashes/122908.rs deleted file mode 100644 index c9da1bc18791..000000000000 --- a/tests/crashes/122908.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ known-bug: #122908 -trait Trait { - async fn handle(slf: &F) {} -} diff --git a/tests/crashes/123141-2.rs b/tests/crashes/123141-2.rs deleted file mode 100644 index 74f961c2a337..000000000000 --- a/tests/crashes/123141-2.rs +++ /dev/null @@ -1,23 +0,0 @@ -//@ known-bug: #123141 - -trait ConstChunksExactTrait { - fn const_chunks_exact(&self) -> ConstChunksExact<'_, T, {N}>; -} - -impl ConstChunksExactTrait for [T] {} - -struct ConstChunksExact<'a, T: 'a, const N: usize> {} - -impl <'a, T: , const N: usize> Iterator for ConstChunksExact<'a, T, {rem}> { - type Item = &'a [T; N]; -} - -fn main() { - let slice = &[1i32, 2, 3, 4, 5, 6, 7, 7, 9, 1i32]; - - let mut iter = [[1, 2, 3], [4, 5, 6], [7, 8 ,9]].iter(); - - for a in slice.const_chunks_exact::<3>() { - assert_eq!(a, iter.next().unwrap()); - } -} diff --git a/tests/crashes/123141.rs b/tests/crashes/123141.rs index 99dfee7670ef..07181387e045 100644 --- a/tests/crashes/123141.rs +++ b/tests/crashes/123141.rs @@ -1,22 +1,23 @@ //@ known-bug: #123141 -trait ConstChunksExactTrait { - fn const_chunks_exact(&self) -> ConstChunksExact<'_, T, { N }>; + +trait Trait { + fn next(self) -> Self::Item; + type Item; } -impl ConstChunksExactTrait for [T] {} +struct Foo(T); -struct ConstChunksExact<'a, T: 'a, const N: usize> {} +impl Trait for Foo { + type Item = Foo; + fn next(self) -> Self::Item { + loop {} + } +} -impl<'a, T, const N: usize> Iterator for ConstChunksExact<'a, T, { rem }> { - type Item = &'a [T; N]; +fn opaque() -> impl Trait { + Foo::<_>(10_u32) } fn main() { - let slice = &[1i32, 2, 3, 4, 5, 6, 7, 7, 9, 1i32]; - - let mut iter = [[1, 2, 3], [4, 5, 6], [7, 8, 9]].iter(); - - for a in slice.const_chunks_exact::<3>() { - assert_eq!(a, iter.next().unwrap()); - } + opaque().next(); } diff --git a/tests/crashes/123255.rs b/tests/crashes/123255.rs new file mode 100644 index 000000000000..a94a2a0422e7 --- /dev/null +++ b/tests/crashes/123255.rs @@ -0,0 +1,13 @@ +//@ known-bug: rust-lang/rust#123255 +//@ edition:2021 +#![crate_type = "lib"] + +pub fn a() {} + +mod handlers { + pub struct C(&()); + pub fn c() -> impl Fn() -> C { + let a1 = (); + || C((crate::a(), a1).into()) + } +} diff --git a/tests/crashes/123276.rs b/tests/crashes/123276.rs new file mode 100644 index 000000000000..d2246f595838 --- /dev/null +++ b/tests/crashes/123276.rs @@ -0,0 +1,25 @@ +//@ known-bug: rust-lang/rust#123276 +//@ edition:2021 + +async fn create_task() { + _ = Some(async { bind(documentation_filter()) }); +} + +async fn bind>(_: F) {} + +fn documentation_filter() -> impl Filter { + AndThen +} + +trait Filter { + type Future; +} + +struct AndThen; + +impl Filter for AndThen +where + Foo: Filter, +{ + type Future = (); +} diff --git a/tests/crashes/123887.rs b/tests/crashes/123887.rs new file mode 100644 index 000000000000..68e2fb0325c3 --- /dev/null +++ b/tests/crashes/123887.rs @@ -0,0 +1,15 @@ +//@ known-bug: rust-lang/rust#123887 +//@ compile-flags: -Clink-dead-code + +#![feature(extern_types)] +#![feature(unsized_fn_params)] + +extern "C" { + pub type ExternType; +} + +impl ExternType { + pub fn f(self) {} +} + +pub fn main() {} diff --git a/tests/crashes/123917.rs b/tests/crashes/123917.rs deleted file mode 100644 index 66e754606622..000000000000 --- a/tests/crashes/123917.rs +++ /dev/null @@ -1,41 +0,0 @@ -//@ known-bug: #123917 -//@ compile-flags: -Zmir-opt-level=5 -Zpolymorphize=on - -use std::marker::PhantomData; - -pub struct Id<'id>(); - -pub struct Item<'life, T> { - data: T, -} - -pub struct Token<'life, 'borrow, 'compact, 'reborrow, T> -where - 'life: 'reborrow, - T: Tokenize, -{ - ptr: *mut ::Tokenized, - ptr: core::ptr::NonNull, - _phantom: PhantomData>, -} - -impl<'life> Arena<'life> { - pub fn tokenize<'before, 'compact, 'borrow, 'reborrow, T, U>( - item: Item<'life, &'before mut T>, - ) -> Token<'life, 'borrow, 'compact, 'reborrow, U> - where - T: Tokenize<'life, 'borrow, 'compact, 'reborrow, Untokenized = U>, - T::Untokenized: Tokenize<'life, 'borrow, 'compact, 'reborrow>, - { - let dst = item.data as *mut T as *mut T::Tokenized; - Token { - ptr: core::ptr::NonNull::new(dst as *mut _).unwrap(), - _phantom: PhantomData, - } - } -} - -pub trait Tokenize { - type Tokenized; - type Untokenized; -} diff --git a/tests/crashes/124151.rs b/tests/crashes/124151.rs deleted file mode 100644 index 5e55ac2aa943..000000000000 --- a/tests/crashes/124151.rs +++ /dev/null @@ -1,14 +0,0 @@ -//@ known-bug: #124151 -#![feature(generic_const_exprs)] - -use std::ops::Add; - -pub struct Dimension; - -pub struct Quantity(S); - -impl Add for Quantity {} - -pub fn add(x: Quantity) -> Quantity { - x + y -} diff --git a/tests/crashes/124342.rs b/tests/crashes/124342.rs deleted file mode 100644 index ae51b3db96f5..000000000000 --- a/tests/crashes/124342.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ known-bug: #124342 -trait Trait2 : Trait { - reuse <() as Trait>::async { - (async || {}).await; - }; -} diff --git a/tests/crashes/124347.rs b/tests/crashes/124347.rs deleted file mode 100644 index d2bc555fe1c7..000000000000 --- a/tests/crashes/124347.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ known-bug: #124347 -trait Trait: ToReuse { - reuse Trait::lolno { &self.0 }; -} diff --git a/tests/crashes/124348.rs b/tests/crashes/124348.rs deleted file mode 100644 index 554f383026cb..000000000000 --- a/tests/crashes/124348.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ known-bug: #124348 -enum Eek { - TheConst, - UnusedByTheConst(Sum), -} - -const EEK_ZERO: &[Eek] = &[]; diff --git a/tests/crashes/124490.rs b/tests/crashes/124490.rs deleted file mode 100644 index 9f605c32cf26..000000000000 --- a/tests/crashes/124490.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ known-bug: rust-lang/rust#124490 -use io::{self as std}; -use std::collections::{self as io}; - -mod a { - pub mod b { - pub mod c {} - } -} - -use a::*; - -use b::c; -use c as b; - -fn main() {} diff --git a/tests/crashes/124552.rs b/tests/crashes/124552.rs deleted file mode 100644 index 5320ce278430..000000000000 --- a/tests/crashes/124552.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ known-bug: rust-lang/rust#124552 - -struct B; - -struct Foo { - b: u32, - b: B, -} - -static BAR: Foo = Foo { b: B }; - -fn main() {} diff --git a/tests/crashes/124583.rs b/tests/crashes/124583.rs deleted file mode 100644 index ffd9d7521add..000000000000 --- a/tests/crashes/124583.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ known-bug: rust-lang/rust#124583 - -fn main() { - let _ = -(-0.0f16); -} diff --git a/tests/crashes/124857.rs b/tests/crashes/124857.rs new file mode 100644 index 000000000000..4b952fd64ccf --- /dev/null +++ b/tests/crashes/124857.rs @@ -0,0 +1,11 @@ +//@ known-bug: rust-lang/rust#124857 +//@ compile-flags: -Znext-solver=coherence + +#![feature(effects)] + +#[const_trait] +trait Foo {} + +impl const Foo for i32 {} + +impl const Foo for T where T: ~const Foo {} diff --git a/tests/crashes/124894.rs b/tests/crashes/124894.rs new file mode 100644 index 000000000000..230cf4a89c15 --- /dev/null +++ b/tests/crashes/124894.rs @@ -0,0 +1,11 @@ +//@ known-bug: rust-lang/rust#124894 +//@ compile-flags: -Znext-solver=coherence + +#![feature(generic_const_exprs)] + +pub trait IsTrue {} +impl IsZST for T where (): IsTrue<{ std::mem::size_of::() == 0 }> {} + +pub trait IsZST {} + +impl IsZST for IsZST {} diff --git a/tests/crashes/125014.rs b/tests/crashes/125014.rs new file mode 100644 index 000000000000..b29042ee5983 --- /dev/null +++ b/tests/crashes/125014.rs @@ -0,0 +1,17 @@ +//@ known-bug: rust-lang/rust#125014 +//@ compile-flags: -Znext-solver=coherence +#![feature(specialization)] + +trait Foo {} + +impl Foo for ::Output {} + +impl Foo for u32 {} + +trait Assoc { + type Output; +} +impl Output for u32 {} +impl Assoc for ::Output { + default type Output = bool; +} diff --git a/tests/crashes/125059.rs b/tests/crashes/125059.rs new file mode 100644 index 000000000000..7e9f7414816e --- /dev/null +++ b/tests/crashes/125059.rs @@ -0,0 +1,12 @@ +//@ known-bug: rust-lang/rust#125059 +#![feature(deref_patterns)] +#![allow(incomplete_features)] + +fn simple_vec(vec: Vec) -> u32 { + (|| match Vec::::new() { + deref!([]) => 100, + _ => 2000, + })() +} + +fn main() {} diff --git a/tests/crashes/125099.rs b/tests/crashes/125099.rs new file mode 100644 index 000000000000..bfc8c8fdcf68 --- /dev/null +++ b/tests/crashes/125099.rs @@ -0,0 +1,24 @@ +//@ known-bug: rust-lang/rust#125099 + +pub trait ContFn: Fn(T) -> Self::Future { + type Future; +} +impl ContFn for F +where + F: Fn(T), +{ + type Future = (); +} + +pub trait SeqHandler { + type Requires; + fn process>() -> impl Sized; +} + +pub struct ConvertToU64; +impl SeqHandler for ConvertToU64 { + type Requires = u64; + fn process>() -> impl Sized {} +} + +fn main() {} diff --git a/tests/crashes/125155.rs b/tests/crashes/125155.rs new file mode 100644 index 000000000000..165061d4b529 --- /dev/null +++ b/tests/crashes/125155.rs @@ -0,0 +1,17 @@ +//@ known-bug: rust-lang/rust#125155 + +enum NestedEnum { + First, + Second, + Third +} +enum Enum { + Variant2(Option<*mut &'a &'b ()>) +} + + +fn foo(x: Enum) -> isize { + match x { + Enum::Variant2(NestedEnum::Third) => 4, + } +} diff --git a/tests/crashes/125185.rs b/tests/crashes/125185.rs new file mode 100644 index 000000000000..8693d6c7662b --- /dev/null +++ b/tests/crashes/125185.rs @@ -0,0 +1,16 @@ +//@ known-bug: rust-lang/rust#125185 +//@ compile-flags: -Zvalidate-mir + +type Foo = impl Send; + +struct A; + +const VALUE: Foo = value(); + +fn test(foo: Foo<'a>, f: impl for<'b> FnMut()) { + match VALUE { + 0 | 0 => {} + + _ => (), + } +} diff --git a/tests/crashes/125249.rs b/tests/crashes/125249.rs new file mode 100644 index 000000000000..18196d7b34fe --- /dev/null +++ b/tests/crashes/125249.rs @@ -0,0 +1,8 @@ +//@ known-bug: rust-lang/rust#125185 +#![feature(return_position_impl_trait_in_trait, return_type_notation)] + +trait IntFactory { + fn stream(&self) -> impl IntFactory + Send>; +} + +pub fn main() {} diff --git a/tests/crashes/125323.rs b/tests/crashes/125323.rs new file mode 100644 index 000000000000..180b7bbad097 --- /dev/null +++ b/tests/crashes/125323.rs @@ -0,0 +1,6 @@ +//@ known-bug: rust-lang/rust#125323 +fn main() { + for _ in 0..0 { + [(); loop {}]; + } +} diff --git a/tests/crashes/125476.rs b/tests/crashes/125476.rs new file mode 100644 index 000000000000..aa9a081388d9 --- /dev/null +++ b/tests/crashes/125476.rs @@ -0,0 +1,4 @@ +//@ known-bug: rust-lang/rust#125476 +//@ only-x86_64 +pub struct Data([u8; usize::MAX >> 16]); +const _: &'static [Data] = &[]; diff --git a/tests/crashes/125512.rs b/tests/crashes/125512.rs new file mode 100644 index 000000000000..1672b24a1143 --- /dev/null +++ b/tests/crashes/125512.rs @@ -0,0 +1,10 @@ +//@ known-bug: rust-lang/rust#125512 +//@ edition:2021 +#![feature(object_safe_for_dispatch)] +trait B { + fn f(a: A) -> A; +} +trait A { + fn concrete(b: B) -> B; +} +fn main() {} diff --git a/tests/crashes/125553.rs b/tests/crashes/125553.rs new file mode 100644 index 000000000000..142c06775bb8 --- /dev/null +++ b/tests/crashes/125553.rs @@ -0,0 +1,15 @@ +//@ known-bug: rust-lang/rust#125553 +//@ edition:2021 + +#[derive(Copy, Clone)] +struct Foo((u32, u32)); + +fn main() { + type T = impl Copy(Copy, Clone) + let foo: T = Foo((1u32, 1u32)); + let x = move || { + let derive = move || { + let Foo((a, b)) = foo; + }; + }; +} diff --git a/tests/crashes/125655.rs b/tests/crashes/125655.rs new file mode 100644 index 000000000000..fbf92ca22be7 --- /dev/null +++ b/tests/crashes/125655.rs @@ -0,0 +1,8 @@ +//@ known-bug: rust-lang/rust#125655 + +fn main() { + static foo: dyn Fn() -> u32 = || -> u32 { + ... + 0 + }; +} diff --git a/tests/crashes/125680.rs b/tests/crashes/125680.rs new file mode 100644 index 000000000000..09f4f318bddd --- /dev/null +++ b/tests/crashes/125680.rs @@ -0,0 +1,15 @@ +//@ known-bug: rust-lang/rust#125680 +//@ edition:2021 + +#![feature(generic_const_exprs)] + +use core::fmt::Debug; + +struct Inline +where + [(); std::mem::offset_of!((T,), 0)]:, +{} + +fn main() { + let dst = Inline::::new(0); // BANG! +} diff --git a/tests/crashes/125758.rs b/tests/crashes/125758.rs new file mode 100644 index 000000000000..86c3b80abab9 --- /dev/null +++ b/tests/crashes/125758.rs @@ -0,0 +1,26 @@ +//@ known-bug: rust-lang/rust#125758 +#![feature(impl_trait_in_assoc_type)] + +trait Trait: Sized { + type Assoc2; +} + +impl Trait for Bar { + type Assoc2 = impl std::fmt::Debug; +} + +struct Foo { + field: ::Assoc2, +} + +enum Bar { + C = 42, + D = 99, +} + +static BAR: u8 = 42; + +static FOO2: (&Foo, &::Assoc2) = + unsafe { (std::mem::transmute(&BAR), std::mem::transmute(&BAR)) }; + +fn main() {} diff --git a/tests/crashes/125768.rs b/tests/crashes/125768.rs new file mode 100644 index 000000000000..3c04431baa0a --- /dev/null +++ b/tests/crashes/125768.rs @@ -0,0 +1,15 @@ +//@ known-bug: rust-lang/rust#125768 + +#![feature(generic_const_exprs)] + +struct Outer(); +impl Outer +where + [(); A + (B * 2)]:, +{ + fn o() -> Union {} +} + +fn main() { + Outer::<1, 1>::o(); +} diff --git a/tests/crashes/121585-1.rs b/tests/crashes/125769.rs similarity index 88% rename from tests/crashes/121585-1.rs rename to tests/crashes/125769.rs index 2a4638efcabd..da19458f7ca7 100644 --- a/tests/crashes/121585-1.rs +++ b/tests/crashes/125769.rs @@ -1,4 +1,5 @@ -//@ known-bug: #121585 +//@ known-bug: rust-lang/rust#125769 + #![feature(generic_const_exprs)] trait Trait {} diff --git a/tests/crashes/125772.rs b/tests/crashes/125772.rs new file mode 100644 index 000000000000..2965cfc9e7c3 --- /dev/null +++ b/tests/crashes/125772.rs @@ -0,0 +1,17 @@ +//@ known-bug: rust-lang/rust#125772 +//@ only-x86_64 +#![feature(generic_const_exprs)] + +struct Outer(); +impl Outer +where + [(); A + (B * 2)]:, +{ + fn i() -> Self { + Self + } +} + +fn main() { + Outer::<1, 1>::o(); +} diff --git a/tests/crashes/125799.rs b/tests/crashes/125799.rs new file mode 100644 index 000000000000..62d5438b4e40 --- /dev/null +++ b/tests/crashes/125799.rs @@ -0,0 +1,22 @@ +//@ known-bug: rust-lang/rust#125799 +//@ only-x86_64 + +trait Trait { + type Assoc; +} + +impl Trait for Vec { + type Assoc = (); +} + +impl Trait for Vec {} + +const BAR: as Trait>::Assoc = 3; + +pub fn main() { + let x: isize = 3; + let _ = match x { + BAR => 2, + _ => 3, + }; +} diff --git a/tests/crashes/125801.rs b/tests/crashes/125801.rs new file mode 100644 index 000000000000..aaa76bd0ba69 --- /dev/null +++ b/tests/crashes/125801.rs @@ -0,0 +1,20 @@ +//@ known-bug: rust-lang/rust#125801 + +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +trait Foo { + type Output; +} + +impl Foo for [u8; 3] { + type Output = [u8; 3]; +} + +static A: <[u8; N] as Foo>::Output = [1, 2, 3]; + +fn main() { + || { + let _ = A[1]; + }; +} diff --git a/tests/crashes/125810.rs b/tests/crashes/125810.rs new file mode 100644 index 000000000000..4a152da8ddf3 --- /dev/null +++ b/tests/crashes/125810.rs @@ -0,0 +1,10 @@ +//@ known-bug: rust-lang/rust#125810 +#![feature(arbitrary_self_types, dispatch_from_dyn)] + +use std::ops::{Deref, DispatchFromDyn}; + +trait Trait + DispatchFromDyn> { + fn MONO_BUF(self: T) -> dyn Trait; +} + +fn main() {} diff --git a/tests/crashes/125841.rs b/tests/crashes/125841.rs new file mode 100644 index 000000000000..e2e61071f1df --- /dev/null +++ b/tests/crashes/125841.rs @@ -0,0 +1,14 @@ +//@ known-bug: rust-lang/rust#125841 +#![feature(non_lifetime_binders)] +fn take(id: impl for Fn(T) -> T) { + id(0); + id(""); +} + +fn take2() -> impl for Fn(T) -> T { + |x| x +} + +fn main() { + take(|x| take2) +} diff --git a/tests/crashes/125843.rs b/tests/crashes/125843.rs new file mode 100644 index 000000000000..8b9a3913c7e6 --- /dev/null +++ b/tests/crashes/125843.rs @@ -0,0 +1,4 @@ +//@ known-bug: rust-lang/rust#125843 +#![feature(non_lifetime_binders)] +trait v0<> {} +fn kind :(v3main impl for v0<'_, v2 = impl v0 + '_>) {} diff --git a/tests/crashes/125874.rs b/tests/crashes/125874.rs new file mode 100644 index 000000000000..6a2713cd7c89 --- /dev/null +++ b/tests/crashes/125874.rs @@ -0,0 +1,22 @@ +//@ known-bug: rust-lang/rust#125874 +pub trait A {} + +pub trait Mirror { + type Assoc: ?Sized; +} +impl Mirror for dyn A { + type Assoc = T; +} + +struct Bar { + foo: ::Assoc, +} + +pub fn main() { + let strct = Bar { foo: 3 }; + + match strct { + Bar { foo: 1, .. } => {} + _ => (), + }; +} diff --git a/tests/crashes/125879.rs b/tests/crashes/125879.rs new file mode 100644 index 000000000000..4318842e4551 --- /dev/null +++ b/tests/crashes/125879.rs @@ -0,0 +1,18 @@ +//@ known-bug: rust-lang/rust#125879 +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +pub type PubAlias0 = PubTy::PrivAssocTy; + +pub struct PubTy; +impl PubTy { + type PrivAssocTy = (); +} + +pub struct S(pub PubAlias0); + +pub unsafe fn foo(a: S) -> S { + a +} + +fn main() {} diff --git a/tests/crashes/125881.rs b/tests/crashes/125881.rs new file mode 100644 index 000000000000..98331d3c9748 --- /dev/null +++ b/tests/crashes/125881.rs @@ -0,0 +1,8 @@ +//@ known-bug: rust-lang/rust#125881 +#![crate_type = "lib"] +#![feature(transmutability)] +#![feature(unboxed_closures,effects)] + +const fn test() -> impl std::mem::BikeshedIntrinsicFrom() { + || {} +} diff --git a/tests/crashes/125888.rs b/tests/crashes/125888.rs new file mode 100644 index 000000000000..ae8f2d6576b7 --- /dev/null +++ b/tests/crashes/125888.rs @@ -0,0 +1,17 @@ +//@ known-bug: rust-lang/rust#125888 +enum NestedEnum { + First, + Second, +} + +enum Enum { + Variant(*const &'a ()), +} + +fn foo(x: Enum) { + match x { + Enum::Variant(NestedEnum::Second) => {} + } +} + +fn main() {} diff --git a/tests/crashes/125957.rs b/tests/crashes/125957.rs new file mode 100644 index 000000000000..e3abe5262eb9 --- /dev/null +++ b/tests/crashes/125957.rs @@ -0,0 +1,20 @@ +//@ known-bug: rust-lang/rust#125957 +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] +#![feature(associated_const_equality)] + +pub struct Equal(); + +pub enum ParseMode { + Raw, +} +pub trait Parse { + const PARSE_MODE: ParseMode; +} +pub trait RenderRaw: Parse {} + +trait GenericVec { + fn unwrap() -> dyn RenderRaw; +} + +fn main() {} diff --git a/tests/crashes/125992.rs b/tests/crashes/125992.rs new file mode 100644 index 000000000000..d78f28ce6de4 --- /dev/null +++ b/tests/crashes/125992.rs @@ -0,0 +1,19 @@ +//@ known-bug: rust-lang/rust#125992 +//@ compile-flags: -Zpolonius=next + +#![feature(inherent_associated_types)] + +type Function = for<'a> fn(&'a i32) -> S<'a>::P; + +struct S<'a>(&'a ()); + +impl<'a> S { + type P = &'a i32; +} + +fn ret_ref_local<'e>() -> &'e i32 { + let f: Function = |x| x; + + let local = 0; + f(&local) +} diff --git a/tests/crashes/126148.rs b/tests/crashes/126148.rs new file mode 100644 index 000000000000..79f8887b4015 --- /dev/null +++ b/tests/crashes/126148.rs @@ -0,0 +1,23 @@ +//@ known-bug: rust-lang/rust#126148 + +#![feature(effects)] +use std::ops::{FromResidual, Try}; + +struct TryMe; +struct Error; + +impl const FromResidual for TryMe {} + +impl const Try for TryMe { + type Output = (); + type Residual = Error; +} + +const fn t() -> TryMe { + TryMe?; + TryMe +} + +const _: () = { + t(); +}; diff --git a/tests/crashes/126182.rs b/tests/crashes/126182.rs new file mode 100644 index 000000000000..2219a6cb5fa1 --- /dev/null +++ b/tests/crashes/126182.rs @@ -0,0 +1,10 @@ +//@ known-bug: rust-lang/rust#126182 + +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +struct Cond; + +struct Thing>(T); + +impl Thing {} diff --git a/tests/crashes/126267.rs b/tests/crashes/126267.rs new file mode 100644 index 000000000000..c0604b90a670 --- /dev/null +++ b/tests/crashes/126267.rs @@ -0,0 +1,30 @@ +//@ known-bug: rust-lang/rust#126267 + +#![feature(transmutability)] +#![crate_type = "lib"] + +pub enum ApiError {} +pub struct TokioError { + b: bool, +} +pub enum Error { + Api { source: ApiError }, + Ethereum, + Tokio { source: TokioError }, +} + +mod assert { + use std::mem::BikeshedIntrinsicFrom; + + pub fn is_transmutable() + where + Dst: BikeshedIntrinsicFrom, // safety is NOT assumed + { + } +} + +fn test() { + struct Src; + type Dst = Error; + assert::is_transmutable::(); +} diff --git a/tests/crashes/126269.rs b/tests/crashes/126269.rs new file mode 100644 index 000000000000..ca4b76eb930d --- /dev/null +++ b/tests/crashes/126269.rs @@ -0,0 +1,12 @@ +//@ known-bug: rust-lang/rust#126269 +#![feature(coerce_unsized)] + +pub enum Foo { + Bar([T; usize::MAX]), +} + +use std::ops::CoerceUnsized; + +impl CoerceUnsized for T {} + +fn main() {} diff --git a/tests/crashes/126272.rs b/tests/crashes/126272.rs new file mode 100644 index 000000000000..3412c7d8f0f4 --- /dev/null +++ b/tests/crashes/126272.rs @@ -0,0 +1,28 @@ +//@ known-bug: rust-lang/rust#126272 + +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +#[derive(Debug, PartialEq, Eq, ConstParamTy)] +struct Foo { + value: i32, + nested: &'static Bar, +} + +#[derive(Debug, PartialEq, Eq, ConstParamTy)] +struct Bar(T); + +struct Test; + +fn main() { + let x: Test< + { + Foo { + value: 3, + nested: &Bar(4), + } + }, + > = Test; +} diff --git a/tests/crashes/126359.rs b/tests/crashes/126359.rs new file mode 100644 index 000000000000..4b28c466b55c --- /dev/null +++ b/tests/crashes/126359.rs @@ -0,0 +1,9 @@ +//@ known-bug: rust-lang/rust#126359 + +struct OppOrder { + arr: [T; N], +} + +fn main() { + let _ = OppOrder::<3, u32> { arr: [0, 0, 0] }; +} diff --git a/tests/crashes/126377.rs b/tests/crashes/126377.rs new file mode 100644 index 000000000000..f8b9b693b655 --- /dev/null +++ b/tests/crashes/126377.rs @@ -0,0 +1,29 @@ +//@ known-bug: rust-lang/rust#126377 + +#![feature(effects)] +#![feature(generic_const_exprs)] + +mod assert { + use std::mem::{Assume, BikeshedIntrinsicFrom}; + + pub fn is_transmutable< + Src, + Dst, + const ASSUME_ALIGNMENT: bool, + const ASSUME_LIFETIMES: bool, + const ASSUME_SAFETY: bool, + const ASSUME_VALIDITY: bool, + >() + where + Dst: BikeshedIntrinsicFrom< + Src, + { } + >, + {} +} + +const fn from_options() -> Assume { + #[repr(C)] struct Src; + #[repr(C)] struct Dst; + assert::is_transmutable::(); +} diff --git a/tests/crashes/126416.rs b/tests/crashes/126416.rs new file mode 100644 index 000000000000..9b6c5169d444 --- /dev/null +++ b/tests/crashes/126416.rs @@ -0,0 +1,20 @@ +//@ known-bug: rust-lang/rust#126416 + +trait Output<'a, T: 'a> { + type Type; +} + +struct Wrapper; + +impl Wrapper { + fn do_something_wrapper(&mut self, _: F) + where + F: for<'a> FnOnce(>::Type), + { + } +} + +fn main() { + let mut wrapper = Wrapper; + wrapper.do_something_wrapper(|value| ()); +} diff --git a/tests/crashes/126521.rs b/tests/crashes/126521.rs new file mode 100644 index 000000000000..0a025aabcf2e --- /dev/null +++ b/tests/crashes/126521.rs @@ -0,0 +1,11 @@ +//@ known-bug: rust-lang/rust#126521 +macro_rules! foo { + ($val:ident) => { + true; + }; +} + +fn main() { + #[expect(semicolon_in_expressions_from_macros)] + let _ = foo!(x); +} diff --git a/tests/debuginfo/collapse-debuginfo-static-external.rs b/tests/debuginfo/collapse-debuginfo-static-external.rs new file mode 100644 index 000000000000..2209bb9bd948 --- /dev/null +++ b/tests/debuginfo/collapse-debuginfo-static-external.rs @@ -0,0 +1,24 @@ +//@ ignore-lldb + +// Test that static debug info is not collapsed with #[collapse_debuginfo(external)] + +//@ compile-flags:-g + +// === GDB TESTS =================================================================================== + +// gdb-command:info line collapse_debuginfo_static_external::FOO +// gdb-check:[...]Line 15[...] + +#[collapse_debuginfo(external)] +macro_rules! decl_foo { + () => { + static FOO: u32 = 0; + }; +} + +decl_foo!(); + +fn main() { + // prevent FOO from getting optimized out + std::hint::black_box(&FOO); +} diff --git a/tests/debuginfo/collapse-debuginfo-static.rs b/tests/debuginfo/collapse-debuginfo-static.rs new file mode 100644 index 000000000000..e6469da4785e --- /dev/null +++ b/tests/debuginfo/collapse-debuginfo-static.rs @@ -0,0 +1,24 @@ +//@ ignore-lldb + +// Test that static debug info is collapsed with #[collapse_debuginfo(yes)] + +//@ compile-flags:-g + +// === GDB TESTS =================================================================================== + +// gdb-command:info line collapse_debuginfo_static::FOO +// gdb-check:[...]Line 19[...] + +#[collapse_debuginfo(yes)] +macro_rules! decl_foo { + () => { + static FOO: u32 = 0; + }; +} + +decl_foo!(); + +fn main() { + // prevent FOO from getting optimized out + std::hint::black_box(&FOO); +} diff --git a/tests/debuginfo/function-names.rs b/tests/debuginfo/function-names.rs index 2b0c2593676b..d9b61e736215 100644 --- a/tests/debuginfo/function-names.rs +++ b/tests/debuginfo/function-names.rs @@ -37,7 +37,7 @@ // Const generic parameter // gdb-command:info functions -q function_names::const_generic_fn.* // gdb-check:[...]static fn function_names::const_generic_fn_bool(); -// gdb-check:[...]static fn function_names::const_generic_fn_non_int<{CONST#ad91263f6d2dd96e}>(); +// gdb-check:[...]static fn function_names::const_generic_fn_non_int<{CONST#a70c39591cb5f53d}>(); // gdb-check:[...]static fn function_names::const_generic_fn_signed_int<-7>(); // gdb-check:[...]static fn function_names::const_generic_fn_unsigned_int<14>(); @@ -76,9 +76,6 @@ // Const generic parameter // cdb-command:x a!function_names::const_generic_fn* // cdb-check:[...] a!function_names::const_generic_fn_bool (void) -// cdb-check:[...] a!function_names::const_generic_fn_non_int (void) -// cdb-check:[...] a!function_names::const_generic_fn_unsigned_int<14> (void) -// cdb-check:[...] a!function_names::const_generic_fn_signed_int<-7> (void) #![allow(unused_variables)] #![feature(omit_gdb_pretty_printer_section)] diff --git a/tests/debuginfo/strings-and-strs.rs b/tests/debuginfo/strings-and-strs.rs index 48d40d167bad..4a6adc2fc533 100644 --- a/tests/debuginfo/strings-and-strs.rs +++ b/tests/debuginfo/strings-and-strs.rs @@ -7,7 +7,7 @@ // gdb-command:run // gdb-command:print plain_string -// gdbr-check:$1 = alloc::string::String {vec: alloc::vec::Vec {buf: alloc::raw_vec::RawVec {ptr: core::ptr::unique::Unique {pointer: core::ptr::non_null::NonNull {pointer: 0x55555555ab80}, _marker: core::marker::PhantomData}, cap: alloc::raw_vec::Cap (5), alloc: alloc::alloc::Global}, len: 5}} +// gdbr-check:$1 = alloc::string::String {vec: alloc::vec::Vec {buf: alloc::raw_vec::RawVec {ptr: core::ptr::unique::Unique {pointer: core::ptr::non_null::NonNull {pointer: 0x[...]}, _marker: core::marker::PhantomData}, cap: alloc::raw_vec::Cap (5), alloc: alloc::alloc::Global}, len: 5}} // gdb-command:print plain_str // gdbr-check:$2 = "Hello" @@ -19,7 +19,7 @@ // gdbr-check:$4 = ("Hello", "World") // gdb-command:print str_in_rc -// gdbr-check:$5 = alloc::rc::Rc<&str, alloc::alloc::Global> {ptr: core::ptr::non_null::NonNull> {pointer: 0x55555555aae0}, phantom: core::marker::PhantomData>, alloc: alloc::alloc::Global} +// gdbr-check:$5 = alloc::rc::Rc<&str, alloc::alloc::Global> {ptr: core::ptr::non_null::NonNull> {pointer: 0x[...]}, phantom: core::marker::PhantomData>, alloc: alloc::alloc::Global} // === LLDB TESTS ================================================================================== diff --git a/tests/debuginfo/unsized.rs b/tests/debuginfo/unsized.rs index f76376de3836..982ab003a2a1 100644 --- a/tests/debuginfo/unsized.rs +++ b/tests/debuginfo/unsized.rs @@ -46,13 +46,13 @@ // cdb-command:dx c // cdb-check:c [Type: ref$ > >] // cdb-check: [+0x000] pointer : 0x[...] [Type: unsized::Foo > *] -// cdb-check: [...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[3]] +// cdb-check: [...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[4]] // cdb-command:dx _box // cdb-check: // cdb-check:_box [Type: alloc::boxed::Box >,alloc::alloc::Global>] // cdb-check:[+0x000] pointer : 0x[...] [Type: unsized::Foo > *] -// cdb-check:[...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[3]] +// cdb-check:[...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[4]] // cdb-command:dx tuple_slice // cdb-check:tuple_slice [Type: ref$ > >] @@ -62,7 +62,7 @@ // cdb-command:dx tuple_dyn // cdb-check:tuple_dyn [Type: ref$ > >] // cdb-check: [+0x000] pointer : 0x[...] [Type: tuple$ > *] -// cdb-check: [...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[3]] +// cdb-check: [...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[4]] #![feature(unsized_tuple_coercion)] #![feature(omit_gdb_pretty_printer_section)] diff --git a/tests/incremental/foreign.rs b/tests/incremental/foreign.rs index cb040fe1296d..1af203e9b77b 100644 --- a/tests/incremental/foreign.rs +++ b/tests/incremental/foreign.rs @@ -1,38 +1,21 @@ // Test what happens we save incremental compilation state that makes // use of foreign items. This used to ICE (#34991). -//@ ignore-sgx no libc - //@ revisions: rpass1 -#![feature(rustc_private)] - -extern crate libc; - use std::ffi::CString; mod mlibc { - use libc::{c_char, c_long, c_longlong}; - extern "C" { - pub fn atol(x: *const c_char) -> c_long; - pub fn atoll(x: *const c_char) -> c_longlong; + // strlen is provided either by an external library or compiler-builtins as a fallback + pub fn strlen(x: *const std::ffi::c_char) -> usize; } } -fn atol(s: String) -> isize { +fn strlen(s: String) -> usize { let c = CString::new(s).unwrap(); - unsafe { mlibc::atol(c.as_ptr()) as isize } -} - -fn atoll(s: String) -> i64 { - let c = CString::new(s).unwrap(); - unsafe { mlibc::atoll(c.as_ptr()) as i64 } + unsafe { mlibc::strlen(c.as_ptr()) } } pub fn main() { - assert_eq!(atol("1024".to_string()) * 10, atol("10240".to_string())); - assert_eq!( - (atoll("11111111111111111".to_string()) * 10), - atoll("111111111111111110".to_string()) - ); + assert_eq!(strlen("1024".to_string()), strlen("2048".to_string())); } diff --git a/tests/mir-opt/address_of.rs b/tests/mir-opt/address_of.rs index 57a317a4a90b..164ab38ebe75 100644 --- a/tests/mir-opt/address_of.rs +++ b/tests/mir-opt/address_of.rs @@ -9,7 +9,7 @@ fn address_of_reborrow() { y as *const [i32; 10]; y as *const dyn Send; y as *const [i32]; - y as *const i32; // This is a cast, not a coercion + y as *const i32; // This is a cast, not a coercion let p: *const _ = y; let p: *const [i32; 10] = y; diff --git a/tests/mir-opt/array_index_is_temporary.rs b/tests/mir-opt/array_index_is_temporary.rs index cd44c31d0004..771fb3771b5c 100644 --- a/tests/mir-opt/array_index_is_temporary.rs +++ b/tests/mir-opt/array_index_is_temporary.rs @@ -9,7 +9,6 @@ unsafe fn foo(z: *mut usize) -> u32 { 99 } - // EMIT_MIR array_index_is_temporary.main.SimplifyCfg-pre-optimizations.after.mir fn main() { // CHECK-LABEL: fn main( diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir index d2a0fb0cb3c3..c0f16ee7ec0b 100644 --- a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir @@ -5,12 +5,15 @@ ty: Coroutine( DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), [ - (), - std::future::ResumeTy, - (), - (), - CoroutineWitness(DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), []), - (), + (), + std::future::ResumeTy, + (), + (), + CoroutineWitness( + DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), + [], + ), + (), ], ), source_info: SourceInfo { @@ -23,12 +26,15 @@ ty: Coroutine( DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), [ - (), - std::future::ResumeTy, - (), - (), - CoroutineWitness(DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), []), - (), + (), + std::future::ResumeTy, + (), + (), + CoroutineWitness( + DefId(0:4 ~ async_await[ccf8]::a::{closure#0}), + [], + ), + (), ], ), source_info: SourceInfo { diff --git a/tests/mir-opt/building/custom/aggregate_exprs.rs b/tests/mir-opt/building/custom/aggregate_exprs.rs index d581886247ff..8985f106817b 100644 --- a/tests/mir-opt/building/custom/aggregate_exprs.rs +++ b/tests/mir-opt/building/custom/aggregate_exprs.rs @@ -7,18 +7,18 @@ use core::intrinsics::mir::*; // EMIT_MIR aggregate_exprs.tuple.built.after.mir #[custom_mir(dialect = "built")] fn tuple() -> (i32, bool) { - mir!( + mir! { { RET = (1, true); Return() } - ) + } } // EMIT_MIR aggregate_exprs.array.built.after.mir #[custom_mir(dialect = "built")] fn array() -> [i32; 2] { - mir!( + mir! { let x: [i32; 2]; let one: i32; { @@ -28,7 +28,7 @@ fn array() -> [i32; 2] { RET = Move(x); Return() } - ) + } } struct Foo { @@ -48,7 +48,7 @@ union Onion { // EMIT_MIR aggregate_exprs.adt.built.after.mir #[custom_mir(dialect = "built")] fn adt() -> Onion { - mir!( + mir! { let one: i32; let x: Foo; let y: Bar; @@ -62,7 +62,7 @@ fn adt() -> Onion { RET = Onion { neon: Field(Variant(y, 0), 1) }; Return() } - ) + } } fn main() { diff --git a/tests/mir-opt/building/custom/arbitrary_let.rs b/tests/mir-opt/building/custom/arbitrary_let.rs index f8ee8504e322..3f251cd81bce 100644 --- a/tests/mir-opt/building/custom/arbitrary_let.rs +++ b/tests/mir-opt/building/custom/arbitrary_let.rs @@ -8,7 +8,7 @@ use core::ptr::{addr_of, addr_of_mut}; // EMIT_MIR arbitrary_let.arbitrary_let.built.after.mir #[custom_mir(dialect = "built")] fn arbitrary_let(x: i32) -> i32 { - mir!( + mir! { { let y = x; Goto(second) @@ -21,7 +21,7 @@ fn arbitrary_let(x: i32) -> i32 { let z = y; Goto(third) } - ) + } } fn main() { diff --git a/tests/mir-opt/building/custom/arrays.rs b/tests/mir-opt/building/custom/arrays.rs index e9a53b7aacb7..4bd6f93e1134 100644 --- a/tests/mir-opt/building/custom/arrays.rs +++ b/tests/mir-opt/building/custom/arrays.rs @@ -7,12 +7,14 @@ use core::intrinsics::mir::*; // EMIT_MIR arrays.arrays.built.after.mir #[custom_mir(dialect = "built")] fn arrays() -> usize { - mir!({ - let x = [5_i32; C]; - let c = Len(x); - RET = c; - Return() - }) + mir! { + { + let x = [5_i32; C]; + let c = Len(x); + RET = c; + Return() + } + } } fn main() { diff --git a/tests/mir-opt/building/custom/as_cast.rs b/tests/mir-opt/building/custom/as_cast.rs index 92aea64db07f..908d378771d5 100644 --- a/tests/mir-opt/building/custom/as_cast.rs +++ b/tests/mir-opt/building/custom/as_cast.rs @@ -7,34 +7,34 @@ use core::intrinsics::mir::*; // EMIT_MIR as_cast.int_to_int.built.after.mir #[custom_mir(dialect = "built")] fn int_to_int(x: u32) -> i32 { - mir!( + mir! { { RET = x as i32; Return() } - ) + } } // EMIT_MIR as_cast.float_to_int.built.after.mir #[custom_mir(dialect = "built")] fn float_to_int(x: f32) -> i32 { - mir!( + mir! { { RET = x as i32; Return() } - ) + } } // EMIT_MIR as_cast.int_to_ptr.built.after.mir #[custom_mir(dialect = "built")] fn int_to_ptr(x: usize) -> *const i32 { - mir!( + mir! { { RET = x as *const i32; Return() } - ) + } } fn main() { diff --git a/tests/mir-opt/building/custom/assume.rs b/tests/mir-opt/building/custom/assume.rs index a477e12f0e03..4a55617136a1 100644 --- a/tests/mir-opt/building/custom/assume.rs +++ b/tests/mir-opt/building/custom/assume.rs @@ -7,34 +7,34 @@ use core::intrinsics::mir::*; // EMIT_MIR assume.assume_local.built.after.mir #[custom_mir(dialect = "built")] fn assume_local(x: bool) { - mir!( + mir! { { Assume(x); Return() } - ) + } } // EMIT_MIR assume.assume_place.built.after.mir #[custom_mir(dialect = "built")] fn assume_place(p: (bool, u8)) { - mir!( + mir! { { Assume(p.0); Return() } - ) + } } // EMIT_MIR assume.assume_constant.built.after.mir #[custom_mir(dialect = "built")] fn assume_constant() { - mir!( + mir! { { Assume(true); Return() } - ) + } } fn main() { diff --git a/tests/mir-opt/building/custom/composite_return.rs b/tests/mir-opt/building/custom/composite_return.rs index 33c903fa0f89..0cf2cc8d1890 100644 --- a/tests/mir-opt/building/custom/composite_return.rs +++ b/tests/mir-opt/building/custom/composite_return.rs @@ -7,14 +7,14 @@ use core::intrinsics::mir::*; // EMIT_MIR composite_return.tuple.built.after.mir #[custom_mir(dialect = "runtime", phase = "optimized")] fn tuple() -> (i32, bool) { - mir!( + mir! { type RET = (i32, bool); { RET.0 = 1; RET.1 = true; Return() } - ) + } } fn main() { diff --git a/tests/mir-opt/building/custom/consts.rs b/tests/mir-opt/building/custom/consts.rs index 1a410177fa54..c64709082dc3 100644 --- a/tests/mir-opt/building/custom/consts.rs +++ b/tests/mir-opt/building/custom/consts.rs @@ -9,14 +9,16 @@ const D: i32 = 5; // EMIT_MIR consts.consts.built.after.mir #[custom_mir(dialect = "built")] fn consts() { - mir!({ - let _a = 5_u8; - let _b = const { 5_i8 }; - let _c = C; - let _d = D; - let _e = consts::<10>; - Return() - }) + mir! { + { + let _a = 5_u8; + let _b = const { 5_i8 }; + let _c = C; + let _d = D; + let _e = consts::<10>; + Return() + } + } } static S: i32 = 0x05050505; @@ -24,11 +26,13 @@ static mut T: i32 = 0x0a0a0a0a; // EMIT_MIR consts.statics.built.after.mir #[custom_mir(dialect = "built")] fn statics() { - mir!({ - let _a: &i32 = Static(S); - let _b: *mut i32 = StaticMut(T); - Return() - }) + mir! { + { + let _a: &i32 = Static(S); + let _b: *mut i32 = StaticMut(T); + Return() + } + } } fn main() { diff --git a/tests/mir-opt/building/custom/debuginfo.rs b/tests/mir-opt/building/custom/debuginfo.rs index 3671a1ef0619..5ab83fd4214b 100644 --- a/tests/mir-opt/building/custom/debuginfo.rs +++ b/tests/mir-opt/building/custom/debuginfo.rs @@ -7,60 +7,62 @@ use core::intrinsics::mir::*; // EMIT_MIR debuginfo.pointee.built.after.mir #[custom_mir(dialect = "built")] fn pointee(opt: &mut Option) { - mir!( + mir! { debug foo => Field::(Variant(*opt, 1), 0); { Return() } - ) + } } // EMIT_MIR debuginfo.numbered.built.after.mir #[custom_mir(dialect = "analysis", phase = "post-cleanup")] fn numbered(i: (u32, i32)) { - mir!( + mir! { debug first => i.0; debug second => i.0; { Return() } - ) + } } -struct S { x: f32 } +struct S { + x: f32, +} // EMIT_MIR debuginfo.structured.built.after.mir #[custom_mir(dialect = "analysis", phase = "post-cleanup")] fn structured(i: S) { - mir!( + mir! { debug x => i.x; { Return() } - ) + } } // EMIT_MIR debuginfo.variant.built.after.mir #[custom_mir(dialect = "built")] fn variant(opt: Option) { - mir!( + mir! { debug inner => Field::(Variant(opt, 1), 0); { Return() } - ) + } } // EMIT_MIR debuginfo.variant_deref.built.after.mir #[custom_mir(dialect = "built")] fn variant_deref(opt: Option<&i32>) { - mir!( + mir! { debug pointer => Field::<&i32>(Variant(opt, 1), 0); debug deref => *Field::<&i32>(Variant(opt, 1), 0); { Return() } - ) + } } fn main() { diff --git a/tests/mir-opt/building/custom/enums.rs b/tests/mir-opt/building/custom/enums.rs index 6aab1503c0a3..88ec228986ab 100644 --- a/tests/mir-opt/building/custom/enums.rs +++ b/tests/mir-opt/building/custom/enums.rs @@ -7,7 +7,7 @@ use core::intrinsics::mir::*; // EMIT_MIR enums.switch_bool.built.after.mir #[custom_mir(dialect = "built")] pub fn switch_bool(b: bool) -> u32 { - mir!( + mir! { { match b { true => t, @@ -25,13 +25,13 @@ pub fn switch_bool(b: bool) -> u32 { RET = 10; Return() } - ) + } } // EMIT_MIR enums.switch_option.built.after.mir #[custom_mir(dialect = "built")] pub fn switch_option(option: Option<()>) -> bool { - mir!( + mir! { { let discr = Discriminant(option); match discr { @@ -50,7 +50,7 @@ pub fn switch_option(option: Option<()>) -> bool { RET = true; Return() } - ) + } } #[repr(u8)] @@ -62,7 +62,7 @@ enum Bool { // EMIT_MIR enums.switch_option_repr.built.after.mir #[custom_mir(dialect = "built")] fn switch_option_repr(option: Bool) -> bool { - mir!( + mir! { { let discr = Discriminant(option); match discr { @@ -80,26 +80,30 @@ fn switch_option_repr(option: Bool) -> bool { RET = false; Return() } - ) + } } // EMIT_MIR enums.set_discr.built.after.mir #[custom_mir(dialect = "runtime", phase = "initial")] fn set_discr(option: &mut Option<()>) { - mir!({ - Deinit(*option); - SetDiscriminant(*option, 0); - Return() - }) + mir! { + { + Deinit(*option); + SetDiscriminant(*option, 0); + Return() + } + } } // EMIT_MIR enums.set_discr_repr.built.after.mir #[custom_mir(dialect = "runtime", phase = "initial")] fn set_discr_repr(b: &mut Bool) { - mir!({ - SetDiscriminant(*b, 0); - Return() - }) + mir! { + { + SetDiscriminant(*b, 0); + Return() + } + } } fn main() { diff --git a/tests/mir-opt/building/custom/operators.f.built.after.mir b/tests/mir-opt/building/custom/operators.f.built.after.mir index 33eb6b720e87..cac82f7b3ea3 100644 --- a/tests/mir-opt/building/custom/operators.f.built.after.mir +++ b/tests/mir-opt/building/custom/operators.f.built.after.mir @@ -21,7 +21,7 @@ fn f(_1: i32, _2: bool) -> i32 { _2 = Le(_1, _1); _2 = Ge(_1, _1); _2 = Gt(_1, _1); - _3 = CheckedAdd(_1, _1); + _3 = AddWithOverflow(_1, _1); _2 = (_3.1: bool); _1 = (_3.0: i32); _0 = _1; diff --git a/tests/mir-opt/building/custom/operators.g.runtime.after.mir b/tests/mir-opt/building/custom/operators.g.runtime.after.mir new file mode 100644 index 000000000000..a0ad7d0f93f4 --- /dev/null +++ b/tests/mir-opt/building/custom/operators.g.runtime.after.mir @@ -0,0 +1,13 @@ +// MIR for `g` after runtime + +fn g(_1: *const i32, _2: *const [i32]) -> () { + let mut _0: (); + let mut _3: (); + let mut _4: usize; + + bb0: { + _3 = PtrMetadata(_1); + _4 = PtrMetadata(_2); + return; + } +} diff --git a/tests/mir-opt/building/custom/operators.rs b/tests/mir-opt/building/custom/operators.rs index eb97bcc73b7e..07abf2d96517 100644 --- a/tests/mir-opt/building/custom/operators.rs +++ b/tests/mir-opt/building/custom/operators.rs @@ -6,27 +6,41 @@ use std::intrinsics::mir::*; // EMIT_MIR operators.f.built.after.mir #[custom_mir(dialect = "built")] pub fn f(a: i32, b: bool) -> i32 { - mir!({ - a = -a; - b = !b; - a = a + a; - a = a - a; - a = a * a; - a = a / a; - a = a % a; - a = a ^ a; - a = a & a; - a = a << a; - a = a >> a; - b = a == a; - b = a < a; - b = a <= a; - b = a >= a; - b = a > a; - let res = Checked(a + a); - b = res.1; - a = res.0; - RET = a; - Return() - }) + mir! { + { + a = -a; + b = !b; + a = a + a; + a = a - a; + a = a * a; + a = a / a; + a = a % a; + a = a ^ a; + a = a & a; + a = a << a; + a = a >> a; + b = a == a; + b = a < a; + b = a <= a; + b = a >= a; + b = a > a; + let res = Checked(a + a); + b = res.1; + a = res.0; + RET = a; + Return() + } + } +} + +// EMIT_MIR operators.g.runtime.after.mir +#[custom_mir(dialect = "runtime")] +pub fn g(p: *const i32, q: *const [i32]) { + mir! { + { + let a = PtrMetadata(p); + let b = PtrMetadata(q); + Return() + } + } } diff --git a/tests/mir-opt/building/custom/projections.rs b/tests/mir-opt/building/custom/projections.rs index ac23fe59097f..0250b9b84b62 100644 --- a/tests/mir-opt/building/custom/projections.rs +++ b/tests/mir-opt/building/custom/projections.rs @@ -12,74 +12,84 @@ union U { // EMIT_MIR projections.unions.built.after.mir #[custom_mir(dialect = "built")] fn unions(u: U) -> i32 { - mir!({ - RET = u.a; - Return() - }) + mir! { + { + RET = u.a; + Return() + } + } } // EMIT_MIR projections.tuples.built.after.mir #[custom_mir(dialect = "analysis", phase = "post-cleanup")] fn tuples(i: (u32, i32)) -> (u32, i32) { - mir!( + mir! { type RET = (u32, i32); { RET.0 = i.0; RET.1 = i.1; Return() } - ) + } } // EMIT_MIR projections.unwrap.built.after.mir #[custom_mir(dialect = "built")] fn unwrap(opt: Option) -> i32 { - mir!({ - RET = Field(Variant(opt, 1), 0); - Return() - }) + mir! { + { + RET = Field(Variant(opt, 1), 0); + Return() + } + } } // EMIT_MIR projections.unwrap_deref.built.after.mir #[custom_mir(dialect = "built")] fn unwrap_deref(opt: Option<&i32>) -> i32 { - mir!({ - RET = *Field::<&i32>(Variant(opt, 1), 0); - Return() - }) + mir! { + { + RET = *Field::<&i32>(Variant(opt, 1), 0); + Return() + } + } } // EMIT_MIR projections.set.built.after.mir #[custom_mir(dialect = "built")] fn set(opt: &mut Option) { - mir!({ - place!(Field(Variant(*opt, 1), 0)) = 10; - Return() - }) + mir! { + { + place!(Field(Variant(*opt, 1), 0)) = 10; + Return() + } + } } // EMIT_MIR projections.simple_index.built.after.mir #[custom_mir(dialect = "built")] fn simple_index(a: [i32; 10], b: &[i32]) -> i32 { - mir!({ - let temp = 3; - RET = a[temp]; - RET = (*b)[temp]; - Return() - }) + mir! { + { + let temp = 3; + RET = a[temp]; + RET = (*b)[temp]; + Return() + } + } } // EMIT_MIR projections.copy_for_deref.built.after.mir #[custom_mir(dialect = "runtime", phase = "initial")] fn copy_for_deref(x: (&i32, i32)) -> i32 { - mir!( + mir! { let temp: &i32; { temp = CopyForDeref(x.0); RET = *temp; Return() } - ) + } } fn main() { diff --git a/tests/mir-opt/building/custom/references.rs b/tests/mir-opt/building/custom/references.rs index 04afe6e6494b..7ea8f8b4c156 100644 --- a/tests/mir-opt/building/custom/references.rs +++ b/tests/mir-opt/building/custom/references.rs @@ -8,31 +8,29 @@ use core::ptr::{addr_of, addr_of_mut}; // EMIT_MIR references.mut_ref.built.after.mir #[custom_mir(dialect = "runtime", phase = "optimized")] pub fn mut_ref(x: &mut i32) -> &mut i32 { - mir!( + mir! { let t: *mut i32; - { t = addr_of_mut!(*x); RET = &mut *t; Retag(RET); Return() } - ) + } } // EMIT_MIR references.immut_ref.built.after.mir #[custom_mir(dialect = "runtime", phase = "optimized")] pub fn immut_ref(x: &i32) -> &i32 { - mir!( + mir! { let t: *const i32; - { t = addr_of!(*x); RET = & *t; Retag(RET); Return() } - ) + } } // EMIT_MIR references.raw_pointer.built.after.mir @@ -40,19 +38,23 @@ pub fn immut_ref(x: &i32) -> &i32 { pub fn raw_pointer(x: *const i32) -> *const i32 { // Regression test for a bug in which unsafetyck was not correctly turned off for // `dialect = "built"` - mir!({ - RET = addr_of!(*x); - Return() - }) + mir! { + { + RET = addr_of!(*x); + Return() + } + } } // EMIT_MIR references.raw_pointer_offset.built.after.mir #[custom_mir(dialect = "built")] pub fn raw_pointer_offset(x: *const i32) -> *const i32 { - mir!({ - RET = Offset(x, 1_isize); - Return() - }) + mir! { + { + RET = Offset(x, 1_isize); + Return() + } + } } fn main() { diff --git a/tests/mir-opt/building/custom/simple_assign.rs b/tests/mir-opt/building/custom/simple_assign.rs index 8442272291e1..d6d054ab5a38 100644 --- a/tests/mir-opt/building/custom/simple_assign.rs +++ b/tests/mir-opt/building/custom/simple_assign.rs @@ -7,32 +7,32 @@ use core::intrinsics::mir::*; // EMIT_MIR simple_assign.simple.built.after.mir #[custom_mir(dialect = "built")] pub fn simple(x: i32) -> i32 { - mir!( + mir! { let temp1: i32; let temp2: _; - { StorageLive(temp1); temp1 = x; Goto(exit) } - exit = { temp2 = Move(temp1); StorageDead(temp1); RET = temp2; Return() } - ) + } } // EMIT_MIR simple_assign.simple_ref.built.after.mir #[custom_mir(dialect = "built")] pub fn simple_ref(x: &mut i32) -> &mut i32 { - mir!({ - RET = Move(x); - Return() - }) + mir! { + { + RET = Move(x); + Return() + } + } } fn main() { diff --git a/tests/mir-opt/building/custom/terminators.rs b/tests/mir-opt/building/custom/terminators.rs index 01c132cf3e77..a8e0b4b35bf4 100644 --- a/tests/mir-opt/building/custom/terminators.rs +++ b/tests/mir-opt/building/custom/terminators.rs @@ -11,7 +11,7 @@ fn ident(t: T) -> T { // EMIT_MIR terminators.direct_call.built.after.mir #[custom_mir(dialect = "built")] fn direct_call(x: i32) -> i32 { - mir!( + mir! { { Call(RET = ident(x), ReturnTo(retblock), UnwindContinue()) } @@ -19,21 +19,20 @@ fn direct_call(x: i32) -> i32 { retblock = { Return() } - ) + } } // EMIT_MIR terminators.indirect_call.built.after.mir #[custom_mir(dialect = "built")] fn indirect_call(x: i32, f: fn(i32) -> i32) -> i32 { - mir!( + mir! { { Call(RET = f(x), ReturnTo(retblock), UnwindContinue()) } - retblock = { Return() } - ) + } } struct WriteOnDrop<'a>(&'a mut i32, i32); @@ -47,51 +46,47 @@ impl<'a> Drop for WriteOnDrop<'a> { // EMIT_MIR terminators.drop_first.built.after.mir #[custom_mir(dialect = "built")] fn drop_first<'a>(a: WriteOnDrop<'a>, b: WriteOnDrop<'a>) { - mir!( + mir! { { Drop(a, ReturnTo(retblock), UnwindContinue()) } - retblock = { a = Move(b); Return() } - ) + } } // EMIT_MIR terminators.drop_second.built.after.mir #[custom_mir(dialect = "built")] fn drop_second<'a>(a: WriteOnDrop<'a>, b: WriteOnDrop<'a>) { - mir!( + mir! { { Drop(b, ReturnTo(retblock), UnwindContinue()) } - retblock = { Return() } - ) + } } // EMIT_MIR terminators.assert_nonzero.built.after.mir #[custom_mir(dialect = "built")] fn assert_nonzero(a: i32) { - mir!( + mir! { { match a { 0 => unreachable, _ => retblock } } - unreachable = { Unreachable() } - retblock = { Return() } - ) + } } fn main() { diff --git a/tests/mir-opt/building/custom/unwind_action.rs b/tests/mir-opt/building/custom/unwind_action.rs index e3d54c721457..87aab1ea70c6 100644 --- a/tests/mir-opt/building/custom/unwind_action.rs +++ b/tests/mir-opt/building/custom/unwind_action.rs @@ -9,14 +9,14 @@ use core::intrinsics::mir::*; // CHECK-NEXT: a() -> [return: bb1, unwind unreachable]; #[custom_mir(dialect = "runtime", phase = "optimized")] pub fn a() { - mir!( + mir! { { Call(RET = a(), ReturnTo(bb1), UnwindUnreachable()) } bb1 = { Return() } - ) + } } // CHECK-LABEL: fn b() @@ -24,14 +24,14 @@ pub fn a() { // CHECK-NEXT: b() -> [return: bb1, unwind continue]; #[custom_mir(dialect = "runtime", phase = "optimized")] pub fn b() { - mir!( + mir! { { Call(RET = b(), ReturnTo(bb1), UnwindContinue()) } bb1 = { Return() } - ) + } } // CHECK-LABEL: fn c() @@ -39,14 +39,14 @@ pub fn b() { // CHECK-NEXT: c() -> [return: bb1, unwind terminate(abi)]; #[custom_mir(dialect = "runtime", phase = "optimized")] pub fn c() { - mir!( + mir! { { Call(RET = c(), ReturnTo(bb1), UnwindTerminate(ReasonAbi)) } bb1 = { Return() } - ) + } } // CHECK-LABEL: fn d() @@ -54,7 +54,7 @@ pub fn c() { // CHECK-NEXT: d() -> [return: bb1, unwind: bb2]; #[custom_mir(dialect = "runtime", phase = "optimized")] pub fn d() { - mir!( + mir! { { Call(RET = d(), ReturnTo(bb1), UnwindCleanup(bb2)) } @@ -64,5 +64,5 @@ pub fn d() { bb2 (cleanup) = { UnwindResume() } - ) + } } diff --git a/tests/mir-opt/building/custom/unwind_terminate.rs b/tests/mir-opt/building/custom/unwind_terminate.rs index c5374fa7b690..9b495d52387d 100644 --- a/tests/mir-opt/building/custom/unwind_terminate.rs +++ b/tests/mir-opt/building/custom/unwind_terminate.rs @@ -8,14 +8,14 @@ use core::intrinsics::mir::*; // CHECK-NEXT: terminate(abi); #[custom_mir(dialect = "runtime", phase = "optimized")] pub fn f() { - mir!( + mir! { { Return() } bb1(cleanup) = { UnwindTerminate(ReasonAbi) } - ) + } } // CHECK-LABEL: fn g() @@ -23,12 +23,12 @@ pub fn f() { // CHECK-NEXT: terminate(cleanup); #[custom_mir(dialect = "runtime", phase = "optimized")] pub fn g() { - mir!( + mir! { { Return() } bb1(cleanup) = { UnwindTerminate(ReasonInCleanup) } - ) + } } diff --git a/tests/mir-opt/building/enum_cast.rs b/tests/mir-opt/building/enum_cast.rs index df8e397c8fec..7ff0cdbfe8df 100644 --- a/tests/mir-opt/building/enum_cast.rs +++ b/tests/mir-opt/building/enum_cast.rs @@ -5,21 +5,24 @@ // EMIT_MIR enum_cast.far.built.after.mir enum Foo { - A + A, } enum Bar { - A, B + A, + B, } #[repr(u8)] enum Boo { - A, B + A, + B, } #[repr(i16)] enum Far { - A, B + A, + B, } fn foo(foo: Foo) -> usize { @@ -40,7 +43,9 @@ fn far(far: Far) -> isize { // EMIT_MIR enum_cast.droppy.built.after.mir enum Droppy { - A, B, C + A, + B, + C, } impl Drop for Droppy { @@ -82,12 +87,15 @@ fn unsigny(x: UnsignedAroundZero) -> u16 { x as u16 } -enum NotStartingAtZero { A = 4, B = 6, C = 8 } +enum NotStartingAtZero { + A = 4, + B = 6, + C = 8, +} // EMIT_MIR enum_cast.offsetty.built.after.mir fn offsetty(x: NotStartingAtZero) -> u32 { x as u32 } -fn main() { -} +fn main() {} diff --git a/tests/mir-opt/building/logical_or_in_conditional.rs b/tests/mir-opt/building/logical_or_in_conditional.rs index deb841f2b0d5..e6872e6f2ec8 100644 --- a/tests/mir-opt/building/logical_or_in_conditional.rs +++ b/tests/mir-opt/building/logical_or_in_conditional.rs @@ -30,9 +30,13 @@ fn test_or() { // EMIT_MIR logical_or_in_conditional.test_complex.built.after.mir fn test_complex() { - if let E::A(_) = E::f() && ((always_true() && Droppy(0).0 > 0) || Droppy(1).0 > 1) {} + if let E::A(_) = E::f() + && ((always_true() && Droppy(0).0 > 0) || Droppy(1).0 > 1) + {} - if !always_true() && let E::B = E::f() {} + if !always_true() + && let E::B = E::f() + {} } fn main() { diff --git a/tests/mir-opt/building/match/simple_match.rs b/tests/mir-opt/building/match/simple_match.rs index 4f0a3046a061..61c337822c85 100644 --- a/tests/mir-opt/building/match/simple_match.rs +++ b/tests/mir-opt/building/match/simple_match.rs @@ -1,7 +1,6 @@ // skip-filecheck // Test that we don't generate unnecessarily large MIR for very simple matches - // EMIT_MIR simple_match.match_bool.built.after.mir fn match_bool(x: bool) -> usize { match x { diff --git a/tests/mir-opt/building/match/sort_candidates.rs b/tests/mir-opt/building/match/sort_candidates.rs index f207f0b32348..593a975a7a47 100644 --- a/tests/mir-opt/building/match/sort_candidates.rs +++ b/tests/mir-opt/building/match/sort_candidates.rs @@ -5,9 +5,9 @@ fn constant_eq(s: &str, b: bool) -> u32 { // Check that we only test "a" once // CHECK-LABEL: fn constant_eq( - // CHECK: bb0: { - // CHECK: [[a:_.*]] = const "a"; - // CHECK-NOT: {{_.*}} = const "a"; + // CHECK-NOT: const "a" + // CHECK: {{_[0-9]+}} = const "a" as &[u8] (Transmute); + // CHECK-NOT: const "a" match (s, b) { ("a", _) if true => 1, ("b", true) => 2, diff --git a/tests/mir-opt/building/shifts.rs b/tests/mir-opt/building/shifts.rs index 849d7b55f3a0..d7747bb2f784 100644 --- a/tests/mir-opt/building/shifts.rs +++ b/tests/mir-opt/building/shifts.rs @@ -3,19 +3,12 @@ // EMIT_MIR shifts.shift_signed.built.after.mir fn shift_signed(small: i8, big: u128, a: i8, b: i32, c: i128) -> ([i8; 3], [u128; 3]) { - ( - [small >> a, small >> b, small >> c], - [big << a, big << b, big << c], - ) + ([small >> a, small >> b, small >> c], [big << a, big << b, big << c]) } // EMIT_MIR shifts.shift_unsigned.built.after.mir fn shift_unsigned(small: u8, big: i128, a: u8, b: u32, c: u128) -> ([u8; 3], [i128; 3]) { - ( - [small >> a, small >> b, small >> c], - [big << a, big << b, big << c], - ) + ([small >> a, small >> b, small >> c], [big << a, big << b, big << c]) } -fn main() { -} +fn main() {} diff --git a/tests/mir-opt/building/storage_live_dead_in_statics.rs b/tests/mir-opt/building/storage_live_dead_in_statics.rs index 1f5692118545..7cb74acbf060 100644 --- a/tests/mir-opt/building/storage_live_dead_in_statics.rs +++ b/tests/mir-opt/building/storage_live_dead_in_statics.rs @@ -3,6 +3,7 @@ // generate `StorageStart` or `StorageEnd` statements. // EMIT_MIR storage_live_dead_in_statics.XXX.built.after.mir +#[rustfmt::skip] static XXX: &'static Foo = &Foo { tup: "hi", data: &[ @@ -20,13 +21,13 @@ static XXX: &'static Foo = &Foo { (0, 1), (0, 2), (0, 3), (0, 1), (0, 2), (0, 3), (0, 1), (0, 2), (0, 3), - ] + ], }; #[derive(Debug)] struct Foo { tup: &'static str, - data: &'static [(u32, u32)] + data: &'static [(u32, u32)], } fn main() { diff --git a/tests/mir-opt/const_allocation3.rs b/tests/mir-opt/const_allocation3.rs index 46be3e1e36e8..9d2006b6fe80 100644 --- a/tests/mir-opt/const_allocation3.rs +++ b/tests/mir-opt/const_allocation3.rs @@ -7,7 +7,7 @@ fn main() { FOO; } -#[repr(packed)] +#[repr(C, packed)] struct Packed { a: [u8; 28], b: &'static i32, diff --git a/tests/mir-opt/const_debuginfo.main.ConstDebugInfo.diff b/tests/mir-opt/const_debuginfo.main.SingleUseConsts.diff similarity index 79% rename from tests/mir-opt/const_debuginfo.main.ConstDebugInfo.diff rename to tests/mir-opt/const_debuginfo.main.SingleUseConsts.diff index c1529dbee13c..ac33f51984cd 100644 --- a/tests/mir-opt/const_debuginfo.main.ConstDebugInfo.diff +++ b/tests/mir-opt/const_debuginfo.main.SingleUseConsts.diff @@ -1,5 +1,5 @@ -- // MIR for `main` before ConstDebugInfo -+ // MIR for `main` after ConstDebugInfo +- // MIR for `main` before SingleUseConsts ++ // MIR for `main` after SingleUseConsts fn main() -> () { let mut _0: (); @@ -56,39 +56,53 @@ bb0: { nop; - _1 = const 1_u8; +- _1 = const 1_u8; nop; - _2 = const 2_u8; +- _2 = const 2_u8; nop; - _3 = const 3_u8; +- _3 = const 3_u8; ++ nop; ++ nop; ++ nop; StorageLive(_4); StorageLive(_5); StorageLive(_6); - _6 = const 1_u8; +- _6 = const 1_u8; ++ nop; StorageLive(_7); - _7 = const 2_u8; - _5 = const 3_u8; +- _7 = const 2_u8; +- _5 = const 3_u8; ++ nop; ++ nop; StorageDead(_7); StorageDead(_6); StorageLive(_8); - _8 = const 3_u8; - _4 = const 6_u8; +- _8 = const 3_u8; +- _4 = const 6_u8; ++ nop; ++ nop; StorageDead(_8); StorageDead(_5); StorageLive(_9); - _9 = const "hello, world!"; +- _9 = const "hello, world!"; ++ nop; StorageLive(_10); _10 = (const true, const false, const 123_u32); StorageLive(_11); - _11 = const Option::::Some(99_u16); +- _11 = const Option::::Some(99_u16); ++ nop; StorageLive(_12); - _12 = const Point {{ x: 32_u32, y: 32_u32 }}; +- _12 = const Point {{ x: 32_u32, y: 32_u32 }}; ++ nop; StorageLive(_13); nop; - _14 = const 32_u32; +- _14 = const 32_u32; ++ nop; StorageLive(_15); - _15 = const 32_u32; - _13 = const 64_u32; +- _15 = const 32_u32; +- _13 = const 64_u32; ++ nop; ++ nop; StorageDead(_15); nop; _0 = const (); diff --git a/tests/mir-opt/const_debuginfo.rs b/tests/mir-opt/const_debuginfo.rs index b880d7e07a6e..907d7fef0674 100644 --- a/tests/mir-opt/const_debuginfo.rs +++ b/tests/mir-opt/const_debuginfo.rs @@ -1,12 +1,14 @@ -//@ test-mir-pass: ConstDebugInfo +//@ test-mir-pass: SingleUseConsts //@ compile-flags: -C overflow-checks=no -Zmir-enable-passes=+GVN +#![allow(unused)] + struct Point { x: u32, y: u32, } -// EMIT_MIR const_debuginfo.main.ConstDebugInfo.diff +// EMIT_MIR const_debuginfo.main.SingleUseConsts.diff fn main() { // CHECK-LABEL: fn main( // CHECK: debug x => const 1_u8; diff --git a/tests/mir-opt/const_prop/checked_add.main.GVN.panic-abort.diff b/tests/mir-opt/const_prop/checked_add.main.GVN.panic-abort.diff index d5117b2f6384..0e93c167ebc9 100644 --- a/tests/mir-opt/const_prop/checked_add.main.GVN.panic-abort.diff +++ b/tests/mir-opt/const_prop/checked_add.main.GVN.panic-abort.diff @@ -11,7 +11,7 @@ bb0: { StorageLive(_1); -- _2 = CheckedAdd(const 1_u32, const 1_u32); +- _2 = AddWithOverflow(const 1_u32, const 1_u32); - assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 1_u32, const 1_u32) -> [success: bb1, unwind unreachable]; + _2 = const (2_u32, false); + assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 1_u32, const 1_u32) -> [success: bb1, unwind unreachable]; diff --git a/tests/mir-opt/const_prop/checked_add.main.GVN.panic-unwind.diff b/tests/mir-opt/const_prop/checked_add.main.GVN.panic-unwind.diff index 2118d37672c0..589eed5776c9 100644 --- a/tests/mir-opt/const_prop/checked_add.main.GVN.panic-unwind.diff +++ b/tests/mir-opt/const_prop/checked_add.main.GVN.panic-unwind.diff @@ -11,7 +11,7 @@ bb0: { StorageLive(_1); -- _2 = CheckedAdd(const 1_u32, const 1_u32); +- _2 = AddWithOverflow(const 1_u32, const 1_u32); - assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 1_u32, const 1_u32) -> [success: bb1, unwind continue]; + _2 = const (2_u32, false); + assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 1_u32, const 1_u32) -> [success: bb1, unwind continue]; diff --git a/tests/mir-opt/const_prop/control_flow_simplification.rs b/tests/mir-opt/const_prop/control_flow_simplification.rs index 39b5f2898306..64605ca11c2c 100644 --- a/tests/mir-opt/const_prop/control_flow_simplification.rs +++ b/tests/mir-opt/const_prop/control_flow_simplification.rs @@ -11,7 +11,7 @@ impl NeedsDrop for This {} // EMIT_MIR control_flow_simplification.hello.GVN.diff // EMIT_MIR control_flow_simplification.hello.PreCodegen.before.mir -fn hello(){ +fn hello() { if ::NEEDS { panic!() } diff --git a/tests/mir-opt/const_prop/indirect.main.GVN.panic-abort.diff b/tests/mir-opt/const_prop/indirect.main.GVN.panic-abort.diff index 8301a4c1aa86..f24b9755eaea 100644 --- a/tests/mir-opt/const_prop/indirect.main.GVN.panic-abort.diff +++ b/tests/mir-opt/const_prop/indirect.main.GVN.panic-abort.diff @@ -14,7 +14,7 @@ StorageLive(_1); StorageLive(_2); - _2 = const 2_u32 as u8 (IntToInt); -- _3 = CheckedAdd(_2, const 1_u8); +- _3 = AddWithOverflow(_2, const 1_u8); - assert(!move (_3.1: bool), "attempt to compute `{} + {}`, which would overflow", move _2, const 1_u8) -> [success: bb1, unwind unreachable]; + _2 = const 2_u8; + _3 = const (3_u8, false); diff --git a/tests/mir-opt/const_prop/indirect.main.GVN.panic-unwind.diff b/tests/mir-opt/const_prop/indirect.main.GVN.panic-unwind.diff index 8dcbfd2c2c11..44ff313b5328 100644 --- a/tests/mir-opt/const_prop/indirect.main.GVN.panic-unwind.diff +++ b/tests/mir-opt/const_prop/indirect.main.GVN.panic-unwind.diff @@ -14,7 +14,7 @@ StorageLive(_1); StorageLive(_2); - _2 = const 2_u32 as u8 (IntToInt); -- _3 = CheckedAdd(_2, const 1_u8); +- _3 = AddWithOverflow(_2, const 1_u8); - assert(!move (_3.1: bool), "attempt to compute `{} + {}`, which would overflow", move _2, const 1_u8) -> [success: bb1, unwind continue]; + _2 = const 2_u8; + _3 = const (3_u8, false); diff --git a/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-abort.diff b/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-abort.diff index 8c5a0df94f42..de9cb7a47a2b 100644 --- a/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-abort.diff +++ b/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-abort.diff @@ -19,7 +19,7 @@ StorageLive(_3); _3 = const 1_u8; StorageLive(_4); -- _4 = CheckedAdd(_2, _3); +- _4 = AddWithOverflow(_2, _3); - assert(!move (_4.1: bool), "attempt to compute `{} + {}`, which would overflow", _2, _3) -> [success: bb1, unwind unreachable]; + _4 = const (0_u8, true); + assert(!const true, "attempt to compute `{} + {}`, which would overflow", const u8::MAX, const 1_u8) -> [success: bb1, unwind unreachable]; diff --git a/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-unwind.diff b/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-unwind.diff index 7887a8a90727..1f19a13c1e8c 100644 --- a/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-unwind.diff +++ b/tests/mir-opt/const_prop/inherit_overflow.main.GVN.panic-unwind.diff @@ -19,7 +19,7 @@ StorageLive(_3); _3 = const 1_u8; StorageLive(_4); -- _4 = CheckedAdd(_2, _3); +- _4 = AddWithOverflow(_2, _3); - assert(!move (_4.1: bool), "attempt to compute `{} + {}`, which would overflow", _2, _3) -> [success: bb1, unwind continue]; + _4 = const (0_u8, true); + assert(!const true, "attempt to compute `{} + {}`, which would overflow", const u8::MAX, const 1_u8) -> [success: bb1, unwind continue]; diff --git a/tests/mir-opt/const_prop/invalid_constant.rs b/tests/mir-opt/const_prop/invalid_constant.rs index 2b7271f63ff2..91ee36ae2c54 100644 --- a/tests/mir-opt/const_prop/invalid_constant.rs +++ b/tests/mir-opt/const_prop/invalid_constant.rs @@ -8,7 +8,11 @@ #[derive(Copy, Clone)] #[repr(u32)] -enum E { A, B, C } +enum E { + A, + B, + C, +} #[derive(Copy, Clone)] enum Empty {} @@ -39,7 +43,5 @@ fn main() { // A non-UTF-8 string slice. Regression test for #75763 and #78520. struct Str; - let _non_utf8_str: Str::<{ - unsafe { std::mem::transmute::<&[u8], &str>(&[0xC0, 0xC1, 0xF5]) } - }>; + let _non_utf8_str: Str<{ unsafe { std::mem::transmute::<&[u8], &str>(&[0xC0, 0xC1, 0xF5]) } }>; } diff --git a/tests/mir-opt/const_prop/offset_of.rs b/tests/mir-opt/const_prop/offset_of.rs index 105cbfb53dd0..264c8a3d21cd 100644 --- a/tests/mir-opt/const_prop/offset_of.rs +++ b/tests/mir-opt/const_prop/offset_of.rs @@ -31,7 +31,7 @@ struct Delta { enum Epsilon { A(u8, u16), B, - C { c: u32 } + C { c: u32 }, } enum Zeta { diff --git a/tests/mir-opt/const_prop/pointer_expose_provenance.rs b/tests/mir-opt/const_prop/pointer_expose_provenance.rs index f148a5b6542e..a76fead98594 100644 --- a/tests/mir-opt/const_prop/pointer_expose_provenance.rs +++ b/tests/mir-opt/const_prop/pointer_expose_provenance.rs @@ -2,7 +2,7 @@ //@ test-mir-pass: GVN #[inline(never)] -fn read(_: usize) { } +fn read(_: usize) {} // EMIT_MIR pointer_expose_provenance.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/return_place.add.GVN.panic-abort.diff b/tests/mir-opt/const_prop/return_place.add.GVN.panic-abort.diff index 51f8227c36b4..b2d40daa80c4 100644 --- a/tests/mir-opt/const_prop/return_place.add.GVN.panic-abort.diff +++ b/tests/mir-opt/const_prop/return_place.add.GVN.panic-abort.diff @@ -6,7 +6,7 @@ let mut _1: (u32, bool); bb0: { -- _1 = CheckedAdd(const 2_u32, const 2_u32); +- _1 = AddWithOverflow(const 2_u32, const 2_u32); - assert(!move (_1.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_u32, const 2_u32) -> [success: bb1, unwind unreachable]; + _1 = const (4_u32, false); + assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_u32, const 2_u32) -> [success: bb1, unwind unreachable]; diff --git a/tests/mir-opt/const_prop/return_place.add.GVN.panic-unwind.diff b/tests/mir-opt/const_prop/return_place.add.GVN.panic-unwind.diff index 8174b4edea65..2eafc51cd3db 100644 --- a/tests/mir-opt/const_prop/return_place.add.GVN.panic-unwind.diff +++ b/tests/mir-opt/const_prop/return_place.add.GVN.panic-unwind.diff @@ -6,7 +6,7 @@ let mut _1: (u32, bool); bb0: { -- _1 = CheckedAdd(const 2_u32, const 2_u32); +- _1 = AddWithOverflow(const 2_u32, const 2_u32); - assert(!move (_1.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_u32, const 2_u32) -> [success: bb1, unwind continue]; + _1 = const (4_u32, false); + assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_u32, const 2_u32) -> [success: bb1, unwind continue]; diff --git a/tests/mir-opt/const_prop/scalar_literal_propagation.rs b/tests/mir-opt/const_prop/scalar_literal_propagation.rs index 9d02f24e76bd..15e32490de49 100644 --- a/tests/mir-opt/const_prop/scalar_literal_propagation.rs +++ b/tests/mir-opt/const_prop/scalar_literal_propagation.rs @@ -10,4 +10,4 @@ fn main() { } #[inline(never)] -fn consume(_: u32) { } +fn consume(_: u32) {} diff --git a/tests/mir-opt/const_prop/switch_int.rs b/tests/mir-opt/const_prop/switch_int.rs index 114380e316de..4ee4182caf6a 100644 --- a/tests/mir-opt/const_prop/switch_int.rs +++ b/tests/mir-opt/const_prop/switch_int.rs @@ -3,7 +3,7 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #[inline(never)] -fn foo(_: i32) { } +fn foo(_: i32) {} // EMIT_MIR switch_int.main.GVN.diff // EMIT_MIR switch_int.main.SimplifyConstCondition-after-const-prop.diff diff --git a/tests/mir-opt/const_prop/transmute.rs b/tests/mir-opt/const_prop/transmute.rs index 9cbf8928753b..892b91a5414c 100644 --- a/tests/mir-opt/const_prop/transmute.rs +++ b/tests/mir-opt/const_prop/transmute.rs @@ -45,7 +45,10 @@ pub unsafe fn undef_union_as_integer() -> u32 { // CHECK-LABEL: fn undef_union_as_integer( // CHECK: _1 = Union32 { // CHECK: _0 = move _1 as u32 (Transmute); - union Union32 { value: u32, unit: () } + union Union32 { + value: u32, + unit: (), + } unsafe { transmute(Union32 { unit: () }) } } diff --git a/tests/mir-opt/const_prop/tuple_literal_propagation.rs b/tests/mir-opt/const_prop/tuple_literal_propagation.rs index 582411c7b59e..e42a62cb6fdf 100644 --- a/tests/mir-opt/const_prop/tuple_literal_propagation.rs +++ b/tests/mir-opt/const_prop/tuple_literal_propagation.rs @@ -10,4 +10,4 @@ fn main() { } #[inline(never)] -fn consume(_: (u32, u32)) { } +fn consume(_: (u32, u32)) {} diff --git a/tests/mir-opt/copy-prop/borrowed_local.rs b/tests/mir-opt/copy-prop/borrowed_local.rs index 512287dd1767..dd1679513f6c 100644 --- a/tests/mir-opt/copy-prop/borrowed_local.rs +++ b/tests/mir-opt/copy-prop/borrowed_local.rs @@ -4,10 +4,12 @@ #![feature(custom_mir, core_intrinsics, freeze)] #![allow(unused_assignments)] extern crate core; -use core::marker::Freeze; use core::intrinsics::mir::*; +use core::marker::Freeze; -fn opaque(_: impl Sized) -> bool { true } +fn opaque(_: impl Sized) -> bool { + true +} fn cmp_ref(a: &u8, b: &u8) -> bool { std::ptr::eq(a as *const u8, b as *const u8) @@ -24,7 +26,7 @@ fn compare_address() -> bool { // CHECK-NEXT: _0 = cmp_ref(_2, _4) // CHECK: bb1: { // CHECK-NEXT: _0 = opaque::(_3) - mir!( + mir! { { let a = 5_u8; let r1 = &a; @@ -40,7 +42,7 @@ fn compare_address() -> bool { ret = { Return() } - ) + } } /// Generic type `T` is `Freeze`, so shared borrows are immutable. @@ -52,7 +54,7 @@ fn borrowed(x: T) -> bool { // CHECK-NEXT: _0 = opaque::<&T>(_3) // CHECK: bb1: { // CHECK-NEXT: _0 = opaque::(_1) - mir!( + mir! { { let a = x; let r1 = &x; @@ -64,7 +66,7 @@ fn borrowed(x: T) -> bool { ret = { Return() } - ) + } } /// Generic type `T` is not known to be `Freeze`, so shared borrows may be mutable. @@ -77,7 +79,7 @@ fn non_freeze(x: T) -> bool { // CHECK-NEXT: _0 = opaque::<&T>(_3) // CHECK: bb1: { // CHECK-NEXT: _0 = opaque::(_2) - mir!( + mir! { { let a = x; let r1 = &x; @@ -89,7 +91,7 @@ fn non_freeze(x: T) -> bool { ret = { Return() } - ) + } } fn main() { diff --git a/tests/mir-opt/copy-prop/custom_move_arg.rs b/tests/mir-opt/copy-prop/custom_move_arg.rs index a82d4618e68c..3dce7807b347 100644 --- a/tests/mir-opt/copy-prop/custom_move_arg.rs +++ b/tests/mir-opt/copy-prop/custom_move_arg.rs @@ -12,17 +12,19 @@ struct NotCopy(bool); // EMIT_MIR custom_move_arg.f.CopyProp.diff #[custom_mir(dialect = "runtime")] fn f(_1: NotCopy) { - mir!({ - let _2 = _1; - Call(RET = opaque(Move(_1)), ReturnTo(bb1), UnwindUnreachable()) + mir! { + { + let _2 = _1; + Call(RET = opaque(Move(_1)), ReturnTo(bb1), UnwindUnreachable()) + } + bb1 = { + let _3 = Move(_2); + Call(RET = opaque(_3), ReturnTo(bb2), UnwindUnreachable()) + } + bb2 = { + Return() + } } - bb1 = { - let _3 = Move(_2); - Call(RET = opaque(_3), ReturnTo(bb2), UnwindUnreachable()) - } - bb2 = { - Return() - }) } #[inline(never)] diff --git a/tests/mir-opt/copy-prop/move_projection.rs b/tests/mir-opt/copy-prop/move_projection.rs index 231e4082e33b..0ac1c4e0ba26 100644 --- a/tests/mir-opt/copy-prop/move_projection.rs +++ b/tests/mir-opt/copy-prop/move_projection.rs @@ -7,13 +7,15 @@ extern crate core; use core::intrinsics::mir::*; -fn opaque(_: impl Sized) -> bool { true } +fn opaque(_: impl Sized) -> bool { + true +} struct Foo(u8); #[custom_mir(dialect = "runtime")] fn f(a: Foo) -> bool { - mir!( + mir! { { let b = a; // This is a move out of a copy, so must become a copy of `a.0`. @@ -26,7 +28,7 @@ fn f(a: Foo) -> bool { ret = { Return() } - ) + } } fn main() { diff --git a/tests/mir-opt/copy-prop/mutate_through_pointer.rs b/tests/mir-opt/copy-prop/mutate_through_pointer.rs index 14ca513c6927..53cca045248d 100644 --- a/tests/mir-opt/copy-prop/mutate_through_pointer.rs +++ b/tests/mir-opt/copy-prop/mutate_through_pointer.rs @@ -18,14 +18,16 @@ use core::intrinsics::mir::*; #[custom_mir(dialect = "analysis", phase = "post-cleanup")] fn f(c: bool) -> bool { - mir!({ - let a = c; - let p = core::ptr::addr_of!(a); - let p2 = core::ptr::addr_of_mut!(*p); - *p2 = false; - RET = c; - Return() - }) + mir! { + { + let a = c; + let p = core::ptr::addr_of!(a); + let p2 = core::ptr::addr_of_mut!(*p); + *p2 = false; + RET = c; + Return() + } + } } fn main() { diff --git a/tests/mir-opt/copy-prop/non_dominate.rs b/tests/mir-opt/copy-prop/non_dominate.rs index 34e7eabc81a2..c01275370ea7 100644 --- a/tests/mir-opt/copy-prop/non_dominate.rs +++ b/tests/mir-opt/copy-prop/non_dominate.rs @@ -8,16 +8,28 @@ use core::intrinsics::mir::*; #[custom_mir(dialect = "analysis", phase = "post-cleanup")] fn f(c: bool) -> bool { - mir!( + mir! { let a: bool; let b: bool; - { Goto(bb1) } - bb1 = { b = c; match b { false => bb3, _ => bb2 }} + { + Goto(bb1) + } + bb1 = { + b = c; + match b { false => bb3, _ => bb2 } + } // This assignment to `a` does not dominate the use in `bb3`. // It should not be replaced by `b`. - bb2 = { a = b; c = false; Goto(bb1) } - bb3 = { RET = a; Return() } - ) + bb2 = { + a = b; + c = false; + Goto(bb1) + } + bb3 = { + RET = a; + Return() + } + } } fn main() { diff --git a/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff index 3606a9e39323..a594c44c316a 100644 --- a/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff +++ b/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff @@ -8,12 +8,11 @@ let mut _3: !; + coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Add, rhs: Counter(1) }; -+ coverage ExpressionId(1) => Expression { lhs: Expression(0), op: Subtract, rhs: Counter(1) }; + coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:10:1 - 10:11; -+ coverage Code(Expression(0)) => $DIR/instrument_coverage.rs:11:5 - 12:17; -+ coverage Code(Expression(1)) => $DIR/instrument_coverage.rs:13:13 - 13:18; ++ coverage Code(Expression(0)) => $DIR/instrument_coverage.rs:12:12 - 12:17; ++ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:13:13 - 13:18; + coverage Code(Counter(1)) => $DIR/instrument_coverage.rs:14:10 - 14:11; -+ coverage Code(Expression(1)) => $DIR/instrument_coverage.rs:16:1 - 16:2; ++ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:16:1 - 16:2; + bb0: { + Coverage::CounterIncrement(0); @@ -35,7 +34,6 @@ } bb4: { -+ Coverage::ExpressionUsed(1); _0 = const (); StorageDead(_2); return; diff --git a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff index 34d011540b9c..efb1559baf5e 100644 --- a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff +++ b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff @@ -8,11 +8,10 @@ coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0) coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) }; - coverage ExpressionId(1) => Expression { lhs: Counter(1), op: Add, rhs: Expression(0) }; coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:13:1 - 14:36; coverage Code(Expression(0)) => $DIR/instrument_coverage_cleanup.rs:14:37 - 14:39; coverage Code(Counter(1)) => $DIR/instrument_coverage_cleanup.rs:14:39 - 14:40; - coverage Code(Expression(1)) => $DIR/instrument_coverage_cleanup.rs:15:1 - 15:2; + coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:15:1 - 15:2; coverage Branch { true_term: Expression(0), false_term: Counter(1) } => $DIR/instrument_coverage_cleanup.rs:14:8 - 14:36; bb0: { @@ -44,7 +43,6 @@ } bb4: { - Coverage::ExpressionUsed(1); StorageDead(_1); return; } diff --git a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff index 6756d5c1e1b2..a0fe9a5c05cd 100644 --- a/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff +++ b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff @@ -8,11 +8,10 @@ coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => $DIR/instrument_coverage_cleanup.rs:14:8: 14:36 (#0) + coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) }; -+ coverage ExpressionId(1) => Expression { lhs: Counter(1), op: Add, rhs: Expression(0) }; + coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:13:1 - 14:36; + coverage Code(Expression(0)) => $DIR/instrument_coverage_cleanup.rs:14:37 - 14:39; + coverage Code(Counter(1)) => $DIR/instrument_coverage_cleanup.rs:14:39 - 14:40; -+ coverage Code(Expression(1)) => $DIR/instrument_coverage_cleanup.rs:15:1 - 15:2; ++ coverage Code(Counter(0)) => $DIR/instrument_coverage_cleanup.rs:15:1 - 15:2; + coverage Branch { true_term: Expression(0), false_term: Counter(1) } => $DIR/instrument_coverage_cleanup.rs:14:8 - 14:36; + bb0: { @@ -41,7 +40,6 @@ } bb4: { -+ Coverage::ExpressionUsed(1); StorageDead(_1); return; } diff --git a/tests/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.panic-abort.diff index b35538f89728..53663c6476bd 100644 --- a/tests/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.panic-abort.diff @@ -40,7 +40,7 @@ + _4 = const 1_i32; StorageLive(_5); - _5 = _2; -- _6 = CheckedAdd(_4, _5); +- _6 = AddWithOverflow(_4, _5); - assert(!move (_6.1: bool), "attempt to compute `{} + {}`, which would overflow", move _4, move _5) -> [success: bb1, unwind unreachable]; + _5 = const 2_i32; + _6 = const (3_i32, false); @@ -57,7 +57,7 @@ StorageLive(_8); StorageLive(_9); - _9 = _7; -- _10 = CheckedAdd(_9, const 1_i32); +- _10 = AddWithOverflow(_9, const 1_i32); - assert(!move (_10.1: bool), "attempt to compute `{} + {}`, which would overflow", move _9, const 1_i32) -> [success: bb2, unwind unreachable]; + _9 = const i32::MAX; + _10 = const (i32::MIN, true); diff --git a/tests/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.panic-unwind.diff index 05c969692337..34feb2a64062 100644 --- a/tests/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.panic-unwind.diff @@ -40,7 +40,7 @@ + _4 = const 1_i32; StorageLive(_5); - _5 = _2; -- _6 = CheckedAdd(_4, _5); +- _6 = AddWithOverflow(_4, _5); - assert(!move (_6.1: bool), "attempt to compute `{} + {}`, which would overflow", move _4, move _5) -> [success: bb1, unwind continue]; + _5 = const 2_i32; + _6 = const (3_i32, false); @@ -57,7 +57,7 @@ StorageLive(_8); StorageLive(_9); - _9 = _7; -- _10 = CheckedAdd(_9, const 1_i32); +- _10 = AddWithOverflow(_9, const 1_i32); - assert(!move (_10.1: bool), "attempt to compute `{} + {}`, which would overflow", move _9, const 1_i32) -> [success: bb2, unwind continue]; + _9 = const i32::MAX; + _10 = const (i32::MIN, true); diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff index ca445046279b..06011f9d7596 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff @@ -80,7 +80,7 @@ bb4: { StorageDead(_8); -- _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _11 = _6 as *const [bool; 0] (PtrToPtr); - _5 = NonNull::<[bool; 0]> { pointer: _11 }; + _11 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff index 1f498c65fa78..eb4a3ffd91d2 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff @@ -84,7 +84,7 @@ bb5: { StorageDead(_8); -- _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _11 = _6 as *const [bool; 0] (PtrToPtr); - _5 = NonNull::<[bool; 0]> { pointer: _11 }; + _11 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff index da72e581496e..a7cc243e548e 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff @@ -80,7 +80,7 @@ bb4: { StorageDead(_8); -- _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _11 = _6 as *const [bool; 0] (PtrToPtr); - _5 = NonNull::<[bool; 0]> { pointer: _11 }; + _11 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff index 920e66452e3b..c905a48862ca 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff @@ -84,7 +84,7 @@ bb5: { StorageDead(_8); -- _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _11 = _6 as *const [bool; 0] (PtrToPtr); - _5 = NonNull::<[bool; 0]> { pointer: _11 }; + _11 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; diff --git a/tests/mir-opt/dataflow-const-prop/enum.rs b/tests/mir-opt/dataflow-const-prop/enum.rs index 5c52f92cd8f1..946cfa4c76c0 100644 --- a/tests/mir-opt/dataflow-const-prop/enum.rs +++ b/tests/mir-opt/dataflow-const-prop/enum.rs @@ -8,7 +8,7 @@ use std::intrinsics::mir::*; #[derive(Copy, Clone)] enum E { V1(i32), - V2(i32) + V2(i32), } // EMIT_MIR enum.simple.DataflowConstProp.diff @@ -23,7 +23,10 @@ fn simple() { // CHECK: switchInt(const 0_isize) -> [0: [[target_bb:bb.*]], 1: bb2, otherwise: bb1]; // CHECK: [[target_bb]]: { // CHECK: [[x]] = const 0_i32; - let x = match e { E::V1(x1) => x1, E::V2(x2) => x2 }; + let x = match e { + E::V1(x1) => x1, + E::V2(x2) => x2, + }; } // EMIT_MIR enum.constant.DataflowConstProp.diff @@ -39,7 +42,10 @@ fn constant() { // CHECK: switchInt(const 0_isize) -> [0: [[target_bb:bb.*]], 1: bb2, otherwise: bb1]; // CHECK: [[target_bb]]: { // CHECK: [[x]] = const 0_i32; - let x = match e { E::V1(x1) => x1, E::V2(x2) => x2 }; + let x = match e { + E::V1(x1) => x1, + E::V2(x2) => x2, + }; } // EMIT_MIR enum.statics.DataflowConstProp.diff @@ -58,7 +64,10 @@ fn statics() { // CHECK: switchInt(const 0_isize) -> [0: [[target_bb:bb.*]], 1: bb2, otherwise: bb1]; // CHECK: [[target_bb]]: { // CHECK: [[x1]] = const 0_i32; - let x1 = match e1 { E::V1(x11) => x11, E::V2(x12) => x12 }; + let x1 = match e1 { + E::V1(x11) => x11, + E::V2(x12) => x12, + }; static RC: &E = &E::V2(4); @@ -72,7 +81,10 @@ fn statics() { // One is `_9 = &(*_12) and another is `_9 = _11`. It is different from what we can // get by printing MIR directly. It is better to check if there are any bugs in the // MIR passes around this stage. - let x2 = match e2 { E::V1(x21) => x21, E::V2(x22) => x22 }; + let x2 = match e2 { + E::V1(x21) => x21, + E::V2(x22) => x22, + }; } #[rustc_layout_scalar_valid_range_start(1)] @@ -84,7 +96,7 @@ struct NonZeroUsize(usize); // CHECK-LABEL: fn mutate_discriminant( #[custom_mir(dialect = "runtime", phase = "post-cleanup")] fn mutate_discriminant() -> u8 { - mir!( + mir! { let x: Option; { SetDiscriminant(x, 1); @@ -109,7 +121,7 @@ fn mutate_discriminant() -> u8 { RET = 2; Unreachable() } - ) + } } // EMIT_MIR enum.multiple.DataflowConstProp.diff @@ -132,7 +144,10 @@ fn multiple(x: bool, i: u8) { // CHECK: [[x2]] = const 0_u8; // CHECK: [[some:_.*]] = (({{_.*}} as Some).0: u8) // CHECK: [[x2]] = [[some]]; - let x2 = match e { Some(i) => i, None => 0 }; + let x2 = match e { + Some(i) => i, + None => 0, + }; // Therefore, `x2` should be `Top` here, and no replacement shall happen. diff --git a/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-abort.diff index ca1bd737caf0..8d62de0c8215 100644 --- a/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-abort.diff @@ -19,7 +19,7 @@ StorageLive(_3); _3 = const 1_u8; StorageLive(_4); -- _4 = CheckedAdd(_2, _3); +- _4 = AddWithOverflow(_2, _3); - assert(!move (_4.1: bool), "attempt to compute `{} + {}`, which would overflow", _2, _3) -> [success: bb1, unwind unreachable]; + _4 = const (0_u8, true); + assert(!const true, "attempt to compute `{} + {}`, which would overflow", const u8::MAX, const 1_u8) -> [success: bb1, unwind unreachable]; diff --git a/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-unwind.diff index 0d7fe9360c11..25624851cb34 100644 --- a/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.panic-unwind.diff @@ -19,7 +19,7 @@ StorageLive(_3); _3 = const 1_u8; StorageLive(_4); -- _4 = CheckedAdd(_2, _3); +- _4 = AddWithOverflow(_2, _3); - assert(!move (_4.1: bool), "attempt to compute `{} + {}`, which would overflow", _2, _3) -> [success: bb1, unwind continue]; + _4 = const (0_u8, true); + assert(!const true, "attempt to compute `{} + {}`, which would overflow", const u8::MAX, const 1_u8) -> [success: bb1, unwind continue]; diff --git a/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs b/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs index 3cd0b715a521..26f79c8c0e48 100644 --- a/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs +++ b/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs @@ -2,9 +2,9 @@ // EMIT_MIR mult_by_zero.test.DataflowConstProp.diff // CHECK-LABEL: fn test( -fn test(x : i32) -> i32 { - x * 0 - // CHECK: _0 = const 0_i32; +fn test(x: i32) -> i32 { + x * 0 + // CHECK: _0 = const 0_i32; } fn main() { diff --git a/tests/mir-opt/dataflow-const-prop/transmute.rs b/tests/mir-opt/dataflow-const-prop/transmute.rs index e7f93f421cf8..d0391f6974b4 100644 --- a/tests/mir-opt/dataflow-const-prop/transmute.rs +++ b/tests/mir-opt/dataflow-const-prop/transmute.rs @@ -45,7 +45,10 @@ pub unsafe fn undef_union_as_integer() -> u32 { // CHECK-LABEL: fn undef_union_as_integer( // CHECK: _1 = Union32 { // CHECK: _0 = move _1 as u32 (Transmute); - union Union32 { value: u32, unit: () } + union Union32 { + value: u32, + unit: (), + } unsafe { transmute(Union32 { unit: () }) } } diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs index edb35061cc65..2556848ec462 100644 --- a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs @@ -29,7 +29,7 @@ struct Packed { fn move_packed(packed: Packed) { // CHECK-LABEL: fn move_packed( // CHECK: = use_both(const 0_i32, (_1.1: i32)) - mir!( + mir! { { // We have a packed struct, verify that the copy is not turned into a move. Call(RET = use_both(0, packed.y), ReturnTo(ret), UnwindContinue()) @@ -37,7 +37,7 @@ fn move_packed(packed: Packed) { ret = { Return() } - ) + } } fn main() { diff --git a/tests/mir-opt/dead-store-elimination/cycle.rs b/tests/mir-opt/dead-store-elimination/cycle.rs index 795d57d36f52..c8f0c2644ab3 100644 --- a/tests/mir-opt/dead-store-elimination/cycle.rs +++ b/tests/mir-opt/dead-store-elimination/cycle.rs @@ -20,7 +20,7 @@ fn cycle(mut x: i32, mut y: i32, mut z: i32) { // CHECK-NOT: {{_.*}} = move {{_.*}}; // We use custom MIR to avoid generating debuginfo, that would force to preserve writes. - mir!( + mir! { let condition: bool; { Call(condition = cond(), ReturnTo(bb1), UnwindContinue()) @@ -38,7 +38,7 @@ fn cycle(mut x: i32, mut y: i32, mut z: i32) { ret = { Return() } - ) + } } fn main() { diff --git a/tests/mir-opt/derefer_complex_case.rs b/tests/mir-opt/derefer_complex_case.rs index b1fa2c8733ef..96034522b9fe 100644 --- a/tests/mir-opt/derefer_complex_case.rs +++ b/tests/mir-opt/derefer_complex_case.rs @@ -4,5 +4,7 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY fn main() { - for &foo in &[42, 43] { drop(foo) } + for &foo in &[42, 43] { + drop(foo) + } } diff --git a/tests/mir-opt/derefer_terminator_test.rs b/tests/mir-opt/derefer_terminator_test.rs index 5de6a61eaf2c..fd16c6c2045e 100644 --- a/tests/mir-opt/derefer_terminator_test.rs +++ b/tests/mir-opt/derefer_terminator_test.rs @@ -7,7 +7,9 @@ fn main() { let b = foo(); let d = foo(); match ****(&&&&b) { - true => {let x = 5;}, + true => { + let x = 5; + } false => {} } let y = 42; diff --git a/tests/mir-opt/derefer_test.rs b/tests/mir-opt/derefer_test.rs index 3ca2144e4fc4..5676fa657fe9 100644 --- a/tests/mir-opt/derefer_test.rs +++ b/tests/mir-opt/derefer_test.rs @@ -2,7 +2,7 @@ //@ test-mir-pass: Derefer // EMIT_MIR derefer_test.main.Derefer.diff fn main() { - let mut a = (42,43); + let mut a = (42, 43); let mut b = (99, &mut a); let x = &mut (*b.1).0; let y = &mut (*b.1).1; diff --git a/tests/mir-opt/derefer_test_multiple.rs b/tests/mir-opt/derefer_test_multiple.rs index 145a19ee6a3c..7c03af00e1e6 100644 --- a/tests/mir-opt/derefer_test_multiple.rs +++ b/tests/mir-opt/derefer_test_multiple.rs @@ -1,7 +1,7 @@ // skip-filecheck //@ test-mir-pass: Derefer // EMIT_MIR derefer_test_multiple.main.Derefer.diff -fn main () { +fn main() { let mut a = (42, 43); let mut b = (99, &mut a); let mut c = (11, &mut b); diff --git a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff index 570ec129f06e..b596e25ddfdf 100644 --- a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff +++ b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff @@ -18,7 +18,7 @@ bb0: { StorageLive(_1); StorageLive(_2); - _2 = const 1_u32; + nop; _1 = Un { us: const 1_u32 }; StorageDead(_2); StorageLive(_3); diff --git a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff index 570ec129f06e..b596e25ddfdf 100644 --- a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff +++ b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff @@ -18,7 +18,7 @@ bb0: { StorageLive(_1); StorageLive(_2); - _2 = const 1_u32; + nop; _1 = Un { us: const 1_u32 }; StorageDead(_2); StorageLive(_3); diff --git a/tests/mir-opt/enum_opt.rs b/tests/mir-opt/enum_opt.rs index cacc7301f122..2cc5df84d6b5 100644 --- a/tests/mir-opt/enum_opt.rs +++ b/tests/mir-opt/enum_opt.rs @@ -7,22 +7,22 @@ // Tests that an enum with a variant with no data gets correctly transformed. pub enum NoData { - Large([u8; 8196]), - None, + Large([u8; 8196]), + None, } // Tests that an enum with a variant with data that is a valid candidate gets transformed. pub enum Candidate { - Small(u8), - Large([u8; 8196]), + Small(u8), + Large([u8; 8196]), } // Tests that an enum which has a discriminant much higher than the variant does not get // tformed. #[repr(u32)] pub enum InvalidIdxs { - A = 302, - Large([u64; 1024]), + A = 302, + Large([u64; 1024]), } // Tests that an enum with too high of a discriminant index (not in bounds of usize) does not @@ -37,51 +37,51 @@ pub enum NotTrunctable { // Tests that an enum with discriminants in random order still gets tformed correctly. #[repr(u32)] pub enum RandOrderDiscr { - A = 13, - B([u8; 1024]) = 5, - C = 7, + A = 13, + B([u8; 1024]) = 5, + C = 7, } // EMIT_MIR enum_opt.unin.EnumSizeOpt.diff pub fn unin() -> NoData { - let mut a = NoData::None; - a = NoData::Large([1; 8196]); - a + let mut a = NoData::None; + a = NoData::Large([1; 8196]); + a } // EMIT_MIR enum_opt.cand.EnumSizeOpt.diff pub fn cand() -> Candidate { - let mut a = Candidate::Small(1); - a = Candidate::Large([1; 8196]); - a + let mut a = Candidate::Small(1); + a = Candidate::Large([1; 8196]); + a } // EMIT_MIR enum_opt.invalid.EnumSizeOpt.diff pub fn invalid() -> InvalidIdxs { - let mut a = InvalidIdxs::A; - a = InvalidIdxs::Large([0; 1024]); - a + let mut a = InvalidIdxs::A; + a = InvalidIdxs::Large([0; 1024]); + a } // EMIT_MIR enum_opt.trunc.EnumSizeOpt.diff pub fn trunc() -> NotTrunctable { - let mut a = NotTrunctable::A; - a = NotTrunctable::B([0; 1024]); - a = NotTrunctable::C([0; 4096]); - a + let mut a = NotTrunctable::A; + a = NotTrunctable::B([0; 1024]); + a = NotTrunctable::C([0; 4096]); + a } pub fn rand_order() -> RandOrderDiscr { - let mut a = RandOrderDiscr::A; - a = RandOrderDiscr::B([0; 1024]); - a = RandOrderDiscr::C; - a + let mut a = RandOrderDiscr::A; + a = RandOrderDiscr::B([0; 1024]); + a = RandOrderDiscr::C; + a } pub fn main() { - unin(); - cand(); - invalid(); - trunc(); - rand_order(); + unin(); + cand(); + invalid(); + trunc(); + rand_order(); } diff --git a/tests/mir-opt/gvn.arithmetic_checked.GVN.panic-abort.diff b/tests/mir-opt/gvn.arithmetic_checked.GVN.panic-abort.diff index a45d9920a684..5bf22af6ae83 100644 --- a/tests/mir-opt/gvn.arithmetic_checked.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.arithmetic_checked.GVN.panic-abort.diff @@ -31,9 +31,9 @@ StorageLive(_3); StorageLive(_4); _4 = _1; -- _5 = CheckedAdd(_4, const 0_u64); +- _5 = AddWithOverflow(_4, const 0_u64); - assert(!move (_5.1: bool), "attempt to compute `{} + {}`, which would overflow", move _4, const 0_u64) -> [success: bb1, unwind unreachable]; -+ _5 = CheckedAdd(_1, const 0_u64); ++ _5 = AddWithOverflow(_1, const 0_u64); + assert(!const false, "attempt to compute `{} + {}`, which would overflow", _1, const 0_u64) -> [success: bb1, unwind unreachable]; } @@ -52,7 +52,7 @@ StorageLive(_7); StorageLive(_8); _8 = _1; -- _9 = CheckedSub(_8, const 0_u64); +- _9 = SubWithOverflow(_8, const 0_u64); - assert(!move (_9.1: bool), "attempt to compute `{} - {}`, which would overflow", move _8, const 0_u64) -> [success: bb3, unwind unreachable]; + _9 = _5; + assert(!const false, "attempt to compute `{} - {}`, which would overflow", _1, const 0_u64) -> [success: bb3, unwind unreachable]; @@ -76,7 +76,7 @@ _12 = _1; StorageLive(_13); _13 = _1; -- _14 = CheckedSub(_12, _13); +- _14 = SubWithOverflow(_12, _13); - assert(!move (_14.1: bool), "attempt to compute `{} - {}`, which would overflow", move _12, move _13) -> [success: bb5, unwind unreachable]; + _14 = const (0_u64, false); + assert(!const false, "attempt to compute `{} - {}`, which would overflow", _1, _1) -> [success: bb5, unwind unreachable]; @@ -99,7 +99,7 @@ StorageLive(_16); StorageLive(_17); _17 = _1; -- _18 = CheckedMul(_17, const 0_u64); +- _18 = MulWithOverflow(_17, const 0_u64); - assert(!move (_18.1: bool), "attempt to compute `{} * {}`, which would overflow", move _17, const 0_u64) -> [success: bb7, unwind unreachable]; + _18 = const (0_u64, false); + assert(!const false, "attempt to compute `{} * {}`, which would overflow", _1, const 0_u64) -> [success: bb7, unwind unreachable]; @@ -120,7 +120,7 @@ StorageLive(_20); StorageLive(_21); _21 = _1; -- _22 = CheckedMul(_21, const 1_u64); +- _22 = MulWithOverflow(_21, const 1_u64); - assert(!move (_22.1: bool), "attempt to compute `{} * {}`, which would overflow", move _21, const 1_u64) -> [success: bb9, unwind unreachable]; + _22 = _5; + assert(!const false, "attempt to compute `{} * {}`, which would overflow", _1, const 1_u64) -> [success: bb9, unwind unreachable]; diff --git a/tests/mir-opt/gvn.arithmetic_checked.GVN.panic-unwind.diff b/tests/mir-opt/gvn.arithmetic_checked.GVN.panic-unwind.diff index 9033b392bd4c..18d2029e4450 100644 --- a/tests/mir-opt/gvn.arithmetic_checked.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.arithmetic_checked.GVN.panic-unwind.diff @@ -31,9 +31,9 @@ StorageLive(_3); StorageLive(_4); _4 = _1; -- _5 = CheckedAdd(_4, const 0_u64); +- _5 = AddWithOverflow(_4, const 0_u64); - assert(!move (_5.1: bool), "attempt to compute `{} + {}`, which would overflow", move _4, const 0_u64) -> [success: bb1, unwind continue]; -+ _5 = CheckedAdd(_1, const 0_u64); ++ _5 = AddWithOverflow(_1, const 0_u64); + assert(!const false, "attempt to compute `{} + {}`, which would overflow", _1, const 0_u64) -> [success: bb1, unwind continue]; } @@ -52,7 +52,7 @@ StorageLive(_7); StorageLive(_8); _8 = _1; -- _9 = CheckedSub(_8, const 0_u64); +- _9 = SubWithOverflow(_8, const 0_u64); - assert(!move (_9.1: bool), "attempt to compute `{} - {}`, which would overflow", move _8, const 0_u64) -> [success: bb3, unwind continue]; + _9 = _5; + assert(!const false, "attempt to compute `{} - {}`, which would overflow", _1, const 0_u64) -> [success: bb3, unwind continue]; @@ -76,7 +76,7 @@ _12 = _1; StorageLive(_13); _13 = _1; -- _14 = CheckedSub(_12, _13); +- _14 = SubWithOverflow(_12, _13); - assert(!move (_14.1: bool), "attempt to compute `{} - {}`, which would overflow", move _12, move _13) -> [success: bb5, unwind continue]; + _14 = const (0_u64, false); + assert(!const false, "attempt to compute `{} - {}`, which would overflow", _1, _1) -> [success: bb5, unwind continue]; @@ -99,7 +99,7 @@ StorageLive(_16); StorageLive(_17); _17 = _1; -- _18 = CheckedMul(_17, const 0_u64); +- _18 = MulWithOverflow(_17, const 0_u64); - assert(!move (_18.1: bool), "attempt to compute `{} * {}`, which would overflow", move _17, const 0_u64) -> [success: bb7, unwind continue]; + _18 = const (0_u64, false); + assert(!const false, "attempt to compute `{} * {}`, which would overflow", _1, const 0_u64) -> [success: bb7, unwind continue]; @@ -120,7 +120,7 @@ StorageLive(_20); StorageLive(_21); _21 = _1; -- _22 = CheckedMul(_21, const 1_u64); +- _22 = MulWithOverflow(_21, const 1_u64); - assert(!move (_22.1: bool), "attempt to compute `{} * {}`, which would overflow", move _21, const 1_u64) -> [success: bb9, unwind continue]; + _22 = _5; + assert(!const false, "attempt to compute `{} * {}`, which would overflow", _1, const 1_u64) -> [success: bb9, unwind continue]; diff --git a/tests/mir-opt/gvn.casts_before_aggregate_raw_ptr.GVN.panic-abort.diff b/tests/mir-opt/gvn.casts_before_aggregate_raw_ptr.GVN.panic-abort.diff new file mode 100644 index 000000000000..6e5bdb16595b --- /dev/null +++ b/tests/mir-opt/gvn.casts_before_aggregate_raw_ptr.GVN.panic-abort.diff @@ -0,0 +1,60 @@ +- // MIR for `casts_before_aggregate_raw_ptr` before GVN ++ // MIR for `casts_before_aggregate_raw_ptr` after GVN + + fn casts_before_aggregate_raw_ptr(_1: *const u32) -> *const [u8] { + debug x => _1; + let mut _0: *const [u8]; + let _2: *const [u8; 4]; + let mut _3: *const u32; + let mut _5: *const [u8; 4]; + let mut _7: *const u8; + let mut _8: *const (); + scope 1 { + debug x => _2; + let _4: *const u8; + scope 2 { + debug x => _4; + let _6: *const (); + scope 3 { + debug x => _6; + } + } + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = _1; +- _2 = move _3 as *const [u8; 4] (PtrToPtr); ++ _2 = _1 as *const [u8; 4] (PtrToPtr); + StorageDead(_3); +- StorageLive(_4); ++ nop; + StorageLive(_5); + _5 = _2; +- _4 = move _5 as *const u8 (PtrToPtr); ++ _4 = _1 as *const u8 (PtrToPtr); + StorageDead(_5); +- StorageLive(_6); ++ nop; + StorageLive(_7); + _7 = _4; +- _6 = move _7 as *const () (PtrToPtr); ++ _6 = _1 as *const () (PtrToPtr); + StorageDead(_7); + StorageLive(_8); + _8 = _6; +- _0 = *const [u8] from (move _8, const 4_usize); ++ _0 = *const [u8] from (_1, const 4_usize); + StorageDead(_8); +- StorageDead(_6); +- StorageDead(_4); +- StorageDead(_2); ++ nop; ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.casts_before_aggregate_raw_ptr.GVN.panic-unwind.diff b/tests/mir-opt/gvn.casts_before_aggregate_raw_ptr.GVN.panic-unwind.diff new file mode 100644 index 000000000000..6e5bdb16595b --- /dev/null +++ b/tests/mir-opt/gvn.casts_before_aggregate_raw_ptr.GVN.panic-unwind.diff @@ -0,0 +1,60 @@ +- // MIR for `casts_before_aggregate_raw_ptr` before GVN ++ // MIR for `casts_before_aggregate_raw_ptr` after GVN + + fn casts_before_aggregate_raw_ptr(_1: *const u32) -> *const [u8] { + debug x => _1; + let mut _0: *const [u8]; + let _2: *const [u8; 4]; + let mut _3: *const u32; + let mut _5: *const [u8; 4]; + let mut _7: *const u8; + let mut _8: *const (); + scope 1 { + debug x => _2; + let _4: *const u8; + scope 2 { + debug x => _4; + let _6: *const (); + scope 3 { + debug x => _6; + } + } + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = _1; +- _2 = move _3 as *const [u8; 4] (PtrToPtr); ++ _2 = _1 as *const [u8; 4] (PtrToPtr); + StorageDead(_3); +- StorageLive(_4); ++ nop; + StorageLive(_5); + _5 = _2; +- _4 = move _5 as *const u8 (PtrToPtr); ++ _4 = _1 as *const u8 (PtrToPtr); + StorageDead(_5); +- StorageLive(_6); ++ nop; + StorageLive(_7); + _7 = _4; +- _6 = move _7 as *const () (PtrToPtr); ++ _6 = _1 as *const () (PtrToPtr); + StorageDead(_7); + StorageLive(_8); + _8 = _6; +- _0 = *const [u8] from (move _8, const 4_usize); ++ _0 = *const [u8] from (_1, const 4_usize); + StorageDead(_8); +- StorageDead(_6); +- StorageDead(_4); +- StorageDead(_2); ++ nop; ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.meta_of_ref_to_slice.GVN.panic-abort.diff b/tests/mir-opt/gvn.meta_of_ref_to_slice.GVN.panic-abort.diff new file mode 100644 index 000000000000..73dbabb56b35 --- /dev/null +++ b/tests/mir-opt/gvn.meta_of_ref_to_slice.GVN.panic-abort.diff @@ -0,0 +1,32 @@ +- // MIR for `meta_of_ref_to_slice` before GVN ++ // MIR for `meta_of_ref_to_slice` after GVN + + fn meta_of_ref_to_slice(_1: *const i32) -> usize { + debug x => _1; + let mut _0: usize; + let _2: *const [i32]; + let mut _3: *const i32; + let mut _4: *const [i32]; + scope 1 { + debug ptr => _2; + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = _1; +- _2 = *const [i32] from (move _3, const 1_usize); ++ _2 = *const [i32] from (_1, const 1_usize); + StorageDead(_3); + StorageLive(_4); + _4 = _2; +- _0 = PtrMetadata(move _4); ++ _0 = const 1_usize; + StorageDead(_4); +- StorageDead(_2); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.meta_of_ref_to_slice.GVN.panic-unwind.diff b/tests/mir-opt/gvn.meta_of_ref_to_slice.GVN.panic-unwind.diff new file mode 100644 index 000000000000..73dbabb56b35 --- /dev/null +++ b/tests/mir-opt/gvn.meta_of_ref_to_slice.GVN.panic-unwind.diff @@ -0,0 +1,32 @@ +- // MIR for `meta_of_ref_to_slice` before GVN ++ // MIR for `meta_of_ref_to_slice` after GVN + + fn meta_of_ref_to_slice(_1: *const i32) -> usize { + debug x => _1; + let mut _0: usize; + let _2: *const [i32]; + let mut _3: *const i32; + let mut _4: *const [i32]; + scope 1 { + debug ptr => _2; + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = _1; +- _2 = *const [i32] from (move _3, const 1_usize); ++ _2 = *const [i32] from (_1, const 1_usize); + StorageDead(_3); + StorageLive(_4); + _4 = _2; +- _0 = PtrMetadata(move _4); ++ _0 = const 1_usize; + StorageDead(_4); +- StorageDead(_2); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs index 9be305152835..720018f112e5 100644 --- a/tests/mir-opt/gvn.rs +++ b/tests/mir-opt/gvn.rs @@ -539,7 +539,7 @@ fn slices() { #[custom_mir(dialect = "analysis")] fn duplicate_slice() -> (bool, bool) { // CHECK-LABEL: fn duplicate_slice( - mir!( + mir! { let au: u128; let bu: u128; let cu: u128; @@ -585,7 +585,7 @@ fn duplicate_slice() -> (bool, bool) { RET = (direct, indirect); Return() } - ) + } } fn repeat() { @@ -623,11 +623,13 @@ fn fn_pointers() { fn indirect_static() { static A: Option = None; - mir!({ - let ptr = Static(A); - let out = Field::(Variant(*ptr, 1), 0); - Return() - }) + mir! { + { + let ptr = Static(A); + let out = Field::(Variant(*ptr, 1), 0); + Return() + } + } } /// Verify that having constant index `u64::MAX` does not yield to an overflow in rustc. @@ -733,7 +735,7 @@ fn borrowed(x: T) { // CHECK-NEXT: _0 = opaque::(_1) // CHECK: bb2: { // CHECK-NEXT: _0 = opaque::(_1) - mir!( + mir! { { let a = x; let r1 = &x; @@ -748,7 +750,7 @@ fn borrowed(x: T) { ret = { Return() } - ) + } } /// Generic type `T` is not known to be `Freeze`, so shared borrows may be mutable. @@ -763,7 +765,7 @@ fn non_freeze(x: T) { // CHECK-NEXT: _0 = opaque::(_2) // CHECK: bb2: { // CHECK-NEXT: _0 = opaque::((*_3)) - mir!( + mir! { { let a = x; let r1 = &x; @@ -778,7 +780,40 @@ fn non_freeze(x: T) { ret = { Return() } - ) + } +} + +// Check that we can const-prop into `from_raw_parts` +fn slice_const_length(x: &[i32]) -> *const [i32] { + // CHECK-LABEL: fn slice_const_length( + // CHECK: _0 = *const [i32] from ({{_[0-9]+}}, const 123_usize); + let ptr = x.as_ptr(); + let len = 123; + std::intrinsics::aggregate_raw_ptr(ptr, len) +} + +fn meta_of_ref_to_slice(x: *const i32) -> usize { + // CHECK-LABEL: fn meta_of_ref_to_slice + // CHECK: _0 = const 1_usize + let ptr: *const [i32] = std::intrinsics::aggregate_raw_ptr(x, 1); + std::intrinsics::ptr_metadata(ptr) +} + +fn slice_from_raw_parts_as_ptr(x: *const u16, n: usize) -> (*const u16, *const f32) { + // CHECK-LABEL: fn slice_from_raw_parts_as_ptr + // CHECK: _8 = _1 as *const f32 (PtrToPtr); + // CHECK: _0 = (_1, move _8); + let ptr: *const [u16] = std::intrinsics::aggregate_raw_ptr(x, n); + (ptr as *const u16, ptr as *const f32) +} + +fn casts_before_aggregate_raw_ptr(x: *const u32) -> *const [u8] { + // CHECK-LABEL: fn casts_before_aggregate_raw_ptr + // CHECK: _0 = *const [u8] from (_1, const 4_usize); + let x = x as *const [u8; 4]; + let x = x as *const u8; + let x = x as *const (); + std::intrinsics::aggregate_raw_ptr(x, 4) } fn main() { @@ -805,6 +840,9 @@ fn main() { wide_ptr_integer(); borrowed(5); non_freeze(5); + slice_const_length(&[1]); + meta_of_ref_to_slice(&42); + slice_from_raw_parts_as_ptr(&123, 456); } #[inline(never)] @@ -838,3 +876,7 @@ fn identity(x: T) -> T { // EMIT_MIR gvn.wide_ptr_integer.GVN.diff // EMIT_MIR gvn.borrowed.GVN.diff // EMIT_MIR gvn.non_freeze.GVN.diff +// EMIT_MIR gvn.slice_const_length.GVN.diff +// EMIT_MIR gvn.meta_of_ref_to_slice.GVN.diff +// EMIT_MIR gvn.slice_from_raw_parts_as_ptr.GVN.diff +// EMIT_MIR gvn.casts_before_aggregate_raw_ptr.GVN.diff diff --git a/tests/mir-opt/gvn.slice_const_length.GVN.panic-abort.diff b/tests/mir-opt/gvn.slice_const_length.GVN.panic-abort.diff new file mode 100644 index 000000000000..fd5fa035d816 --- /dev/null +++ b/tests/mir-opt/gvn.slice_const_length.GVN.panic-abort.diff @@ -0,0 +1,48 @@ +- // MIR for `slice_const_length` before GVN ++ // MIR for `slice_const_length` after GVN + + fn slice_const_length(_1: &[i32]) -> *const [i32] { + debug x => _1; + let mut _0: *const [i32]; + let _2: *const i32; + let mut _3: &[i32]; + let mut _5: *const i32; + let mut _6: usize; + scope 1 { + debug ptr => _2; + let _4: usize; + scope 2 { + debug len => _4; + } + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = &(*_1); + _2 = core::slice::::as_ptr(move _3) -> [return: bb1, unwind unreachable]; + } + + bb1: { + StorageDead(_3); +- StorageLive(_4); ++ nop; + _4 = const 123_usize; + StorageLive(_5); + _5 = _2; + StorageLive(_6); +- _6 = _4; +- _0 = *const [i32] from (move _5, move _6); ++ _6 = const 123_usize; ++ _0 = *const [i32] from (_2, const 123_usize); + StorageDead(_6); + StorageDead(_5); +- StorageDead(_4); +- StorageDead(_2); ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.slice_const_length.GVN.panic-unwind.diff b/tests/mir-opt/gvn.slice_const_length.GVN.panic-unwind.diff new file mode 100644 index 000000000000..98945cf9724d --- /dev/null +++ b/tests/mir-opt/gvn.slice_const_length.GVN.panic-unwind.diff @@ -0,0 +1,48 @@ +- // MIR for `slice_const_length` before GVN ++ // MIR for `slice_const_length` after GVN + + fn slice_const_length(_1: &[i32]) -> *const [i32] { + debug x => _1; + let mut _0: *const [i32]; + let _2: *const i32; + let mut _3: &[i32]; + let mut _5: *const i32; + let mut _6: usize; + scope 1 { + debug ptr => _2; + let _4: usize; + scope 2 { + debug len => _4; + } + } + + bb0: { +- StorageLive(_2); ++ nop; + StorageLive(_3); + _3 = &(*_1); + _2 = core::slice::::as_ptr(move _3) -> [return: bb1, unwind continue]; + } + + bb1: { + StorageDead(_3); +- StorageLive(_4); ++ nop; + _4 = const 123_usize; + StorageLive(_5); + _5 = _2; + StorageLive(_6); +- _6 = _4; +- _0 = *const [i32] from (move _5, move _6); ++ _6 = const 123_usize; ++ _0 = *const [i32] from (_2, const 123_usize); + StorageDead(_6); + StorageDead(_5); +- StorageDead(_4); +- StorageDead(_2); ++ nop; ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.slice_from_raw_parts_as_ptr.GVN.panic-abort.diff b/tests/mir-opt/gvn.slice_from_raw_parts_as_ptr.GVN.panic-abort.diff new file mode 100644 index 000000000000..75bcd2a8d72c --- /dev/null +++ b/tests/mir-opt/gvn.slice_from_raw_parts_as_ptr.GVN.panic-abort.diff @@ -0,0 +1,51 @@ +- // MIR for `slice_from_raw_parts_as_ptr` before GVN ++ // MIR for `slice_from_raw_parts_as_ptr` after GVN + + fn slice_from_raw_parts_as_ptr(_1: *const u16, _2: usize) -> (*const u16, *const f32) { + debug x => _1; + debug n => _2; + let mut _0: (*const u16, *const f32); + let _3: *const [u16]; + let mut _4: *const u16; + let mut _5: usize; + let mut _6: *const u16; + let mut _7: *const [u16]; + let mut _8: *const f32; + let mut _9: *const [u16]; + scope 1 { + debug ptr => _3; + } + + bb0: { +- StorageLive(_3); ++ nop; + StorageLive(_4); + _4 = _1; + StorageLive(_5); + _5 = _2; +- _3 = *const [u16] from (move _4, move _5); ++ _3 = *const [u16] from (_1, _2); + StorageDead(_5); + StorageDead(_4); + StorageLive(_6); + StorageLive(_7); + _7 = _3; +- _6 = move _7 as *const u16 (PtrToPtr); ++ _6 = _1; + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); + _9 = _3; +- _8 = move _9 as *const f32 (PtrToPtr); ++ _8 = _1 as *const f32 (PtrToPtr); + StorageDead(_9); +- _0 = (move _6, move _8); ++ _0 = (_1, move _8); + StorageDead(_8); + StorageDead(_6); +- StorageDead(_3); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/gvn.slice_from_raw_parts_as_ptr.GVN.panic-unwind.diff b/tests/mir-opt/gvn.slice_from_raw_parts_as_ptr.GVN.panic-unwind.diff new file mode 100644 index 000000000000..75bcd2a8d72c --- /dev/null +++ b/tests/mir-opt/gvn.slice_from_raw_parts_as_ptr.GVN.panic-unwind.diff @@ -0,0 +1,51 @@ +- // MIR for `slice_from_raw_parts_as_ptr` before GVN ++ // MIR for `slice_from_raw_parts_as_ptr` after GVN + + fn slice_from_raw_parts_as_ptr(_1: *const u16, _2: usize) -> (*const u16, *const f32) { + debug x => _1; + debug n => _2; + let mut _0: (*const u16, *const f32); + let _3: *const [u16]; + let mut _4: *const u16; + let mut _5: usize; + let mut _6: *const u16; + let mut _7: *const [u16]; + let mut _8: *const f32; + let mut _9: *const [u16]; + scope 1 { + debug ptr => _3; + } + + bb0: { +- StorageLive(_3); ++ nop; + StorageLive(_4); + _4 = _1; + StorageLive(_5); + _5 = _2; +- _3 = *const [u16] from (move _4, move _5); ++ _3 = *const [u16] from (_1, _2); + StorageDead(_5); + StorageDead(_4); + StorageLive(_6); + StorageLive(_7); + _7 = _3; +- _6 = move _7 as *const u16 (PtrToPtr); ++ _6 = _1; + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); + _9 = _3; +- _8 = move _9 as *const f32 (PtrToPtr); ++ _8 = _1 as *const f32 (PtrToPtr); + StorageDead(_9); +- _0 = (move _6, move _8); ++ _0 = (_1, move _8); + StorageDead(_8); + StorageDead(_6); +- StorageDead(_3); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/inline/inline_cycle.rs b/tests/mir-opt/inline/inline_cycle.rs index 1826e38f894d..651112ccfd7f 100644 --- a/tests/mir-opt/inline/inline_cycle.rs +++ b/tests/mir-opt/inline/inline_cycle.rs @@ -32,7 +32,6 @@ impl Call for A { } } - impl Call for B { #[inline] fn call() { diff --git a/tests/mir-opt/inline/inline_cycle_generic.rs b/tests/mir-opt/inline/inline_cycle_generic.rs index 667bf7f9254d..64f208b1c70b 100644 --- a/tests/mir-opt/inline/inline_cycle_generic.rs +++ b/tests/mir-opt/inline/inline_cycle_generic.rs @@ -26,7 +26,6 @@ impl Call for A { } } - impl Call for B { #[inline] fn call() { diff --git a/tests/mir-opt/inline/inline_options.rs b/tests/mir-opt/inline/inline_options.rs index 7d7c4f718bd7..bd9e0e2d2074 100644 --- a/tests/mir-opt/inline/inline_options.rs +++ b/tests/mir-opt/inline/inline_options.rs @@ -16,8 +16,16 @@ fn main() { // Cost is approximately 3 * 25 + 5 = 80. #[inline] -pub fn not_inlined() { g(); g(); g(); } -pub fn inlined() { g(); g(); g(); } +pub fn not_inlined() { + g(); + g(); + g(); +} +pub fn inlined() { + g(); + g(); + g(); +} #[inline(never)] fn g() {} diff --git a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff index df873600577a..45ce933a55ad 100644 --- a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff @@ -27,10 +27,12 @@ + } + } + scope 7 (inlined slice_from_raw_parts_mut::) { ++ scope 8 (inlined std::ptr::from_raw_parts_mut::<[A], A>) { ++ } + } + } + } -+ scope 8 (inlined std::ptr::drop_in_place::> - shim(Some(Option))) { ++ scope 9 (inlined std::ptr::drop_in_place::> - shim(Some(Option))) { + let mut _14: isize; + let mut _15: isize; + } diff --git a/tests/mir-opt/inline/inline_specialization.rs b/tests/mir-opt/inline/inline_specialization.rs index 6453abc00812..b9d40aca3a43 100644 --- a/tests/mir-opt/inline/inline_specialization.rs +++ b/tests/mir-opt/inline/inline_specialization.rs @@ -5,7 +5,7 @@ fn main() { // CHECK-LABEL: fn main( // CHECK: (inlined as Foo>::bar) - let x = as Foo>::bar(); + let x = as Foo>::bar(); } trait Foo { @@ -14,5 +14,7 @@ trait Foo { impl Foo for Vec { #[inline(always)] - default fn bar() -> u32 { 123 } + default fn bar() -> u32 { + 123 + } } diff --git a/tests/mir-opt/inline/issue_106141.outer.Inline.panic-abort.diff b/tests/mir-opt/inline/issue_106141.outer.Inline.panic-abort.diff index 24e5587f9632..2ab79cc2a509 100644 --- a/tests/mir-opt/inline/issue_106141.outer.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/issue_106141.outer.Inline.panic-abort.diff @@ -8,7 +8,7 @@ + let mut _2: bool; + let mut _3: bool; + scope 2 { -+ debug buffer => const inner::promoted[0]; ++ debug buffer => _1; + scope 3 { + debug index => _0; + } diff --git a/tests/mir-opt/inline/issue_106141.outer.Inline.panic-unwind.diff b/tests/mir-opt/inline/issue_106141.outer.Inline.panic-unwind.diff index 4eb8a5c98473..4d96a8628859 100644 --- a/tests/mir-opt/inline/issue_106141.outer.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/issue_106141.outer.Inline.panic-unwind.diff @@ -8,7 +8,7 @@ + let mut _2: bool; + let mut _3: bool; + scope 2 { -+ debug buffer => const inner::promoted[0]; ++ debug buffer => _1; + scope 3 { + debug index => _0; + } diff --git a/tests/mir-opt/inline/issue_106141.rs b/tests/mir-opt/inline/issue_106141.rs index 1cca7d76b25a..d311ad4527f2 100644 --- a/tests/mir-opt/inline/issue_106141.rs +++ b/tests/mir-opt/inline/issue_106141.rs @@ -19,11 +19,7 @@ fn inner() -> usize { // CHECK: = {{.*}}[_0]; let buffer = &[true]; let index = index(); - if buffer[index] { - index - } else { - 0 - } + if buffer[index] { index } else { 0 } } fn main() { diff --git a/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.rs b/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.rs index 75772c161277..021af7f81bb2 100644 --- a/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.rs +++ b/tests/mir-opt/inline/issue_76997_inline_scopes_parenting.rs @@ -13,6 +13,9 @@ fn main() { // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: } - let f = |x| { let y = x; y }; + let f = |x| { + let y = x; + y + }; f(()) } diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff index a624ab78dad1..0d9d58316ea1 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff @@ -8,6 +8,12 @@ let mut _3: u16; let mut _4: u32; + scope 1 (inlined core::num::::unchecked_shl) { ++ let mut _5: bool; ++ let _6: (); ++ scope 2 (inlined core::ub_checks::check_language_ub) { ++ scope 3 (inlined core::ub_checks::check_language_ub::runtime) { ++ } ++ } + } bb0: { @@ -16,10 +22,20 @@ StorageLive(_4); _4 = _2; - _0 = core::num::::unchecked_shl(move _3, move _4) -> [return: bb1, unwind unreachable]; -- } -- -- bb1: { ++ StorageLive(_6); ++ StorageLive(_5); ++ _5 = UbChecks(); ++ switchInt(move _5) -> [0: bb2, otherwise: bb1]; + } + + bb1: { ++ _6 = core::num::::unchecked_shl::precondition_check(_4) -> [return: bb2, unwind unreachable]; ++ } ++ ++ bb2: { ++ StorageDead(_5); + _0 = ShlUnchecked(_3, _4); ++ StorageDead(_6); StorageDead(_4); StorageDead(_3); return; diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff index 81a0a5b39f71..82f7eceaa180 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff @@ -8,6 +8,12 @@ let mut _3: u16; let mut _4: u32; + scope 1 (inlined core::num::::unchecked_shl) { ++ let mut _5: bool; ++ let _6: (); ++ scope 2 (inlined core::ub_checks::check_language_ub) { ++ scope 3 (inlined core::ub_checks::check_language_ub::runtime) { ++ } ++ } + } bb0: { @@ -16,10 +22,20 @@ StorageLive(_4); _4 = _2; - _0 = core::num::::unchecked_shl(move _3, move _4) -> [return: bb1, unwind continue]; -- } -- -- bb1: { ++ StorageLive(_6); ++ StorageLive(_5); ++ _5 = UbChecks(); ++ switchInt(move _5) -> [0: bb2, otherwise: bb1]; + } + + bb1: { ++ _6 = core::num::::unchecked_shl::precondition_check(_4) -> [return: bb2, unwind unreachable]; ++ } ++ ++ bb2: { ++ StorageDead(_5); + _0 = ShlUnchecked(_3, _4); ++ StorageDead(_6); StorageDead(_4); StorageDead(_3); return; diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir index 9e3802b7501f..dc27685ee79b 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir @@ -5,6 +5,10 @@ fn unchecked_shl_unsigned_smaller(_1: u16, _2: u32) -> u16 { debug b => _2; let mut _0: u16; scope 1 (inlined core::num::::unchecked_shl) { + scope 2 (inlined core::ub_checks::check_language_ub) { + scope 3 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } bb0: { diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir index 9e3802b7501f..dc27685ee79b 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir @@ -5,6 +5,10 @@ fn unchecked_shl_unsigned_smaller(_1: u16, _2: u32) -> u16 { debug b => _2; let mut _0: u16; scope 1 (inlined core::num::::unchecked_shl) { + scope 2 (inlined core::ub_checks::check_language_ub) { + scope 3 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } bb0: { diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff index a2b3ad4b3bab..6894b2466990 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff @@ -8,6 +8,12 @@ let mut _3: i64; let mut _4: u32; + scope 1 (inlined core::num::::unchecked_shr) { ++ let mut _5: bool; ++ let _6: (); ++ scope 2 (inlined core::ub_checks::check_language_ub) { ++ scope 3 (inlined core::ub_checks::check_language_ub::runtime) { ++ } ++ } + } bb0: { @@ -16,10 +22,20 @@ StorageLive(_4); _4 = _2; - _0 = core::num::::unchecked_shr(move _3, move _4) -> [return: bb1, unwind unreachable]; -- } -- -- bb1: { ++ StorageLive(_6); ++ StorageLive(_5); ++ _5 = UbChecks(); ++ switchInt(move _5) -> [0: bb2, otherwise: bb1]; + } + + bb1: { ++ _6 = core::num::::unchecked_shr::precondition_check(_4) -> [return: bb2, unwind unreachable]; ++ } ++ ++ bb2: { ++ StorageDead(_5); + _0 = ShrUnchecked(_3, _4); ++ StorageDead(_6); StorageDead(_4); StorageDead(_3); return; diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff index 2ff6b532d61f..070f4a1c5c83 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff @@ -8,6 +8,12 @@ let mut _3: i64; let mut _4: u32; + scope 1 (inlined core::num::::unchecked_shr) { ++ let mut _5: bool; ++ let _6: (); ++ scope 2 (inlined core::ub_checks::check_language_ub) { ++ scope 3 (inlined core::ub_checks::check_language_ub::runtime) { ++ } ++ } + } bb0: { @@ -16,10 +22,20 @@ StorageLive(_4); _4 = _2; - _0 = core::num::::unchecked_shr(move _3, move _4) -> [return: bb1, unwind continue]; -- } -- -- bb1: { ++ StorageLive(_6); ++ StorageLive(_5); ++ _5 = UbChecks(); ++ switchInt(move _5) -> [0: bb2, otherwise: bb1]; + } + + bb1: { ++ _6 = core::num::::unchecked_shr::precondition_check(_4) -> [return: bb2, unwind unreachable]; ++ } ++ ++ bb2: { ++ StorageDead(_5); + _0 = ShrUnchecked(_3, _4); ++ StorageDead(_6); StorageDead(_4); StorageDead(_3); return; diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir index f3961982648a..54f093b87d19 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-abort.mir @@ -5,6 +5,10 @@ fn unchecked_shr_signed_bigger(_1: i64, _2: u32) -> i64 { debug b => _2; let mut _0: i64; scope 1 (inlined core::num::::unchecked_shr) { + scope 2 (inlined core::ub_checks::check_language_ub) { + scope 3 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } bb0: { diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir index f3961982648a..54f093b87d19 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.PreCodegen.after.panic-unwind.mir @@ -5,6 +5,10 @@ fn unchecked_shr_signed_bigger(_1: i64, _2: u32) -> i64 { debug b => _2; let mut _0: i64; scope 1 (inlined core::num::::unchecked_shr) { + scope 2 (inlined core::ub_checks::check_language_ub) { + scope 3 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } bb0: { diff --git a/tests/mir-opt/instsimplify/casts.roundtrip.InstSimplify.diff b/tests/mir-opt/instsimplify/casts.roundtrip.InstSimplify.diff index a6d68cd4e4b8..e87ac762dfeb 100644 --- a/tests/mir-opt/instsimplify/casts.roundtrip.InstSimplify.diff +++ b/tests/mir-opt/instsimplify/casts.roundtrip.InstSimplify.diff @@ -14,7 +14,7 @@ StorageLive(_4); _4 = _1; _3 = move _4 as *mut u8 (PtrToPtr); - _2 = move _3 as *const u8 (PointerCoercion(MutToConstPointer)); + _2 = move _3 as *const u8 (PtrToPtr); StorageDead(_4); StorageDead(_3); - _0 = move _2 as *const u8 (PtrToPtr); diff --git a/tests/mir-opt/instsimplify/casts.rs b/tests/mir-opt/instsimplify/casts.rs index a7786fa570f1..15ceea767136 100644 --- a/tests/mir-opt/instsimplify/casts.rs +++ b/tests/mir-opt/instsimplify/casts.rs @@ -21,7 +21,7 @@ pub fn roundtrip(x: *const u8) -> *const u8 { // CHECK-LABEL: fn roundtrip( // CHECK: _4 = _1; // CHECK: _3 = move _4 as *mut u8 (PtrToPtr); - // CHECK: _2 = move _3 as *const u8 (PointerCoercion(MutToConstPointer)); + // CHECK: _2 = move _3 as *const u8 (PtrToPtr); x as *mut u8 as *const u8 } diff --git a/tests/mir-opt/instsimplify/combine_array_len.rs b/tests/mir-opt/instsimplify/combine_array_len.rs index 86455e8b52d5..f12284f64821 100644 --- a/tests/mir-opt/instsimplify/combine_array_len.rs +++ b/tests/mir-opt/instsimplify/combine_array_len.rs @@ -7,9 +7,9 @@ fn norm2(x: [f32; 2]) -> f32 { // CHECK-NOT: Len( let a = x[0]; let b = x[1]; - a*a + b*b + a * a + b * b } fn main() { - assert_eq!(norm2([3.0, 4.0]), 5.0*5.0); + assert_eq!(norm2([3.0, 4.0]), 5.0 * 5.0); } diff --git a/tests/mir-opt/instsimplify/combine_transmutes.rs b/tests/mir-opt/instsimplify/combine_transmutes.rs index 0be7466001fe..c3622c20697a 100644 --- a/tests/mir-opt/instsimplify/combine_transmutes.rs +++ b/tests/mir-opt/instsimplify/combine_transmutes.rs @@ -5,7 +5,7 @@ #![feature(custom_mir)] use std::intrinsics::mir::*; -use std::mem::{MaybeUninit, ManuallyDrop, transmute}; +use std::mem::{transmute, ManuallyDrop, MaybeUninit}; // EMIT_MIR combine_transmutes.identity_transmutes.InstSimplify.diff pub unsafe fn identity_transmutes() { @@ -61,4 +61,7 @@ pub unsafe fn adt_transmutes() { let _a: ManuallyDrop = transmute(MaybeUninit::::uninit()); } -pub union Union32 { u32: u32, i32: i32 } +pub union Union32 { + u32: u32, + i32: i32, +} diff --git a/tests/mir-opt/instsimplify/duplicate_switch_targets.rs b/tests/mir-opt/instsimplify/duplicate_switch_targets.rs index 454728249b1a..a47d9d5a71d7 100644 --- a/tests/mir-opt/instsimplify/duplicate_switch_targets.rs +++ b/tests/mir-opt/instsimplify/duplicate_switch_targets.rs @@ -10,7 +10,7 @@ use std::intrinsics::mir::*; pub unsafe fn assert_zero(x: u8) -> u8 { // CHECK-LABEL: fn assert_zero( // CHECK: switchInt({{.*}}) -> [0: {{bb.*}}, otherwise: {{bb.*}}] - mir!( + mir! { { match x { 0 => retblock, @@ -25,5 +25,5 @@ pub unsafe fn assert_zero(x: u8) -> u8 { RET = x; Return() } - ) + } } diff --git a/tests/mir-opt/instsimplify/ref_of_deref.pointers.InstSimplify.diff b/tests/mir-opt/instsimplify/ref_of_deref.pointers.InstSimplify.diff new file mode 100644 index 000000000000..52b3d1e1d40b --- /dev/null +++ b/tests/mir-opt/instsimplify/ref_of_deref.pointers.InstSimplify.diff @@ -0,0 +1,58 @@ +- // MIR for `pointers` before InstSimplify ++ // MIR for `pointers` after InstSimplify + + fn pointers(_1: *const [i32], _2: *mut i32) -> () { + debug const_ptr => _1; + debug mut_ptr => _2; + let mut _0: (); + let _3: &[i32]; + scope 1 { + debug _a => _3; + let _4: &i32; + scope 2 { + debug _b => _4; + let _5: &mut i32; + scope 3 { + debug _c => _5; + let _6: *const [i32]; + scope 4 { + debug _d => _6; + let _7: *const i32; + scope 5 { + debug _e => _7; + let _8: *mut i32; + scope 6 { + debug _f => _8; + } + } + } + } + } + } + + bb0: { + StorageLive(_3); + _3 = &(*_1); + StorageLive(_4); + _4 = &(*_2); + StorageLive(_5); + _5 = &mut (*_2); + StorageLive(_6); +- _6 = &raw const (*_1); ++ _6 = _1; + StorageLive(_7); + _7 = &raw const (*_2); + StorageLive(_8); +- _8 = &raw mut (*_2); ++ _8 = _2; + _0 = const (); + StorageDead(_8); + StorageDead(_7); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/ref_of_deref.references.InstSimplify.diff b/tests/mir-opt/instsimplify/ref_of_deref.references.InstSimplify.diff new file mode 100644 index 000000000000..ca0828a225a0 --- /dev/null +++ b/tests/mir-opt/instsimplify/ref_of_deref.references.InstSimplify.diff @@ -0,0 +1,58 @@ +- // MIR for `references` before InstSimplify ++ // MIR for `references` after InstSimplify + + fn references(_1: &i32, _2: &mut [i32]) -> () { + debug const_ref => _1; + debug mut_ref => _2; + let mut _0: (); + let _3: &i32; + scope 1 { + debug _a => _3; + let _4: &[i32]; + scope 2 { + debug _b => _4; + let _5: &mut [i32]; + scope 3 { + debug _c => _5; + let _6: *const i32; + scope 4 { + debug _d => _6; + let _7: *const [i32]; + scope 5 { + debug _e => _7; + let _8: *mut [i32]; + scope 6 { + debug _f => _8; + } + } + } + } + } + } + + bb0: { + StorageLive(_3); +- _3 = &(*_1); ++ _3 = _1; + StorageLive(_4); + _4 = &(*_2); + StorageLive(_5); +- _5 = &mut (*_2); ++ _5 = _2; + StorageLive(_6); + _6 = &raw const (*_1); + StorageLive(_7); + _7 = &raw const (*_2); + StorageLive(_8); + _8 = &raw mut (*_2); + _0 = const (); + StorageDead(_8); + StorageDead(_7); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/ref_of_deref.rs b/tests/mir-opt/instsimplify/ref_of_deref.rs new file mode 100644 index 000000000000..37e164bc17f1 --- /dev/null +++ b/tests/mir-opt/instsimplify/ref_of_deref.rs @@ -0,0 +1,40 @@ +//@ test-mir-pass: InstSimplify +#![crate_type = "lib"] +#![feature(raw_ref_op)] + +// For each of these, only 2 of the 6 should simplify, +// as the others have the wrong types. + +// EMIT_MIR ref_of_deref.references.InstSimplify.diff +// CHECK-LABEL: references +pub fn references(const_ref: &i32, mut_ref: &mut [i32]) { + // CHECK: _3 = _1; + let _a = &*const_ref; + // CHECK: _4 = &(*_2); + let _b = &*mut_ref; + // CHECK: _5 = _2; + let _c = &mut *mut_ref; + // CHECK: _6 = &raw const (*_1); + let _d = &raw const *const_ref; + // CHECK: _7 = &raw const (*_2); + let _e = &raw const *mut_ref; + // CHECK: _8 = &raw mut (*_2); + let _f = &raw mut *mut_ref; +} + +// EMIT_MIR ref_of_deref.pointers.InstSimplify.diff +// CHECK-LABEL: pointers +pub unsafe fn pointers(const_ptr: *const [i32], mut_ptr: *mut i32) { + // CHECK: _3 = &(*_1); + let _a = &*const_ptr; + // CHECK: _4 = &(*_2); + let _b = &*mut_ptr; + // CHECK: _5 = &mut (*_2); + let _c = &mut *mut_ptr; + // CHECK: _6 = _1; + let _d = &raw const *const_ptr; + // CHECK: _7 = &raw const (*_2); + let _e = &raw const *mut_ptr; + // CHECK: _8 = _2; + let _f = &raw mut *mut_ptr; +} diff --git a/tests/mir-opt/issue_104451_unwindable_intrinsics.rs b/tests/mir-opt/issue_104451_unwindable_intrinsics.rs index cd068f122367..80655a583c34 100644 --- a/tests/mir-opt/issue_104451_unwindable_intrinsics.rs +++ b/tests/mir-opt/issue_104451_unwindable_intrinsics.rs @@ -5,9 +5,7 @@ // EMIT_MIR issue_104451_unwindable_intrinsics.main.AbortUnwindingCalls.after.mir fn main() { - unsafe { - core::intrinsics::const_eval_select((), ow_ct, ow_ct) - } + unsafe { core::intrinsics::const_eval_select((), ow_ct, ow_ct) } } const fn ow_ct() -> ! { diff --git a/tests/mir-opt/issue_41110.rs b/tests/mir-opt/issue_41110.rs index 38602d5eaef4..5d042815f3b4 100644 --- a/tests/mir-opt/issue_41110.rs +++ b/tests/mir-opt/issue_41110.rs @@ -3,7 +3,6 @@ // check that we don't emit multiple drop flags when they are not needed. - // EMIT_MIR issue_41110.main.ElaborateDrops.diff fn main() { let x = S.other(S.id()); @@ -21,11 +20,12 @@ pub fn test() { struct S; impl Drop for S { - fn drop(&mut self) { - } + fn drop(&mut self) {} } impl S { - fn id(self) -> Self { self } + fn id(self) -> Self { + self + } fn other(self, s: Self) {} } diff --git a/tests/mir-opt/issue_41697.rs b/tests/mir-opt/issue_41697.rs index 92d382c39407..b031f1dc7201 100644 --- a/tests/mir-opt/issue_41697.rs +++ b/tests/mir-opt/issue_41697.rs @@ -14,9 +14,8 @@ trait Foo { fn get(&self) -> [u8; 2]; } - // EMIT_MIR issue_41697.{impl#0}-{constant#0}.SimplifyCfg-promote-consts.after.mir -impl Foo for [u8; 1+1] { +impl Foo for [u8; 1 + 1] { fn get(&self) -> [u8; 2] { *self } @@ -33,7 +32,7 @@ fn unsize_nested_fat_ptr(x: Arc) -> Arc { } fn main() { - let x: Box> = Box::new(Bar([1,2])); + let x: Box> = Box::new(Bar([1, 2])); assert_eq!(unsize_fat_ptr(&*x).0.get(), [1, 2]); let x: Arc = Arc::new([3, 4]); diff --git a/tests/mir-opt/issue_41697.{impl#0}-{constant#0}.SimplifyCfg-promote-consts.after.mir b/tests/mir-opt/issue_41697.{impl#0}-{constant#0}.SimplifyCfg-promote-consts.after.mir index 7dafeabaacc7..27a5a8dd5526 100644 --- a/tests/mir-opt/issue_41697.{impl#0}-{constant#0}.SimplifyCfg-promote-consts.after.mir +++ b/tests/mir-opt/issue_41697.{impl#0}-{constant#0}.SimplifyCfg-promote-consts.after.mir @@ -1,11 +1,11 @@ -// MIR for `::{constant#0}` after SimplifyCfg-promote-consts +// MIR for `::{constant#0}` after SimplifyCfg-promote-consts -::{constant#0}: usize = { +::{constant#0}: usize = { let mut _0: usize; let mut _1: (usize, bool); bb0: { - _1 = CheckedAdd(const 1_usize, const 1_usize); + _1 = AddWithOverflow(const 1_usize, const 1_usize); assert(!move (_1.1: bool), "attempt to compute `{} + {}`, which would overflow", const 1_usize, const 1_usize) -> [success: bb1, unwind: bb2]; } diff --git a/tests/mir-opt/issue_41888.rs b/tests/mir-opt/issue_41888.rs index 70b202186336..1744d9f85701 100644 --- a/tests/mir-opt/issue_41888.rs +++ b/tests/mir-opt/issue_41888.rs @@ -15,11 +15,13 @@ fn main() { } } -fn cond() -> bool { false } +fn cond() -> bool { + false +} struct K; enum E { F(K), - G(Box) + G(Box), } diff --git a/tests/mir-opt/issue_72181.rs b/tests/mir-opt/issue_72181.rs index 3748c2af83d2..dea8ecbd3ec7 100644 --- a/tests/mir-opt/issue_72181.rs +++ b/tests/mir-opt/issue_72181.rs @@ -9,16 +9,18 @@ enum Never {} union Foo { a: u64, - b: Never + b: Never, } - // EMIT_MIR issue_72181.foo.built.after.mir -fn foo(xs: [(Never, u32); 1]) -> u32 { xs[0].1 } +fn foo(xs: [(Never, u32); 1]) -> u32 { + xs[0].1 +} // EMIT_MIR issue_72181.bar.built.after.mir -fn bar([(_, x)]: [(Never, u32); 1]) -> u32 { x } - +fn bar([(_, x)]: [(Never, u32); 1]) -> u32 { + x +} // EMIT_MIR issue_72181.main.built.after.mir fn main() { diff --git a/tests/mir-opt/issue_72181_1.rs b/tests/mir-opt/issue_72181_1.rs index 32e946559d77..5c8346166aec 100644 --- a/tests/mir-opt/issue_72181_1.rs +++ b/tests/mir-opt/issue_72181_1.rs @@ -14,9 +14,7 @@ fn f(v: Void) -> ! { // EMIT_MIR issue_72181_1.main.built.after.mir fn main() { - let v: Void = unsafe { - std::mem::transmute::<(), Void>(()) - }; + let v: Void = unsafe { std::mem::transmute::<(), Void>(()) }; f(v); } diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff index b2539f391d1a..1f88339b5861 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff @@ -29,8 +29,8 @@ _3 = &_4; _2 = move _3 as &[T] (PointerCoercion(Unsize)); StorageDead(_3); - _5 = const 3_usize; - _6 = const true; + nop; + nop; goto -> bb2; } diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff index ff7f12c093ce..19a581ba3f09 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff @@ -29,8 +29,8 @@ _3 = &_4; _2 = move _3 as &[T] (PointerCoercion(Unsize)); StorageDead(_3); - _5 = const 3_usize; - _6 = const true; + nop; + nop; goto -> bb2; } diff --git a/tests/mir-opt/issue_91633.rs b/tests/mir-opt/issue_91633.rs index f7d59f5adfb6..d24c2e19aa73 100644 --- a/tests/mir-opt/issue_91633.rs +++ b/tests/mir-opt/issue_91633.rs @@ -1,32 +1,30 @@ // skip-filecheck //@ compile-flags: -Z mir-opt-level=0 // EMIT_MIR issue_91633.hey.built.after.mir -fn hey (it: &[T]) - where - [T] : std::ops::Index, - { - let _ = &it[0]; - } +fn hey(it: &[T]) +where + [T]: std::ops::Index, +{ + let _ = &it[0]; +} // EMIT_MIR issue_91633.bar.built.after.mir -fn bar (it: Box<[T]>) - where - [T] : std::ops::Index, - { - let _ = it[0]; - } +fn bar(it: Box<[T]>) +where + [T]: std::ops::Index, +{ + let _ = it[0]; +} // EMIT_MIR issue_91633.fun.built.after.mir -fn fun (it: &[T]) -> &T - { - let f = &it[0]; - f - } +fn fun(it: &[T]) -> &T { + let f = &it[0]; + f +} // EMIT_MIR issue_91633.foo.built.after.mir -fn foo (it: Box<[T]>) -> T - { - let f = it[0].clone(); - f - } - fn main(){} +fn foo(it: Box<[T]>) -> T { + let f = it[0].clone(); + f +} +fn main() {} diff --git a/tests/mir-opt/issue_99325.main.built.after.32bit.mir b/tests/mir-opt/issue_99325.main.built.after.32bit.mir index 0b0bac73e9f4..b7a054b5d540 100644 --- a/tests/mir-opt/issue_99325.main.built.after.32bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.32bit.mir @@ -2,7 +2,7 @@ | User Type Annotations | 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }: &'static [u8; 4_usize]], user_self_ty: None }), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }], user_self_ty: None }), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/issue_99325.main.built.after.64bit.mir b/tests/mir-opt/issue_99325.main.built.after.64bit.mir index 0b0bac73e9f4..b7a054b5d540 100644 --- a/tests/mir-opt/issue_99325.main.built.after.64bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.64bit.mir @@ -2,7 +2,7 @@ | User Type Annotations | 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }: &'static [u8; 4_usize]], user_self_ty: None }), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }], user_self_ty: None }), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/issues/issue_75439.rs b/tests/mir-opt/issues/issue_75439.rs index 8c710a33aa8e..ea763dd1b10d 100644 --- a/tests/mir-opt/issues/issue_75439.rs +++ b/tests/mir-opt/issues/issue_75439.rs @@ -8,11 +8,7 @@ pub fn foo(bytes: [u8; 16]) -> Option<[u8; 4]> { // big endian `u32`s let dwords: [u32; 4] = unsafe { transmute(bytes) }; const FF: u32 = 0x0000_ffff_u32.to_be(); - if let [0, 0, 0 | FF, ip] = dwords { - Some(unsafe { transmute(ip) }) - } else { - None - } + if let [0, 0, 0 | FF, ip] = dwords { Some(unsafe { transmute(ip) }) } else { None } } fn main() { diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs index 57f4e4a2654f..b4c133716800 100644 --- a/tests/mir-opt/jump_threading.rs +++ b/tests/mir-opt/jump_threading.rs @@ -157,7 +157,7 @@ fn custom_discr(x: bool) -> u8 { #[custom_mir(dialect = "runtime", phase = "post-cleanup")] fn multiple_match(x: u8) -> u8 { // CHECK-LABEL: fn multiple_match( - mir!( + mir! { { // CHECK: bb0: { // CHECK: switchInt([[x:_.*]]) -> [3: bb1, otherwise: bb2]; @@ -220,7 +220,7 @@ fn multiple_match(x: u8) -> u8 { RET = 11; Return() } - ) + } } /// Both 1-3-4 and 2-3-4 are threadable. As 1 and 2 are the only predecessors of 3, @@ -228,7 +228,7 @@ fn multiple_match(x: u8) -> u8 { #[custom_mir(dialect = "runtime", phase = "post-cleanup")] fn duplicate_chain(x: bool) -> u8 { // CHECK-LABEL: fn duplicate_chain( - mir!( + mir! { let a: u8; { // CHECK: bb0: { @@ -278,7 +278,7 @@ fn duplicate_chain(x: bool) -> u8 { RET = 9; Return() } - ) + } } #[rustc_layout_scalar_valid_range_start(1)] @@ -292,7 +292,7 @@ fn mutate_discriminant() -> u8 { // CHECK-NOT: goto -> {{bb.*}}; // CHECK: switchInt( // CHECK-NOT: goto -> {{bb.*}}; - mir!( + mir! { let x: Option; { SetDiscriminant(x, 1); @@ -313,7 +313,7 @@ fn mutate_discriminant() -> u8 { RET = 2; Unreachable() } - ) + } } /// Verify that we do not try to reason when there are mutable pointers involved. @@ -330,11 +330,7 @@ fn mutable_ref() -> bool { let a = std::ptr::addr_of_mut!(x); x = 7; unsafe { *a = 8 }; - if x == 7 { - true - } else { - false - } + if x == 7 { true } else { false } } /// This function has 2 TOs: 1-3-4 and 0-1-3-4-6. @@ -342,7 +338,7 @@ fn mutable_ref() -> bool { #[custom_mir(dialect = "runtime", phase = "post-cleanup")] fn renumbered_bb(x: bool) -> u8 { // CHECK-LABEL: fn renumbered_bb( - mir!( + mir! { let a: bool; let b: bool; { @@ -398,7 +394,7 @@ fn renumbered_bb(x: bool) -> u8 { // Duplicate of bb4. // CHECK: bb9: { // CHECK-NEXT: goto -> bb6; - ) + } } /// This function has 3 TOs: 1-4-5, 0-1-4-7-5-8 and 3-4-7-5-6 @@ -408,7 +404,7 @@ fn renumbered_bb(x: bool) -> u8 { #[custom_mir(dialect = "runtime", phase = "post-cleanup")] fn disappearing_bb(x: u8) -> u8 { // CHECK-LABEL: fn disappearing_bb( - mir!( + mir! { let a: bool; let b: bool; { @@ -450,7 +446,7 @@ fn disappearing_bb(x: u8) -> u8 { // CHECK: goto -> bb5; // CHECK: bb10: { // CHECK: goto -> bb6; - ) + } } /// Verify that we can thread jumps when we assign from an aggregate constant. @@ -461,18 +457,14 @@ fn aggregate(x: u8) -> u8 { const FOO: (u8, u8) = (5, 13); let (a, b) = FOO; - if a == 7 { - b - } else { - a - } + if a == 7 { b } else { a } } /// Verify that we can leverage the existence of an `Assume` terminator. #[custom_mir(dialect = "runtime", phase = "post-cleanup")] fn assume(a: u8, b: bool) -> u8 { // CHECK-LABEL: fn assume( - mir!( + mir! { { // CHECK: bb0: { // CHECK-NEXT: switchInt(_1) -> [7: bb1, otherwise: bb2] @@ -511,7 +503,7 @@ fn assume(a: u8, b: bool) -> u8 { } // CHECK: bb6: { // CHECK-NEXT: goto -> bb5; - ) + } } fn main() { diff --git a/tests/mir-opt/lower_array_len.rs b/tests/mir-opt/lower_array_len.rs index 1c30c4c89b96..62fc9ef67d66 100644 --- a/tests/mir-opt/lower_array_len.rs +++ b/tests/mir-opt/lower_array_len.rs @@ -7,11 +7,7 @@ pub fn array_bound(index: usize, slice: &[u8; N]) -> u8 { // CHECK-LABEL: fn array_bound( // CHECK: [[len:_.*]] = const N; // CHECK: Lt(move {{_.*}}, move [[len]]); - if index < slice.len() { - slice[index] - } else { - 42 - } + if index < slice.len() { slice[index] } else { 42 } } // EMIT_MIR lower_array_len.array_bound_mut.NormalizeArrayLen.diff diff --git a/tests/mir-opt/lower_intrinsics.get_metadata.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.get_metadata.LowerIntrinsics.panic-abort.diff new file mode 100644 index 000000000000..d256058c05ee --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.get_metadata.LowerIntrinsics.panic-abort.diff @@ -0,0 +1,63 @@ +- // MIR for `get_metadata` before LowerIntrinsics ++ // MIR for `get_metadata` after LowerIntrinsics + + fn get_metadata(_1: *const i32, _2: *const [u8], _3: *const dyn Debug) -> () { + debug a => _1; + debug b => _2; + debug c => _3; + let mut _0: (); + let _4: (); + let mut _5: *const i32; + let mut _7: *const [u8]; + let mut _9: *const dyn std::fmt::Debug; + scope 1 { + debug _unit => _4; + let _6: usize; + scope 2 { + debug _usize => _6; + let _8: std::ptr::DynMetadata; + scope 3 { + debug _vtable => _8; + } + } + } + + bb0: { + StorageLive(_4); + StorageLive(_5); + _5 = _1; +- _4 = ptr_metadata::(move _5) -> [return: bb1, unwind unreachable]; ++ _4 = PtrMetadata(move _5); ++ goto -> bb1; + } + + bb1: { + StorageDead(_5); + StorageLive(_6); + StorageLive(_7); + _7 = _2; +- _6 = ptr_metadata::<[u8], usize>(move _7) -> [return: bb2, unwind unreachable]; ++ _6 = PtrMetadata(move _7); ++ goto -> bb2; + } + + bb2: { + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); + _9 = _3; +- _8 = ptr_metadata::>(move _9) -> [return: bb3, unwind unreachable]; ++ _8 = PtrMetadata(move _9); ++ goto -> bb3; + } + + bb3: { + StorageDead(_9); + _0 = const (); + StorageDead(_8); + StorageDead(_6); + StorageDead(_4); + return; + } + } + diff --git a/tests/mir-opt/lower_intrinsics.get_metadata.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.get_metadata.LowerIntrinsics.panic-unwind.diff new file mode 100644 index 000000000000..d256058c05ee --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.get_metadata.LowerIntrinsics.panic-unwind.diff @@ -0,0 +1,63 @@ +- // MIR for `get_metadata` before LowerIntrinsics ++ // MIR for `get_metadata` after LowerIntrinsics + + fn get_metadata(_1: *const i32, _2: *const [u8], _3: *const dyn Debug) -> () { + debug a => _1; + debug b => _2; + debug c => _3; + let mut _0: (); + let _4: (); + let mut _5: *const i32; + let mut _7: *const [u8]; + let mut _9: *const dyn std::fmt::Debug; + scope 1 { + debug _unit => _4; + let _6: usize; + scope 2 { + debug _usize => _6; + let _8: std::ptr::DynMetadata; + scope 3 { + debug _vtable => _8; + } + } + } + + bb0: { + StorageLive(_4); + StorageLive(_5); + _5 = _1; +- _4 = ptr_metadata::(move _5) -> [return: bb1, unwind unreachable]; ++ _4 = PtrMetadata(move _5); ++ goto -> bb1; + } + + bb1: { + StorageDead(_5); + StorageLive(_6); + StorageLive(_7); + _7 = _2; +- _6 = ptr_metadata::<[u8], usize>(move _7) -> [return: bb2, unwind unreachable]; ++ _6 = PtrMetadata(move _7); ++ goto -> bb2; + } + + bb2: { + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); + _9 = _3; +- _8 = ptr_metadata::>(move _9) -> [return: bb3, unwind unreachable]; ++ _8 = PtrMetadata(move _9); ++ goto -> bb3; + } + + bb3: { + StorageDead(_9); + _0 = const (); + StorageDead(_8); + StorageDead(_6); + StorageDead(_4); + return; + } + } + diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs index 12e526ab0741..2569f4f4de5d 100644 --- a/tests/mir-opt/lower_intrinsics.rs +++ b/tests/mir-opt/lower_intrinsics.rs @@ -184,9 +184,9 @@ pub fn assume() { // EMIT_MIR lower_intrinsics.with_overflow.LowerIntrinsics.diff pub fn with_overflow(a: i32, b: i32) { // CHECK-LABEL: fn with_overflow( - // CHECK: CheckedAdd( - // CHECK: CheckedSub( - // CHECK: CheckedMul( + // CHECK: AddWithOverflow( + // CHECK: SubWithOverflow( + // CHECK: MulWithOverflow( let _x = core::intrinsics::add_with_overflow(a, b); let _y = core::intrinsics::sub_with_overflow(a, b); @@ -258,3 +258,12 @@ pub fn make_pointers(a: *const u8, b: *mut (), n: usize) { let _slice_const: *const [u16] = aggregate_raw_ptr(a, n); let _slice_mut: *mut [u64] = aggregate_raw_ptr(b, n); } + +// EMIT_MIR lower_intrinsics.get_metadata.LowerIntrinsics.diff +pub fn get_metadata(a: *const i32, b: *const [u8], c: *const dyn std::fmt::Debug) { + use std::intrinsics::ptr_metadata; + + let _unit = ptr_metadata(a); + let _usize = ptr_metadata(b); + let _vtable = ptr_metadata(c); +} diff --git a/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.panic-abort.diff index da84449aaa52..efbbeeeac73d 100644 --- a/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.panic-abort.diff +++ b/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.panic-abort.diff @@ -31,7 +31,7 @@ StorageLive(_5); _5 = _2; - _3 = add_with_overflow::(move _4, move _5) -> [return: bb1, unwind unreachable]; -+ _3 = CheckedAdd(move _4, move _5); ++ _3 = AddWithOverflow(move _4, move _5); + goto -> bb1; } @@ -44,7 +44,7 @@ StorageLive(_8); _8 = _2; - _6 = sub_with_overflow::(move _7, move _8) -> [return: bb2, unwind unreachable]; -+ _6 = CheckedSub(move _7, move _8); ++ _6 = SubWithOverflow(move _7, move _8); + goto -> bb2; } @@ -57,7 +57,7 @@ StorageLive(_11); _11 = _2; - _9 = mul_with_overflow::(move _10, move _11) -> [return: bb3, unwind unreachable]; -+ _9 = CheckedMul(move _10, move _11); ++ _9 = MulWithOverflow(move _10, move _11); + goto -> bb3; } diff --git a/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.panic-unwind.diff index da84449aaa52..efbbeeeac73d 100644 --- a/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.panic-unwind.diff +++ b/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.panic-unwind.diff @@ -31,7 +31,7 @@ StorageLive(_5); _5 = _2; - _3 = add_with_overflow::(move _4, move _5) -> [return: bb1, unwind unreachable]; -+ _3 = CheckedAdd(move _4, move _5); ++ _3 = AddWithOverflow(move _4, move _5); + goto -> bb1; } @@ -44,7 +44,7 @@ StorageLive(_8); _8 = _2; - _6 = sub_with_overflow::(move _7, move _8) -> [return: bb2, unwind unreachable]; -+ _6 = CheckedSub(move _7, move _8); ++ _6 = SubWithOverflow(move _7, move _8); + goto -> bb2; } @@ -57,7 +57,7 @@ StorageLive(_11); _11 = _2; - _9 = mul_with_overflow::(move _10, move _11) -> [return: bb3, unwind unreachable]; -+ _9 = CheckedMul(move _10, move _11); ++ _9 = MulWithOverflow(move _10, move _11); + goto -> bb3; } diff --git a/tests/mir-opt/lower_slice_len.rs b/tests/mir-opt/lower_slice_len.rs index b82094dc18f9..6fd7e4bf72cd 100644 --- a/tests/mir-opt/lower_slice_len.rs +++ b/tests/mir-opt/lower_slice_len.rs @@ -5,11 +5,7 @@ pub fn bound(index: usize, slice: &[u8]) -> u8 { // CHECK-LABEL: fn bound( // CHECK-NOT: ::len( - if index < slice.len() { - slice[index] - } else { - 42 - } + if index < slice.len() { slice[index] } else { 42 } } fn main() { diff --git a/tests/mir-opt/matches_reduce_branches.rs b/tests/mir-opt/matches_reduce_branches.rs index fa466220b653..176d68bcd401 100644 --- a/tests/mir-opt/matches_reduce_branches.rs +++ b/tests/mir-opt/matches_reduce_branches.rs @@ -88,7 +88,7 @@ fn match_u8_i16(i: EnumAu8) -> i16 { fn match_u8_i16_2(i: EnumAu8) -> i16 { // CHECK-LABEL: fn match_u8_i16_2( // CHECK: switchInt - mir!( + mir! { { let a = Discriminant(i); match a { @@ -110,7 +110,7 @@ fn match_u8_i16_2(i: EnumAu8) -> i16 { ret = { Return() } - ) + } } // EMIT_MIR matches_reduce_branches.match_u8_i16_failed.MatchBranchSimplification.diff @@ -158,7 +158,7 @@ fn match_u8_u16(i: EnumBu8) -> u16 { fn match_u8_u16_2(i: EnumBu8) -> i16 { // CHECK-LABEL: fn match_u8_u16_2( // CHECK: switchInt - mir!( + mir! { { let a = Discriminant(i); match a { @@ -187,7 +187,7 @@ fn match_u8_u16_2(i: EnumBu8) -> i16 { ret = { Return() } - ) + } } #[repr(i8)] diff --git a/tests/mir-opt/matches_u8.rs b/tests/mir-opt/matches_u8.rs index f0be82d02571..86d646256742 100644 --- a/tests/mir-opt/matches_u8.rs +++ b/tests/mir-opt/matches_u8.rs @@ -1,7 +1,6 @@ // skip-filecheck //@ test-mir-pass: MatchBranchSimplification - // EMIT_MIR matches_u8.exhaustive_match.MatchBranchSimplification.diff // EMIT_MIR matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff @@ -27,9 +26,9 @@ pub fn exhaustive_match_i8(e: E) -> i8 { } fn main() { - assert_eq!(exhaustive_match(E::A), 0); - assert_eq!(exhaustive_match(E::B), 1); + assert_eq!(exhaustive_match(E::A), 0); + assert_eq!(exhaustive_match(E::B), 1); - assert_eq!(exhaustive_match_i8(E::A), 0); - assert_eq!(exhaustive_match_i8(E::B), 1); + assert_eq!(exhaustive_match_i8(E::A), 0); + assert_eq!(exhaustive_match_i8(E::B), 1); } diff --git a/tests/mir-opt/nll/named_lifetimes_basic.rs b/tests/mir-opt/nll/named_lifetimes_basic.rs index cc8385370038..93f4a8bfd598 100644 --- a/tests/mir-opt/nll/named_lifetimes_basic.rs +++ b/tests/mir-opt/nll/named_lifetimes_basic.rs @@ -10,7 +10,8 @@ #![allow(warnings)] // EMIT_MIR named_lifetimes_basic.use_x.nll.0.mir -fn use_x<'a, 'b: 'a, 'c>(w: &'a mut i32, x: &'b u32, y: &'a u32, z: &'c u32) -> bool { true } - -fn main() { +fn use_x<'a, 'b: 'a, 'c>(w: &'a mut i32, x: &'b u32, y: &'a u32, z: &'c u32) -> bool { + true } + +fn main() {} diff --git a/tests/mir-opt/nrvo_miscompile_111005.rs b/tests/mir-opt/nrvo_miscompile_111005.rs index 18814b0678fe..03008fa81915 100644 --- a/tests/mir-opt/nrvo_miscompile_111005.rs +++ b/tests/mir-opt/nrvo_miscompile_111005.rs @@ -10,12 +10,14 @@ use core::intrinsics::mir::*; // EMIT_MIR nrvo_miscompile_111005.wrong.RenameReturnPlace.diff #[custom_mir(dialect = "runtime", phase = "initial")] pub fn wrong(arg: char) -> char { - mir!({ - let temp = arg; - RET = temp; - temp = 'b'; - Return() - }) + mir! { + { + let temp = arg; + RET = temp; + temp = 'b'; + Return() + } + } } fn main() { diff --git a/tests/mir-opt/nrvo_simple.rs b/tests/mir-opt/nrvo_simple.rs index 5d2894a704a5..df540472e1cc 100644 --- a/tests/mir-opt/nrvo_simple.rs +++ b/tests/mir-opt/nrvo_simple.rs @@ -10,5 +10,7 @@ fn nrvo(init: fn(&mut [u8; 1024])) -> [u8; 1024] { } fn main() { - let _ = nrvo(|buf| { buf[4] = 4; }); + let _ = nrvo(|buf| { + buf[4] = 4; + }); } diff --git a/tests/mir-opt/or_pattern.rs b/tests/mir-opt/or_pattern.rs new file mode 100644 index 000000000000..0ad0ce8ead1e --- /dev/null +++ b/tests/mir-opt/or_pattern.rs @@ -0,0 +1,24 @@ +// skip-filecheck + +// EMIT_MIR or_pattern.shortcut_second_or.SimplifyCfg-initial.after.mir +fn shortcut_second_or() { + // Check that after matching `0`, failing to match `2 | 3` skips trying to match `(1, 2 | 3)`. + match ((0, 0), 0) { + (x @ (0, _) | x @ (_, 1), y @ 2 | y @ 3) => {} + _ => {} + } +} + +// EMIT_MIR or_pattern.single_switchint.SimplifyCfg-initial.after.mir +fn single_switchint() { + // Check how many `SwitchInt`s we do. In theory a single one is necessary. + match (1, true) { + (1, true) => 1, + (2, false) => 2, + (1 | 2, true | false) => 3, + (3 | 4, true | false) => 4, + _ => 5, + }; +} + +fn main() {} diff --git a/tests/mir-opt/or_pattern.shortcut_second_or.SimplifyCfg-initial.after.mir b/tests/mir-opt/or_pattern.shortcut_second_or.SimplifyCfg-initial.after.mir new file mode 100644 index 000000000000..56edd38a6da4 --- /dev/null +++ b/tests/mir-opt/or_pattern.shortcut_second_or.SimplifyCfg-initial.after.mir @@ -0,0 +1,100 @@ +// MIR for `shortcut_second_or` after SimplifyCfg-initial + +fn shortcut_second_or() -> () { + let mut _0: (); + let mut _1: ((i32, i32), i32); + let mut _2: (i32, i32); + let _3: (i32, i32); + let _4: i32; + scope 1 { + debug x => _3; + debug y => _4; + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + _2 = (const 0_i32, const 0_i32); + _1 = (move _2, const 0_i32); + StorageDead(_2); + PlaceMention(_1); + switchInt(((_1.0: (i32, i32)).0: i32)) -> [0: bb4, otherwise: bb2]; + } + + bb1: { + _0 = const (); + goto -> bb14; + } + + bb2: { + switchInt(((_1.0: (i32, i32)).1: i32)) -> [1: bb3, otherwise: bb1]; + } + + bb3: { + switchInt((_1.1: i32)) -> [2: bb7, 3: bb8, otherwise: bb1]; + } + + bb4: { + switchInt((_1.1: i32)) -> [2: bb5, 3: bb6, otherwise: bb1]; + } + + bb5: { + falseEdge -> [real: bb10, imaginary: bb6]; + } + + bb6: { + falseEdge -> [real: bb11, imaginary: bb2]; + } + + bb7: { + falseEdge -> [real: bb12, imaginary: bb8]; + } + + bb8: { + falseEdge -> [real: bb13, imaginary: bb1]; + } + + bb9: { + _0 = const (); + StorageDead(_4); + StorageDead(_3); + goto -> bb14; + } + + bb10: { + StorageLive(_3); + _3 = (_1.0: (i32, i32)); + StorageLive(_4); + _4 = (_1.1: i32); + goto -> bb9; + } + + bb11: { + StorageLive(_3); + _3 = (_1.0: (i32, i32)); + StorageLive(_4); + _4 = (_1.1: i32); + goto -> bb9; + } + + bb12: { + StorageLive(_3); + _3 = (_1.0: (i32, i32)); + StorageLive(_4); + _4 = (_1.1: i32); + goto -> bb9; + } + + bb13: { + StorageLive(_3); + _3 = (_1.0: (i32, i32)); + StorageLive(_4); + _4 = (_1.1: i32); + goto -> bb9; + } + + bb14: { + StorageDead(_1); + return; + } +} diff --git a/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir b/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir new file mode 100644 index 000000000000..eafe95b4a11e --- /dev/null +++ b/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir @@ -0,0 +1,75 @@ +// MIR for `single_switchint` after SimplifyCfg-initial + +fn single_switchint() -> () { + let mut _0: (); + let _1: i32; + let mut _2: (i32, bool); + + bb0: { + StorageLive(_1); + StorageLive(_2); + _2 = (const 1_i32, const true); + PlaceMention(_2); + switchInt((_2.0: i32)) -> [1: bb2, 2: bb4, otherwise: bb1]; + } + + bb1: { + switchInt((_2.0: i32)) -> [3: bb8, 4: bb8, otherwise: bb7]; + } + + bb2: { + switchInt((_2.1: bool)) -> [0: bb6, otherwise: bb3]; + } + + bb3: { + falseEdge -> [real: bb9, imaginary: bb4]; + } + + bb4: { + switchInt((_2.1: bool)) -> [0: bb5, otherwise: bb6]; + } + + bb5: { + falseEdge -> [real: bb10, imaginary: bb6]; + } + + bb6: { + falseEdge -> [real: bb11, imaginary: bb1]; + } + + bb7: { + _1 = const 5_i32; + goto -> bb13; + } + + bb8: { + falseEdge -> [real: bb12, imaginary: bb7]; + } + + bb9: { + _1 = const 1_i32; + goto -> bb13; + } + + bb10: { + _1 = const 2_i32; + goto -> bb13; + } + + bb11: { + _1 = const 3_i32; + goto -> bb13; + } + + bb12: { + _1 = const 4_i32; + goto -> bb13; + } + + bb13: { + StorageDead(_2); + StorageDead(_1); + _0 = const (); + return; + } +} diff --git a/tests/mir-opt/packed_struct_drop_aligned.rs b/tests/mir-opt/packed_struct_drop_aligned.rs index dff941c4fa0c..3abc6426e7f0 100644 --- a/tests/mir-opt/packed_struct_drop_aligned.rs +++ b/tests/mir-opt/packed_struct_drop_aligned.rs @@ -1,7 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY - // EMIT_MIR packed_struct_drop_aligned.main.SimplifyCfg-pre-optimizations.after.mir fn main() { let mut x = Packed(Aligned(Droppy(0))); diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir index af0a0efc2a66..4edf0d2c47c0 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir @@ -8,6 +8,10 @@ fn checked_shl(_1: u32, _2: u32) -> Option { let mut _3: bool; let mut _4: u32; scope 2 (inlined core::num::::unchecked_shl) { + scope 3 (inlined core::ub_checks::check_language_ub) { + scope 4 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir index af0a0efc2a66..4edf0d2c47c0 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir @@ -8,6 +8,10 @@ fn checked_shl(_1: u32, _2: u32) -> Option { let mut _3: bool; let mut _4: u32; scope 2 (inlined core::num::::unchecked_shl) { + scope 3 (inlined core::ub_checks::check_language_ub) { + scope 4 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } diff --git a/tests/mir-opt/pre-codegen/intrinsics.rs b/tests/mir-opt/pre-codegen/intrinsics.rs index e5c059cda12e..0482b85e95f8 100644 --- a/tests/mir-opt/pre-codegen/intrinsics.rs +++ b/tests/mir-opt/pre-codegen/intrinsics.rs @@ -12,7 +12,6 @@ pub fn f_unit() { f_dispatch(()); } - // EMIT_MIR intrinsics.f_u64.PreCodegen.after.mir pub fn f_u64() { f_dispatch(0u64); @@ -28,8 +27,7 @@ pub fn f_dispatch(t: T) { } #[inline(never)] -pub fn f_zst(_t: T) { -} +pub fn f_zst(_t: T) {} #[inline(never)] pub fn f_non_zst(_t: T) {} diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff index 17d83752fc13..465cb1a9b1f4 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff @@ -23,16 +23,15 @@ let mut _12: isize; let _13: std::alloc::AllocError; let mut _14: !; - let _15: &str; - let mut _16: &dyn std::fmt::Debug; - let mut _17: &std::alloc::AllocError; + let mut _15: &dyn std::fmt::Debug; + let mut _16: &std::alloc::AllocError; scope 7 { } scope 8 { } } scope 9 (inlined NonNull::<[u8]>::as_ptr) { - let mut _18: *const [u8]; + let mut _17: *const [u8]; } } scope 3 (inlined #[track_caller] Option::::unwrap) { @@ -87,36 +86,33 @@ StorageDead(_8); StorageDead(_7); StorageLive(_12); - StorageLive(_15); _12 = discriminant(_6); switchInt(move _12) -> [0: bb6, 1: bb5, otherwise: bb1]; } bb5: { - _15 = const "called `Result::unwrap()` on an `Err` value"; + StorageLive(_15); StorageLive(_16); - StorageLive(_17); - _17 = &_13; - _16 = move _17 as &dyn std::fmt::Debug (PointerCoercion(Unsize)); - StorageDead(_17); - _14 = result::unwrap_failed(move _15, move _16) -> unwind unreachable; + _16 = &_13; + _15 = move _16 as &dyn std::fmt::Debug (PointerCoercion(Unsize)); + StorageDead(_16); + _14 = result::unwrap_failed(const "called `Result::unwrap()` on an `Err` value", move _15) -> unwind unreachable; } bb6: { _5 = move ((_6 as Ok).0: std::ptr::NonNull<[u8]>); - StorageDead(_15); StorageDead(_12); StorageDead(_6); -- StorageLive(_18); +- StorageLive(_17); + nop; - _18 = (_5.0: *const [u8]); -- _4 = move _18 as *mut [u8] (PtrToPtr); -- StorageDead(_18); -+ _4 = _18 as *mut [u8] (PtrToPtr); + _17 = (_5.0: *const [u8]); +- _4 = move _17 as *mut [u8] (PtrToPtr); +- StorageDead(_17); ++ _4 = _17 as *mut [u8] (PtrToPtr); + nop; StorageDead(_5); - _3 = move _4 as *mut u8 (PtrToPtr); -+ _3 = _18 as *mut u8 (PtrToPtr); ++ _3 = _17 as *mut u8 (PtrToPtr); StorageDead(_4); StorageDead(_3); - StorageDead(_1); diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff index 1cf950402c36..925d8997b8a5 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff @@ -23,16 +23,15 @@ let mut _12: isize; let _13: std::alloc::AllocError; let mut _14: !; - let _15: &str; - let mut _16: &dyn std::fmt::Debug; - let mut _17: &std::alloc::AllocError; + let mut _15: &dyn std::fmt::Debug; + let mut _16: &std::alloc::AllocError; scope 7 { } scope 8 { } } scope 9 (inlined NonNull::<[u8]>::as_ptr) { - let mut _18: *const [u8]; + let mut _17: *const [u8]; } } scope 3 (inlined #[track_caller] Option::::unwrap) { @@ -87,36 +86,33 @@ StorageDead(_8); StorageDead(_7); StorageLive(_12); - StorageLive(_15); _12 = discriminant(_6); switchInt(move _12) -> [0: bb6, 1: bb5, otherwise: bb1]; } bb5: { - _15 = const "called `Result::unwrap()` on an `Err` value"; + StorageLive(_15); StorageLive(_16); - StorageLive(_17); - _17 = &_13; - _16 = move _17 as &dyn std::fmt::Debug (PointerCoercion(Unsize)); - StorageDead(_17); - _14 = result::unwrap_failed(move _15, move _16) -> unwind unreachable; + _16 = &_13; + _15 = move _16 as &dyn std::fmt::Debug (PointerCoercion(Unsize)); + StorageDead(_16); + _14 = result::unwrap_failed(const "called `Result::unwrap()` on an `Err` value", move _15) -> unwind unreachable; } bb6: { _5 = move ((_6 as Ok).0: std::ptr::NonNull<[u8]>); - StorageDead(_15); StorageDead(_12); StorageDead(_6); -- StorageLive(_18); +- StorageLive(_17); + nop; - _18 = (_5.0: *const [u8]); -- _4 = move _18 as *mut [u8] (PtrToPtr); -- StorageDead(_18); -+ _4 = _18 as *mut [u8] (PtrToPtr); + _17 = (_5.0: *const [u8]); +- _4 = move _17 as *mut [u8] (PtrToPtr); +- StorageDead(_17); ++ _4 = _17 as *mut [u8] (PtrToPtr); + nop; StorageDead(_5); - _3 = move _4 as *mut u8 (PtrToPtr); -+ _3 = _18 as *mut u8 (PtrToPtr); ++ _3 = _17 as *mut u8 (PtrToPtr); StorageDead(_4); StorageDead(_3); - StorageDead(_1); diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-abort.diff index 0e7e1f971ec3..2f34a62b3d13 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-abort.diff @@ -24,7 +24,7 @@ bb0: { StorageLive(_1); -- _2 = CheckedAdd(const 2_i32, const 2_i32); +- _2 = AddWithOverflow(const 2_i32, const 2_i32); - assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> [success: bb1, unwind unreachable]; + _2 = const (4_i32, false); + assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> [success: bb1, unwind unreachable]; diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-unwind.diff index 9071a3339c08..da7add371a5b 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-unwind.diff @@ -24,7 +24,7 @@ bb0: { StorageLive(_1); -- _2 = CheckedAdd(const 2_i32, const 2_i32); +- _2 = AddWithOverflow(const 2_i32, const 2_i32); - assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> [success: bb1, unwind continue]; + _2 = const (4_i32, false); + assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> [success: bb1, unwind continue]; diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-abort.diff index 0e7e1f971ec3..2f34a62b3d13 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-abort.diff @@ -24,7 +24,7 @@ bb0: { StorageLive(_1); -- _2 = CheckedAdd(const 2_i32, const 2_i32); +- _2 = AddWithOverflow(const 2_i32, const 2_i32); - assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> [success: bb1, unwind unreachable]; + _2 = const (4_i32, false); + assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> [success: bb1, unwind unreachable]; diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-unwind.diff index 9071a3339c08..da7add371a5b 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-unwind.diff @@ -24,7 +24,7 @@ bb0: { StorageLive(_1); -- _2 = CheckedAdd(const 2_i32, const 2_i32); +- _2 = AddWithOverflow(const 2_i32, const 2_i32); - assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> [success: bb1, unwind continue]; + _2 = const (4_i32, false); + assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> [success: bb1, unwind continue]; diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff index 5f2a096bb1f7..802bfbbcdc5f 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff @@ -26,7 +26,7 @@ bb0: { StorageLive(_1); - _2 = CheckedAdd(const 2_i32, const 2_i32); + _2 = AddWithOverflow(const 2_i32, const 2_i32); assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> [success: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff index a49546f158c8..de94a5574031 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff @@ -26,7 +26,7 @@ bb0: { StorageLive(_1); - _2 = CheckedAdd(const 2_i32, const 2_i32); + _2 = AddWithOverflow(const 2_i32, const 2_i32); assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> [success: bb1, unwind continue]; } diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff index 5f2a096bb1f7..802bfbbcdc5f 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff @@ -26,7 +26,7 @@ bb0: { StorageLive(_1); - _2 = CheckedAdd(const 2_i32, const 2_i32); + _2 = AddWithOverflow(const 2_i32, const 2_i32); assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> [success: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff index a49546f158c8..de94a5574031 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff @@ -26,7 +26,7 @@ bb0: { StorageLive(_1); - _2 = CheckedAdd(const 2_i32, const 2_i32); + _2 = AddWithOverflow(const 2_i32, const 2_i32); assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> [success: bb1, unwind continue]; } diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.rs b/tests/mir-opt/pre-codegen/optimizes_into_variable.rs index 7a1fb1e76c03..de5e2d5c3121 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.rs +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.rs @@ -15,5 +15,5 @@ struct Point { fn main() { let x = 2 + 2; let y = [0, 1, 2, 3, 4, 5][3]; - let z = (Point { x: 12, y: 42}).y; + let z = (Point { x: 12, y: 42 }).y; } diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir index db0c84bd560f..2f6139712ffc 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir @@ -12,12 +12,10 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { scope 3 (inlined std::ptr::const_ptr::::add) { } scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::<[u32]>) { - let mut _5: *const (); - let mut _7: usize; + let mut _5: usize; scope 5 (inlined std::ptr::metadata::<[u32]>) { - let mut _6: std::ptr::metadata::PtrRepr<[u32]>; } - scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) { + scope 6 (inlined std::ptr::from_raw_parts::<[u32], ()>) { } } } @@ -29,14 +27,8 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { _4 = Offset(_3, _2); StorageDead(_3); StorageLive(_5); - _5 = _4 as *const () (PtrToPtr); - StorageLive(_7); - StorageLive(_6); - _6 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _1 }; - _7 = ((_6.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize); - StorageDead(_6); - _0 = *const [u32] from (_5, _7); - StorageDead(_7); + _5 = PtrMetadata(_1); + _0 = *const [u32] from (_4, _5); StorageDead(_5); StorageDead(_4); return; diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir index db0c84bd560f..2f6139712ffc 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir @@ -12,12 +12,10 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { scope 3 (inlined std::ptr::const_ptr::::add) { } scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::<[u32]>) { - let mut _5: *const (); - let mut _7: usize; + let mut _5: usize; scope 5 (inlined std::ptr::metadata::<[u32]>) { - let mut _6: std::ptr::metadata::PtrRepr<[u32]>; } - scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) { + scope 6 (inlined std::ptr::from_raw_parts::<[u32], ()>) { } } } @@ -29,14 +27,8 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { _4 = Offset(_3, _2); StorageDead(_3); StorageLive(_5); - _5 = _4 as *const () (PtrToPtr); - StorageLive(_7); - StorageLive(_6); - _6 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _1 }; - _7 = ((_6.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize); - StorageDead(_6); - _0 = *const [u32] from (_5, _7); - StorageDead(_7); + _5 = PtrMetadata(_1); + _0 = *const [u32] from (_4, _5); StorageDead(_5); StorageDead(_4); return; diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir index 766bd29ef41c..8d47e63eff2a 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir @@ -14,17 +14,19 @@ fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 { scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::) { scope 5 (inlined std::ptr::metadata::) { } - scope 6 (inlined std::ptr::from_raw_parts::) { + scope 6 (inlined std::ptr::from_raw_parts::) { } } } bb0: { + StorageLive(_4); StorageLive(_3); _3 = _1 as *const u8 (PtrToPtr); _4 = Offset(_3, _2); StorageDead(_3); _0 = _4 as *const u32 (PtrToPtr); + StorageDead(_4); return; } } diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir index 766bd29ef41c..8d47e63eff2a 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir @@ -14,17 +14,19 @@ fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 { scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::) { scope 5 (inlined std::ptr::metadata::) { } - scope 6 (inlined std::ptr::from_raw_parts::) { + scope 6 (inlined std::ptr::from_raw_parts::) { } } } bb0: { + StorageLive(_4); StorageLive(_3); _3 = _1 as *const u8 (PtrToPtr); _4 = Offset(_3, _2); StorageDead(_3); _0 = _4 as *const u32 (PtrToPtr); + StorageDead(_4); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_index.rs b/tests/mir-opt/pre-codegen/slice_index.rs index 04bbbff57b34..886e57a3380c 100644 --- a/tests/mir-opt/pre-codegen/slice_index.rs +++ b/tests/mir-opt/pre-codegen/slice_index.rs @@ -1,5 +1,4 @@ -// skip-filecheck -//@ compile-flags: -O -C debuginfo=0 -Zmir-opt-level=2 +//@ compile-flags: -O -C debuginfo=0 -Zmir-opt-level=2 -Z ub-checks=yes // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![crate_type = "lib"] @@ -9,21 +8,39 @@ use std::ops::Range; // EMIT_MIR slice_index.slice_index_usize.PreCodegen.after.mir pub fn slice_index_usize(slice: &[u32], index: usize) -> u32 { + // CHECK-LABEL: slice_index_usize + // CHECK: [[LEN:_[0-9]+]] = Len((*_1)) + // CHECK: Lt(_2, [[LEN]]) + // CHECK-NOT: precondition_check + // CHECK: _0 = (*_1)[_2]; slice[index] } // EMIT_MIR slice_index.slice_get_mut_usize.PreCodegen.after.mir pub fn slice_get_mut_usize(slice: &mut [u32], index: usize) -> Option<&mut u32> { + // CHECK-LABEL: slice_get_mut_usize + // CHECK: [[LEN:_[0-9]+]] = Len((*_1)) + // CHECK: Lt(_2, move [[LEN]]) + // CHECK-NOT: precondition_check slice.get_mut(index) } // EMIT_MIR slice_index.slice_index_range.PreCodegen.after.mir pub fn slice_index_range(slice: &[u32], index: Range) -> &[u32] { + // CHECK-LABEL: slice_index_range &slice[index] } // EMIT_MIR slice_index.slice_get_unchecked_mut_range.PreCodegen.after.mir pub unsafe fn slice_get_unchecked_mut_range(slice: &mut [u32], index: Range) -> &mut [u32] { + // CHECK-LABEL: slice_get_unchecked_mut_range + // CHECK: [[START:_[0-9]+]] = move (_2.0: usize); + // CHECK: [[END:_[0-9]+]] = move (_2.1: usize); + // CHECK: precondition_check + // CHECK: [[LEN:_[0-9]+]] = SubUnchecked([[END]], [[START]]); + // CHECK: [[PTR:_[0-9]+]] = Offset({{_[0-9]+}}, [[START]]); + // CHECK: [[SLICE:_[0-9]+]] = *mut [u32] from ([[PTR]], [[LEN]]) + // CHECK: _0 = &mut (*[[SLICE]]); slice.get_unchecked_mut(index) } @@ -32,5 +49,12 @@ pub unsafe fn slice_ptr_get_unchecked_range( slice: *const [u32], index: Range, ) -> *const [u32] { + // CHECK-LABEL: slice_ptr_get_unchecked_range + // CHECK: [[START:_[0-9]+]] = move (_2.0: usize); + // CHECK: [[END:_[0-9]+]] = move (_2.1: usize); + // CHECK: precondition_check + // CHECK: [[LEN:_[0-9]+]] = SubUnchecked([[END]], [[START]]); + // CHECK: [[PTR:_[0-9]+]] = Offset({{_[0-9]+}}, [[START]]); + // CHECK: _0 = *const [u32] from ([[PTR]], [[LEN]]) slice.get_unchecked(index) } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir index 7e20817cf230..f2ef2b0cc3c6 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir @@ -5,13 +5,54 @@ fn slice_get_mut_usize(_1: &mut [u32], _2: usize) -> Option<&mut u32> { debug index => _2; let mut _0: std::option::Option<&mut u32>; scope 1 (inlined core::slice::::get_mut::) { + scope 2 (inlined >::get_mut) { + let mut _3: usize; + let mut _4: bool; + let mut _5: *mut [u32]; + let mut _7: *mut u32; + let mut _8: &mut u32; + scope 3 (inlined core::slice::index::get_mut_noubcheck::) { + let _6: *mut u32; + scope 4 { + } + } + } } bb0: { - _0 = >::get_mut(move _2, move _1) -> [return: bb1, unwind unreachable]; + StorageLive(_7); + StorageLive(_4); + StorageLive(_3); + _3 = Len((*_1)); + _4 = Lt(_2, move _3); + switchInt(move _4) -> [0: bb1, otherwise: bb2]; } bb1: { + StorageDead(_3); + _0 = const Option::<&mut u32>::None; + goto -> bb3; + } + + bb2: { + StorageDead(_3); + StorageLive(_8); + StorageLive(_5); + _5 = &raw mut (*_1); + StorageLive(_6); + _6 = _5 as *mut u32 (PtrToPtr); + _7 = Offset(_6, _2); + StorageDead(_6); + StorageDead(_5); + _8 = &mut (*_7); + _0 = Option::<&mut u32>::Some(move _8); + StorageDead(_8); + goto -> bb3; + } + + bb3: { + StorageDead(_4); + StorageDead(_7); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir index 2f65b8c64017..f2ef2b0cc3c6 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir @@ -5,13 +5,54 @@ fn slice_get_mut_usize(_1: &mut [u32], _2: usize) -> Option<&mut u32> { debug index => _2; let mut _0: std::option::Option<&mut u32>; scope 1 (inlined core::slice::::get_mut::) { + scope 2 (inlined >::get_mut) { + let mut _3: usize; + let mut _4: bool; + let mut _5: *mut [u32]; + let mut _7: *mut u32; + let mut _8: &mut u32; + scope 3 (inlined core::slice::index::get_mut_noubcheck::) { + let _6: *mut u32; + scope 4 { + } + } + } } bb0: { - _0 = >::get_mut(move _2, move _1) -> [return: bb1, unwind continue]; + StorageLive(_7); + StorageLive(_4); + StorageLive(_3); + _3 = Len((*_1)); + _4 = Lt(_2, move _3); + switchInt(move _4) -> [0: bb1, otherwise: bb2]; } bb1: { + StorageDead(_3); + _0 = const Option::<&mut u32>::None; + goto -> bb3; + } + + bb2: { + StorageDead(_3); + StorageLive(_8); + StorageLive(_5); + _5 = &raw mut (*_1); + StorageLive(_6); + _6 = _5 as *mut u32 (PtrToPtr); + _7 = Offset(_6, _2); + StorageDead(_6); + StorageDead(_5); + _8 = &mut (*_7); + _0 = Option::<&mut u32>::Some(move _8); + StorageDead(_8); + goto -> bb3; + } + + bb3: { + StorageDead(_4); + StorageDead(_7); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir index 62fe1a868578..41cca811922e 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir @@ -8,20 +8,25 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> let mut _4: usize; scope 1 (inlined core::slice::::get_unchecked_mut::>) { let mut _5: *mut [u32]; - let mut _9: *mut [u32]; + let mut _12: *mut [u32]; scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked_mut) { - let _6: usize; - let mut _7: *mut u32; - let mut _8: *mut u32; + let mut _7: usize; + let _8: (); + let _9: usize; scope 3 { - scope 6 (inlined std::ptr::mut_ptr::::as_mut_ptr) { - } - scope 7 (inlined std::ptr::mut_ptr::::add) { - } - scope 8 (inlined slice_from_raw_parts_mut::) { + scope 6 (inlined core::slice::index::get_offset_len_mut_noubcheck::) { + let _11: *mut u32; + scope 7 { + } + scope 8 (inlined core::slice::index::get_mut_noubcheck::) { + let _10: *mut u32; + scope 9 { + } + } } } scope 4 (inlined std::ptr::mut_ptr::::len) { + let mut _6: *const [u32]; scope 5 (inlined std::ptr::metadata::<[u32]>) { } } @@ -33,18 +38,28 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> _4 = move (_2.1: usize); StorageLive(_5); _5 = &raw mut (*_1); - StorageLive(_6); - _6 = SubUnchecked(_4, _3); - StorageLive(_8); + StorageLive(_9); StorageLive(_7); - _7 = _5 as *mut u32 (PtrToPtr); - _8 = Offset(_7, _3); - StorageDead(_7); - _9 = *mut [u32] from (_8, _6); - StorageDead(_8); + StorageLive(_6); + _6 = _5 as *const [u32] (PtrToPtr); + _7 = PtrMetadata(_6); StorageDead(_6); + _8 = as SliceIndex<[T]>>::get_unchecked_mut::precondition_check(_3, _4, move _7) -> [return: bb1, unwind unreachable]; + } + + bb1: { + StorageDead(_7); + _9 = SubUnchecked(_4, _3); + StorageLive(_11); + StorageLive(_10); + _10 = _5 as *mut u32 (PtrToPtr); + _11 = Offset(_10, _3); + StorageDead(_10); + _12 = *mut [u32] from (_11, _9); + StorageDead(_11); + StorageDead(_9); StorageDead(_5); - _0 = &mut (*_9); + _0 = &mut (*_12); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir index 62fe1a868578..41cca811922e 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir @@ -8,20 +8,25 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> let mut _4: usize; scope 1 (inlined core::slice::::get_unchecked_mut::>) { let mut _5: *mut [u32]; - let mut _9: *mut [u32]; + let mut _12: *mut [u32]; scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked_mut) { - let _6: usize; - let mut _7: *mut u32; - let mut _8: *mut u32; + let mut _7: usize; + let _8: (); + let _9: usize; scope 3 { - scope 6 (inlined std::ptr::mut_ptr::::as_mut_ptr) { - } - scope 7 (inlined std::ptr::mut_ptr::::add) { - } - scope 8 (inlined slice_from_raw_parts_mut::) { + scope 6 (inlined core::slice::index::get_offset_len_mut_noubcheck::) { + let _11: *mut u32; + scope 7 { + } + scope 8 (inlined core::slice::index::get_mut_noubcheck::) { + let _10: *mut u32; + scope 9 { + } + } } } scope 4 (inlined std::ptr::mut_ptr::::len) { + let mut _6: *const [u32]; scope 5 (inlined std::ptr::metadata::<[u32]>) { } } @@ -33,18 +38,28 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> _4 = move (_2.1: usize); StorageLive(_5); _5 = &raw mut (*_1); - StorageLive(_6); - _6 = SubUnchecked(_4, _3); - StorageLive(_8); + StorageLive(_9); StorageLive(_7); - _7 = _5 as *mut u32 (PtrToPtr); - _8 = Offset(_7, _3); - StorageDead(_7); - _9 = *mut [u32] from (_8, _6); - StorageDead(_8); + StorageLive(_6); + _6 = _5 as *const [u32] (PtrToPtr); + _7 = PtrMetadata(_6); StorageDead(_6); + _8 = as SliceIndex<[T]>>::get_unchecked_mut::precondition_check(_3, _4, move _7) -> [return: bb1, unwind unreachable]; + } + + bb1: { + StorageDead(_7); + _9 = SubUnchecked(_4, _3); + StorageLive(_11); + StorageLive(_10); + _10 = _5 as *mut u32 (PtrToPtr); + _11 = Offset(_10, _3); + StorageDead(_10); + _12 = *mut [u32] from (_11, _9); + StorageDead(_11); + StorageDead(_9); StorageDead(_5); - _0 = &mut (*_9); + _0 = &mut (*_12); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir index 000432ccca7d..c61bebe6cc3c 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir @@ -8,15 +8,19 @@ fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range) - let mut _4: usize; scope 1 (inlined std::ptr::const_ptr::::get_unchecked::>) { scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked) { - let _5: usize; - let mut _6: *const u32; - let mut _7: *const u32; + let mut _5: usize; + let _6: (); + let _7: usize; scope 3 { - scope 6 (inlined std::ptr::const_ptr::::as_ptr) { - } - scope 7 (inlined std::ptr::const_ptr::::add) { - } - scope 8 (inlined slice_from_raw_parts::) { + scope 6 (inlined core::slice::index::get_offset_len_noubcheck::) { + let _9: *const u32; + scope 7 { + } + scope 8 (inlined core::slice::index::get_noubcheck::) { + let _8: *const u32; + scope 9 { + } + } } } scope 4 (inlined std::ptr::const_ptr::::len) { @@ -29,16 +33,23 @@ fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range) - bb0: { _3 = move (_2.0: usize); _4 = move (_2.1: usize); - StorageLive(_5); - _5 = SubUnchecked(_4, _3); StorageLive(_7); - StorageLive(_6); - _6 = _1 as *const u32 (PtrToPtr); - _7 = Offset(_6, _3); - StorageDead(_6); - _0 = *const [u32] from (_7, _5); - StorageDead(_7); + StorageLive(_5); + _5 = PtrMetadata(_1); + _6 = as SliceIndex<[T]>>::get_unchecked::precondition_check(_3, _4, move _5) -> [return: bb1, unwind unreachable]; + } + + bb1: { StorageDead(_5); + _7 = SubUnchecked(_4, _3); + StorageLive(_9); + StorageLive(_8); + _8 = _1 as *const u32 (PtrToPtr); + _9 = Offset(_8, _3); + StorageDead(_8); + _0 = *const [u32] from (_9, _7); + StorageDead(_9); + StorageDead(_7); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir index 000432ccca7d..c61bebe6cc3c 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir @@ -8,15 +8,19 @@ fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range) - let mut _4: usize; scope 1 (inlined std::ptr::const_ptr::::get_unchecked::>) { scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked) { - let _5: usize; - let mut _6: *const u32; - let mut _7: *const u32; + let mut _5: usize; + let _6: (); + let _7: usize; scope 3 { - scope 6 (inlined std::ptr::const_ptr::::as_ptr) { - } - scope 7 (inlined std::ptr::const_ptr::::add) { - } - scope 8 (inlined slice_from_raw_parts::) { + scope 6 (inlined core::slice::index::get_offset_len_noubcheck::) { + let _9: *const u32; + scope 7 { + } + scope 8 (inlined core::slice::index::get_noubcheck::) { + let _8: *const u32; + scope 9 { + } + } } } scope 4 (inlined std::ptr::const_ptr::::len) { @@ -29,16 +33,23 @@ fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range) - bb0: { _3 = move (_2.0: usize); _4 = move (_2.1: usize); - StorageLive(_5); - _5 = SubUnchecked(_4, _3); StorageLive(_7); - StorageLive(_6); - _6 = _1 as *const u32 (PtrToPtr); - _7 = Offset(_6, _3); - StorageDead(_6); - _0 = *const [u32] from (_7, _5); - StorageDead(_7); + StorageLive(_5); + _5 = PtrMetadata(_1); + _6 = as SliceIndex<[T]>>::get_unchecked::precondition_check(_3, _4, move _5) -> [return: bb1, unwind unreachable]; + } + + bb1: { StorageDead(_5); + _7 = SubUnchecked(_4, _3); + StorageLive(_9); + StorageLive(_8); + _8 = _1 as *const u32 (PtrToPtr); + _9 = Offset(_8, _3); + StorageDead(_8); + _0 = *const [u32] from (_9, _7); + StorageDead(_9); + StorageDead(_7); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir index d979c5ec1d5a..8eb102e68f36 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir @@ -4,35 +4,34 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _12: std::slice::Iter<'_, T>; + let mut _11: std::slice::Iter<'_, T>; + let mut _12: std::iter::Enumerate>; let mut _13: std::iter::Enumerate>; - let mut _14: std::iter::Enumerate>; - let mut _15: &mut std::iter::Enumerate>; - let mut _16: std::option::Option<(usize, &T)>; - let mut _17: isize; - let mut _20: &impl Fn(usize, &T); - let mut _21: (usize, &T); - let _22: (); + let mut _14: &mut std::iter::Enumerate>; + let mut _15: std::option::Option<(usize, &T)>; + let mut _16: isize; + let mut _19: &impl Fn(usize, &T); + let mut _20: (usize, &T); + let _21: (); scope 1 { - debug iter => _14; - let _18: usize; - let _19: &T; + debug iter => _13; + let _17: usize; + let _18: &T; scope 2 { - debug i => _18; - debug x => _19; + debug i => _17; + debug x => _18; } } scope 3 (inlined core::slice::::iter) { scope 4 (inlined std::slice::Iter::<'_, T>::new) { let _3: usize; - let mut _7: bool; + let mut _7: *mut T; let mut _8: *mut T; - let mut _9: *mut T; - let mut _11: *const T; + let mut _10: *const T; scope 5 { let _6: std::ptr::NonNull; scope 6 { - let _10: *const T; + let _9: *const T; scope 7 { } scope 11 (inlined without_provenance::) { @@ -61,7 +60,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb0: { - StorageLive(_12); + StorageLive(_11); StorageLive(_3); StorageLive(_6); StorageLive(_4); @@ -70,62 +69,59 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { _4 = &raw const (*_1); _5 = _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: _5 }; - StorageLive(_10); - StorageLive(_7); - _7 = const ::IS_ZST; - switchInt(move _7) -> [0: bb1, otherwise: bb2]; + StorageLive(_9); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } bb1: { - StorageLive(_9); StorageLive(_8); - _8 = _4 as *mut T (PtrToPtr); - _9 = Offset(_8, _3); + StorageLive(_7); + _7 = _4 as *mut T (PtrToPtr); + _8 = Offset(_7, _3); + StorageDead(_7); + _9 = move _8 as *const T (PtrToPtr); StorageDead(_8); - _10 = move _9 as *const T (PointerCoercion(MutToConstPointer)); - StorageDead(_9); goto -> bb3; } bb2: { - _10 = _3 as *const T (Transmute); + _9 = _3 as *const T (Transmute); goto -> bb3; } bb3: { - StorageDead(_7); - StorageLive(_11); - _11 = _10; - _12 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _11, _marker: const ZeroSized: PhantomData<&T> }; - StorageDead(_11); + StorageLive(_10); + _10 = _9; + _11 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _10, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_10); + StorageDead(_9); StorageDead(_5); StorageDead(_4); StorageDead(_6); StorageDead(_3); - _13 = Enumerate::> { iter: _12, count: const 0_usize }; - StorageDead(_12); - StorageLive(_14); - _14 = _13; + _12 = Enumerate::> { iter: _11, count: const 0_usize }; + StorageDead(_11); + StorageLive(_13); + _13 = _12; goto -> bb4; } bb4: { - StorageLive(_16); StorageLive(_15); - _15 = &mut _14; - _16 = > as Iterator>::next(move _15) -> [return: bb5, unwind unreachable]; + StorageLive(_14); + _14 = &mut _13; + _15 = > as Iterator>::next(move _14) -> [return: bb5, unwind unreachable]; } bb5: { - StorageDead(_15); - _17 = discriminant(_16); - switchInt(move _17) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageDead(_14); + _16 = discriminant(_15); + switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10]; } bb6: { - StorageDead(_16); - StorageDead(_14); + StorageDead(_15); + StorageDead(_13); drop(_2) -> [return: bb7, unwind unreachable]; } @@ -134,19 +130,19 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb8: { - _18 = (((_16 as Some).0: (usize, &T)).0: usize); - _19 = (((_16 as Some).0: (usize, &T)).1: &T); + _17 = (((_15 as Some).0: (usize, &T)).0: usize); + _18 = (((_15 as Some).0: (usize, &T)).1: &T); + StorageLive(_19); + _19 = &_2; StorageLive(_20); - _20 = &_2; - StorageLive(_21); - _21 = (_18, _19); - _22 = >::call(move _20, move _21) -> [return: bb9, unwind unreachable]; + _20 = (_17, _18); + _21 = >::call(move _19, move _20) -> [return: bb9, unwind unreachable]; } bb9: { - StorageDead(_21); StorageDead(_20); - StorageDead(_16); + StorageDead(_19); + StorageDead(_15); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir index 8491c49f7673..f805967d64ff 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir @@ -4,35 +4,34 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _12: std::slice::Iter<'_, T>; + let mut _11: std::slice::Iter<'_, T>; + let mut _12: std::iter::Enumerate>; let mut _13: std::iter::Enumerate>; - let mut _14: std::iter::Enumerate>; - let mut _15: &mut std::iter::Enumerate>; - let mut _16: std::option::Option<(usize, &T)>; - let mut _17: isize; - let mut _20: &impl Fn(usize, &T); - let mut _21: (usize, &T); - let _22: (); + let mut _14: &mut std::iter::Enumerate>; + let mut _15: std::option::Option<(usize, &T)>; + let mut _16: isize; + let mut _19: &impl Fn(usize, &T); + let mut _20: (usize, &T); + let _21: (); scope 1 { - debug iter => _14; - let _18: usize; - let _19: &T; + debug iter => _13; + let _17: usize; + let _18: &T; scope 2 { - debug i => _18; - debug x => _19; + debug i => _17; + debug x => _18; } } scope 3 (inlined core::slice::::iter) { scope 4 (inlined std::slice::Iter::<'_, T>::new) { let _3: usize; - let mut _7: bool; + let mut _7: *mut T; let mut _8: *mut T; - let mut _9: *mut T; - let mut _11: *const T; + let mut _10: *const T; scope 5 { let _6: std::ptr::NonNull; scope 6 { - let _10: *const T; + let _9: *const T; scope 7 { } scope 11 (inlined without_provenance::) { @@ -61,7 +60,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb0: { - StorageLive(_12); + StorageLive(_11); StorageLive(_3); StorageLive(_6); StorageLive(_4); @@ -70,62 +69,59 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { _4 = &raw const (*_1); _5 = _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: _5 }; - StorageLive(_10); - StorageLive(_7); - _7 = const ::IS_ZST; - switchInt(move _7) -> [0: bb1, otherwise: bb2]; + StorageLive(_9); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } bb1: { - StorageLive(_9); StorageLive(_8); - _8 = _4 as *mut T (PtrToPtr); - _9 = Offset(_8, _3); + StorageLive(_7); + _7 = _4 as *mut T (PtrToPtr); + _8 = Offset(_7, _3); + StorageDead(_7); + _9 = move _8 as *const T (PtrToPtr); StorageDead(_8); - _10 = move _9 as *const T (PointerCoercion(MutToConstPointer)); - StorageDead(_9); goto -> bb3; } bb2: { - _10 = _3 as *const T (Transmute); + _9 = _3 as *const T (Transmute); goto -> bb3; } bb3: { - StorageDead(_7); - StorageLive(_11); - _11 = _10; - _12 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _11, _marker: const ZeroSized: PhantomData<&T> }; - StorageDead(_11); + StorageLive(_10); + _10 = _9; + _11 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _10, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_10); + StorageDead(_9); StorageDead(_5); StorageDead(_4); StorageDead(_6); StorageDead(_3); - _13 = Enumerate::> { iter: _12, count: const 0_usize }; - StorageDead(_12); - StorageLive(_14); - _14 = _13; + _12 = Enumerate::> { iter: _11, count: const 0_usize }; + StorageDead(_11); + StorageLive(_13); + _13 = _12; goto -> bb4; } bb4: { - StorageLive(_16); StorageLive(_15); - _15 = &mut _14; - _16 = > as Iterator>::next(move _15) -> [return: bb5, unwind: bb11]; + StorageLive(_14); + _14 = &mut _13; + _15 = > as Iterator>::next(move _14) -> [return: bb5, unwind: bb11]; } bb5: { - StorageDead(_15); - _17 = discriminant(_16); - switchInt(move _17) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageDead(_14); + _16 = discriminant(_15); + switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10]; } bb6: { - StorageDead(_16); - StorageDead(_14); + StorageDead(_15); + StorageDead(_13); drop(_2) -> [return: bb7, unwind continue]; } @@ -134,19 +130,19 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb8: { - _18 = (((_16 as Some).0: (usize, &T)).0: usize); - _19 = (((_16 as Some).0: (usize, &T)).1: &T); + _17 = (((_15 as Some).0: (usize, &T)).0: usize); + _18 = (((_15 as Some).0: (usize, &T)).1: &T); + StorageLive(_19); + _19 = &_2; StorageLive(_20); - _20 = &_2; - StorageLive(_21); - _21 = (_18, _19); - _22 = >::call(move _20, move _21) -> [return: bb9, unwind: bb11]; + _20 = (_17, _18); + _21 = >::call(move _19, move _20) -> [return: bb9, unwind: bb11]; } bb9: { - StorageDead(_21); StorageDead(_20); - StorageDead(_16); + StorageDead(_19); + StorageDead(_15); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir index 67dd0c85ea1d..6ae64200f4e7 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir @@ -4,32 +4,31 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); + let mut _11: std::slice::Iter<'_, T>; let mut _12: std::slice::Iter<'_, T>; - let mut _13: std::slice::Iter<'_, T>; - let mut _14: &mut std::slice::Iter<'_, T>; - let mut _15: std::option::Option<&T>; - let mut _16: isize; - let mut _18: &impl Fn(&T); - let mut _19: (&T,); - let _20: (); + let mut _13: &mut std::slice::Iter<'_, T>; + let mut _14: std::option::Option<&T>; + let mut _15: isize; + let mut _17: &impl Fn(&T); + let mut _18: (&T,); + let _19: (); scope 1 { - debug iter => _13; - let _17: &T; + debug iter => _12; + let _16: &T; scope 2 { - debug x => _17; + debug x => _16; } } scope 3 (inlined core::slice::::iter) { scope 4 (inlined std::slice::Iter::<'_, T>::new) { let _3: usize; - let mut _7: bool; + let mut _7: *mut T; let mut _8: *mut T; - let mut _9: *mut T; - let mut _11: *const T; + let mut _10: *const T; scope 5 { let _6: std::ptr::NonNull; scope 6 { - let _10: *const T; + let _9: *const T; scope 7 { } scope 11 (inlined without_provenance::) { @@ -62,60 +61,57 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { _4 = &raw const (*_1); _5 = _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: _5 }; - StorageLive(_10); - StorageLive(_7); - _7 = const ::IS_ZST; - switchInt(move _7) -> [0: bb1, otherwise: bb2]; + StorageLive(_9); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } bb1: { - StorageLive(_9); StorageLive(_8); - _8 = _4 as *mut T (PtrToPtr); - _9 = Offset(_8, _3); + StorageLive(_7); + _7 = _4 as *mut T (PtrToPtr); + _8 = Offset(_7, _3); + StorageDead(_7); + _9 = move _8 as *const T (PtrToPtr); StorageDead(_8); - _10 = move _9 as *const T (PointerCoercion(MutToConstPointer)); - StorageDead(_9); goto -> bb3; } bb2: { - _10 = _3 as *const T (Transmute); + _9 = _3 as *const T (Transmute); goto -> bb3; } bb3: { - StorageDead(_7); - StorageLive(_11); - _11 = _10; - _12 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _11, _marker: const ZeroSized: PhantomData<&T> }; - StorageDead(_11); + StorageLive(_10); + _10 = _9; + _11 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _10, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_10); + StorageDead(_9); StorageDead(_5); StorageDead(_4); StorageDead(_6); StorageDead(_3); - StorageLive(_13); - _13 = _12; + StorageLive(_12); + _12 = _11; goto -> bb4; } bb4: { - StorageLive(_15); StorageLive(_14); - _14 = &mut _13; - _15 = as Iterator>::next(move _14) -> [return: bb5, unwind unreachable]; + StorageLive(_13); + _13 = &mut _12; + _14 = as Iterator>::next(move _13) -> [return: bb5, unwind unreachable]; } bb5: { - StorageDead(_14); - _16 = discriminant(_15); - switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageDead(_13); + _15 = discriminant(_14); + switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10]; } bb6: { - StorageDead(_15); - StorageDead(_13); + StorageDead(_14); + StorageDead(_12); drop(_2) -> [return: bb7, unwind unreachable]; } @@ -124,18 +120,18 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb8: { - _17 = ((_15 as Some).0: &T); + _16 = ((_14 as Some).0: &T); + StorageLive(_17); + _17 = &_2; StorageLive(_18); - _18 = &_2; - StorageLive(_19); - _19 = (_17,); - _20 = >::call(move _18, move _19) -> [return: bb9, unwind unreachable]; + _18 = (_16,); + _19 = >::call(move _17, move _18) -> [return: bb9, unwind unreachable]; } bb9: { - StorageDead(_19); StorageDead(_18); - StorageDead(_15); + StorageDead(_17); + StorageDead(_14); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir index 7c41e9e1f1b7..ac72329fcd64 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir @@ -4,32 +4,31 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); + let mut _11: std::slice::Iter<'_, T>; let mut _12: std::slice::Iter<'_, T>; - let mut _13: std::slice::Iter<'_, T>; - let mut _14: &mut std::slice::Iter<'_, T>; - let mut _15: std::option::Option<&T>; - let mut _16: isize; - let mut _18: &impl Fn(&T); - let mut _19: (&T,); - let _20: (); + let mut _13: &mut std::slice::Iter<'_, T>; + let mut _14: std::option::Option<&T>; + let mut _15: isize; + let mut _17: &impl Fn(&T); + let mut _18: (&T,); + let _19: (); scope 1 { - debug iter => _13; - let _17: &T; + debug iter => _12; + let _16: &T; scope 2 { - debug x => _17; + debug x => _16; } } scope 3 (inlined core::slice::::iter) { scope 4 (inlined std::slice::Iter::<'_, T>::new) { let _3: usize; - let mut _7: bool; + let mut _7: *mut T; let mut _8: *mut T; - let mut _9: *mut T; - let mut _11: *const T; + let mut _10: *const T; scope 5 { let _6: std::ptr::NonNull; scope 6 { - let _10: *const T; + let _9: *const T; scope 7 { } scope 11 (inlined without_provenance::) { @@ -62,60 +61,57 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { _4 = &raw const (*_1); _5 = _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: _5 }; - StorageLive(_10); - StorageLive(_7); - _7 = const ::IS_ZST; - switchInt(move _7) -> [0: bb1, otherwise: bb2]; + StorageLive(_9); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } bb1: { - StorageLive(_9); StorageLive(_8); - _8 = _4 as *mut T (PtrToPtr); - _9 = Offset(_8, _3); + StorageLive(_7); + _7 = _4 as *mut T (PtrToPtr); + _8 = Offset(_7, _3); + StorageDead(_7); + _9 = move _8 as *const T (PtrToPtr); StorageDead(_8); - _10 = move _9 as *const T (PointerCoercion(MutToConstPointer)); - StorageDead(_9); goto -> bb3; } bb2: { - _10 = _3 as *const T (Transmute); + _9 = _3 as *const T (Transmute); goto -> bb3; } bb3: { - StorageDead(_7); - StorageLive(_11); - _11 = _10; - _12 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _11, _marker: const ZeroSized: PhantomData<&T> }; - StorageDead(_11); + StorageLive(_10); + _10 = _9; + _11 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _10, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_10); + StorageDead(_9); StorageDead(_5); StorageDead(_4); StorageDead(_6); StorageDead(_3); - StorageLive(_13); - _13 = _12; + StorageLive(_12); + _12 = _11; goto -> bb4; } bb4: { - StorageLive(_15); StorageLive(_14); - _14 = &mut _13; - _15 = as Iterator>::next(move _14) -> [return: bb5, unwind: bb11]; + StorageLive(_13); + _13 = &mut _12; + _14 = as Iterator>::next(move _13) -> [return: bb5, unwind: bb11]; } bb5: { - StorageDead(_14); - _16 = discriminant(_15); - switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageDead(_13); + _15 = discriminant(_14); + switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10]; } bb6: { - StorageDead(_15); - StorageDead(_13); + StorageDead(_14); + StorageDead(_12); drop(_2) -> [return: bb7, unwind continue]; } @@ -124,18 +120,18 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb8: { - _17 = ((_15 as Some).0: &T); + _16 = ((_14 as Some).0: &T); + StorageLive(_17); + _17 = &_2; StorageLive(_18); - _18 = &_2; - StorageLive(_19); - _19 = (_17,); - _20 = >::call(move _18, move _19) -> [return: bb9, unwind: bb11]; + _18 = (_16,); + _19 = >::call(move _17, move _18) -> [return: bb9, unwind: bb11]; } bb9: { - StorageDead(_19); StorageDead(_18); - StorageDead(_15); + StorageDead(_17); + StorageDead(_14); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir index ffeef1e04a15..8008336e268c 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir @@ -4,35 +4,34 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _12: std::slice::Iter<'_, T>; + let mut _11: std::slice::Iter<'_, T>; + let mut _12: std::iter::Rev>; let mut _13: std::iter::Rev>; - let mut _14: std::iter::Rev>; - let mut _16: std::option::Option<&T>; - let mut _17: isize; - let mut _19: &impl Fn(&T); - let mut _20: (&T,); - let _21: (); + let mut _15: std::option::Option<&T>; + let mut _16: isize; + let mut _18: &impl Fn(&T); + let mut _19: (&T,); + let _20: (); scope 1 { - debug iter => _14; - let _18: &T; + debug iter => _13; + let _17: &T; scope 2 { - debug x => _18; + debug x => _17; } scope 17 (inlined > as Iterator>::next) { - let mut _15: &mut std::slice::Iter<'_, T>; + let mut _14: &mut std::slice::Iter<'_, T>; } } scope 3 (inlined core::slice::::iter) { scope 4 (inlined std::slice::Iter::<'_, T>::new) { let _3: usize; - let mut _7: bool; + let mut _7: *mut T; let mut _8: *mut T; - let mut _9: *mut T; - let mut _11: *const T; + let mut _10: *const T; scope 5 { let _6: std::ptr::NonNull; scope 6 { - let _10: *const T; + let _9: *const T; scope 7 { } scope 11 (inlined without_provenance::) { @@ -61,7 +60,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb0: { - StorageLive(_12); + StorageLive(_11); StorageLive(_3); StorageLive(_6); StorageLive(_4); @@ -70,62 +69,59 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _4 = &raw const (*_1); _5 = _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: _5 }; - StorageLive(_10); - StorageLive(_7); - _7 = const ::IS_ZST; - switchInt(move _7) -> [0: bb1, otherwise: bb2]; + StorageLive(_9); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } bb1: { - StorageLive(_9); StorageLive(_8); - _8 = _4 as *mut T (PtrToPtr); - _9 = Offset(_8, _3); + StorageLive(_7); + _7 = _4 as *mut T (PtrToPtr); + _8 = Offset(_7, _3); + StorageDead(_7); + _9 = move _8 as *const T (PtrToPtr); StorageDead(_8); - _10 = move _9 as *const T (PointerCoercion(MutToConstPointer)); - StorageDead(_9); goto -> bb3; } bb2: { - _10 = _3 as *const T (Transmute); + _9 = _3 as *const T (Transmute); goto -> bb3; } bb3: { - StorageDead(_7); - StorageLive(_11); - _11 = _10; - _12 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _11, _marker: const ZeroSized: PhantomData<&T> }; - StorageDead(_11); + StorageLive(_10); + _10 = _9; + _11 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _10, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_10); + StorageDead(_9); StorageDead(_5); StorageDead(_4); StorageDead(_6); StorageDead(_3); - _13 = Rev::> { iter: _12 }; - StorageDead(_12); - StorageLive(_14); - _14 = _13; + _12 = Rev::> { iter: _11 }; + StorageDead(_11); + StorageLive(_13); + _13 = _12; goto -> bb4; } bb4: { - StorageLive(_16); StorageLive(_15); - _15 = &mut (_14.0: std::slice::Iter<'_, T>); - _16 = as DoubleEndedIterator>::next_back(move _15) -> [return: bb5, unwind unreachable]; + StorageLive(_14); + _14 = &mut (_13.0: std::slice::Iter<'_, T>); + _15 = as DoubleEndedIterator>::next_back(move _14) -> [return: bb5, unwind unreachable]; } bb5: { - StorageDead(_15); - _17 = discriminant(_16); - switchInt(move _17) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageDead(_14); + _16 = discriminant(_15); + switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10]; } bb6: { - StorageDead(_16); - StorageDead(_14); + StorageDead(_15); + StorageDead(_13); drop(_2) -> [return: bb7, unwind unreachable]; } @@ -134,18 +130,18 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb8: { - _18 = ((_16 as Some).0: &T); + _17 = ((_15 as Some).0: &T); + StorageLive(_18); + _18 = &_2; StorageLive(_19); - _19 = &_2; - StorageLive(_20); - _20 = (_18,); - _21 = >::call(move _19, move _20) -> [return: bb9, unwind unreachable]; + _19 = (_17,); + _20 = >::call(move _18, move _19) -> [return: bb9, unwind unreachable]; } bb9: { - StorageDead(_20); StorageDead(_19); - StorageDead(_16); + StorageDead(_18); + StorageDead(_15); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir index c7cd37afd866..47253bf7a0d3 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir @@ -4,35 +4,34 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _12: std::slice::Iter<'_, T>; + let mut _11: std::slice::Iter<'_, T>; + let mut _12: std::iter::Rev>; let mut _13: std::iter::Rev>; - let mut _14: std::iter::Rev>; - let mut _16: std::option::Option<&T>; - let mut _17: isize; - let mut _19: &impl Fn(&T); - let mut _20: (&T,); - let _21: (); + let mut _15: std::option::Option<&T>; + let mut _16: isize; + let mut _18: &impl Fn(&T); + let mut _19: (&T,); + let _20: (); scope 1 { - debug iter => _14; - let _18: &T; + debug iter => _13; + let _17: &T; scope 2 { - debug x => _18; + debug x => _17; } scope 17 (inlined > as Iterator>::next) { - let mut _15: &mut std::slice::Iter<'_, T>; + let mut _14: &mut std::slice::Iter<'_, T>; } } scope 3 (inlined core::slice::::iter) { scope 4 (inlined std::slice::Iter::<'_, T>::new) { let _3: usize; - let mut _7: bool; + let mut _7: *mut T; let mut _8: *mut T; - let mut _9: *mut T; - let mut _11: *const T; + let mut _10: *const T; scope 5 { let _6: std::ptr::NonNull; scope 6 { - let _10: *const T; + let _9: *const T; scope 7 { } scope 11 (inlined without_provenance::) { @@ -61,7 +60,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb0: { - StorageLive(_12); + StorageLive(_11); StorageLive(_3); StorageLive(_6); StorageLive(_4); @@ -70,62 +69,59 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _4 = &raw const (*_1); _5 = _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: _5 }; - StorageLive(_10); - StorageLive(_7); - _7 = const ::IS_ZST; - switchInt(move _7) -> [0: bb1, otherwise: bb2]; + StorageLive(_9); + switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } bb1: { - StorageLive(_9); StorageLive(_8); - _8 = _4 as *mut T (PtrToPtr); - _9 = Offset(_8, _3); + StorageLive(_7); + _7 = _4 as *mut T (PtrToPtr); + _8 = Offset(_7, _3); + StorageDead(_7); + _9 = move _8 as *const T (PtrToPtr); StorageDead(_8); - _10 = move _9 as *const T (PointerCoercion(MutToConstPointer)); - StorageDead(_9); goto -> bb3; } bb2: { - _10 = _3 as *const T (Transmute); + _9 = _3 as *const T (Transmute); goto -> bb3; } bb3: { - StorageDead(_7); - StorageLive(_11); - _11 = _10; - _12 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _11, _marker: const ZeroSized: PhantomData<&T> }; - StorageDead(_11); + StorageLive(_10); + _10 = _9; + _11 = std::slice::Iter::<'_, T> { ptr: _6, end_or_len: move _10, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_10); + StorageDead(_9); StorageDead(_5); StorageDead(_4); StorageDead(_6); StorageDead(_3); - _13 = Rev::> { iter: _12 }; - StorageDead(_12); - StorageLive(_14); - _14 = _13; + _12 = Rev::> { iter: _11 }; + StorageDead(_11); + StorageLive(_13); + _13 = _12; goto -> bb4; } bb4: { - StorageLive(_16); StorageLive(_15); - _15 = &mut (_14.0: std::slice::Iter<'_, T>); - _16 = as DoubleEndedIterator>::next_back(move _15) -> [return: bb5, unwind: bb11]; + StorageLive(_14); + _14 = &mut (_13.0: std::slice::Iter<'_, T>); + _15 = as DoubleEndedIterator>::next_back(move _14) -> [return: bb5, unwind: bb11]; } bb5: { - StorageDead(_15); - _17 = discriminant(_16); - switchInt(move _17) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageDead(_14); + _16 = discriminant(_15); + switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10]; } bb6: { - StorageDead(_16); - StorageDead(_14); + StorageDead(_15); + StorageDead(_13); drop(_2) -> [return: bb7, unwind continue]; } @@ -134,18 +130,18 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb8: { - _18 = ((_16 as Some).0: &T); + _17 = ((_15 as Some).0: &T); + StorageLive(_18); + _18 = &_2; StorageLive(_19); - _19 = &_2; - StorageLive(_20); - _20 = (_18,); - _21 = >::call(move _19, move _20) -> [return: bb9, unwind: bb11]; + _19 = (_17,); + _20 = >::call(move _18, move _19) -> [return: bb9, unwind: bb11]; } bb9: { - StorageDead(_20); StorageDead(_19); - StorageDead(_16); + StorageDead(_18); + StorageDead(_15); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/try_identity.rs b/tests/mir-opt/pre-codegen/try_identity.rs index 2e17a3ae6e7e..264b303e381d 100644 --- a/tests/mir-opt/pre-codegen/try_identity.rs +++ b/tests/mir-opt/pre-codegen/try_identity.rs @@ -17,18 +17,16 @@ fn new(x: Result) -> Result { } { ControlFlow::Continue(v) => v, ControlFlow::Break(e) => return Err(e), - } + }, ) } // EMIT_MIR try_identity.old.PreCodegen.after.mir fn old(x: Result) -> Result { - Ok( - match x { - Ok(v) => v, - Err(e) => return Err(e), - } - ) + Ok(match x { + Ok(v) => v, + Err(e) => return Err(e), + }) } fn main() { diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir index eabecaed0517..1c9ed25d7f2b 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir @@ -37,6 +37,10 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { scope 11 (inlined slice_from_raw_parts::) { debug data => _4; debug len => _5; + scope 12 (inlined std::ptr::from_raw_parts::<[u8], u8>) { + debug data_pointer => _4; + debug metadata => _5; + } } } } diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir index eabecaed0517..1c9ed25d7f2b 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir @@ -37,6 +37,10 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { scope 11 (inlined slice_from_raw_parts::) { debug data => _4; debug len => _5; + scope 12 (inlined std::ptr::from_raw_parts::<[u8], u8>) { + debug data_pointer => _4; + debug metadata => _5; + } } } } diff --git a/tests/mir-opt/reference_prop.rs b/tests/mir-opt/reference_prop.rs index e0b0d6994206..58d8b524ad64 100644 --- a/tests/mir-opt/reference_prop.rs +++ b/tests/mir-opt/reference_prop.rs @@ -658,7 +658,7 @@ fn read_through_raw(x: &mut usize) -> usize { // CHECK-NEXT: return; use std::intrinsics::mir::*; - mir!( + mir! { let r1: &mut usize; let r2: &mut usize; let p1: *mut usize; @@ -674,7 +674,7 @@ fn read_through_raw(x: &mut usize) -> usize { RET = *p2; Return() } - ) + } } #[custom_mir(dialect = "runtime", phase = "post-cleanup")] @@ -683,7 +683,7 @@ fn multiple_storage() { // CHECK: _3 = (*_2); use std::intrinsics::mir::*; - mir!( + mir! { let x: i32; { StorageLive(x); @@ -700,7 +700,7 @@ fn multiple_storage() { retblock = { Return() } - ) + } } #[custom_mir(dialect = "runtime", phase = "post-cleanup")] @@ -709,7 +709,7 @@ fn dominate_storage() { // CHECK: _5 = (*_2); use std::intrinsics::mir::*; - mir!( + mir! { let x: i32; let r: &i32; let c: i32; @@ -730,7 +730,7 @@ fn dominate_storage() { let d = true; match d { false => bb2, _ => bb0 } } - ) + } } #[custom_mir(dialect = "runtime", phase = "post-cleanup")] @@ -739,7 +739,7 @@ fn maybe_dead(m: bool) { // CHECK: (*_5) = const 7_i32; use std::intrinsics::mir::*; - mir!( + mir! { let x: i32; let y: i32; { @@ -774,7 +774,7 @@ fn maybe_dead(m: bool) { retblock = { Return() } - ) + } } fn mut_raw_then_mut_shr() -> (i32, i32) { @@ -787,7 +787,9 @@ fn mut_raw_then_mut_shr() -> (i32, i32) { let xshr = &*xref; // Verify that we completely replace with `x` in both cases. let a = *xshr; - unsafe { *xraw = 4; } + unsafe { + *xraw = 4; + } (a, x) } @@ -842,8 +844,7 @@ fn debuginfo() { // `constant_index_from_end` and `subslice` should not be promoted, as their value depends // on the slice length. - if let [_, ref constant_index, subslice @ .., ref constant_index_from_end] = &[6; 10][..] { - } + if let [_, ref constant_index, subslice @ .., ref constant_index_from_end] = &[6; 10][..] {} let multiple_borrow = &&&mut T(6).0; } diff --git a/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-abort.mir b/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-abort.mir index f9d58ea60a39..6dba667dd155 100644 --- a/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-abort.mir +++ b/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-abort.mir @@ -64,7 +64,7 @@ fn array_casts() -> () { StorageLive(_4); _4 = &mut _1; _3 = &raw mut (*_4); - _2 = move _3 as *mut usize (PointerCoercion(ArrayToPointer)); + _2 = move _3 as *mut usize (PtrToPtr); StorageDead(_3); StorageDead(_4); StorageLive(_5); @@ -87,7 +87,7 @@ fn array_casts() -> () { StorageLive(_11); _11 = &_8; _10 = &raw const (*_11); - _9 = move _10 as *const usize (PointerCoercion(ArrayToPointer)); + _9 = move _10 as *const usize (PtrToPtr); StorageDead(_10); StorageDead(_11); StorageLive(_12); diff --git a/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-unwind.mir b/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-unwind.mir index b0b70cd5d910..fa812002e26c 100644 --- a/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-unwind.mir +++ b/tests/mir-opt/retag.array_casts.SimplifyCfg-pre-optimizations.after.panic-unwind.mir @@ -64,7 +64,7 @@ fn array_casts() -> () { StorageLive(_4); _4 = &mut _1; _3 = &raw mut (*_4); - _2 = move _3 as *mut usize (PointerCoercion(ArrayToPointer)); + _2 = move _3 as *mut usize (PtrToPtr); StorageDead(_3); StorageDead(_4); StorageLive(_5); @@ -87,7 +87,7 @@ fn array_casts() -> () { StorageLive(_11); _11 = &_8; _10 = &raw const (*_11); - _9 = move _10 as *const usize (PointerCoercion(ArrayToPointer)); + _9 = move _10 as *const usize (PtrToPtr); StorageDead(_10); StorageDead(_11); StorageLive(_12); diff --git a/tests/mir-opt/retag.rs b/tests/mir-opt/retag.rs index 43d74aa5726f..001c55991383 100644 --- a/tests/mir-opt/retag.rs +++ b/tests/mir-opt/retag.rs @@ -59,7 +59,9 @@ pub fn main() { fn array_casts() { let mut x: [usize; 2] = [0, 0]; let p = &mut x as *mut usize; - unsafe { *p.add(1) = 1; } + unsafe { + *p.add(1) = 1; + } let x: [usize; 2] = [0, 1]; let p = &x as *const usize; diff --git a/tests/mir-opt/return_an_array.rs b/tests/mir-opt/return_an_array.rs index 09146a824fcd..673b5df7d730 100644 --- a/tests/mir-opt/return_an_array.rs +++ b/tests/mir-opt/return_an_array.rs @@ -2,8 +2,8 @@ // this tests move up progration, which is not yet implemented fn foo() -> [u8; 1024] { - let x = [0; 1024]; - return x; + let x = [0; 1024]; + return x; } -fn main() { } +fn main() {} diff --git a/tests/mir-opt/set_no_discriminant.rs b/tests/mir-opt/set_no_discriminant.rs index 0c29d1faf02a..586e28ae426a 100644 --- a/tests/mir-opt/set_no_discriminant.rs +++ b/tests/mir-opt/set_no_discriminant.rs @@ -19,7 +19,7 @@ pub fn f() -> usize { // CHECK-NOT: goto // CHECK: switchInt( // CHECK-NOT: goto - mir!( + mir! { let a: isize; let e: E; { @@ -39,7 +39,7 @@ pub fn f() -> usize { RET = 1; Return() } - ) + } } // EMIT_MIR set_no_discriminant.generic.JumpThreading.diff @@ -49,7 +49,7 @@ pub fn generic() -> usize { // CHECK-NOT: goto // CHECK: switchInt( // CHECK-NOT: goto - mir!( + mir! { let a: isize; let e: E; { @@ -69,7 +69,7 @@ pub fn generic() -> usize { RET = 1; Return() } - ) + } } fn main() { diff --git a/tests/mir-opt/simplify_dead_blocks.rs b/tests/mir-opt/simplify_dead_blocks.rs index 686eac582364..b9a404fd35c8 100644 --- a/tests/mir-opt/simplify_dead_blocks.rs +++ b/tests/mir-opt/simplify_dead_blocks.rs @@ -24,7 +24,7 @@ pub unsafe fn assert_nonzero_nonmax(x: u8) -> u8 { // CHECK-NEXT: _0 = _1; // CHECK-NEXT: return; // CHECK-NEXT: } - mir!( + mir! { { match x { 0 => unreachable, @@ -48,5 +48,5 @@ pub unsafe fn assert_nonzero_nonmax(x: u8) -> u8 { RET = x; Return() } - ) + } } diff --git a/tests/mir-opt/simplify_locals.rs b/tests/mir-opt/simplify_locals.rs index f57611111cfc..6511b5e87e47 100644 --- a/tests/mir-opt/simplify_locals.rs +++ b/tests/mir-opt/simplify_locals.rs @@ -1,13 +1,12 @@ // skip-filecheck //@ test-mir-pass: SimplifyLocals-before-const-prop - #![feature(thread_local)] #[derive(Copy, Clone)] enum E { - A, - B, + A, + B, } // EMIT_MIR simplify_locals.c.SimplifyLocals-before-const-prop.diff @@ -26,7 +25,7 @@ fn d1() { // EMIT_MIR simplify_locals.d2.SimplifyLocals-before-const-prop.diff fn d2() { // Unused set discriminant - {(10, E::A)}.1 = E::B; + { (10, E::A) }.1 = E::B; } // EMIT_MIR simplify_locals.r.SimplifyLocals-before-const-prop.diff @@ -37,7 +36,8 @@ fn r() { let _ = &mut a; } -#[thread_local] static mut X: u32 = 0; +#[thread_local] +static mut X: u32 = 0; // EMIT_MIR simplify_locals.t1.SimplifyLocals-before-const-prop.diff fn t1() { diff --git a/tests/mir-opt/simplify_locals_fixedpoint.rs b/tests/mir-opt/simplify_locals_fixedpoint.rs index 6947d31dc3e1..0b6c95630c0a 100644 --- a/tests/mir-opt/simplify_locals_fixedpoint.rs +++ b/tests/mir-opt/simplify_locals_fixedpoint.rs @@ -4,9 +4,7 @@ fn foo() { if let (Some(a), None) = (Option::::None, Option::::None) { - if a > 42u8 { - - } + if a > 42u8 {} } } diff --git a/tests/mir-opt/simplify_match.rs b/tests/mir-opt/simplify_match.rs index 2eac93edbb8e..b035b6339fae 100644 --- a/tests/mir-opt/simplify_match.rs +++ b/tests/mir-opt/simplify_match.rs @@ -5,8 +5,11 @@ fn noop() {} // EMIT_MIR simplify_match.main.GVN.diff fn main() { - match { let x = false; x } { + match { + let x = false; + x + } { true => noop(), - false => {}, + false => {} } } diff --git a/tests/mir-opt/single_use_consts.assign_const_to_return.SingleUseConsts.panic-abort.diff b/tests/mir-opt/single_use_consts.assign_const_to_return.SingleUseConsts.panic-abort.diff new file mode 100644 index 000000000000..8818c891e94b --- /dev/null +++ b/tests/mir-opt/single_use_consts.assign_const_to_return.SingleUseConsts.panic-abort.diff @@ -0,0 +1,12 @@ +- // MIR for `assign_const_to_return` before SingleUseConsts ++ // MIR for `assign_const_to_return` after SingleUseConsts + + fn assign_const_to_return() -> bool { + let mut _0: bool; + + bb0: { + _0 = const ::ASSOC_BOOL; + return; + } + } + diff --git a/tests/mir-opt/single_use_consts.assign_const_to_return.SingleUseConsts.panic-unwind.diff b/tests/mir-opt/single_use_consts.assign_const_to_return.SingleUseConsts.panic-unwind.diff new file mode 100644 index 000000000000..8818c891e94b --- /dev/null +++ b/tests/mir-opt/single_use_consts.assign_const_to_return.SingleUseConsts.panic-unwind.diff @@ -0,0 +1,12 @@ +- // MIR for `assign_const_to_return` before SingleUseConsts ++ // MIR for `assign_const_to_return` after SingleUseConsts + + fn assign_const_to_return() -> bool { + let mut _0: bool; + + bb0: { + _0 = const ::ASSOC_BOOL; + return; + } + } + diff --git a/tests/mir-opt/single_use_consts.if_const.SingleUseConsts.panic-abort.diff b/tests/mir-opt/single_use_consts.if_const.SingleUseConsts.panic-abort.diff new file mode 100644 index 000000000000..468076e5ee3c --- /dev/null +++ b/tests/mir-opt/single_use_consts.if_const.SingleUseConsts.panic-abort.diff @@ -0,0 +1,31 @@ +- // MIR for `if_const` before SingleUseConsts ++ // MIR for `if_const` after SingleUseConsts + + fn if_const() -> i32 { + let mut _0: i32; + let mut _1: bool; + + bb0: { + StorageLive(_1); +- _1 = const ::ASSOC_BOOL; +- switchInt(move _1) -> [0: bb2, otherwise: bb1]; ++ nop; ++ switchInt(const ::ASSOC_BOOL) -> [0: bb2, otherwise: bb1]; + } + + bb1: { + _0 = const 7_i32; + goto -> bb3; + } + + bb2: { + _0 = const 42_i32; + goto -> bb3; + } + + bb3: { + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/single_use_consts.if_const.SingleUseConsts.panic-unwind.diff b/tests/mir-opt/single_use_consts.if_const.SingleUseConsts.panic-unwind.diff new file mode 100644 index 000000000000..468076e5ee3c --- /dev/null +++ b/tests/mir-opt/single_use_consts.if_const.SingleUseConsts.panic-unwind.diff @@ -0,0 +1,31 @@ +- // MIR for `if_const` before SingleUseConsts ++ // MIR for `if_const` after SingleUseConsts + + fn if_const() -> i32 { + let mut _0: i32; + let mut _1: bool; + + bb0: { + StorageLive(_1); +- _1 = const ::ASSOC_BOOL; +- switchInt(move _1) -> [0: bb2, otherwise: bb1]; ++ nop; ++ switchInt(const ::ASSOC_BOOL) -> [0: bb2, otherwise: bb1]; + } + + bb1: { + _0 = const 7_i32; + goto -> bb3; + } + + bb2: { + _0 = const 42_i32; + goto -> bb3; + } + + bb3: { + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/single_use_consts.if_const_debug.SingleUseConsts.panic-abort.diff b/tests/mir-opt/single_use_consts.if_const_debug.SingleUseConsts.panic-abort.diff new file mode 100644 index 000000000000..ad1a2b300f2a --- /dev/null +++ b/tests/mir-opt/single_use_consts.if_const_debug.SingleUseConsts.panic-abort.diff @@ -0,0 +1,46 @@ +- // MIR for `if_const_debug` before SingleUseConsts ++ // MIR for `if_const_debug` after SingleUseConsts + + fn if_const_debug() -> i32 { + let mut _0: i32; + let _1: bool; + let _2: (); + let mut _3: bool; + scope 1 { +- debug my_bool => _1; ++ debug my_bool => const ::ASSOC_BOOL; + } + + bb0: { + StorageLive(_1); +- _1 = const ::ASSOC_BOOL; ++ nop; + StorageLive(_2); + _2 = do_whatever() -> [return: bb1, unwind unreachable]; + } + + bb1: { + StorageDead(_2); + StorageLive(_3); +- _3 = _1; ++ _3 = const ::ASSOC_BOOL; + switchInt(move _3) -> [0: bb3, otherwise: bb2]; + } + + bb2: { + _0 = const 7_i32; + goto -> bb4; + } + + bb3: { + _0 = const 42_i32; + goto -> bb4; + } + + bb4: { + StorageDead(_3); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/single_use_consts.if_const_debug.SingleUseConsts.panic-unwind.diff b/tests/mir-opt/single_use_consts.if_const_debug.SingleUseConsts.panic-unwind.diff new file mode 100644 index 000000000000..827a292e5d02 --- /dev/null +++ b/tests/mir-opt/single_use_consts.if_const_debug.SingleUseConsts.panic-unwind.diff @@ -0,0 +1,46 @@ +- // MIR for `if_const_debug` before SingleUseConsts ++ // MIR for `if_const_debug` after SingleUseConsts + + fn if_const_debug() -> i32 { + let mut _0: i32; + let _1: bool; + let _2: (); + let mut _3: bool; + scope 1 { +- debug my_bool => _1; ++ debug my_bool => const ::ASSOC_BOOL; + } + + bb0: { + StorageLive(_1); +- _1 = const ::ASSOC_BOOL; ++ nop; + StorageLive(_2); + _2 = do_whatever() -> [return: bb1, unwind continue]; + } + + bb1: { + StorageDead(_2); + StorageLive(_3); +- _3 = _1; ++ _3 = const ::ASSOC_BOOL; + switchInt(move _3) -> [0: bb3, otherwise: bb2]; + } + + bb2: { + _0 = const 7_i32; + goto -> bb4; + } + + bb3: { + _0 = const 42_i32; + goto -> bb4; + } + + bb4: { + StorageDead(_3); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/single_use_consts.keep_parameter.SingleUseConsts.panic-abort.diff b/tests/mir-opt/single_use_consts.keep_parameter.SingleUseConsts.panic-abort.diff new file mode 100644 index 000000000000..f7d823af9e3d --- /dev/null +++ b/tests/mir-opt/single_use_consts.keep_parameter.SingleUseConsts.panic-abort.diff @@ -0,0 +1,14 @@ +- // MIR for `keep_parameter` before SingleUseConsts ++ // MIR for `keep_parameter` after SingleUseConsts + + fn keep_parameter(_1: i32) -> () { + debug other => _1; + let mut _0: (); + + bb0: { + _1 = const ::ASSOC_INT; + _0 = const (); + return; + } + } + diff --git a/tests/mir-opt/single_use_consts.keep_parameter.SingleUseConsts.panic-unwind.diff b/tests/mir-opt/single_use_consts.keep_parameter.SingleUseConsts.panic-unwind.diff new file mode 100644 index 000000000000..f7d823af9e3d --- /dev/null +++ b/tests/mir-opt/single_use_consts.keep_parameter.SingleUseConsts.panic-unwind.diff @@ -0,0 +1,14 @@ +- // MIR for `keep_parameter` before SingleUseConsts ++ // MIR for `keep_parameter` after SingleUseConsts + + fn keep_parameter(_1: i32) -> () { + debug other => _1; + let mut _0: (); + + bb0: { + _1 = const ::ASSOC_INT; + _0 = const (); + return; + } + } + diff --git a/tests/mir-opt/single_use_consts.match_const.SingleUseConsts.panic-abort.diff b/tests/mir-opt/single_use_consts.match_const.SingleUseConsts.panic-abort.diff new file mode 100644 index 000000000000..8d87438a47ae --- /dev/null +++ b/tests/mir-opt/single_use_consts.match_const.SingleUseConsts.panic-abort.diff @@ -0,0 +1,36 @@ +- // MIR for `match_const` before SingleUseConsts ++ // MIR for `match_const` after SingleUseConsts + + fn match_const() -> &str { + let mut _0: &str; + let mut _1: i32; + + bb0: { + StorageLive(_1); +- _1 = const ::ASSOC_INT; +- switchInt(_1) -> [7: bb2, 42: bb3, otherwise: bb1]; ++ nop; ++ switchInt(const ::ASSOC_INT) -> [7: bb2, 42: bb3, otherwise: bb1]; + } + + bb1: { + _0 = const "world"; + goto -> bb4; + } + + bb2: { + _0 = const "hello"; + goto -> bb4; + } + + bb3: { + _0 = const "towel"; + goto -> bb4; + } + + bb4: { + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/single_use_consts.match_const.SingleUseConsts.panic-unwind.diff b/tests/mir-opt/single_use_consts.match_const.SingleUseConsts.panic-unwind.diff new file mode 100644 index 000000000000..8d87438a47ae --- /dev/null +++ b/tests/mir-opt/single_use_consts.match_const.SingleUseConsts.panic-unwind.diff @@ -0,0 +1,36 @@ +- // MIR for `match_const` before SingleUseConsts ++ // MIR for `match_const` after SingleUseConsts + + fn match_const() -> &str { + let mut _0: &str; + let mut _1: i32; + + bb0: { + StorageLive(_1); +- _1 = const ::ASSOC_INT; +- switchInt(_1) -> [7: bb2, 42: bb3, otherwise: bb1]; ++ nop; ++ switchInt(const ::ASSOC_INT) -> [7: bb2, 42: bb3, otherwise: bb1]; + } + + bb1: { + _0 = const "world"; + goto -> bb4; + } + + bb2: { + _0 = const "hello"; + goto -> bb4; + } + + bb3: { + _0 = const "towel"; + goto -> bb4; + } + + bb4: { + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/single_use_consts.match_const_debug.SingleUseConsts.panic-abort.diff b/tests/mir-opt/single_use_consts.match_const_debug.SingleUseConsts.panic-abort.diff new file mode 100644 index 000000000000..f192f3feb96e --- /dev/null +++ b/tests/mir-opt/single_use_consts.match_const_debug.SingleUseConsts.panic-abort.diff @@ -0,0 +1,47 @@ +- // MIR for `match_const_debug` before SingleUseConsts ++ // MIR for `match_const_debug` after SingleUseConsts + + fn match_const_debug() -> &str { + let mut _0: &str; + let _1: i32; + let _2: (); + scope 1 { +- debug my_int => _1; ++ debug my_int => const ::ASSOC_INT; + } + + bb0: { + StorageLive(_1); +- _1 = const ::ASSOC_INT; ++ nop; + StorageLive(_2); + _2 = do_whatever() -> [return: bb1, unwind unreachable]; + } + + bb1: { + StorageDead(_2); +- switchInt(_1) -> [7: bb3, 42: bb4, otherwise: bb2]; ++ switchInt(const ::ASSOC_INT) -> [7: bb3, 42: bb4, otherwise: bb2]; + } + + bb2: { + _0 = const "world"; + goto -> bb5; + } + + bb3: { + _0 = const "hello"; + goto -> bb5; + } + + bb4: { + _0 = const "towel"; + goto -> bb5; + } + + bb5: { + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/single_use_consts.match_const_debug.SingleUseConsts.panic-unwind.diff b/tests/mir-opt/single_use_consts.match_const_debug.SingleUseConsts.panic-unwind.diff new file mode 100644 index 000000000000..261faf415f3b --- /dev/null +++ b/tests/mir-opt/single_use_consts.match_const_debug.SingleUseConsts.panic-unwind.diff @@ -0,0 +1,47 @@ +- // MIR for `match_const_debug` before SingleUseConsts ++ // MIR for `match_const_debug` after SingleUseConsts + + fn match_const_debug() -> &str { + let mut _0: &str; + let _1: i32; + let _2: (); + scope 1 { +- debug my_int => _1; ++ debug my_int => const ::ASSOC_INT; + } + + bb0: { + StorageLive(_1); +- _1 = const ::ASSOC_INT; ++ nop; + StorageLive(_2); + _2 = do_whatever() -> [return: bb1, unwind continue]; + } + + bb1: { + StorageDead(_2); +- switchInt(_1) -> [7: bb3, 42: bb4, otherwise: bb2]; ++ switchInt(const ::ASSOC_INT) -> [7: bb3, 42: bb4, otherwise: bb2]; + } + + bb2: { + _0 = const "world"; + goto -> bb5; + } + + bb3: { + _0 = const "hello"; + goto -> bb5; + } + + bb4: { + _0 = const "towel"; + goto -> bb5; + } + + bb5: { + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/single_use_consts.never_used_debug.SingleUseConsts.panic-abort.diff b/tests/mir-opt/single_use_consts.never_used_debug.SingleUseConsts.panic-abort.diff new file mode 100644 index 000000000000..8ef94a790a34 --- /dev/null +++ b/tests/mir-opt/single_use_consts.never_used_debug.SingleUseConsts.panic-abort.diff @@ -0,0 +1,21 @@ +- // MIR for `never_used_debug` before SingleUseConsts ++ // MIR for `never_used_debug` after SingleUseConsts + + fn never_used_debug() -> () { + let mut _0: (); + let _1: i32; + scope 1 { +- debug my_int => _1; ++ debug my_int => const ::ASSOC_INT; + } + + bb0: { + StorageLive(_1); +- _1 = const ::ASSOC_INT; ++ nop; + _0 = const (); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/single_use_consts.never_used_debug.SingleUseConsts.panic-unwind.diff b/tests/mir-opt/single_use_consts.never_used_debug.SingleUseConsts.panic-unwind.diff new file mode 100644 index 000000000000..8ef94a790a34 --- /dev/null +++ b/tests/mir-opt/single_use_consts.never_used_debug.SingleUseConsts.panic-unwind.diff @@ -0,0 +1,21 @@ +- // MIR for `never_used_debug` before SingleUseConsts ++ // MIR for `never_used_debug` after SingleUseConsts + + fn never_used_debug() -> () { + let mut _0: (); + let _1: i32; + scope 1 { +- debug my_int => _1; ++ debug my_int => const ::ASSOC_INT; + } + + bb0: { + StorageLive(_1); +- _1 = const ::ASSOC_INT; ++ nop; + _0 = const (); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/single_use_consts.rs b/tests/mir-opt/single_use_consts.rs new file mode 100644 index 000000000000..ecb602c647a5 --- /dev/null +++ b/tests/mir-opt/single_use_consts.rs @@ -0,0 +1,80 @@ +//@ test-mir-pass: SingleUseConsts +//@ compile-flags: -C debuginfo=full +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY + +trait MyTrait { + const ASSOC_BOOL: bool; + const ASSOC_INT: i32; +} + +// EMIT_MIR single_use_consts.if_const.SingleUseConsts.diff +fn if_const() -> i32 { + // CHECK-LABEL: fn if_const( + // CHECK: switchInt(const ::ASSOC_BOOL) + if T::ASSOC_BOOL { 7 } else { 42 } +} + +// EMIT_MIR single_use_consts.match_const.SingleUseConsts.diff +fn match_const() -> &'static str { + // CHECK-LABEL: fn match_const( + // CHECK: switchInt(const ::ASSOC_INT) + match T::ASSOC_INT { + 7 => "hello", + 42 => "towel", + _ => "world", + } +} + +// EMIT_MIR single_use_consts.if_const_debug.SingleUseConsts.diff +fn if_const_debug() -> i32 { + // CHECK-LABEL: fn if_const_debug( + // CHECK: my_bool => const ::ASSOC_BOOL; + // FIXME: `if` forces a temporary (unlike `match`), so the const isn't direct + // CHECK: _3 = const ::ASSOC_BOOL; + // CHECK: switchInt(move _3) + let my_bool = T::ASSOC_BOOL; + do_whatever(); + if my_bool { 7 } else { 42 } +} + +// EMIT_MIR single_use_consts.match_const_debug.SingleUseConsts.diff +fn match_const_debug() -> &'static str { + // CHECK-LABEL: fn match_const_debug( + // CHECK: my_int => const ::ASSOC_INT; + // CHECK: switchInt(const ::ASSOC_INT) + let my_int = T::ASSOC_INT; + do_whatever(); + match my_int { + 7 => "hello", + 42 => "towel", + _ => "world", + } +} + +// EMIT_MIR single_use_consts.never_used_debug.SingleUseConsts.diff +#[allow(unused_variables)] +fn never_used_debug() { + // CHECK-LABEL: fn never_used_debug( + // CHECK: my_int => const ::ASSOC_INT; + // CHECK-NOT: ASSOC_INT + // CHECK: nop + // CHECK-NOT: ASSOC_INT + let my_int = T::ASSOC_INT; +} + +// EMIT_MIR single_use_consts.assign_const_to_return.SingleUseConsts.diff +fn assign_const_to_return() -> bool { + // CHECK-LABEL: fn assign_const_to_return( + // CHECK: _0 = const ::ASSOC_BOOL; + T::ASSOC_BOOL +} + +// EMIT_MIR single_use_consts.keep_parameter.SingleUseConsts.diff +fn keep_parameter(mut other: i32) { + // CHECK-LABEL: fn keep_parameter( + // CHECK: _1 = const ::ASSOC_INT; + // CHECK: _0 = const (); + other = T::ASSOC_INT; +} + +fn do_whatever() {} diff --git a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff index 0d518f78f6b5..819f3f86d145 100644 --- a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff +++ b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff @@ -11,30 +11,28 @@ let _9: (); let _10: (); let mut _11: std::fmt::Arguments<'_>; - let mut _12: &[&str]; - let mut _13: &[&str; 3]; - let _14: &[&str; 3]; - let _15: [&str; 3]; - let mut _16: &[core::fmt::rt::Argument<'_>]; - let mut _17: &[core::fmt::rt::Argument<'_>; 2]; - let _18: &[core::fmt::rt::Argument<'_>; 2]; - let _19: [core::fmt::rt::Argument<'_>; 2]; - let mut _20: core::fmt::rt::Argument<'_>; - let mut _21: &std::boxed::Box; - let _22: &std::boxed::Box; - let mut _23: core::fmt::rt::Argument<'_>; - let mut _24: &u32; - let _25: &u32; - let mut _27: bool; + let mut _12: &[&str; 3]; + let _13: &[&str; 3]; + let _14: [&str; 3]; + let mut _15: &[core::fmt::rt::Argument<'_>; 2]; + let _16: &[core::fmt::rt::Argument<'_>; 2]; + let _17: [core::fmt::rt::Argument<'_>; 2]; + let mut _18: core::fmt::rt::Argument<'_>; + let mut _19: &std::boxed::Box; + let _20: &std::boxed::Box; + let mut _21: core::fmt::rt::Argument<'_>; + let mut _22: &u32; + let _23: &u32; + let mut _25: bool; + let mut _26: isize; + let mut _27: isize; let mut _28: isize; - let mut _29: isize; - let mut _30: isize; -+ let _31: std::result::Result, ::Err>; -+ let _32: u32; ++ let _29: std::result::Result, ::Err>; ++ let _30: u32; scope 1 { - debug foo => _1; -+ debug ((foo: Foo).0: std::result::Result, ::Err>) => _31; -+ debug ((foo: Foo).1: u32) => _32; ++ debug ((foo: Foo).0: std::result::Result, ::Err>) => _29; ++ debug ((foo: Foo).1: u32) => _30; let _5: std::result::Result, ::Err>; scope 2 { debug x => _5; @@ -44,17 +42,17 @@ scope 4 { debug x => _8; let _8: std::boxed::Box; - let mut _26: &[&str; 3]; + let mut _24: &[&str; 3]; } } } } bb0: { - _27 = const false; + _25 = const false; - StorageLive(_1); -+ StorageLive(_31); -+ StorageLive(_32); ++ StorageLive(_29); ++ StorageLive(_30); + nop; StorageLive(_2); StorageLive(_3); @@ -68,83 +66,77 @@ _2 = Result::, ::Err>::Ok(move _3); StorageDead(_3); - _1 = Foo:: { x: move _2, y: const 7_u32 }; -+ _31 = move _2; -+ _32 = const 7_u32; ++ _29 = move _2; ++ _30 = const 7_u32; + nop; StorageDead(_2); StorageLive(_5); - _27 = const true; + _25 = const true; - _5 = move (_1.0: std::result::Result, ::Err>); -+ _5 = move _31; ++ _5 = move _29; StorageLive(_6); - _6 = (_1.1: u32); -+ _6 = _32; ++ _6 = _30; _7 = discriminant(_5); switchInt(move _7) -> [0: bb2, otherwise: bb7]; } bb2: { StorageLive(_8); - _27 = const false; + _25 = const false; _8 = move ((_5 as Ok).0: std::boxed::Box); StorageLive(_9); StorageLive(_10); StorageLive(_11); StorageLive(_12); StorageLive(_13); - StorageLive(_14); - _26 = const foo::::promoted[0]; - _14 = &(*_26); - _13 = &(*_14); - _12 = move _13 as &[&str] (PointerCoercion(Unsize)); - StorageDead(_13); + _24 = const foo::::promoted[0]; + _13 = &(*_24); + _12 = &(*_13); + StorageLive(_15); StorageLive(_16); StorageLive(_17); StorageLive(_18); StorageLive(_19); StorageLive(_20); - StorageLive(_21); - StorageLive(_22); - _22 = &_8; - _21 = &(*_22); - _20 = core::fmt::rt::Argument::<'_>::new_display::>(move _21) -> [return: bb3, unwind unreachable]; + _20 = &_8; + _19 = &(*_20); + _18 = core::fmt::rt::Argument::<'_>::new_display::>(move _19) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_21); + StorageDead(_19); + StorageLive(_21); + StorageLive(_22); StorageLive(_23); - StorageLive(_24); - StorageLive(_25); - _25 = &_6; - _24 = &(*_25); - _23 = core::fmt::rt::Argument::<'_>::new_display::(move _24) -> [return: bb4, unwind unreachable]; + _23 = &_6; + _22 = &(*_23); + _21 = core::fmt::rt::Argument::<'_>::new_display::(move _22) -> [return: bb4, unwind unreachable]; } bb4: { - StorageDead(_24); - _19 = [move _20, move _23]; - StorageDead(_23); - StorageDead(_20); - _18 = &_19; - _17 = &(*_18); - _16 = move _17 as &[core::fmt::rt::Argument<'_>] (PointerCoercion(Unsize)); - StorageDead(_17); - _11 = Arguments::<'_>::new_v1(move _12, move _16) -> [return: bb5, unwind unreachable]; + StorageDead(_22); + _17 = [move _18, move _21]; + StorageDead(_21); + StorageDead(_18); + _16 = &_17; + _15 = &(*_16); + _11 = Arguments::<'_>::new_v1::<3, 2>(move _12, move _15) -> [return: bb5, unwind unreachable]; } bb5: { - StorageDead(_16); + StorageDead(_15); StorageDead(_12); _10 = _eprint(move _11) -> [return: bb6, unwind unreachable]; } bb6: { StorageDead(_11); - StorageDead(_25); - StorageDead(_22); - StorageDead(_19); - StorageDead(_18); - StorageDead(_14); + StorageDead(_23); + StorageDead(_20); + StorageDead(_17); + StorageDead(_16); + StorageDead(_13); StorageDead(_10); _9 = const (); StorageDead(_9); @@ -164,22 +156,22 @@ bb9: { StorageDead(_6); - _28 = discriminant(_5); - switchInt(move _28) -> [0: bb11, otherwise: bb13]; + _26 = discriminant(_5); + switchInt(move _26) -> [0: bb11, otherwise: bb13]; } bb10: { - _27 = const false; + _25 = const false; StorageDead(_5); - StorageDead(_1); -+ StorageDead(_31); -+ StorageDead(_32); ++ StorageDead(_29); ++ StorageDead(_30); + nop; return; } bb11: { - switchInt(_27) -> [0: bb10, otherwise: bb12]; + switchInt(_25) -> [0: bb10, otherwise: bb12]; } bb12: { diff --git a/tests/mir-opt/sroa/lifetimes.rs b/tests/mir-opt/sroa/lifetimes.rs index 6c18dbaf5a23..90aa2a10938e 100644 --- a/tests/mir-opt/sroa/lifetimes.rs +++ b/tests/mir-opt/sroa/lifetimes.rs @@ -19,10 +19,7 @@ fn foo() { // CHECK-NOT: [foo:_.*]: Foo // CHECK-NOT: Box - let foo: Foo = Foo { - x: Ok(Box::new(5_u32)), - y: 7_u32, - }; + let foo: Foo = Foo { x: Ok(Box::new(5_u32)), y: 7_u32 }; let x = foo.x; let y = foo.y; diff --git a/tests/mir-opt/switch_to_self.rs b/tests/mir-opt/switch_to_self.rs index fc270fd33cf8..51a7c13494fb 100644 --- a/tests/mir-opt/switch_to_self.rs +++ b/tests/mir-opt/switch_to_self.rs @@ -8,7 +8,7 @@ use std::intrinsics::mir::*; // EMIT_MIR switch_to_self.test.MatchBranchSimplification.diff #[custom_mir(dialect = "runtime", phase = "post-cleanup")] pub fn test(x: bool) { - mir!( + mir! { { Goto(bb0) } @@ -18,5 +18,5 @@ pub fn test(x: bool) { bb1 = { match x { false => bb0, _ => bb1 } } - ) + } } diff --git a/tests/mir-opt/uninhabited_enum.rs b/tests/mir-opt/uninhabited_enum.rs index 8816f31f9dfa..859535852cf0 100644 --- a/tests/mir-opt/uninhabited_enum.rs +++ b/tests/mir-opt/uninhabited_enum.rs @@ -6,15 +6,15 @@ pub enum Void {} // EMIT_MIR uninhabited_enum.process_never.SimplifyLocals-final.after.mir #[no_mangle] pub fn process_never(input: *const !) { - let _input = unsafe { &*input }; + let _input = unsafe { &*input }; } // EMIT_MIR uninhabited_enum.process_void.SimplifyLocals-final.after.mir #[no_mangle] pub fn process_void(input: *const Void) { - let _input = unsafe { &*input }; - // In the future, this should end with `unreachable`, but we currently only do - // unreachability analysis for `!`. + let _input = unsafe { &*input }; + // In the future, this should end with `unreachable`, but we currently only do + // unreachability analysis for `!`. } fn main() {} diff --git a/tests/mir-opt/unnamed-fields/field_access.rs b/tests/mir-opt/unnamed-fields/field_access.rs index 5badfa1646bc..cc0ac9a34274 100644 --- a/tests/mir-opt/unnamed-fields/field_access.rs +++ b/tests/mir-opt/unnamed-fields/field_access.rs @@ -17,7 +17,7 @@ struct Foo { _: struct { d: [u8; 1], } - } + }, } #[repr(C)] @@ -31,10 +31,9 @@ union Bar { _: union { d: [u8; 1], } - } + }, } - fn access(_: T) {} // CHECK-LABEL: fn foo( @@ -71,5 +70,4 @@ fn bar(bar: Bar) { } } - fn main() {} diff --git a/tests/mir-opt/unreachable.rs b/tests/mir-opt/unreachable.rs index 5838b35a553a..881e3542f0ac 100644 --- a/tests/mir-opt/unreachable.rs +++ b/tests/mir-opt/unreachable.rs @@ -35,7 +35,7 @@ fn if_let() { _y = 42; } - match _x { } + match _x {} } } @@ -56,7 +56,7 @@ fn as_match() { // CHECK: return; match empty() { None => {} - Some(_x) => match _x {} + Some(_x) => match _x {}, } } diff --git a/tests/mir-opt/unusual_item_types.rs b/tests/mir-opt/unusual_item_types.rs index 788475431044..2f05981e812e 100644 --- a/tests/mir-opt/unusual_item_types.rs +++ b/tests/mir-opt/unusual_item_types.rs @@ -3,7 +3,6 @@ // that we don't create filenames containing `<` and `>` //@ compile-flags: -Zmir-opt-level=0 - struct A; // EMIT_MIR unusual_item_types.{impl#0}-ASSOCIATED_CONSTANT.built.after.mir @@ -23,8 +22,8 @@ enum E { V = 5, } +// EMIT_MIR core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir pub fn main() { let f = Test::X as fn(usize) -> Test; -// EMIT_MIR core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir let v = Vec::::new(); } diff --git a/tests/mir-opt/unusual_item_types.{impl#0}-ASSOCIATED_CONSTANT.built.after.mir b/tests/mir-opt/unusual_item_types.{impl#0}-ASSOCIATED_CONSTANT.built.after.mir index a5121ae550d6..e2edbfcd4fa3 100644 --- a/tests/mir-opt/unusual_item_types.{impl#0}-ASSOCIATED_CONSTANT.built.after.mir +++ b/tests/mir-opt/unusual_item_types.{impl#0}-ASSOCIATED_CONSTANT.built.after.mir @@ -1,6 +1,6 @@ -// MIR for `::ASSOCIATED_CONSTANT` after built +// MIR for `::ASSOCIATED_CONSTANT` after built -const ::ASSOCIATED_CONSTANT: i32 = { +const ::ASSOCIATED_CONSTANT: i32 = { let mut _0: i32; bb0: { diff --git a/tests/pretty/ast-stmt-expr-attr.rs b/tests/pretty/ast-stmt-expr-attr.rs index 899f7173f704..fd7272a1b1fd 100644 --- a/tests/pretty/ast-stmt-expr-attr.rs +++ b/tests/pretty/ast-stmt-expr-attr.rs @@ -13,17 +13,17 @@ fn syntax() { let _ = #[attr] (); let _ = #[attr] (#[attr] 0,); let _ = #[attr] (#[attr] 0, 0); - let _ = (#[attr] 0) + #[attr] 0; - let _ = (#[attr] 0) / #[attr] 0; - let _ = (#[attr] 0) & #[attr] 0; - let _ = (#[attr] 0) % #[attr] 0; + let _ = #[attr] 0 + #[attr] 0; + let _ = #[attr] 0 / #[attr] 0; + let _ = #[attr] 0 & #[attr] 0; + let _ = #[attr] 0 % #[attr] 0; let _ = #[attr] (0 + 0); let _ = #[attr] !0; let _ = #[attr] -0; let _ = #[attr] false; let _ = #[attr] 0; let _ = #[attr] 'c'; - let _ = (#[attr] x) as Y; + let _ = #[attr] x as Y; let _ = #[attr] (x as Y); let _ = #[attr] while true { @@ -88,9 +88,9 @@ fn syntax() { let _ = (); foo }; - let _ = (#[attr] x) = y; + let _ = #[attr] x = y; let _ = #[attr] (x = y); - let _ = (#[attr] x) += y; + let _ = #[attr] x += y; let _ = #[attr] (x += y); let _ = #[attr] foo.bar; let _ = (#[attr] foo).bar; @@ -98,8 +98,8 @@ fn syntax() { let _ = (#[attr] foo).0; let _ = #[attr] foo[bar]; let _ = (#[attr] foo)[bar]; - let _ = (#[attr] 0)..#[attr] 0; - let _ = (#[attr] 0)..; + let _ = #[attr] 0..#[attr] 0; + let _ = #[attr] 0..; let _ = #[attr] (0..0); let _ = #[attr] (0..); let _ = #[attr] (..0); diff --git a/tests/pretty/hir-fn-variadic.pp b/tests/pretty/hir-fn-variadic.pp index 978e65c825bb..dfbaff696440 100644 --- a/tests/pretty/hir-fn-variadic.pp +++ b/tests/pretty/hir-fn-variadic.pp @@ -9,7 +9,7 @@ use ::std::prelude::rust_2015::*; extern crate std; extern "C" { - fn foo(x: i32, va1: ...); + unsafe fn foo(x: i32, va1: ...); } unsafe extern "C" fn bar(_: i32, mut va2: ...) -> usize { va2.arg::() } diff --git a/tests/pretty/issue-4264.pp b/tests/pretty/issue-4264.pp index af64260d0200..018ccf82daeb 100644 --- a/tests/pretty/issue-4264.pp +++ b/tests/pretty/issue-4264.pp @@ -11,15 +11,15 @@ extern crate std; fn foo(_: [i32; (3 as usize)]) ({ } as ()) fn bar() ({ - const FOO: usize = ((5 as usize) - (4 as usize) as usize); - let _: [(); (FOO as usize)] = ([(() as ())] as [(); 1]); + const FOO: usize = ((5 as usize) - (4 as usize) as usize); + let _: [(); (FOO as usize)] = ([(() as ())] as [(); 1]); - let _: [(); (1 as usize)] = ([(() as ())] as [(); 1]); + let _: [(); (1 as usize)] = ([(() as ())] as [(); 1]); - let _ = - (((&([(1 as i32), (2 as i32), (3 as i32)] as [i32; 3]) as - &[i32; 3]) as *const _ as *const [i32; 3]) as - *const [i32; (3 as usize)] as *const [i32; 3]); + let _ = + (((&([(1 as i32), (2 as i32), (3 as i32)] as [i32; 3]) as &[i32; 3]) + as *const _ as *const [i32; 3]) as *const [i32; (3 as usize)] + as *const [i32; 3]); @@ -29,17 +29,17 @@ fn bar() ({ - ({ - let res = - ((::alloc::fmt::format as - for<'a> fn(Arguments<'a>) -> String {format})(((format_arguments::new_const - as - fn(&[&'static str]) -> Arguments<'_> {Arguments::<'_>::new_const})((&([("test" - as &str)] as [&str; 1]) as &[&str; 1])) as Arguments<'_>)) - as String); - (res as String) - } as String); - } as ()) + ({ + let res = + ((::alloc::fmt::format as + for<'a> fn(Arguments<'a>) -> String {format})(((format_arguments::new_const + as + fn(&[&'static str; 1]) -> Arguments<'_> {Arguments::<'_>::new_const::<1>})((&([("test" + as &str)] as [&str; 1]) as &[&str; 1])) as Arguments<'_>)) + as String); + (res as String) + } as String); +} as ()) type Foo = [i32; (3 as usize)]; struct Bar { x: [i32; (3 as usize)], @@ -48,9 +48,9 @@ struct TupleBar([i32; (4 as usize)]); enum Baz { BazVariant([i32; (5 as usize)]), } fn id(x: T) -> T ({ (x as T) } as T) fn use_id() ({ - let _ = - ((id::<[i32; (3 as usize)]> as - fn([i32; 3]) -> [i32; 3] {id::<[i32; 3]>})(([(1 as i32), - (2 as i32), (3 as i32)] as [i32; 3])) as [i32; 3]); - } as ()) + let _ = + ((id::<[i32; (3 as usize)]> as + fn([i32; 3]) -> [i32; 3] {id::<[i32; 3]>})(([(1 as i32), + (2 as i32), (3 as i32)] as [i32; 3])) as [i32; 3]); +} as ()) fn main() ({ } as ()) diff --git a/tests/pretty/stmt_expr_attributes.rs b/tests/pretty/stmt_expr_attributes.rs index 5eb7d2fcae36..01a503ce7eea 100644 --- a/tests/pretty/stmt_expr_attributes.rs +++ b/tests/pretty/stmt_expr_attributes.rs @@ -147,13 +147,13 @@ fn _11() { let _ = #[rustc_dummy] (0); let _ = #[rustc_dummy] (0,); let _ = #[rustc_dummy] (0, 0); - let _ = (#[rustc_dummy] 0) + #[rustc_dummy] 0; + let _ = #[rustc_dummy] 0 + #[rustc_dummy] 0; let _ = #[rustc_dummy] !0; let _ = #[rustc_dummy] -0i32; let _ = #[rustc_dummy] false; let _ = #[rustc_dummy] 'c'; let _ = #[rustc_dummy] 0; - let _ = (#[rustc_dummy] 0) as usize; + let _ = #[rustc_dummy] 0 as usize; let _ = #[rustc_dummy] while false { #![rustc_dummy] @@ -213,8 +213,8 @@ fn _11() { #![rustc_dummy] }; let mut x = 0; - let _ = (#[rustc_dummy] x) = 15; - let _ = (#[rustc_dummy] x) += 15; + let _ = #[rustc_dummy] x = 15; + let _ = #[rustc_dummy] x += 15; let s = Foo { data: () }; let _ = #[rustc_dummy] s.data; let _ = (#[rustc_dummy] s).data; @@ -224,8 +224,8 @@ fn _11() { let v = vec!(0); let _ = #[rustc_dummy] v[0]; let _ = (#[rustc_dummy] v)[0]; - let _ = (#[rustc_dummy] 0)..#[rustc_dummy] 0; - let _ = (#[rustc_dummy] 0)..; + let _ = #[rustc_dummy] 0..#[rustc_dummy] 0; + let _ = #[rustc_dummy] 0..; let _ = #[rustc_dummy] (0..0); let _ = #[rustc_dummy] (0..); let _ = #[rustc_dummy] (..0); diff --git a/tests/run-make-fulldeps/hotplug_codegen_backend/Makefile b/tests/run-make-fulldeps/hotplug_codegen_backend/Makefile deleted file mode 100644 index 3a5a66b6755e..000000000000 --- a/tests/run-make-fulldeps/hotplug_codegen_backend/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -include ../../run-make/tools.mk - -# ignore-stage1 - -# This test both exists as a check that -Zcodegen-backend is capable of loading external codegen -# backends and that this external codegen backend is only included in the dep info if -# -Zbinary-dep-depinfo is used. - -all: - $(RUSTC) the_backend.rs --crate-name the_backend --crate-type dylib \ - -o $(TMPDIR)/the_backend.dylib - - $(RUSTC) some_crate.rs --crate-name some_crate --crate-type lib -o $(TMPDIR)/some_crate \ - -Z codegen-backend=$(TMPDIR)/the_backend.dylib -Z unstable-options \ - --emit link,dep-info - grep -x "This has been \"compiled\" successfully." $(TMPDIR)/libsome_crate.rlib - # don't declare a dependency on the codegen backend if -Zbinary-dep-depinfo isn't used. - grep -v "the_backend.dylib" $(TMPDIR)/some_crate.d - - $(RUSTC) some_crate.rs --crate-name some_crate --crate-type lib -o $(TMPDIR)/some_crate \ - -Z codegen-backend=$(TMPDIR)/the_backend.dylib -Z unstable-options \ - --emit link,dep-info -Zbinary-dep-depinfo - grep -x "This has been \"compiled\" successfully." $(TMPDIR)/libsome_crate.rlib - # but declare a dependency on the codegen backend if -Zbinary-dep-depinfo it used. - grep "the_backend.dylib" $(TMPDIR)/some_crate.d diff --git a/tests/run-make-fulldeps/hotplug_codegen_backend/some_crate.rs b/tests/run-make-fulldeps/hotplug_codegen_backend/some_crate.rs deleted file mode 100644 index da27b7f3463d..000000000000 --- a/tests/run-make-fulldeps/hotplug_codegen_backend/some_crate.rs +++ /dev/null @@ -1,2 +0,0 @@ -#![feature(no_core)] -#![no_core] diff --git a/tests/run-make-fulldeps/issue-19371/Makefile b/tests/run-make-fulldeps/issue-19371/Makefile deleted file mode 100644 index edec68f08623..000000000000 --- a/tests/run-make-fulldeps/issue-19371/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -include ../../run-make/tools.mk - -# This test ensures that rustc compile_input can be called twice in one task -# without causing a panic. -# The program needs the path to rustc to get sysroot. - -all: - $(RUSTC) foo.rs - $(call RUN,foo $(TMPDIR) $(RUSTC)) diff --git a/tests/run-make-fulldeps/obtain-borrowck/Makefile b/tests/run-make-fulldeps/obtain-borrowck/Makefile deleted file mode 100644 index 233f5c9eaf49..000000000000 --- a/tests/run-make-fulldeps/obtain-borrowck/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -include ../../run-make/tools.mk - -# This example shows how to implement a rustc driver that retrieves MIR bodies -# together with the borrow checker information. - -# How to run this -# $ ./x.py test tests/run-make-fulldeps/obtain-borrowck - -DRIVER_BINARY := "$(TMPDIR)"/driver -SYSROOT := $(shell $(RUSTC) --print sysroot) - -ifdef IS_WINDOWS -LIBSTD := -L "$(SYSROOT)\\lib\\rustlib\\$(TARGET)\\lib" -else -LIBSTD := -endif - -all: - $(RUSTC) driver.rs -o "$(DRIVER_BINARY)" - $(TARGET_RPATH_ENV) "$(DRIVER_BINARY)" --sysroot $(SYSROOT) $(LIBSTD) test.rs -o "$(TMPDIR)/driver_test" > "$(TMPDIR)"/output.stdout - -ifdef RUSTC_BLESS_TEST - cp "$(TMPDIR)"/output.stdout output.stdout -else - $(DIFF) output.stdout "$(TMPDIR)"/output.stdout -endif diff --git a/tests/run-make-fulldeps/pretty-expanded/Makefile b/tests/run-make-fulldeps/pretty-expanded/Makefile deleted file mode 100644 index 48199179ecef..000000000000 --- a/tests/run-make-fulldeps/pretty-expanded/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -include ../../run-make/tools.mk - -all: - $(RUSTC) -o $(TMPDIR)/input.expanded.rs -Zunpretty=expanded input.rs diff --git a/tests/run-make-fulldeps/pretty-expanded/input.rs b/tests/run-make-fulldeps/pretty-expanded/input.rs deleted file mode 100644 index 02b235068a1d..000000000000 --- a/tests/run-make-fulldeps/pretty-expanded/input.rs +++ /dev/null @@ -1,8 +0,0 @@ -// #13544 - -#[derive(Debug)] pub struct A; -#[derive(Debug)] pub struct B(isize); -#[derive(Debug)] pub struct C { x: isize } -#[derive(Debug)] pub enum D {} -#[derive(Debug)] pub enum E { y } -#[derive(Debug)] pub enum F { z(isize) } diff --git a/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs b/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs index 1bdb63475712..d937514c2ca2 100644 --- a/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs +++ b/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs @@ -5,19 +5,14 @@ use std::path::PathBuf; -use run_make_support::{aux_build, rustc}; +use run_make_support::{aux_build, fs_wrapper, rustc, source_root}; fn main() { aux_build().input("stable.rs").emit("metadata").run(); - let mut stable_path = PathBuf::from(env!("TMPDIR")); - stable_path.push("libstable.rmeta"); - let output = - rustc().input("main.rs").emit("metadata").extern_("stable", &stable_path).command_output(); - - let stderr = String::from_utf8_lossy(&output.stderr); - let version = include_str!(concat!(env!("S"), "/src/version")); + rustc().input("main.rs").emit("metadata").extern_("stable", "libstable.rmeta").run(); + let version = fs_wrapper::read_to_string(source_root().join("src/version")); let expected_string = format!("stable since {}", version.trim()); - assert!(stderr.contains(&expected_string)); + output.assert_stderr_contains(expected_string); } diff --git a/tests/run-make/README.md b/tests/run-make/README.md new file mode 100644 index 000000000000..a6c1b4b7db76 --- /dev/null +++ b/tests/run-make/README.md @@ -0,0 +1,43 @@ +# The `run-make` test suite + +The `run-make` test suite contains tests which are the most flexible out of all +the [rust-lang/rust](https://github.com/rust-lang/rust) test suites. `run-make` +tests can basically contain arbitrary code, and are supported by the +[`run_make_support`] library. + +## Infrastructure + +There are two kinds of run-make tests: + +1. The new `rmake.rs` version: this allows run-make tests to be written in Rust + (with `rmake.rs` as the main test file). +2. The legacy `Makefile` version: this is what run-make tests were written with + before support for `rmake.rs` was introduced. + +The implementation for collecting and building the `rmake.rs` recipes (or +`Makefile`s) are in +[`src/tools/compiletest/src/runtest.rs`](../../src/tools/compiletest/src/runtest.rs), +in `run_rmake_v2_test` and `run_rmake_legacy_test`. + +### Rust-based `run-make` tests: `rmake.rs` + +The setup for the `rmake.rs` version is a 3-stage process: + +1. First, we build the [`run_make_support`] library in bootstrap as a tool lib. +2. Then, we compile the `rmake.rs` "recipe" linking the support library and its + dependencies in, and provide a bunch of env vars. We setup a directory + structure within `build//test/run-make/` + + ``` + / + rmake.exe # recipe binary + rmake_out/ # sources from test sources copied over + ``` + + and copy non-`rmake.rs` input support files over to `rmake_out/`. The + support library is made available as an [*extern prelude*][extern_prelude]. +3. Finally, we run the recipe binary and set `rmake_out/` as the working + directory. + +[`run_make_support`]: ../../src/tools/run-make-support +[extern_prelude]: https://doc.rust-lang.org/reference/names/preludes.html#extern-prelude diff --git a/tests/run-make/a-b-a-linker-guard/a.rs b/tests/run-make/a-b-a-linker-guard/a.rs index aa07b1e71607..22686760f1aa 100644 --- a/tests/run-make/a-b-a-linker-guard/a.rs +++ b/tests/run-make/a-b-a-linker-guard/a.rs @@ -2,7 +2,7 @@ #![crate_type = "dylib"] #[cfg(x)] -pub fn foo(x: u32) { } +pub fn foo(x: u32) {} #[cfg(y)] -pub fn foo(x: i32) { } +pub fn foo(x: i32) {} diff --git a/tests/run-make/alloc-no-oom-handling/rmake.rs b/tests/run-make/alloc-no-oom-handling/rmake.rs index fec3c6532940..89a6636d9a0c 100644 --- a/tests/run-make/alloc-no-oom-handling/rmake.rs +++ b/tests/run-make/alloc-no-oom-handling/rmake.rs @@ -1,15 +1,15 @@ -// This test checks that alloc can still compile correctly +// This test checks that alloc can still compile successfully // when the unstable no_global_oom_handling feature is turned on. // See https://github.com/rust-lang/rust/pull/84266 -use run_make_support::rustc; +use run_make_support::{rustc, source_root}; fn main() { rustc() .edition("2021") .arg("-Dwarnings") .crate_type("rlib") - .input("../../../library/alloc/src/lib.rs") + .input(source_root().join("library/alloc/src/lib.rs")) .cfg("no_global_oom_handling") .run(); } diff --git a/tests/run-make/alloc-no-rc/rmake.rs b/tests/run-make/alloc-no-rc/rmake.rs index c5744a3f5eef..12171c2148f1 100644 --- a/tests/run-make/alloc-no-rc/rmake.rs +++ b/tests/run-make/alloc-no-rc/rmake.rs @@ -1,15 +1,15 @@ -// This test checks that alloc can still compile correctly +// This test checks that alloc can still compile successfully // when the unstable no_rc feature is turned on. // See https://github.com/rust-lang/rust/pull/84266 -use run_make_support::rustc; +use run_make_support::{rustc, source_root}; fn main() { rustc() .edition("2021") .arg("-Dwarnings") .crate_type("rlib") - .input("../../../library/alloc/src/lib.rs") + .input(source_root().join("library/alloc/src/lib.rs")) .cfg("no_rc") .run(); } diff --git a/tests/run-make/alloc-no-sync/rmake.rs b/tests/run-make/alloc-no-sync/rmake.rs index 6410eca80abf..29f204f30673 100644 --- a/tests/run-make/alloc-no-sync/rmake.rs +++ b/tests/run-make/alloc-no-sync/rmake.rs @@ -1,15 +1,15 @@ -// This test checks that alloc can still compile correctly +// This test checks that alloc can still compile successfully // when the unstable no_sync feature is turned on. // See https://github.com/rust-lang/rust/pull/84266 -use run_make_support::rustc; +use run_make_support::{rustc, source_root}; fn main() { rustc() .edition("2021") .arg("-Dwarnings") .crate_type("rlib") - .input("../../../library/alloc/src/lib.rs") + .input(source_root().join("library/alloc/src/lib.rs")) .cfg("no_sync") .run(); } diff --git a/tests/run-make/allocator-shim-circular-deps/Makefile b/tests/run-make/allocator-shim-circular-deps/Makefile deleted file mode 100644 index f667e2e2ec29..000000000000 --- a/tests/run-make/allocator-shim-circular-deps/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# This test is designed to intentionally introduce a circular dependency scenario to check that a specific compiler bug doesn't make a resurgence. -# The bug in question arose when at least one crate required a global allocator, and that crate was placed after the one defining it in the linker order. -# The generated symbols.o should not result in any linker errors. -# See https://github.com/rust-lang/rust/issues/112715 - -# ignore-cross-compile -include ../tools.mk - -all: - rm -rf $(TMPDIR) && mkdir $(TMPDIR) - $(RUSTC) my_lib.rs - $(RUSTC) main.rs --test --extern my_lib=$(TMPDIR)/libmy_lib.rlib diff --git a/tests/run-make/allocator-shim-circular-deps/rmake.rs b/tests/run-make/allocator-shim-circular-deps/rmake.rs new file mode 100644 index 000000000000..7d6b0bd204a1 --- /dev/null +++ b/tests/run-make/allocator-shim-circular-deps/rmake.rs @@ -0,0 +1,16 @@ +// This test is designed to intentionally introduce a circular dependency scenario to check +// that a specific compiler bug doesn't make a resurgence. +// The bug in question arose when at least one crate +// required a global allocator, and that crate was placed after +// the one defining it in the linker order. +// The generated symbols.o should not result in any linker errors. +// See https://github.com/rust-lang/rust/issues/112715 + +//@ ignore-cross-compile + +use run_make_support::{rust_lib_name, rustc}; + +fn main() { + rustc().input("my_lib.rs").run(); + rustc().input("main.rs").arg("--test").extern_("my_lib", rust_lib_name("my_lib")).run(); +} diff --git a/tests/run-make/allow-non-lint-warnings-cmdline/Makefile b/tests/run-make/allow-non-lint-warnings-cmdline/Makefile deleted file mode 100644 index 78b9a7b98989..000000000000 --- a/tests/run-make/allow-non-lint-warnings-cmdline/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# Test that -A warnings makes the 'empty trait list for derive' warning go away -OUT=$(shell $(RUSTC) foo.rs -A warnings 2>&1 | grep "warning" ) - -all: foo - test -z '$(OUT)' - -# This is just to make sure the above command actually succeeds -foo: - $(RUSTC) foo.rs -A warnings diff --git a/tests/run-make/allow-non-lint-warnings-cmdline/foo.rs b/tests/run-make/allow-non-lint-warnings-cmdline/foo.rs deleted file mode 100644 index 46e72da2de9f..000000000000 --- a/tests/run-make/allow-non-lint-warnings-cmdline/foo.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive()] -#[derive(Copy, Clone)] -pub struct Foo; - -pub fn main() { } diff --git a/tests/run-make/allow-warnings-cmdline-stability/Makefile b/tests/run-make/allow-warnings-cmdline-stability/Makefile deleted file mode 100644 index 368a39af6bfe..000000000000 --- a/tests/run-make/allow-warnings-cmdline-stability/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# Test that -A warnings makes the 'empty trait list for derive' warning go away -DEP=$(shell $(RUSTC) bar.rs) -OUT=$(shell $(RUSTC) foo.rs -A warnings 2>&1 | grep "warning" ) - -all: foo bar - test -z '$(OUT)' - -# These are just to ensure that the above commands actually work -bar: - $(RUSTC) bar.rs - -foo: bar - $(RUSTC) foo.rs -A warnings diff --git a/tests/run-make/allow-warnings-cmdline-stability/foo.rs b/tests/run-make/allow-warnings-cmdline-stability/foo.rs index 869b54354168..0b91b63c1185 100644 --- a/tests/run-make/allow-warnings-cmdline-stability/foo.rs +++ b/tests/run-make/allow-warnings-cmdline-stability/foo.rs @@ -2,4 +2,6 @@ extern crate bar; -pub fn main() { bar::baz() } +pub fn main() { + bar::baz() +} diff --git a/tests/run-make/allow-warnings-cmdline-stability/rmake.rs b/tests/run-make/allow-warnings-cmdline-stability/rmake.rs new file mode 100644 index 000000000000..22a31266176b --- /dev/null +++ b/tests/run-make/allow-warnings-cmdline-stability/rmake.rs @@ -0,0 +1,13 @@ +// Test that `-Awarnings` suppresses warnings for unstable APIs. + +use run_make_support::rustc; + +fn main() { + rustc().input("bar.rs").run(); + rustc() + .input("foo.rs") + .arg("-Awarnings") + .run() + .assert_stdout_not_contains("warning") + .assert_stderr_not_contains("warning"); +} diff --git a/tests/run-make/arguments-non-c-like-enum/rmake.rs b/tests/run-make/arguments-non-c-like-enum/rmake.rs index 13230206ca87..88f4d664aa62 100644 --- a/tests/run-make/arguments-non-c-like-enum/rmake.rs +++ b/tests/run-make/arguments-non-c-like-enum/rmake.rs @@ -1,14 +1,14 @@ //! Check that non-trivial `repr(C)` enum in Rust has valid C layout. //@ ignore-cross-compile -use run_make_support::{cc, extra_c_flags, extra_cxx_flags, run, rustc, static_lib}; +use run_make_support::{cc, extra_c_flags, extra_cxx_flags, run, rustc, static_lib_name}; pub fn main() { use std::path::Path; rustc().input("nonclike.rs").crate_type("staticlib").run(); cc().input("test.c") - .input(static_lib("nonclike")) + .input(static_lib_name("nonclike")) .out_exe("test") .args(&extra_c_flags()) .args(&extra_cxx_flags()) diff --git a/tests/run-make/artifact-incr-cache-no-obj/rmake.rs b/tests/run-make/artifact-incr-cache-no-obj/rmake.rs index 6613698ae1df..d5bc46dff470 100644 --- a/tests/run-make/artifact-incr-cache-no-obj/rmake.rs +++ b/tests/run-make/artifact-incr-cache-no-obj/rmake.rs @@ -5,17 +5,15 @@ // // Fixes: rust-lang/rust#123234 -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn main() { - let inc_dir = tmp_dir(); - for _ in 0..=1 { rustc() .input("lib.rs") .crate_type("lib") .emit("asm,dep-info,link,mir,llvm-ir,llvm-bc") - .incremental(&inc_dir) + .incremental("incremental") .run(); } } diff --git a/tests/run-make/artifact-incr-cache/rmake.rs b/tests/run-make/artifact-incr-cache/rmake.rs index 106f363eb8da..b4b63313cfc4 100644 --- a/tests/run-make/artifact-incr-cache/rmake.rs +++ b/tests/run-make/artifact-incr-cache/rmake.rs @@ -7,17 +7,15 @@ // Also see discussion at // -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn main() { - let inc_dir = tmp_dir(); - for _ in 0..=1 { rustc() .input("lib.rs") .crate_type("lib") .emit("obj,asm,dep-info,link,mir,llvm-ir,llvm-bc") - .incremental(&inc_dir) + .incremental("incremental") .run(); } } diff --git a/tests/run-make/atomic-lock-free/atomic_lock_free.rs b/tests/run-make/atomic-lock-free/atomic_lock_free.rs index 47d90b1856be..1f1116b9bfd1 100644 --- a/tests/run-make/atomic-lock-free/atomic_lock_free.rs +++ b/tests/run-make/atomic-lock-free/atomic_lock_free.rs @@ -1,5 +1,5 @@ #![feature(no_core, intrinsics, lang_items)] -#![crate_type="rlib"] +#![crate_type = "rlib"] #![no_core] extern "rust-intrinsic" { diff --git a/tests/run-make/bare-outfile/Makefile b/tests/run-make/bare-outfile/Makefile deleted file mode 100644 index ad6fe4bd167c..000000000000 --- a/tests/run-make/bare-outfile/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# This test checks that manually setting the output file as a bare file with no file extension still results in successful compilation. - -# ignore-cross-compile -include ../tools.mk - -all: - cp foo.rs $(TMPDIR) - cd $(TMPDIR) && $(RUSTC) -o foo foo.rs - $(call RUN,foo) diff --git a/tests/run-make/bare-outfile/foo.rs b/tests/run-make/bare-outfile/foo.rs index f79c691f0853..f328e4d9d04c 100644 --- a/tests/run-make/bare-outfile/foo.rs +++ b/tests/run-make/bare-outfile/foo.rs @@ -1,2 +1 @@ -fn main() { -} +fn main() {} diff --git a/tests/run-make/bare-outfile/rmake.rs b/tests/run-make/bare-outfile/rmake.rs new file mode 100644 index 000000000000..badf1396b73e --- /dev/null +++ b/tests/run-make/bare-outfile/rmake.rs @@ -0,0 +1,11 @@ +// This test checks that manually setting the output file as a bare file with no file extension +// still results in successful compilation. + +//@ ignore-cross-compile + +use run_make_support::{run, rustc}; + +fn main() { + rustc().output("foo").input("foo.rs").run(); + run("foo"); +} diff --git a/tests/run-make/issue-51671/app.rs b/tests/run-make/bin-emit-no-symbols/app.rs similarity index 100% rename from tests/run-make/issue-51671/app.rs rename to tests/run-make/bin-emit-no-symbols/app.rs diff --git a/tests/run-make/bin-emit-no-symbols/rmake.rs b/tests/run-make/bin-emit-no-symbols/rmake.rs new file mode 100644 index 000000000000..5586e53c0508 --- /dev/null +++ b/tests/run-make/bin-emit-no-symbols/rmake.rs @@ -0,0 +1,16 @@ +// When setting the crate type as a "bin" (in app.rs), +// this could cause a bug where some symbols would not be +// emitted in the object files. This has been fixed, and +// this test checks that the correct symbols have been successfully +// emitted inside the object files. +// See https://github.com/rust-lang/rust/issues/51671 + +use run_make_support::{llvm_readobj, rustc}; + +fn main() { + rustc().emit("obj").input("app.rs").run(); + let out = llvm_readobj().input("app.o").arg("--symbols").run(); + out.assert_stdout_contains("rust_begin_unwind"); + out.assert_stdout_contains("rust_eh_personality"); + out.assert_stdout_contains("__rg_oom"); +} diff --git a/tests/run-make/box-struct-no-segfault/foo.rs b/tests/run-make/box-struct-no-segfault/foo.rs index 1dcabe42dc11..0897b74b3567 100644 --- a/tests/run-make/box-struct-no-segfault/foo.rs +++ b/tests/run-make/box-struct-no-segfault/foo.rs @@ -1,8 +1,8 @@ -#![crate_type="lib"] +#![crate_type = "lib"] pub struct Foo(()); impl Foo { - pub fn new() -> Foo { - Foo(()) - } + pub fn new() -> Foo { + Foo(()) + } } diff --git a/tests/run-make/box-struct-no-segfault/main.rs b/tests/run-make/box-struct-no-segfault/main.rs index de12b1fd9dc3..1a456af48e8d 100644 --- a/tests/run-make/box-struct-no-segfault/main.rs +++ b/tests/run-make/box-struct-no-segfault/main.rs @@ -1,7 +1,7 @@ -#![crate_type="lib"] +#![crate_type = "lib"] extern crate foo; use foo::Foo; pub fn crash() -> Box { - Box::new(Foo::new()) + Box::new(Foo::new()) } diff --git a/tests/run-make/box-struct-no-segfault/rmake.rs b/tests/run-make/box-struct-no-segfault/rmake.rs index 5406f765e6c5..1bbefd03541a 100644 --- a/tests/run-make/box-struct-no-segfault/rmake.rs +++ b/tests/run-make/box-struct-no-segfault/rmake.rs @@ -5,9 +5,9 @@ // This test checks that this bug does not resurface. // See https://github.com/rust-lang/rust/issues/28766 -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn main() { rustc().opt().input("foo.rs").run(); - rustc().opt().library_search_path(tmp_dir()).input("main.rs").run(); + rustc().opt().input("main.rs").run(); } diff --git a/tests/run-make/c-dynamic-dylib/Makefile b/tests/run-make/c-dynamic-dylib/Makefile index 0d4096404867..39561b28222c 100644 --- a/tests/run-make/c-dynamic-dylib/Makefile +++ b/tests/run-make/c-dynamic-dylib/Makefile @@ -4,7 +4,7 @@ # ignore-cross-compile include ../tools.mk -# ignore-macos +# ignore-apple # # This hits an assertion in the linker on older versions of osx apparently diff --git a/tests/run-make/c-dynamic-rlib/Makefile b/tests/run-make/c-dynamic-rlib/Makefile index a64e89cc0dc4..7b05e3d91a09 100644 --- a/tests/run-make/c-dynamic-rlib/Makefile +++ b/tests/run-make/c-dynamic-rlib/Makefile @@ -4,7 +4,7 @@ # ignore-cross-compile include ../tools.mk -# ignore-macos +# ignore-apple # # This hits an assertion in the linker on older versions of osx apparently diff --git a/tests/run-make/c-link-to-rust-dylib/Makefile b/tests/run-make/c-link-to-rust-dylib/Makefile deleted file mode 100644 index 201f717ece49..000000000000 --- a/tests/run-make/c-link-to-rust-dylib/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# This test checks that C linking with Rust does not encounter any errors, with dynamic libraries. -# See https://github.com/rust-lang/rust/issues/10434 - -# ignore-cross-compile -include ../tools.mk - -all: $(TMPDIR)/$(call BIN,bar) - $(call RUN,bar) - $(call REMOVE_DYLIBS,foo) - $(call FAIL,bar) - -ifdef IS_MSVC -$(TMPDIR)/$(call BIN,bar): $(call DYLIB,foo) - $(CC) bar.c $(TMPDIR)/foo.dll.lib $(call OUT_EXE,bar) -else -$(TMPDIR)/$(call BIN,bar): $(call DYLIB,foo) - $(CC) bar.c -lfoo -o $(call RUN_BINFILE,bar) -L $(TMPDIR) -endif - -$(call DYLIB,foo): foo.rs - $(RUSTC) foo.rs diff --git a/tests/run-make/c-link-to-rust-dylib/rmake.rs b/tests/run-make/c-link-to-rust-dylib/rmake.rs new file mode 100644 index 000000000000..b8ea0b6b345b --- /dev/null +++ b/tests/run-make/c-link-to-rust-dylib/rmake.rs @@ -0,0 +1,35 @@ +// This test checks that C linking with Rust does not encounter any errors, with dynamic libraries. +// See . + +//@ ignore-cross-compile + +use run_make_support::{ + cc, cwd, dynamic_lib_extension, fs_wrapper, is_msvc, read_dir, run, run_fail, rustc, +}; + +fn main() { + rustc().input("foo.rs").run(); + + if is_msvc() { + let lib = "foo.dll.lib"; + + cc().input("bar.c").arg(lib).out_exe("bar").run(); + } else { + cc().input("bar.c").arg("-lfoo").output("bar").library_search_path(cwd()).run(); + } + + run("bar"); + + let expected_extension = dynamic_lib_extension(); + read_dir(cwd(), |path| { + if path.is_file() + && path.extension().is_some_and(|ext| ext == expected_extension) + && path.file_name().and_then(|name| name.to_str()).is_some_and(|name| { + name.ends_with(".so") || name.ends_with(".dll") || name.ends_with(".dylib") + }) + { + fs_wrapper::remove_file(path); + } + }); + run_fail("bar"); +} diff --git a/tests/run-make/c-link-to-rust-staticlib/rmake.rs b/tests/run-make/c-link-to-rust-staticlib/rmake.rs index 63d5eb78c698..2edd36b9ec0b 100644 --- a/tests/run-make/c-link-to-rust-staticlib/rmake.rs +++ b/tests/run-make/c-link-to-rust-staticlib/rmake.rs @@ -3,13 +3,14 @@ //@ ignore-cross-compile -use run_make_support::{cc, extra_c_flags, run, rustc, static_lib}; +use run_make_support::fs_wrapper::remove_file; +use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name}; use std::fs; fn main() { rustc().input("foo.rs").run(); - cc().input("bar.c").input(static_lib("foo")).out_exe("bar").args(&extra_c_flags()).run(); + cc().input("bar.c").input(static_lib_name("foo")).out_exe("bar").args(&extra_c_flags()).run(); run("bar"); - fs::remove_file(static_lib("foo")); + remove_file(static_lib_name("foo")); run("bar"); } diff --git a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs index 5830ef033d38..196c6440747a 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs @@ -1,19 +1,16 @@ #![crate_type = "staticlib"] #![feature(c_variadic)] -#![feature(rustc_private)] -extern crate libc; - -use libc::{c_char, c_double, c_int, c_long, c_longlong}; use std::ffi::VaList; -use std::ffi::{CString, CStr}; +use std::ffi::{c_char, c_double, c_int, c_long, c_longlong}; +use std::ffi::{CStr, CString}; macro_rules! continue_if { ($cond:expr) => { if !($cond) { return 0xff; } - } + }; } unsafe fn compare_c_str(ptr: *const c_char, val: &str) -> bool { @@ -62,13 +59,11 @@ pub unsafe extern "C" fn check_list_copy_0(mut ap: VaList) -> usize { continue_if!(ap.arg::() == 16); continue_if!(ap.arg::() == 'A' as c_char); continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Skip Me!")); - ap.with_copy(|mut ap| { - if compare_c_str(ap.arg::<*const c_char>(), "Correct") { - 0 - } else { - 0xff - } - }) + ap.with_copy( + |mut ap| { + if compare_c_str(ap.arg::<*const c_char>(), "Correct") { 0 } else { 0xff } + }, + ) } #[no_mangle] diff --git a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs index 7a450efff942..a01e259bce01 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs @@ -5,12 +5,12 @@ //@ ignore-cross-compile -use run_make_support::{cc, extra_c_flags, run, rustc, static_lib}; +use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name}; fn main() { rustc().input("checkrust.rs").run(); cc().input("test.c") - .input(static_lib("checkrust")) + .input(static_lib_name("checkrust")) .out_exe("test") .args(&extra_c_flags()) .run(); diff --git a/tests/run-make/cdylib/Makefile b/tests/run-make/cdylib/Makefile deleted file mode 100644 index 2c6414c32553..000000000000 --- a/tests/run-make/cdylib/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# When the cdylib crate type was added as a variation of dylib, it needed a test to check its function. -# See https://github.com/rust-lang/rust/pull/33553 - -# ignore-cross-compile -include ../tools.mk - -all: $(call RUN_BINFILE,foo) - $(call RUN,foo) - rm $(call DYLIB,foo) - $(RUSTC) foo.rs -C lto - $(call RUN,foo) - -ifdef IS_MSVC -$(call RUN_BINFILE,foo): $(call DYLIB,foo) - $(CC) $(CFLAGS) foo.c $(TMPDIR)/foo.dll.lib $(call OUT_EXE,foo) -else -$(call RUN_BINFILE,foo): $(call DYLIB,foo) - $(CC) $(CFLAGS) foo.c -lfoo -o $(call RUN_BINFILE,foo) -L $(TMPDIR) -endif - -$(call DYLIB,foo): - $(RUSTC) bar.rs - $(RUSTC) foo.rs diff --git a/tests/run-make/cdylib/rmake.rs b/tests/run-make/cdylib/rmake.rs new file mode 100644 index 000000000000..55ea227ab519 --- /dev/null +++ b/tests/run-make/cdylib/rmake.rs @@ -0,0 +1,30 @@ +// This test tries to check that basic cdylib libraries can be compiled and linked successfully +// with C code, that the cdylib itself can depend on another rlib, and that the library can be built +// with LTO. +// +// - `bar.rs` is a rlib +// - `foo.rs` is a cdylib that relies on an extern crate `bar` and defines two `extern "C"` +// functions: +// - `foo()` which calls `bar::bar()`. +// - `bar()` which implements basic addition. + +//@ ignore-cross-compile + +use run_make_support::{cc, cwd, dynamic_lib_name, fs_wrapper, is_msvc, run, rustc}; + +fn main() { + rustc().input("bar.rs").run(); + rustc().input("foo.rs").run(); + + if is_msvc() { + cc().input("foo.c").arg("foo.dll.lib").out_exe("foo").run(); + } else { + cc().input("foo.c").arg("-lfoo").library_search_path(cwd()).output("foo").run(); + } + + run("foo"); + fs_wrapper::remove_file(dynamic_lib_name("foo")); + + rustc().input("foo.rs").arg("-Clto").run(); + run("foo"); +} diff --git a/tests/run-make/issue-26092/blank.rs b/tests/run-make/clear-error-blank-output/blank.rs similarity index 100% rename from tests/run-make/issue-26092/blank.rs rename to tests/run-make/clear-error-blank-output/blank.rs diff --git a/tests/run-make/clear-error-blank-output/rmake.rs b/tests/run-make/clear-error-blank-output/rmake.rs new file mode 100644 index 000000000000..e0f042cf805b --- /dev/null +++ b/tests/run-make/clear-error-blank-output/rmake.rs @@ -0,0 +1,13 @@ +// When an empty output file is passed to rustc, the ensuing error message +// should be clear. However, calling file_stem on an empty path returns None, +// which, when unwrapped, causes a panic, stopping execution of rustc +// and printing an obscure message instead of reaching the helpful +// error message. This test checks that the panic does not occur. +// See https://github.com/rust-lang/rust/pull/26199 + +use run_make_support::rustc; + +fn main() { + let output = rustc().output("").stdin(b"fn main() {}").run_fail(); + output.assert_stderr_not_contains("panic"); +} diff --git a/tests/run-make/codegen-options-parsing/Makefile b/tests/run-make/codegen-options-parsing/Makefile deleted file mode 100644 index beaf233502bb..000000000000 --- a/tests/run-make/codegen-options-parsing/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -# This test intentionally feeds invalid inputs to codegen and checks if the error message outputs contain specific helpful indications. - -# ignore-cross-compile -include ../tools.mk - -all: - #Option taking a number - $(RUSTC) -C codegen-units dummy.rs 2>&1 | \ - $(CGREP) 'codegen option `codegen-units` requires a number' - $(RUSTC) -C codegen-units= dummy.rs 2>&1 | \ - $(CGREP) 'incorrect value `` for codegen option `codegen-units` - a number was expected' - $(RUSTC) -C codegen-units=foo dummy.rs 2>&1 | \ - $(CGREP) 'incorrect value `foo` for codegen option `codegen-units` - a number was expected' - $(RUSTC) -C codegen-units=1 dummy.rs - #Option taking a string - $(RUSTC) -C extra-filename dummy.rs 2>&1 | \ - $(CGREP) 'codegen option `extra-filename` requires a string' - $(RUSTC) -C extra-filename= dummy.rs 2>&1 - $(RUSTC) -C extra-filename=foo dummy.rs 2>&1 - #Option taking no argument - $(RUSTC) -C lto= dummy.rs 2>&1 | \ - $(CGREP) 'codegen option `lto` - either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, `fat`, or omitted' - $(RUSTC) -C lto=1 dummy.rs 2>&1 | \ - $(CGREP) 'codegen option `lto` - either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, `fat`, or omitted' - $(RUSTC) -C lto=foo dummy.rs 2>&1 | \ - $(CGREP) 'codegen option `lto` - either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, `fat`, or omitted' - $(RUSTC) -C lto dummy.rs - - # Should not link dead code... - $(RUSTC) --print link-args dummy.rs 2>&1 | \ - $(CGREP) -e '--gc-sections|-z[^ ]* [^ ]*|-dead_strip|/OPT:REF' - # ... unless you specifically ask to keep it - $(RUSTC) --print link-args -C link-dead-code dummy.rs 2>&1 | \ - $(CGREP) -ve '--gc-sections|-z[^ ]* [^ ]*|-dead_strip|/OPT:REF' diff --git a/tests/run-make/codegen-options-parsing/rmake.rs b/tests/run-make/codegen-options-parsing/rmake.rs new file mode 100644 index 000000000000..c78b41a88dc6 --- /dev/null +++ b/tests/run-make/codegen-options-parsing/rmake.rs @@ -0,0 +1,56 @@ +// This test intentionally feeds invalid inputs to codegen and checks if the error message outputs +// contain specific helpful indications. + +//@ ignore-cross-compile + +use run_make_support::regex::Regex; +use run_make_support::rustc; + +fn main() { + // Option taking a number. + rustc() + .input("dummy.rs") + .arg("-Ccodegen-units") + .run_fail() + .assert_stderr_contains("codegen option `codegen-units` requires a number"); + rustc().input("dummy.rs").arg("-Ccodegen-units=").run_fail().assert_stderr_contains( + "incorrect value `` for codegen option `codegen-units` - a number was expected", + ); + rustc().input("dummy.rs").arg("-Ccodegen-units=foo").run_fail().assert_stderr_contains( + "incorrect value `foo` for codegen option `codegen-units` - a number was expected", + ); + rustc().input("dummy.rs").arg("-Ccodegen-units=1").run(); + + // Option taking a string. + rustc() + .input("dummy.rs") + .arg("-Cextra-filename") + .run_fail() + .assert_stderr_contains("codegen option `extra-filename` requires a string"); + rustc().input("dummy.rs").arg("-Cextra-filename=").run(); + rustc().input("dummy.rs").arg("-Cextra-filename=foo").run(); + + // Option taking no argument. + rustc().input("dummy.rs").arg("-Clto=").run_fail().assert_stderr_contains( + "codegen option `lto` - either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, \ + `fat`, or omitted", + ); + rustc().input("dummy.rs").arg("-Clto=1").run_fail().assert_stderr_contains( + "codegen option `lto` - either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, \ + `fat`, or omitted", + ); + rustc().input("dummy.rs").arg("-Clto=foo").run_fail().assert_stderr_contains( + "codegen option `lto` - either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, \ + `fat`, or omitted", + ); + rustc().input("dummy.rs").arg("-Clto").run(); + + let regex = Regex::new("--gc-sections|-z[^ ]* [^ ]*|-dead_strip|/OPT:REF").unwrap(); + // Should not link dead code... + let stdout = rustc().input("dummy.rs").print("link-args").run().stdout_utf8(); + assert!(regex.is_match(&stdout)); + // ... unless you specifically ask to keep it + let stdout = + rustc().input("dummy.rs").print("link-args").arg("-Clink-dead-code").run().stdout_utf8(); + assert!(!regex.is_match(&stdout)); +} diff --git a/tests/run-make/compile-stdin/Makefile b/tests/run-make/compile-stdin/Makefile deleted file mode 100644 index b3d7cc777a01..000000000000 --- a/tests/run-make/compile-stdin/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# When provided standard input piped directly into rustc, this test checks that the compilation completes successfully and that the output can be executed. -# See https://github.com/rust-lang/rust/pull/28805 - -# ignore-cross-compile -include ../tools.mk - -all: - echo 'fn main(){}' | $(RUSTC) - - $(call RUN,rust_out) diff --git a/tests/run-make/compile-stdin/rmake.rs b/tests/run-make/compile-stdin/rmake.rs new file mode 100644 index 000000000000..f93080dfdc4f --- /dev/null +++ b/tests/run-make/compile-stdin/rmake.rs @@ -0,0 +1,13 @@ +// When provided standard input piped directly into rustc, this test checks that the compilation +// completes successfully and that the output can be executed. +// +// See . + +//@ ignore-cross-compile + +use run_make_support::{run, rustc}; + +fn main() { + rustc().arg("-").stdin("fn main() {}").run(); + run("rust_out"); +} diff --git a/tests/run-make/compiler-builtins/Cargo.toml b/tests/run-make/compiler-builtins/Cargo.toml new file mode 100644 index 000000000000..869210c4a683 --- /dev/null +++ b/tests/run-make/compiler-builtins/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "scratch" +version = "0.1.0" +edition = "2021" + +[lib] +path = "lib.rs" diff --git a/tests/run-make/compiler-builtins/lib.rs b/tests/run-make/compiler-builtins/lib.rs new file mode 100644 index 000000000000..0c9ac1ac8e4b --- /dev/null +++ b/tests/run-make/compiler-builtins/lib.rs @@ -0,0 +1 @@ +#![no_std] diff --git a/tests/run-make/compiler-builtins/rmake.rs b/tests/run-make/compiler-builtins/rmake.rs index f5da50ebb043..10ff9cd282d8 100644 --- a/tests/run-make/compiler-builtins/rmake.rs +++ b/tests/run-make/compiler-builtins/rmake.rs @@ -14,41 +14,29 @@ #![deny(warnings)] -use run_make_support::object; +use run_make_support::fs_wrapper::{read, read_dir}; use run_make_support::object::read::archive::ArchiveFile; use run_make_support::object::read::Object; use run_make_support::object::ObjectSection; use run_make_support::object::ObjectSymbol; use run_make_support::object::RelocationTarget; -use run_make_support::set_host_rpath; -use run_make_support::tmp_dir; +use run_make_support::{cmd, env_var, object}; use std::collections::HashSet; - -const MANIFEST: &str = r#" -[package] -name = "scratch" -version = "0.1.0" -edition = "2021" - -[lib] -path = "lib.rs""#; +use std::path::PathBuf; fn main() { - let target_dir = tmp_dir().join("target"); - let target = std::env::var("TARGET").unwrap(); + let target_dir = PathBuf::from("target"); + let target = env_var("TARGET"); println!("Testing compiler_builtins for {}", target); - // Set up the tiniest Cargo project: An empty no_std library. Just enough to run -Zbuild-std. - let manifest_path = tmp_dir().join("Cargo.toml"); - std::fs::write(&manifest_path, MANIFEST.as_bytes()).unwrap(); - std::fs::write(tmp_dir().join("lib.rs"), b"#![no_std]").unwrap(); + let manifest_path = PathBuf::from("Cargo.toml"); - let path = std::env::var("PATH").unwrap(); - let rustc = std::env::var("RUSTC").unwrap(); - let bootstrap_cargo = std::env::var("BOOTSTRAP_CARGO").unwrap(); - let mut cmd = std::process::Command::new(bootstrap_cargo); - cmd.args([ + let path = env_var("PATH"); + let rustc = env_var("RUSTC"); + let bootstrap_cargo = env_var("BOOTSTRAP_CARGO"); + let mut cmd = cmd(bootstrap_cargo); + cmd.args(&[ "build", "--manifest-path", manifest_path.to_str().unwrap(), @@ -56,7 +44,6 @@ fn main() { "--target", &target, ]) - .env_clear() .env("PATH", path) .env("RUSTC", rustc) .env("RUSTFLAGS", "-Copt-level=0 -Cdebug-assertions=yes") @@ -65,14 +52,11 @@ fn main() { // Visual Studio 2022 requires that the LIB env var be set so it can // find the Windows SDK. .env("LIB", std::env::var("LIB").unwrap_or_default()); - set_host_rpath(&mut cmd); - let status = cmd.status().unwrap(); - assert!(status.success()); + cmd.run(); let rlibs_path = target_dir.join(target).join("debug").join("deps"); - let compiler_builtins_rlib = std::fs::read_dir(rlibs_path) - .unwrap() + let compiler_builtins_rlib = read_dir(rlibs_path) .find_map(|e| { let path = e.unwrap().path(); let file_name = path.file_name().unwrap().to_str().unwrap(); @@ -86,7 +70,7 @@ fn main() { // rlib files are archives, where the archive members each a CGU, and we also have one called // lib.rmeta which is the encoded metadata. Each of the CGUs is an object file. - let data = std::fs::read(compiler_builtins_rlib).unwrap(); + let data = read(compiler_builtins_rlib); let mut defined_symbols = HashSet::new(); let mut undefined_relocations = HashSet::new(); diff --git a/tests/run-make/compiler-lookup-paths-2/c.rs b/tests/run-make/compiler-lookup-paths-2/c.rs index e37bc2e1dcef..1326edb575ed 100644 --- a/tests/run-make/compiler-lookup-paths-2/c.rs +++ b/tests/run-make/compiler-lookup-paths-2/c.rs @@ -1,3 +1,3 @@ #![crate_type = "lib"] -extern crate b; extern crate a; +extern crate b; diff --git a/tests/run-make/const-prop-lint/Makefile b/tests/run-make/const-prop-lint/Makefile deleted file mode 100644 index f29f282f7876..000000000000 --- a/tests/run-make/const-prop-lint/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -include ../tools.mk - -# Test that emitting an error because of arithmetic -# overflow lint does not leave .o files around -# because of interrupted codegen. - -all: - $(RUSTC) input.rs; test $$? -eq 1 - ls *.o; test $$? -ne 0 diff --git a/tests/run-make/const-prop-lint/rmake.rs b/tests/run-make/const-prop-lint/rmake.rs new file mode 100644 index 000000000000..d194f70d9169 --- /dev/null +++ b/tests/run-make/const-prop-lint/rmake.rs @@ -0,0 +1,16 @@ +// Tests that const prop lints interrupting codegen don't leave `.o` files around. + +use run_make_support::{cwd, fs_wrapper, rustc}; + +fn main() { + rustc().input("input.rs").run_fail().assert_exit_code(1); + + for entry in fs_wrapper::read_dir(cwd()) { + let entry = entry.unwrap(); + let path = entry.path(); + + if path.is_file() && path.extension().is_some_and(|ext| ext == "o") { + panic!("there should not be `.o` files!"); + } + } +} diff --git a/tests/run-make/const_fn_mir/Makefile b/tests/run-make/const_fn_mir/Makefile deleted file mode 100644 index 3399446130d7..000000000000 --- a/tests/run-make/const_fn_mir/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# needs-unwind -Cpanic=abort gives different MIR output -include ../tools.mk - -all: - $(RUSTC) main.rs --emit=mir -o "$(TMPDIR)"/dump.mir - $(RUSTC_TEST_OP) "$(TMPDIR)"/dump.mir dump.mir diff --git a/tests/run-make/const_fn_mir/dump.mir b/tests/run-make/const_fn_mir/dump.mir index ced170bbeee2..b1802c990cf8 100644 --- a/tests/run-make/const_fn_mir/dump.mir +++ b/tests/run-make/const_fn_mir/dump.mir @@ -5,7 +5,7 @@ fn foo() -> i32 { let mut _1: (i32, bool); bb0: { - _1 = CheckedAdd(const 5_i32, const 6_i32); + _1 = AddWithOverflow(const 5_i32, const 6_i32); assert(!move (_1.1: bool), "attempt to compute `{} + {}`, which would overflow", const 5_i32, const 6_i32) -> [success: bb1, unwind continue]; } @@ -21,7 +21,7 @@ fn foo() -> i32 { let mut _1: (i32, bool); bb0: { - _1 = CheckedAdd(const 5_i32, const 6_i32); + _1 = AddWithOverflow(const 5_i32, const 6_i32); assert(!move (_1.1: bool), "attempt to compute `{} + {}`, which would overflow", const 5_i32, const 6_i32) -> [success: bb1, unwind continue]; } diff --git a/tests/run-make/const_fn_mir/rmake.rs b/tests/run-make/const_fn_mir/rmake.rs new file mode 100644 index 000000000000..1ba93421855d --- /dev/null +++ b/tests/run-make/const_fn_mir/rmake.rs @@ -0,0 +1,10 @@ +// The `needs-unwind -Cpanic=abort` gives a different MIR output. + +//@ needs-unwind + +use run_make_support::{cwd, diff, rustc}; + +fn main() { + rustc().input("main.rs").emit("mir").output("dump-actual.mir").run(); + diff().expected_file("dump.mir").actual_file("dump-actual.mir").run(); +} diff --git a/tests/run-make/core-no-fp-fmt-parse/rmake.rs b/tests/run-make/core-no-fp-fmt-parse/rmake.rs index aef28fd25281..3586922f28ec 100644 --- a/tests/run-make/core-no-fp-fmt-parse/rmake.rs +++ b/tests/run-make/core-no-fp-fmt-parse/rmake.rs @@ -1,14 +1,14 @@ // This test checks that the core library of Rust can be compiled without enabling // support for formatting and parsing floating-point numbers. -use run_make_support::rustc; +use run_make_support::{rustc, source_root}; fn main() { rustc() .edition("2021") .arg("-Dwarnings") .crate_type("rlib") - .input("../../../library/core/src/lib.rs") + .input(source_root().join("library/core/src/lib.rs")) .cfg("no_fp_fmt_parse") .run(); } diff --git a/tests/run-make/core-no-oom-handling/Makefile b/tests/run-make/core-no-oom-handling/Makefile deleted file mode 100644 index 28c5261ff854..000000000000 --- a/tests/run-make/core-no-oom-handling/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -include ../tools.mk - -FAKEROOT=$(TMPDIR)/fakeroot - -all: - $(RUSTC) --edition=2021 -Dwarnings --crate-type=rlib ../../../library/core/src/lib.rs --sysroot=$(FAKEROOT) --cfg no_global_oom_handling diff --git a/tests/run-make/core-no-oom-handling/rmake.rs b/tests/run-make/core-no-oom-handling/rmake.rs new file mode 100644 index 000000000000..a9e2b33e2107 --- /dev/null +++ b/tests/run-make/core-no-oom-handling/rmake.rs @@ -0,0 +1,16 @@ +// This test checks that the core library can still compile successfully +// when the no_global_oom_handling feature is turned on. +// See https://github.com/rust-lang/rust/pull/110649 + +use run_make_support::{rustc, source_root}; + +fn main() { + rustc() + .edition("2021") + .arg("-Dwarnings") + .crate_type("rlib") + .input(source_root().join("library/core/src/lib.rs")) + .sysroot("fakeroot") + .cfg("no_global_oom_handling") + .run(); +} diff --git a/tests/run-make/crate-data-smoke/Makefile b/tests/run-make/crate-data-smoke/Makefile deleted file mode 100644 index a453f65ff3ea..000000000000 --- a/tests/run-make/crate-data-smoke/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -include ../tools.mk - -all: - [ `$(RUSTC) --print crate-name crate.rs` = "foo" ] - [ `$(RUSTC) --print file-names crate.rs` = "$(call BIN,foo)" ] - [ `$(RUSTC) --print file-names --crate-type=lib \ - --test crate.rs` = "$(call BIN,foo)" ] - [ `$(RUSTC) --print file-names --test lib.rs` = "$(call BIN,mylib)" ] - $(RUSTC) --print file-names lib.rs - $(RUSTC) --print file-names rlib.rs diff --git a/tests/run-make/crate-data-smoke/rmake.rs b/tests/run-make/crate-data-smoke/rmake.rs new file mode 100644 index 000000000000..70f8e46b6d92 --- /dev/null +++ b/tests/run-make/crate-data-smoke/rmake.rs @@ -0,0 +1,21 @@ +use run_make_support::{bin_name, rust_lib_name, rustc}; + +fn main() { + rustc().print("crate-name").input("crate.rs").run().assert_stdout_equals("foo"); + rustc().print("file-names").input("crate.rs").run().assert_stdout_equals(bin_name("foo")); + rustc() + .print("file-names") + .crate_type("lib") + .arg("--test") + .input("crate.rs") + .run() + .assert_stdout_equals(bin_name("foo")); + rustc() + .print("file-names") + .arg("--test") + .input("lib.rs") + .run() + .assert_stdout_equals(bin_name("mylib")); + rustc().print("file-names").input("lib.rs").run().assert_stdout_equals(rust_lib_name("mylib")); + rustc().print("file-names").input("rlib.rs").run().assert_stdout_equals(rust_lib_name("mylib")); +} diff --git a/tests/run-make/cross-lang-lto-clang/Makefile b/tests/run-make/cross-lang-lto-clang/Makefile index acaebf439d65..acf49c8f5c85 100644 --- a/tests/run-make/cross-lang-lto-clang/Makefile +++ b/tests/run-make/cross-lang-lto-clang/Makefile @@ -1,4 +1,4 @@ -# needs-matching-clang +# needs-force-clang-based-tests # This test makes sure that cross-language inlining actually works by checking # the generated machine code. diff --git a/tests/run-make/cross-lang-lto-clang/rustlib.rs b/tests/run-make/cross-lang-lto-clang/rustlib.rs index 8a74d74a420b..9aff29ef9bb6 100644 --- a/tests/run-make/cross-lang-lto-clang/rustlib.rs +++ b/tests/run-make/cross-lang-lto-clang/rustlib.rs @@ -1,4 +1,4 @@ -#![crate_type="staticlib"] +#![crate_type = "staticlib"] #[no_mangle] pub extern "C" fn rust_always_inlined() -> u32 { diff --git a/tests/run-make/cross-lang-lto-pgo-smoketest/Makefile b/tests/run-make/cross-lang-lto-pgo-smoketest/Makefile index 70085d9bde1d..738e23f9c661 100644 --- a/tests/run-make/cross-lang-lto-pgo-smoketest/Makefile +++ b/tests/run-make/cross-lang-lto-pgo-smoketest/Makefile @@ -1,4 +1,7 @@ -# needs-matching-clang +# needs-force-clang-based-tests + +# FIXME(#126180): This test doesn't actually run anywhere, because the only +# CI job that sets RUSTBUILD_FORCE_CLANG_BASED_TESTS runs very few tests. # This test makes sure that cross-language inlining can be used in conjunction # with profile-guided optimization. The test only tests that the whole workflow diff --git a/tests/run-make/cross-lang-lto-pgo-smoketest/rustlib.rs b/tests/run-make/cross-lang-lto-pgo-smoketest/rustlib.rs index 8a74d74a420b..9aff29ef9bb6 100644 --- a/tests/run-make/cross-lang-lto-pgo-smoketest/rustlib.rs +++ b/tests/run-make/cross-lang-lto-pgo-smoketest/rustlib.rs @@ -1,4 +1,4 @@ -#![crate_type="staticlib"] +#![crate_type = "staticlib"] #[no_mangle] pub extern "C" fn rust_always_inlined() -> u32 { diff --git a/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs b/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs index 61f32762d8b7..f40da6621287 100644 --- a/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs +++ b/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs @@ -1,9 +1,12 @@ //! Make sure that cross-language LTO works on riscv targets, //! which requires extra `target-abi` metadata to be emitted. -//@ needs-matching-clang +//@ needs-force-clang-based-tests //@ needs-llvm-components riscv -use run_make_support::{bin_name, clang, llvm_readobj, rustc, tmp_dir}; +// FIXME(#126180): This test doesn't actually run anywhere, because the only +// CI job that sets RUSTBUILD_FORCE_CLANG_BASED_TESTS runs very few tests. + +use run_make_support::{bin_name, clang, llvm_readobj, rustc}; use std::{ env, path::PathBuf, @@ -30,11 +33,11 @@ fn check_target(target: &str, clang_target: &str, carch: &str, is_double_float: .no_stdlib() .out_exe("riscv-xlto") .input("cstart.c") - .input(tmp_dir().join("libriscv_xlto.rlib")) + .input("libriscv_xlto.rlib") .run(); // Check that the built binary has correct float abi - let executable = tmp_dir().join(bin_name("riscv-xlto")); + let executable = bin_name("riscv-xlto"); let output = llvm_readobj().input(&executable).file_header().run(); let stdout = String::from_utf8_lossy(&output.stdout); eprintln!("obj:\n{}", stdout); diff --git a/tests/run-make/cross-lang-lto-upstream-rlibs/staticlib.rs b/tests/run-make/cross-lang-lto-upstream-rlibs/staticlib.rs index 34951dda3b6e..76c24df78987 100644 --- a/tests/run-make/cross-lang-lto-upstream-rlibs/staticlib.rs +++ b/tests/run-make/cross-lang-lto-upstream-rlibs/staticlib.rs @@ -1,4 +1,4 @@ -#![crate_type="staticlib"] +#![crate_type = "staticlib"] extern crate upstream; diff --git a/tests/run-make/debug-assertions/debug.rs b/tests/run-make/debug-assertions/debug.rs index 76ca60a71c88..9eebf60ded09 100644 --- a/tests/run-make/debug-assertions/debug.rs +++ b/tests/run-make/debug-assertions/debug.rs @@ -15,19 +15,33 @@ fn main() { fn debug_assert_eq() { let mut hit1 = false; let mut hit2 = false; - debug_assert_eq!({ hit1 = true; 1 }, { hit2 = true; 2 }); + debug_assert_eq!( + { + hit1 = true; + 1 + }, + { + hit2 = true; + 2 + } + ); assert!(!hit1); assert!(!hit2); } fn debug_assert() { let mut hit = false; - debug_assert!({ hit = true; false }); + debug_assert!({ + hit = true; + false + }); assert!(!hit); } fn overflow() { - fn add(a: u8, b: u8) -> u8 { a + b } + fn add(a: u8, b: u8) -> u8 { + a + b + } add(200u8, 200u8); } diff --git a/tests/run-make/dep-graph/Makefile b/tests/run-make/dep-graph/Makefile deleted file mode 100644 index d06333f4454b..000000000000 --- a/tests/run-make/dep-graph/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -include ../tools.mk - -# ignore-cross-compile - -# Just verify that we successfully run and produce dep graphs when requested. - -all: - RUST_DEP_GRAPH=$(TMPDIR)/dep-graph $(RUSTC) \ - -Cincremental=$(TMPDIR)/incr \ - -Zquery-dep-graph -Zdump-dep-graph foo.rs - test -f $(TMPDIR)/dep-graph.txt - test -f $(TMPDIR)/dep-graph.dot diff --git a/tests/run-make/dep-graph/rmake.rs b/tests/run-make/dep-graph/rmake.rs new file mode 100644 index 000000000000..351418997f1d --- /dev/null +++ b/tests/run-make/dep-graph/rmake.rs @@ -0,0 +1,18 @@ +// Just verify that we successfully run and produce dep graphs when requested. + +//@ ignore-cross-compile + +use run_make_support::{path, rustc}; + +fn main() { + rustc() + .input("foo.rs") + .incremental(path("incr")) + .arg("-Zquery-dep-graph") + .arg("-Zdump-dep-graph") + .env("RUST_DEP_GRAPH", path("dep-graph")) + .run(); + + assert!(path("dep-graph.txt").is_file()); + assert!(path("dep-graph.dot").is_file()); +} diff --git a/tests/run-make/dep-info-spaces/lib.rs b/tests/run-make/dep-info-spaces/lib.rs index 6264e7b67ec6..4e061892cf7a 100644 --- a/tests/run-make/dep-info-spaces/lib.rs +++ b/tests/run-make/dep-info-spaces/lib.rs @@ -1,4 +1,4 @@ -#[path="foo foo.rs"] +#[path = "foo foo.rs"] pub mod foo; pub mod bar; diff --git a/tests/run-make/dep-info/lib.rs b/tests/run-make/dep-info/lib.rs index eb8631259d43..6e2781cd4b20 100644 --- a/tests/run-make/dep-info/lib.rs +++ b/tests/run-make/dep-info/lib.rs @@ -1,4 +1,4 @@ #![crate_name = "foo"] -pub mod foo; pub mod bar; +pub mod foo; diff --git a/tests/run-make/issue-38237/bar.rs b/tests/run-make/deref-impl-rustdoc-ice/bar.rs similarity index 100% rename from tests/run-make/issue-38237/bar.rs rename to tests/run-make/deref-impl-rustdoc-ice/bar.rs diff --git a/tests/run-make/issue-38237/baz.rs b/tests/run-make/deref-impl-rustdoc-ice/baz.rs similarity index 61% rename from tests/run-make/issue-38237/baz.rs rename to tests/run-make/deref-impl-rustdoc-ice/baz.rs index cd2425f9b692..f606ef9bd6b6 100644 --- a/tests/run-make/issue-38237/baz.rs +++ b/tests/run-make/deref-impl-rustdoc-ice/baz.rs @@ -1,8 +1,10 @@ -extern crate foo; extern crate bar; +extern crate foo; pub struct Bar; impl ::std::ops::Deref for Bar { type Target = bar::S; - fn deref(&self) -> &Self::Target { unimplemented!() } + fn deref(&self) -> &Self::Target { + unimplemented!() + } } diff --git a/tests/run-make/issue-38237/foo.rs b/tests/run-make/deref-impl-rustdoc-ice/foo.rs similarity index 91% rename from tests/run-make/issue-38237/foo.rs rename to tests/run-make/deref-impl-rustdoc-ice/foo.rs index a106e4fde5cc..df00ed4b9e0b 100644 --- a/tests/run-make/issue-38237/foo.rs +++ b/tests/run-make/deref-impl-rustdoc-ice/foo.rs @@ -3,7 +3,9 @@ extern crate proc_macro; #[proc_macro_derive(A)] -pub fn derive(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { ts } +pub fn derive(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { + ts +} #[derive(Debug)] struct S; diff --git a/tests/run-make/deref-impl-rustdoc-ice/rmake.rs b/tests/run-make/deref-impl-rustdoc-ice/rmake.rs new file mode 100644 index 000000000000..91fc0a9025fa --- /dev/null +++ b/tests/run-make/deref-impl-rustdoc-ice/rmake.rs @@ -0,0 +1,16 @@ +// A very specific set of circumstances (mainly, implementing Deref, and +// having a procedural macro and a Debug derivation in external crates) caused +// an internal compiler error (ICE) when trying to use rustdoc. This test +// reproduces the exact circumstances which caused the bug and checks +// that it does not happen again. +// See https://github.com/rust-lang/rust/issues/38237 + +//@ ignore-cross-compile + +use run_make_support::{cwd, rustc, rustdoc}; + +fn main() { + rustc().input("foo.rs").run(); + rustc().input("bar.rs").run(); + rustdoc().input("baz.rs").library_search_path(cwd()).output(cwd()).run(); +} diff --git a/tests/run-make/doctests-keep-binaries/rmake.rs b/tests/run-make/doctests-keep-binaries/rmake.rs index 0613ef4839b1..e48c8a0cef35 100644 --- a/tests/run-make/doctests-keep-binaries/rmake.rs +++ b/tests/run-make/doctests-keep-binaries/rmake.rs @@ -1,15 +1,15 @@ // Check that valid binaries are persisted by running them, regardless of whether the // --run or --no-run option is used. -use run_make_support::{run, rustc, rustdoc, tmp_dir}; -use std::fs::{create_dir, remove_dir_all}; +use run_make_support::fs_wrapper::{create_dir, remove_dir_all}; +use run_make_support::{run, rustc, rustdoc}; use std::path::Path; fn setup_test_env(callback: F) { - let out_dir = tmp_dir().join("doctests"); - create_dir(&out_dir).expect("failed to create doctests folder"); + let out_dir = Path::new("doctests"); + create_dir(&out_dir); rustc().input("t.rs").crate_type("rlib").run(); - callback(&out_dir, &tmp_dir().join("libt.rlib")); + callback(&out_dir, Path::new("libt.rlib")); remove_dir_all(out_dir); } @@ -43,20 +43,18 @@ fn main() { check_generated_binaries(); }); // Behavior with --test-run-directory with relative paths. - setup_test_env(|_out_dir, extern_path| { - let run_dir = "rundir"; - let run_dir_path = tmp_dir().join("rundir"); - create_dir(&run_dir_path).expect("failed to create rundir folder"); + setup_test_env(|_out_dir, _extern_path| { + let run_dir_path = Path::new("rundir"); + create_dir(&run_dir_path); rustdoc() - .current_dir(tmp_dir()) - .input(std::env::current_dir().unwrap().join("t.rs")) + .input("t.rs") .arg("-Zunstable-options") .arg("--test") .arg("--persist-doctests") .arg("doctests") .arg("--test-run-directory") - .arg(run_dir) + .arg(run_dir_path) .extern_("t", "libt.rlib") .run(); diff --git a/tests/run-make/doctests-runtool/rmake.rs b/tests/run-make/doctests-runtool/rmake.rs index 6cc7c6bbdafd..5208730d336d 100644 --- a/tests/run-make/doctests-runtool/rmake.rs +++ b/tests/run-make/doctests-runtool/rmake.rs @@ -1,13 +1,12 @@ // Tests behavior of rustdoc `--runtool`. -use run_make_support::{rustc, rustdoc, tmp_dir}; -use std::env::current_dir; -use std::fs::{create_dir, remove_dir_all}; +use run_make_support::fs_wrapper::{create_dir, remove_dir_all}; +use run_make_support::{rustc, rustdoc}; use std::path::PathBuf; fn mkdir(name: &str) -> PathBuf { - let dir = tmp_dir().join(name); - create_dir(&dir).expect("failed to create doctests folder"); + let dir = PathBuf::from(name); + create_dir(&dir); dir } @@ -22,7 +21,7 @@ fn main() { rustc().input("runtool.rs").output(&run_tool_binary).run(); rustdoc() - .input(current_dir().unwrap().join("t.rs")) + .input("t.rs") .arg("-Zunstable-options") .arg("--test") .arg("--test-run-directory") @@ -30,7 +29,6 @@ fn main() { .arg("--runtool") .arg(&run_tool_binary) .extern_("t", "libt.rlib") - .current_dir(tmp_dir()) .run(); remove_dir_all(run_dir); diff --git a/tests/run-make/duplicate-output-flavors/Makefile b/tests/run-make/duplicate-output-flavors/Makefile deleted file mode 100644 index e33279966c97..000000000000 --- a/tests/run-make/duplicate-output-flavors/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -include ../tools.mk - -all: - $(RUSTC) --crate-type=rlib foo.rs - $(RUSTC) --crate-type=rlib,rlib foo.rs diff --git a/tests/run-make/duplicate-output-flavors/rmake.rs b/tests/run-make/duplicate-output-flavors/rmake.rs new file mode 100644 index 000000000000..09545228807b --- /dev/null +++ b/tests/run-make/duplicate-output-flavors/rmake.rs @@ -0,0 +1,6 @@ +use run_make_support::rustc; + +fn main() { + rustc().input("foo.rs").crate_type("rlib").run(); + rustc().input("foo.rs").crate_type("rlib,rlib").run(); +} diff --git a/tests/run-make/dylib-chain/m2.rs b/tests/run-make/dylib-chain/m2.rs index 62176ddc9f35..92950a919154 100644 --- a/tests/run-make/dylib-chain/m2.rs +++ b/tests/run-make/dylib-chain/m2.rs @@ -1,4 +1,6 @@ #![crate_type = "dylib"] extern crate m1; -pub fn m2() { m1::m1() } +pub fn m2() { + m1::m1() +} diff --git a/tests/run-make/dylib-chain/m3.rs b/tests/run-make/dylib-chain/m3.rs index d213aeda9ac1..28a8ca9d7dde 100644 --- a/tests/run-make/dylib-chain/m3.rs +++ b/tests/run-make/dylib-chain/m3.rs @@ -1,4 +1,6 @@ #![crate_type = "dylib"] extern crate m2; -pub fn m3() { m2::m2() } +pub fn m3() { + m2::m2() +} diff --git a/tests/run-make/dylib-chain/m4.rs b/tests/run-make/dylib-chain/m4.rs index fa8ec6079dea..c732512af9fc 100644 --- a/tests/run-make/dylib-chain/m4.rs +++ b/tests/run-make/dylib-chain/m4.rs @@ -1,3 +1,5 @@ extern crate m3; -fn main() { m3::m3() } +fn main() { + m3::m3() +} diff --git a/tests/run-make/emit-named-files/Makefile b/tests/run-make/emit-named-files/Makefile deleted file mode 100644 index 2b97b841fc00..000000000000 --- a/tests/run-make/emit-named-files/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -include ../tools.mk - -OUT=$(TMPDIR)/emit - -all: asm llvm-bc llvm-ir obj metadata link dep-info mir - -asm: $(OUT) - $(RUSTC) --emit asm=$(OUT)/libfoo.s foo.rs - test -f $(OUT)/libfoo.s -llvm-bc: $(OUT) - $(RUSTC) --emit llvm-bc=$(OUT)/libfoo.bc foo.rs - test -f $(OUT)/libfoo.bc -llvm-ir: $(OUT) - $(RUSTC) --emit llvm-ir=$(OUT)/libfoo.ll foo.rs - test -f $(OUT)/libfoo.ll -obj: $(OUT) - $(RUSTC) --emit obj=$(OUT)/libfoo.o foo.rs - test -f $(OUT)/libfoo.o -metadata: $(OUT) - $(RUSTC) --emit metadata=$(OUT)/libfoo.rmeta foo.rs - test -f $(OUT)/libfoo.rmeta -link: $(OUT) - $(RUSTC) --emit link=$(OUT)/libfoo.rlib foo.rs - test -f $(OUT)/libfoo.rlib -dep-info: $(OUT) - $(RUSTC) --emit dep-info=$(OUT)/libfoo.d foo.rs - test -f $(OUT)/libfoo.d -mir: $(OUT) - $(RUSTC) --emit mir=$(OUT)/libfoo.mir foo.rs - test -f $(OUT)/libfoo.mir - -$(OUT): - mkdir -p $(OUT) diff --git a/tests/run-make/emit-named-files/rmake.rs b/tests/run-make/emit-named-files/rmake.rs new file mode 100644 index 000000000000..79c3ee90c987 --- /dev/null +++ b/tests/run-make/emit-named-files/rmake.rs @@ -0,0 +1,24 @@ +use std::path::Path; + +use run_make_support::{fs_wrapper, rustc}; + +fn emit_and_check(out_dir: &Path, out_file: &str, format: &str) { + let out_file = out_dir.join(out_file); + rustc().input("foo.rs").emit(&format!("{format}={}", out_file.display())).run(); + assert!(out_file.is_file()); +} + +fn main() { + let out_dir = Path::new("emit"); + + fs_wrapper::create_dir(&out_dir); + + emit_and_check(&out_dir, "libfoo.s", "asm"); + emit_and_check(&out_dir, "libfoo.bc", "llvm-bc"); + emit_and_check(&out_dir, "libfoo.ll", "llvm-ir"); + emit_and_check(&out_dir, "libfoo.o", "obj"); + emit_and_check(&out_dir, "libfoo.rmeta", "metadata"); + emit_and_check(&out_dir, "libfoo.rlib", "link"); + emit_and_check(&out_dir, "libfoo.d", "dep-info"); + emit_and_check(&out_dir, "libfoo.mir", "mir"); +} diff --git a/tests/run-make/emit-stack-sizes/Makefile b/tests/run-make/emit-stack-sizes/Makefile index f636ebd28f2e..b546fcba5121 100644 --- a/tests/run-make/emit-stack-sizes/Makefile +++ b/tests/run-make/emit-stack-sizes/Makefile @@ -1,10 +1,10 @@ include ../tools.mk # ignore-windows -# ignore-macos +# ignore-apple # # This feature only works when the output object format is ELF so we ignore -# macOS and Windows +# Apple and Windows # check that the .stack_sizes section is generated all: diff --git a/tests/run-make/emit/Makefile b/tests/run-make/emit/Makefile deleted file mode 100644 index b3ca0b79fb0c..000000000000 --- a/tests/run-make/emit/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) -Copt-level=0 --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs - $(RUSTC) -Copt-level=1 --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs - $(RUSTC) -Copt-level=2 --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs - $(RUSTC) -Copt-level=3 --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs - $(RUSTC) -Copt-level=s --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs - $(RUSTC) -Copt-level=z --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs - $(RUSTC) -Copt-level=0 --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs - $(call RUN,test-26235) || exit 1 - $(RUSTC) -Copt-level=1 --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs - $(call RUN,test-26235) || exit 1 - $(RUSTC) -Copt-level=2 --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs - $(call RUN,test-26235) || exit 1 - $(RUSTC) -Copt-level=3 --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs - $(call RUN,test-26235) || exit 1 - $(RUSTC) -Copt-level=s --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs - $(call RUN,test-26235) || exit 1 - $(RUSTC) -Copt-level=z --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs - $(call RUN,test-26235) || exit 1 diff --git a/tests/run-make/emit/rmake.rs b/tests/run-make/emit/rmake.rs new file mode 100644 index 000000000000..8b3ddb66f923 --- /dev/null +++ b/tests/run-make/emit/rmake.rs @@ -0,0 +1,19 @@ +// A bug from 2015 would cause errors when emitting multiple types of files +// in the same rustc call. A fix was created in #30452. This test checks that rustc still compiles +// a source file successfully when emission of multiple output artifacts are requested. +// See https://github.com/rust-lang/rust/pull/30452 + +//@ ignore-cross-compile + +use run_make_support::{run, rustc}; + +fn main() { + let opt_levels = ["0", "1", "2", "3", "s", "z"]; + for level in opt_levels { + rustc().opt_level(level).emit("llvm-bc,llvm-ir,asm,obj,link").input("test-24876.rs").run(); + } + for level in opt_levels { + rustc().opt_level(level).emit("llvm-bc,llvm-ir,asm,obj,link").input("test-26235.rs").run(); + run("test-26235"); + } +} diff --git a/tests/run-make/emit/test-26235.rs b/tests/run-make/emit/test-26235.rs index 07d975f3317c..d91c46d2dec8 100644 --- a/tests/run-make/emit/test-26235.rs +++ b/tests/run-make/emit/test-26235.rs @@ -6,16 +6,22 @@ fn main() { type Key = u32; const NUM_THREADS: usize = 2; - #[derive(Clone,Copy)] + #[derive(Clone, Copy)] struct Stats { upsert: S, delete: S, insert: S, - update: S + update: S, }; - impl Stats where S: Copy { - fn dot(self, s: Stats, f: F) -> Stats where F: Fn(S, T) -> B { + impl Stats + where + S: Copy, + { + fn dot(self, s: Stats, f: F) -> Stats + where + F: Fn(S, T) -> B, + { let Stats { upsert: u1, delete: d1, insert: i1, update: p1 } = self; let Stats { upsert: u2, delete: d2, insert: i2, update: p2 } = s; Stats { upsert: f(u1, u2), delete: f(d1, d2), insert: f(i1, i2), update: f(p1, p2) } @@ -38,9 +44,12 @@ fn main() { make_threads(); { - let Stats { ref upsert, ref delete, ref insert, ref update } = stats.iter().fold( - Stats::new(0), |res, &s| res.dot(s, |x: Key, y: Key| x.wrapping_add(y))); - println!("upserts: {}, deletes: {}, inserts: {}, updates: {}", - upsert, delete, insert, update); + let Stats { ref upsert, ref delete, ref insert, ref update } = stats + .iter() + .fold(Stats::new(0), |res, &s| res.dot(s, |x: Key, y: Key| x.wrapping_add(y))); + println!( + "upserts: {}, deletes: {}, inserts: {}, updates: {}", + upsert, delete, insert, update + ); } } diff --git a/tests/run-make/error-found-staticlib-instead-crate/Makefile b/tests/run-make/error-found-staticlib-instead-crate/Makefile deleted file mode 100644 index 0eae41d720cc..000000000000 --- a/tests/run-make/error-found-staticlib-instead-crate/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -include ../tools.mk - -all: - $(RUSTC) foo.rs --crate-type staticlib - $(RUSTC) bar.rs 2>&1 | $(CGREP) "found staticlib" diff --git a/tests/run-make/error-found-staticlib-instead-crate/rmake.rs b/tests/run-make/error-found-staticlib-instead-crate/rmake.rs new file mode 100644 index 000000000000..8c707092b7e4 --- /dev/null +++ b/tests/run-make/error-found-staticlib-instead-crate/rmake.rs @@ -0,0 +1,11 @@ +// When rustc is looking for a crate but is given a staticlib instead, +// the error message should be helpful and indicate precisely the cause +// of the compilation failure. +// See https://github.com/rust-lang/rust/pull/21978 + +use run_make_support::rustc; + +fn main() { + rustc().input("foo.rs").crate_type("staticlib").run(); + rustc().input("bar.rs").run_fail().assert_stderr_contains("found staticlib"); +} diff --git a/tests/run-make/exit-code/rmake.rs b/tests/run-make/exit-code/rmake.rs index 76d7777581b5..f290554831db 100644 --- a/tests/run-make/exit-code/rmake.rs +++ b/tests/run-make/exit-code/rmake.rs @@ -1,25 +1,26 @@ // Test that we exit with the correct exit code for successful / unsuccessful / ICE compilations -use run_make_support::{rustc, rustdoc, tmp_dir}; +use run_make_support::{rustc, rustdoc}; fn main() { rustc().arg("success.rs").run(); - rustc().arg("--invalid-arg-foo").run_fail_assert_exit_code(1); + rustc().arg("--invalid-arg-foo").run_fail().assert_exit_code(1); - rustc().arg("compile-error.rs").run_fail_assert_exit_code(1); + rustc().arg("compile-error.rs").run_fail().assert_exit_code(1); rustc() .env("RUSTC_ICE", "0") .arg("-Ztreat-err-as-bug") .arg("compile-error.rs") - .run_fail_assert_exit_code(101); + .run_fail() + .assert_exit_code(101); - rustdoc().arg("success.rs").output(tmp_dir().join("exit-code")).run(); + rustdoc().arg("success.rs").output("exit-code").run(); - rustdoc().arg("--invalid-arg-foo").run_fail_assert_exit_code(1); + rustdoc().arg("--invalid-arg-foo").run_fail().assert_exit_code(1); - rustdoc().arg("compile-error.rs").run_fail_assert_exit_code(1); + rustdoc().arg("compile-error.rs").run_fail().assert_exit_code(1); - rustdoc().arg("lint-failure.rs").run_fail_assert_exit_code(1); + rustdoc().arg("lint-failure.rs").run_fail().assert_exit_code(1); } diff --git a/tests/run-make/extern-diff-internal-name/test.rs b/tests/run-make/extern-diff-internal-name/test.rs index 4c53dc28a80e..d210f8833165 100644 --- a/tests/run-make/extern-diff-internal-name/test.rs +++ b/tests/run-make/extern-diff-internal-name/test.rs @@ -1,5 +1,4 @@ #[macro_use] extern crate foo; -fn main() { -} +fn main() {} diff --git a/tests/run-make/extern-flag-disambiguates/a.rs b/tests/run-make/extern-flag-disambiguates/a.rs index 2b1a3190150f..fe4bb5ccac40 100644 --- a/tests/run-make/extern-flag-disambiguates/a.rs +++ b/tests/run-make/extern-flag-disambiguates/a.rs @@ -3,4 +3,6 @@ static FOO: usize = 3; -pub fn token() -> &'static usize { &FOO } +pub fn token() -> &'static usize { + &FOO +} diff --git a/tests/run-make/extern-flag-disambiguates/b.rs b/tests/run-make/extern-flag-disambiguates/b.rs index 1d7a7339ce27..fc3303e5ef8a 100644 --- a/tests/run-make/extern-flag-disambiguates/b.rs +++ b/tests/run-make/extern-flag-disambiguates/b.rs @@ -5,5 +5,9 @@ extern crate a; static FOO: usize = 3; -pub fn token() -> &'static usize { &FOO } -pub fn a_token() -> &'static usize { a::token() } +pub fn token() -> &'static usize { + &FOO +} +pub fn a_token() -> &'static usize { + a::token() +} diff --git a/tests/run-make/extern-flag-disambiguates/c.rs b/tests/run-make/extern-flag-disambiguates/c.rs index 3f9d143ed2dc..26ac787e74d1 100644 --- a/tests/run-make/extern-flag-disambiguates/c.rs +++ b/tests/run-make/extern-flag-disambiguates/c.rs @@ -5,5 +5,9 @@ extern crate a; static FOO: usize = 3; -pub fn token() -> &'static usize { &FOO } -pub fn a_token() -> &'static usize { a::token() } +pub fn token() -> &'static usize { + &FOO +} +pub fn a_token() -> &'static usize { + a::token() +} diff --git a/tests/run-make/extern-flag-disambiguates/d.rs b/tests/run-make/extern-flag-disambiguates/d.rs index 249c6a107ff3..f0ab2b063e11 100644 --- a/tests/run-make/extern-flag-disambiguates/d.rs +++ b/tests/run-make/extern-flag-disambiguates/d.rs @@ -1,9 +1,13 @@ -#[cfg(before)] extern crate a; +#[cfg(before)] +extern crate a; +#[cfg(after)] +extern crate a; extern crate b; extern crate c; -#[cfg(after)] extern crate a; -fn t(a: &'static usize) -> usize { a as *const _ as usize } +fn t(a: &'static usize) -> usize { + a as *const _ as usize +} fn main() { assert_eq!(t(a::token()), t(b::a_token())); diff --git a/tests/run-make/extern-flag-fun/Makefile b/tests/run-make/extern-flag-fun/Makefile deleted file mode 100644 index 687cdfd76755..000000000000 --- a/tests/run-make/extern-flag-fun/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) bar.rs --crate-type=rlib - $(RUSTC) bar.rs --crate-type=rlib -C extra-filename=-a - $(RUSTC) bar-alt.rs --crate-type=rlib - $(RUSTC) foo.rs --extern bar=no-exist && exit 1 || exit 0 - $(RUSTC) foo.rs --extern bar=foo.rs && exit 1 || exit 0 - $(RUSTC) foo.rs \ - --extern bar=$(TMPDIR)/libbar.rlib \ - --extern bar=$(TMPDIR)/libbar-alt.rlib \ - && exit 1 || exit 0 - $(RUSTC) foo.rs \ - --extern bar=$(TMPDIR)/libbar.rlib \ - --extern bar=$(TMPDIR)/libbar-a.rlib - $(RUSTC) foo.rs --extern bar=$(TMPDIR)/libbar.rlib - # Try to be sneaky and load a private crate from with a non-private name. - $(RUSTC) rustc.rs -Zforce-unstable-if-unmarked --crate-type=rlib - $(RUSTC) gated_unstable.rs --extern alloc=$(TMPDIR)/librustc.rlib 2>&1 | $(CGREP) 'rustc_private' diff --git a/tests/run-make/extern-flag-fun/rmake.rs b/tests/run-make/extern-flag-fun/rmake.rs new file mode 100644 index 000000000000..c1825f6bbb88 --- /dev/null +++ b/tests/run-make/extern-flag-fun/rmake.rs @@ -0,0 +1,38 @@ +// The --extern flag can override the default crate search of +// the compiler and directly fetch a given path. There are a few rules +// to follow: for example, there can't be more than one rlib, the crates must +// be valid ("no-exist" in this test), and private crates can't be loaded +// as non-private. This test checks that these rules are enforced. +// See https://github.com/rust-lang/rust/pull/15319 + +use run_make_support::{rust_lib_name, rustc}; + +fn main() { + rustc().input("bar.rs").crate_type("rlib").run(); + // Exactly the same rlib as the first line, only the filename changes. + rustc().input("bar.rs").crate_type("rlib").extra_filename("-a").run(); + rustc().input("bar-alt.rs").crate_type("rlib").run(); + // The crate must be valid. + rustc().input("foo.rs").extern_("bar", "no-exist").run_fail(); + rustc().input("foo.rs").extern_("bar", "foo.rs").run_fail(); + // Compilation fails with two different rlibs. + rustc() + .input("foo.rs") + .extern_("bar", rust_lib_name("bar")) + .extern_("bar", rust_lib_name("bar-alt")) + .run_fail(); + // Even though this one has seemingly two rlibs, they are one and the same. + rustc() + .input("foo.rs") + .extern_("bar", rust_lib_name("bar")) + .extern_("bar", rust_lib_name("bar-a")) + .run(); + rustc().input("foo.rs").extern_("bar", rust_lib_name("bar")).run(); + // Try to be sneaky and load a private crate from with a non-private name. + rustc().input("rustc.rs").arg("-Zforce-unstable-if-unmarked").crate_type("rlib").run(); + rustc() + .input("gated_unstable.rs") + .extern_("alloc", rust_lib_name("rustc")) + .run_fail() + .assert_stderr_contains("rustc_private"); +} diff --git a/tests/run-make/extern-fn-explicit-align/test.rs b/tests/run-make/extern-fn-explicit-align/test.rs index 846622de3cd1..81991b5919ce 100644 --- a/tests/run-make/extern-fn-explicit-align/test.rs +++ b/tests/run-make/extern-fn-explicit-align/test.rs @@ -1,6 +1,6 @@ // Issue #80127: Passing structs via FFI should work with explicit alignment. -use std::ffi::{CStr, c_char}; +use std::ffi::{c_char, CStr}; use std::ptr::null_mut; #[repr(C)] @@ -18,7 +18,7 @@ pub struct TwoU64s { #[repr(C)] pub struct WrappedU64s { - pub a: TwoU64s + pub a: TwoU64s, } #[repr(C)] diff --git a/tests/run-make/extern-fn-reachable/dylib.rs b/tests/run-make/extern-fn-reachable/dylib.rs index cd0179348705..fe0c7023b27b 100644 --- a/tests/run-make/extern-fn-reachable/dylib.rs +++ b/tests/run-make/extern-fn-reachable/dylib.rs @@ -1,14 +1,19 @@ #![crate_type = "dylib"] #![allow(dead_code)] -#[no_mangle] pub extern "C" fn fun1() {} -#[no_mangle] extern "C" fn fun2() {} +#[no_mangle] +pub extern "C" fn fun1() {} +#[no_mangle] +extern "C" fn fun2() {} mod foo { - #[no_mangle] pub extern "C" fn fun3() {} + #[no_mangle] + pub extern "C" fn fun3() {} } pub mod bar { - #[no_mangle] pub extern "C" fn fun4() {} + #[no_mangle] + pub extern "C" fn fun4() {} } -#[no_mangle] pub fn fun5() {} +#[no_mangle] +pub fn fun5() {} diff --git a/tests/run-make/extern-fn-struct-passing-abi/test.rs b/tests/run-make/extern-fn-struct-passing-abi/test.rs index 99e079f98a87..f898592fce98 100644 --- a/tests/run-make/extern-fn-struct-passing-abi/test.rs +++ b/tests/run-make/extern-fn-struct-passing-abi/test.rs @@ -90,8 +90,12 @@ extern "C" { fn byval_rect_with_many_huge(a: Huge, b: Huge, c: Huge, d: Huge, e: Huge, f: Huge, g: Rect); fn byval_rect_with_many_huge64( - a: Huge64, b: Huge64, c: Huge64, - d: Huge64, e: Huge64, f: Huge64, + a: Huge64, + b: Huge64, + c: Huge64, + d: Huge64, + e: Huge64, + f: Huge64, g: Rect, ); diff --git a/tests/run-make/extern-multiple-copies/bar.rs b/tests/run-make/extern-multiple-copies/bar.rs index c6b3595f6775..aa0bee77cb29 100644 --- a/tests/run-make/extern-multiple-copies/bar.rs +++ b/tests/run-make/extern-multiple-copies/bar.rs @@ -1,5 +1,5 @@ -extern crate foo2; // foo2 first to exhibit the bug extern crate foo1; +extern crate foo2; // foo2 first to exhibit the bug fn main() { /* ... */ diff --git a/tests/run-make/issue-53964/app.rs b/tests/run-make/external-crate-panic-handle-no-lint/app.rs similarity index 99% rename from tests/run-make/issue-53964/app.rs rename to tests/run-make/external-crate-panic-handle-no-lint/app.rs index 8127b9578bfe..e0168579ccab 100644 --- a/tests/run-make/issue-53964/app.rs +++ b/tests/run-make/external-crate-panic-handle-no-lint/app.rs @@ -1,7 +1,6 @@ #![crate_type = "bin"] #![no_main] #![no_std] - #![deny(unused_extern_crates)] // `panic` provides a `panic_handler` so it shouldn't trip the `unused_extern_crates` lint diff --git a/tests/run-make/issue-53964/panic.rs b/tests/run-make/external-crate-panic-handle-no-lint/panic.rs similarity index 100% rename from tests/run-make/issue-53964/panic.rs rename to tests/run-make/external-crate-panic-handle-no-lint/panic.rs diff --git a/tests/run-make/external-crate-panic-handle-no-lint/rmake.rs b/tests/run-make/external-crate-panic-handle-no-lint/rmake.rs new file mode 100644 index 000000000000..e54ef53e3dcb --- /dev/null +++ b/tests/run-make/external-crate-panic-handle-no-lint/rmake.rs @@ -0,0 +1,12 @@ +// Defining a crate that provides panic handling as an external crate +// could uselessly trigger the "unused external crate" lint. In this test, +// if the lint is triggered, it will trip #![deny(unused_extern_crates)], +// and cause the test to fail. +// See https://github.com/rust-lang/rust/issues/53964 + +use run_make_support::rustc; + +fn main() { + rustc().input("panic.rs").run(); + rustc().input("app.rs").panic("abort").emit("obj").run(); +} diff --git a/tests/run-make/fpic/Makefile b/tests/run-make/fpic/Makefile deleted file mode 100644 index c38dd8d6e8c4..000000000000 --- a/tests/run-make/fpic/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# ignore-windows -# ignore-macos - -# Test for #39529. -# `-z text` causes ld to error if there are any non-PIC sections - -all: - $(RUSTC) hello.rs -C link-args=-Wl,-z,text diff --git a/tests/run-make/fpic/hello.rs b/tests/run-make/fpic/hello.rs deleted file mode 100644 index 45590d86ba6c..000000000000 --- a/tests/run-make/fpic/hello.rs +++ /dev/null @@ -1 +0,0 @@ -fn main() { } diff --git a/tests/run-make/glibc-staticlib-args/Makefile b/tests/run-make/glibc-staticlib-args/Makefile deleted file mode 100644 index cad6c049e459..000000000000 --- a/tests/run-make/glibc-staticlib-args/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# ignore-cross-compile -# only-gnu -# only-linux - -include ../tools.mk - -# This ensures that std::env::args works in a library called from C on glibc Linux. - -all: - $(RUSTC) --crate-type=staticlib library.rs - $(CC) program.c $(call STATICLIB,library) $(call OUT_EXE,program) \ - $(EXTRACFLAGS) $(EXTRACXXFLAGS) - $(call RUN,program) diff --git a/tests/run-make/glibc-staticlib-args/rmake.rs b/tests/run-make/glibc-staticlib-args/rmake.rs new file mode 100644 index 000000000000..8ab10419ab9d --- /dev/null +++ b/tests/run-make/glibc-staticlib-args/rmake.rs @@ -0,0 +1,18 @@ +// This ensures that std::env::args works in a library called from C on glibc Linux. + +//@ only-gnu +//@ only-linux +//@ ignore-cross-compile + +use run_make_support::{bin_name, cc, extra_c_flags, extra_cxx_flags, run, rustc, static_lib_name}; + +fn main() { + rustc().input("library.rs").crate_type("staticlib").run(); + cc().input("program.c") + .arg(static_lib_name("library")) + .out_exe("program") + .args(&extra_c_flags()) + .args(&extra_cxx_flags()) + .run(); + run(&bin_name("program")); +} diff --git a/tests/run-make/inaccessible-temp-dir/Makefile b/tests/run-make/inaccessible-temp-dir/Makefile deleted file mode 100644 index abdba4eb8614..000000000000 --- a/tests/run-make/inaccessible-temp-dir/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -# only-linux -# ignore-arm - linker error on `armhf-gnu` - -include ../tools.mk - -# Issue #66530: We would ICE if someone compiled with `-o /dev/null`, -# because we would try to generate auxiliary files in `/dev/` (which -# at least the OS X file system rejects). -# -# An attempt to `-Ztemps-dir` into a directory we cannot write into should -# indeed be an error; but not an ICE. -# -# However, some folks run tests as root, which can write `/dev/` and end -# up clobbering `/dev/null`. Instead we'll use an inaccessible path, which -# also used to ICE, but even root can't magically write there. -# -# Note that `-Ztemps-dir` uses `create_dir_all` so it is not sufficient to -# use a directory with non-existing parent like `/does-not-exist/output`. - -all: - # Create an inaccessible directory - mkdir $(TMPDIR)/inaccessible - chmod 000 $(TMPDIR)/inaccessible - - # Run rustc with `-Ztemps-dir` set to a directory - # *inside* the inaccessible one, so that it can't create it - $(RUSTC) program.rs -Ztemps-dir=$(TMPDIR)/inaccessible/tmp 2>&1 \ - | $(CGREP) 'failed to find or create the directory specified by `--temps-dir`' - - # Make the inaccessible directory accessible, - # so that compiletest can delete the temp dir - chmod +rw $(TMPDIR)/inaccessible diff --git a/tests/run-make/inaccessible-temp-dir/rmake.rs b/tests/run-make/inaccessible-temp-dir/rmake.rs new file mode 100644 index 000000000000..be24e47b6dec --- /dev/null +++ b/tests/run-make/inaccessible-temp-dir/rmake.rs @@ -0,0 +1,38 @@ +// Issue #66530: We would ICE if someone compiled with `-o /dev/null`, +// because we would try to generate auxiliary files in `/dev/` (which +// at least the OS X file system rejects). +// +// An attempt to `-Ztemps-dir` into a directory we cannot write into should +// indeed be an error; but not an ICE. +// +// However, some folks run tests as root, which can write `/dev/` and end +// up clobbering `/dev/null`. Instead we'll use an inaccessible path, which +// also used to ICE, but even root can't magically write there. +// +// Note that `-Ztemps-dir` uses `create_dir_all` so it is not sufficient to +// use a directory with non-existing parent like `/does-not-exist/output`. +// See https://github.com/rust-lang/rust/issues/66530 + +//@ ignore-arm +// Reason: linker error on `armhf-gnu` +//@ ignore-windows +// Reason: `set_readonly` has no effect on directories +// and does not prevent modification. + +use run_make_support::{fs_wrapper, rustc, test_while_readonly}; + +fn main() { + // Create an inaccessible directory. + fs_wrapper::create_dir("inaccessible"); + test_while_readonly("inaccessible", || { + // Run rustc with `-Z temps-dir` set to a directory *inside* the inaccessible one, + // so that it can't create `tmp`. + rustc() + .input("program.rs") + .arg("-Ztemps-dir=inaccessible/tmp") + .run_fail() + .assert_stderr_contains( + "failed to find or create the directory specified by `--temps-dir`", + ); + }); +} diff --git a/tests/run-make/incr-prev-body-beyond-eof/Makefile b/tests/run-make/incr-prev-body-beyond-eof/Makefile deleted file mode 100644 index aa47552f52c9..000000000000 --- a/tests/run-make/incr-prev-body-beyond-eof/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# ignore-none no-std is not supported -# ignore-nvptx64-nvidia-cuda FIXME: can't find crate for `std` - -include ../tools.mk - -# Tests that we don't ICE during incremental compilation after modifying a -# function span such that its previous end line exceeds the number of lines -# in the new file, but its start line/column and length remain the same. - -SRC=$(TMPDIR)/src -INCR=$(TMPDIR)/incr - -all: - mkdir $(SRC) - mkdir $(INCR) - cp a.rs $(SRC)/main.rs - $(RUSTC) -C incremental=$(INCR) $(SRC)/main.rs --target $(TARGET) - cp b.rs $(SRC)/main.rs - $(RUSTC) -C incremental=$(INCR) $(SRC)/main.rs --target $(TARGET) diff --git a/tests/run-make/incr-prev-body-beyond-eof/a.rs b/tests/run-make/incr-prev-body-beyond-eof/a.rs index ca70fb563349..59ac25444000 100644 --- a/tests/run-make/incr-prev-body-beyond-eof/a.rs +++ b/tests/run-make/incr-prev-body-beyond-eof/a.rs @@ -9,8 +9,4 @@ fn main() { // Basically, avoid modifying this file, including adding or removing whitespace! fn foo() { assert_eq!(1, 1); - - - - } diff --git a/tests/run-make/incr-prev-body-beyond-eof/b.rs b/tests/run-make/incr-prev-body-beyond-eof/b.rs index a272e44a6326..2f26a1ffe97a 100644 --- a/tests/run-make/incr-prev-body-beyond-eof/b.rs +++ b/tests/run-make/incr-prev-body-beyond-eof/b.rs @@ -8,5 +8,5 @@ fn main() { // a.rs, the body must end on a line number which does not exist in b.rs. // Basically, avoid modifying this file, including adding or removing whitespace! fn foo() { - assert_eq!(1, 1);//// + assert_eq!(1, 1); //// } diff --git a/tests/run-make/incr-prev-body-beyond-eof/rmake.rs b/tests/run-make/incr-prev-body-beyond-eof/rmake.rs new file mode 100644 index 000000000000..e6d6ae95aaaf --- /dev/null +++ b/tests/run-make/incr-prev-body-beyond-eof/rmake.rs @@ -0,0 +1,26 @@ +// After modifying the span of a function, if the length of +// the span remained the same but the end line number became different, +// this would cause an internal compiler error (ICE), fixed in #76256. + +// This test compiles main.rs twice, first with end line 16 and +// then with end line 12. If compilation is successful, the end line +// was hashed by rustc in addition to the span length, and the fix still +// works. + +//@ ignore-none +// reason: no-std is not supported + +//@ ignore-nvptx64-nvidia-cuda +// FIXME: can't find crate for `std` + +use run_make_support::fs_wrapper as fs; +use run_make_support::rustc; + +fn main() { + fs::create_dir("src"); + fs::create_dir("incr"); + fs::copy("a.rs", "src/main.rs"); + rustc().incremental("incr").input("src/main.rs").run(); + fs::copy("b.rs", "src/main.rs"); + rustc().incremental("incr").input("src/main.rs").run(); +} diff --git a/tests/run-make/incremental-debugger-visualizer/Makefile b/tests/run-make/incremental-debugger-visualizer/Makefile deleted file mode 100644 index 8cfe41597adf..000000000000 --- a/tests/run-make/incremental-debugger-visualizer/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -include ../tools.mk - -# This test makes sure that changes to files referenced via #[debugger_visualizer] -# are picked up when compiling incrementally. - -# We have to copy the source to $(TMPDIR) because Github CI mounts the source -# directory as readonly. We need to apply modifications to some of the source -# file. -SRC_DIR := $(TMPDIR)/src -INCR_CACHE_DIR := $(TMPDIR)/incremental - -all: - rm -rf $(TMPDIR)/* - mkdir $(SRC_DIR) - cp ./foo.rs $(SRC_DIR) - echo "GDB script v1" > $(SRC_DIR)/foo.py - echo "Natvis v1" > $(SRC_DIR)/foo.natvis - $(RUSTC) $(SRC_DIR)/foo.rs \ - --crate-type=rlib \ - --emit metadata \ - -C incremental=$(INCR_CACHE_DIR) \ - -Z incremental-verify-ich - $(CGREP) "GDB script v1" < $(TMPDIR)/libfoo.rmeta - $(CGREP) "Natvis v1" < $(TMPDIR)/libfoo.rmeta - - # Change only the GDB script and check that the change has been picked up - echo "GDB script v2" > $(SRC_DIR)/foo.py - $(RUSTC) $(SRC_DIR)/foo.rs \ - --crate-type=rlib \ - --emit metadata \ - -C incremental=$(INCR_CACHE_DIR) \ - -Z incremental-verify-ich - - $(CGREP) "GDB script v2" < $(TMPDIR)/libfoo.rmeta - $(CGREP) -v "GDB script v1" < $(TMPDIR)/libfoo.rmeta - $(CGREP) "Natvis v1" < $(TMPDIR)/libfoo.rmeta - - # Now change the Natvis version and check that the change has been picked up - echo "Natvis v2" > $(SRC_DIR)/foo.natvis - $(RUSTC) $(SRC_DIR)/foo.rs \ - --crate-type=rlib \ - --emit metadata \ - -C incremental=$(INCR_CACHE_DIR) \ - -Z incremental-verify-ich - - $(CGREP) "GDB script v2" < $(TMPDIR)/libfoo.rmeta - $(CGREP) -v "GDB script v1" < $(TMPDIR)/libfoo.rmeta - $(CGREP) "Natvis v2" < $(TMPDIR)/libfoo.rmeta - $(CGREP) -v "Natvis v1" < $(TMPDIR)/libfoo.rmeta diff --git a/tests/run-make/incremental-debugger-visualizer/rmake.rs b/tests/run-make/incremental-debugger-visualizer/rmake.rs new file mode 100644 index 000000000000..1ef3af873530 --- /dev/null +++ b/tests/run-make/incremental-debugger-visualizer/rmake.rs @@ -0,0 +1,56 @@ +// This test ensures that changes to files referenced via #[debugger_visualizer] +// (in this case, foo.py and foo.natvis) are picked up when compiling incrementally. +// See https://github.com/rust-lang/rust/pull/111641 + +use run_make_support::{fs_wrapper, invalid_utf8_contains, invalid_utf8_not_contains, rustc}; +use std::io::Read; + +fn main() { + fs_wrapper::create_file("foo.py"); + fs_wrapper::write("foo.py", "GDB script v1"); + fs_wrapper::create_file("foo.natvis"); + fs_wrapper::write("foo.natvis", "Natvis v1"); + rustc() + .input("foo.rs") + .crate_type("rlib") + .emit("metadata") + .incremental("incremental") + .arg("-Zincremental-verify-ich") + .run(); + + invalid_utf8_contains("libfoo.rmeta", "GDB script v1"); + invalid_utf8_contains("libfoo.rmeta", "Natvis v1"); + + // Change only the GDB script and check that the change has been picked up + fs_wrapper::remove_file("foo.py"); + fs_wrapper::create_file("foo.py"); + fs_wrapper::write("foo.py", "GDB script v2"); + rustc() + .input("foo.rs") + .crate_type("rlib") + .emit("metadata") + .incremental("incremental") + .arg("-Zincremental-verify-ich") + .run(); + + invalid_utf8_contains("libfoo.rmeta", "GDB script v2"); + invalid_utf8_not_contains("libfoo.rmeta", "GDB script v1"); + invalid_utf8_contains("libfoo.rmeta", "Natvis v1"); + + // Now change the Natvis version and check that the change has been picked up + fs_wrapper::remove_file("foo.natvis"); + fs_wrapper::create_file("foo.natvis"); + fs_wrapper::write("foo.natvis", "Natvis v2"); + rustc() + .input("foo.rs") + .crate_type("rlib") + .emit("metadata") + .incremental("incremental") + .arg("-Zincremental-verify-ich") + .run(); + + invalid_utf8_contains("libfoo.rmeta", "GDB script v2"); + invalid_utf8_not_contains("libfoo.rmeta", "GDB script v1"); + invalid_utf8_not_contains("libfoo.rmeta", "Natvis v1"); + invalid_utf8_contains("libfoo.rmeta", "Natvis v2"); +} diff --git a/tests/run-make/incremental-session-fail/Makefile b/tests/run-make/incremental-session-fail/Makefile deleted file mode 100644 index f43eece2eb70..000000000000 --- a/tests/run-make/incremental-session-fail/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -include ../tools.mk - -SESSION_DIR := $(TMPDIR)/session -OUTPUT_FILE := $(TMPDIR)/build-output - -all: - echo $(TMPDIR) - # Make it so that rustc will fail to create a session directory. - touch $(SESSION_DIR) - # Check exit code is 1 for an error, and not 101 for ICE. - $(RUSTC) foo.rs --crate-type=rlib -C incremental=$(SESSION_DIR) > $(OUTPUT_FILE) 2>&1; [ $$? -eq 1 ] - $(CGREP) "could not create incremental compilation crate directory" < $(OUTPUT_FILE) - # -v tests are fragile, hopefully this text won't change - $(CGREP) -v "internal compiler error" < $(OUTPUT_FILE) diff --git a/tests/run-make/incremental-session-fail/rmake.rs b/tests/run-make/incremental-session-fail/rmake.rs new file mode 100644 index 000000000000..0283709f2cf8 --- /dev/null +++ b/tests/run-make/incremental-session-fail/rmake.rs @@ -0,0 +1,15 @@ +// Failing to create the directory where output incremental +// files would be stored used to cause an ICE (Internal Compiler +// Error). This was patched in #85698, and this test checks that +// the ensuing compilation failure is not an ICE. +// See https://github.com/rust-lang/rust/pull/85698 + +use run_make_support::{fs_wrapper, rustc}; + +fn main() { + fs_wrapper::create_file("session"); + // rustc should fail to create the session directory here. + let out = rustc().input("foo.rs").crate_type("rlib").incremental("session").run_fail(); + out.assert_stderr_contains("could not create incremental compilation crate directory"); + out.assert_stderr_not_contains("internal compiler error"); +} diff --git a/tests/run-make/inline-always-many-cgu/foo.rs b/tests/run-make/inline-always-many-cgu/foo.rs index 65fe69c16fe7..b7c5371ea49c 100644 --- a/tests/run-make/inline-always-many-cgu/foo.rs +++ b/tests/run-make/inline-always-many-cgu/foo.rs @@ -2,11 +2,9 @@ pub mod a { #[inline(always)] - pub fn foo() { - } + pub fn foo() {} - pub fn bar() { - } + pub fn bar() {} } #[no_mangle] diff --git a/tests/run-make/interdependent-c-libraries/main.rs b/tests/run-make/interdependent-c-libraries/main.rs index 2aba427df474..a429030d3a2b 100644 --- a/tests/run-make/interdependent-c-libraries/main.rs +++ b/tests/run-make/interdependent-c-libraries/main.rs @@ -1,5 +1,5 @@ -extern crate foo; extern crate bar; +extern crate foo; fn main() { bar::doit(); diff --git a/tests/run-make/intrinsic-unreachable/exit-ret.rs b/tests/run-make/intrinsic-unreachable/exit-ret.rs index c8ba5b4599f2..3653316b5c67 100644 --- a/tests/run-make/intrinsic-unreachable/exit-ret.rs +++ b/tests/run-make/intrinsic-unreachable/exit-ret.rs @@ -1,4 +1,4 @@ -#![crate_type="lib"] +#![crate_type = "lib"] use std::arch::asm; #[deny(unreachable_code)] diff --git a/tests/run-make/intrinsic-unreachable/exit-unreachable.rs b/tests/run-make/intrinsic-unreachable/exit-unreachable.rs index 75f893eb2df1..d0f193d49eef 100644 --- a/tests/run-make/intrinsic-unreachable/exit-unreachable.rs +++ b/tests/run-make/intrinsic-unreachable/exit-unreachable.rs @@ -1,5 +1,5 @@ #![feature(core_intrinsics)] -#![crate_type="lib"] +#![crate_type = "lib"] use std::arch::asm; use std::intrinsics; diff --git a/tests/run-make/invalid-staticlib/Makefile b/tests/run-make/invalid-staticlib/Makefile deleted file mode 100644 index 3f0f74ce3cb0..000000000000 --- a/tests/run-make/invalid-staticlib/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -include ../tools.mk - -all: - touch $(TMPDIR)/libfoo.a - echo | $(RUSTC) - --crate-type=rlib -lstatic=foo 2>&1 | $(CGREP) "failed to add native library" diff --git a/tests/run-make/invalid-staticlib/rmake.rs b/tests/run-make/invalid-staticlib/rmake.rs new file mode 100644 index 000000000000..451292932477 --- /dev/null +++ b/tests/run-make/invalid-staticlib/rmake.rs @@ -0,0 +1,17 @@ +// If the static library provided is not valid (in this test, +// created as an empty file), +// rustc should print a normal error message and not throw +// an internal compiler error (ICE). +// See https://github.com/rust-lang/rust/pull/28673 + +use run_make_support::{fs_wrapper, rustc, static_lib_name}; + +fn main() { + fs_wrapper::create_file(static_lib_name("foo")); + rustc() + .arg("-") + .crate_type("rlib") + .arg("-lstatic=foo") + .run_fail() + .assert_stderr_contains("failed to add native library"); +} diff --git a/tests/run-make/issue-107495-archive-permissions/rmake.rs b/tests/run-make/issue-107495-archive-permissions/rmake.rs index db25e9b033c1..ee281fe0a5f0 100644 --- a/tests/run-make/issue-107495-archive-permissions/rmake.rs +++ b/tests/run-make/issue-107495-archive-permissions/rmake.rs @@ -3,8 +3,8 @@ #[cfg(unix)] extern crate libc; -use run_make_support::{aux_build, tmp_dir}; -use std::fs; +use run_make_support::{aux_build, fs_wrapper}; + #[cfg(unix)] use std::os::unix::fs::PermissionsExt; use std::path::Path; @@ -16,11 +16,11 @@ fn main() { } aux_build().arg("foo.rs").run(); - verify(&tmp_dir().join("libfoo.rlib")); + verify(Path::new("libfoo.rlib")); } fn verify(path: &Path) { - let perm = fs::metadata(path).unwrap().permissions(); + let perm = fs_wrapper::metadata(path).permissions(); assert!(!perm.readonly()); diff --git a/tests/run-make/issue-10971-temps-dir/Makefile b/tests/run-make/issue-10971-temps-dir/Makefile deleted file mode 100644 index 6e1649a58d23..000000000000 --- a/tests/run-make/issue-10971-temps-dir/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -include ../tools.mk - -# Regression test for issue #10971 -# Running two invocations in parallel would overwrite each other's temp files. - -all: - touch $(TMPDIR)/lib.rs - - $(RUSTC) --crate-type=lib -Z temps-dir=$(TMPDIR)/temp1 $(TMPDIR)/lib.rs & \ - $(RUSTC) --crate-type=staticlib -Z temps-dir=$(TMPDIR)/temp2 $(TMPDIR)/lib.rs diff --git a/tests/run-make/issue-125484-used-dependencies/dependency.rs b/tests/run-make/issue-125484-used-dependencies/dependency.rs new file mode 100644 index 000000000000..d15abba59766 --- /dev/null +++ b/tests/run-make/issue-125484-used-dependencies/dependency.rs @@ -0,0 +1 @@ +// Empty diff --git a/tests/run-make/issue-125484-used-dependencies/main.rs b/tests/run-make/issue-125484-used-dependencies/main.rs new file mode 100644 index 000000000000..0c1a1f8208ad --- /dev/null +++ b/tests/run-make/issue-125484-used-dependencies/main.rs @@ -0,0 +1,9 @@ +pub type Foo = something::same::Thing; + +mod something { + pub mod same { + pub struct Thing; + } +} + +fn main() {} diff --git a/tests/run-make/issue-125484-used-dependencies/rmake.rs b/tests/run-make/issue-125484-used-dependencies/rmake.rs new file mode 100644 index 000000000000..bc0a18de66e7 --- /dev/null +++ b/tests/run-make/issue-125484-used-dependencies/rmake.rs @@ -0,0 +1,18 @@ +// Non-regression test for issues #125474, #125484, #125646, with the repro taken from #125484. Some +// queries use "used dependencies" while others use "speculatively loaded dependencies", and an +// indexing ICE appeared in some cases when these were unexpectedly used in the same context. + +// FIXME: this should probably be a UI test instead of a run-make test, but I *cannot* find a way to +// make compiletest annotations reproduce the ICE with the minimizations from issues #125474 and +// #125484. + +use run_make_support::rustc; + +fn main() { + // The dependency is not itself significant, apart from sharing a name with one of main's + // modules. + rustc().crate_name("same").crate_type("rlib").input("dependency.rs").run(); + + // Here, an ICE would happen when building the linker command. + rustc().input("main.rs").extern_("same", "libsame.rlib").run(); +} diff --git a/tests/run-make/issue-18943/foo.rs b/tests/run-make/issue-18943/foo.rs index d18400dd3a54..54daec8dd1ea 100644 --- a/tests/run-make/issue-18943/foo.rs +++ b/tests/run-make/issue-18943/foo.rs @@ -1,5 +1,5 @@ -trait Foo { } +trait Foo {} -trait Bar { } +trait Bar {} -impl<'a> Foo for Bar + 'a { } +impl<'a> Foo for Bar + 'a {} diff --git a/tests/run-make/issue-20626/foo.rs b/tests/run-make/issue-20626/foo.rs index a474e234e72d..1007686d9fe1 100644 --- a/tests/run-make/issue-20626/foo.rs +++ b/tests/run-make/issue-20626/foo.rs @@ -1,4 +1,6 @@ -fn identity(a: &u32) -> &u32 { a } +fn identity(a: &u32) -> &u32 { + a +} fn print_foo(f: &fn(&u32) -> &u32, x: &u32) { print!("{}", (*f)(x)); diff --git a/tests/run-make/issue-22131/foo.rs b/tests/run-make/issue-22131/foo.rs index 33255d76879f..7b955a07b979 100644 --- a/tests/run-make/issue-22131/foo.rs +++ b/tests/run-make/issue-22131/foo.rs @@ -2,4 +2,6 @@ /// assert_eq!(foo::foo(), 1); /// ``` #[cfg(feature = "bar")] -pub fn foo() -> i32 { 1 } +pub fn foo() -> i32 { + 1 +} diff --git a/tests/run-make/issue-24445/Makefile b/tests/run-make/issue-24445/Makefile deleted file mode 100644 index a13910aa73ed..000000000000 --- a/tests/run-make/issue-24445/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# only-linux - -all: - $(RUSTC) foo.rs - $(CC) foo.c -lfoo -L $(TMPDIR) -Wl,--gc-sections -lpthread -ldl -o $(TMPDIR)/foo - $(call RUN,foo) - $(CC) foo.c -lfoo -L $(TMPDIR) -Wl,--gc-sections -lpthread -ldl -pie -fPIC -o $(TMPDIR)/foo - $(call RUN,foo) diff --git a/tests/run-make/issue-26006/in/libc/lib.rs b/tests/run-make/issue-26006/in/libc/lib.rs index 23f2bf51800a..bad155a99bd6 100644 --- a/tests/run-make/issue-26006/in/libc/lib.rs +++ b/tests/run-make/issue-26006/in/libc/lib.rs @@ -1,3 +1,3 @@ -#![crate_type="rlib"] +#![crate_type = "rlib"] -pub fn something(){} +pub fn something() {} diff --git a/tests/run-make/issue-26006/in/time/lib.rs b/tests/run-make/issue-26006/in/time/lib.rs index 87f2f824a366..51ed27cd713f 100644 --- a/tests/run-make/issue-26006/in/time/lib.rs +++ b/tests/run-make/issue-26006/in/time/lib.rs @@ -1,4 +1,4 @@ #![feature(rustc_private)] extern crate libc; -fn main(){} +fn main() {} diff --git a/tests/run-make/issue-26092/Makefile b/tests/run-make/issue-26092/Makefile deleted file mode 100644 index 96822e7690be..000000000000 --- a/tests/run-make/issue-26092/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -include ../tools.mk - -# This test ensures that rustc does not panic with `-o ""` option. - -all: - $(RUSTC) -o "" blank.rs 2>&1 | $(CGREP) -i 'panic' && exit 1 || exit 0 diff --git a/tests/run-make/issue-30063/Makefile b/tests/run-make/issue-30063/Makefile deleted file mode 100644 index 8a69ca79f515..000000000000 --- a/tests/run-make/issue-30063/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - rm -f $(TMPDIR)/foo-output - $(RUSTC) -C codegen-units=4 -o $(TMPDIR)/foo-output foo.rs - rm $(TMPDIR)/foo-output - - rm -f $(TMPDIR)/asm-output - $(RUSTC) -C codegen-units=4 --emit=asm -o $(TMPDIR)/asm-output foo.rs - rm $(TMPDIR)/asm-output - - rm -f $(TMPDIR)/bc-output - $(RUSTC) -C codegen-units=4 --emit=llvm-bc -o $(TMPDIR)/bc-output foo.rs - rm $(TMPDIR)/bc-output - - rm -f $(TMPDIR)/ir-output - $(RUSTC) -C codegen-units=4 --emit=llvm-ir -o $(TMPDIR)/ir-output foo.rs - rm $(TMPDIR)/ir-output - - rm -f $(TMPDIR)/link-output - $(RUSTC) -C codegen-units=4 --emit=link -o $(TMPDIR)/link-output foo.rs - rm $(TMPDIR)/link-output - - rm -f $(TMPDIR)/obj-output - $(RUSTC) -C codegen-units=4 --emit=obj -o $(TMPDIR)/obj-output foo.rs - rm $(TMPDIR)/obj-output - - rm -f $(TMPDIR)/dep-output - $(RUSTC) -C codegen-units=4 --emit=dep-info -o $(TMPDIR)/dep-output foo.rs - rm $(TMPDIR)/dep-output - -# # (This case doesn't work yet, and may be fundamentally wrong-headed anyway.) -# rm -f $(TMPDIR)/multi-output -# $(RUSTC) -C codegen-units=4 --emit=asm,obj -o $(TMPDIR)/multi-output foo.rs -# rm $(TMPDIR)/multi-output diff --git a/tests/run-make/issue-30063/foo.rs b/tests/run-make/issue-30063/foo.rs deleted file mode 100644 index 45590d86ba6c..000000000000 --- a/tests/run-make/issue-30063/foo.rs +++ /dev/null @@ -1 +0,0 @@ -fn main() { } diff --git a/tests/run-make/issue-37839/b.rs b/tests/run-make/issue-37839/b.rs index 355d2b165274..067f47c1b7a1 100644 --- a/tests/run-make/issue-37839/b.rs +++ b/tests/run-make/issue-37839/b.rs @@ -1,2 +1,3 @@ #![crate_type = "lib"] -#[macro_use] extern crate a; +#[macro_use] +extern crate a; diff --git a/tests/run-make/issue-37893/Makefile b/tests/run-make/issue-37893/Makefile deleted file mode 100644 index 44e4a321a30a..000000000000 --- a/tests/run-make/issue-37893/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) a.rs && $(RUSTC) b.rs && $(RUSTC) c.rs diff --git a/tests/run-make/issue-37893/b.rs b/tests/run-make/issue-37893/b.rs deleted file mode 100644 index 355d2b165274..000000000000 --- a/tests/run-make/issue-37893/b.rs +++ /dev/null @@ -1,2 +0,0 @@ -#![crate_type = "lib"] -#[macro_use] extern crate a; diff --git a/tests/run-make/issue-38237/Makefile b/tests/run-make/issue-38237/Makefile deleted file mode 100644 index 80dddc5bd133..000000000000 --- a/tests/run-make/issue-38237/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) foo.rs; $(RUSTC) bar.rs - $(RUSTDOC) baz.rs -L $(TMPDIR) -o $(TMPDIR) diff --git a/tests/run-make/issue-46239/Makefile b/tests/run-make/issue-46239/Makefile deleted file mode 100644 index 0006ced25156..000000000000 --- a/tests/run-make/issue-46239/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) main.rs -C opt-level=1 - $(call RUN,main) diff --git a/tests/run-make/issue-46239/main.rs b/tests/run-make/issue-46239/main.rs deleted file mode 100644 index b7df5cf4d812..000000000000 --- a/tests/run-make/issue-46239/main.rs +++ /dev/null @@ -1,8 +0,0 @@ -fn project(x: &(T,)) -> &T { &x.0 } - -fn dummy() {} - -fn main() { - let f = (dummy as fn(),); - (*project(&f))(); -} diff --git a/tests/run-make/issue-47551/Makefile b/tests/run-make/issue-47551/Makefile index 5a6ac725701b..3fe0a6e74e02 100644 --- a/tests/run-make/issue-47551/Makefile +++ b/tests/run-make/issue-47551/Makefile @@ -4,6 +4,7 @@ include ../tools.mk all: - $(RUSTC) eh_frame-terminator.rs + # --target $(TARGET) ensures the right gcc flags are used for cross compilation + $(RUSTC) --target $(TARGET) eh_frame-terminator.rs $(call RUN,eh_frame-terminator) | $(CGREP) '1122334455667788' objdump --dwarf=frames $(TMPDIR)/eh_frame-terminator | $(CGREP) 'ZERO terminator' diff --git a/tests/run-make/issue-47551/eh_frame-terminator.rs b/tests/run-make/issue-47551/eh_frame-terminator.rs index 35db4bc7d1f7..0c90d8c791c7 100644 --- a/tests/run-make/issue-47551/eh_frame-terminator.rs +++ b/tests/run-make/issue-47551/eh_frame-terminator.rs @@ -7,9 +7,7 @@ struct Foo { impl Foo { const fn new() -> Self { - Self { - array: [0x1122_3344_5566_7788; 10240] - } + Self { array: [0x1122_3344_5566_7788; 10240] } } } diff --git a/tests/run-make/issue-51671/Makefile b/tests/run-make/issue-51671/Makefile deleted file mode 100644 index c93645369928..000000000000 --- a/tests/run-make/issue-51671/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -include ../tools.mk - -# ignore-windows-msvc - -all: - $(RUSTC) --emit=obj app.rs - nm $(TMPDIR)/app.o | $(CGREP) rust_begin_unwind - nm $(TMPDIR)/app.o | $(CGREP) rust_eh_personality - nm $(TMPDIR)/app.o | $(CGREP) __rg_oom diff --git a/tests/run-make/issue-53964/Makefile b/tests/run-make/issue-53964/Makefile deleted file mode 100644 index 6bd83021374d..000000000000 --- a/tests/run-make/issue-53964/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -include ../tools.mk - -all: - $(RUSTC) panic.rs - $(RUSTC) -C panic=abort --emit=obj app.rs -L $(TMPDIR) diff --git a/tests/run-make/issue-64153/Makefile b/tests/run-make/issue-64153/Makefile deleted file mode 100644 index f42ea620fb9d..000000000000 --- a/tests/run-make/issue-64153/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -include ../tools.mk - -# `llvm-objdump`'s output looks different on windows than on other platforms. -# It should be enough to check on Unix platforms, so: -# ignore-windows - -# Staticlibs don't include Rust object files from upstream crates if the same -# code was already pulled into the lib via LTO. However, the bug described in -# https://github.com/rust-lang/rust/issues/64153 lead to this exclusion not -# working properly if the upstream crate was compiled with an explicit filename -# (via `-o`). -# -# This test makes sure that functions defined in the upstream crates do not -# appear twice in the final staticlib when listing all the symbols from it. - -all: - $(RUSTC) --crate-type rlib upstream.rs -o $(TMPDIR)/libupstream.rlib -Ccodegen-units=1 - $(RUSTC) --crate-type staticlib downstream.rs -Clto -Ccodegen-units=1 -o $(TMPDIR)/libdownstream.a - # Dump all the symbols from the staticlib into `syms` - "$(LLVM_BIN_DIR)"/llvm-objdump -t $(TMPDIR)/libdownstream.a > $(TMPDIR)/syms - # Count the global instances of `issue64153_test_function`. There'll be 2 - # if the `upstream` object file got erroneously included twice. - # The line we are testing for with the regex looks something like: - # 0000000000000000 g F .text.issue64153_test_function 00000023 issue64153_test_function - grep -c -e "[[:space:]]g[[:space:]]*F[[:space:]].*issue64153_test_function" $(TMPDIR)/syms > $(TMPDIR)/count - [ "$$(cat $(TMPDIR)/count)" -eq "1" ] diff --git a/tests/run-make/issue-69368/c.rs b/tests/run-make/issue-69368/c.rs index 729c4249a053..9d72657aa593 100644 --- a/tests/run-make/issue-69368/c.rs +++ b/tests/run-make/issue-69368/c.rs @@ -2,8 +2,8 @@ #![feature(start)] #![no_std] -extern crate alloc; extern crate a; +extern crate alloc; extern crate b; use alloc::vec::Vec; diff --git a/tests/run-make/issue-84395-lto-embed-bitcode/Makefile b/tests/run-make/issue-84395-lto-embed-bitcode/Makefile index 95c8d08a18bc..aabe90754a65 100644 --- a/tests/run-make/issue-84395-lto-embed-bitcode/Makefile +++ b/tests/run-make/issue-84395-lto-embed-bitcode/Makefile @@ -1,4 +1,7 @@ -# needs-matching-clang +# needs-force-clang-based-tests + +# FIXME(#126180): This test doesn't actually run anywhere, because the only +# CI job that sets RUSTBUILD_FORCE_CLANG_BASED_TESTS runs very few tests. # This test makes sure the embed bitcode in elf created with # lto-embed-bitcode=optimized is valid llvm BC module. diff --git a/tests/run-make/issue-85441/Makefile b/tests/run-make/issue-85441/Makefile deleted file mode 100644 index 987d7f7d4a76..000000000000 --- a/tests/run-make/issue-85441/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# only-windows-msvc - -include ../tools.mk - -# Tests that WS2_32.dll is not unnecessarily linked, see issue #85441 - -all: - $(RUSTC) empty.rs - objdump -p $(TMPDIR)/empty.exe | $(CGREP) -v -i "WS2_32.dll" diff --git a/tests/run-make/issue-88756-default-output/output-default.stdout b/tests/run-make/issue-88756-default-output/output-default.stdout index 12c1b389fb34..bc38d9e9dc6b 100644 --- a/tests/run-make/issue-88756-default-output/output-default.stdout +++ b/tests/run-make/issue-88756-default-output/output-default.stdout @@ -157,6 +157,8 @@ Options: Comma separated list of types of output for rustdoc to emit --no-run Compile doctests without running them + --remap-path-prefix FROM=TO + Remap source names in compiler messages --show-type-layout Include the memory layout of types in the docs --nocapture Don't capture stdout and stderr of tests diff --git a/tests/run-make/libtest-padding/tests.rs b/tests/run-make/libtest-padding/tests.rs index cadf07237e95..97a99b47b5c7 100644 --- a/tests/run-make/libtest-padding/tests.rs +++ b/tests/run-make/libtest-padding/tests.rs @@ -8,11 +8,7 @@ fn short_test_name() {} fn this_is_a_really_long_test_name() {} #[bench] -fn short_bench_name(b: &mut test::Bencher) { - b.iter(|| 1); -} +fn short_bench_name(b: &mut test::Bencher) {} #[bench] -fn this_is_a_really_long_bench_name(b: &mut test::Bencher) { - b.iter(|| 1); -} +fn this_is_a_really_long_bench_name(b: &mut test::Bencher) {} diff --git a/tests/run-make/link-arg/Makefile b/tests/run-make/link-arg/Makefile deleted file mode 100644 index 103527c3e14d..000000000000 --- a/tests/run-make/link-arg/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -include ../tools.mk -RUSTC_FLAGS = -C link-arg="-lfoo" -C link-arg="-lbar" --print link-args - -all: - $(RUSTC) $(RUSTC_FLAGS) empty.rs | $(CGREP) lfoo lbar diff --git a/tests/run-make/link-arg/empty.rs b/tests/run-make/link-arg/empty.rs index 45590d86ba6c..f328e4d9d04c 100644 --- a/tests/run-make/link-arg/empty.rs +++ b/tests/run-make/link-arg/empty.rs @@ -1 +1 @@ -fn main() { } +fn main() {} diff --git a/tests/run-make/link-arg/rmake.rs b/tests/run-make/link-arg/rmake.rs new file mode 100644 index 000000000000..a6d68800792f --- /dev/null +++ b/tests/run-make/link-arg/rmake.rs @@ -0,0 +1,20 @@ +// In 2016, the rustc flag "-C link-arg" was introduced - it can be repeatedly used +// to add single arguments to the linker. This test passes 2 arguments to the linker using it, +// then checks that the compiler's output contains the arguments passed to it. +// This ensures that the compiler successfully parses this flag. +// See https://github.com/rust-lang/rust/pull/36574 + +use run_make_support::rustc; + +fn main() { + // We are only checking for the output of --print=link-args, + // rustc failing or succeeding does not matter. + let out = rustc() + .input("empty.rs") + .link_arg("-lfoo") + .link_arg("-lbar") + .print("link-args") + .run_unchecked(); + out.assert_stdout_contains("lfoo"); + out.assert_stdout_contains("lbar"); +} diff --git a/tests/run-make/link-dedup/Makefile b/tests/run-make/link-dedup/Makefile deleted file mode 100644 index eff18ab48ab4..000000000000 --- a/tests/run-make/link-dedup/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# ignore-msvc - -include ../tools.mk - -all: - $(RUSTC) depa.rs - $(RUSTC) depb.rs - $(RUSTC) depc.rs - $(RUSTC) empty.rs --cfg bar 2>&1 | $(CGREP) '"-ltesta" "-ltestb" "-ltesta"' - $(RUSTC) empty.rs 2>&1 | $(CGREP) '"-ltesta"' - $(RUSTC) empty.rs 2>&1 | $(CGREP) -v '"-ltestb"' - $(RUSTC) empty.rs 2>&1 | $(CGREP) -v '"-ltesta" "-ltesta" "-ltesta"' diff --git a/tests/run-make/link-dedup/rmake.rs b/tests/run-make/link-dedup/rmake.rs new file mode 100644 index 000000000000..9bff3a4b44c7 --- /dev/null +++ b/tests/run-make/link-dedup/rmake.rs @@ -0,0 +1,24 @@ +// When native libraries are passed to the linker, there used to be an annoyance +// where multiple instances of the same library in a row would cause duplication in +// outputs. This has been fixed, and this test checks that it stays fixed. +// With the --cfg flag, -ltestb gets added to the output, breaking up the chain of -ltesta. +// Without the --cfg flag, there should be a single -ltesta, no more, no less. +// See https://github.com/rust-lang/rust/pull/84794 + +//@ ignore-msvc + +use run_make_support::rustc; + +fn main() { + rustc().input("depa.rs").run(); + rustc().input("depb.rs").run(); + rustc().input("depc.rs").run(); + let output = rustc().input("empty.rs").cfg("bar").run_fail(); + output.assert_stderr_contains(r#""-ltesta" "-ltestb" "-ltesta""#); + let output = rustc().input("empty.rs").run_fail(); + output.assert_stderr_contains(r#""-ltesta""#); + let output = rustc().input("empty.rs").run_fail(); + output.assert_stderr_not_contains(r#""-ltestb""#); + let output = rustc().input("empty.rs").run_fail(); + output.assert_stderr_not_contains(r#""-ltesta" "-ltesta" "-ltesta""#); +} diff --git a/tests/run-make/link-framework/Makefile b/tests/run-make/link-framework/Makefile index f33347ac7f8f..96d832ad4a83 100644 --- a/tests/run-make/link-framework/Makefile +++ b/tests/run-make/link-framework/Makefile @@ -1,4 +1,4 @@ -# only-macos +# only-apple # # Check that linking to a framework actually makes it to the linker. diff --git a/tests/run-make/link-path-order/main.rs b/tests/run-make/link-path-order/main.rs index 8024e343d19a..20a517dcda9b 100644 --- a/tests/run-make/link-path-order/main.rs +++ b/tests/run-make/link-path-order/main.rs @@ -1,10 +1,8 @@ -#![feature(rustc_private)] - -extern crate libc; +use std::ffi::c_int; #[link(name = "foo", kind = "static")] extern "C" { - fn should_return_one() -> libc::c_int; + fn should_return_one() -> c_int; } fn main() { diff --git a/tests/run-make/llvm-outputs/Makefile b/tests/run-make/llvm-outputs/Makefile deleted file mode 100644 index cccf1dd66fb9..000000000000 --- a/tests/run-make/llvm-outputs/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -include ../tools.mk - -all: - echo 'fn main() {}' | $(BARE_RUSTC) - --out-dir=$(TMPDIR)/random_directory_that_does_not_exist_ir/ --emit=llvm-ir - echo 'fn main() {}' | $(BARE_RUSTC) - --out-dir=$(TMPDIR)/random_directory_that_does_not_exist_bc/ --emit=llvm-bc diff --git a/tests/run-make/llvm-outputs/rmake.rs b/tests/run-make/llvm-outputs/rmake.rs new file mode 100644 index 000000000000..2dc3030ca91b --- /dev/null +++ b/tests/run-make/llvm-outputs/rmake.rs @@ -0,0 +1,20 @@ +// test that directories get created when emitting llvm bitcode and IR + +use run_make_support::{cwd, run_in_tmpdir, rustc}; +use std::path::PathBuf; + +fn main() { + let mut path_bc = PathBuf::new(); + let mut path_ir = PathBuf::new(); + run_in_tmpdir(|| { + let p = cwd(); + path_bc = p.join("nonexistant_dir_bc"); + path_ir = p.join("nonexistant_dir_ir"); + rustc().input("-").stdin("fn main() {}").out_dir(&path_bc).emit("llvm-bc").run(); + rustc().input("-").stdin("fn main() {}").out_dir(&path_ir).emit("llvm-ir").run(); + assert!(path_bc.exists()); + assert!(path_ir.exists()); + }); + assert!(!path_bc.exists()); + assert!(!path_ir.exists()); +} diff --git a/tests/run-make/long-linker-command-lines-cmd-exe/foo.rs b/tests/run-make/long-linker-command-lines-cmd-exe/foo.rs index 74d7b9b07f69..1d5202dcdb49 100644 --- a/tests/run-make/long-linker-command-lines-cmd-exe/foo.rs +++ b/tests/run-make/long-linker-command-lines-cmd-exe/foo.rs @@ -13,13 +13,13 @@ use std::env; use std::fs::{self, File}; -use std::io::{BufWriter, Write, Read}; +use std::io::{BufWriter, Read, Write}; use std::path::PathBuf; use std::process::Command; fn main() { if !cfg!(windows) { - return + return; } let tmpdir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); @@ -31,16 +31,16 @@ fn main() { let file = file.to_str().unwrap(); fs::copy(&file[1..], &ok).unwrap(); } - None => { File::create(¬_ok).unwrap(); } + None => { + File::create(¬_ok).unwrap(); + } } - return + return; } let rustc = env::var_os("RUSTC").unwrap_or("rustc".into()); let me = env::current_exe().unwrap(); - let bat = me.parent() - .unwrap() - .join("foo.bat"); + let bat = me.parent().unwrap().join("foo.bat"); let bat_linker = format!("linker={}", bat.display()); for i in (1..).map(|i| i * 10) { println!("attempt: {}", i); @@ -61,8 +61,10 @@ fn main() { drop(fs::remove_file(¬_ok)); let status = Command::new(&rustc) .arg(&file) - .arg("-C").arg(&bat_linker) - .arg("--out-dir").arg(&tmpdir) + .arg("-C") + .arg(&bat_linker) + .arg("--out-dir") + .arg(&tmpdir) .env("YOU_ARE_A_LINKER", "1") .env("MY_LINKER", &me) .status() @@ -74,7 +76,7 @@ fn main() { if !ok.exists() { assert!(not_ok.exists()); - continue + continue; } let mut contents = Vec::new(); @@ -96,6 +98,6 @@ fn main() { assert!(contents.windows(exp.len()).any(|w| w == &exp[..])); } - break + break; } } diff --git a/tests/run-make/long-linker-command-lines/foo.rs b/tests/run-make/long-linker-command-lines/foo.rs index db238c0cf1a9..9d4a701ad876 100644 --- a/tests/run-make/long-linker-command-lines/foo.rs +++ b/tests/run-make/long-linker-command-lines/foo.rs @@ -34,9 +34,7 @@ fn write_test_case(file: &Path, n: usize) -> HashSet { fn read_linker_args(path: &Path) -> String { let contents = fs::read(path).unwrap(); if cfg!(target_env = "msvc") { - let mut i = contents.chunks(2).map(|c| { - c[0] as u16 | ((c[1] as u16) << 8) - }); + let mut i = contents.chunks(2).map(|c| c[0] as u16 | ((c[1] as u16) << 8)); assert_eq!(i.next(), Some(0xfeff), "Expected UTF-16 BOM"); String::from_utf16(&i.collect::>()).unwrap() } else { @@ -52,7 +50,7 @@ fn main() { let file = file.to_str().expect("non-utf8 file argument"); fs::copy(&file[1..], &ok).unwrap(); } - return + return; } let rustc = env::var_os("RUSTC").unwrap_or("rustc".into()); @@ -65,28 +63,35 @@ fn main() { drop(fs::remove_file(&ok)); let output = Command::new(&rustc) .arg(&file) - .arg("-C").arg(&me_as_linker) - .arg("--out-dir").arg(&tmpdir) + .arg("-C") + .arg(&me_as_linker) + .arg("--out-dir") + .arg(&tmpdir) .env("YOU_ARE_A_LINKER", "1") .output() .unwrap(); if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); - panic!("status: {}\nstdout:\n{}\nstderr:\n{}", - output.status, - String::from_utf8_lossy(&output.stdout), - stderr.lines().map(|l| { - if l.len() > 200 { - format!("{}...\n", &l[..200]) - } else { - format!("{}\n", l) - } - }).collect::()); + panic!( + "status: {}\nstdout:\n{}\nstderr:\n{}", + output.status, + String::from_utf8_lossy(&output.stdout), + stderr + .lines() + .map(|l| { + if l.len() > 200 { + format!("{}...\n", &l[..200]) + } else { + format!("{}\n", l) + } + }) + .collect::() + ); } if !ok.exists() { - continue + continue; } let linker_args = read_linker_args(&ok); @@ -101,6 +106,6 @@ fn main() { linker_args, ); - break + break; } } diff --git a/tests/run-make/issue-64153/downstream.rs b/tests/run-make/lto-avoid-object-duplication/downstream.rs similarity index 100% rename from tests/run-make/issue-64153/downstream.rs rename to tests/run-make/lto-avoid-object-duplication/downstream.rs diff --git a/tests/run-make/lto-avoid-object-duplication/rmake.rs b/tests/run-make/lto-avoid-object-duplication/rmake.rs new file mode 100644 index 000000000000..b0e7494cb513 --- /dev/null +++ b/tests/run-make/lto-avoid-object-duplication/rmake.rs @@ -0,0 +1,40 @@ +// ignore-tidy-tab +// Staticlibs don't include Rust object files from upstream crates if the same +// code was already pulled into the lib via LTO. However, the bug described in +// https://github.com/rust-lang/rust/issues/64153 lead to this exclusion not +// working properly if the upstream crate was compiled with an explicit filename +// (via `-o`). + +// This test makes sure that functions defined in the upstream crates do not +// appear twice in the final staticlib when listing all the symbols from it. + +//@ ignore-windows +// Reason: `llvm-objdump`'s output looks different on windows than on other platforms. +// Only checking on Unix platforms should suffice. +//FIXME(Oneirical): This could be adapted to work on Windows by checking how +// that output differs. + +use run_make_support::{llvm_objdump, regex, rust_lib_name, rustc, static_lib_name}; + +fn main() { + rustc() + .crate_type("rlib") + .input("upstream.rs") + .output(rust_lib_name("upstream")) + .codegen_units(1) + .run(); + rustc() + .crate_type("staticlib") + .input("downstream.rs") + .arg("-Clto") + .output(static_lib_name("downstream")) + .codegen_units(1) + .run(); + let syms = llvm_objdump().arg("-t").input(static_lib_name("downstream")).run().stdout_utf8(); + let re = regex::Regex::new(r#"\s*g\s*F\s.*issue64153_test_function"#).unwrap(); + // Count the global instances of `issue64153_test_function`. There'll be 2 + // if the `upstream` object file got erroneously included twice. + // The line we are testing for with the regex looks something like: + // 0000000000000000 g F .text.issue64153_test_function 00000023 issue64153_test_function + assert_eq!(re.find_iter(syms.as_str()).count(), 1); +} diff --git a/tests/run-make/issue-64153/upstream.rs b/tests/run-make/lto-avoid-object-duplication/upstream.rs similarity index 100% rename from tests/run-make/issue-64153/upstream.rs rename to tests/run-make/lto-avoid-object-duplication/upstream.rs diff --git a/tests/run-make/lto-smoke/Makefile b/tests/run-make/lto-smoke/Makefile deleted file mode 100644 index 13a09fce7340..000000000000 --- a/tests/run-make/lto-smoke/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: noparam bool_true bool_false thin fat - -noparam: - $(RUSTC) lib.rs - $(RUSTC) main.rs -C lto - $(call RUN,main) - -bool_true: - $(RUSTC) lib.rs - $(RUSTC) main.rs -C lto=yes - $(call RUN,main) - - -bool_false: - $(RUSTC) lib.rs - $(RUSTC) main.rs -C lto=off - $(call RUN,main) - -thin: - $(RUSTC) lib.rs - $(RUSTC) main.rs -C lto=thin - $(call RUN,main) - -fat: - $(RUSTC) lib.rs - $(RUSTC) main.rs -C lto=fat - $(call RUN,main) - diff --git a/tests/run-make/lto-smoke/rmake.rs b/tests/run-make/lto-smoke/rmake.rs new file mode 100644 index 000000000000..692945135cd3 --- /dev/null +++ b/tests/run-make/lto-smoke/rmake.rs @@ -0,0 +1,16 @@ +// A simple smoke test to check that link time optimization +// (LTO) is accepted by the compiler, and that +// passing its various flags still results in successful compilation. +// See https://github.com/rust-lang/rust/issues/10741 + +//@ ignore-cross-compile + +use run_make_support::rustc; + +fn main() { + let lto_flags = ["-Clto", "-Clto=yes", "-Clto=off", "-Clto=thin", "-Clto=fat"]; + for flag in lto_flags { + rustc().input("lib.rs").run(); + rustc().input("main.rs").arg(flag).run(); + } +} diff --git a/tests/run-make/macos-fat-archive/Makefile b/tests/run-make/macos-fat-archive/Makefile index b6582c809e8b..0feb39a23cb5 100644 --- a/tests/run-make/macos-fat-archive/Makefile +++ b/tests/run-make/macos-fat-archive/Makefile @@ -1,4 +1,4 @@ -# only-macos +# only-apple include ../tools.mk diff --git a/tests/run-make/manual-crate-name/Makefile b/tests/run-make/manual-crate-name/Makefile deleted file mode 100644 index c00e20c7c57d..000000000000 --- a/tests/run-make/manual-crate-name/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -include ../tools.mk - -all: - $(RUSTC) --crate-name foo bar.rs - rm $(TMPDIR)/libfoo.rlib diff --git a/tests/run-make/manual-crate-name/rmake.rs b/tests/run-make/manual-crate-name/rmake.rs new file mode 100644 index 000000000000..f171f78087c5 --- /dev/null +++ b/tests/run-make/manual-crate-name/rmake.rs @@ -0,0 +1,7 @@ +use run_make_support::rustc; +use std::path::Path; + +fn main() { + rustc().input("bar.rs").crate_name("foo").run(); + assert!(Path::new("libfoo.rlib").is_file()); +} diff --git a/tests/run-make/many-crates-but-no-match/crateA1.rs b/tests/run-make/many-crates-but-no-match/crateA1.rs index 3fed5a38e2ca..f3025909b7de 100644 --- a/tests/run-make/many-crates-but-no-match/crateA1.rs +++ b/tests/run-make/many-crates-but-no-match/crateA1.rs @@ -1,4 +1,4 @@ -#![crate_name="crateA"] +#![crate_name = "crateA"] // Base crate pub fn func() {} diff --git a/tests/run-make/many-crates-but-no-match/crateA2.rs b/tests/run-make/many-crates-but-no-match/crateA2.rs index 8db07a015ff2..ff9f9d4d300b 100644 --- a/tests/run-make/many-crates-but-no-match/crateA2.rs +++ b/tests/run-make/many-crates-but-no-match/crateA2.rs @@ -1,4 +1,6 @@ -#![crate_name="crateA"] +#![crate_name = "crateA"] // Base crate -pub fn func() { println!("hello"); } +pub fn func() { + println!("hello"); +} diff --git a/tests/run-make/many-crates-but-no-match/crateA3.rs b/tests/run-make/many-crates-but-no-match/crateA3.rs index a1e8e40a38c2..dddf7f728eed 100644 --- a/tests/run-make/many-crates-but-no-match/crateA3.rs +++ b/tests/run-make/many-crates-but-no-match/crateA3.rs @@ -1,4 +1,6 @@ -#![crate_name="crateA"] +#![crate_name = "crateA"] // Base crate -pub fn foo() { println!("world!"); } +pub fn foo() { + println!("world!"); +} diff --git a/tests/run-make/metadata-flag-frobs-symbols/foo.rs b/tests/run-make/metadata-flag-frobs-symbols/foo.rs index 696aed2fa1db..cdc8433d8a40 100644 --- a/tests/run-make/metadata-flag-frobs-symbols/foo.rs +++ b/tests/run-make/metadata-flag-frobs-symbols/foo.rs @@ -3,4 +3,6 @@ static FOO: usize = 3; -pub fn foo() -> &'static usize { &FOO } +pub fn foo() -> &'static usize { + &FOO +} diff --git a/tests/run-make/mixing-deps/Makefile b/tests/run-make/mixing-deps/Makefile deleted file mode 100644 index c2a5a2a0abbe..000000000000 --- a/tests/run-make/mixing-deps/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) both.rs -C prefer-dynamic - $(RUSTC) dylib.rs -C prefer-dynamic - $(RUSTC) prog.rs - $(call RUN,prog) diff --git a/tests/run-make/mixing-deps/dylib.rs b/tests/run-make/mixing-deps/dylib.rs index 88976d5b663f..f3dd134249db 100644 --- a/tests/run-make/mixing-deps/dylib.rs +++ b/tests/run-make/mixing-deps/dylib.rs @@ -3,4 +3,6 @@ extern crate both; use std::mem; -pub fn addr() -> usize { unsafe { mem::transmute(&both::foo) } } +pub fn addr() -> usize { + unsafe { mem::transmute(&both::foo) } +} diff --git a/tests/run-make/mixing-deps/prog.rs b/tests/run-make/mixing-deps/prog.rs index 188981dc1a31..1e52ee16982d 100644 --- a/tests/run-make/mixing-deps/prog.rs +++ b/tests/run-make/mixing-deps/prog.rs @@ -1,9 +1,8 @@ -extern crate dylib; extern crate both; +extern crate dylib; use std::mem; fn main() { - assert_eq!(unsafe { mem::transmute::<&isize, usize>(&both::foo) }, - dylib::addr()); + assert_eq!(unsafe { mem::transmute::<&isize, usize>(&both::foo) }, dylib::addr()); } diff --git a/tests/run-make/mixing-deps/rmake.rs b/tests/run-make/mixing-deps/rmake.rs new file mode 100644 index 000000000000..fb31bbef9d19 --- /dev/null +++ b/tests/run-make/mixing-deps/rmake.rs @@ -0,0 +1,13 @@ +// This test invokes the main function in prog.rs, which has dependencies +// in both an rlib and a dylib. This test checks that these different library +// types can be successfully mixed. +//@ ignore-cross-compile + +use run_make_support::{run, rustc}; + +fn main() { + rustc().input("both.rs").arg("-Cprefer-dynamic").run(); + rustc().input("dylib.rs").arg("-Cprefer-dynamic").run(); + rustc().input("prog.rs").run(); + run("prog"); +} diff --git a/tests/run-make/mixing-formats/Makefile b/tests/run-make/mixing-formats/Makefile deleted file mode 100644 index d01978a15995..000000000000 --- a/tests/run-make/mixing-formats/Makefile +++ /dev/null @@ -1,75 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# Testing various mixings of rlibs and dylibs. Makes sure that it's possible to -# link an rlib to a dylib. The dependency tree among the file looks like: -# -# foo -# / \ -# bar1 bar2 -# / \ / -# baz baz2 -# -# This is generally testing the permutations of the foo/bar1/bar2 layer against -# the baz/baz2 layer - -all: - # Building just baz - $(RUSTC) --crate-type=rlib foo.rs - $(RUSTC) --crate-type=dylib bar1.rs -C prefer-dynamic - $(RUSTC) --crate-type=dylib,rlib baz.rs -C prefer-dynamic - $(RUSTC) --crate-type=bin baz.rs - rm $(TMPDIR)/* - $(RUSTC) --crate-type=dylib foo.rs -C prefer-dynamic - $(RUSTC) --crate-type=rlib bar1.rs - $(RUSTC) --crate-type=dylib,rlib baz.rs -C prefer-dynamic - $(RUSTC) --crate-type=bin baz.rs - rm $(TMPDIR)/* - # Building baz2 - $(RUSTC) --crate-type=rlib foo.rs - $(RUSTC) --crate-type=dylib bar1.rs -C prefer-dynamic - $(RUSTC) --crate-type=dylib bar2.rs -C prefer-dynamic - $(RUSTC) --crate-type=dylib baz2.rs && exit 1 || exit 0 - $(RUSTC) --crate-type=bin baz2.rs && exit 1 || exit 0 - rm $(TMPDIR)/* - $(RUSTC) --crate-type=rlib foo.rs - $(RUSTC) --crate-type=rlib bar1.rs - $(RUSTC) --crate-type=dylib bar2.rs -C prefer-dynamic - $(RUSTC) --crate-type=dylib,rlib baz2.rs - $(RUSTC) --crate-type=bin baz2.rs - rm $(TMPDIR)/* - $(RUSTC) --crate-type=rlib foo.rs - $(RUSTC) --crate-type=dylib bar1.rs -C prefer-dynamic - $(RUSTC) --crate-type=rlib bar2.rs - $(RUSTC) --crate-type=dylib,rlib baz2.rs -C prefer-dynamic - $(RUSTC) --crate-type=bin baz2.rs - rm $(TMPDIR)/* - $(RUSTC) --crate-type=rlib foo.rs - $(RUSTC) --crate-type=rlib bar1.rs - $(RUSTC) --crate-type=rlib bar2.rs - $(RUSTC) --crate-type=dylib,rlib baz2.rs -C prefer-dynamic - $(RUSTC) --crate-type=bin baz2.rs - rm $(TMPDIR)/* - $(RUSTC) --crate-type=dylib foo.rs -C prefer-dynamic - $(RUSTC) --crate-type=rlib bar1.rs - $(RUSTC) --crate-type=rlib bar2.rs - $(RUSTC) --crate-type=dylib,rlib baz2.rs -C prefer-dynamic - $(RUSTC) --crate-type=bin baz2.rs - rm $(TMPDIR)/* - $(RUSTC) --crate-type=dylib foo.rs -C prefer-dynamic - $(RUSTC) --crate-type=dylib bar1.rs -C prefer-dynamic - $(RUSTC) --crate-type=rlib bar2.rs - $(RUSTC) --crate-type=dylib,rlib baz2.rs - $(RUSTC) --crate-type=bin baz2.rs - rm $(TMPDIR)/* - $(RUSTC) --crate-type=dylib foo.rs -C prefer-dynamic - $(RUSTC) --crate-type=rlib bar1.rs - $(RUSTC) --crate-type=dylib bar2.rs -C prefer-dynamic - $(RUSTC) --crate-type=dylib,rlib baz2.rs - $(RUSTC) --crate-type=bin baz2.rs - rm $(TMPDIR)/* - $(RUSTC) --crate-type=dylib foo.rs -C prefer-dynamic - $(RUSTC) --crate-type=dylib bar1.rs -C prefer-dynamic - $(RUSTC) --crate-type=dylib bar2.rs -C prefer-dynamic - $(RUSTC) --crate-type=dylib,rlib baz2.rs - $(RUSTC) --crate-type=bin baz2.rs diff --git a/tests/run-make/mixing-formats/rmake.rs b/tests/run-make/mixing-formats/rmake.rs new file mode 100644 index 000000000000..968d75b10f7d --- /dev/null +++ b/tests/run-make/mixing-formats/rmake.rs @@ -0,0 +1,86 @@ +// Testing various mixings of rlibs and dylibs. Makes sure that it's possible to +// link an rlib to a dylib. The dependency tree among the file looks like: +// +// foo +// / \ +// bar1 bar2 +// / \ / +// baz baz2 +// +// This is generally testing the permutations of the foo/bar1/bar2 layer against +// the baz/baz2 layer + +//@ ignore-cross-compile + +use run_make_support::{run_in_tmpdir, rustc}; + +fn main() { + run_in_tmpdir(|| { + // Building just baz + rustc().crate_type("rlib").input("foo.rs").run(); + rustc().crate_type("dylib").input("bar1.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("dylib,rlib").input("baz.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("bin").input("baz.rs").run(); + }); + run_in_tmpdir(|| { + rustc().crate_type("dylib").input("foo.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("rlib").input("bar1.rs").run(); + rustc().crate_type("dylib,rlib").input("baz.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("bin").input("baz.rs").run(); + }); + run_in_tmpdir(|| { + // Building baz2 + rustc().crate_type("rlib").input("foo.rs").run(); + rustc().crate_type("dylib").input("bar1.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("dylib").input("bar2.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("dylib").input("baz2.rs").run_fail().assert_exit_code(1); + rustc().crate_type("bin").input("baz2.rs").run_fail().assert_exit_code(1); + }); + run_in_tmpdir(|| { + rustc().crate_type("rlib").input("foo.rs").run(); + rustc().crate_type("rlib").input("bar1.rs").run(); + rustc().crate_type("dylib").input("bar2.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("dylib,rlib").input("baz2.rs").run(); + rustc().crate_type("bin").input("baz2.rs").run(); + }); + run_in_tmpdir(|| { + rustc().crate_type("rlib").input("foo.rs").run(); + rustc().crate_type("dylib").input("bar1.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("rlib").input("bar2.rs").run(); + rustc().crate_type("dylib,rlib").input("baz2.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("bin").input("baz2.rs").run(); + }); + run_in_tmpdir(|| { + rustc().crate_type("rlib").input("foo.rs").run(); + rustc().crate_type("rlib").input("bar1.rs").run(); + rustc().crate_type("rlib").input("bar2.rs").run(); + rustc().crate_type("dylib,rlib").input("baz2.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("bin").input("baz2.rs").run(); + }); + run_in_tmpdir(|| { + rustc().crate_type("dylib").input("foo.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("rlib").input("bar1.rs").run(); + rustc().crate_type("rlib").input("bar2.rs").run(); + rustc().crate_type("dylib,rlib").input("baz2.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("bin").input("baz2.rs").run(); + }); + run_in_tmpdir(|| { + rustc().crate_type("dylib").input("foo.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("dylib").input("bar1.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("rlib").input("bar2.rs").run(); + rustc().crate_type("dylib,rlib").input("baz2.rs").run(); + rustc().crate_type("bin").input("baz2.rs").run(); + }); + run_in_tmpdir(|| { + rustc().crate_type("dylib").input("foo.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("rlib").input("bar1.rs").run(); + rustc().crate_type("dylib").input("bar2.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("dylib,rlib").input("baz2.rs").run(); + rustc().crate_type("bin").input("baz2.rs").run(); + }); + rustc().crate_type("dylib").input("foo.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("dylib").input("bar1.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("dylib").input("bar2.rs").arg("-Cprefer-dynamic").run(); + rustc().crate_type("dylib,rlib").input("baz2.rs").run(); + rustc().crate_type("bin").input("baz2.rs").run(); +} diff --git a/tests/run-make/mixing-libs/dylib.rs b/tests/run-make/mixing-libs/dylib.rs index 6856887501c2..cde5cc139ba6 100644 --- a/tests/run-make/mixing-libs/dylib.rs +++ b/tests/run-make/mixing-libs/dylib.rs @@ -1,4 +1,6 @@ #![crate_type = "dylib"] extern crate rlib; -pub fn dylib() { rlib::rlib() } +pub fn dylib() { + rlib::rlib() +} diff --git a/tests/run-make/multiple-emits/Makefile b/tests/run-make/multiple-emits/Makefile deleted file mode 100644 index d1f297644854..000000000000 --- a/tests/run-make/multiple-emits/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -include ../tools.mk - -all: - $(RUSTC) foo.rs --emit=asm,llvm-ir -o $(TMPDIR)/out 2>&1 - rm $(TMPDIR)/out.ll $(TMPDIR)/out.s - $(RUSTC) foo.rs --emit=asm,llvm-ir -o $(TMPDIR)/out2.ext 2>&1 - rm $(TMPDIR)/out2.ll $(TMPDIR)/out2.s diff --git a/tests/run-make/multiple-emits/rmake.rs b/tests/run-make/multiple-emits/rmake.rs new file mode 100644 index 000000000000..67c0ebb9864a --- /dev/null +++ b/tests/run-make/multiple-emits/rmake.rs @@ -0,0 +1,13 @@ +use run_make_support::{cwd, path, rustc}; + +fn main() { + rustc().input("foo.rs").emit("asm,llvm-ir").output("out").run(); + + assert!(path("out.ll").is_file()); + assert!(path("out.s").is_file()); + + rustc().input("foo.rs").emit("asm,llvm-ir").output("out2.ext").run(); + + assert!(path("out2.ll").is_file()); + assert!(path("out2.s").is_file()); +} diff --git a/tests/run-make/native-link-modifier-verbatim-linker/Makefile b/tests/run-make/native-link-modifier-verbatim-linker/Makefile deleted file mode 100644 index 256dc2d0664e..000000000000 --- a/tests/run-make/native-link-modifier-verbatim-linker/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# ignore-cross-compile -# ignore-macos - -include ../tools.mk - -all: - # Verbatim allows specify precise name. - $(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/local_some_strange_name.ext - $(RUSTC) main.rs -l static:+verbatim=local_some_strange_name.ext - - # With verbatim any other name cannot be used (local). - $(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/liblocal_native_dep.a - $(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/local_native_dep.a - $(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/local_native_dep.lib - $(RUSTC) main.rs -l static:+verbatim=local_native_dep 2>&1 | $(CGREP) "local_native_dep" diff --git a/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs b/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs new file mode 100644 index 000000000000..6868cb368ccc --- /dev/null +++ b/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs @@ -0,0 +1,41 @@ +// `verbatim` is a native link modifier that forces rustc to only accept libraries with +// a specified name. This test checks that this modifier works as intended. +// This test is the same as native-link-modifier-rustc, but without rlibs. +// See https://github.com/rust-lang/rust/issues/99425 + +//@ ignore-apple +// Reason: linking fails due to the unusual ".ext" staticlib name. + +use run_make_support::rustc; + +fn main() { + // Verbatim allows for the specification of a precise name + // - in this case, the unconventional ".ext" extension. + rustc() + .input("local_native_dep.rs") + .crate_type("staticlib") + .output("local_some_strange_name.ext") + .run(); + rustc().input("main.rs").arg("-lstatic:+verbatim=local_some_strange_name.ext").run(); + + // This section voluntarily avoids using static_lib_name helpers to be verbatim. + // With verbatim, even these common library names are refused + // - it wants local_native_dep without + // any file extensions. + rustc() + .input("local_native_dep.rs") + .crate_type("staticlib") + .output("liblocal_native_dep.a") + .run(); + rustc().input("local_native_dep.rs").crate_type("staticlib").output("local_native_dep.a").run(); + rustc() + .input("local_native_dep.rs") + .crate_type("staticlib") + .output("local_native_dep.lib") + .run(); + rustc() + .input("main.rs") + .arg("-lstatic:+verbatim=local_native_dep") + .run_fail() + .assert_stderr_contains("local_native_dep"); +} diff --git a/tests/run-make/native-link-modifier-verbatim-rustc/Makefile b/tests/run-make/native-link-modifier-verbatim-rustc/Makefile deleted file mode 100644 index dfd6ec50fc00..000000000000 --- a/tests/run-make/native-link-modifier-verbatim-rustc/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -include ../tools.mk - -all: - # Verbatim allows specify precise name. - $(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/upstream_some_strange_name.ext - $(RUSTC) rust_dep.rs -l static:+verbatim=upstream_some_strange_name.ext --crate-type rlib - - # With verbatim any other name cannot be used (upstream). - $(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/libupstream_native_dep.a - $(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/upstream_native_dep.a - $(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/upstream_native_dep.lib - $(RUSTC) rust_dep.rs -l static:+verbatim=upstream_native_dep --crate-type rlib 2>&1 | $(CGREP) "upstream_native_dep" diff --git a/tests/run-make/native-link-modifier-verbatim-rustc/rmake.rs b/tests/run-make/native-link-modifier-verbatim-rustc/rmake.rs new file mode 100644 index 000000000000..703b8a80ef3e --- /dev/null +++ b/tests/run-make/native-link-modifier-verbatim-rustc/rmake.rs @@ -0,0 +1,47 @@ +// `verbatim` is a native link modifier that forces rustc to only accept libraries with +// a specified name. This test checks that this modifier works as intended. +// This test is the same as native-link-modifier-linker, but with rlibs. +// See https://github.com/rust-lang/rust/issues/99425 + +use run_make_support::rustc; + +fn main() { + // Verbatim allows for the specification of a precise name + // - in this case, the unconventional ".ext" extension. + rustc() + .input("upstream_native_dep.rs") + .crate_type("staticlib") + .output("upstream_some_strange_name.ext") + .run(); + rustc() + .input("rust_dep.rs") + .crate_type("rlib") + .arg("-lstatic:+verbatim=upstream_some_strange_name.ext") + .run(); + + // This section voluntarily avoids using static_lib_name helpers to be verbatim. + // With verbatim, even these common library names are refused + // - it wants upstream_native_dep without + // any file extensions. + rustc() + .input("upstream_native_dep.rs") + .crate_type("staticlib") + .output("libupstream_native_dep.a") + .run(); + rustc() + .input("upstream_native_dep.rs") + .crate_type("staticlib") + .output("upstream_native_dep.a") + .run(); + rustc() + .input("upstream_native_dep.rs") + .crate_type("staticlib") + .output("upstream_native_dep.lib") + .run(); + rustc() + .input("rust_dep.rs") + .crate_type("rlib") + .arg("-lstatic:+verbatim=upstream_native_dep") + .run_fail() + .assert_stderr_contains("upstream_native_dep"); +} diff --git a/tests/run-make/native-link-modifier-whole-archive/native_lib_in_src.rs b/tests/run-make/native-link-modifier-whole-archive/native_lib_in_src.rs index 971f3be7a61e..d5bc0c3eb697 100644 --- a/tests/run-make/native-link-modifier-whole-archive/native_lib_in_src.rs +++ b/tests/run-make/native-link-modifier-whole-archive/native_lib_in_src.rs @@ -1,9 +1,11 @@ use std::io::Write; -#[link(name = "c_static_lib_with_constructor", - kind = "static", - modifiers = "-bundle,+whole-archive")] -extern {} +#[link( + name = "c_static_lib_with_constructor", + kind = "static", + modifiers = "-bundle,+whole-archive" +)] +extern "C" {} pub fn hello() { print!("native_lib_in_src."); diff --git a/tests/run-make/no-builtins-lto/Makefile b/tests/run-make/no-builtins-lto/Makefile deleted file mode 100644 index c8f05d9918b9..000000000000 --- a/tests/run-make/no-builtins-lto/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -include ../tools.mk - -all: - # Compile a `#![no_builtins]` rlib crate - $(RUSTC) no_builtins.rs - # Build an executable that depends on that crate using LTO. The no_builtins crate doesn't - # participate in LTO, so its rlib must be explicitly linked into the final binary. Verify this by - # grepping the linker arguments. - $(RUSTC) main.rs -C lto --print link-args | $(CGREP) 'libno_builtins.rlib' diff --git a/tests/run-make/no-builtins-lto/rmake.rs b/tests/run-make/no-builtins-lto/rmake.rs new file mode 100644 index 000000000000..8e0c3a636490 --- /dev/null +++ b/tests/run-make/no-builtins-lto/rmake.rs @@ -0,0 +1,20 @@ +// The rlib produced by a no_builtins crate should be explicitely linked +// during compilation, and as a result be present in the linker arguments. +// See the comments inside this file for more details. +// See https://github.com/rust-lang/rust/pull/35637 + +use run_make_support::{rust_lib_name, rustc}; + +fn main() { + // Compile a `#![no_builtins]` rlib crate + rustc().input("no_builtins.rs").run(); + // Build an executable that depends on that crate using LTO. The no_builtins crate doesn't + // participate in LTO, so its rlib must be explicitly + // linked into the final binary. Verify this by grepping the linker arguments. + rustc() + .input("main.rs") + .arg("-Clto") + .print("link-args") + .run() + .assert_stdout_contains(rust_lib_name("no_builtins")); +} diff --git a/tests/run-make/no-input-file/no-input-file.stderr b/tests/run-make/no-input-file/no-input-file.stderr deleted file mode 100644 index b843eb524f3d..000000000000 --- a/tests/run-make/no-input-file/no-input-file.stderr +++ /dev/null @@ -1,2 +0,0 @@ -error: no input filename given - diff --git a/tests/run-make/no-input-file/rmake.rs b/tests/run-make/no-input-file/rmake.rs index 15e582311f09..fc558b22fb49 100644 --- a/tests/run-make/no-input-file/rmake.rs +++ b/tests/run-make/no-input-file/rmake.rs @@ -1,7 +1,9 @@ -use run_make_support::{diff, rustc}; +use run_make_support::rustc; fn main() { - let output = rustc().print("crate-name").run_fail_assert_exit_code(1); - - diff().expected_file("no-input-file.stderr").actual_text("output", output.stderr).run(); + rustc() + .print("crate-name") + .run_fail() + .assert_exit_code(1) + .assert_stderr_equals("error: no input filename given"); } diff --git a/tests/run-make/no-intermediate-extras/Makefile b/tests/run-make/no-intermediate-extras/Makefile deleted file mode 100644 index 83b5cedcf2a6..000000000000 --- a/tests/run-make/no-intermediate-extras/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# ignore-cross-compile -# Regression test for issue #10973 - -include ../tools.mk - -all: - $(RUSTC) --crate-type=rlib --test foo.rs - rm $(TMPDIR)/foo.bc && exit 1 || exit 0 diff --git a/tests/run-make/no-intermediate-extras/rmake.rs b/tests/run-make/no-intermediate-extras/rmake.rs new file mode 100644 index 000000000000..0641417504ef --- /dev/null +++ b/tests/run-make/no-intermediate-extras/rmake.rs @@ -0,0 +1,17 @@ +// When using the --test flag with an rlib, this used to generate +// an unwanted .bc file, which should not exist. This test checks +// that the bug causing the generation of this file has not returned. +// See https://github.com/rust-lang/rust/issues/10973 + +//@ ignore-cross-compile + +use run_make_support::rustc; +use std::fs; + +fn main() { + rustc().crate_type("rlib").arg("--test").input("foo.rs").run(); + assert!( + fs::remove_file("foo.bc").is_err(), + "An unwanted .bc file was created by run-make/no-intermediate-extras." + ); +} diff --git a/tests/run-make/issue-24445/foo.c b/tests/run-make/non-pie-thread-local/foo.c similarity index 100% rename from tests/run-make/issue-24445/foo.c rename to tests/run-make/non-pie-thread-local/foo.c diff --git a/tests/run-make/issue-24445/foo.rs b/tests/run-make/non-pie-thread-local/foo.rs similarity index 76% rename from tests/run-make/issue-24445/foo.rs rename to tests/run-make/non-pie-thread-local/foo.rs index b67f3847cd49..30d2537750d7 100644 --- a/tests/run-make/issue-24445/foo.rs +++ b/tests/run-make/non-pie-thread-local/foo.rs @@ -2,7 +2,9 @@ struct Destroy; impl Drop for Destroy { - fn drop(&mut self) { println!("drop"); } + fn drop(&mut self) { + println!("drop"); + } } thread_local! { diff --git a/tests/run-make/non-pie-thread-local/rmake.rs b/tests/run-make/non-pie-thread-local/rmake.rs new file mode 100644 index 000000000000..1758f1a0855b --- /dev/null +++ b/tests/run-make/non-pie-thread-local/rmake.rs @@ -0,0 +1,34 @@ +// It was once required to use a position-independent executable (PIE) +// in order to use the thread_local! macro, or some symbols would contain +// a NULL address. This was fixed, and this test checks a non-PIE, then a PIE +// build to see if this bug makes a resurgence. +// See https://github.com/rust-lang/rust/pull/24448 + +//@ ignore-cross-compile +//@ only-linux + +use run_make_support::{cc, cwd, run, rustc}; + +fn main() { + rustc().input("foo.rs").run(); + cc().input("foo.c") + .arg("-lfoo") + .library_search_path(cwd()) + .arg("-Wl,--gc-sections") + .arg("-lpthread") + .arg("-ldl") + .out_exe("foo") + .run(); + run("foo"); + cc().input("foo.c") + .arg("-lfoo") + .library_search_path(cwd()) + .arg("-Wl,--gc-sections") + .arg("-lpthread") + .arg("-ldl") + .arg("-pie") + .arg("-fPIC") + .out_exe("foo") + .run(); + run("foo"); +} diff --git a/tests/run-make/non-unicode-env/rmake.rs b/tests/run-make/non-unicode-env/rmake.rs index a4843a52efd5..bffd9477f8fe 100644 --- a/tests/run-make/non-unicode-env/rmake.rs +++ b/tests/run-make/non-unicode-env/rmake.rs @@ -1,3 +1,4 @@ +use run_make_support::fs_wrapper; use run_make_support::rustc; fn main() { @@ -6,7 +7,6 @@ fn main() { #[cfg(windows)] let non_unicode: std::ffi::OsString = std::os::windows::ffi::OsStringExt::from_wide(&[0xD800]); let output = rustc().input("non_unicode_env.rs").env("NON_UNICODE_VAR", non_unicode).run_fail(); - let actual = std::str::from_utf8(&output.stderr).unwrap(); - let expected = std::fs::read_to_string("non_unicode_env.stderr").unwrap(); - assert_eq!(actual, expected); + let expected = fs_wrapper::read_to_string("non_unicode_env.stderr"); + output.assert_stderr_equals(expected); } diff --git a/tests/run-make/non-unicode-in-incremental-dir/rmake.rs b/tests/run-make/non-unicode-in-incremental-dir/rmake.rs index 40152e0411d3..895d9e00a2d4 100644 --- a/tests/run-make/non-unicode-in-incremental-dir/rmake.rs +++ b/tests/run-make/non-unicode-in-incremental-dir/rmake.rs @@ -1,24 +1,24 @@ -use run_make_support::{rustc, tmp_dir}; +use run_make_support::{fs_wrapper, rustc}; fn main() { #[cfg(unix)] let non_unicode: &std::ffi::OsStr = std::os::unix::ffi::OsStrExt::from_bytes(&[0xFF]); #[cfg(windows)] let non_unicode: std::ffi::OsString = std::os::windows::ffi::OsStringExt::from_wide(&[0xD800]); - match std::fs::create_dir(tmp_dir().join(&non_unicode)) { + match std::fs::create_dir(&non_unicode) { // If an error occurs, check if creating a directory with a valid Unicode name would // succeed. - Err(e) if std::fs::create_dir(tmp_dir().join("valid_unicode")).is_ok() => { + Err(e) if std::fs::create_dir("valid_unicode").is_ok() => { // Filesystem doesn't appear support non-Unicode paths. return; } Err(e) => panic!("error creating non-Unicode directory: {e}"), _ => {} } - let incr_dir = tmp_dir().join("incr-dir"); + let incr_dir = "incr-dir"; rustc().input("foo.rs").incremental(&incr_dir).run(); - for crate_dir in std::fs::read_dir(&incr_dir).unwrap() { - std::fs::create_dir(crate_dir.unwrap().path().join(&non_unicode)).unwrap(); + for crate_dir in fs_wrapper::read_dir(&incr_dir) { + fs_wrapper::create_dir(crate_dir.unwrap().path().join(&non_unicode)); } rustc().input("foo.rs").incremental(&incr_dir).run(); } diff --git a/tests/run-make/notify-all-emit-artifacts/lib.rs b/tests/run-make/notify-all-emit-artifacts/lib.rs new file mode 100644 index 000000000000..6ed194204b45 --- /dev/null +++ b/tests/run-make/notify-all-emit-artifacts/lib.rs @@ -0,0 +1,21 @@ +fn one() -> usize { + 1 +} + +pub mod a { + pub fn two() -> usize { + ::one() + ::one() + } +} + +pub mod b { + pub fn three() -> usize { + ::one() + ::a::two() + } +} + +#[inline(never)] +pub fn main() { + a::two(); + b::three(); +} diff --git a/tests/run-make/notify-all-emit-artifacts/rmake.rs b/tests/run-make/notify-all-emit-artifacts/rmake.rs new file mode 100644 index 000000000000..321eda489410 --- /dev/null +++ b/tests/run-make/notify-all-emit-artifacts/rmake.rs @@ -0,0 +1,43 @@ +// rust should produce artifact notifications about files it was asked to --emit. +// +// It should work in incremental mode both on the first pass where files are generated as well +// as on subsequent passes where they are taken from the incremental cache +// +// See +extern crate run_make_support; + +use run_make_support::{cwd, rustc}; + +fn main() { + // With single codegen unit files are renamed to match the source file name + for _ in 0..=1 { + let output = rustc() + .input("lib.rs") + .emit("obj,asm,llvm-ir,llvm-bc,mir") + .codegen_units(1) + .json("artifacts") + .error_format("json") + .incremental(cwd()) + .run(); + let stderr = output.stderr_utf8(); + for file in &["lib.o", "lib.ll", "lib.bc", "lib.s"] { + assert!(stderr.contains(file), "No {:?} in {:?}", file, stderr); + } + } + + // with multiple codegen units files keep codegen unit id part. + for _ in 0..=1 { + let output = rustc() + .input("lib.rs") + .emit("obj,asm,llvm-ir,llvm-bc,mir") + .codegen_units(2) + .json("artifacts") + .error_format("json") + .incremental(cwd()) + .run(); + let stderr = output.stderr_utf8(); + for file in &["rcgu.o", "rcgu.ll", "rcgu.bc", "rcgu.s"] { + assert!(stderr.contains(file), "No {:?} in {:?}", file, stderr); + } + } +} diff --git a/tests/run-make/optimization-remarks-dir-pgo/Makefile b/tests/run-make/optimization-remarks-dir-pgo/Makefile index 3bc3d7d14288..57ffd6e70f00 100644 --- a/tests/run-make/optimization-remarks-dir-pgo/Makefile +++ b/tests/run-make/optimization-remarks-dir-pgo/Makefile @@ -1,10 +1,6 @@ # needs-profiler-support -# ignore-windows-gnu # ignore-cross-compile -# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works -# properly. Since we only have GCC on the CI ignore the test for now. - include ../tools.mk PROFILE_DIR=$(TMPDIR)/profiles diff --git a/tests/run-make/output-filename-conflicts-with-directory/Makefile b/tests/run-make/output-filename-conflicts-with-directory/Makefile deleted file mode 100644 index 45221356cd97..000000000000 --- a/tests/run-make/output-filename-conflicts-with-directory/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -include ../tools.mk - -all: - cp foo.rs $(TMPDIR)/foo.rs - mkdir $(TMPDIR)/foo - $(RUSTC) $(TMPDIR)/foo.rs -o $(TMPDIR)/foo 2>&1 \ - | $(CGREP) -e "the generated executable for the input file \".*foo\.rs\" conflicts with the existing directory \".*foo\"" diff --git a/tests/run-make/output-filename-conflicts-with-directory/rmake.rs b/tests/run-make/output-filename-conflicts-with-directory/rmake.rs new file mode 100644 index 000000000000..4b5c9e8d1187 --- /dev/null +++ b/tests/run-make/output-filename-conflicts-with-directory/rmake.rs @@ -0,0 +1,14 @@ +// ignore-tidy-linelength +// When the compiled executable would conflict with a directory, a +// rustc error should be displayed instead of a verbose and +// potentially-confusing linker error. +// See https://github.com/rust-lang/rust/pull/47203 + +use run_make_support::{fs_wrapper, rustc}; + +fn main() { + fs_wrapper::create_dir("foo"); + rustc().input("foo.rs").output("foo").run_fail().assert_stderr_contains( + r#"the generated executable for the input file "foo.rs" conflicts with the existing directory "foo""#, + ); +} diff --git a/tests/run-make/output-filename-overwrites-input/Makefile b/tests/run-make/output-filename-overwrites-input/Makefile deleted file mode 100644 index fe5d231382dc..000000000000 --- a/tests/run-make/output-filename-overwrites-input/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - cp foo.rs $(TMPDIR)/foo - $(RUSTC) $(TMPDIR)/foo -o $(TMPDIR)/foo 2>&1 \ - | $(CGREP) -e "the input file \".*foo\" would be overwritten by the generated executable" - cp bar.rs $(TMPDIR)/bar.rlib - $(RUSTC) $(TMPDIR)/bar.rlib -o $(TMPDIR)/bar.rlib 2>&1 \ - | $(CGREP) -e "the input file \".*bar.rlib\" would be overwritten by the generated executable" - $(RUSTC) foo.rs 2>&1 && $(RUSTC) -Z ls=root $(TMPDIR)/foo 2>&1 - cp foo.rs $(TMPDIR)/foo.rs - $(RUSTC) $(TMPDIR)/foo.rs -o $(TMPDIR)/foo.rs 2>&1 \ - | $(CGREP) -e "the input file \".*foo.rs\" would be overwritten by the generated executable" diff --git a/tests/run-make/output-filename-overwrites-input/rmake.rs b/tests/run-make/output-filename-overwrites-input/rmake.rs new file mode 100644 index 000000000000..c6055e818a17 --- /dev/null +++ b/tests/run-make/output-filename-overwrites-input/rmake.rs @@ -0,0 +1,21 @@ +// If rustc is invoked on a file that would be overwritten by the +// compilation, the compilation should fail, to avoid accidental loss. +// See https://github.com/rust-lang/rust/pull/46814 + +//@ ignore-cross-compile + +use run_make_support::{fs_wrapper, rustc}; + +fn main() { + fs_wrapper::copy("foo.rs", "foo"); + rustc().input("foo").output("foo").run_fail().assert_stderr_contains( + r#"the input file "foo" would be overwritten by the generated executable"#, + ); + fs_wrapper::copy("bar.rs", "bar.rlib"); + rustc().input("bar.rlib").output("bar.rlib").run_fail().assert_stderr_contains( + r#"the input file "bar.rlib" would be overwritten by the generated executable"#, + ); + rustc().input("foo.rs").output("foo.rs").run_fail().assert_stderr_contains( + r#"the input file "foo.rs" would be overwritten by the generated executable"#, + ); +} diff --git a/tests/run-make/output-with-hyphens/Makefile b/tests/run-make/output-with-hyphens/Makefile deleted file mode 100644 index 846c9a66a89a..000000000000 --- a/tests/run-make/output-with-hyphens/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) foo-bar.rs --crate-type bin - [ -f $(TMPDIR)/$(call BIN,foo-bar) ] - $(RUSTC) foo-bar.rs --crate-type lib - [ -f $(TMPDIR)/libfoo_bar.rlib ] diff --git a/tests/run-make/output-with-hyphens/rmake.rs b/tests/run-make/output-with-hyphens/rmake.rs new file mode 100644 index 000000000000..79deb772bc5d --- /dev/null +++ b/tests/run-make/output-with-hyphens/rmake.rs @@ -0,0 +1,17 @@ +// Rust files with hyphens in their filename should +// not result in compiled libraries keeping that hyphen - +// it should become an underscore. Only bin executables +// should keep the hyphen. This test ensures that this rule +// remains enforced. +// See https://github.com/rust-lang/rust/pull/23786 + +//@ ignore-cross-compile + +use run_make_support::{bin_name, path, rust_lib_name, rustc}; + +fn main() { + rustc().input("foo-bar.rs").crate_type("bin").run(); + assert!(path(bin_name("foo-bar")).exists()); + rustc().input("foo-bar.rs").crate_type("lib").run(); + assert!(path(rust_lib_name("foo_bar")).exists()); +} diff --git a/tests/run-make/panic-impl-transitive/rmake.rs b/tests/run-make/panic-impl-transitive/rmake.rs index 86308f593b31..c09b233b23e1 100644 --- a/tests/run-make/panic-impl-transitive/rmake.rs +++ b/tests/run-make/panic-impl-transitive/rmake.rs @@ -6,14 +6,9 @@ // function in the crate. // See https://github.com/rust-lang/rust/pull/50338 -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; fn main() { rustc().input("panic-impl-provider.rs").run(); - rustc() - .input("panic-impl-consumer.rs") - .panic("abort") - .emit("llvm-ir") - .library_search_path(tmp_dir()) - .run(); + rustc().input("panic-impl-consumer.rs").panic("abort").emit("llvm-ir").run(); } diff --git a/tests/run-make/parallel-rustc-no-overwrite/rmake.rs b/tests/run-make/parallel-rustc-no-overwrite/rmake.rs new file mode 100644 index 000000000000..3f032cf3762a --- /dev/null +++ b/tests/run-make/parallel-rustc-no-overwrite/rmake.rs @@ -0,0 +1,25 @@ +// When two instances of rustc are invoked in parallel, they +// can conflict on their temporary files and overwrite each others', +// leading to unsuccessful compilation. The -Z temps-dir flag adds +// separate designated directories for each rustc invocation, preventing +// conflicts. This test uses this flag and checks for successful compilation. +// See https://github.com/rust-lang/rust/pull/83846 + +use run_make_support::{fs_wrapper, rustc}; +use std::sync::{Arc, Barrier}; +use std::thread; + +fn main() { + fs_wrapper::create_file("lib.rs"); + let barrier = Arc::new(Barrier::new(2)); + let handle = { + let barrier = Arc::clone(&barrier); + thread::spawn(move || { + barrier.wait(); + rustc().crate_type("lib").arg("-Ztemps-dir=temp1").input("lib.rs").run(); + }) + }; + barrier.wait(); + rustc().crate_type("staticlib").arg("-Ztemps-dir=temp2").input("lib.rs").run(); + handle.join().expect("lib thread panicked"); +} diff --git a/tests/run-make/pass-non-c-like-enum-to-c/nonclike.rs b/tests/run-make/pass-non-c-like-enum-to-c/nonclike.rs index 517286a868d8..7b5c620b3381 100644 --- a/tests/run-make/pass-non-c-like-enum-to-c/nonclike.rs +++ b/tests/run-make/pass-non-c-like-enum-to-c/nonclike.rs @@ -4,7 +4,7 @@ pub enum TT { BB, } -#[repr(C,u8)] +#[repr(C, u8)] pub enum T { A(u64), B, @@ -16,6 +16,6 @@ extern "C" { } fn main() { - assert_eq!(33, unsafe { tt_add(TT::AA(1,2), TT::AA(10,20)) }); + assert_eq!(33, unsafe { tt_add(TT::AA(1, 2), TT::AA(10, 20)) }); assert_eq!(11, unsafe { t_add(T::A(1), T::A(10)) }); } diff --git a/tests/run-make/pdb-buildinfo-cl-cmd/main.rs b/tests/run-make/pdb-buildinfo-cl-cmd/main.rs index f79c691f0853..f328e4d9d04c 100644 --- a/tests/run-make/pdb-buildinfo-cl-cmd/main.rs +++ b/tests/run-make/pdb-buildinfo-cl-cmd/main.rs @@ -1,2 +1 @@ -fn main() { -} +fn main() {} diff --git a/tests/run-make/pgo-branch-weights/Makefile b/tests/run-make/pgo-branch-weights/Makefile deleted file mode 100644 index 4c9f8b2493ab..000000000000 --- a/tests/run-make/pgo-branch-weights/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -# needs-profiler-support -# ignore-windows-gnu -# ignore-cross-compile - -# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works -# properly. Since we only have GCC on the CI ignore the test for now. - -include ../tools.mk - -# For some very small programs GNU ld seems to not properly handle -# instrumentation sections correctly. Neither Gold nor LLD have that problem. -ifeq ($(UNAME),Linux) -ifneq (,$(findstring x86,$(TARGET))) -COMMON_FLAGS=-Clink-args=-fuse-ld=gold -endif -endif - - -all: - # We don't compile `opaque` with either optimizations or instrumentation. - $(RUSTC) $(COMMON_FLAGS) opaque.rs || exit 1 - # Compile the test program with instrumentation - mkdir -p "$(TMPDIR)/prof_data_dir" || exit 1 - $(RUSTC) $(COMMON_FLAGS) interesting.rs \ - -Cprofile-generate="$(TMPDIR)/prof_data_dir" -O -Ccodegen-units=1 || exit 1 - $(RUSTC) $(COMMON_FLAGS) main.rs -Cprofile-generate="$(TMPDIR)/prof_data_dir" -O || exit 1 - # The argument below generates to the expected branch weights - $(call RUN,main aaaaaaaaaaaa2bbbbbbbbbbbb2bbbbbbbbbbbbbbbbcc) || exit 1 - "$(LLVM_BIN_DIR)/llvm-profdata" merge \ - -o "$(TMPDIR)/prof_data_dir/merged.profdata" \ - "$(TMPDIR)/prof_data_dir" || exit 1 - $(RUSTC) $(COMMON_FLAGS) interesting.rs \ - -Cprofile-use="$(TMPDIR)/prof_data_dir/merged.profdata" -O \ - -Ccodegen-units=1 --emit=llvm-ir || exit 1 - cat "$(TMPDIR)/interesting.ll" | "$(LLVM_FILECHECK)" filecheck-patterns.txt diff --git a/tests/run-make/pgo-branch-weights/interesting.rs b/tests/run-make/pgo-branch-weights/interesting.rs index a26d6fd69d14..7066496f4beb 100644 --- a/tests/run-make/pgo-branch-weights/interesting.rs +++ b/tests/run-make/pgo-branch-weights/interesting.rs @@ -1,5 +1,5 @@ -#![crate_name="interesting"] -#![crate_type="rlib"] +#![crate_name = "interesting"] +#![crate_type = "rlib"] extern crate opaque; @@ -22,7 +22,6 @@ pub fn function_called_42_times(c: char) { // This branch is taken 12 times opaque::f1(); } else { - if c == 'b' { // This branch is taken 28 times opaque::f2(); diff --git a/tests/run-make/pgo-branch-weights/opaque.rs b/tests/run-make/pgo-branch-weights/opaque.rs index 72f93c9feab6..39dedf0f7bf5 100644 --- a/tests/run-make/pgo-branch-weights/opaque.rs +++ b/tests/run-make/pgo-branch-weights/opaque.rs @@ -1,5 +1,5 @@ -#![crate_name="opaque"] -#![crate_type="rlib"] +#![crate_name = "opaque"] +#![crate_type = "rlib"] pub fn f1() {} pub fn f2() {} diff --git a/tests/run-make/pgo-branch-weights/rmake.rs b/tests/run-make/pgo-branch-weights/rmake.rs new file mode 100644 index 000000000000..d3cb79c39afa --- /dev/null +++ b/tests/run-make/pgo-branch-weights/rmake.rs @@ -0,0 +1,41 @@ +// This test generates an instrumented binary - a program which +// will keep track of how many times it calls each function, a useful +// feature for optimization. Then, an argument (aaaaaaaaaaaa2bbbbbbbbbbbb2bbbbbbbbbbbbbbbbcc) +// is passed into the instrumented binary, which should react with a number of function calls +// fully known in advance. (For example, the letter 'a' results in calling f1()) + +// If the test passes, the expected function call count was added to the use-phase LLVM-IR. +// See https://github.com/rust-lang/rust/pull/66631 + +//@ needs-profiler-support +//@ ignore-cross-compile + +use run_make_support::{fs_wrapper, llvm_filecheck, llvm_profdata, run_with_args, rustc}; +use std::path::Path; + +fn main() { + let path_prof_data_dir = Path::new("prof_data_dir"); + let path_merged_profdata = path_prof_data_dir.join("merged.profdata"); + rustc().input("opaque.rs").run(); + fs_wrapper::create_dir_all(&path_prof_data_dir); + rustc() + .input("interesting.rs") + .profile_generate(&path_prof_data_dir) + .opt() + .codegen_units(1) + .run(); + rustc().input("main.rs").profile_generate(&path_prof_data_dir).opt().run(); + run_with_args("main", &["aaaaaaaaaaaa2bbbbbbbbbbbb2bbbbbbbbbbbbbbbbcc"]); + llvm_profdata().merge().output(&path_merged_profdata).input(path_prof_data_dir).run(); + rustc() + .input("interesting.rs") + .profile_use(path_merged_profdata) + .opt() + .codegen_units(1) + .emit("llvm-ir") + .run(); + llvm_filecheck() + .patterns("filecheck-patterns.txt") + .stdin(fs_wrapper::read("interesting.ll")) + .run(); +} diff --git a/tests/run-make/pgo-gen-lto/Makefile b/tests/run-make/pgo-gen-lto/Makefile index 8b647846af3b..54164c995222 100644 --- a/tests/run-make/pgo-gen-lto/Makefile +++ b/tests/run-make/pgo-gen-lto/Makefile @@ -1,10 +1,6 @@ # needs-profiler-support -# ignore-windows-gnu # ignore-cross-compile -# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works -# properly. Since we only have GCC on the CI ignore the test for now. - include ../tools.mk COMPILE_FLAGS=-Copt-level=3 -Clto=fat -Cprofile-generate="$(TMPDIR)" diff --git a/tests/run-make/pgo-gen-no-imp-symbols/Makefile b/tests/run-make/pgo-gen-no-imp-symbols/Makefile index 7f72b11b611e..d2baa145ba50 100644 --- a/tests/run-make/pgo-gen-no-imp-symbols/Makefile +++ b/tests/run-make/pgo-gen-no-imp-symbols/Makefile @@ -1,8 +1,6 @@ -# needs-profiler-support - include ../tools.mk -COMPILE_FLAGS=-O -Ccodegen-units=1 -Cprofile-generate="$(TMPDIR)" +COMPILE_FLAGS=-O -Ccodegen-units=1 -Cprofile-generate="$(TMPDIR)" -Zno-profiler-runtime all: $(RUSTC) $(COMPILE_FLAGS) --emit=llvm-ir test.rs diff --git a/tests/run-make/pgo-gen/Makefile b/tests/run-make/pgo-gen/Makefile index bf32cfdb802f..c1d456986fb2 100644 --- a/tests/run-make/pgo-gen/Makefile +++ b/tests/run-make/pgo-gen/Makefile @@ -1,10 +1,6 @@ # needs-profiler-support -# ignore-windows-gnu # ignore-cross-compile -# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works -# properly. Since we only have GCC on the CI ignore the test for now. - include ../tools.mk COMPILE_FLAGS=-g -Cprofile-generate="$(TMPDIR)" diff --git a/tests/run-make/pgo-indirect-call-promotion/Makefile b/tests/run-make/pgo-indirect-call-promotion/Makefile index 542eb244d395..8d1e69c4aba3 100644 --- a/tests/run-make/pgo-indirect-call-promotion/Makefile +++ b/tests/run-make/pgo-indirect-call-promotion/Makefile @@ -1,10 +1,6 @@ # needs-profiler-support -# ignore-windows-gnu # ignore-cross-compile -# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works -# properly. Since we only have GCC on the CI ignore the test for now. - include ../tools.mk all: diff --git a/tests/run-make/pgo-indirect-call-promotion/interesting.rs b/tests/run-make/pgo-indirect-call-promotion/interesting.rs index 4fd096d626de..398017f9611b 100644 --- a/tests/run-make/pgo-indirect-call-promotion/interesting.rs +++ b/tests/run-make/pgo-indirect-call-promotion/interesting.rs @@ -1,5 +1,5 @@ -#![crate_name="interesting"] -#![crate_type="rlib"] +#![crate_name = "interesting"] +#![crate_type = "rlib"] extern crate opaque; @@ -15,7 +15,6 @@ pub fn function_called_never() { #[no_mangle] pub fn call_a_bunch_of_functions(fns: &[fn()]) { - // Indirect call promotion transforms the below into something like // // for f in fns { @@ -33,13 +32,11 @@ pub fn call_a_bunch_of_functions(fns: &[fn()]) { } } - pub trait Foo { fn foo(&self); } impl Foo for u32 { - #[no_mangle] fn foo(&self) { opaque::opaque_f2(); @@ -48,7 +45,6 @@ impl Foo for u32 { #[no_mangle] pub fn call_a_bunch_of_trait_methods(trait_objects: &[&dyn Foo]) { - // Same as above, just with vtables in between for x in trait_objects { x.foo(); diff --git a/tests/run-make/pgo-indirect-call-promotion/main.rs b/tests/run-make/pgo-indirect-call-promotion/main.rs index 27181f30710b..604f1f748737 100644 --- a/tests/run-make/pgo-indirect-call-promotion/main.rs +++ b/tests/run-make/pgo-indirect-call-promotion/main.rs @@ -2,9 +2,8 @@ extern crate interesting; fn main() { // function pointer case - let fns: Vec<_> = std::iter::repeat(interesting::function_called_always as fn()) - .take(1000) - .collect(); + let fns: Vec<_> = + std::iter::repeat(interesting::function_called_always as fn()).take(1000).collect(); interesting::call_a_bunch_of_functions(&fns[..]); // Trait object case diff --git a/tests/run-make/pgo-indirect-call-promotion/opaque.rs b/tests/run-make/pgo-indirect-call-promotion/opaque.rs index 9628d711c505..d7b2e810bed1 100644 --- a/tests/run-make/pgo-indirect-call-promotion/opaque.rs +++ b/tests/run-make/pgo-indirect-call-promotion/opaque.rs @@ -1,5 +1,5 @@ -#![crate_name="opaque"] -#![crate_type="rlib"] +#![crate_name = "opaque"] +#![crate_type = "rlib"] #[no_mangle] pub fn opaque_f1() {} diff --git a/tests/run-make/pgo-use/Makefile b/tests/run-make/pgo-use/Makefile index 9f440118daee..92098a4019c4 100644 --- a/tests/run-make/pgo-use/Makefile +++ b/tests/run-make/pgo-use/Makefile @@ -1,10 +1,6 @@ # needs-profiler-support -# ignore-windows-gnu # ignore-cross-compile -# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works -# properly. Since we only have GCC on the CI ignore the test for now. - include ../tools.mk # This test makes sure that PGO profiling data leads to cold functions being diff --git a/tests/run-make/pgo-use/main.rs b/tests/run-make/pgo-use/main.rs index eb9192c87e6f..6150cff4d63a 100644 --- a/tests/run-make/pgo-use/main.rs +++ b/tests/run-make/pgo-use/main.rs @@ -11,7 +11,7 @@ pub fn hot_function(c: u8) { fn main() { let arg = std::env::args().skip(1).next().unwrap(); - for i in 0 .. 1000_000 { + for i in 0..1000_000 { let some_value = arg.as_bytes()[i % arg.len()]; if some_value == b'!' { // This branch is never taken at runtime diff --git a/tests/run-make/pointer-auth-link-with-c/test.rs b/tests/run-make/pointer-auth-link-with-c/test.rs index 615ad0aeb3d0..1a3be80e898b 100644 --- a/tests/run-make/pointer-auth-link-with-c/test.rs +++ b/tests/run-make/pointer-auth-link-with-c/test.rs @@ -4,5 +4,7 @@ extern "C" { } fn main() { - unsafe {foo();} + unsafe { + foo(); + } } diff --git a/tests/run-make/prefer-dylib/Makefile b/tests/run-make/prefer-dylib/Makefile deleted file mode 100644 index cc26e70ae67c..000000000000 --- a/tests/run-make/prefer-dylib/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) bar.rs --crate-type=dylib --crate-type=rlib -C prefer-dynamic - $(RUSTC) foo.rs -C prefer-dynamic - $(call RUN,foo) - rm $(TMPDIR)/*bar* - $(call FAIL,foo) diff --git a/tests/run-make/prefer-dylib/rmake.rs b/tests/run-make/prefer-dylib/rmake.rs new file mode 100644 index 000000000000..6b3b3ad6d3b8 --- /dev/null +++ b/tests/run-make/prefer-dylib/rmake.rs @@ -0,0 +1,14 @@ +//@ ignore-cross-compile + +use run_make_support::{cwd, dynamic_lib_name, fs_wrapper, read_dir, run, run_fail, rustc}; + +fn main() { + rustc().input("bar.rs").crate_type("dylib").crate_type("rlib").arg("-Cprefer-dynamic").run(); + rustc().input("foo.rs").arg("-Cprefer-dynamic").run(); + + run("foo"); + + fs_wrapper::remove_file(dynamic_lib_name("bar")); + // This time the command should fail. + run_fail("foo"); +} diff --git a/tests/run-make/prefer-rlib/Makefile b/tests/run-make/prefer-rlib/Makefile deleted file mode 100644 index 2e86b9c1dd7e..000000000000 --- a/tests/run-make/prefer-rlib/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) bar.rs --crate-type=dylib --crate-type=rlib - ls $(TMPDIR)/$(call RLIB_GLOB,bar) - $(RUSTC) foo.rs - rm $(TMPDIR)/*bar* - $(call RUN,foo) diff --git a/tests/run-make/prefer-rlib/rmake.rs b/tests/run-make/prefer-rlib/rmake.rs new file mode 100644 index 000000000000..96861a264e62 --- /dev/null +++ b/tests/run-make/prefer-rlib/rmake.rs @@ -0,0 +1,15 @@ +// Check that `foo.rs` prefers to link to `bar` statically, and can be executed even if the `bar` +// library artifacts are removed. + +//@ ignore-cross-compile + +use run_make_support::{dynamic_lib_name, fs_wrapper, path, run, rust_lib_name, rustc}; + +fn main() { + rustc().input("bar.rs").crate_type("dylib").crate_type("rlib").run(); + assert!(path(rust_lib_name("bar")).exists()); + rustc().input("foo.rs").run(); + fs_wrapper::remove_file(rust_lib_name("bar")); + fs_wrapper::remove_file(dynamic_lib_name("bar")); + run("foo"); +} diff --git a/tests/run-make/pretty-print-to-file/input.rs b/tests/run-make/pretty-print-to-file/input.rs index aa828155b205..ce23804e4307 100644 --- a/tests/run-make/pretty-print-to-file/input.rs +++ b/tests/run-make/pretty-print-to-file/input.rs @@ -1,5 +1,5 @@ -#[crate_type="lib"] +#[crate_type = "lib"] -pub fn -foo() -> i32 -{ 45 } +pub fn foo() -> i32 { + 45 +} diff --git a/tests/run-make/print-cfg/rmake.rs b/tests/run-make/print-cfg/rmake.rs index 6e72c16f1f9a..d11eda1db2ae 100644 --- a/tests/run-make/print-cfg/rmake.rs +++ b/tests/run-make/print-cfg/rmake.rs @@ -7,10 +7,10 @@ use std::collections::HashSet; use std::ffi::OsString; -use std::io::BufRead; use std::iter::FromIterator; +use std::path::PathBuf; -use run_make_support::{rustc, tmp_dir}; +use run_make_support::{fs_wrapper, rustc}; struct PrintCfg { target: &'static str, @@ -83,21 +83,20 @@ fn check(PrintCfg { target, includes, disallow }: PrintCfg) { // --print=cfg { let output = rustc().target(target).print("cfg").run(); - - let stdout = String::from_utf8(output.stdout).unwrap(); + let stdout = output.stdout_utf8(); check_(&stdout, includes, disallow); } // --print=cfg=PATH { - let tmp_path = tmp_dir().join(format!("{target}.cfg")); + let tmp_path = PathBuf::from(format!("{target}.cfg")); let mut print_arg = OsString::from("--print=cfg="); print_arg.push(tmp_path.as_os_str()); - let output = rustc().target(target).arg(print_arg).run(); + rustc().target(target).arg(print_arg).run(); - let output = std::fs::read_to_string(&tmp_path).unwrap(); + let output = fs_wrapper::read_to_string(&tmp_path); check_(&output, includes, disallow); } diff --git a/tests/run-make/print-check-cfg/lib.rs b/tests/run-make/print-check-cfg/lib.rs new file mode 100644 index 000000000000..c09e4c98dffd --- /dev/null +++ b/tests/run-make/print-check-cfg/lib.rs @@ -0,0 +1 @@ +// empty crate diff --git a/tests/run-make/print-check-cfg/rmake.rs b/tests/run-make/print-check-cfg/rmake.rs new file mode 100644 index 000000000000..f4b02b5e265b --- /dev/null +++ b/tests/run-make/print-check-cfg/rmake.rs @@ -0,0 +1,144 @@ +//! This checks the output of `--print=check-cfg` + +extern crate run_make_support; + +use std::collections::HashSet; +use std::iter::FromIterator; +use std::ops::Deref; + +use run_make_support::rustc; + +struct CheckCfg { + args: &'static [&'static str], + contains: Contains, +} + +enum Contains { + Some { contains: &'static [&'static str], doesnt_contain: &'static [&'static str] }, + Only(&'static str), +} + +fn main() { + check(CheckCfg { args: &[], contains: Contains::Only("any()=any()") }); + check(CheckCfg { + args: &["--check-cfg=cfg()"], + contains: Contains::Some { + contains: &["unix", "miri"], + doesnt_contain: &["any()", "any()=any()"], + }, + }); + check(CheckCfg { + args: &["--check-cfg=cfg(any())"], + contains: Contains::Some { + contains: &["any()", "unix", r#"target_feature="crt-static""#], + doesnt_contain: &["any()=any()"], + }, + }); + check(CheckCfg { + args: &["--check-cfg=cfg(feature)"], + contains: Contains::Some { + contains: &["unix", "miri", "feature"], + doesnt_contain: &["any()", "any()=any()", "feature=none()", "feature="], + }, + }); + check(CheckCfg { + args: &[r#"--check-cfg=cfg(feature, values(none(), "", "test", "lol"))"#], + contains: Contains::Some { + contains: &["feature", "feature=\"\"", "feature=\"test\"", "feature=\"lol\""], + doesnt_contain: &["any()", "any()=any()", "feature=none()", "feature="], + }, + }); + check(CheckCfg { + args: &["--check-cfg=cfg(feature, values())"], + contains: Contains::Some { + contains: &["feature="], + doesnt_contain: &["any()", "any()=any()", "feature=none()", "feature"], + }, + }); + check(CheckCfg { + args: &["--check-cfg=cfg(feature, values())", "--check-cfg=cfg(feature, values(none()))"], + contains: Contains::Some { + contains: &["feature"], + doesnt_contain: &["any()", "any()=any()", "feature=none()", "feature="], + }, + }); + check(CheckCfg { + args: &[ + r#"--check-cfg=cfg(feature, values(any()))"#, + r#"--check-cfg=cfg(feature, values("tmp"))"#, + ], + contains: Contains::Some { + contains: &["unix", "miri", "feature=any()"], + doesnt_contain: &["any()", "any()=any()", "feature", "feature=", "feature=\"tmp\""], + }, + }); + check(CheckCfg { + args: &[ + r#"--check-cfg=cfg(has_foo, has_bar)"#, + r#"--check-cfg=cfg(feature, values("tmp"))"#, + r#"--check-cfg=cfg(feature, values("tmp"))"#, + ], + contains: Contains::Some { + contains: &["has_foo", "has_bar", "feature=\"tmp\""], + doesnt_contain: &["any()", "any()=any()", "feature"], + }, + }); +} + +fn check(CheckCfg { args, contains }: CheckCfg) { + let output = rustc() + .input("lib.rs") + .arg("-Zunstable-options") + .arg("--print=check-cfg") + .args(&*args) + .run(); + + let stdout = output.stdout_utf8(); + + let mut found = HashSet::::new(); + + for l in stdout.lines() { + assert!(l == l.trim()); + if let Some((left, right)) = l.split_once('=') { + if right != "any()" && right != "" { + assert!(right.starts_with("\"")); + assert!(right.ends_with("\"")); + } + assert!(!left.contains("\"")); + } else { + assert!(!l.contains("\"")); + } + assert!(found.insert(l.to_string()), "{}", &l); + } + + match contains { + Contains::Some { contains, doesnt_contain } => { + { + let should_found = + HashSet::::from_iter(contains.iter().map(|s| s.to_string())); + let diff: Vec<_> = should_found.difference(&found).collect(); + assert!( + diff.is_empty(), + "should found: {:?}, didn't found {:?}", + &should_found, + &diff + ); + } + { + let should_not_find = + HashSet::::from_iter(doesnt_contain.iter().map(|s| s.to_string())); + let diff: Vec<_> = should_not_find.intersection(&found).collect(); + assert!( + diff.is_empty(), + "should not find {:?}, did found {:?}", + &should_not_find, + &diff + ); + } + } + Contains::Only(only) => { + assert!(found.contains(&only.to_string()), "{:?} != {:?}", &only, &found); + assert!(found.len() == 1, "len: {}, instead of 1", found.len()); + } + } +} diff --git a/tests/run-make/print-native-static-libs/rmake.rs b/tests/run-make/print-native-static-libs/rmake.rs index edb85d568c67..b4e7c0e17ff8 100644 --- a/tests/run-make/print-native-static-libs/rmake.rs +++ b/tests/run-make/print-native-static-libs/rmake.rs @@ -30,9 +30,7 @@ fn main() { .run(); let mut found_note = false; - for l in output.stderr.lines() { - let l = l.expect("utf-8 string"); - + for l in output.stderr_utf8().lines() { let Some(args) = l.strip_prefix("note: native-static-libs:") else { continue; }; diff --git a/tests/run-make/print-to-output/rmake.rs b/tests/run-make/print-to-output/rmake.rs index 1763cd378d2d..66f62a7015ab 100644 --- a/tests/run-make/print-to-output/rmake.rs +++ b/tests/run-make/print-to-output/rmake.rs @@ -2,8 +2,9 @@ //! output to a file (instead of stdout) use std::ffi::OsString; +use std::path::PathBuf; -use run_make_support::{rustc, target, tmp_dir}; +use run_make_support::{fs_wrapper, rustc, target}; struct Option<'a> { target: &'a str, @@ -38,21 +39,17 @@ fn check(args: Option) { } // --print={option} - let stdout = { - let output = rustc().target(args.target).print(args.option).run(); - - String::from_utf8(output.stdout).unwrap() - }; + let stdout = rustc().target(args.target).print(args.option).run().stdout_utf8(); // --print={option}=PATH let output = { - let tmp_path = tmp_dir().join(format!("{}.txt", args.option)); + let tmp_path = PathBuf::from(format!("{}.txt", args.option)); let mut print_arg = OsString::from(format!("--print={}=", args.option)); print_arg.push(tmp_path.as_os_str()); - let _output = rustc().target(args.target).arg(print_arg).run(); + rustc().target(args.target).arg(print_arg).run(); - std::fs::read_to_string(&tmp_path).unwrap() + fs_wrapper::read_to_string(&tmp_path) }; check_(&stdout, args.includes); diff --git a/tests/run-make/issue-37893/a.rs b/tests/run-make/proc-macro-init-order/a.rs similarity index 100% rename from tests/run-make/issue-37893/a.rs rename to tests/run-make/proc-macro-init-order/a.rs diff --git a/tests/run-make/proc-macro-init-order/b.rs b/tests/run-make/proc-macro-init-order/b.rs new file mode 100644 index 000000000000..067f47c1b7a1 --- /dev/null +++ b/tests/run-make/proc-macro-init-order/b.rs @@ -0,0 +1,3 @@ +#![crate_type = "lib"] +#[macro_use] +extern crate a; diff --git a/tests/run-make/issue-37893/c.rs b/tests/run-make/proc-macro-init-order/c.rs similarity index 100% rename from tests/run-make/issue-37893/c.rs rename to tests/run-make/proc-macro-init-order/c.rs index b9c2155728cb..505f8470338e 100644 --- a/tests/run-make/issue-37893/c.rs +++ b/tests/run-make/proc-macro-init-order/c.rs @@ -1,3 +1,3 @@ #![crate_type = "staticlib"] -extern crate b; extern crate a; +extern crate b; diff --git a/tests/run-make/proc-macro-init-order/rmake.rs b/tests/run-make/proc-macro-init-order/rmake.rs new file mode 100644 index 000000000000..3c3fc813381c --- /dev/null +++ b/tests/run-make/proc-macro-init-order/rmake.rs @@ -0,0 +1,15 @@ +// a.rs is a procedural macro crate, on which b.rs and c.rs depend. A now +// patched bug caused a compilation failure if the proc-macro crate was +// initialized with its dependents in this exact order. This test checks +// that compilation succeeds even when initialization is done in this order. +// See https://github.com/rust-lang/rust/issues/37893 + +//@ ignore-cross-compile + +use run_make_support::rustc; + +fn main() { + rustc().input("a.rs").run(); + rustc().input("b.rs").run(); + rustc().input("c.rs").run(); +} diff --git a/tests/run-make/prune-link-args/empty.rs b/tests/run-make/prune-link-args/empty.rs index 45590d86ba6c..f328e4d9d04c 100644 --- a/tests/run-make/prune-link-args/empty.rs +++ b/tests/run-make/prune-link-args/empty.rs @@ -1 +1 @@ -fn main() { } +fn main() {} diff --git a/tests/run-make/raw-dylib-alt-calling-convention/driver.rs b/tests/run-make/raw-dylib-alt-calling-convention/driver.rs index b7f372c6b2b2..918e013af6ef 100644 --- a/tests/run-make/raw-dylib-alt-calling-convention/driver.rs +++ b/tests/run-make/raw-dylib-alt-calling-convention/driver.rs @@ -2,7 +2,6 @@ extern crate raw_dylib_alt_calling_convention_test; fn main() { raw_dylib_alt_calling_convention_test::library_function( - std::env::args().skip(1).next().map_or( - false, - |s| std::str::FromStr::from_str(&s).unwrap())); + std::env::args().skip(1).next().map_or(false, |s| std::str::FromStr::from_str(&s).unwrap()), + ); } diff --git a/tests/run-make/raw-dylib-c/lib.rs b/tests/run-make/raw-dylib-c/lib.rs index f17125f308c2..ebb08a0ba9e9 100644 --- a/tests/run-make/raw-dylib-c/lib.rs +++ b/tests/run-make/raw-dylib-c/lib.rs @@ -1,16 +1,16 @@ #[link(name = "extern_1.dll", kind = "raw-dylib", modifiers = "+verbatim")] -extern { +extern "C" { fn extern_fn_1(); } #[link(name = "extern_2", kind = "raw-dylib")] -extern { +extern "C" { fn extern_fn_3(); } pub fn library_function() { #[link(name = "extern_1", kind = "raw-dylib")] - extern { + extern "C" { fn extern_fn_2(); fn print_extern_variable(); static mut extern_variable: i32; diff --git a/tests/run-make/raw-dylib-cross-compilation/lib.rs b/tests/run-make/raw-dylib-cross-compilation/lib.rs index 3338ac0a0b50..d3b7fd23ca82 100644 --- a/tests/run-make/raw-dylib-cross-compilation/lib.rs +++ b/tests/run-make/raw-dylib-cross-compilation/lib.rs @@ -8,7 +8,7 @@ trait Sized {} #[link(name = "extern_1", kind = "raw-dylib")] -extern { +extern "C" { fn extern_fn(); } diff --git a/tests/run-make/raw-dylib-custom-dlltool/lib.rs b/tests/run-make/raw-dylib-custom-dlltool/lib.rs index 2f3f497a00de..db1d64505038 100644 --- a/tests/run-make/raw-dylib-custom-dlltool/lib.rs +++ b/tests/run-make/raw-dylib-custom-dlltool/lib.rs @@ -1,5 +1,5 @@ #[link(name = "extern_1", kind = "raw-dylib")] -extern { +extern "C" { fn extern_fn_1(); } diff --git a/tests/run-make/raw-dylib-import-name-type/driver.rs b/tests/run-make/raw-dylib-import-name-type/driver.rs index 6c1c212f187b..b235fef9e308 100644 --- a/tests/run-make/raw-dylib-import-name-type/driver.rs +++ b/tests/run-make/raw-dylib-import-name-type/driver.rs @@ -76,7 +76,7 @@ extern "vectorcall" { } #[link(name = "extern", kind = "raw-dylib")] -extern { +extern "C" { fn print_extern_variable_undecorated(); fn print_extern_variable_noprefix(); fn print_extern_variable_decorated(); diff --git a/tests/run-make/raw-dylib-inline-cross-dylib/driver.rs b/tests/run-make/raw-dylib-inline-cross-dylib/driver.rs index 0c3125be6f59..53f3fcb2f880 100644 --- a/tests/run-make/raw-dylib-inline-cross-dylib/driver.rs +++ b/tests/run-make/raw-dylib-inline-cross-dylib/driver.rs @@ -2,7 +2,7 @@ extern crate raw_dylib_test; extern crate raw_dylib_test_wrapper; #[link(name = "extern_2", kind = "raw-dylib")] -extern { +extern "C" { fn extern_fn_2(); } diff --git a/tests/run-make/raw-dylib-inline-cross-dylib/lib.rs b/tests/run-make/raw-dylib-inline-cross-dylib/lib.rs index 4877cb80aea5..07e289a21d97 100644 --- a/tests/run-make/raw-dylib-inline-cross-dylib/lib.rs +++ b/tests/run-make/raw-dylib-inline-cross-dylib/lib.rs @@ -1,5 +1,5 @@ #[link(name = "extern_1", kind = "raw-dylib")] -extern { +extern "C" { fn extern_fn_1(); fn extern_fn_2(); } diff --git a/tests/run-make/raw-dylib-link-ordinal/lib.rs b/tests/run-make/raw-dylib-link-ordinal/lib.rs index 1bbb45bbc772..69f0a5bca46f 100644 --- a/tests/run-make/raw-dylib-link-ordinal/lib.rs +++ b/tests/run-make/raw-dylib-link-ordinal/lib.rs @@ -1,5 +1,5 @@ #[link(name = "exporter", kind = "raw-dylib")] -extern { +extern "C" { #[link_ordinal(13)] fn imported_function(); #[link_ordinal(5)] diff --git a/tests/run-make/reachable-extern-fn-available-lto/rmake.rs b/tests/run-make/reachable-extern-fn-available-lto/rmake.rs index c7262b9461b3..558444846fd1 100644 --- a/tests/run-make/reachable-extern-fn-available-lto/rmake.rs +++ b/tests/run-make/reachable-extern-fn-available-lto/rmake.rs @@ -9,10 +9,10 @@ //@ ignore-cross-compile -use run_make_support::{cc, extra_c_flags, run, rustc, static_lib}; +use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name}; fn main() { - let libbar_path = static_lib("bar"); + let libbar_path = static_lib_name("bar"); rustc().input("foo.rs").crate_type("rlib").run(); rustc() .input("bar.rs") diff --git a/tests/run-make/repr128-dwarf/rmake.rs b/tests/run-make/repr128-dwarf/rmake.rs index fd5dd8104441..a65cf234edff 100644 --- a/tests/run-make/repr128-dwarf/rmake.rs +++ b/tests/run-make/repr128-dwarf/rmake.rs @@ -1,15 +1,15 @@ //@ ignore-windows // This test should be replaced with one in tests/debuginfo once GDB or LLDB support 128-bit enums. -use gimli::{AttributeValue, Dwarf, EndianRcSlice, Reader, RunTimeEndian}; +use gimli::{AttributeValue, EndianRcSlice, Reader, RunTimeEndian}; use object::{Object, ObjectSection}; -use run_make_support::{gimli, object, rustc, tmp_dir}; -use std::borrow::Cow; +use run_make_support::{fs_wrapper, gimli, object, rustc}; use std::collections::HashMap; +use std::path::PathBuf; use std::rc::Rc; fn main() { - let output = tmp_dir().join("repr128"); + let output = PathBuf::from("repr128"); rustc().input("main.rs").output(&output).arg("-Cdebuginfo=2").run(); // Mach-O uses packed debug info let dsym_location = output @@ -19,8 +19,7 @@ fn main() { .join("DWARF") .join("repr128"); let output = - std::fs::read(if dsym_location.try_exists().unwrap() { dsym_location } else { output }) - .unwrap(); + fs_wrapper::read(if dsym_location.try_exists().unwrap() { dsym_location } else { output }); let obj = object::File::parse(output.as_slice()).unwrap(); let endian = if obj.is_little_endian() { RunTimeEndian::Little } else { RunTimeEndian::Big }; let dwarf = gimli::Dwarf::load(|section| -> Result<_, ()> { diff --git a/tests/run-make/reproducible-build-2/linker.rs b/tests/run-make/reproducible-build-2/linker.rs index 998d1f328596..ec238b2fa562 100644 --- a/tests/run-make/reproducible-build-2/linker.rs +++ b/tests/run-make/reproducible-build-2/linker.rs @@ -1,7 +1,7 @@ use std::env; -use std::path::Path; use std::fs::File; use std::io::{Read, Write}; +use std::path::Path; fn main() { let mut dst = env::current_exe().unwrap(); @@ -19,7 +19,7 @@ fn main() { if !path.is_file() { out.push_str(&arg); out.push_str("\n"); - continue + continue; } let mut contents = Vec::new(); diff --git a/tests/run-make/reproducible-build-2/reproducible-build-aux.rs b/tests/run-make/reproducible-build-2/reproducible-build-aux.rs index 8105b3d2bda3..9708b2d15b42 100644 --- a/tests/run-make/reproducible-build-2/reproducible-build-aux.rs +++ b/tests/run-make/reproducible-build-2/reproducible-build-aux.rs @@ -1,4 +1,4 @@ -#![crate_type="lib"] +#![crate_type = "lib"] pub static STATIC: i32 = 1234; @@ -18,7 +18,7 @@ impl Drop for Struct { pub enum Enum { Variant1, Variant2(u32), - Variant3 { x: u32 } + Variant3 { x: u32 }, } pub struct TupleStruct(pub i8, pub i16, pub i32, pub i64); diff --git a/tests/run-make/reproducible-build-2/reproducible-build.rs b/tests/run-make/reproducible-build-2/reproducible-build.rs index a6c04774c869..849b3d510af8 100644 --- a/tests/run-make/reproducible-build-2/reproducible-build.rs +++ b/tests/run-make/reproducible-build-2/reproducible-build.rs @@ -18,6 +18,8 @@ // - Trait object shims // - Fn Pointer shims +// ignore-tidy-linelength + #![allow(dead_code, warnings)] extern crate reproducible_build_aux; @@ -40,7 +42,7 @@ impl Drop for Struct { pub enum Enum { Variant1, Variant2(u32), - Variant3 { x: u32 } + Variant3 { x: u32 }, } struct TupleStruct(i8, i16, i32, i64); @@ -67,19 +69,14 @@ fn main() { generic_fn::>(); generic_fn::, reproducible_build_aux::Struct>(); - let dropped = Struct { - x: "", - y: 'a', - }; + let dropped = Struct { x: "", y: 'a' }; let _ = Enum::Variant1; let _ = Enum::Variant2(0); let _ = Enum::Variant3 { x: 0 }; let _ = TupleStruct(1, 2, 3, 4); - let closure = |x| { - x + 1i32 - }; + let closure = |x| x + 1i32; fn inner i32>(f: F) -> i32 { f(STATIC) @@ -94,13 +91,13 @@ fn main() { f(0); } - with_fn_once_adapter(|_:i32| { }); + with_fn_once_adapter(|_: i32| {}); reproducible_build_aux::regular_fn(STATIC); reproducible_build_aux::generic_fn::(); reproducible_build_aux::generic_fn::>(); - reproducible_build_aux::generic_fn::, - reproducible_build_aux::Struct>(); + reproducible_build_aux::generic_fn::, reproducible_build_aux::Struct>( + ); let _ = reproducible_build_aux::Enum::Variant1; let _ = reproducible_build_aux::Enum::Variant2(0); diff --git a/tests/run-make/reproducible-build/linker.rs b/tests/run-make/reproducible-build/linker.rs index 3dda6f190e44..ab3b4049cc32 100644 --- a/tests/run-make/reproducible-build/linker.rs +++ b/tests/run-make/reproducible-build/linker.rs @@ -1,7 +1,7 @@ use std::env; -use std::path::Path; use std::fs::File; use std::io::{Read, Write}; +use std::path::Path; fn main() { let mut dst = env::current_exe().unwrap(); @@ -19,7 +19,7 @@ fn main() { if !path.is_file() { out.push_str(&arg); out.push_str("\n"); - continue + continue; } let mut contents = Vec::new(); diff --git a/tests/run-make/reproducible-build/reproducible-build-aux.rs b/tests/run-make/reproducible-build/reproducible-build-aux.rs index 8105b3d2bda3..9708b2d15b42 100644 --- a/tests/run-make/reproducible-build/reproducible-build-aux.rs +++ b/tests/run-make/reproducible-build/reproducible-build-aux.rs @@ -1,4 +1,4 @@ -#![crate_type="lib"] +#![crate_type = "lib"] pub static STATIC: i32 = 1234; @@ -18,7 +18,7 @@ impl Drop for Struct { pub enum Enum { Variant1, Variant2(u32), - Variant3 { x: u32 } + Variant3 { x: u32 }, } pub struct TupleStruct(pub i8, pub i16, pub i32, pub i64); diff --git a/tests/run-make/reproducible-build/reproducible-build.rs b/tests/run-make/reproducible-build/reproducible-build.rs index a6c04774c869..849b3d510af8 100644 --- a/tests/run-make/reproducible-build/reproducible-build.rs +++ b/tests/run-make/reproducible-build/reproducible-build.rs @@ -18,6 +18,8 @@ // - Trait object shims // - Fn Pointer shims +// ignore-tidy-linelength + #![allow(dead_code, warnings)] extern crate reproducible_build_aux; @@ -40,7 +42,7 @@ impl Drop for Struct { pub enum Enum { Variant1, Variant2(u32), - Variant3 { x: u32 } + Variant3 { x: u32 }, } struct TupleStruct(i8, i16, i32, i64); @@ -67,19 +69,14 @@ fn main() { generic_fn::>(); generic_fn::, reproducible_build_aux::Struct>(); - let dropped = Struct { - x: "", - y: 'a', - }; + let dropped = Struct { x: "", y: 'a' }; let _ = Enum::Variant1; let _ = Enum::Variant2(0); let _ = Enum::Variant3 { x: 0 }; let _ = TupleStruct(1, 2, 3, 4); - let closure = |x| { - x + 1i32 - }; + let closure = |x| x + 1i32; fn inner i32>(f: F) -> i32 { f(STATIC) @@ -94,13 +91,13 @@ fn main() { f(0); } - with_fn_once_adapter(|_:i32| { }); + with_fn_once_adapter(|_: i32| {}); reproducible_build_aux::regular_fn(STATIC); reproducible_build_aux::generic_fn::(); reproducible_build_aux::generic_fn::>(); - reproducible_build_aux::generic_fn::, - reproducible_build_aux::Struct>(); + reproducible_build_aux::generic_fn::, reproducible_build_aux::Struct>( + ); let _ = reproducible_build_aux::Enum::Variant1; let _ = reproducible_build_aux::Enum::Variant2(0); diff --git a/tests/run-make/issue-85441/empty.rs b/tests/run-make/reset-codegen-1/foo.rs similarity index 100% rename from tests/run-make/issue-85441/empty.rs rename to tests/run-make/reset-codegen-1/foo.rs diff --git a/tests/run-make/reset-codegen-1/rmake.rs b/tests/run-make/reset-codegen-1/rmake.rs new file mode 100644 index 000000000000..19d42b3d6d5e --- /dev/null +++ b/tests/run-make/reset-codegen-1/rmake.rs @@ -0,0 +1,42 @@ +// When rustc received 4 codegen-units, an output path and an emit flag all simultaneously, +// this could cause an annoying recompilation issue, uselessly lengthening the build process. +// A fix was delivered, which resets codegen-units to 1 when necessary, +// but as it directly affected the way codegen-units are manipulated, +// this test was created to check that this fix did not cause compilation failures. +// See https://github.com/rust-lang/rust/issues/30063 + +//@ ignore-cross-compile + +use run_make_support::{bin_name, fs_wrapper, rustc}; +use std::path::Path; + +fn compile(output_file: &str, emit: Option<&str>) { + let mut rustc = rustc(); + let rustc = rustc.codegen_units(4).output(output_file).input("foo.rs"); + if let Some(emit) = emit { + rustc.emit(emit); + } + rustc.run(); +} + +fn main() { + let flags = [ + ("foo-output", None), + ("asm-output", Some("asm")), + ("bc-output", Some("llvm-bc")), + ("ir-output", Some("llvm-ir")), + ("link-output", Some("link")), + ("obj-output", Some("obj")), + ("dep-output", Some("dep-info")), + ]; + for (output_file, emit) in flags { + // In the None case, bin_name is required for successful Windows compilation. + let output_file = &bin_name(output_file); + compile(output_file, emit); + assert!(Path::new(output_file).is_file()); + } + + compile("multi-output", Some("asm,obj")); + assert!(Path::new("multi-output.s").is_file()); + assert!(Path::new("multi-output.o").is_file()); +} diff --git a/tests/run-make/resolve-rename/Makefile b/tests/run-make/resolve-rename/Makefile deleted file mode 100644 index 00f83a5d6b2d..000000000000 --- a/tests/run-make/resolve-rename/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -include ../tools.mk - -all: - $(RUSTC) -C extra-filename=-hash foo.rs - $(RUSTC) bar.rs - mv $(TMPDIR)/libfoo-hash.rlib $(TMPDIR)/libfoo-another-hash.rlib - $(RUSTC) baz.rs diff --git a/tests/run-make/resolve-rename/bar.rs b/tests/run-make/resolve-rename/bar.rs index 4a09ce355e68..706ff4535202 100644 --- a/tests/run-make/resolve-rename/bar.rs +++ b/tests/run-make/resolve-rename/bar.rs @@ -2,4 +2,6 @@ extern crate foo; -pub fn bar() { foo::foo() } +pub fn bar() { + foo::foo() +} diff --git a/tests/run-make/resolve-rename/baz.rs b/tests/run-make/resolve-rename/baz.rs index 9176073ef97f..74822fb4e1ef 100644 --- a/tests/run-make/resolve-rename/baz.rs +++ b/tests/run-make/resolve-rename/baz.rs @@ -2,4 +2,6 @@ extern crate bar; -pub fn baz() { bar::bar() } +pub fn baz() { + bar::bar() +} diff --git a/tests/run-make/resolve-rename/rmake.rs b/tests/run-make/resolve-rename/rmake.rs new file mode 100644 index 000000000000..09bd4165b2a5 --- /dev/null +++ b/tests/run-make/resolve-rename/rmake.rs @@ -0,0 +1,16 @@ +// If a library is compiled with -C extra-filename, the rust compiler +// will take this into account when searching for libraries. However, +// if that library is then renamed, the rust compiler should fall back +// to its regular library location logic and not immediately fail to find +// the renamed library. +// See https://github.com/rust-lang/rust/pull/49253 + +use run_make_support::fs_wrapper; +use run_make_support::rustc; + +fn main() { + rustc().extra_filename("-hash").input("foo.rs").run(); + rustc().input("bar.rs").run(); + fs_wrapper::rename("libfoo-hash.rlib", "libfoo-another-hash.rlib"); + rustc().input("baz.rs").run(); +} diff --git a/tests/run-make/return-non-c-like-enum-from-c/nonclike.rs b/tests/run-make/return-non-c-like-enum-from-c/nonclike.rs index ea22a2a56e09..1cd6360bdd23 100644 --- a/tests/run-make/return-non-c-like-enum-from-c/nonclike.rs +++ b/tests/run-make/return-non-c-like-enum-from-c/nonclike.rs @@ -4,7 +4,7 @@ pub enum TT { BB, } -#[repr(C,u8)] +#[repr(C, u8)] pub enum T { A(u64), B, diff --git a/tests/run-make/return-non-c-like-enum/nonclike.rs b/tests/run-make/return-non-c-like-enum/nonclike.rs index de529cf641ab..32884d8d690b 100644 --- a/tests/run-make/return-non-c-like-enum/nonclike.rs +++ b/tests/run-make/return-non-c-like-enum/nonclike.rs @@ -9,7 +9,7 @@ pub extern "C" fn tt_new(a: u64, b: u64) -> TT { TT::AA(a, b) } -#[repr(C,u8)] +#[repr(C, u8)] pub enum T { A(u64), B, diff --git a/tests/run-make/rlib-chain/m2.rs b/tests/run-make/rlib-chain/m2.rs index eba12fe12188..9430e1d156c0 100644 --- a/tests/run-make/rlib-chain/m2.rs +++ b/tests/run-make/rlib-chain/m2.rs @@ -1,4 +1,6 @@ #![crate_type = "rlib"] extern crate m1; -pub fn m2() { m1::m1() } +pub fn m2() { + m1::m1() +} diff --git a/tests/run-make/rlib-chain/m3.rs b/tests/run-make/rlib-chain/m3.rs index ade191db49c8..b45712e55e16 100644 --- a/tests/run-make/rlib-chain/m3.rs +++ b/tests/run-make/rlib-chain/m3.rs @@ -1,4 +1,6 @@ #![crate_type = "rlib"] extern crate m2; -pub fn m3() { m2::m2() } +pub fn m3() { + m2::m2() +} diff --git a/tests/run-make/rlib-chain/m4.rs b/tests/run-make/rlib-chain/m4.rs index fa8ec6079dea..c732512af9fc 100644 --- a/tests/run-make/rlib-chain/m4.rs +++ b/tests/run-make/rlib-chain/m4.rs @@ -1,3 +1,5 @@ extern crate m3; -fn main() { m3::m3() } +fn main() { + m3::m3() +} diff --git a/tests/run-make/run-in-tmpdir-self-test/rmake.rs b/tests/run-make/run-in-tmpdir-self-test/rmake.rs new file mode 100644 index 000000000000..83b99bfe8632 --- /dev/null +++ b/tests/run-make/run-in-tmpdir-self-test/rmake.rs @@ -0,0 +1,38 @@ +//! This is a self-test for the `run_in_tmpdir` helper in the support library. This test tries to +//! check that files and directories created within the temporary directory gets properly cleared +//! when returning from the closure. + +use std::fs; +use std::path::{Path, PathBuf}; + +use run_make_support::{cwd, run_in_tmpdir}; + +fn main() { + let mut file_path = PathBuf::new(); + let mut dir_path = PathBuf::new(); + let mut readonly_file_path = PathBuf::new(); + let test_cwd = cwd(); + run_in_tmpdir(|| { + assert_ne!(test_cwd, cwd(), "test cwd should not be the same as tmpdir cwd"); + + file_path = cwd().join("foo.txt"); + fs::write(&file_path, "hi").unwrap(); + + dir_path = cwd().join("bar"); + fs::create_dir_all(&dir_path).unwrap(); + + readonly_file_path = cwd().join("readonly-file.txt"); + fs::write(&readonly_file_path, "owo").unwrap(); + let mut perms = fs::metadata(&readonly_file_path).unwrap().permissions(); + perms.set_readonly(true); + fs::set_permissions(&mut readonly_file_path, perms).unwrap(); + + assert!(file_path.exists()); + assert!(dir_path.exists()); + assert!(readonly_file_path.exists()); + }); + assert!(!file_path.exists()); + assert!(!dir_path.exists()); + assert!(!readonly_file_path.exists()); + assert_eq!(test_cwd, cwd(), "test cwd is not correctly restored"); +} diff --git a/tests/run-make/rust-lld-by-default/rmake.rs b/tests/run-make/rust-lld-by-default/rmake.rs index 876968727f3c..5b065f86f53f 100644 --- a/tests/run-make/rust-lld-by-default/rmake.rs +++ b/tests/run-make/rust-lld-by-default/rmake.rs @@ -4,8 +4,6 @@ //@ needs-rust-lld //@ only-x86_64-unknown-linux-gnu -extern crate run_make_support; - use run_make_support::regex::Regex; use run_make_support::rustc; use std::process::Output; @@ -19,8 +17,9 @@ fn main() { .input("main.rs") .run(); assert!( - find_lld_version_in_logs(output), - "the LLD version string should be present in the output logs" + find_lld_version_in_logs(output.stderr_utf8()), + "the LLD version string should be present in the output logs:\n{}", + output.stderr_utf8() ); // But it can still be disabled by turning the linker feature off. @@ -31,13 +30,13 @@ fn main() { .input("main.rs") .run(); assert!( - !find_lld_version_in_logs(output), - "the LLD version string should not be present in the output logs" + !find_lld_version_in_logs(output.stderr_utf8()), + "the LLD version string should not be present in the output logs:\n{}", + output.stderr_utf8() ); } -fn find_lld_version_in_logs(output: Output) -> bool { +fn find_lld_version_in_logs(stderr: String) -> bool { let lld_version_re = Regex::new(r"^LLD [0-9]+\.[0-9]+\.[0-9]+").unwrap(); - let stderr = std::str::from_utf8(&output.stderr).unwrap(); - stderr.lines().any(|line| lld_version_re.is_match(line)) + stderr.lines().any(|line| lld_version_re.is_match(line.trim())) } diff --git a/tests/run-make/rust-lld-custom-target/rmake.rs b/tests/run-make/rust-lld-custom-target/rmake.rs index 9bdb69f47d8e..f3dc2275f625 100644 --- a/tests/run-make/rust-lld-custom-target/rmake.rs +++ b/tests/run-make/rust-lld-custom-target/rmake.rs @@ -23,8 +23,9 @@ fn main() { .input("lib.rs") .run(); assert!( - find_lld_version_in_logs(output), - "the LLD version string should be present in the output logs" + find_lld_version_in_logs(output.stderr_utf8()), + "the LLD version string should be present in the output logs:\n{}", + output.stderr_utf8() ); // But it can also be disabled via linker features. @@ -37,13 +38,13 @@ fn main() { .input("lib.rs") .run(); assert!( - !find_lld_version_in_logs(output), - "the LLD version string should not be present in the output logs" + !find_lld_version_in_logs(output.stderr_utf8()), + "the LLD version string should not be present in the output logs:\n{}", + output.stderr_utf8() ); } -fn find_lld_version_in_logs(output: Output) -> bool { +fn find_lld_version_in_logs(stderr: String) -> bool { let lld_version_re = Regex::new(r"^LLD [0-9]+\.[0-9]+\.[0-9]+").unwrap(); - let stderr = std::str::from_utf8(&output.stderr).unwrap(); - stderr.lines().any(|line| lld_version_re.is_match(line)) + stderr.lines().any(|line| lld_version_re.is_match(line.trim())) } diff --git a/tests/run-make/rust-lld/rmake.rs b/tests/run-make/rust-lld/rmake.rs index feeb82e709e1..3eb60367c7f9 100644 --- a/tests/run-make/rust-lld/rmake.rs +++ b/tests/run-make/rust-lld/rmake.rs @@ -21,8 +21,9 @@ fn main() { .input("main.rs") .run(); assert!( - find_lld_version_in_logs(output), - "the LLD version string should be present in the output logs" + find_lld_version_in_logs(output.stderr_utf8()), + "the LLD version string should be present in the output logs:\n{}", + output.stderr_utf8() ); // It should not be used when we explictly opt-out of lld. @@ -33,8 +34,9 @@ fn main() { .input("main.rs") .run(); assert!( - !find_lld_version_in_logs(output), - "the LLD version string should not be present in the output logs" + !find_lld_version_in_logs(output.stderr_utf8()), + "the LLD version string should not be present in the output logs:\n{}", + output.stderr_utf8() ); // While we're here, also check that the last linker feature flag "wins" when passed multiple @@ -50,13 +52,13 @@ fn main() { .input("main.rs") .run(); assert!( - find_lld_version_in_logs(output), - "the LLD version string should be present in the output logs" + find_lld_version_in_logs(output.stderr_utf8()), + "the LLD version string should be present in the output logs:\n{}", + output.stderr_utf8() ); } -fn find_lld_version_in_logs(output: Output) -> bool { +fn find_lld_version_in_logs(stderr: String) -> bool { let lld_version_re = Regex::new(r"^LLD [0-9]+\.[0-9]+\.[0-9]+").unwrap(); - let stderr = std::str::from_utf8(&output.stderr).unwrap(); - stderr.lines().any(|line| lld_version_re.is_match(line)) + stderr.lines().any(|line| lld_version_re.is_match(line.trim())) } diff --git a/tests/run-make/rustdoc-determinism/rmake.rs b/tests/run-make/rustdoc-determinism/rmake.rs index 09097d4507da..2d4f357d6819 100644 --- a/tests/run-make/rustdoc-determinism/rmake.rs +++ b/tests/run-make/rustdoc-determinism/rmake.rs @@ -1,14 +1,15 @@ // Assert that the search index is generated deterministically, regardless of the // order that crates are documented in. -use run_make_support::{diff, rustdoc, tmp_dir}; +use run_make_support::{diff, rustdoc}; +use std::path::Path; fn main() { - let foo_first = tmp_dir().join("foo_first"); + let foo_first = Path::new("foo_first"); rustdoc().input("foo.rs").output(&foo_first).run(); rustdoc().input("bar.rs").output(&foo_first).run(); - let bar_first = tmp_dir().join("bar_first"); + let bar_first = Path::new("bar_first"); rustdoc().input("bar.rs").output(&bar_first).run(); rustdoc().input("foo.rs").output(&bar_first).run(); diff --git a/tests/run-make/rustdoc-error-lines/input.rs b/tests/run-make/rustdoc-error-lines/input.rs index b4db182e85f2..1ca2efc0dd59 100644 --- a/tests/run-make/rustdoc-error-lines/input.rs +++ b/tests/run-make/rustdoc-error-lines/input.rs @@ -6,9 +6,7 @@ /// #![feature(bool_to_option)] /// let x: char = 1; /// ``` -pub fn foo() { - -} +pub fn foo() {} /// Add some text around the test... /// diff --git a/tests/run-make/rustdoc-error-lines/rmake.rs b/tests/run-make/rustdoc-error-lines/rmake.rs index 31536c78dd46..ea5ec2faed95 100644 --- a/tests/run-make/rustdoc-error-lines/rmake.rs +++ b/tests/run-make/rustdoc-error-lines/rmake.rs @@ -4,17 +4,15 @@ use run_make_support::rustdoc; fn main() { - let output = - String::from_utf8(rustdoc().input("input.rs").arg("--test").command_output().stdout) - .unwrap(); + let output = rustdoc().input("input.rs").arg("--test").run_fail().stdout_utf8(); let should_contain = &[ "input.rs - foo (line 5)", "input.rs:7:15", - "input.rs - bar (line 15)", - "input.rs:17:15", - "input.rs - bar (line 24)", - "input.rs:26:15", + "input.rs - bar (line 13)", + "input.rs:15:15", + "input.rs - bar (line 22)", + "input.rs:24:15", ]; for text in should_contain { assert!(output.contains(text), "output doesn't contains {:?}", text); diff --git a/tests/run-make/rustdoc-map-file/foo.rs b/tests/run-make/rustdoc-map-file/foo.rs index e12b9d2292c5..f98543f9b052 100644 --- a/tests/run-make/rustdoc-map-file/foo.rs +++ b/tests/run-make/rustdoc-map-file/foo.rs @@ -1,5 +1,5 @@ -pub use private::Quz; pub use hidden::Bar; +pub use private::Quz; mod private { pub struct Quz; @@ -12,5 +12,5 @@ pub mod hidden { #[macro_export] macro_rules! foo { - () => {} + () => {}; } diff --git a/tests/run-make/rustdoc-map-file/rmake.rs b/tests/run-make/rustdoc-map-file/rmake.rs index d017b41bcdd2..08f9595ef9f6 100644 --- a/tests/run-make/rustdoc-map-file/rmake.rs +++ b/tests/run-make/rustdoc-map-file/rmake.rs @@ -1,7 +1,7 @@ -use run_make_support::{python_command, rustdoc, tmp_dir}; +use run_make_support::{python_command, rustdoc}; fn main() { - let out_dir = tmp_dir().join("out"); + let out_dir = "out"; rustdoc() .input("foo.rs") .arg("-Zunstable-options") @@ -9,5 +9,5 @@ fn main() { .output(&out_dir) .run(); // FIXME (GuillaumeGomez): Port the python script to Rust as well. - assert!(python_command().arg("validate_json.py").arg(&out_dir).status().unwrap().success()); + python_command().arg("validate_json.py").arg(&out_dir).run(); } diff --git a/tests/run-make/rustdoc-output-path/rmake.rs b/tests/run-make/rustdoc-output-path/rmake.rs index 74fb0a56ac63..28e0fd98f93c 100644 --- a/tests/run-make/rustdoc-output-path/rmake.rs +++ b/tests/run-make/rustdoc-output-path/rmake.rs @@ -1,9 +1,10 @@ // Checks that if the output folder doesn't exist, rustdoc will create it. -use run_make_support::{rustdoc, tmp_dir}; +use run_make_support::rustdoc; +use std::path::Path; fn main() { - let out_dir = tmp_dir().join("foo/bar/doc"); + let out_dir = Path::new("foo/bar/doc"); rustdoc().input("foo.rs").output(&out_dir).run(); assert!(out_dir.exists()); } diff --git a/tests/run-make/rustdoc-scrape-examples-invalid-expr/rmake.rs b/tests/run-make/rustdoc-scrape-examples-invalid-expr/rmake.rs index 537d3e2d724a..e9c54fa39223 100644 --- a/tests/run-make/rustdoc-scrape-examples-invalid-expr/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-invalid-expr/rmake.rs @@ -2,5 +2,5 @@ mod scrape; fn main() { - scrape::scrape(); + scrape::scrape(&[]); } diff --git a/tests/run-make/rustdoc-scrape-examples-invalid-expr/src/lib.rs b/tests/run-make/rustdoc-scrape-examples-invalid-expr/src/lib.rs index c30c99dec603..ba8ee66fc78b 100644 --- a/tests/run-make/rustdoc-scrape-examples-invalid-expr/src/lib.rs +++ b/tests/run-make/rustdoc-scrape-examples-invalid-expr/src/lib.rs @@ -1 +1,3 @@ -pub const fn f() -> usize { 5 } +pub const fn f() -> usize { + 5 +} diff --git a/tests/run-make/rustdoc-scrape-examples-macros/Makefile b/tests/run-make/rustdoc-scrape-examples-macros/Makefile deleted file mode 100644 index edc19d8cb5de..000000000000 --- a/tests/run-make/rustdoc-scrape-examples-macros/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# ignore-cross-compile -include ../../run-make/tools.mk - -OUTPUT_DIR := "$(TMPDIR)/rustdoc" -DYLIB_NAME := $(shell echo | $(RUSTC) --crate-name foobar_macro --crate-type dylib --print file-names -) - -all: - $(RUSTC) src/proc.rs --crate-name foobar_macro --edition=2021 --crate-type proc-macro --emit=dep-info,link - - $(RUSTC) src/lib.rs --crate-name foobar --edition=2021 --crate-type lib --emit=dep-info,link - - $(RUSTDOC) examples/ex.rs --crate-name ex --crate-type bin --output $(OUTPUT_DIR) \ - --extern foobar=$(TMPDIR)/libfoobar.rlib --extern foobar_macro=$(TMPDIR)/$(DYLIB_NAME) \ - -Z unstable-options --scrape-examples-output-path $(TMPDIR)/ex.calls --scrape-examples-target-crate foobar - - $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --output $(OUTPUT_DIR) \ - -Z unstable-options --with-examples $(TMPDIR)/ex.calls - - $(HTMLDOCCK) $(OUTPUT_DIR) src/lib.rs diff --git a/tests/run-make/rustdoc-scrape-examples-macros/examples/ex.rs b/tests/run-make/rustdoc-scrape-examples-macros/examples/ex.rs index 4d8c8b30e311..70a9a187f07e 100644 --- a/tests/run-make/rustdoc-scrape-examples-macros/examples/ex.rs +++ b/tests/run-make/rustdoc-scrape-examples-macros/examples/ex.rs @@ -8,20 +8,20 @@ a_proc_macro!(); // no #[an_attr_macro] fn a() { - f(); // no + f(); // no } #[an_attr_macro(with_span)] fn b() { - f(); // yes + f(); // yes } fn c() { - a_rules_macro!(f()); // yes + a_rules_macro!(f()); // yes } fn d() { - a_rules_macro!(()); // no + a_rules_macro!(()); // no } -fn main(){} +fn main() {} diff --git a/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs b/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs new file mode 100644 index 000000000000..bfe4a1df4569 --- /dev/null +++ b/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs @@ -0,0 +1,60 @@ +//@ ignore-cross-compile + +use run_make_support::{htmldocck, rust_lib_name, rustc, rustdoc}; + +fn main() { + let out_dir = "rustdoc"; + let ex_dir = "ex.calls"; + let proc_crate_name = "foobar_macro"; + let crate_name = "foobar"; + + let dylib_name = rustc() + .crate_name(proc_crate_name) + .crate_type("dylib") + .arg("--print") + .arg("file-names") + .arg("-") + .run() + .stdout_utf8(); + + rustc() + .input("src/proc.rs") + .crate_name(proc_crate_name) + .edition("2021") + .crate_type("proc-macro") + .emit("dep-info,link") + .run(); + rustc() + .input("src/lib.rs") + .crate_name(crate_name) + .edition("2021") + .crate_type("lib") + .emit("dep-info,link") + .run(); + + rustdoc() + .input("examples/ex.rs") + .crate_name("ex") + .crate_type("bin") + .output(&out_dir) + .extern_(crate_name, rust_lib_name(crate_name)) + .extern_(proc_crate_name, dylib_name.trim()) + .arg("-Zunstable-options") + .arg("--scrape-examples-output-path") + .arg(&ex_dir) + .arg("--scrape-examples-target-crate") + .arg(crate_name) + .run(); + + rustdoc() + .input("src/lib.rs") + .crate_name(crate_name) + .crate_type("lib") + .output(&out_dir) + .arg("-Zunstable-options") + .arg("--with-examples") + .arg(&ex_dir) + .run(); + + htmldocck().arg(out_dir).arg("src/lib.rs").run(); +} diff --git a/tests/run-make/rustdoc-scrape-examples-macros/src/lib.rs b/tests/run-make/rustdoc-scrape-examples-macros/src/lib.rs index d8658a0f2557..26ab1390a49a 100644 --- a/tests/run-make/rustdoc-scrape-examples-macros/src/lib.rs +++ b/tests/run-make/rustdoc-scrape-examples-macros/src/lib.rs @@ -8,5 +8,7 @@ pub fn f() {} #[macro_export] macro_rules! a_rules_macro { - ($e:expr) => { ($e, foobar::f()); } + ($e:expr) => { + ($e, foobar::f()); + }; } diff --git a/tests/run-make/rustdoc-scrape-examples-multiple/Makefile b/tests/run-make/rustdoc-scrape-examples-multiple/Makefile deleted file mode 100644 index 453a7d4bc8b8..000000000000 --- a/tests/run-make/rustdoc-scrape-examples-multiple/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -deps := ex ex2 - -include ./scrape.mk - -all: scrape diff --git a/tests/run-make/rustdoc-scrape-examples-multiple/rmake.rs b/tests/run-make/rustdoc-scrape-examples-multiple/rmake.rs new file mode 100644 index 000000000000..e9c54fa39223 --- /dev/null +++ b/tests/run-make/rustdoc-scrape-examples-multiple/rmake.rs @@ -0,0 +1,6 @@ +#[path = "../rustdoc-scrape-examples-remap/scrape.rs"] +mod scrape; + +fn main() { + scrape::scrape(&[]); +} diff --git a/tests/run-make/rustdoc-scrape-examples-multiple/scrape.mk b/tests/run-make/rustdoc-scrape-examples-multiple/scrape.mk deleted file mode 100644 index 57220bc6635c..000000000000 --- a/tests/run-make/rustdoc-scrape-examples-multiple/scrape.mk +++ /dev/null @@ -1,21 +0,0 @@ -include ../tools.mk - -OUTPUT_DIR := "$(TMPDIR)/rustdoc" - -$(TMPDIR)/%.calls: $(TMPDIR)/libfoobar.rmeta - $(RUSTDOC) examples/$*.rs --crate-name $* --crate-type bin --output $(OUTPUT_DIR) \ - --extern foobar=$(TMPDIR)/libfoobar.rmeta \ - -Z unstable-options \ - --scrape-examples-output-path $@ \ - --scrape-examples-target-crate foobar \ - $(extra_flags) - -$(TMPDIR)/lib%.rmeta: src/lib.rs - $(RUSTC) src/lib.rs --crate-name $* --crate-type lib --emit=metadata - -scrape: $(foreach d,$(deps),$(TMPDIR)/$(d).calls) - $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --output $(OUTPUT_DIR) \ - -Z unstable-options \ - $(foreach d,$(deps),--with-examples $(TMPDIR)/$(d).calls) - - $(HTMLDOCCK) $(OUTPUT_DIR) src/lib.rs diff --git a/tests/run-make/rustdoc-scrape-examples-ordering/rmake.rs b/tests/run-make/rustdoc-scrape-examples-ordering/rmake.rs index 537d3e2d724a..e9c54fa39223 100644 --- a/tests/run-make/rustdoc-scrape-examples-ordering/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-ordering/rmake.rs @@ -2,5 +2,5 @@ mod scrape; fn main() { - scrape::scrape(); + scrape::scrape(&[]); } diff --git a/tests/run-make/rustdoc-scrape-examples-remap/rmake.rs b/tests/run-make/rustdoc-scrape-examples-remap/rmake.rs index d9deaf279ce5..4e3b895aef0c 100644 --- a/tests/run-make/rustdoc-scrape-examples-remap/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-remap/rmake.rs @@ -1,5 +1,5 @@ mod scrape; fn main() { - scrape::scrape(); + scrape::scrape(&[]); } diff --git a/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs b/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs index 709388b54921..5d67ee2580fb 100644 --- a/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs +++ b/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs @@ -1,13 +1,10 @@ -use run_make_support::{htmldocck, rustc, rustdoc, source_path, tmp_dir}; -use std::fs::read_dir; +use run_make_support::{fs_wrapper, htmldocck, rustc, rustdoc, source_root}; use std::path::Path; -pub fn scrape() { - let lib_dir = tmp_dir(); - let out_dir = tmp_dir().join("rustdoc"); +pub fn scrape(extra_args: &[&str]) { + let out_dir = Path::new("rustdoc"); let crate_name = "foobar"; - let deps = read_dir("examples") - .unwrap() + let deps = fs_wrapper::read_dir("examples") .filter_map(|entry| entry.ok().map(|e| e.path())) .filter(|path| path.is_file() && path.extension().is_some_and(|ext| ext == "rs")) .collect::>(); @@ -23,12 +20,13 @@ pub fn scrape() { .crate_name(&dep_stem) .crate_type("bin") .output(&out_dir) - .extern_(crate_name, lib_dir.join(format!("lib{crate_name}.rmeta"))) + .extern_(crate_name, format!("lib{crate_name}.rmeta")) .arg("-Zunstable-options") .arg("--scrape-examples-output-path") .arg(&out_example) .arg("--scrape-examples-target-crate") .arg(crate_name) + .args(extra_args) .run(); out_deps.push(out_example); } @@ -45,5 +43,5 @@ pub fn scrape() { } rustdoc.run(); - assert!(htmldocck().arg(out_dir).arg("src/lib.rs").status().unwrap().success()); + htmldocck().arg(out_dir).arg("src/lib.rs").run(); } diff --git a/tests/run-make/rustdoc-scrape-examples-test/Makefile b/tests/run-make/rustdoc-scrape-examples-test/Makefile deleted file mode 100644 index 1235ead67515..000000000000 --- a/tests/run-make/rustdoc-scrape-examples-test/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -extra_flags := --scrape-tests -deps := ex - -include ../rustdoc-scrape-examples-multiple/scrape.mk - -all: scrape diff --git a/tests/run-make/rustdoc-scrape-examples-test/examples/ex.rs b/tests/run-make/rustdoc-scrape-examples-test/examples/ex.rs index d1a9a74e7825..c37b8dd48853 100644 --- a/tests/run-make/rustdoc-scrape-examples-test/examples/ex.rs +++ b/tests/run-make/rustdoc-scrape-examples-test/examples/ex.rs @@ -2,5 +2,5 @@ fn main() {} #[test] fn a_test() { - foobar::ok(); + foobar::ok(); } diff --git a/tests/run-make/rustdoc-scrape-examples-test/rmake.rs b/tests/run-make/rustdoc-scrape-examples-test/rmake.rs new file mode 100644 index 000000000000..f96ba113ff73 --- /dev/null +++ b/tests/run-make/rustdoc-scrape-examples-test/rmake.rs @@ -0,0 +1,6 @@ +#[path = "../rustdoc-scrape-examples-remap/scrape.rs"] +mod scrape; + +fn main() { + scrape::scrape(&["--scrape-tests"]); +} diff --git a/tests/run-make/rustdoc-scrape-examples-whitespace/Makefile b/tests/run-make/rustdoc-scrape-examples-whitespace/Makefile deleted file mode 100644 index 7786ff762cb3..000000000000 --- a/tests/run-make/rustdoc-scrape-examples-whitespace/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -deps := ex - -include ../rustdoc-scrape-examples-multiple/scrape.mk - -all: scrape diff --git a/tests/run-make/rustdoc-scrape-examples-whitespace/examples/ex.rs b/tests/run-make/rustdoc-scrape-examples-whitespace/examples/ex.rs index 44ff689dfc87..09df782a1367 100644 --- a/tests/run-make/rustdoc-scrape-examples-whitespace/examples/ex.rs +++ b/tests/run-make/rustdoc-scrape-examples-whitespace/examples/ex.rs @@ -1,8 +1,10 @@ struct Foo; impl Foo { - fn bar() { foobar::ok(); } + fn bar() { + foobar::ok(); + } } fn main() { - Foo::bar(); + Foo::bar(); } diff --git a/tests/run-make/rustdoc-scrape-examples-whitespace/rmake.rs b/tests/run-make/rustdoc-scrape-examples-whitespace/rmake.rs new file mode 100644 index 000000000000..e9c54fa39223 --- /dev/null +++ b/tests/run-make/rustdoc-scrape-examples-whitespace/rmake.rs @@ -0,0 +1,6 @@ +#[path = "../rustdoc-scrape-examples-remap/scrape.rs"] +mod scrape; + +fn main() { + scrape::scrape(&[]); +} diff --git a/tests/run-make/rustdoc-shared-flags/rmake.rs b/tests/run-make/rustdoc-shared-flags/rmake.rs index 2db613f78176..d9a16d78a342 100644 --- a/tests/run-make/rustdoc-shared-flags/rmake.rs +++ b/tests/run-make/rustdoc-shared-flags/rmake.rs @@ -1,8 +1,8 @@ use run_make_support::{rustc, rustdoc, Diff}; fn compare_outputs(args: &[&str]) { - let rustc_output = String::from_utf8(rustc().args(args).command_output().stdout).unwrap(); - let rustdoc_output = String::from_utf8(rustdoc().args(args).command_output().stdout).unwrap(); + let rustc_output = rustc().args(args).run().stdout_utf8(); + let rustdoc_output = rustdoc().args(args).run().stdout_utf8(); Diff::new().expected_text("rustc", rustc_output).actual_text("rustdoc", rustdoc_output).run(); } diff --git a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs index 66530a4f08ee..3246fc565064 100644 --- a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs +++ b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs @@ -1,14 +1,14 @@ // Test that rustdoc will properly canonicalize the target spec json path just like rustc. -use run_make_support::{rustc, rustdoc, tmp_dir}; +use run_make_support::{cwd, rustc, rustdoc}; fn main() { - let out_dir = tmp_dir().join("rustdoc-target-spec-json-path"); + let out_dir = "rustdoc-target-spec-json-path"; rustc().crate_type("lib").input("dummy_core.rs").target("target.json").run(); rustdoc() .input("my_crate.rs") .output(out_dir) - .library_search_path(tmp_dir()) + .library_search_path(cwd()) .target("target.json") .run(); } diff --git a/tests/run-make/rustdoc-test-args/rmake.rs b/tests/run-make/rustdoc-test-args/rmake.rs index 66f3f7cf131c..80eb768c14c6 100644 --- a/tests/run-make/rustdoc-test-args/rmake.rs +++ b/tests/run-make/rustdoc-test-args/rmake.rs @@ -1,15 +1,15 @@ -use run_make_support::{rustdoc, tmp_dir}; +use run_make_support::{fs_wrapper, rustdoc}; +use std::iter; use std::path::Path; -use std::{fs, iter}; fn generate_a_lot_of_cfgs(path: &Path) { let content = iter::repeat("--cfg=a\n").take(100_000).collect::(); - fs::write(path, content.as_bytes()).expect("failed to create args file"); + fs_wrapper::write(path, content.as_bytes()); } fn main() { - let arg_file = tmp_dir().join("args"); + let arg_file = Path::new("args"); generate_a_lot_of_cfgs(&arg_file); - rustdoc().out_dir(tmp_dir()).input("foo.rs").arg_file(&arg_file).arg("--test").run(); + rustdoc().input("foo.rs").arg_file(&arg_file).arg("--test").run(); } diff --git a/tests/run-make/rustdoc-themes/rmake.rs b/tests/run-make/rustdoc-themes/rmake.rs index e9da4e259400..4174c0552bea 100644 --- a/tests/run-make/rustdoc-themes/rmake.rs +++ b/tests/run-make/rustdoc-themes/rmake.rs @@ -1,14 +1,15 @@ // Test that rustdoc will properly load in a theme file and display it in the theme selector. -use run_make_support::{htmldocck, rustdoc, source_path, tmp_dir}; +use run_make_support::{fs_wrapper, htmldocck, rustdoc, source_root}; +use std::path::Path; fn main() { - let out_dir = tmp_dir().join("rustdoc-themes"); - let test_css = out_dir.join("test.css"); + let out_dir = Path::new("rustdoc-themes"); + let test_css = "test.css"; - let no_script = - std::fs::read_to_string(source_path().join("src/librustdoc/html/static/css/noscript.css")) - .unwrap(); + let no_script = fs_wrapper::read_to_string( + source_root().join("src/librustdoc/html/static/css/noscript.css"), + ); let mut test_content = String::new(); let mut found_begin_light = false; @@ -23,9 +24,9 @@ fn main() { } } assert!(!test_content.is_empty()); - std::fs::create_dir_all(&out_dir).unwrap(); - std::fs::write(&test_css, test_content).unwrap(); + fs_wrapper::create_dir_all(&out_dir); + fs_wrapper::write(&test_css, test_content); rustdoc().output(&out_dir).input("foo.rs").arg("--theme").arg(&test_css).run(); - assert!(htmldocck().arg(out_dir).arg("foo.rs").status().unwrap().success()); + htmldocck().arg(out_dir).arg("foo.rs").run(); } diff --git a/tests/run-make/rustdoc-verify-output-files/Makefile b/tests/run-make/rustdoc-verify-output-files/Makefile deleted file mode 100644 index 76f233ab445d..000000000000 --- a/tests/run-make/rustdoc-verify-output-files/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -include ../tools.mk - -OUTPUT_DIR := "$(TMPDIR)/rustdoc" -TMP_OUTPUT_DIR := "$(TMPDIR)/tmp-rustdoc" - -all: - # Generate html docs - $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --out-dir $(OUTPUT_DIR) - - # Copy first output for to check if it's exactly same after second compilation - cp -R $(OUTPUT_DIR) $(TMP_OUTPUT_DIR) - - # Generate html docs once again on same output - $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --out-dir $(OUTPUT_DIR) - - # Check if everything exactly same - $(DIFF) -r $(OUTPUT_DIR) $(TMP_OUTPUT_DIR) - - # Generate json doc on the same output - $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --out-dir $(OUTPUT_DIR) -Z unstable-options --output-format json - - # Check if expected json file is generated - [ -e $(OUTPUT_DIR)/foobar.json ] - - # Copy first json output to check if it's exactly same after second compilation - cp -R $(OUTPUT_DIR)/foobar.json $(TMP_OUTPUT_DIR)/foobar.json - - # Generate json doc on the same output - $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --out-dir $(OUTPUT_DIR) -Z unstable-options --output-format json - - # Check if all docs(including both json and html formats) are still the same after multiple compilations - $(DIFF) -r $(OUTPUT_DIR) $(TMP_OUTPUT_DIR) diff --git a/tests/run-make/rustdoc-verify-output-files/rmake.rs b/tests/run-make/rustdoc-verify-output-files/rmake.rs new file mode 100644 index 000000000000..1bf41c68114c --- /dev/null +++ b/tests/run-make/rustdoc-verify-output-files/rmake.rs @@ -0,0 +1,49 @@ +use run_make_support::fs_wrapper::copy; +use std::path::{Path, PathBuf}; + +use run_make_support::{copy_dir_all, recursive_diff, rustdoc}; + +#[derive(PartialEq)] +enum JsonOutput { + Yes, + No, +} + +fn generate_docs(out_dir: &Path, json_output: JsonOutput) { + let mut rustdoc = rustdoc(); + rustdoc.input("src/lib.rs").crate_name("foobar").crate_type("lib").out_dir(&out_dir); + if json_output == JsonOutput::Yes { + rustdoc.arg("-Zunstable-options").output_format("json"); + } + rustdoc.run(); +} + +fn main() { + let out_dir = PathBuf::from("rustdoc"); + let tmp_out_dir = PathBuf::from("tmp-rustdoc"); + + // Generate HTML docs. + generate_docs(&out_dir, JsonOutput::No); + + // Copy first output for to check if it's exactly same after second compilation. + copy_dir_all(&out_dir, &tmp_out_dir); + + // Generate html docs once again on same output. + generate_docs(&out_dir, JsonOutput::No); + + // Generate json doc on the same output. + generate_docs(&out_dir, JsonOutput::Yes); + + // Check if expected json file is generated. + assert!(out_dir.join("foobar.json").is_file()); + + // Copy first json output to check if it's exactly same after second compilation. + copy(out_dir.join("foobar.json"), tmp_out_dir.join("foobar.json")); + + // Generate json doc on the same output. + generate_docs(&out_dir, JsonOutput::Yes); + + // Check if all docs(including both json and html formats) are still the same after multiple + // compilations. + recursive_diff(&out_dir, &tmp_out_dir); +} diff --git a/tests/run-make/rustdoc-with-out-dir-option/rmake.rs b/tests/run-make/rustdoc-with-out-dir-option/rmake.rs index 86471093834f..ded89c9ae791 100644 --- a/tests/run-make/rustdoc-with-out-dir-option/rmake.rs +++ b/tests/run-make/rustdoc-with-out-dir-option/rmake.rs @@ -1,7 +1,7 @@ -use run_make_support::{htmldocck, rustdoc, tmp_dir}; +use run_make_support::{htmldocck, rustdoc}; fn main() { - let out_dir = tmp_dir().join("rustdoc"); + let out_dir = "rustdoc"; rustdoc().input("src/lib.rs").crate_name("foobar").crate_type("lib").output(&out_dir).run(); - assert!(htmldocck().arg(out_dir).arg("src/lib.rs").status().unwrap().success()); + htmldocck().arg(out_dir).arg("src/lib.rs").run(); } diff --git a/tests/run-make/rustdoc-with-output-option/Makefile b/tests/run-make/rustdoc-with-output-option/Makefile deleted file mode 100644 index d0a8205a8ee5..000000000000 --- a/tests/run-make/rustdoc-with-output-option/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -include ../tools.mk - -OUTPUT_DIR := "$(TMPDIR)/rustdoc" - -all: - $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --output $(OUTPUT_DIR) - - $(HTMLDOCCK) $(OUTPUT_DIR) src/lib.rs diff --git a/tests/run-make/rustdoc-with-output-option/rmake.rs b/tests/run-make/rustdoc-with-output-option/rmake.rs new file mode 100644 index 000000000000..f7fbbec6986c --- /dev/null +++ b/tests/run-make/rustdoc-with-output-option/rmake.rs @@ -0,0 +1,16 @@ +use run_make_support::{htmldocck, rustdoc}; + +fn main() { + let out_dir = "rustdoc"; + + rustdoc() + .input("src/lib.rs") + .crate_name("foobar") + .crate_type("lib") + // This is intentionally using `--output` option flag and not the `output()` method. + .arg("--output") + .arg(&out_dir) + .run(); + + htmldocck().arg(out_dir).arg("src/lib.rs").run(); +} diff --git a/tests/run-make/rustdoc-with-short-out-dir-option/Makefile b/tests/run-make/rustdoc-with-short-out-dir-option/Makefile deleted file mode 100644 index 1b9327bce225..000000000000 --- a/tests/run-make/rustdoc-with-short-out-dir-option/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -include ../tools.mk - -OUTPUT_DIR := "$(TMPDIR)/rustdoc" - -all: - $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib -o $(OUTPUT_DIR) - - $(HTMLDOCCK) $(OUTPUT_DIR) src/lib.rs diff --git a/tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs b/tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs new file mode 100644 index 000000000000..4a8896cc975b --- /dev/null +++ b/tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs @@ -0,0 +1,16 @@ +use run_make_support::{htmldocck, rustdoc}; + +fn main() { + let out_dir = "rustdoc"; + + rustdoc() + .input("src/lib.rs") + .crate_name("foobar") + .crate_type("lib") + // This is intentionally using `-o` option flag and not the `output()` method. + .arg("-o") + .arg(&out_dir) + .run(); + + htmldocck().arg(out_dir).arg("src/lib.rs").run(); +} diff --git a/tests/run-make/same-lib-two-locations-no-panic/bar.rs b/tests/run-make/same-lib-two-locations-no-panic/bar.rs deleted file mode 100644 index bb7b36c496ef..000000000000 --- a/tests/run-make/same-lib-two-locations-no-panic/bar.rs +++ /dev/null @@ -1,3 +0,0 @@ -extern crate foo; - -fn main() {} diff --git a/tests/run-make/same-lib-two-locations-no-panic/foo.rs b/tests/run-make/same-lib-two-locations-no-panic/foo.rs deleted file mode 100644 index 82b2dfe9fdb5..000000000000 --- a/tests/run-make/same-lib-two-locations-no-panic/foo.rs +++ /dev/null @@ -1 +0,0 @@ -#![crate_name = "foo"] diff --git a/tests/run-make/same-lib-two-locations-no-panic/rmake.rs b/tests/run-make/same-lib-two-locations-no-panic/rmake.rs deleted file mode 100644 index 2900c3c8b749..000000000000 --- a/tests/run-make/same-lib-two-locations-no-panic/rmake.rs +++ /dev/null @@ -1,28 +0,0 @@ -// A path which contains the same rlib or dylib in two locations -// should not cause an assertion panic in the compiler. -// This test tries to replicate the linked issue and checks -// if the bugged error makes a resurgence. - -// See https://github.com/rust-lang/rust/issues/11908 - -//@ ignore-cross-compile - -use run_make_support::{dynamic_lib, rust_lib, rustc, tmp_dir}; -use std::fs; - -fn main() { - let tmp_dir_other = tmp_dir().join("other"); - - fs::create_dir(&tmp_dir_other); - rustc().input("foo.rs").crate_type("dylib").arg("-Cprefer-dynamic").run(); - fs::rename(dynamic_lib("foo"), &tmp_dir_other); - rustc().input("foo.rs").crate_type("dylib").arg("-Cprefer-dynamic").run(); - rustc().input("bar.rs").library_search_path(&tmp_dir_other).run(); - fs::remove_dir_all(tmp_dir()); - - fs::create_dir_all(&tmp_dir_other); - rustc().input("foo.rs").crate_type("rlib").run(); - fs::rename(rust_lib("foo"), &tmp_dir_other); - rustc().input("foo.rs").crate_type("rlib").run(); - rustc().input("bar.rs").library_search_path(tmp_dir_other).run(); -} diff --git a/tests/run-make/separate-link-fail/Makefile b/tests/run-make/separate-link-fail/Makefile deleted file mode 100644 index bfd18fbf972d..000000000000 --- a/tests/run-make/separate-link-fail/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -include ../tools.mk - -all: - echo 'fn main(){}' > $(TMPDIR)/main.rs - # Make sure that this fails - ! $(RUSTC) -Z link-only $(TMPDIR)/main.rs 2> $(TMPDIR)/stderr.txt - $(CGREP) "The input does not look like a .rlink file" < $(TMPDIR)/stderr.txt diff --git a/tests/run-make/separate-link-fail/foo.rs b/tests/run-make/separate-link-fail/foo.rs new file mode 100644 index 000000000000..f328e4d9d04c --- /dev/null +++ b/tests/run-make/separate-link-fail/foo.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make/separate-link-fail/rmake.rs b/tests/run-make/separate-link-fail/rmake.rs new file mode 100644 index 000000000000..b5d5300de68f --- /dev/null +++ b/tests/run-make/separate-link-fail/rmake.rs @@ -0,0 +1,15 @@ +// rustc usually wants Rust code as its input. The flag `link-only` is one +// exception, where a .rlink file is instead requested. The compiler should +// fail when the user is wrongly passing the original Rust code +// instead of the generated .rlink file when this flag is on. +// https://github.com/rust-lang/rust/issues/95297 + +use run_make_support::rustc; + +fn main() { + rustc() + .arg("-Zlink-only") + .input("foo.rs") + .run_fail() + .assert_stderr_contains("The input does not look like a .rlink file"); +} diff --git a/tests/run-make/separate-link/Makefile b/tests/run-make/separate-link/Makefile deleted file mode 100644 index d01158d9f5fb..000000000000 --- a/tests/run-make/separate-link/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - echo 'fn main(){}' | $(RUSTC) -Z no-link - - $(RUSTC) -Z link-only $(TMPDIR)/rust_out.rlink - $(call RUN,rust_out) diff --git a/tests/run-make/separate-link/rmake.rs b/tests/run-make/separate-link/rmake.rs new file mode 100644 index 000000000000..e91b25489bc5 --- /dev/null +++ b/tests/run-make/separate-link/rmake.rs @@ -0,0 +1,14 @@ +// The compiler flags no-link (and by extension, link-only) used to be broken +// due to changes in encoding/decoding. This was patched, and this test checks +// that these flags are not broken again, resulting in successful compilation. +// See https://github.com/rust-lang/rust/issues/77857 + +//@ ignore-cross-compile + +use run_make_support::{run, rustc}; + +fn main() { + rustc().stdin(b"fn main(){}").arg("-Zno-link").arg("-").run(); + rustc().arg("-Zlink-only").input("rust_out.rlink").run(); + run("rust_out"); +} diff --git a/tests/run-make/share-generics-dylib/linked_leaf.rs b/tests/run-make/share-generics-dylib/linked_leaf.rs index e510dad691c5..200a732d588d 100644 --- a/tests/run-make/share-generics-dylib/linked_leaf.rs +++ b/tests/run-make/share-generics-dylib/linked_leaf.rs @@ -1,11 +1,12 @@ +// Blank line after this one because it must come before `instance_user_{a,b}_rlib`. extern crate instance_user_dylib; + extern crate instance_user_a_rlib; extern crate instance_user_b_rlib; use std::cell::Cell; fn main() { - instance_user_a_rlib::foo(); instance_user_b_rlib::foo(); instance_user_dylib::foo(); diff --git a/tests/run-make/short-ice/Makefile b/tests/run-make/short-ice/Makefile deleted file mode 100644 index 23006fc09e2f..000000000000 --- a/tests/run-make/short-ice/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -include ../tools.mk - -# ignore-windows - -export RUSTC := $(RUSTC_ORIGINAL) -export LD_LIBRARY_PATH := $(HOST_RPATH_DIR) -export TMPDIR := $(TMPDIR) - -all: - bash check.sh diff --git a/tests/run-make/short-ice/check.sh b/tests/run-make/short-ice/check.sh deleted file mode 100644 index 56babd2142f8..000000000000 --- a/tests/run-make/short-ice/check.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/sh -export RUSTC_ICE=0 -RUST_BACKTRACE=1 $RUSTC src/lib.rs -Z treat-err-as-bug=1 1>$TMPDIR/rust-test-1.log 2>&1 -RUST_BACKTRACE=full $RUSTC src/lib.rs -Z treat-err-as-bug=1 1>$TMPDIR/rust-test-2.log 2>&1 - -short=$(cat $TMPDIR/rust-test-1.log | wc -l) -full=$(cat $TMPDIR/rust-test-2.log | wc -l) -rustc_query_count=$(cat $TMPDIR/rust-test-1.log | grep rustc_query_ | wc -l) -rustc_query_count_full=$(cat $TMPDIR/rust-test-2.log | grep rustc_query_ | wc -l) - -begin_count=$(cat $TMPDIR/rust-test-2.log | grep __rust_begin_short_backtrace | wc -l) -end_count=$(cat $TMPDIR/rust-test-2.log | grep __rust_end_short_backtrace | wc -l) - -cat $TMPDIR/rust-test-1.log -echo "=====================" -cat $TMPDIR/rust-test-2.log -echo "=====================" - -echo "short backtrace: $short" -echo "full backtrace: $full" -echo "begin_count: $begin_count" -echo "end_count : $end_count" -echo "rustc_query_count: $rustc_query_count" -echo "rustc_query_count_full: $rustc_query_count_full" - -## backtraces to vary a bit depending on platform and configuration options, -## here we make sure that the short backtrace of rustc_query is shorter than the full, -## and marks are in pairs. -if [ $short -lt $full ] && - [ $begin_count -eq $end_count ] && - [ $(($rustc_query_count + 5)) -lt $rustc_query_count_full ] && - [ $rustc_query_count_full -gt 5 ]; then - exit 0 -else - exit 1 -fi diff --git a/tests/run-make/short-ice/rmake.rs b/tests/run-make/short-ice/rmake.rs new file mode 100644 index 000000000000..81403931c783 --- /dev/null +++ b/tests/run-make/short-ice/rmake.rs @@ -0,0 +1,42 @@ +// Backtraces in internal compiler errors used to be unbearably long, spanning +// multiple hundreds of lines. A fix was pushed in #108938, and this test gathers +// varied metrics on level 1 and full-level backtraces to check that the output +// was shortened down to an appropriate length. +// See https://github.com/rust-lang/rust/issues/107910 + +//@ ignore-windows +// Reason: the assert_eq! on line 32 fails, as error output on Windows is different. + +use run_make_support::rustc; + +fn main() { + let rust_test_1 = + rustc().set_backtrace_level("1").input("src/lib.rs").arg("-Ztreat-err-as-bug=1").run_fail(); + let rust_test_2 = rustc() + .set_backtrace_level("full") + .input("src/lib.rs") + .arg("-Ztreat-err-as-bug=1") + .run_fail(); + + let mut rust_test_log_1 = rust_test_1.stderr_utf8(); + rust_test_log_1.push_str(&rust_test_1.stdout_utf8()); + let rust_test_log_1 = rust_test_log_1.as_str(); + + let mut rust_test_log_2 = rust_test_2.stderr_utf8(); + rust_test_log_2.push_str(&rust_test_2.stdout_utf8()); + let rust_test_log_2 = rust_test_log_2.as_str(); + + let rustc_query_count_full = count_lines_with(rust_test_log_2, "rustc_query_"); + + assert!(rust_test_log_1.lines().count() < rust_test_log_2.lines().count()); + assert_eq!( + count_lines_with(rust_test_log_2, "__rust_begin_short_backtrace"), + count_lines_with(rust_test_log_2, "__rust_end_short_backtrace") + ); + assert!(count_lines_with(rust_test_log_1, "rustc_query_") + 5 < rustc_query_count_full); + assert!(rustc_query_count_full > 5); +} + +fn count_lines_with(s: &str, search: &str) -> usize { + s.lines().filter(|l| l.contains(search)).count() +} diff --git a/tests/run-make/simple-dylib/Makefile b/tests/run-make/simple-dylib/Makefile deleted file mode 100644 index f3e1c1da88ce..000000000000 --- a/tests/run-make/simple-dylib/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# ignore-cross-compile -include ../tools.mk -all: - $(RUSTC) bar.rs --crate-type=dylib -C prefer-dynamic - $(RUSTC) foo.rs - $(call RUN,foo) diff --git a/tests/run-make/simple-dylib/bar.rs b/tests/run-make/simple-dylib/bar.rs deleted file mode 100644 index c5c0bc606cd6..000000000000 --- a/tests/run-make/simple-dylib/bar.rs +++ /dev/null @@ -1 +0,0 @@ -pub fn bar() {} diff --git a/tests/run-make/simple-dylib/foo.rs b/tests/run-make/simple-dylib/foo.rs deleted file mode 100644 index 8d68535e3b64..000000000000 --- a/tests/run-make/simple-dylib/foo.rs +++ /dev/null @@ -1,5 +0,0 @@ -extern crate bar; - -fn main() { - bar::bar(); -} diff --git a/tests/run-make/simple-rlib/Makefile b/tests/run-make/simple-rlib/Makefile deleted file mode 100644 index 28df61a15478..000000000000 --- a/tests/run-make/simple-rlib/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# ignore-cross-compile -include ../tools.mk -all: - $(RUSTC) bar.rs --crate-type=rlib - $(RUSTC) foo.rs - $(call RUN,foo) diff --git a/tests/run-make/simple-rlib/bar.rs b/tests/run-make/simple-rlib/bar.rs deleted file mode 100644 index c5c0bc606cd6..000000000000 --- a/tests/run-make/simple-rlib/bar.rs +++ /dev/null @@ -1 +0,0 @@ -pub fn bar() {} diff --git a/tests/run-make/simple-rlib/foo.rs b/tests/run-make/simple-rlib/foo.rs deleted file mode 100644 index 8d68535e3b64..000000000000 --- a/tests/run-make/simple-rlib/foo.rs +++ /dev/null @@ -1,5 +0,0 @@ -extern crate bar; - -fn main() { - bar::bar(); -} diff --git a/tests/run-make/split-debuginfo/main.rs b/tests/run-make/split-debuginfo/main.rs index 21fa16e40a4a..e815672fa819 100644 --- a/tests/run-make/split-debuginfo/main.rs +++ b/tests/run-make/split-debuginfo/main.rs @@ -1,6 +1,6 @@ extern crate bar; -use bar::{Bar, make_bar}; +use bar::{make_bar, Bar}; fn main() { let b = make_bar(3); diff --git a/tests/run-make/stable-symbol-names/stable-symbol-names1.rs b/tests/run-make/stable-symbol-names/stable-symbol-names1.rs index b85a428278cd..ff20d4306dd7 100644 --- a/tests/run-make/stable-symbol-names/stable-symbol-names1.rs +++ b/tests/run-make/stable-symbol-names/stable-symbol-names1.rs @@ -1,31 +1,31 @@ -#![crate_type="rlib"] +#![crate_type = "rlib"] pub trait Foo { - fn generic_method(); + fn generic_method(); } pub struct Bar; impl Foo for Bar { - fn generic_method() {} + fn generic_method() {} } pub fn mono_function() { - Bar::generic_method::(); + Bar::generic_method::(); } pub fn mono_function_lifetime<'a>(x: &'a u64) -> u64 { - *x + *x } pub fn generic_function(t: T) -> T { - t + t } pub fn user() { - generic_function(0u32); - generic_function("abc"); - let x = 2u64; - generic_function(&x); - let _ = mono_function_lifetime(&x); + generic_function(0u32); + generic_function("abc"); + let x = 2u64; + generic_function(&x); + let _ = mono_function_lifetime(&x); } diff --git a/tests/run-make/stable-symbol-names/stable-symbol-names2.rs b/tests/run-make/stable-symbol-names/stable-symbol-names2.rs index 33df9d6c6898..6a50c2a4f573 100644 --- a/tests/run-make/stable-symbol-names/stable-symbol-names2.rs +++ b/tests/run-make/stable-symbol-names/stable-symbol-names2.rs @@ -1,17 +1,17 @@ -#![crate_type="rlib"] +#![crate_type = "rlib"] extern crate stable_symbol_names1; pub fn user() { - stable_symbol_names1::generic_function(1u32); - stable_symbol_names1::generic_function("def"); - let x = 2u64; - stable_symbol_names1::generic_function(&x); - stable_symbol_names1::mono_function(); - stable_symbol_names1::mono_function_lifetime(&0); + stable_symbol_names1::generic_function(1u32); + stable_symbol_names1::generic_function("def"); + let x = 2u64; + stable_symbol_names1::generic_function(&x); + stable_symbol_names1::mono_function(); + stable_symbol_names1::mono_function_lifetime(&0); } pub fn trait_impl_test_function() { - use stable_symbol_names1::*; - Bar::generic_method::(); + use stable_symbol_names1::*; + Bar::generic_method::(); } diff --git a/tests/run-make/static-unwinding/lib.rs b/tests/run-make/static-unwinding/lib.rs index 3fb1117a1105..975de43cfd8d 100644 --- a/tests/run-make/static-unwinding/lib.rs +++ b/tests/run-make/static-unwinding/lib.rs @@ -5,11 +5,16 @@ pub static mut statik: isize = 0; struct A; impl Drop for A { fn drop(&mut self) { - unsafe { statik = 1; } + unsafe { + statik = 1; + } } } -pub fn callback(f: F) where F: FnOnce() { +pub fn callback(f: F) +where + F: FnOnce(), +{ let _a = A; f(); } diff --git a/tests/run-make/static-unwinding/main.rs b/tests/run-make/static-unwinding/main.rs index 0c66ea1aa07f..ceb0a24804c5 100644 --- a/tests/run-make/static-unwinding/main.rs +++ b/tests/run-make/static-unwinding/main.rs @@ -7,15 +7,19 @@ static mut statik: isize = 0; struct A; impl Drop for A { fn drop(&mut self) { - unsafe { statik = 1; } + unsafe { + statik = 1; + } } } fn main() { - thread::spawn(move|| { + thread::spawn(move || { let _a = A; lib::callback(|| panic!()); - }).join().unwrap_err(); + }) + .join() + .unwrap_err(); unsafe { assert_eq!(lib::statik, 1); diff --git a/tests/run-make/stdin-rustc/rmake.rs b/tests/run-make/stdin-rustc/rmake.rs index c07a6df4d84a..d599f813563c 100644 --- a/tests/run-make/stdin-rustc/rmake.rs +++ b/tests/run-make/stdin-rustc/rmake.rs @@ -1,6 +1,7 @@ //! This test checks rustc `-` (stdin) support -use run_make_support::{is_windows, rustc, tmp_dir}; +use run_make_support::{is_windows, rustc}; +use std::path::PathBuf; const HELLO_WORLD: &str = r#" fn main() { @@ -11,16 +12,16 @@ fn main() { const NOT_UTF8: &[u8] = &[0xff, 0xff, 0xff]; fn main() { - let out_dir = tmp_dir(); - // echo $HELLO_WORLD | rustc - rustc().arg("-").stdin(HELLO_WORLD).run(); assert!( - out_dir.join(if !is_windows() { "rust_out" } else { "rust_out.exe" }).try_exists().unwrap() + PathBuf::from(if !is_windows() { "rust_out" } else { "rust_out.exe" }) + .try_exists() + .unwrap() ); // echo $NOT_UTF8 | rustc - - let output = rustc().arg("-").stdin(NOT_UTF8).run_fail(); - let stderr = String::from_utf8(output.stderr).unwrap(); - assert!(stderr.contains("error: couldn't read from stdin, as it did not contain valid UTF-8")); + rustc().arg("-").stdin(NOT_UTF8).run_fail().assert_stderr_contains( + "error: couldn't read from stdin, as it did not contain valid UTF-8", + ); } diff --git a/tests/run-make/stdin-rustdoc/rmake.rs b/tests/run-make/stdin-rustdoc/rmake.rs index 584a610ed63f..a72fe1bc7dae 100644 --- a/tests/run-make/stdin-rustdoc/rmake.rs +++ b/tests/run-make/stdin-rustdoc/rmake.rs @@ -1,6 +1,7 @@ //! This test checks rustdoc `-` (stdin) handling -use run_make_support::{rustdoc, tmp_dir}; +use run_make_support::rustdoc; +use std::path::PathBuf; static INPUT: &str = r#" //! ``` @@ -10,8 +11,7 @@ pub struct F; "#; fn main() { - let tmp_dir = tmp_dir(); - let out_dir = tmp_dir.join("doc"); + let out_dir = PathBuf::from("doc"); // rustdoc - rustdoc().arg("-").out_dir(&out_dir).stdin(INPUT).run(); diff --git a/tests/run-make/suspicious-library/Makefile b/tests/run-make/suspicious-library/Makefile deleted file mode 100644 index 3b5ab3c53a56..000000000000 --- a/tests/run-make/suspicious-library/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) foo.rs -C prefer-dynamic - touch $(call DYLIB,foo-something-special) - touch $(call DYLIB,foo-something-special2) - $(RUSTC) bar.rs diff --git a/tests/run-make/suspicious-library/bar.rs b/tests/run-make/suspicious-library/bar.rs index 550c94cd0c60..030693a694f9 100644 --- a/tests/run-make/suspicious-library/bar.rs +++ b/tests/run-make/suspicious-library/bar.rs @@ -1,3 +1,5 @@ extern crate foo; -fn main() { foo::foo() } +fn main() { + foo::foo() +} diff --git a/tests/run-make/suspicious-library/rmake.rs b/tests/run-make/suspicious-library/rmake.rs new file mode 100644 index 000000000000..e789607482b8 --- /dev/null +++ b/tests/run-make/suspicious-library/rmake.rs @@ -0,0 +1,14 @@ +// This test creates some fake dynamic libraries with nothing inside, +// and checks if rustc avoids them and successfully compiles as a result. + +//@ ignore-cross-compile + +use run_make_support::{dynamic_lib_name, rustc}; +use std::fs::File; + +fn main() { + rustc().input("foo.rs").arg("-Cprefer-dynamic").run(); + File::create(dynamic_lib_name("foo-something-special")).unwrap(); + File::create(dynamic_lib_name("foo-something-special2")).unwrap(); + rustc().input("bar.rs").run(); +} diff --git a/tests/run-make/symbol-mangling-hashed/Makefile b/tests/run-make/symbol-mangling-hashed/Makefile index 68894b2192ab..c95036ead958 100644 --- a/tests/run-make/symbol-mangling-hashed/Makefile +++ b/tests/run-make/symbol-mangling-hashed/Makefile @@ -35,10 +35,10 @@ all: # Check hashed symbol name [ "$$($(NM) $(TMPDIR)/$(DYLIB_NAME) | grep -c hello)" -eq "0" ] - [ "$$($(NM) $(TMPDIR)/$(DYLIB_NAME) | grep _RNxC7a_dylib | grep -c ' T ')" -eq "1" ] + [ "$$($(NM) $(TMPDIR)/$(DYLIB_NAME) | grep _RNxC7a_dylib | grep -c ' T ')" -eq "2" ] [ "$$($(NM) $(TMPDIR)/$(SO_NAME) | grep b_dylib | grep -c hello)" -eq "1" ] - [ "$$($(NM) $(TMPDIR)/$(SO_NAME) | grep _RNxC6a_rlib | grep -c ' T ')" -eq "1" ] + [ "$$($(NM) $(TMPDIR)/$(SO_NAME) | grep _RNxC6a_rlib | grep -c ' T ')" -eq "2" ] [ "$$($(NM) $(TMPDIR)/$(SO_NAME) | grep _RNxC7a_dylib | grep -c ' U ')" -eq "1" ] [ "$$($(NM) $(TMPDIR)/$(BIN_NAME) | grep _RNxC6a_rlib | grep -c ' U ')" -eq "1" ] diff --git a/tests/run-make/symbol-mangling-hashed/a_dylib.rs b/tests/run-make/symbol-mangling-hashed/a_dylib.rs index 8aec8fd82a53..49d65b72cacc 100644 --- a/tests/run-make/symbol-mangling-hashed/a_dylib.rs +++ b/tests/run-make/symbol-mangling-hashed/a_dylib.rs @@ -1,4 +1,4 @@ -#![crate_type="dylib"] +#![crate_type = "dylib"] pub fn hello() { println!("hello dylib"); } diff --git a/tests/run-make/symbol-mangling-hashed/a_rlib.rs b/tests/run-make/symbol-mangling-hashed/a_rlib.rs index 873c86c5d0b4..71e44ccc2007 100644 --- a/tests/run-make/symbol-mangling-hashed/a_rlib.rs +++ b/tests/run-make/symbol-mangling-hashed/a_rlib.rs @@ -1,4 +1,4 @@ -#![crate_type="rlib"] +#![crate_type = "rlib"] pub fn hello() { println!("hello rlib"); diff --git a/tests/run-make/symbol-mangling-hashed/b_bin.rs b/tests/run-make/symbol-mangling-hashed/b_bin.rs index bcc53c37e122..8ee7fecda62a 100644 --- a/tests/run-make/symbol-mangling-hashed/b_bin.rs +++ b/tests/run-make/symbol-mangling-hashed/b_bin.rs @@ -1,5 +1,5 @@ -extern crate a_rlib; extern crate a_dylib; +extern crate a_rlib; extern crate b_dylib; fn main() { diff --git a/tests/run-make/symbol-mangling-hashed/b_dylib.rs b/tests/run-make/symbol-mangling-hashed/b_dylib.rs index c26a04b39ec3..3252c9c75c2a 100644 --- a/tests/run-make/symbol-mangling-hashed/b_dylib.rs +++ b/tests/run-make/symbol-mangling-hashed/b_dylib.rs @@ -1,7 +1,7 @@ -#![crate_type="dylib"] +#![crate_type = "dylib"] -extern crate a_rlib; extern crate a_dylib; +extern crate a_rlib; pub fn hello() { a_rlib::hello(); diff --git a/tests/run-make/symbol-visibility/a_cdylib.rs b/tests/run-make/symbol-visibility/a_cdylib.rs index d4fbff85bfe6..cc034c8a31e5 100644 --- a/tests/run-make/symbol-visibility/a_cdylib.rs +++ b/tests/run-make/symbol-visibility/a_cdylib.rs @@ -1,4 +1,4 @@ -#![crate_type="cdylib"] +#![crate_type = "cdylib"] extern crate an_rlib; diff --git a/tests/run-make/symbol-visibility/a_rust_dylib.rs b/tests/run-make/symbol-visibility/a_rust_dylib.rs index a47df0ab7eed..22c0482343ee 100644 --- a/tests/run-make/symbol-visibility/a_rust_dylib.rs +++ b/tests/run-make/symbol-visibility/a_rust_dylib.rs @@ -1,4 +1,4 @@ -#![crate_type="dylib"] +#![crate_type = "dylib"] extern crate an_rlib; @@ -12,4 +12,6 @@ pub extern "C" fn public_c_function_from_rust_dylib() { } // This should be exported if -Zshare-generics=yes -pub fn public_generic_function_from_rust_dylib(x: T) -> T { x } +pub fn public_generic_function_from_rust_dylib(x: T) -> T { + x +} diff --git a/tests/run-make/symbol-visibility/an_executable.rs b/tests/run-make/symbol-visibility/an_executable.rs index 3f5e125ad193..945873bd9ce1 100644 --- a/tests/run-make/symbol-visibility/an_executable.rs +++ b/tests/run-make/symbol-visibility/an_executable.rs @@ -1,4 +1,4 @@ -#![crate_type="bin"] +#![crate_type = "bin"] extern crate an_rlib; diff --git a/tests/run-make/symbol-visibility/an_rlib.rs b/tests/run-make/symbol-visibility/an_rlib.rs index 3696422b11e9..b291dbe571cd 100644 --- a/tests/run-make/symbol-visibility/an_rlib.rs +++ b/tests/run-make/symbol-visibility/an_rlib.rs @@ -1,4 +1,4 @@ -#![crate_type="rlib"] +#![crate_type = "rlib"] pub fn public_rust_function_from_rlib() {} diff --git a/tests/run-make/symlinked-extern/Makefile b/tests/run-make/symlinked-extern/Makefile deleted file mode 100644 index 28c764b84e8b..000000000000 --- a/tests/run-make/symlinked-extern/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# ignore-windows -# `ln` is actually `cp` on msys. - -all: - $(RUSTC) foo.rs - mkdir -p $(TMPDIR)/other - ln -nsf $(TMPDIR)/libfoo.rlib $(TMPDIR)/other - $(RUSTC) bar.rs -L $(TMPDIR) - $(RUSTC) baz.rs --extern foo=$(TMPDIR)/other/libfoo.rlib -L $(TMPDIR) diff --git a/tests/run-make/symlinked-extern/bar.rs b/tests/run-make/symlinked-extern/bar.rs index cd9c959d5e6a..895582d5f13b 100644 --- a/tests/run-make/symlinked-extern/bar.rs +++ b/tests/run-make/symlinked-extern/bar.rs @@ -2,5 +2,4 @@ extern crate foo; -pub fn bar(_s: foo::S) { -} +pub fn bar(_s: foo::S) {} diff --git a/tests/run-make/symlinked-extern/foo.rs b/tests/run-make/symlinked-extern/foo.rs index c00700b8cf83..4dcf6e135f60 100644 --- a/tests/run-make/symlinked-extern/foo.rs +++ b/tests/run-make/symlinked-extern/foo.rs @@ -2,4 +2,6 @@ pub struct S; -pub fn foo() -> S { S } +pub fn foo() -> S { + S +} diff --git a/tests/run-make/symlinked-extern/rmake.rs b/tests/run-make/symlinked-extern/rmake.rs new file mode 100644 index 000000000000..98f69aefbe62 --- /dev/null +++ b/tests/run-make/symlinked-extern/rmake.rs @@ -0,0 +1,21 @@ +// Crates that are resolved normally have their path canonicalized and all +// symlinks resolved. This did not happen for paths specified +// using the --extern option to rustc, which could lead to rustc thinking +// that it encountered two different versions of a crate, when it's +// actually the same version found through different paths. +// See https://github.com/rust-lang/rust/pull/16505 + +// This test checks that --extern and symlinks together +// can result in successful compilation. + +//@ ignore-cross-compile + +use run_make_support::{create_symlink, cwd, fs_wrapper, rustc}; + +fn main() { + rustc().input("foo.rs").run(); + fs_wrapper::create_dir_all("other"); + create_symlink("libfoo.rlib", "other"); + rustc().input("bar.rs").library_search_path(cwd()).run(); + rustc().input("baz.rs").extern_("foo", "other").library_search_path(cwd()).run(); +} diff --git a/tests/run-make/symlinked-libraries/Makefile b/tests/run-make/symlinked-libraries/Makefile deleted file mode 100644 index fb0b6127e6f5..000000000000 --- a/tests/run-make/symlinked-libraries/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# ignore-windows -# `ln` is actually `cp` on msys. - -all: - $(RUSTC) foo.rs -C prefer-dynamic - mkdir -p $(TMPDIR)/other - ln -nsf $(TMPDIR)/$(call DYLIB_GLOB,foo) $(TMPDIR)/other - $(RUSTC) bar.rs -L $(TMPDIR)/other diff --git a/tests/run-make/symlinked-libraries/rmake.rs b/tests/run-make/symlinked-libraries/rmake.rs new file mode 100644 index 000000000000..eaf0c44206a5 --- /dev/null +++ b/tests/run-make/symlinked-libraries/rmake.rs @@ -0,0 +1,16 @@ +// When a directory and a symlink simultaneously exist with the same name, +// setting that name as the library search path should not cause rustc +// to avoid looking in the symlink and cause an error. This test creates +// a directory and a symlink named "other", and places the library in the symlink. +// If it succeeds, the library was successfully found. +// See https://github.com/rust-lang/rust/issues/12459 + +//@ ignore-cross-compile +use run_make_support::{create_symlink, dynamic_lib_name, fs_wrapper, rustc}; + +fn main() { + rustc().input("foo.rs").arg("-Cprefer-dynamic").run(); + fs_wrapper::create_dir_all("other"); + create_symlink(dynamic_lib_name("foo"), "other"); + rustc().input("bar.rs").library_search_path("other").run(); +} diff --git a/tests/run-make/symlinked-rlib/Makefile b/tests/run-make/symlinked-rlib/Makefile deleted file mode 100644 index a8565f683c3e..000000000000 --- a/tests/run-make/symlinked-rlib/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# ignore-windows -# `ln` is actually `cp` on msys. - -all: - $(RUSTC) foo.rs --crate-type=rlib -o $(TMPDIR)/foo.xxx - ln -nsf $(TMPDIR)/foo.xxx $(TMPDIR)/libfoo.rlib - $(RUSTC) bar.rs -L $(TMPDIR) diff --git a/tests/run-make/symlinked-rlib/rmake.rs b/tests/run-make/symlinked-rlib/rmake.rs new file mode 100644 index 000000000000..3759ca25928a --- /dev/null +++ b/tests/run-make/symlinked-rlib/rmake.rs @@ -0,0 +1,16 @@ +// Rustc did not recognize libraries which were symlinked +// to files having extension other than .rlib. This was fixed +// in #32828. This test creates a symlink to "foo.xxx", which has +// an unusual file extension, and checks that rustc can successfully +// use it as an rlib library. +// See https://github.com/rust-lang/rust/pull/32828 + +//@ ignore-cross-compile + +use run_make_support::{create_symlink, cwd, rustc}; + +fn main() { + rustc().input("foo.rs").crate_type("rlib").output("foo.xxx").run(); + create_symlink("foo.xxx", "libfoo.rlib"); + rustc().input("bar.rs").library_search_path(cwd()).run(); +} diff --git a/tests/run-make/target-cpu-native/foo.rs b/tests/run-make/target-cpu-native/foo.rs index f79c691f0853..f328e4d9d04c 100644 --- a/tests/run-make/target-cpu-native/foo.rs +++ b/tests/run-make/target-cpu-native/foo.rs @@ -1,2 +1 @@ -fn main() { -} +fn main() {} diff --git a/tests/run-make/test-harness/test-ignore-cfg.rs b/tests/run-make/test-harness/test-ignore-cfg.rs index 31ef131f2ad7..c6bd7680bd5c 100644 --- a/tests/run-make/test-harness/test-ignore-cfg.rs +++ b/tests/run-make/test-harness/test-ignore-cfg.rs @@ -1,9 +1,7 @@ #[test] #[cfg_attr(ignorecfg, ignore)] -fn shouldignore() { -} +fn shouldignore() {} #[test] #[cfg_attr(noignorecfg, ignore)] -fn shouldnotignore() { -} +fn shouldnotignore() {} diff --git a/tests/run-make/track-pgo-dep-info/Makefile b/tests/run-make/track-pgo-dep-info/Makefile index 6c7f67d0f0a9..3afe3662fa75 100644 --- a/tests/run-make/track-pgo-dep-info/Makefile +++ b/tests/run-make/track-pgo-dep-info/Makefile @@ -1,5 +1,4 @@ # needs-profiler-support -# ignore-windows-gnu include ../tools.mk diff --git a/tests/run-make/type-mismatch-same-crate-name/crateA.rs b/tests/run-make/type-mismatch-same-crate-name/crateA.rs index 4871c8c2e9cc..0dccfbb4bed6 100644 --- a/tests/run-make/type-mismatch-same-crate-name/crateA.rs +++ b/tests/run-make/type-mismatch-same-crate-name/crateA.rs @@ -3,7 +3,7 @@ mod foo { } mod bar { - pub trait Bar{} + pub trait Bar {} pub fn bar() -> Box { unimplemented!() @@ -12,5 +12,5 @@ mod bar { // This makes the publicly accessible path // differ from the internal one. +pub use bar::{bar, Bar}; pub use foo::Foo; -pub use bar::{Bar, bar}; diff --git a/tests/run-make/type-mismatch-same-crate-name/crateB.rs b/tests/run-make/type-mismatch-same-crate-name/crateB.rs index 24fcc7cadc10..e946c0ecac76 100644 --- a/tests/run-make/type-mismatch-same-crate-name/crateB.rs +++ b/tests/run-make/type-mismatch-same-crate-name/crateB.rs @@ -1,4 +1,4 @@ extern crate crateA; -pub fn try_foo(x: crateA::Foo){} -pub fn try_bar(x: Box){} +pub fn try_foo(x: crateA::Foo) {} +pub fn try_bar(x: Box) {} diff --git a/tests/run-make/use-suggestions-rust-2018/use-suggestions.rs b/tests/run-make/use-suggestions-rust-2018/use-suggestions.rs index d262d6f9877c..1bb987d0ebdf 100644 --- a/tests/run-make/use-suggestions-rust-2018/use-suggestions.rs +++ b/tests/run-make/use-suggestions-rust-2018/use-suggestions.rs @@ -1,3 +1,3 @@ fn main() { - let x = Baz{}; + let x = Baz {}; } diff --git a/tests/run-make/used-cdylib-macos/Makefile b/tests/run-make/used-cdylib-macos/Makefile index 38a4c31c7b3a..bdf914a1ca95 100644 --- a/tests/run-make/used-cdylib-macos/Makefile +++ b/tests/run-make/used-cdylib-macos/Makefile @@ -1,9 +1,9 @@ include ../tools.mk -# only-macos +# only-apple # # This checks that `#[used]` passes through to the linker on -# darwin. This is subject to change in the future, see +# Apple targets. This is subject to change in the future, see # https://github.com/rust-lang/rust/pull/93718 for discussion all: diff --git a/tests/run-make/used/Makefile b/tests/run-make/used/Makefile deleted file mode 100644 index e80eb9e40208..000000000000 --- a/tests/run-make/used/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -include ../tools.mk - -# ignore-windows-msvc - -all: - $(RUSTC) -C opt-level=3 --emit=obj used.rs - nm $(TMPDIR)/used.o | $(CGREP) FOO diff --git a/tests/run-make/used/rmake.rs b/tests/run-make/used/rmake.rs new file mode 100644 index 000000000000..56ef5c6b9cc0 --- /dev/null +++ b/tests/run-make/used/rmake.rs @@ -0,0 +1,15 @@ +// This test ensures that the compiler is keeping static variables, even if not referenced +// by another part of the program, in the output object file. +// +// It comes from #39987 which implements this RFC for the #[used] attribute: +// https://rust-lang.github.io/rfcs/2386-used.html + +//@ ignore-msvc + +use run_make_support::{cmd, rustc}; + +fn main() { + rustc().opt_level("3").emit("obj").input("used.rs").run(); + + cmd("nm").arg("used.o").run().assert_stdout_contains("FOO"); +} diff --git a/tests/run-make/wasm-abi/rmake.rs b/tests/run-make/wasm-abi/rmake.rs index a2dcafbbe0f1..0fc326babd94 100644 --- a/tests/run-make/wasm-abi/rmake.rs +++ b/tests/run-make/wasm-abi/rmake.rs @@ -1,14 +1,14 @@ //@ only-wasm32-wasip1 //@ needs-wasmtime -use run_make_support::{rustc, tmp_dir}; +use run_make_support::rustc; use std::path::Path; use std::process::Command; fn main() { rustc().input("foo.rs").target("wasm32-wasip1").run(); - let file = tmp_dir().join("foo.wasm"); + let file = Path::new("foo.wasm"); run(&file, "return_two_i32", "1\n2\n"); run(&file, "return_two_i64", "3\n4\n"); diff --git a/tests/run-make/wasm-custom-section/bar.rs b/tests/run-make/wasm-custom-section/bar.rs index c95f3e1438b7..1e94e99ff086 100644 --- a/tests/run-make/wasm-custom-section/bar.rs +++ b/tests/run-make/wasm-custom-section/bar.rs @@ -10,4 +10,4 @@ pub static A: [u8; 2] = [5, 6]; pub static B: [u8; 2] = [7, 8]; #[no_mangle] -pub extern fn foo() {} +pub extern "C" fn foo() {} diff --git a/tests/run-make/wasm-custom-section/rmake.rs b/tests/run-make/wasm-custom-section/rmake.rs index 0303ca05ca63..e958f5086ac6 100644 --- a/tests/run-make/wasm-custom-section/rmake.rs +++ b/tests/run-make/wasm-custom-section/rmake.rs @@ -1,13 +1,13 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{fs_wrapper, rustc, wasmparser}; use std::collections::HashMap; fn main() { rustc().input("foo.rs").target("wasm32-wasip1").run(); rustc().input("bar.rs").target("wasm32-wasip1").arg("-Clto").opt().run(); - let file = std::fs::read(&tmp_dir().join("bar.wasm")).unwrap(); + let file = fs_wrapper::read("bar.wasm"); let mut custom = HashMap::new(); for payload in wasmparser::Parser::new(0).parse_all(&file) { diff --git a/tests/run-make/wasm-custom-sections-opt/foo.rs b/tests/run-make/wasm-custom-sections-opt/foo.rs index 9af7728b7f3a..f521dd937a0e 100644 --- a/tests/run-make/wasm-custom-sections-opt/foo.rs +++ b/tests/run-make/wasm-custom-sections-opt/foo.rs @@ -13,7 +13,7 @@ pub mod another { } #[no_mangle] -pub extern fn foo() { +pub extern "C" fn foo() { // This will import `another::foo` through ThinLTO passes, and it better not // also accidentally import the `FOO` custom section into this module as // well diff --git a/tests/run-make/wasm-custom-sections-opt/rmake.rs b/tests/run-make/wasm-custom-sections-opt/rmake.rs index 50916b1bf815..346cffecc774 100644 --- a/tests/run-make/wasm-custom-sections-opt/rmake.rs +++ b/tests/run-make/wasm-custom-sections-opt/rmake.rs @@ -1,18 +1,18 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{fs_wrapper, rustc, wasmparser}; use std::collections::HashMap; use std::path::Path; fn main() { rustc().input("foo.rs").target("wasm32-wasip1").opt().run(); - verify(&tmp_dir().join("foo.wasm")); + verify(Path::new("foo.wasm")); } fn verify(path: &Path) { eprintln!("verify {path:?}"); - let file = std::fs::read(&path).unwrap(); + let file = fs_wrapper::read(&path); let mut custom = HashMap::new(); for payload in wasmparser::Parser::new(0).parse_all(&file) { diff --git a/tests/run-make/wasm-exceptions-nostd/src/arena_alloc.rs b/tests/run-make/wasm-exceptions-nostd/src/arena_alloc.rs index 572d253309ce..12f1b4d8d12f 100644 --- a/tests/run-make/wasm-exceptions-nostd/src/arena_alloc.rs +++ b/tests/run-make/wasm-exceptions-nostd/src/arena_alloc.rs @@ -14,9 +14,7 @@ pub struct ArenaAllocator { impl ArenaAllocator { pub const fn new() -> Self { - Self { - arena: UnsafeCell::new(Arena::new()), - } + Self { arena: UnsafeCell::new(Arena::new()) } } } @@ -42,10 +40,7 @@ struct Arena { impl Arena { pub const fn new() -> Self { - Self { - buf: [0x55; ARENA_SIZE], - allocated: 0, - } + Self { buf: [0x55; ARENA_SIZE], allocated: 0 } } pub unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 { diff --git a/tests/run-make/wasm-exceptions-nostd/src/lib.rs b/tests/run-make/wasm-exceptions-nostd/src/lib.rs index 9d336510469c..3ea8797d3a6d 100644 --- a/tests/run-make/wasm-exceptions-nostd/src/lib.rs +++ b/tests/run-make/wasm-exceptions-nostd/src/lib.rs @@ -1,9 +1,7 @@ #![no_std] #![crate_type = "cdylib"] - // Allow a few unstable features because we create a panic // runtime for native wasm exceptions from scratch - #![feature(core_intrinsics)] #![feature(lang_items)] #![feature(link_llvm_intrinsics)] @@ -39,20 +37,24 @@ pub extern "C" fn start() -> usize { let data = 0x1234usize as *mut u8; // Something to recognize unsafe { - core::intrinsics::catch_unwind(|data: *mut u8| { - let _log_on_drop = LogOnDrop; + core::intrinsics::catch_unwind( + |data: *mut u8| { + let _log_on_drop = LogOnDrop; - logging::log_str(&alloc::format!("`r#try` called with ptr {:?}", data)); - let x = [12]; - let _ = x[4]; // should panic + logging::log_str(&alloc::format!("`r#try` called with ptr {:?}", data)); + let x = [12]; + let _ = x[4]; // should panic - logging::log_str("This line should not be visible! :("); - }, data, |data, exception| { - let exception = *Box::from_raw(exception as *mut String); - logging::log_str("Caught something!"); - logging::log_str(&alloc::format!(" data : {:?}", data)); - logging::log_str(&alloc::format!(" exception: {:?}", exception)); - }); + logging::log_str("This line should not be visible! :("); + }, + data, + |data, exception| { + let exception = *Box::from_raw(exception as *mut String); + logging::log_str("Caught something!"); + logging::log_str(&alloc::format!(" data : {:?}", data)); + logging::log_str(&alloc::format!(" exception: {:?}", exception)); + }, + ); } logging::log_str("This program terminates correctly."); diff --git a/tests/run-make/wasm-exceptions-nostd/src/panicking.rs b/tests/run-make/wasm-exceptions-nostd/src/panicking.rs index 4a8923fd43db..52a32f3cd303 100644 --- a/tests/run-make/wasm-exceptions-nostd/src/panicking.rs +++ b/tests/run-make/wasm-exceptions-nostd/src/panicking.rs @@ -17,10 +17,7 @@ fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { use alloc::boxed::Box; use alloc::string::ToString; - let msg = info - .message() - .map(|msg| msg.to_string()) - .unwrap_or("(no message)".to_string()); + let msg = info.message().map(|msg| msg.to_string()).unwrap_or("(no message)".to_string()); let exception = Box::new(msg.to_string()); unsafe { let exception_raw = Box::into_raw(exception); diff --git a/tests/run-make/wasm-export-all-symbols/bar.rs b/tests/run-make/wasm-export-all-symbols/bar.rs index ac9c20a57e5a..039291f902e5 100644 --- a/tests/run-make/wasm-export-all-symbols/bar.rs +++ b/tests/run-make/wasm-export-all-symbols/bar.rs @@ -1,7 +1,7 @@ #![crate_type = "rlib"] #[no_mangle] -pub extern fn foo() {} +pub extern "C" fn foo() {} #[no_mangle] pub static FOO: u64 = 42; diff --git a/tests/run-make/wasm-export-all-symbols/rmake.rs b/tests/run-make/wasm-export-all-symbols/rmake.rs index f4c51bc4ab41..41a2eaaafc31 100644 --- a/tests/run-make/wasm-export-all-symbols/rmake.rs +++ b/tests/run-make/wasm-export-all-symbols/rmake.rs @@ -1,6 +1,6 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{fs_wrapper, rustc, wasmparser}; use std::collections::HashMap; use std::path::Path; use wasmparser::ExternalKind::*; @@ -18,12 +18,9 @@ fn test(args: &[&str]) { rustc().input("foo.rs").target("wasm32-wasip1").args(args).run(); rustc().input("main.rs").target("wasm32-wasip1").args(args).run(); + verify_exports(Path::new("foo.wasm"), &[("foo", Func), ("FOO", Global), ("memory", Memory)]); verify_exports( - &tmp_dir().join("foo.wasm"), - &[("foo", Func), ("FOO", Global), ("memory", Memory)], - ); - verify_exports( - &tmp_dir().join("main.wasm"), + Path::new("main.wasm"), &[ ("foo", Func), ("FOO", Global), @@ -36,7 +33,7 @@ fn test(args: &[&str]) { fn verify_exports(path: &Path, exports: &[(&str, wasmparser::ExternalKind)]) { println!("verify {path:?}"); - let file = std::fs::read(path).unwrap(); + let file = fs_wrapper::read(path); let mut wasm_exports = HashMap::new(); for payload in wasmparser::Parser::new(0).parse_all(&file) { let payload = payload.unwrap(); diff --git a/tests/run-make/wasm-import-module/rmake.rs b/tests/run-make/wasm-import-module/rmake.rs index 6eed229e9078..881242c59a27 100644 --- a/tests/run-make/wasm-import-module/rmake.rs +++ b/tests/run-make/wasm-import-module/rmake.rs @@ -1,6 +1,6 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{fs_wrapper, rustc, wasmparser}; use std::collections::HashMap; use wasmparser::TypeRef::Func; @@ -8,7 +8,7 @@ fn main() { rustc().input("foo.rs").target("wasm32-wasip1").run(); rustc().input("bar.rs").target("wasm32-wasip1").arg("-Clto").opt().run(); - let file = std::fs::read(&tmp_dir().join("bar.wasm")).unwrap(); + let file = fs_wrapper::read("bar.wasm"); let mut imports = HashMap::new(); for payload in wasmparser::Parser::new(0).parse_all(&file) { diff --git a/tests/run-make/wasm-override-linker/Makefile b/tests/run-make/wasm-override-linker/Makefile index 52339f9261c4..1a01a574dee2 100644 --- a/tests/run-make/wasm-override-linker/Makefile +++ b/tests/run-make/wasm-override-linker/Makefile @@ -1,4 +1,7 @@ -# needs-matching-clang +# needs-force-clang-based-tests + +# FIXME(#126180): This test doesn't actually run anywhere, because the only +# CI job that sets RUSTBUILD_FORCE_CLANG_BASED_TESTS runs very few tests. include ../tools.mk diff --git a/tests/run-make/wasm-panic-small/rmake.rs b/tests/run-make/wasm-panic-small/rmake.rs index 373b966401ce..891cac4bc9fb 100644 --- a/tests/run-make/wasm-panic-small/rmake.rs +++ b/tests/run-make/wasm-panic-small/rmake.rs @@ -1,7 +1,7 @@ //@ only-wasm32-wasip1 #![deny(warnings)] -use run_make_support::{rustc, tmp_dir}; +use run_make_support::{fs_wrapper, rustc}; fn main() { test("a"); @@ -15,7 +15,7 @@ fn test(cfg: &str) { rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").opt().cfg(cfg).run(); - let bytes = std::fs::read(&tmp_dir().join("foo.wasm")).unwrap(); + let bytes = fs_wrapper::read("foo.wasm"); println!("{}", bytes.len()); assert!(bytes.len() < 40_000); } diff --git a/tests/run-make/wasm-spurious-import/rmake.rs b/tests/run-make/wasm-spurious-import/rmake.rs index 458c7bfccb74..88e4c83c94ca 100644 --- a/tests/run-make/wasm-spurious-import/rmake.rs +++ b/tests/run-make/wasm-spurious-import/rmake.rs @@ -1,6 +1,6 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{fs_wrapper, rustc, wasmparser}; use std::collections::HashMap; fn main() { @@ -13,7 +13,7 @@ fn main() { .arg("-Copt-level=z") .run(); - let file = std::fs::read(&tmp_dir().join("main.wasm")).unwrap(); + let file = fs_wrapper::read("main.wasm"); let mut imports = HashMap::new(); for payload in wasmparser::Parser::new(0).parse_all(&file) { diff --git a/tests/run-make/wasm-stringify-ints-small/rmake.rs b/tests/run-make/wasm-stringify-ints-small/rmake.rs index 9fac0c0c215d..42e415b3a862 100644 --- a/tests/run-make/wasm-stringify-ints-small/rmake.rs +++ b/tests/run-make/wasm-stringify-ints-small/rmake.rs @@ -1,12 +1,12 @@ //@ only-wasm32-wasip1 #![deny(warnings)] -use run_make_support::{rustc, tmp_dir}; +use run_make_support::{fs_wrapper, rustc}; fn main() { rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").opt().run(); - let bytes = std::fs::read(&tmp_dir().join("foo.wasm")).unwrap(); + let bytes = fs_wrapper::read("foo.wasm"); println!("{}", bytes.len()); assert!(bytes.len() < 50_000); } diff --git a/tests/run-make/wasm-symbols-different-module/rmake.rs b/tests/run-make/wasm-symbols-different-module/rmake.rs index 521d2c31ee61..734f79d834a9 100644 --- a/tests/run-make/wasm-symbols-different-module/rmake.rs +++ b/tests/run-make/wasm-symbols-different-module/rmake.rs @@ -1,7 +1,8 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{fs_wrapper, rustc, wasmparser}; use std::collections::{HashMap, HashSet}; +use std::path::Path; fn main() { test_file("foo.rs", &[("a", &["foo"]), ("b", &["foo"])]); @@ -22,7 +23,7 @@ fn test(file: &str, args: &[&str], expected_imports: &[(&str, &[&str])]) { rustc().input(file).target("wasm32-wasip1").args(args).run(); - let file = std::fs::read(&tmp_dir().join(file).with_extension("wasm")).unwrap(); + let file = fs_wrapper::read(Path::new(file).with_extension("wasm")); let mut imports = HashMap::new(); for payload in wasmparser::Parser::new(0).parse_all(&file) { diff --git a/tests/run-make/wasm-symbols-not-exported/bar.rs b/tests/run-make/wasm-symbols-not-exported/bar.rs index 6ffbd3ec6900..dd4d14416e70 100644 --- a/tests/run-make/wasm-symbols-not-exported/bar.rs +++ b/tests/run-make/wasm-symbols-not-exported/bar.rs @@ -11,15 +11,14 @@ unsafe impl GlobalAlloc for B { 1 as *mut u8 } - unsafe fn dealloc(&self, ptr: *mut u8, x: Layout) { - } + unsafe fn dealloc(&self, ptr: *mut u8, x: Layout) {} } #[global_allocator] static A: B = B; #[no_mangle] -pub extern fn foo(a: u32) -> u32 { +pub extern "C" fn foo(a: u32) -> u32 { assert_eq!(a, 3); a * 2 } diff --git a/tests/run-make/wasm-symbols-not-exported/foo.rs b/tests/run-make/wasm-symbols-not-exported/foo.rs index d46baee01b96..f85103f35354 100644 --- a/tests/run-make/wasm-symbols-not-exported/foo.rs +++ b/tests/run-make/wasm-symbols-not-exported/foo.rs @@ -1,7 +1,7 @@ #![crate_type = "cdylib"] #[no_mangle] -pub extern fn foo() { +pub extern "C" fn foo() { println!("foo"); panic!("test"); } diff --git a/tests/run-make/wasm-symbols-not-exported/rmake.rs b/tests/run-make/wasm-symbols-not-exported/rmake.rs index 1b020b67a385..4c817a6fd820 100644 --- a/tests/run-make/wasm-symbols-not-exported/rmake.rs +++ b/tests/run-make/wasm-symbols-not-exported/rmake.rs @@ -1,23 +1,23 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{fs_wrapper, rustc, wasmparser}; use std::path::Path; fn main() { rustc().input("foo.rs").target("wasm32-wasip1").run(); - verify_symbols(&tmp_dir().join("foo.wasm")); + verify_symbols(Path::new("foo.wasm")); rustc().input("foo.rs").target("wasm32-wasip1").opt().run(); - verify_symbols(&tmp_dir().join("foo.wasm")); + verify_symbols(Path::new("foo.wasm")); rustc().input("bar.rs").target("wasm32-wasip1").run(); - verify_symbols(&tmp_dir().join("bar.wasm")); + verify_symbols(Path::new("bar.wasm")); rustc().input("bar.rs").target("wasm32-wasip1").opt().run(); - verify_symbols(&tmp_dir().join("bar.wasm")); + verify_symbols(Path::new("bar.wasm")); } fn verify_symbols(path: &Path) { eprintln!("verify {path:?}"); - let file = std::fs::read(&path).unwrap(); + let file = fs_wrapper::read(&path); for payload in wasmparser::Parser::new(0).parse_all(&file) { let payload = payload.unwrap(); diff --git a/tests/run-make/wasm-symbols-not-imported/foo.rs b/tests/run-make/wasm-symbols-not-imported/foo.rs index b25bdc9800d1..6af8d50a2ace 100644 --- a/tests/run-make/wasm-symbols-not-imported/foo.rs +++ b/tests/run-make/wasm-symbols-not-imported/foo.rs @@ -4,7 +4,7 @@ use core::panic::PanicInfo; #[no_mangle] -pub extern fn foo() { +pub extern "C" fn foo() { panic!() } diff --git a/tests/run-make/wasm-symbols-not-imported/rmake.rs b/tests/run-make/wasm-symbols-not-imported/rmake.rs index a653ab61b2c0..58c9f9c6f45c 100644 --- a/tests/run-make/wasm-symbols-not-imported/rmake.rs +++ b/tests/run-make/wasm-symbols-not-imported/rmake.rs @@ -1,22 +1,22 @@ //@ only-wasm32-wasip1 -use run_make_support::{rustc, tmp_dir, wasmparser}; +use run_make_support::{fs_wrapper, rustc, wasmparser}; use std::path::Path; fn main() { rustc().input("foo.rs").target("wasm32-wasip1").run(); - verify_symbols(&tmp_dir().join("foo.wasm")); + verify_symbols(Path::new("foo.wasm")); rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").run(); - verify_symbols(&tmp_dir().join("foo.wasm")); + verify_symbols(Path::new("foo.wasm")); rustc().input("foo.rs").target("wasm32-wasip1").opt().run(); - verify_symbols(&tmp_dir().join("foo.wasm")); + verify_symbols(Path::new("foo.wasm")); rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").opt().run(); - verify_symbols(&tmp_dir().join("foo.wasm")); + verify_symbols(Path::new("foo.wasm")); } fn verify_symbols(path: &Path) { eprintln!("verify {path:?}"); - let file = std::fs::read(&path).unwrap(); + let file = fs_wrapper::read(&path); for payload in wasmparser::Parser::new(0).parse_all(&file) { let payload = payload.unwrap(); diff --git a/tests/run-make/windows-binary-no-external-deps/Makefile b/tests/run-make/windows-binary-no-external-deps/Makefile deleted file mode 100644 index 8960020fed55..000000000000 --- a/tests/run-make/windows-binary-no-external-deps/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -include ../tools.mk - -# only-windows - -PATH=$(SYSTEMROOT)/system32 - -all: - $(RUSTC) hello.rs - $(TMPDIR)/hello.exe diff --git a/tests/run-make/windows-binary-no-external-deps/rmake.rs b/tests/run-make/windows-binary-no-external-deps/rmake.rs new file mode 100644 index 000000000000..61fdb202221c --- /dev/null +++ b/tests/run-make/windows-binary-no-external-deps/rmake.rs @@ -0,0 +1,20 @@ +//! Ensure that we aren't relying on any non-system DLLs when running +//! a "hello world" application by setting `PATH` to `C:\Windows\System32`. +//@ only-windows + +use run_make_support::{cwd, env_var, rustc}; +use std::path::PathBuf; +use std::process::Command; + +fn main() { + rustc().input("hello.rs").run(); + + let windows_dir = env_var("SystemRoot"); + let system32: PathBuf = [&windows_dir, "System32"].iter().collect(); + // Note: This does not use the support wrappers so that we can precisely control the PATH + let exe = cwd().join("hello.exe"); + let status = Command::new(exe).env("PATH", &system32).spawn().unwrap().wait().unwrap(); + if !status.success() { + panic!("Command failed!\noutput status: `{status}`"); + } +} diff --git a/tests/run-make/windows-safeseh/Makefile b/tests/run-make/windows-safeseh/Makefile deleted file mode 100644 index d6a403961d79..000000000000 --- a/tests/run-make/windows-safeseh/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# only-windows -# needs-rust-lld - -include ../tools.mk - -all: foo bar - -# Ensure that LLD can link when an .rlib contains a synthetic object -# file referencing exported or used symbols. -foo: - $(RUSTC) -C linker=rust-lld foo.rs - -# Ensure that LLD can link when /WHOLEARCHIVE: is used with an .rlib. -# Previously, lib.rmeta was not marked as (trivially) SAFESEH-aware. -bar: baz - $(RUSTC) -C linker=rust-lld -C link-arg=/WHOLEARCHIVE:libbaz.rlib bar.rs - -baz: - $(RUSTC) baz.rs diff --git a/tests/run-make/windows-safeseh/rmake.rs b/tests/run-make/windows-safeseh/rmake.rs new file mode 100644 index 000000000000..10e6b38aa8df --- /dev/null +++ b/tests/run-make/windows-safeseh/rmake.rs @@ -0,0 +1,15 @@ +//@ only-windows +//@ needs-rust-lld + +use run_make_support::rustc; + +fn main() { + // Ensure that LLD can link when an .rlib contains a synthetic object + // file referencing exported or used symbols. + rustc().input("foo.rs").linker("rust-lld").run(); + + // Ensure that LLD can link when /WHOLEARCHIVE: is used with an .rlib. + // Previously, lib.rmeta was not marked as (trivially) SAFESEH-aware. + rustc().input("baz.rs").run(); + rustc().input("bar.rs").linker("rust-lld").link_arg("/WHOLEARCHIVE:libbaz.rlib").run(); +} diff --git a/tests/run-make/windows-spawn/Makefile b/tests/run-make/windows-spawn/Makefile deleted file mode 100644 index b6cdb169bab4..000000000000 --- a/tests/run-make/windows-spawn/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -include ../tools.mk - -# only-windows - -all: - $(RUSTC) -o "$(TMPDIR)/hopefullydoesntexist bar.exe" hello.rs - $(RUSTC) spawn.rs - $(TMPDIR)/spawn.exe diff --git a/tests/run-make/windows-spawn/rmake.rs b/tests/run-make/windows-spawn/rmake.rs new file mode 100644 index 000000000000..a6a7acd7ccb6 --- /dev/null +++ b/tests/run-make/windows-spawn/rmake.rs @@ -0,0 +1,16 @@ +//@ only-windows + +use run_make_support::{run, rustc}; + +// On Windows `Command` uses `CreateProcessW` to run a new process. +// However, in the past std used to not pass in the application name, leaving +// `CreateProcessW` to use heuristics to guess the intended name from the +// command line string. Sometimes this could go very wrong. +// E.g. in Rust 1.0 `Command::new("foo").arg("bar").spawn()` will try to launch +// `foo bar.exe` if foo.exe does not exist. Which is clearly not desired. + +fn main() { + rustc().input("hello.rs").output("hopefullydoesntexist bar.exe").run(); + rustc().input("spawn.rs").run(); + run("spawn"); +} diff --git a/tests/run-make/windows-spawn/spawn.rs b/tests/run-make/windows-spawn/spawn.rs index c34da3d5fde4..4afbaf876cd0 100644 --- a/tests/run-make/windows-spawn/spawn.rs +++ b/tests/run-make/windows-spawn/spawn.rs @@ -3,10 +3,8 @@ use std::process::Command; fn main() { // Make sure it doesn't try to run "hopefullydoesntexist bar.exe". - assert_eq!(Command::new("hopefullydoesntexist") - .arg("bar") - .spawn() - .unwrap_err() - .kind(), - ErrorKind::NotFound); + assert_eq!( + Command::new("hopefullydoesntexist").arg("bar").spawn().unwrap_err().kind(), + ErrorKind::NotFound + ); } diff --git a/tests/run-make/windows-subsystem/Makefile b/tests/run-make/windows-subsystem/Makefile deleted file mode 100644 index e3cf9181de41..000000000000 --- a/tests/run-make/windows-subsystem/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) windows.rs - $(RUSTC) console.rs diff --git a/tests/run-make/windows-ws2_32/empty.rs b/tests/run-make/windows-ws2_32/empty.rs new file mode 100644 index 000000000000..f328e4d9d04c --- /dev/null +++ b/tests/run-make/windows-ws2_32/empty.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make/windows-ws2_32/rmake.rs b/tests/run-make/windows-ws2_32/rmake.rs new file mode 100644 index 000000000000..fde59452bc55 --- /dev/null +++ b/tests/run-make/windows-ws2_32/rmake.rs @@ -0,0 +1,25 @@ +//@ only-msvc + +// Tests that WS2_32.dll is not unnecessarily linked, see issue #85441 + +use run_make_support::object::{self, read::Object}; +use run_make_support::{fs_wrapper, rustc}; + +fn main() { + rustc().input("empty.rs").run(); + rustc().input("tcp.rs").run(); + + assert!(!links_ws2_32("empty.exe")); + assert!(links_ws2_32("tcp.exe")); +} + +fn links_ws2_32(exe: &str) -> bool { + let binary_data = fs_wrapper::read(exe); + let file = object::File::parse(&*binary_data).unwrap(); + for import in file.imports().unwrap() { + if import.library().eq_ignore_ascii_case(b"WS2_32.dll") { + return true; + } + } + false +} diff --git a/tests/run-make/windows-ws2_32/tcp.rs b/tests/run-make/windows-ws2_32/tcp.rs new file mode 100644 index 000000000000..3d0248c76d78 --- /dev/null +++ b/tests/run-make/windows-ws2_32/tcp.rs @@ -0,0 +1,5 @@ +use std::net::TcpListener; + +fn main() { + TcpListener::bind("127.0.0.1:80").unwrap(); +} diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/build.rs b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/build.rs index 3a7aa1be868c..f1fb58039485 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/build.rs +++ b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/build.rs @@ -1,17 +1,9 @@ fn main() { - cc::Build::new() - .file("foo.c") - .compile("foo_c"); + cc::Build::new().file("foo.c").compile("foo_c"); - cc::Build::new() - .file("foo_asm.s") - .compile("foo_asm"); + cc::Build::new().file("foo_asm.s").compile("foo_asm"); - cc::Build::new() - .cpp(true) - .cpp_set_stdlib(None) - .file("foo_cxx.cpp") - .compile("foo_cxx"); + cc::Build::new().cpp(true).cpp_set_stdlib(None).file("foo_cxx.cpp").compile("foo_cxx"); // When the cmake crate detects the clang compiler, it passes the // "--target" argument to the linker which subsequently fails. The @@ -20,11 +12,11 @@ fn main() { // `CMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY` can be used // https://cmake.org/cmake/help/v3.5/module/CMakeForceCompiler.html let dst = cmake::Config::new("libcmake_foo") - .build_target("cmake_foo") - .define("CMAKE_C_COMPILER_FORCED", "1") - .define("CMAKE_CXX_COMPILER_FORCED", "1") - .define("CMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY", "1") - .build(); + .build_target("cmake_foo") + .define("CMAKE_C_COMPILER_FORCED", "1") + .define("CMAKE_CXX_COMPILER_FORCED", "1") + .define("CMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY", "1") + .build(); println!("cargo:rustc-link-search=native={}/build/", dst.display()); println!("cargo:rustc-link-lib=static=cmake_foo"); } diff --git a/tests/run-pass-valgrind/cast-enum-with-dtor.rs b/tests/run-pass-valgrind/cast-enum-with-dtor.rs index f7ef92df8fbe..a57dc3734789 100644 --- a/tests/run-pass-valgrind/cast-enum-with-dtor.rs +++ b/tests/run-pass-valgrind/cast-enum-with-dtor.rs @@ -2,14 +2,14 @@ // check dtor calling order when casting enums. +use std::mem; use std::sync::atomic; use std::sync::atomic::Ordering; -use std::mem; enum E { A = 0, B = 1, - C = 2 + C = 2, } static FLAG: atomic::AtomicUsize = atomic::AtomicUsize::new(0); @@ -19,7 +19,7 @@ impl Drop for E { // avoid dtor loop unsafe { mem::forget(mem::replace(self, E::B)) }; - FLAG.store(FLAG.load(Ordering::SeqCst)+1, Ordering::SeqCst); + FLAG.store(FLAG.load(Ordering::SeqCst) + 1, Ordering::SeqCst); } } diff --git a/tests/run-pass-valgrind/cleanup-auto-borrow-obj.rs b/tests/run-pass-valgrind/cleanup-auto-borrow-obj.rs index dfc094abeb9b..e4ce80b33059 100644 --- a/tests/run-pass-valgrind/cleanup-auto-borrow-obj.rs +++ b/tests/run-pass-valgrind/cleanup-auto-borrow-obj.rs @@ -7,12 +7,15 @@ static mut DROP_RAN: bool = false; struct Foo; impl Drop for Foo { fn drop(&mut self) { - unsafe { DROP_RAN = true; } + unsafe { + DROP_RAN = true; + } } } - -trait Trait { fn dummy(&self) { } } +trait Trait { + fn dummy(&self) {} +} impl Trait for Foo {} pub fn main() { diff --git a/tests/run-pass-valgrind/coerce-match-calls.rs b/tests/run-pass-valgrind/coerce-match-calls.rs index f6c7151ff103..8c7375610dd7 100644 --- a/tests/run-pass-valgrind/coerce-match-calls.rs +++ b/tests/run-pass-valgrind/coerce-match-calls.rs @@ -7,9 +7,15 @@ use std::boxed::Box; pub fn main() { let _: Box<[isize]> = if true { Box::new([1, 2, 3]) } else { Box::new([1]) }; - let _: Box<[isize]> = match true { true => Box::new([1, 2, 3]), false => Box::new([1]) }; + let _: Box<[isize]> = match true { + true => Box::new([1, 2, 3]), + false => Box::new([1]), + }; // Check we don't get over-keen at propagating coercions in the case of casts. let x = if true { 42 } else { 42u8 } as u16; - let x = match true { true => 42, false => 42u8 } as u16; + let x = match true { + true => 42, + false => 42u8, + } as u16; } diff --git a/tests/run-pass-valgrind/coerce-match.rs b/tests/run-pass-valgrind/coerce-match.rs index 3f33264c5a80..95f16a8cc896 100644 --- a/tests/run-pass-valgrind/coerce-match.rs +++ b/tests/run-pass-valgrind/coerce-match.rs @@ -12,11 +12,20 @@ pub fn main() { }; let _: Box<[isize]> = match true { - true => { let b: Box<_> = Box::new([1, 2, 3]); b } - false => { let b: Box<_> = Box::new([1]); b } + true => { + let b: Box<_> = Box::new([1, 2, 3]); + b + } + false => { + let b: Box<_> = Box::new([1]); + b + } }; // Check we don't get over-keen at propagating coercions in the case of casts. let x = if true { 42 } else { 42u8 } as u16; - let x = match true { true => 42, false => 42u8 } as u16; + let x = match true { + true => 42, + false => 42u8, + } as u16; } diff --git a/tests/run-pass-valgrind/down-with-thread-dtors.rs b/tests/run-pass-valgrind/down-with-thread-dtors.rs index 15aeac98c667..0d3745bba5ba 100644 --- a/tests/run-pass-valgrind/down-with-thread-dtors.rs +++ b/tests/run-pass-valgrind/down-with-thread-dtors.rs @@ -27,13 +27,17 @@ impl Drop for Bar { impl Drop for Baz { fn drop(&mut self) { - unsafe { HIT = true; } + unsafe { + HIT = true; + } } } fn main() { std::thread::spawn(|| { FOO.with(|_| {}); - }).join().unwrap(); + }) + .join() + .unwrap(); assert!(unsafe { HIT }); } diff --git a/tests/run-pass-valgrind/dst-dtor-1.rs b/tests/run-pass-valgrind/dst-dtor-1.rs index 5b8433f61456..47065151a037 100644 --- a/tests/run-pass-valgrind/dst-dtor-1.rs +++ b/tests/run-pass-valgrind/dst-dtor-1.rs @@ -3,15 +3,19 @@ static mut DROP_RAN: bool = false; struct Foo; impl Drop for Foo { fn drop(&mut self) { - unsafe { DROP_RAN = true; } + unsafe { + DROP_RAN = true; + } } } -trait Trait { fn dummy(&self) { } } +trait Trait { + fn dummy(&self) {} +} impl Trait for Foo {} struct Fat { - f: T + f: T, } pub fn main() { diff --git a/tests/run-pass-valgrind/dst-dtor-2.rs b/tests/run-pass-valgrind/dst-dtor-2.rs index 991fe00950bb..d8abebfb4473 100644 --- a/tests/run-pass-valgrind/dst-dtor-2.rs +++ b/tests/run-pass-valgrind/dst-dtor-2.rs @@ -3,12 +3,14 @@ static mut DROP_RAN: isize = 0; struct Foo; impl Drop for Foo { fn drop(&mut self) { - unsafe { DROP_RAN += 1; } + unsafe { + DROP_RAN += 1; + } } } struct Fat { - f: T + f: T, } pub fn main() { diff --git a/tests/run-pass-valgrind/dst-dtor-3.rs b/tests/run-pass-valgrind/dst-dtor-3.rs index f0c2dda5ab05..09adaca21c71 100644 --- a/tests/run-pass-valgrind/dst-dtor-3.rs +++ b/tests/run-pass-valgrind/dst-dtor-3.rs @@ -5,11 +5,15 @@ static mut DROP_RAN: bool = false; struct Foo; impl Drop for Foo { fn drop(&mut self) { - unsafe { DROP_RAN = true; } + unsafe { + DROP_RAN = true; + } } } -trait Trait { fn dummy(&self) { } } +trait Trait { + fn dummy(&self) {} +} impl Trait for Foo {} pub fn main() { diff --git a/tests/run-pass-valgrind/dst-dtor-4.rs b/tests/run-pass-valgrind/dst-dtor-4.rs index ad6d46f7c088..a66ac8e3cfca 100644 --- a/tests/run-pass-valgrind/dst-dtor-4.rs +++ b/tests/run-pass-valgrind/dst-dtor-4.rs @@ -5,7 +5,9 @@ static mut DROP_RAN: isize = 0; struct Foo; impl Drop for Foo { fn drop(&mut self) { - unsafe { DROP_RAN += 1; } + unsafe { + DROP_RAN += 1; + } } } diff --git a/tests/run-pass-valgrind/exit-flushes.rs b/tests/run-pass-valgrind/exit-flushes.rs index fa9196a3eecb..c2072cf0bf83 100644 --- a/tests/run-pass-valgrind/exit-flushes.rs +++ b/tests/run-pass-valgrind/exit-flushes.rs @@ -1,6 +1,6 @@ //@ ignore-wasm32 no subprocess support //@ ignore-sgx no processes -//@ ignore-macos this needs valgrind 3.11 or higher; see +//@ ignore-apple this needs valgrind 3.11 or higher; see // https://github.com/rust-lang/rust/pull/30365#issuecomment-165763679 use std::env; @@ -11,8 +11,7 @@ fn main() { print!("hello!"); exit(0); } else { - let out = Command::new(env::args().next().unwrap()).arg("foo") - .output().unwrap(); + let out = Command::new(env::args().next().unwrap()).arg("foo").output().unwrap(); assert!(out.status.success()); assert_eq!(String::from_utf8(out.stdout).unwrap(), "hello!"); assert_eq!(String::from_utf8(out.stderr).unwrap(), ""); diff --git a/tests/run-pass-valgrind/unsized-locals/by-value-trait-objects-rust-call.rs b/tests/run-pass-valgrind/unsized-locals/by-value-trait-objects-rust-call.rs index ece4dea9aaf6..5d3f558a63a9 100644 --- a/tests/run-pass-valgrind/unsized-locals/by-value-trait-objects-rust-call.rs +++ b/tests/run-pass-valgrind/unsized-locals/by-value-trait-objects-rust-call.rs @@ -43,7 +43,6 @@ impl FnOnce<()> for D { } } - fn main() { let x = *(Box::new(A) as Box>); assert_eq!(x.call_once(()), format!("hello")); diff --git a/tests/run-pass-valgrind/unsized-locals/by-value-trait-objects-rust-call2.rs b/tests/run-pass-valgrind/unsized-locals/by-value-trait-objects-rust-call2.rs index 94df2b0b83f0..9b6648f2e27a 100644 --- a/tests/run-pass-valgrind/unsized-locals/by-value-trait-objects-rust-call2.rs +++ b/tests/run-pass-valgrind/unsized-locals/by-value-trait-objects-rust-call2.rs @@ -51,7 +51,6 @@ impl FnOnce<(String, Box)> for D { } } - fn main() { let (s1, s2) = (format!("s1"), format!("s2").into_boxed_str()); let x = *(Box::new(A) as Box), Output = String>>); @@ -61,10 +60,10 @@ fn main() { assert_eq!(x.call_once((s1, s2)), format!("42")); let (s1, s2) = (format!("s1"), format!("s2").into_boxed_str()); let x = *(Box::new(C(format!("jumping fox"))) - as Box), Output = String>>); + as Box), Output = String>>); assert_eq!(x.call_once((s1, s2)), format!("jumping fox")); let (s1, s2) = (format!("s1"), format!("s2").into_boxed_str()); let x = *(Box::new(D(Box::new(format!("lazy dog")))) - as Box), Output = String>>); + as Box), Output = String>>); assert_eq!(x.call_once((s1, s2)), format!("lazy dog")); } diff --git a/tests/run-pass-valgrind/unsized-locals/by-value-trait-objects.rs b/tests/run-pass-valgrind/unsized-locals/by-value-trait-objects.rs index 3d67101e734c..3f6b6d262b5d 100644 --- a/tests/run-pass-valgrind/unsized-locals/by-value-trait-objects.rs +++ b/tests/run-pass-valgrind/unsized-locals/by-value-trait-objects.rs @@ -36,7 +36,6 @@ impl Foo for D { } } - fn main() { let x = *(Box::new(A) as Box); assert_eq!(x.foo(), format!("hello")); diff --git a/tests/rustdoc-gui/help-page.goml b/tests/rustdoc-gui/help-page.goml deleted file mode 100644 index 09d33af139cd..000000000000 --- a/tests/rustdoc-gui/help-page.goml +++ /dev/null @@ -1,69 +0,0 @@ -// This test ensures that opening the help page in its own tab works. -include: "utils.goml" -go-to: "file://" + |DOC_PATH| + "/help.html" -set-window-size: (1000, 1000) // Try desktop size first. -wait-for: "#help" -assert-css: ("#help", {"display": "block"}) -assert-css: ("#help dd", {"font-size": "16px"}) -click: "#help-button > a" -assert-css: ("#help", {"display": "block"}) -compare-elements-property: (".sub", "#help", ["offsetWidth"]) -compare-elements-position: (".sub", "#help", ["x"]) -set-window-size: (500, 1000) // Try mobile next. -assert-css: ("#help", {"display": "block"}) -compare-elements-property: (".sub", "#help", ["offsetWidth"]) -compare-elements-position: (".sub", "#help", ["x"]) - -// Checking the color of the elements of the help menu. -show-text: true -define-function: ( - "check-colors", - [theme, color, background, box_shadow], - block { - call-function: ("switch-theme", {"theme": |theme|}) - assert-css: ("#help kbd", { - "color": |color|, - "background-color": |background|, - "box-shadow": |box_shadow| + " 0px -1px 0px 0px inset", - }, ALL) - }, -) - -call-function: ("check-colors", { - "theme": "ayu", - "color": "#c5c5c5", - "background": "#314559", - "box_shadow": "#5c6773", -}) -call-function: ("check-colors", { - "theme": "dark", - "color": "#000", - "background": "#fafbfc", - "box_shadow": "#c6cbd1", -}) -call-function: ("check-colors", { - "theme": "light", - "color": "#000", - "background": "#fafbfc", - "box_shadow": "#c6cbd1", -}) - -// This test ensures that opening the help popover without switching pages works. -go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -set-window-size: (1000, 1000) // Only supported on desktop. -assert-false: "#help" -click: "#help-button > a" -assert-css: ("#help", {"display": "block"}) -assert-css: ("#help dd", {"font-size": "16px"}) -click: "#help-button > a" -assert-css: ("#help", {"display": "none"}) -compare-elements-property-false: (".sub", "#help", ["offsetWidth"]) -compare-elements-position-false: (".sub", "#help", ["x"]) - -// This test ensures that the "the rustdoc book" anchor link within the help popover works. -go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -set-window-size: (1000, 1000) // Popover only appears when the screen width is >700px. -assert-false: "#help" -click: "#help-button > a" -click: "//*[@id='help']//a[text()='the rustdoc book']" -wait-for-document-property: ({"URL": "https://doc.rust-lang.org/"}, STARTS_WITH) diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index ffd169812b63..c4d7c2b0b859 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -24,7 +24,7 @@ const PARSED = [ original: "-> *", returned: [], userQuery: "-> *", - error: "Unexpected `*` after ` `", + error: "Unexpected `*` after ` ` (not a valid identifier)", }, { query: 'a<"P">', @@ -204,16 +204,25 @@ const PARSED = [ original: "_:", returned: [], userQuery: "_:", - error: "Unexpected `:` (expected path after type filter `_:`)", + error: "Unexpected `_` (not a valid identifier)", }, { - query: "_:a", + query: "ab:", elems: [], foundElems: 0, - original: "_:a", + original: "ab:", returned: [], - userQuery: "_:a", - error: "Unknown type filter `_`", + userQuery: "ab:", + error: "Unexpected `:` (expected path after type filter `ab:`)", + }, + { + query: "a:b", + elems: [], + foundElems: 0, + original: "a:b", + returned: [], + userQuery: "a:b", + error: "Unknown type filter `a`", }, { query: "a-bb", @@ -240,7 +249,7 @@ const PARSED = [ original: "ab'", returned: [], userQuery: "ab'", - error: "Unexpected `'` after `b`", + error: "Unexpected `'` after `b` (not a valid identifier)", }, { query: "a->", diff --git a/tests/rustdoc-js/assoc-type-backtrack.rs b/tests/rustdoc-js/assoc-type-backtrack.rs index c3cdd78c6e1c..2dfede9dc383 100644 --- a/tests/rustdoc-js/assoc-type-backtrack.rs +++ b/tests/rustdoc-js/assoc-type-backtrack.rs @@ -5,22 +5,27 @@ pub trait MyTrait2 { pub trait MyTrait { type Item; fn next(&mut self) -> Option; - fn fold(self, init: B, f: F) -> B where + fn fold(self, init: B, f: F) -> B + where Self: Sized, - F: MyTrait2<(B, Self::Item), Output=B>; + F: MyTrait2<(B, Self::Item), Output = B>; } pub struct Cloned(I); -impl<'a, T, I> MyTrait for Cloned where +impl<'a, T, I> MyTrait for Cloned +where T: 'a + Clone, - I: MyTrait + I: MyTrait, { type Item = T; - fn next(&mut self) -> Option { loop {} } - fn fold(self, init: B, f: F) -> B where + fn next(&mut self) -> Option { + loop {} + } + fn fold(self, init: B, f: F) -> B + where Self: Sized, - F: MyTrait2<(B, Self::Item), Output=B> + F: MyTrait2<(B, Self::Item), Output = B>, { loop {} } @@ -32,7 +37,7 @@ pub trait MyFuture { pub trait MyIntoFuture { type Output; - type Fut: MyFuture; + type Fut: MyFuture; fn into_future(self) -> Self::Fut; fn into_future_2(self, other: Self) -> Self::Fut; } diff --git a/tests/rustdoc-js/assoc-type-loop.rs b/tests/rustdoc-js/assoc-type-loop.rs index f123c83f50fd..09efe8c7be5c 100644 --- a/tests/rustdoc-js/assoc-type-loop.rs +++ b/tests/rustdoc-js/assoc-type-loop.rs @@ -1,9 +1,9 @@ -#![crate_name="foo"] +#![crate_name = "foo"] // reduced from sqlx 0.7.3 use std::future::Future; -use std::pin::Pin; use std::ops::{Deref, DerefMut}; +use std::pin::Pin; pub enum Error {} pub trait Acquire<'c> { type Database: Database; @@ -16,7 +16,7 @@ pub trait Connection { type Database: Database; type Options: ConnectionOptions; fn begin( - &mut self + &mut self, ) -> Pin, Error>> + Send + '_>> where Self: Sized; @@ -28,7 +28,8 @@ pub struct Transaction<'c, DB: Database> { _db: &'c DB, } impl<'t, 'c, DB: Database> Acquire<'t> for &'t mut Transaction<'c, DB> - where ::Connection: Send +where + ::Connection: Send, { type Database = DB; type Connection = &'t mut ::Connection; diff --git a/tests/rustdoc-js/auxiliary/interner.rs b/tests/rustdoc-js/auxiliary/interner.rs index c95029be9f0f..e4e4ff6276d6 100644 --- a/tests/rustdoc-js/auxiliary/interner.rs +++ b/tests/rustdoc-js/auxiliary/interner.rs @@ -77,17 +77,14 @@ pub trait Interner: Sized { type ClosureKind: Copy + Debug + Hash + Eq; // Required method - fn mk_canonical_var_infos( - self, - infos: &[CanonicalVarInfo] - ) -> Self::CanonicalVars; + fn mk_canonical_var_infos(self, infos: &[CanonicalVarInfo]) -> Self::CanonicalVars; } pub trait DebugWithInfcx: Debug { // Required method fn fmt>( this: WithInfcx<'_, Infcx, &Self>, - f: &mut Formatter<'_> + f: &mut Formatter<'_>, ) -> std::fmt::Result; } @@ -130,11 +127,7 @@ pub struct TypeFlags; pub trait Ty> { // Required method - fn new_anon_bound( - interner: I, - debruijn: DebruijnIndex, - var: BoundVar - ) -> Self; + fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar) -> Self; } pub trait PlaceholderLike { @@ -152,12 +145,7 @@ pub struct BoundVar; pub struct ConstKind(std::marker::PhantomData); pub trait Const> { // Required method - fn new_anon_bound( - interner: I, - debruijn: DebruijnIndex, - var: BoundVar, - ty: I::Ty - ) -> Self; + fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar, ty: I::Ty) -> Self; } pub trait ConstTy { @@ -170,25 +158,28 @@ pub struct DebruijnIndex; pub struct RegionKind(std::marker::PhantomData); pub trait Region> { // Required method - fn new_anon_bound( - interner: I, - debruijn: DebruijnIndex, - var: BoundVar - ) -> Self; + fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar) -> Self; } pub trait TypeVisitor: Sized { type Result: VisitorResult = (); // Provided methods - fn visit_binder>( - &mut self, - t: &I::Binder - ) -> Self::Result { unimplemented!() } - fn visit_ty(&mut self, t: I::Ty) -> Self::Result { unimplemented!() } - fn visit_region(&mut self, _r: I::Region) -> Self::Result { unimplemented!() } - fn visit_const(&mut self, c: I::Const) -> Self::Result { unimplemented!() } - fn visit_predicate(&mut self, p: I::Predicate) -> Self::Result { unimplemented!() } + fn visit_binder>(&mut self, t: &I::Binder) -> Self::Result { + unimplemented!() + } + fn visit_ty(&mut self, t: I::Ty) -> Self::Result { + unimplemented!() + } + fn visit_region(&mut self, _r: I::Region) -> Self::Result { + unimplemented!() + } + fn visit_const(&mut self, c: I::Const) -> Self::Result { + unimplemented!() + } + fn visit_predicate(&mut self, p: I::Predicate) -> Self::Result { + unimplemented!() + } } pub trait VisitorResult { @@ -206,7 +197,9 @@ impl VisitorResult for () { fn output() -> Self {} fn from_residual(_: Self::Residual) -> Self {} fn from_branch(_: ControlFlow) -> Self {} - fn branch(self) -> ControlFlow { ControlFlow::Continue(()) } + fn branch(self) -> ControlFlow { + ControlFlow::Continue(()) + } } pub struct WithInfcx<'a, Infcx: InferCtxtLike, T> { @@ -221,24 +214,18 @@ pub trait InferCtxtLike { fn interner(&self) -> Self::Interner; fn universe_of_ty(&self, ty: TyVid) -> Option; fn root_ty_var(&self, vid: TyVid) -> TyVid; - fn probe_ty_var( - &self, - vid: TyVid - ) -> Option<::Ty>; + fn probe_ty_var(&self, vid: TyVid) -> Option<::Ty>; fn universe_of_lt( &self, - lt: ::InferRegion + lt: ::InferRegion, ) -> Option; fn opportunistic_resolve_lt_var( &self, - vid: ::InferRegion + vid: ::InferRegion, ) -> Option<::Region>; fn universe_of_ct(&self, ct: ConstVid) -> Option; fn root_ct_var(&self, vid: ConstVid) -> ConstVid; - fn probe_ct_var( - &self, - vid: ConstVid - ) -> Option<::Const>; + fn probe_ct_var(&self, vid: ConstVid) -> Option<::Const>; } pub struct TyVid; diff --git a/tests/rustdoc-js/basic.js b/tests/rustdoc-js/basic.js index e186d510887c..c38b8435c2d8 100644 --- a/tests/rustdoc-js/basic.js +++ b/tests/rustdoc-js/basic.js @@ -1,6 +1,6 @@ const EXPECTED = { 'query': 'Fo', 'others': [ - { 'path': 'basic', 'name': 'Foo' }, + { 'path': 'basic', 'name': 'Foo', 'desc': 'Docs for Foo' }, ], }; diff --git a/tests/rustdoc-js/doc-alias.js b/tests/rustdoc-js/doc-alias.js index 7e4e8a776d89..e57bd71419dc 100644 --- a/tests/rustdoc-js/doc-alias.js +++ b/tests/rustdoc-js/doc-alias.js @@ -5,6 +5,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Struct', + 'desc': 'Doc for Struct', 'alias': 'StructItem', 'href': '../doc_alias/struct.Struct.html', 'is_alias': true @@ -17,6 +18,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Struct', 'name': 'field', + 'desc': 'Doc for Struct’s field', 'alias': 'StructFieldItem', 'href': '../doc_alias/struct.Struct.html#structfield.field', 'is_alias': true @@ -29,6 +31,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Struct', 'name': 'method', + 'desc': 'Doc for Struct::method', 'alias': 'StructMethodItem', 'href': '../doc_alias/struct.Struct.html#method.method', 'is_alias': true @@ -45,6 +48,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Struct', 'name': 'ImplConstItem', + 'desc': 'Doc for Struct::ImplConstItem', 'alias': 'StructImplConstItem', 'href': '../doc_alias/struct.Struct.html#associatedconstant.ImplConstItem', 'is_alias': true @@ -57,6 +61,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Struct', 'name': 'function', + 'desc': 'Doc for Trait::function implemented for Struct', 'alias': 'ImplTraitFunction', 'href': '../doc_alias/struct.Struct.html#method.function', 'is_alias': true @@ -69,6 +74,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Enum', + 'desc': 'Doc for Enum', 'alias': 'EnumItem', 'href': '../doc_alias/enum.Enum.html', 'is_alias': true @@ -81,6 +87,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Enum', 'name': 'Variant', + 'desc': 'Doc for Enum::Variant', 'alias': 'VariantItem', 'href': '../doc_alias/enum.Enum.html#variant.Variant', 'is_alias': true @@ -93,6 +100,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Enum', 'name': 'method', + 'desc': 'Doc for Enum::method', 'alias': 'EnumMethodItem', 'href': '../doc_alias/enum.Enum.html#method.method', 'is_alias': true @@ -105,6 +113,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Typedef', + 'desc': 'Doc for type alias Typedef', 'alias': 'TypedefItem', 'href': '../doc_alias/type.Typedef.html', 'is_alias': true @@ -117,6 +126,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Trait', + 'desc': 'Doc for Trait', 'alias': 'TraitItem', 'href': '../doc_alias/trait.Trait.html', 'is_alias': true @@ -129,6 +139,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Trait', 'name': 'Target', + 'desc': 'Doc for Trait::Target', 'alias': 'TraitTypeItem', 'href': '../doc_alias/trait.Trait.html#associatedtype.Target', 'is_alias': true @@ -141,6 +152,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Trait', 'name': 'AssociatedConst', + 'desc': 'Doc for Trait::AssociatedConst', 'alias': 'AssociatedConstItem', 'href': '../doc_alias/trait.Trait.html#associatedconstant.AssociatedConst', 'is_alias': true @@ -153,6 +165,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Trait', 'name': 'function', + 'desc': 'Doc for Trait::function', 'alias': 'TraitFunctionItem', 'href': '../doc_alias/trait.Trait.html#tymethod.function', 'is_alias': true @@ -165,6 +178,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'function', + 'desc': 'Doc for function', 'alias': 'FunctionItem', 'href': '../doc_alias/fn.function.html', 'is_alias': true @@ -177,6 +191,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Module', + 'desc': 'Doc for Module', 'alias': 'ModuleItem', 'href': '../doc_alias/Module/index.html', 'is_alias': true @@ -189,6 +204,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Const', + 'desc': 'Doc for Const', 'alias': 'ConstItem', 'href': '../doc_alias/constant.Const.html', 'is_alias': true @@ -205,6 +221,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Static', + 'desc': 'Doc for Static', 'alias': 'StaticItem', 'href': '../doc_alias/static.Static.html', 'is_alias': true @@ -217,6 +234,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Union', + 'desc': 'Doc for Union', 'alias': 'UnionItem', 'href': '../doc_alias/union.Union.html', 'is_alias': true @@ -225,6 +243,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Union', 'name': 'union_item', + 'desc': 'Doc for Union::union_item', 'href': '../doc_alias/union.Union.html#structfield.union_item' }, ], @@ -235,6 +254,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Union', 'name': 'union_item', + 'desc': 'Doc for Union::union_item', 'alias': 'UnionFieldItem', 'href': '../doc_alias/union.Union.html#structfield.union_item', 'is_alias': true @@ -247,6 +267,7 @@ const EXPECTED = [ { 'path': 'doc_alias::Union', 'name': 'method', + 'desc': 'Doc for Union::method', 'alias': 'UnionMethodItem', 'href': '../doc_alias/union.Union.html#method.method', 'is_alias': true @@ -259,6 +280,7 @@ const EXPECTED = [ { 'path': 'doc_alias', 'name': 'Macro', + 'desc': 'Doc for Macro', 'alias': 'MacroItem', 'href': '../doc_alias/macro.Macro.html', 'is_alias': true diff --git a/tests/rustdoc-js/doc-alias.rs b/tests/rustdoc-js/doc-alias.rs index 750b7b757bc3..bf075385327a 100644 --- a/tests/rustdoc-js/doc-alias.rs +++ b/tests/rustdoc-js/doc-alias.rs @@ -1,12 +1,16 @@ +/// Doc for `Struct` #[doc(alias = "StructItem")] pub struct Struct { + /// Doc for `Struct`'s `field` #[doc(alias = "StructFieldItem")] pub field: u32, } impl Struct { + /// Doc for `Struct::ImplConstItem` #[doc(alias = "StructImplConstItem")] pub const ImplConstItem: i32 = 0; + /// Doc for `Struct::method` #[doc(alias = "StructMethodItem")] pub fn method(&self) {} } @@ -15,61 +19,80 @@ impl Trait for Struct { type Target = u32; const AssociatedConst: i32 = 12; + /// Doc for `Trait::function` implemented for Struct #[doc(alias = "ImplTraitFunction")] - fn function() -> Self::Target { 0 } + fn function() -> Self::Target { + 0 + } } +/// Doc for `Enum` #[doc(alias = "EnumItem")] pub enum Enum { + /// Doc for `Enum::Variant` #[doc(alias = "VariantItem")] Variant, } impl Enum { + /// Doc for `Enum::method` #[doc(alias = "EnumMethodItem")] pub fn method(&self) {} } +/// Doc for type alias `Typedef` #[doc(alias = "TypedefItem")] pub type Typedef = i32; +/// Doc for `Trait` #[doc(alias = "TraitItem")] pub trait Trait { + /// Doc for `Trait::Target` #[doc(alias = "TraitTypeItem")] type Target; + /// Doc for `Trait::AssociatedConst` #[doc(alias = "AssociatedConstItem")] const AssociatedConst: i32; + /// Doc for `Trait::function` #[doc(alias = "TraitFunctionItem")] fn function() -> Self::Target; } +/// Doc for `function` #[doc(alias = "FunctionItem")] pub fn function() {} +/// Doc for `Module` #[doc(alias = "ModuleItem")] pub mod Module {} +/// Doc for `Const` #[doc(alias = "ConstItem")] pub const Const: u32 = 0; +/// Doc for `Static` #[doc(alias = "StaticItem")] pub static Static: u32 = 0; +/// Doc for `Union` #[doc(alias = "UnionItem")] pub union Union { + /// Doc for `Union::union_item` #[doc(alias = "UnionFieldItem")] pub union_item: u32, pub y: f32, } impl Union { + /// Doc for `Union::method` #[doc(alias = "UnionMethodItem")] pub fn method(&self) {} } +/// Doc for `Macro` #[doc(alias = "MacroItem")] #[macro_export] macro_rules! Macro { - () => {} + () => {}; } diff --git a/tests/rustdoc-js/enum-variant-not-type.rs b/tests/rustdoc-js/enum-variant-not-type.rs index 421bddf62894..f0facd63cfcd 100644 --- a/tests/rustdoc-js/enum-variant-not-type.rs +++ b/tests/rustdoc-js/enum-variant-not-type.rs @@ -5,10 +5,14 @@ pub trait MyTrait { fn not_appearing(&self) -> Option<&Self::T>; } -pub fn my_fn(t: X) -> X { t } +pub fn my_fn(t: X) -> X { + t +} pub trait AutoCorrectConfounder { type InsertUnnecessarilyLongTypeNameHere; - fn assoc_type_acts_like_generic(&self, x: &Self::InsertUnnecessarilyLongTypeNameHere) - -> Option<&Self::InsertUnnecessarilyLongTypeNameHere>; + fn assoc_type_acts_like_generic( + &self, + x: &Self::InsertUnnecessarilyLongTypeNameHere, + ) -> Option<&Self::InsertUnnecessarilyLongTypeNameHere>; } diff --git a/tests/rustdoc-js/foreign-type-path.rs b/tests/rustdoc-js/foreign-type-path.rs index 83400104ea77..f9228c7b9ac3 100644 --- a/tests/rustdoc-js/foreign-type-path.rs +++ b/tests/rustdoc-js/foreign-type-path.rs @@ -2,12 +2,11 @@ pub mod aaaaaaa { - extern { + extern "C" { pub type MyForeignType; } impl MyForeignType { pub fn my_method() {} } - } diff --git a/tests/rustdoc-js/full-path-function.rs b/tests/rustdoc-js/full-path-function.rs index 8dcc3f2b69d8..a1a9220654d0 100644 --- a/tests/rustdoc-js/full-path-function.rs +++ b/tests/rustdoc-js/full-path-function.rs @@ -2,16 +2,26 @@ pub mod sac { pub struct Sac; impl Sac { - pub fn len(&self) -> usize { 0 } + pub fn len(&self) -> usize { + 0 + } } } pub mod b { pub struct Sac; impl Sac { - pub fn len(&self) -> usize { 0 } - pub fn bar(&self, w: u32) -> usize { 0 } - pub fn bar2(&self, w: u32) -> u32 { 0 } - pub fn string(w: String) -> u32 { 0 } + pub fn len(&self) -> usize { + 0 + } + pub fn bar(&self, w: u32) -> usize { + 0 + } + pub fn bar2(&self, w: u32) -> u32 { + 0 + } + pub fn string(w: String) -> u32 { + 0 + } } } diff --git a/tests/rustdoc-js/gat.rs b/tests/rustdoc-js/gat.rs index b4861cc683ff..7a2b5551114c 100644 --- a/tests/rustdoc-js/gat.rs +++ b/tests/rustdoc-js/gat.rs @@ -2,7 +2,15 @@ pub trait Foo { type Assoc; } -pub fn sample = u8>>(_: X) -> u32 { loop {} } -pub fn synergy(_: impl Foo = u8>) -> ! { loop {} } -pub fn consider(_: impl Foo = u32>) -> bool { loop {} } -pub fn integrate(_: impl Foo = T>) -> T { loop {} } +pub fn sample = u8>>(_: X) -> u32 { + loop {} +} +pub fn synergy(_: impl Foo = u8>) -> ! { + loop {} +} +pub fn consider(_: impl Foo = u32>) -> bool { + loop {} +} +pub fn integrate(_: impl Foo = T>) -> T { + loop {} +} diff --git a/tests/rustdoc-js/generics-impl.rs b/tests/rustdoc-js/generics-impl.rs index 696218021d5a..27d44fdd7e92 100644 --- a/tests/rustdoc-js/generics-impl.rs +++ b/tests/rustdoc-js/generics-impl.rs @@ -1,4 +1,4 @@ -use std::io::{Result as IoResult, Read}; +use std::io::{Read, Result as IoResult}; pub struct Aaaaaaa; @@ -29,7 +29,10 @@ impl Ddddddd { pub fn ggggggg(self) -> u64 { 1 } - pub fn hhhhhhh() -> Self where T: Default { + pub fn hhhhhhh() -> Self + where + T: Default, + { Ddddddd(T::default()) } } diff --git a/tests/rustdoc-js/generics-trait.rs b/tests/rustdoc-js/generics-trait.rs index 20db117ccd5c..58c86361e419 100644 --- a/tests/rustdoc-js/generics-trait.rs +++ b/tests/rustdoc-js/generics-trait.rs @@ -1,8 +1,16 @@ pub trait SomeTrait {} pub trait OtherThingxxxxxxxx {} -pub fn alef() -> Result { loop {} } -pub fn bet() -> Result { loop {} } +pub fn alef() -> Result { + loop {} +} +pub fn bet() -> Result { + loop {} +} -pub fn alpha(_param: Result) { loop {} } -pub fn beta(_param: Result) { loop {} } +pub fn alpha(_param: Result) { + loop {} +} +pub fn beta(_param: Result) { + loop {} +} diff --git a/tests/rustdoc-js/generics.rs b/tests/rustdoc-js/generics.rs index 055c51c7ec57..c3ca13666141 100644 --- a/tests/rustdoc-js/generics.rs +++ b/tests/rustdoc-js/generics.rs @@ -3,26 +3,41 @@ pub struct Q; pub struct R(T); // returns test -pub fn alef() -> R

{ loop {} } -pub fn bet() -> R { loop {} } +pub fn alef() -> R

{ + loop {} +} +pub fn bet() -> R { + loop {} +} // in_args test -pub fn alpha(_x: R

) { loop {} } -pub fn beta(_x: R) { loop {} } +pub fn alpha(_x: R

) { + loop {} +} +pub fn beta(_x: R) { + loop {} +} // test case with multiple appearances of the same type -pub struct ExtraCreditStructMulti { t: T, u: U } +pub struct ExtraCreditStructMulti { + t: T, + u: U, +} pub struct ExtraCreditInnerMulti {} pub fn extracreditlabhomework( - _param: ExtraCreditStructMulti -) { loop {} } -pub fn redherringmatchforextracredit( - _param: ExtraCreditStructMulti -) { loop {} } + _param: ExtraCreditStructMulti, +) { + loop {} +} +pub fn redherringmatchforextracredit(_param: ExtraCreditStructMulti) { + loop {} +} pub trait TraitCat {} pub trait TraitDog {} pub fn gamma(t: T) {} -pub fn super_soup(s: Result) -> Result { s } +pub fn super_soup(s: Result) -> Result { + s +} diff --git a/tests/rustdoc-js/hof.rs b/tests/rustdoc-js/hof.rs index 4d2c6e331cac..e4d550250e8a 100644 --- a/tests/rustdoc-js/hof.rs +++ b/tests/rustdoc-js/hof.rs @@ -4,9 +4,9 @@ pub struct First(T); pub struct Second(T); pub struct Third(T); -pub fn fn_ptr(_: fn (First) -> !, _: bool) {} -pub fn fn_once(_: impl FnOnce (Second) -> !, _: u8) {} -pub fn fn_mut(_: impl FnMut (Third) -> !, _: i8) {} -pub fn fn_(_: impl Fn (u32) -> !, _: char) {} +pub fn fn_ptr(_: fn(First) -> !, _: bool) {} +pub fn fn_once(_: impl FnOnce(Second) -> !, _: u8) {} +pub fn fn_mut(_: impl FnMut(Third) -> !, _: i8) {} +pub fn fn_(_: impl Fn(u32) -> !, _: char) {} pub fn multiple(_: impl Fn(&'static str, &'static str) -> i8) {} diff --git a/tests/rustdoc-js/macro-search.rs b/tests/rustdoc-js/macro-search.rs index dc397490cf58..7fa9cbc4ab23 100644 --- a/tests/rustdoc-js/macro-search.rs +++ b/tests/rustdoc-js/macro-search.rs @@ -1,10 +1,10 @@ #[macro_export] macro_rules! abracadabra { - () => {} + () => {}; } #[macro_export] macro_rules! abracadabra_b { - () => {} + () => {}; } pub fn abracadabra() {} pub fn abracadabra_c() {} diff --git a/tests/rustdoc-js/never-search.rs b/tests/rustdoc-js/never-search.rs index 299b4660dae9..800f9ead8b35 100644 --- a/tests/rustdoc-js/never-search.rs +++ b/tests/rustdoc-js/never-search.rs @@ -3,11 +3,27 @@ #[allow(nonstandard_style)] pub struct never; -pub fn loops() -> ! { loop {} } -pub fn returns() -> never { never } +pub fn loops() -> ! { + loop {} +} +pub fn returns() -> never { + never +} -pub fn impossible(x: !) { match x {} } -pub fn uninteresting(x: never) { match x { never => {} } } +pub fn impossible(x: !) { + match x {} +} +pub fn uninteresting(x: never) { + match x { + never => {} + } +} -pub fn box_impossible(x: Box) { match *x {} } -pub fn box_uninteresting(x: Box) { match *x { never => {} } } +pub fn box_impossible(x: Box) { + match *x {} +} +pub fn box_uninteresting(x: Box) { + match *x { + never => {} + } +} diff --git a/tests/rustdoc-js/non-english-identifier.js b/tests/rustdoc-js/non-english-identifier.js new file mode 100644 index 000000000000..1765a69152a8 --- /dev/null +++ b/tests/rustdoc-js/non-english-identifier.js @@ -0,0 +1,163 @@ +const PARSED = [ + { + query: '中文', + elems: [{ + name: "中文", + fullPath: ["中文"], + pathWithoutLast: [], + pathLast: "中文", + generics: [], + typeFilter: -1, + }], + returned: [], + foundElems: 1, + original: "中文", + userQuery: "中文", + error: null, + }, + { + query: '_0Mixed中英文', + elems: [{ + name: "_0mixed中英文", + fullPath: ["_0mixed中英文"], + pathWithoutLast: [], + pathLast: "_0mixed中英文", + generics: [], + typeFilter: -1, + }], + foundElems: 1, + original: "_0Mixed中英文", + returned: [], + userQuery: "_0mixed中英文", + error: null, + }, + { + query: 'my_crate::中文API', + elems: [{ + name: "my_crate::中文api", + fullPath: ["my_crate", "中文api"], + pathWithoutLast: ["my_crate"], + pathLast: "中文api", + generics: [], + typeFilter: -1, + }], + foundElems: 1, + original: "my_crate::中文API", + returned: [], + userQuery: "my_crate::中文api", + error: null, + }, + { + query: '类型A,类型B<约束C>->返回类型<关联类型=路径::约束D>', + elems: [{ + name: "类型a", + fullPath: ["类型a"], + pathWithoutLast: [], + pathLast: "类型a", + generics: [], + }, { + name: "类型b", + fullPath: ["类型b"], + pathWithoutLast: [], + pathLast: "类型b", + generics: [{ + name: "约束c", + fullPath: ["约束c"], + pathWithoutLast: [], + pathLast: "约束c", + generics: [], + }], + }], + foundElems: 3, + totalElems: 5, + literalSearch: true, + original: "类型A,类型B<约束C>->返回类型<关联类型=路径::约束D>", + returned: [{ + name: "返回类型", + fullPath: ["返回类型"], + pathWithoutLast: [], + pathLast: "返回类型", + generics: [], + }], + userQuery: "类型a,类型b<约束c>->返回类型<关联类型=路径::约束d>", + error: null, + }, + { + query: 'my_crate 中文宏!', + elems: [{ + name: "my_crate 中文宏", + fullPath: ["my_crate", "中文宏"], + pathWithoutLast: ["my_crate"], + pathLast: "中文宏", + generics: [], + typeFilter: 16, + }], + foundElems: 1, + original: "my_crate 中文宏!", + returned: [], + userQuery: "my_crate 中文宏!", + error: null, + }, + { + query: '非法符号——', + elems: [], + foundElems: 0, + original: "非法符号——", + returned: [], + userQuery: "非法符号——", + error: "Unexpected `—` after `号` (not a valid identifier)", + } +] +const EXPECTED = [ + { + query: '加法', + others: [ + { + name: "add", + path: "non_english_identifier", + is_alias: true, + alias: "加法", + href: "../non_english_identifier/macro.add.html" + }, + { + name: "add", + path: "non_english_identifier", + is_alias: true, + alias: "加法", + href: "../non_english_identifier/fn.add.html" + }, + { + name: "加法", + path: "non_english_identifier", + href: "../non_english_identifier/trait.加法.html", + desc: "Add" + }, + { + name: "中文名称的加法宏", + path: "non_english_identifier", + href: "../non_english_identifier/macro.中文名称的加法宏.html", + }, + { + name: "中文名称的加法API", + path: "non_english_identifier", + href: "../non_english_identifier/fn.中文名称的加法API.html", + }], + in_args: [{ + name: "加上", + path: "non_english_identifier::加法", + href: "../non_english_identifier/trait.加法.html#tymethod.加上", + }], + returned: [], + }, + { // Extensive type-based search is still buggy, experimental & work-in-progress. + query: '可迭代->可选', + others: [{ + name: "总计", + path: "non_english_identifier", + href: "../non_english_identifier/fn.总计.html", + desc: "“sum”" + }], + in_args: [], + returned: [], + }, +]; diff --git a/tests/rustdoc-js/non-english-identifier.rs b/tests/rustdoc-js/non-english-identifier.rs new file mode 100644 index 000000000000..70b5141472c8 --- /dev/null +++ b/tests/rustdoc-js/non-english-identifier.rs @@ -0,0 +1,47 @@ +#[doc(alias = "加法")] +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +pub fn 中文名称的加法API(left: usize, right: usize) -> usize { + left + right +} + +#[macro_export] +macro_rules! 中文名称的加法宏 { + ($left:expr, $right:expr) => { + ($left) + ($right) + }; +} + +#[doc(alias = "加法")] +#[macro_export] +macro_rules! add { + ($left:expr, $right:expr) => { + ($left) + ($right) + }; +} + +/// Add +pub trait 加法<类型> { + type 结果; + fn 加上(self, 被加数: 类型) -> Self::结果; +} + +/// IntoIterator +pub trait 可迭代 { + type 项; + type 转为迭代器: Iterator; + fn 迭代(self) -> Self::转为迭代器; +} + +pub type 可选<类型> = Option<类型>; + +/// "sum" +pub fn 总计<集合, 个体>(容器: 集合) -> 可选<集合::项> +where + 集合: 可迭代<项 = 个体>, + 个体: 加法<个体, 结果 = 个体>, +{ + 容器.迭代().reduce(|累计值, 当前值| 累计值.加上(当前值)) +} diff --git a/tests/rustdoc-js/path-maxeditdistance.rs b/tests/rustdoc-js/path-maxeditdistance.rs index 3861280d59bf..88af4f487741 100644 --- a/tests/rustdoc-js/path-maxeditdistance.rs +++ b/tests/rustdoc-js/path-maxeditdistance.rs @@ -1,3 +1,3 @@ -#![crate_name="abracadabra"] +#![crate_name = "abracadabra"] pub struct HocusPocusPrestidigitation; diff --git a/tests/rustdoc-js/prototype.rs b/tests/rustdoc-js/prototype.rs index 5f6d73cc1962..77553561238c 100644 --- a/tests/rustdoc-js/prototype.rs +++ b/tests/rustdoc-js/prototype.rs @@ -1,4 +1,4 @@ // The alias needed to be there to reproduce the bug // that used to be here. -#[doc(alias="other_alias")] +#[doc(alias = "other_alias")] pub fn something_else() {} diff --git a/tests/rustdoc-js/reexport-dedup-macro.rs b/tests/rustdoc-js/reexport-dedup-macro.rs index 3d18da3951c7..f70b9c3d7338 100644 --- a/tests/rustdoc-js/reexport-dedup-macro.rs +++ b/tests/rustdoc-js/reexport-dedup-macro.rs @@ -1,5 +1,5 @@ //@ aux-crate: macro_in_module=macro-in-module.rs -#![crate_name="foo"] +#![crate_name = "foo"] extern crate macro_in_module; // Test case based on the relationship between alloc and std. diff --git a/tests/rustdoc-js/reexport-dedup-method.rs b/tests/rustdoc-js/reexport-dedup-method.rs index 5dbd66e9c8ed..c00df094d7ac 100644 --- a/tests/rustdoc-js/reexport-dedup-method.rs +++ b/tests/rustdoc-js/reexport-dedup-method.rs @@ -1,5 +1,5 @@ // This test enforces that the (renamed) reexports are present in the search results. -#![crate_name="foo"] +#![crate_name = "foo"] pub mod fmt { pub struct Subscriber; @@ -14,5 +14,5 @@ mod foo { } } -pub use foo::AnotherOne; pub use fmt::Subscriber; +pub use foo::AnotherOne; diff --git a/tests/rustdoc-js/reexport-dedup.rs b/tests/rustdoc-js/reexport-dedup.rs index 40aea9633036..83c23d5c3664 100644 --- a/tests/rustdoc-js/reexport-dedup.rs +++ b/tests/rustdoc-js/reexport-dedup.rs @@ -1,5 +1,5 @@ // This test enforces that the (renamed) reexports are present in the search results. -#![crate_name="foo"] +#![crate_name = "foo"] pub mod fmt { pub struct Subscriber; @@ -8,5 +8,5 @@ mod foo { pub struct AnotherOne; } -pub use foo::AnotherOne; pub use fmt::Subscriber; +pub use foo::AnotherOne; diff --git a/tests/rustdoc-js/reexport.rs b/tests/rustdoc-js/reexport.rs index d51b7fb369c2..0b3718cd9a3e 100644 --- a/tests/rustdoc-js/reexport.rs +++ b/tests/rustdoc-js/reexport.rs @@ -9,5 +9,5 @@ mod foo { pub struct AnotherOne; } -pub use foo::AnotherOne; pub use fmt::Subscriber as FmtSubscriber; +pub use foo::AnotherOne; diff --git a/tests/rustdoc-js/reference.rs b/tests/rustdoc-js/reference.rs index 3a0a23c65d5b..93b2a6df8e6c 100644 --- a/tests/rustdoc-js/reference.rs +++ b/tests/rustdoc-js/reference.rs @@ -7,13 +7,17 @@ pub fn pinky(input: &usize, manage: usize) { pub struct Thumb; impl Thumb { - pub fn up(&self, finger: Thumb) { unimplemented!() } + pub fn up(&self, finger: Thumb) { + unimplemented!() + } } pub enum Index {} impl Index { - pub fn point(self, data: &Index) { unimplemented!() } + pub fn point(self, data: &Index) { + unimplemented!() + } } pub union Ring { @@ -22,11 +26,15 @@ pub union Ring { } impl Ring { - pub fn wear(&mut self, extra: &Ring) { unimplemented!() } + pub fn wear(&mut self, extra: &Ring) { + unimplemented!() + } } extern "C" { pub type Middle; } -pub fn show(left: &&mut Middle, right: &mut &Middle) { unimplemented!() } +pub fn show(left: &&mut Middle, right: &mut &Middle) { + unimplemented!() +} diff --git a/tests/rustdoc-js/slice-array.rs b/tests/rustdoc-js/slice-array.rs index 15ac4294f3d7..e4e34a26fa25 100644 --- a/tests/rustdoc-js/slice-array.rs +++ b/tests/rustdoc-js/slice-array.rs @@ -3,12 +3,20 @@ pub struct Q; pub struct R(T); // returns test -pub fn alef() -> &'static [R

] { loop {} } -pub fn bet() -> R<[Q; 32]> { loop {} } +pub fn alef() -> &'static [R

pub fn create(
-) -> Padding00000000000000000000000000000000000000000000000000000000000000000000000000000000
\ No newline at end of file +
pub fn create() -> Padding00000000000000000000000000000000000000000000000000000000000000000000000000000000
\ No newline at end of file diff --git a/tests/rustdoc/decl-trailing-whitespace.declaration.html b/tests/rustdoc/decl-trailing-whitespace.declaration.html index 59c318c16f3b..0cc3f0fa244e 100644 --- a/tests/rustdoc/decl-trailing-whitespace.declaration.html +++ b/tests/rustdoc/decl-trailing-whitespace.declaration.html @@ -3,7 +3,7 @@ fn poll_write( self, cx: &mut Option<String>, - buf: &mut [usize] + buf: &mut [usize], ) -> Option<Result<usize, Error>>; fn poll_flush(self, cx: &mut Option<String>) -> Option<Result<(), Error>>; fn poll_close(self, cx: &mut Option<String>) -> Option<Result<(), Error>>; @@ -12,6 +12,6 @@ fn poll_write_vectored( self, cx: &mut Option<String>, - bufs: &[usize] + bufs: &[usize], ) -> Option<Result<usize, Error>> { ... } } \ No newline at end of file diff --git a/tests/rustdoc/issue-88600.rs b/tests/rustdoc/enum-variant-doc-hidden-field-88600.rs similarity index 93% rename from tests/rustdoc/issue-88600.rs rename to tests/rustdoc/enum-variant-doc-hidden-field-88600.rs index f89af472f6e4..31d96e9db758 100644 --- a/tests/rustdoc/issue-88600.rs +++ b/tests/rustdoc/enum-variant-doc-hidden-field-88600.rs @@ -1,4 +1,6 @@ // This test ensure that #[doc(hidden)] is applied correctly in enum variant fields. +// https://github.com/rust-lang/rust/issues/88600 +#![crate_name = "foo"] // Denotes a field which should be hidden. pub struct H; @@ -6,7 +8,7 @@ pub struct H; // Denotes a field which should not be hidden (shown). pub struct S; -// @has issue_88600/enum.FooEnum.html +// @has foo/enum.FooEnum.html pub enum FooEnum { // @has - '//*[@id="variant.HiddenTupleItem"]//h3' 'HiddenTupleItem(/* private fields */)' // @count - '//*[@id="variant.HiddenTupleItem.field.0"]' 0 diff --git a/tests/rustdoc/issue-89309-heading-levels.rs b/tests/rustdoc/heading-levels-89309.rs similarity index 92% rename from tests/rustdoc/issue-89309-heading-levels.rs rename to tests/rustdoc/heading-levels-89309.rs index bb706c28ffa5..caa994285252 100644 --- a/tests/rustdoc/issue-89309-heading-levels.rs +++ b/tests/rustdoc/heading-levels-89309.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/89309 #![crate_name = "foo"] // @has foo/trait.Read.html diff --git a/tests/rustdoc/auxiliary/issue-85454.rs b/tests/rustdoc/inline_cross/auxiliary/issue-85454.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-85454.rs rename to tests/rustdoc/inline_cross/auxiliary/issue-85454.rs diff --git a/tests/rustdoc/auxiliary/issue-98697-reexport-with-anonymous-lifetime.rs b/tests/rustdoc/inline_cross/auxiliary/reexport-with-anonymous-lifetime-98697.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-98697-reexport-with-anonymous-lifetime.rs rename to tests/rustdoc/inline_cross/auxiliary/reexport-with-anonymous-lifetime-98697.rs diff --git a/tests/rustdoc/inline_cross/impl_trait.rs b/tests/rustdoc/inline_cross/impl_trait.rs index 47e2c9dc76bc..19d1673f2eb9 100644 --- a/tests/rustdoc/inline_cross/impl_trait.rs +++ b/tests/rustdoc/inline_cross/impl_trait.rs @@ -11,7 +11,7 @@ pub use impl_trait_aux::func; // @has impl_trait/fn.func2.html // @has - '//pre[@class="rust item-decl"]' "func2(" // @has - '//pre[@class="rust item-decl"]' "_x: impl Deref> + Iterator," -// @has - '//pre[@class="rust item-decl"]' "_y: impl Iterator )" +// @has - '//pre[@class="rust item-decl"]' "_y: impl Iterator, )" // @!has - '//pre[@class="rust item-decl"]' 'where' pub use impl_trait_aux::func2; diff --git a/tests/rustdoc/issue-85454.rs b/tests/rustdoc/inline_cross/qpath-self-85454.rs similarity index 94% rename from tests/rustdoc/issue-85454.rs rename to tests/rustdoc/inline_cross/qpath-self-85454.rs index 790db0c5dcfe..de806db77094 100644 --- a/tests/rustdoc/issue-85454.rs +++ b/tests/rustdoc/inline_cross/qpath-self-85454.rs @@ -1,6 +1,7 @@ //@ aux-build:issue-85454.rs //@ build-aux-docs #![crate_name = "foo"] +// https://github.com/rust-lang/rust/issues/85454 extern crate issue_85454; diff --git a/tests/rustdoc/inline_cross/reexport-with-anonymous-lifetime-98697.rs b/tests/rustdoc/inline_cross/reexport-with-anonymous-lifetime-98697.rs new file mode 100644 index 000000000000..fe6e5a39c810 --- /dev/null +++ b/tests/rustdoc/inline_cross/reexport-with-anonymous-lifetime-98697.rs @@ -0,0 +1,18 @@ +//@ aux-build:reexport-with-anonymous-lifetime-98697.rs +//@ ignore-cross-compile +#![crate_name = "foo"] + +// When reexporting a function with a HRTB with anonymous lifetimes, +// make sure the anonymous lifetimes are not rendered. +// +// https://github.com/rust-lang/rust/issues/98697 + +extern crate reexport_with_anonymous_lifetime_98697; + +// @has foo/fn.repro.html '//pre[@class="rust item-decl"]/code' 'fn repro()where F: Fn(&str)' +// @!has foo/fn.repro.html '//pre[@class="rust item-decl"]/code' 'for<' +pub use reexport_with_anonymous_lifetime_98697::repro; + +// @has foo/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl MyTrait<&Extra> for Extra' +// @!has foo/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl<' +pub use reexport_with_anonymous_lifetime_98697::Extra; diff --git a/tests/rustdoc/issue-94183-blanket-impl-reexported-trait.rs b/tests/rustdoc/inline_local/blanket-impl-reexported-trait-94183.rs similarity index 95% rename from tests/rustdoc/issue-94183-blanket-impl-reexported-trait.rs rename to tests/rustdoc/inline_local/blanket-impl-reexported-trait-94183.rs index 95ddd4c74715..343e030da9e2 100644 --- a/tests/rustdoc/issue-94183-blanket-impl-reexported-trait.rs +++ b/tests/rustdoc/inline_local/blanket-impl-reexported-trait-94183.rs @@ -2,6 +2,7 @@ // This test ensures that a publicly re-exported private trait will // appear in the blanket impl list. +// https://github.com/rust-lang/rust/issues/94183 #![crate_name = "foo"] // @has 'foo/struct.S.html' diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-81141-2.rs similarity index 85% rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs rename to tests/rustdoc/inline_local/private-reexport-in-public-api-81141-2.rs index fba310cec6d6..c066f54b32b2 100644 --- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs +++ b/tests/rustdoc/inline_local/private-reexport-in-public-api-81141-2.rs @@ -1,5 +1,6 @@ //@ edition:2015 +// https://github.com/rust-lang/rust/issues/81141 #![crate_name = "foo"] use external::Public as Private; diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-81141.rs similarity index 98% rename from tests/rustdoc/issue-81141-private-reexport-in-public-api.rs rename to tests/rustdoc/inline_local/private-reexport-in-public-api-81141.rs index bd54d02c6ec8..d695ed7fbfac 100644 --- a/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs +++ b/tests/rustdoc/inline_local/private-reexport-in-public-api-81141.rs @@ -1,6 +1,7 @@ // This test ensures that if a private re-export is present in a public API, it'll be // replaced by the first public item in the re-export chain or by the private item. +// https://github.com/rust-lang/rust/issues/81141 #![crate_name = "foo"] use crate::bar::Bar as Alias; diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-generics-81141.rs similarity index 85% rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs rename to tests/rustdoc/inline_local/private-reexport-in-public-api-generics-81141.rs index 7e289508628f..1c86c769a124 100644 --- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs +++ b/tests/rustdoc/inline_local/private-reexport-in-public-api-generics-81141.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/81141 #![crate_name = "foo"] use crate::bar::Foo as Alias; diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-hidden-81141.rs similarity index 85% rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs rename to tests/rustdoc/inline_local/private-reexport-in-public-api-hidden-81141.rs index 388f69ba3265..7d6fadf26e2f 100644 --- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs +++ b/tests/rustdoc/inline_local/private-reexport-in-public-api-hidden-81141.rs @@ -1,5 +1,6 @@ //@ compile-flags: -Z unstable-options --document-hidden-items +// https://github.com/rust-lang/rust/issues/81141 #![crate_name = "foo"] #[doc(hidden)] diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-private-81141.rs similarity index 94% rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs rename to tests/rustdoc/inline_local/private-reexport-in-public-api-private-81141.rs index 2633f98c4f30..6bf507838d55 100644 --- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs +++ b/tests/rustdoc/inline_local/private-reexport-in-public-api-private-81141.rs @@ -1,5 +1,6 @@ //@ compile-flags: --document-private-items +// https://github.com/rust-lang/rust/issues/81141 #![crate_name = "foo"] use crate::bar::Bar as Alias; diff --git a/tests/rustdoc/inline_local/reexported-macro-and-macro-export-sidebar-89852.rs b/tests/rustdoc/inline_local/reexported-macro-and-macro-export-sidebar-89852.rs new file mode 100644 index 000000000000..cffe1289ce82 --- /dev/null +++ b/tests/rustdoc/inline_local/reexported-macro-and-macro-export-sidebar-89852.rs @@ -0,0 +1,16 @@ +//@ edition:2018 + +// https://github.com/rust-lang/rust/issues/89852 +#![crate_name = "foo"] +#![no_core] +#![feature(no_core)] + +// @matchesraw 'foo/sidebar-items.js' '"repro"' +// @!matchesraw 'foo/sidebar-items.js' '"repro".*"repro"' + +#[macro_export] +macro_rules! repro { + () => {}; +} + +pub use crate::repro as repro2; diff --git a/tests/rustdoc/issue-86620.rs b/tests/rustdoc/issue-86620.rs deleted file mode 100644 index a7ac0f1d291b..000000000000 --- a/tests/rustdoc/issue-86620.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ aux-build:issue-86620-1.rs - -extern crate issue_86620_1; - -use issue_86620_1::*; - -// @!has issue_86620/struct.S.html '//*[@id="method.vzip"]//a[@class="fnname"]/@href' #tymethod.vzip -// @has issue_86620/struct.S.html '//*[@id="method.vzip"]//a[@class="anchor"]/@href' #method.vzip -pub struct S; diff --git a/tests/rustdoc/issue-89852.rs b/tests/rustdoc/issue-89852.rs deleted file mode 100644 index e9b3d80c92ee..000000000000 --- a/tests/rustdoc/issue-89852.rs +++ /dev/null @@ -1,14 +0,0 @@ -//@ edition:2018 - -#![no_core] -#![feature(no_core)] - -// @matchesraw 'issue_89852/sidebar-items.js' '"repro"' -// @!matchesraw 'issue_89852/sidebar-items.js' '"repro".*"repro"' - -#[macro_export] -macro_rules! repro { - () => {}; -} - -pub use crate::repro as repro2; diff --git a/tests/rustdoc/issue-95873.rs b/tests/rustdoc/issue-95873.rs deleted file mode 100644 index 83f1f2f75bf0..000000000000 --- a/tests/rustdoc/issue-95873.rs +++ /dev/null @@ -1,2 +0,0 @@ -// @has issue_95873/index.html "//*[@class='item-name']" "pub use ::std as x;" -pub use ::std as x; diff --git a/tests/rustdoc/issue-98697.rs b/tests/rustdoc/issue-98697.rs deleted file mode 100644 index df9f29151113..000000000000 --- a/tests/rustdoc/issue-98697.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ aux-build:issue-98697-reexport-with-anonymous-lifetime.rs -//@ ignore-cross-compile - -// When reexporting a function with a HRTB with anonymous lifetimes, -// make sure the anonymous lifetimes are not rendered. -// -// https://github.com/rust-lang/rust/issues/98697 - -extern crate issue_98697_reexport_with_anonymous_lifetime; - -// @has issue_98697/fn.repro.html '//pre[@class="rust item-decl"]/code' 'fn repro()where F: Fn(&str)' -// @!has issue_98697/fn.repro.html '//pre[@class="rust item-decl"]/code' 'for<' -pub use issue_98697_reexport_with_anonymous_lifetime::repro; - -// @has issue_98697/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl MyTrait<&Extra> for Extra' -// @!has issue_98697/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl<' -pub use issue_98697_reexport_with_anonymous_lifetime::Extra; diff --git a/tests/rustdoc/line-breaks.rs b/tests/rustdoc/line-breaks.rs index 21aa3a03ce43..0f760d51973c 100644 --- a/tests/rustdoc/line-breaks.rs +++ b/tests/rustdoc/line-breaks.rs @@ -6,7 +6,7 @@ use std::ops::Add; // @matches foo/fn.function_with_a_really_long_name.html '//*[@class="rust item-decl"]//code' "\ // function_with_a_really_long_name\(\n\ // \ parameter_one: i32,\n\ -// \ parameter_two: i32\n\ +// \ parameter_two: i32,\n\ // \) -> Option$" pub fn function_with_a_really_long_name(parameter_one: i32, parameter_two: i32) -> Option { Some(parameter_one + parameter_two) diff --git a/tests/rustdoc/method-anchor-in-blanket-impl-86620.rs b/tests/rustdoc/method-anchor-in-blanket-impl-86620.rs new file mode 100644 index 000000000000..537dadd21241 --- /dev/null +++ b/tests/rustdoc/method-anchor-in-blanket-impl-86620.rs @@ -0,0 +1,11 @@ +//@ aux-build:issue-86620-1.rs +#![crate_name = "foo"] +// https://github.com/rust-lang/rust/issues/86620 + +extern crate issue_86620_1; + +use issue_86620_1::*; + +// @!has foo/struct.S.html '//*[@id="method.vzip"]//a[@class="fnname"]/@href' #tymethod.vzip +// @has foo/struct.S.html '//*[@id="method.vzip"]//a[@class="anchor"]/@href' #method.vzip +pub struct S; diff --git a/tests/rustdoc/issue-99221-multiple-macro-rules-w-same-name-submodule.rs b/tests/rustdoc/multiple-macro-rules-w-same-name-submodule-99221.rs similarity index 85% rename from tests/rustdoc/issue-99221-multiple-macro-rules-w-same-name-submodule.rs rename to tests/rustdoc/multiple-macro-rules-w-same-name-submodule-99221.rs index d3ccd1c069b6..ed1e42c1f4ee 100644 --- a/tests/rustdoc/issue-99221-multiple-macro-rules-w-same-name-submodule.rs +++ b/tests/rustdoc/multiple-macro-rules-w-same-name-submodule-99221.rs @@ -2,6 +2,7 @@ //@ build-aux-docs //@ ignore-cross-compile +// https://github.com/rust-lang/rust/issues/99221 #![crate_name = "foo"] #[macro_use] diff --git a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-83375.rs similarity index 85% rename from tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs rename to tests/rustdoc/multiple-mods-w-same-name-doc-inline-83375.rs index d0960dfef436..9b3dfd45370b 100644 --- a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs +++ b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-83375.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/83375 #![crate_name = "foo"] pub mod sub { diff --git a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-last-item-83375.rs similarity index 85% rename from tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs rename to tests/rustdoc/multiple-mods-w-same-name-doc-inline-last-item-83375.rs index 9bce25846d85..7bad825b35fe 100644 --- a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs +++ b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-last-item-83375.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/83375 #![crate_name = "foo"] pub mod sub { diff --git a/tests/rustdoc/pub-use-root-path-95873.rs b/tests/rustdoc/pub-use-root-path-95873.rs new file mode 100644 index 000000000000..5a817fb3409e --- /dev/null +++ b/tests/rustdoc/pub-use-root-path-95873.rs @@ -0,0 +1,5 @@ +// https://github.com/rust-lang/rust/issues/95873 +#![crate_name = "foo"] + +// @has foo/index.html "//*[@class='item-name']" "pub use ::std as x;" +pub use ::std as x; diff --git a/tests/rustdoc/reexports-priv.rs b/tests/rustdoc/reexports-priv.rs index 1eee262d2330..97318a014105 100644 --- a/tests/rustdoc/reexports-priv.rs +++ b/tests/rustdoc/reexports-priv.rs @@ -98,7 +98,7 @@ pub mod outer { pub use reexports::foo; // @has 'foo/outer/inner/fn.foo_crate.html' '//pre[@class="rust item-decl"]' 'pub(crate) fn foo_crate()' pub(crate) use reexports::foo_crate; - // @has 'foo/outer/inner/fn.foo_super.html' '//pre[@class="rust item-decl"]' 'pub(in outer) fn foo_super( )' + // @has 'foo/outer/inner/fn.foo_super.html' '//pre[@class="rust item-decl"]' 'pub(in outer) fn foo_super()' pub(super) use::reexports::foo_super; // @!has 'foo/outer/inner/fn.foo_self.html' pub(self) use reexports::foo_self; diff --git a/tests/rustdoc/issue-80233-normalize-auto-trait.rs b/tests/rustdoc/synthetic_auto/normalize-auto-trait-80233.rs similarity index 86% rename from tests/rustdoc/issue-80233-normalize-auto-trait.rs rename to tests/rustdoc/synthetic_auto/normalize-auto-trait-80233.rs index 62fbc2444dbc..064980186307 100644 --- a/tests/rustdoc/issue-80233-normalize-auto-trait.rs +++ b/tests/rustdoc/synthetic_auto/normalize-auto-trait-80233.rs @@ -1,7 +1,9 @@ // Regression test for issue #80233 // Tests that we don't ICE when processing auto traits +// https://github.com/rust-lang/rust/issues/80233 #![crate_type = "lib"] +#![crate_name = "foo"] pub trait Trait1 {} pub trait Trait2 { @@ -30,7 +32,7 @@ impl Trait3 for Vec { pub struct Struct1 {} -// @has issue_80233_normalize_auto_trait/struct.Question.html +// @has foo/struct.Question.html // @has - '//h3[@class="code-header"]' 'impl Send for Question' pub struct Question { pub ins: < as Trait3>::Type3 as Trait2>::Type2, diff --git a/tests/rustdoc/typedef-inner-variants.rs b/tests/rustdoc/typedef-inner-variants.rs index d87a1cb6facb..0e65fdaf2afd 100644 --- a/tests/rustdoc/typedef-inner-variants.rs +++ b/tests/rustdoc/typedef-inner-variants.rs @@ -117,3 +117,10 @@ pub type HighlyGenericAABB = HighlyGenericStruct; // @count - '//*[@id="variants"]' 0 // @count - '//*[@id="fields"]' 1 pub use cross_crate_generic_typedef::InlineU64; + +// @has 'inner_variants/type.InlineEnum.html' +// @count - '//*[@id="aliased-type"]' 1 +// @count - '//*[@id="variants"]' 1 +// @count - '//*[@id="fields"]' 0 +// @count - '//*[@class="variant"]' 2 +pub type InlineEnum = cross_crate_generic_typedef::GenericEnum; diff --git a/tests/rustdoc/issue-96381.rs b/tests/rustdoc/underscore-type-in-trait-impl-96381.rs similarity index 85% rename from tests/rustdoc/issue-96381.rs rename to tests/rustdoc/underscore-type-in-trait-impl-96381.rs index 90875c076057..6d6e5e0a8398 100644 --- a/tests/rustdoc/issue-96381.rs +++ b/tests/rustdoc/underscore-type-in-trait-impl-96381.rs @@ -1,4 +1,5 @@ //@ should-fail +// https://github.com/rust-lang/rust/issues/96381 #![allow(unused)] diff --git a/tests/run-make-fulldeps/obtain-borrowck/test.rs b/tests/ui-fulldeps/auxiliary/obtain-borrowck-input.rs similarity index 94% rename from tests/run-make-fulldeps/obtain-borrowck/test.rs rename to tests/ui-fulldeps/auxiliary/obtain-borrowck-input.rs index f7b4b41feaf9..7213e06792a4 100644 --- a/tests/run-make-fulldeps/obtain-borrowck/test.rs +++ b/tests/ui-fulldeps/auxiliary/obtain-borrowck-input.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + trait X { fn provided(&self) -> usize { 5 diff --git a/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs similarity index 89% rename from tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs rename to tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs index 28a1e27ccccc..f273bbc99a8a 100644 --- a/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs +++ b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs @@ -1,3 +1,5 @@ +//@ edition: 2021 + #![feature(rustc_private)] #![deny(warnings)] @@ -27,7 +29,9 @@ use std::any::Any; struct TheBackend; impl CodegenBackend for TheBackend { - fn locale_resource(&self) -> &'static str { "" } + fn locale_resource(&self) -> &'static str { + "" + } fn codegen_crate<'a, 'tcx>( &self, @@ -62,7 +66,10 @@ impl CodegenBackend for TheBackend { codegen_results: CodegenResults, outputs: &OutputFilenames, ) -> Result<(), ErrorGuaranteed> { - use rustc_session::{config::{CrateType, OutFileName}, output::out_filename}; + use rustc_session::{ + config::{CrateType, OutFileName}, + output::out_filename, + }; use std::io::Write; let crate_name = codegen_results.crate_info.local_crate_name; for &crate_type in sess.opts.crate_types.iter() { @@ -73,11 +80,11 @@ impl CodegenBackend for TheBackend { match output_name { OutFileName::Real(ref path) => { let mut out_file = ::std::fs::File::create(path).unwrap(); - write!(out_file, "This has been \"compiled\" successfully.").unwrap(); + writeln!(out_file, "This has been 'compiled' successfully.").unwrap(); } OutFileName::Stdout => { let mut stdout = std::io::stdout(); - write!(stdout, "This has been \"compiled\" successfully.").unwrap(); + writeln!(stdout, "This has been 'compiled' successfully.").unwrap(); } } } diff --git a/tests/ui-fulldeps/codegen-backend/hotplug.bindep.stdout b/tests/ui-fulldeps/codegen-backend/hotplug.bindep.stdout new file mode 100644 index 000000000000..4d58fd503d0a --- /dev/null +++ b/tests/ui-fulldeps/codegen-backend/hotplug.bindep.stdout @@ -0,0 +1,4 @@ +$TEST_BUILD_DIR/codegen-backend/hotplug.bindep/libhotplug.rlib: $DIR/hotplug.rs $TEST_BUILD_DIR/codegen-backend/hotplug.bindep/auxiliary/libthe_backend.so + +$DIR/hotplug.rs: +$TEST_BUILD_DIR/codegen-backend/hotplug.bindep/auxiliary/libthe_backend.so: diff --git a/tests/ui-fulldeps/codegen-backend/hotplug.dep.stdout b/tests/ui-fulldeps/codegen-backend/hotplug.dep.stdout new file mode 100644 index 000000000000..48b7534d8faf --- /dev/null +++ b/tests/ui-fulldeps/codegen-backend/hotplug.dep.stdout @@ -0,0 +1,3 @@ +$TEST_BUILD_DIR/codegen-backend/hotplug.dep/libhotplug.rlib: $DIR/hotplug.rs + +$DIR/hotplug.rs: diff --git a/tests/ui-fulldeps/codegen-backend/hotplug.normal.stdout b/tests/ui-fulldeps/codegen-backend/hotplug.normal.stdout new file mode 100644 index 000000000000..1aa032de9e49 --- /dev/null +++ b/tests/ui-fulldeps/codegen-backend/hotplug.normal.stdout @@ -0,0 +1 @@ +This has been 'compiled' successfully. diff --git a/tests/ui-fulldeps/codegen-backend/hotplug.rs b/tests/ui-fulldeps/codegen-backend/hotplug.rs new file mode 100644 index 000000000000..dc0fb3f9efd0 --- /dev/null +++ b/tests/ui-fulldeps/codegen-backend/hotplug.rs @@ -0,0 +1,20 @@ +//@ edition: 2021 +//@ build-pass +//@ ignore-stage1 (requires matching sysroot built with in-tree compiler) + +//@ aux-codegen-backend: the_backend.rs +//@ normalize-stdout-test: "libthe_backend.dylib" -> "libthe_backend.so" +//@ normalize-stdout-test: "the_backend.dll" -> "libthe_backend.so" + +//@ revisions: normal dep bindep +//@ compile-flags: --crate-type=lib +//@ [normal] compile-flags: --emit=link=- +//@ [dep] compile-flags: --emit=link,dep-info=- +//@ [bindep] compile-flags: --emit=link,dep-info=- -Zbinary-dep-depinfo + +#![feature(no_core)] +#![no_core] + +// This test both exists as a check that -Zcodegen-backend is capable of loading external codegen +// backends and that this external codegen backend is only included in the dep info if +// -Zbinary-dep-depinfo is used. diff --git a/tests/ui-fulldeps/internal-lints/diagnostics.rs b/tests/ui-fulldeps/internal-lints/diagnostics.rs index 380b2b6c431d..5fcff74064a6 100644 --- a/tests/ui-fulldeps/internal-lints/diagnostics.rs +++ b/tests/ui-fulldeps/internal-lints/diagnostics.rs @@ -14,8 +14,8 @@ extern crate rustc_session; extern crate rustc_span; use rustc_errors::{ - Diag, DiagCtxt, DiagInner, DiagMessage, Diagnostic, EmissionGuarantee, Level, LintDiagnostic, - SubdiagMessageOp, SubdiagMessage, Subdiagnostic, + Diag, DiagCtxtHandle, DiagInner, DiagMessage, Diagnostic, EmissionGuarantee, Level, + LintDiagnostic, SubdiagMessage, SubdiagMessageOp, Subdiagnostic, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::Span; @@ -39,7 +39,7 @@ struct Note { pub struct UntranslatableInDiagnostic; impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UntranslatableInDiagnostic { - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { Diag::new(dcx, level, "untranslatable diagnostic") //~^ ERROR diagnostics should be created using translatable messages } @@ -48,7 +48,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UntranslatableInDiagnostic pub struct TranslatableInDiagnostic; impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for TranslatableInDiagnostic { - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { Diag::new(dcx, level, crate::fluent_generated::no_crate_example) } } @@ -81,14 +81,10 @@ impl Subdiagnostic for TranslatableInAddtoDiag { pub struct UntranslatableInLintDiagnostic; impl<'a> LintDiagnostic<'a, ()> for UntranslatableInLintDiagnostic { - fn decorate_lint<'b, >(self, diag: &'b mut Diag<'a, ()>) { + fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { diag.note("untranslatable diagnostic"); //~^ ERROR diagnostics should be created using translatable messages } - - fn msg(&self) -> DiagMessage { - unreachable!(); - } } pub struct TranslatableInLintDiagnostic; @@ -97,13 +93,9 @@ impl<'a> LintDiagnostic<'a, ()> for TranslatableInLintDiagnostic { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { diag.note(crate::fluent_generated::no_crate_note); } - - fn msg(&self) -> DiagMessage { - unreachable!(); - } } -pub fn make_diagnostics<'a>(dcx: &'a DiagCtxt) { +pub fn make_diagnostics<'a>(dcx: DiagCtxtHandle<'a>) { let _diag = dcx.struct_err(crate::fluent_generated::no_crate_example); //~^ ERROR diagnostics should only be created in `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls @@ -115,7 +107,7 @@ pub fn make_diagnostics<'a>(dcx: &'a DiagCtxt) { // Check that `rustc_lint_diagnostics`-annotated functions aren't themselves linted for // `diagnostic_outside_of_impl`. #[rustc_lint_diagnostics] -pub fn skipped_because_of_annotation<'a>(dcx: &'a DiagCtxt) { +pub fn skipped_because_of_annotation<'a>(dcx: DiagCtxtHandle<'a>) { #[allow(rustc::untranslatable_diagnostic)] let _diag = dcx.struct_err("untranslatable diagnostic"); // okay! } diff --git a/tests/ui-fulldeps/internal-lints/diagnostics.stderr b/tests/ui-fulldeps/internal-lints/diagnostics.stderr index ee5400f6f951..669324ce5d4c 100644 --- a/tests/ui-fulldeps/internal-lints/diagnostics.stderr +++ b/tests/ui-fulldeps/internal-lints/diagnostics.stderr @@ -23,7 +23,7 @@ LL | diag.note("untranslatable diagnostic"); | ^^^^ error: diagnostics should only be created in `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls - --> $DIR/diagnostics.rs:107:21 + --> $DIR/diagnostics.rs:99:21 | LL | let _diag = dcx.struct_err(crate::fluent_generated::no_crate_example); | ^^^^^^^^^^ @@ -35,13 +35,13 @@ LL | #![deny(rustc::diagnostic_outside_of_impl)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should only be created in `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls - --> $DIR/diagnostics.rs:110:21 + --> $DIR/diagnostics.rs:102:21 | LL | let _diag = dcx.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:110:21 + --> $DIR/diagnostics.rs:102:21 | LL | let _diag = dcx.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^ diff --git a/tests/ui-fulldeps/mod_dir_path_canonicalized.rs b/tests/ui-fulldeps/mod_dir_path_canonicalized.rs index ef70e64ed7e9..99cb5fc5aa1c 100644 --- a/tests/ui-fulldeps/mod_dir_path_canonicalized.rs +++ b/tests/ui-fulldeps/mod_dir_path_canonicalized.rs @@ -16,7 +16,7 @@ extern crate rustc_span; #[allow(unused_extern_crates)] extern crate rustc_driver; -use rustc_parse::new_parser_from_file; +use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal}; use rustc_session::parse::ParseSess; use std::path::Path; @@ -34,6 +34,6 @@ fn parse() { let path = Path::new(file!()); let path = path.canonicalize().unwrap(); - let mut parser = new_parser_from_file(&psess, &path, None); + let mut parser = unwrap_or_emit_fatal(new_parser_from_file(&psess, &path, None)); let _ = parser.parse_crate_mod(); } diff --git a/tests/run-make-fulldeps/obtain-borrowck/driver.rs b/tests/ui-fulldeps/obtain-borrowck.rs similarity index 96% rename from tests/run-make-fulldeps/obtain-borrowck/driver.rs rename to tests/ui-fulldeps/obtain-borrowck.rs index e67ec8690f81..e6c703addd92 100644 --- a/tests/run-make-fulldeps/obtain-borrowck/driver.rs +++ b/tests/ui-fulldeps/obtain-borrowck.rs @@ -1,3 +1,10 @@ +//@ edition: 2021 +//@ run-pass +//@ check-run-results +//@ run-flags: --sysroot {{sysroot-base}} --edition=2021 {{src-base}}/auxiliary/obtain-borrowck-input.rs +//@ ignore-stage1 (requires matching sysroot built with in-tree compiler) +// ignore-tidy-linelength + #![feature(rustc_private)] //! This program implements a rustc driver that retrieves MIR bodies with diff --git a/tests/run-make-fulldeps/obtain-borrowck/output.stdout b/tests/ui-fulldeps/obtain-borrowck.run.stdout similarity index 100% rename from tests/run-make-fulldeps/obtain-borrowck/output.stdout rename to tests/ui-fulldeps/obtain-borrowck.run.stdout diff --git a/tests/ui-fulldeps/pprust-expr-roundtrip.rs b/tests/ui-fulldeps/pprust-expr-roundtrip.rs index 2b1fec943871..762ad0b79ecc 100644 --- a/tests/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/tests/ui-fulldeps/pprust-expr-roundtrip.rs @@ -36,7 +36,7 @@ use rustc_ast::mut_visit::{visit_clobber, MutVisitor}; use rustc_ast::ptr::P; use rustc_ast::*; use rustc_ast_pretty::pprust; -use rustc_parse::new_parser_from_source_str; +use rustc_parse::{new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_session::parse::ParseSess; use rustc_span::source_map::Spanned; use rustc_span::symbol::Ident; @@ -46,8 +46,9 @@ use thin_vec::{thin_vec, ThinVec}; fn parse_expr(psess: &ParseSess, src: &str) -> Option> { let src_as_string = src.to_string(); - let mut p = - new_parser_from_source_str(psess, FileName::Custom(src_as_string.clone()), src_as_string); + let mut p = unwrap_or_emit_fatal( + new_parser_from_source_str(psess, FileName::Custom(src_as_string.clone()), src_as_string) + ); p.parse_expr().map_err(|e| e.cancel()).ok() } diff --git a/tests/run-make-fulldeps/issue-19371/foo.rs b/tests/ui-fulldeps/run-compiler-twice.rs similarity index 65% rename from tests/run-make-fulldeps/issue-19371/foo.rs rename to tests/ui-fulldeps/run-compiler-twice.rs index 327c99a02c6f..02748626723d 100644 --- a/tests/run-make-fulldeps/issue-19371/foo.rs +++ b/tests/ui-fulldeps/run-compiler-twice.rs @@ -1,3 +1,13 @@ +//@ edition: 2021 +//@ run-pass +//@ run-flags: {{sysroot-base}} {{target-linker}} +//@ ignore-stage1 (requires matching sysroot built with in-tree compiler) + +// Regression test for . +// +// This test ensures that `compile_input` can be called twice in one task +// without causing a panic. + #![feature(rustc_private)] extern crate rustc_driver; @@ -5,12 +15,12 @@ extern crate rustc_interface; extern crate rustc_session; extern crate rustc_span; +use std::path::{Path, PathBuf}; + use rustc_interface::interface; use rustc_session::config::{Input, Options, OutFileName, OutputType, OutputTypes}; use rustc_span::FileName; -use std::path::PathBuf; - fn main() { let src = r#" fn main() {} @@ -18,28 +28,28 @@ fn main() { let args: Vec = std::env::args().collect(); - if args.len() < 4 { - panic!("expected rustc path"); + if args.len() < 2 { + panic!("expected sysroot (and optional linker)"); } - let tmpdir = PathBuf::from(&args[1]); + let sysroot = PathBuf::from(&args[1]); + let linker = args.get(2).map(PathBuf::from); - let mut sysroot = PathBuf::from(&args[3]); - sysroot.pop(); - sysroot.pop(); + // compiletest sets the current dir to `output_base_dir` when running. + let tmpdir = std::env::current_dir().unwrap().join("tmp"); + std::fs::create_dir_all(&tmpdir).unwrap(); - compile(src.to_string(), tmpdir.join("out"), sysroot.clone()); - - compile(src.to_string(), tmpdir.join("out"), sysroot.clone()); + compile(src.to_string(), tmpdir.join("out"), sysroot.clone(), linker.as_deref()); + compile(src.to_string(), tmpdir.join("out"), sysroot.clone(), linker.as_deref()); } -fn compile(code: String, output: PathBuf, sysroot: PathBuf) { +fn compile(code: String, output: PathBuf, sysroot: PathBuf, linker: Option<&Path>) { let mut opts = Options::default(); opts.output_types = OutputTypes::new(&[(OutputType::Exe, None)]); opts.maybe_sysroot = Some(sysroot); - if let Ok(linker) = std::env::var("RUSTC_LINKER") { - opts.cg.linker = Some(linker.into()); + if let Some(linker) = linker { + opts.cg.linker = Some(linker.to_owned()); } let name = FileName::anon_source_code(&code); diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 043c7c166540..4f50837d5fe4 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -603,7 +603,6 @@ struct LintAttributeOnSessionDiag {} #[derive(LintDiagnostic)] #[lint(no_crate_example, code = E0123)] //~^ ERROR `#[lint(...)]` is not a valid attribute -//~| ERROR `#[lint(...)]` is not a valid attribute //~| ERROR diagnostic slug not specified //~| ERROR cannot find attribute `lint` in this scope struct LintAttributeOnLintDiag {} diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 1266430e2f77..e36c6852d3b2 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -378,14 +378,6 @@ error: `#[lint(...)]` is not a valid attribute LL | #[lint(no_crate_example, code = E0123)] | ^ -error: `#[lint(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:604:1 - | -LL | #[lint(no_crate_example, code = E0123)] - | ^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error: diagnostic slug not specified --> $DIR/diagnostic-derive.rs:604:1 | @@ -395,19 +387,19 @@ LL | #[lint(no_crate_example, code = E0123)] = help: specify the slug as the first argument to the attribute, such as `#[diag(compiletest_example)]` error: specified multiple times - --> $DIR/diagnostic-derive.rs:614:53 + --> $DIR/diagnostic-derive.rs:613:53 | LL | #[suggestion(no_crate_suggestion, code = "...", code = ",,,")] | ^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:614:39 + --> $DIR/diagnostic-derive.rs:613:39 | LL | #[suggestion(no_crate_suggestion, code = "...", code = ",,,")] | ^^^^ error: wrong types for suggestion - --> $DIR/diagnostic-derive.rs:623:24 + --> $DIR/diagnostic-derive.rs:622:24 | LL | suggestion: (Span, usize), | ^^^^^ @@ -415,7 +407,7 @@ LL | suggestion: (Span, usize), = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)` error: wrong types for suggestion - --> $DIR/diagnostic-derive.rs:631:17 + --> $DIR/diagnostic-derive.rs:630:17 | LL | suggestion: (Span,), | ^^^^^^^ @@ -423,13 +415,13 @@ LL | suggestion: (Span,), = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)` error: suggestion without `code = "..."` - --> $DIR/diagnostic-derive.rs:638:5 + --> $DIR/diagnostic-derive.rs:637:5 | LL | #[suggestion(no_crate_suggestion)] | ^ error: `#[multipart_suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:645:1 + --> $DIR/diagnostic-derive.rs:644:1 | LL | #[multipart_suggestion(no_crate_suggestion)] | ^ @@ -437,7 +429,7 @@ LL | #[multipart_suggestion(no_crate_suggestion)] = help: consider creating a `Subdiagnostic` instead error: `#[multipart_suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:648:1 + --> $DIR/diagnostic-derive.rs:647:1 | LL | #[multipart_suggestion()] | ^ @@ -445,7 +437,7 @@ LL | #[multipart_suggestion()] = help: consider creating a `Subdiagnostic` instead error: `#[multipart_suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:652:5 + --> $DIR/diagnostic-derive.rs:651:5 | LL | #[multipart_suggestion(no_crate_suggestion)] | ^ @@ -453,7 +445,7 @@ LL | #[multipart_suggestion(no_crate_suggestion)] = help: consider creating a `Subdiagnostic` instead error: `#[suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:660:1 + --> $DIR/diagnostic-derive.rs:659:1 | LL | #[suggestion(no_crate_suggestion, code = "...")] | ^ @@ -461,7 +453,7 @@ LL | #[suggestion(no_crate_suggestion, code = "...")] = help: `#[label]` and `#[suggestion]` can only be applied to fields error: `#[label]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:669:1 + --> $DIR/diagnostic-derive.rs:668:1 | LL | #[label] | ^ @@ -469,61 +461,61 @@ LL | #[label] = help: `#[label]` and `#[suggestion]` can only be applied to fields error: `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:703:5 + --> $DIR/diagnostic-derive.rs:702:5 | LL | #[subdiagnostic(bad)] | ^ error: `#[subdiagnostic = ...]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:711:5 + --> $DIR/diagnostic-derive.rs:710:5 | LL | #[subdiagnostic = "bad"] | ^ error: `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:719:5 + --> $DIR/diagnostic-derive.rs:718:5 | LL | #[subdiagnostic(bad, bad)] | ^ error: `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:727:5 + --> $DIR/diagnostic-derive.rs:726:5 | LL | #[subdiagnostic("bad")] | ^ error: `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:735:5 + --> $DIR/diagnostic-derive.rs:734:5 | LL | #[subdiagnostic(eager)] | ^ error: `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:743:5 + --> $DIR/diagnostic-derive.rs:742:5 | LL | #[subdiagnostic(eager)] | ^ error: `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:764:5 + --> $DIR/diagnostic-derive.rs:763:5 | LL | #[subdiagnostic(eager)] | ^ error: expected at least one string literal for `code(...)` - --> $DIR/diagnostic-derive.rs:795:23 + --> $DIR/diagnostic-derive.rs:794:23 | LL | #[suggestion(code())] | ^ error: `code(...)` must contain only string literals - --> $DIR/diagnostic-derive.rs:803:23 + --> $DIR/diagnostic-derive.rs:802:23 | LL | #[suggestion(code(foo))] | ^^^ error: `#[suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:827:5 + --> $DIR/diagnostic-derive.rs:826:5 | LL | #[suggestion(no_crate_suggestion, code = "")] | ^ @@ -539,13 +531,13 @@ LL | #[diag = "E0123"] | ^ maybe a missing crate `core`? error[E0433]: failed to resolve: maybe a missing crate `core`? - --> $DIR/diagnostic-derive.rs:803:23 + --> $DIR/diagnostic-derive.rs:802:23 | LL | #[suggestion(code(foo))] | ^^^ maybe a missing crate `core`? error[E0433]: failed to resolve: maybe a missing crate `core`? - --> $DIR/diagnostic-derive.rs:812:25 + --> $DIR/diagnostic-derive.rs:811:25 | LL | #[suggestion(code = 3)] | ^ maybe a missing crate `core`? @@ -587,19 +579,19 @@ LL | #[lint(no_crate_example, code = E0123)] | ^^^^ help: a built-in attribute with a similar name exists: `link` error: cannot find attribute `multipart_suggestion` in this scope - --> $DIR/diagnostic-derive.rs:645:3 + --> $DIR/diagnostic-derive.rs:644:3 | LL | #[multipart_suggestion(no_crate_suggestion)] | ^^^^^^^^^^^^^^^^^^^^ error: cannot find attribute `multipart_suggestion` in this scope - --> $DIR/diagnostic-derive.rs:648:3 + --> $DIR/diagnostic-derive.rs:647:3 | LL | #[multipart_suggestion()] | ^^^^^^^^^^^^^^^^^^^^ error: cannot find attribute `multipart_suggestion` in this scope - --> $DIR/diagnostic-derive.rs:652:7 + --> $DIR/diagnostic-derive.rs:651:7 | LL | #[multipart_suggestion(no_crate_suggestion)] | ^^^^^^^^^^^^^^^^^^^^ @@ -611,7 +603,7 @@ LL | #[diag(nonsense, code = E0123)] | ^^^^^^^^ not found in `crate::fluent_generated` error[E0425]: cannot find value `__code_34` in this scope - --> $DIR/diagnostic-derive.rs:809:10 + --> $DIR/diagnostic-derive.rs:808:10 | LL | #[derive(Diagnostic)] | ^^^^^^^^^^ not found in this scope @@ -632,7 +624,7 @@ note: required by a bound in `Diag::<'a, G>::arg` --> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC = note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 86 previous errors +error: aborting due to 85 previous errors Some errors have detailed explanations: E0277, E0425, E0433. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui-fulldeps/stable-mir/check_def_ty.rs b/tests/ui-fulldeps/stable-mir/check_def_ty.rs new file mode 100644 index 000000000000..9f45b62d3434 --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/check_def_ty.rs @@ -0,0 +1,114 @@ +//@ run-pass +//! Test that users are able to use stable mir APIs to retrieve type information from a crate item +//! definition. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 +//@ edition: 2021 + +#![feature(rustc_private)] +#![feature(assert_matches)] +#![feature(control_flow_enum)] + +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate stable_mir; + +use rustc_smir::rustc_internal; +use stable_mir::ty::{Ty, ForeignItemKind}; +use stable_mir::*; +use std::io::Write; +use std::ops::ControlFlow; + +const CRATE_NAME: &str = "crate_def_ty"; + +/// Test if we can retrieve type information from different definitions. +fn test_def_tys() -> ControlFlow<()> { + let items = stable_mir::all_local_items(); + for item in &items { + // Type from crate items. + let ty = item.ty(); + match item.name().as_str() { + "STATIC_STR" => assert!(ty.kind().is_ref()), + "CONST_U32" => assert!(ty.kind().is_integral()), + "main" => { check_fn_def(ty) } + _ => unreachable!("Unexpected item: `{item:?}`") + } + } + + let foreign_items = stable_mir::local_crate().foreign_modules(); + for item in foreign_items[0].module().items() { + // Type from foreign items. + let ty = item.ty(); + let item_kind = item.kind(); + let name = item.name(); + match item_kind { + ForeignItemKind::Fn(fn_def) => { + assert_eq!(&name, "extern_fn"); + assert_eq!(ty, fn_def.ty()); + check_fn_def(ty) + } + ForeignItemKind::Static(def) => { + assert_eq!(&name, "EXT_STATIC"); + assert_eq!(ty, def.ty()); + assert!(ty.kind().is_integral()) + } + _ => unreachable!("Unexpected kind: {item_kind:?}") + }; + } + + ControlFlow::Continue(()) +} + +fn check_fn_def(ty: Ty) { + let kind = ty.kind(); + let (def, args) = kind.fn_def().expect(&format!("Expected function type, but found: {ty}")); + assert!(def.ty().kind().is_fn()); + assert_eq!(def.ty_with_args(args), ty); +} + +/// This test will generate and analyze a dummy crate using the stable mir. +/// For that, it will first write the dummy crate into a file. +/// Then it will create a `StableMir` using custom arguments and then +/// it will run the compiler. +fn main() { + let path = "defs_ty_input.rs"; + generate_input(&path).unwrap(); + let args = vec![ + "rustc".to_string(), + "-Cpanic=abort".to_string(), + "--crate-name".to_string(), + CRATE_NAME.to_string(), + path.to_string(), + ]; + run!(args, test_def_tys).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + // We would like to check intrinsic definition. + #![feature(core_intrinsics)] + static STATIC_STR: &str = "foo"; + const CONST_U32: u32 = 0u32; + + fn main() {{ + let _c = core::char::from_u32(99); + let _v = Vec::::new(); + let _i = std::intrinsics::size_of::(); + }} + + extern "C" {{ + fn extern_fn(x: i32) -> i32; + static EXT_STATIC: i32; + }} + "# + )?; + Ok(()) +} diff --git a/tests/ui-fulldeps/stable-mir/check_instance.rs b/tests/ui-fulldeps/stable-mir/check_instance.rs index 218c7b3e3863..f971426723f2 100644 --- a/tests/ui-fulldeps/stable-mir/check_instance.rs +++ b/tests/ui-fulldeps/stable-mir/check_instance.rs @@ -33,7 +33,7 @@ fn test_stable_mir() -> ControlFlow<()> { // Get all items and split generic vs monomorphic items. let (generic, mono): (Vec<_>, Vec<_>) = items.into_iter().partition(|item| item.requires_monomorphization()); - assert_eq!(mono.len(), 3, "Expected 2 mono functions and one constant"); + assert_eq!(mono.len(), 4, "Expected 2 mono functions and one constant"); assert_eq!(generic.len(), 2, "Expected 2 generic functions"); // For all monomorphic items, get the correspondent instances. @@ -57,8 +57,9 @@ fn test_body(body: mir::Body) { for term in body.blocks.iter().map(|bb| &bb.terminator) { match &term.kind { Call { func, .. } => { - let TyKind::RigidTy(ty) = func.ty(body.locals()).unwrap().kind() else { unreachable! - () }; + let TyKind::RigidTy(ty) = func.ty(body.locals()).unwrap().kind() else { + unreachable!() + }; let RigidTy::FnDef(def, args) = ty else { unreachable!() }; let instance = Instance::resolve(def, &args).unwrap(); let mangled_name = instance.mangled_name(); @@ -102,6 +103,9 @@ fn generate_input(path: &str) -> std::io::Result<()> { write!( file, r#" + + struct Foo(()); + pub fn ty_param(t: &T) -> T where T: Clone {{ t.clone() }} @@ -116,6 +120,7 @@ fn generate_input(path: &str) -> std::io::Result<()> { }} pub fn monomorphic() {{ + Foo(()); let v = vec![10]; let dup = ty_param(&v); assert_eq!(v, dup); diff --git a/tests/ui-fulldeps/stable-mir/check_intrinsics.rs b/tests/ui-fulldeps/stable-mir/check_intrinsics.rs index 171850b89bb5..d7f37f36681b 100644 --- a/tests/ui-fulldeps/stable-mir/check_intrinsics.rs +++ b/tests/ui-fulldeps/stable-mir/check_intrinsics.rs @@ -11,6 +11,7 @@ //@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 #![feature(rustc_private)] +#![feature(assert_matches)] extern crate rustc_hir; #[macro_use] @@ -23,8 +24,8 @@ use rustc_smir::rustc_internal; use stable_mir::mir::mono::{Instance, InstanceKind}; use stable_mir::mir::visit::{Location, MirVisitor}; use stable_mir::mir::{LocalDecl, Terminator, TerminatorKind}; -use stable_mir::ty::{RigidTy, TyKind}; -use std::collections::HashSet; +use stable_mir::ty::{FnDef, GenericArgs, RigidTy, TyKind}; +use std::assert_matches::assert_matches; use std::convert::TryFrom; use std::io::Write; use std::ops::ControlFlow; @@ -39,9 +40,10 @@ fn test_intrinsics() -> ControlFlow<()> { visitor.visit_body(&main_body); let calls = visitor.calls; - assert_eq!(calls.len(), 2, "Expected 2 calls, but found: {calls:?}"); - for intrinsic in &calls { - check_intrinsic(intrinsic) + assert_eq!(calls.len(), 3, "Expected 3 calls, but found: {calls:?}"); + for (fn_def, args) in calls.into_iter() { + check_instance(&Instance::resolve(fn_def, &args).unwrap()); + check_def(fn_def); } ControlFlow::Continue(()) @@ -53,22 +55,44 @@ fn test_intrinsics() -> ControlFlow<()> { /// /// If by any chance this test breaks because you changed how an intrinsic is implemented, please /// update the test to invoke a different intrinsic. -fn check_intrinsic(intrinsic: &Instance) { - assert_eq!(intrinsic.kind, InstanceKind::Intrinsic); - let name = intrinsic.intrinsic_name().unwrap(); - if intrinsic.has_body() { - let Some(body) = intrinsic.body() else { unreachable!("Expected a body") }; +/// +/// In StableMIR, we only expose intrinsic body if they are not marked with +/// `rustc_intrinsic_must_be_overridden`. +fn check_instance(instance: &Instance) { + assert_eq!(instance.kind, InstanceKind::Intrinsic); + let name = instance.intrinsic_name().unwrap(); + if instance.has_body() { + let Some(body) = instance.body() else { unreachable!("Expected a body") }; assert!(!body.blocks.is_empty()); assert_eq!(&name, "likely"); } else { - assert!(intrinsic.body().is_none()); - assert_eq!(&name, "size_of_val"); + assert!(instance.body().is_none()); + assert_matches!(name.as_str(), "size_of_val" | "vtable_size"); + } +} + +fn check_def(fn_def: FnDef) { + assert!(fn_def.is_intrinsic()); + let intrinsic = fn_def.as_intrinsic().unwrap(); + assert_eq!(fn_def, intrinsic.into()); + + let name = intrinsic.fn_name(); + match name.as_str() { + "likely" => { + assert!(!intrinsic.must_be_overridden()); + assert!(fn_def.has_body()); + } + "vtable_size" | "size_of_val" => { + assert!(intrinsic.must_be_overridden()); + assert!(!fn_def.has_body()); + } + _ => unreachable!("Unexpected intrinsic: {}", name), } } struct CallsVisitor<'a> { locals: &'a [LocalDecl], - calls: HashSet, + calls: Vec<(FnDef, GenericArgs)>, } impl<'a> MirVisitor for CallsVisitor<'a> { @@ -80,7 +104,7 @@ impl<'a> MirVisitor for CallsVisitor<'a> { else { return; }; - self.calls.insert(Instance::resolve(def, &args).unwrap()); + self.calls.push((def, args.clone())); } _ => {} } @@ -106,6 +130,7 @@ fn generate_input(path: &str) -> std::io::Result<()> { #![feature(core_intrinsics)] use std::intrinsics::*; pub fn use_intrinsics(init: bool) -> bool {{ + let vtable_sz = unsafe {{ vtable_size(0 as *const ()) }}; let sz = unsafe {{ size_of_val("hi") }}; likely(init && sz == 2) }} diff --git a/tests/ui-fulldeps/stable-mir/check_transform.rs b/tests/ui-fulldeps/stable-mir/check_transform.rs index 6345ee24f789..1d3e4c6845ba 100644 --- a/tests/ui-fulldeps/stable-mir/check_transform.rs +++ b/tests/ui-fulldeps/stable-mir/check_transform.rs @@ -21,8 +21,8 @@ extern crate stable_mir; use rustc_smir::rustc_internal; use stable_mir::mir::alloc::GlobalAlloc; use stable_mir::mir::mono::Instance; -use stable_mir::mir::{Body, Constant, Operand, Rvalue, StatementKind, TerminatorKind}; -use stable_mir::ty::{Const, ConstantKind}; +use stable_mir::mir::{Body, ConstOperand, Operand, Rvalue, StatementKind, TerminatorKind}; +use stable_mir::ty::{ConstantKind, MirConst}; use stable_mir::{CrateDef, CrateItems, ItemKind}; use std::convert::TryFrom; use std::io::Write; @@ -72,12 +72,12 @@ fn check_msg(body: &Body, expected: &str) { .unwrap() } }; - let ConstantKind::Allocated(alloc) = msg_const.literal.kind() else { + let ConstantKind::Allocated(alloc) = msg_const.const_.kind() else { unreachable!() }; assert_eq!(alloc.provenance.ptrs.len(), 1); - let alloc_prov_id = alloc.provenance.ptrs[0].1 .0; + let alloc_prov_id = alloc.provenance.ptrs[0].1.0; let GlobalAlloc::Memory(val) = GlobalAlloc::from(alloc_prov_id) else { unreachable!() }; @@ -95,9 +95,9 @@ fn change_panic_msg(mut body: Body, new_msg: &str) -> Body { for bb in &mut body.blocks { match &mut bb.terminator.kind { TerminatorKind::Call { args, .. } => { - let new_const = Const::from_str(new_msg); - args[0] = Operand::Constant(Constant { - literal: new_const, + let new_const = MirConst::from_str(new_msg); + args[0] = Operand::Constant(ConstOperand { + const_: new_const, span: bb.terminator.span, user_ty: None, }); diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs index 373d1cce1d73..1be9ada28227 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -52,17 +52,12 @@ //@ revisions: m68k //@[m68k] compile-flags: --target m68k-unknown-linux-gnu //@[m68k] needs-llvm-components: m68k -// FIXME: disabled on nvptx64 since the target ABI fails the sanity check -// see https://github.com/rust-lang/rust/issues/117480 -/* revisions: nvptx64 - [nvptx64] compile-flags: --target nvptx64-nvidia-cuda - [nvptx64] needs-llvm-components: nvptx -*/ -// FIXME: disabled since it fails on CI saying the csky component is missing -/* revisions: csky - [csky] compile-flags: --target csky-unknown-linux-gnuabiv2 - [csky] needs-llvm-components: csky -*/ +//@ revisions: csky +//@[csky] compile-flags: --target csky-unknown-linux-gnuabiv2 +//@[csky] needs-llvm-components: csky +//@ revisions: nvptx64 +//@[nvptx64] compile-flags: --target nvptx64-nvidia-cuda +//@[nvptx64] needs-llvm-components: nvptx #![feature(rustc_attrs, unsized_fn_params, transparent_unions)] #![cfg_attr(not(host), feature(no_core, lang_items), no_std, no_core)] #![allow(unused, improper_ctypes_definitions, internal_features)] @@ -74,8 +69,7 @@ #[cfg(host)] use std::{ - any::Any, marker::PhantomData, mem::ManuallyDrop, num::NonZero, ptr::NonNull, rc::Rc, - sync::Arc, + any::Any, marker::PhantomData, mem::ManuallyDrop, num::NonZero, ptr::NonNull, rc::Rc, sync::Arc, }; /// To work cross-target this test must be no_core. diff --git a/tests/ui/abi/fixed_x18.rs b/tests/ui/abi/fixed_x18.rs new file mode 100644 index 000000000000..f1ff3e1d5341 --- /dev/null +++ b/tests/ui/abi/fixed_x18.rs @@ -0,0 +1,25 @@ +// This tests that -Zfixed-x18 causes a compilation failure on targets other than aarch64. +// Behavior on aarch64 is tested by tests/codegen/fixed-x18.rs. +// +//@ revisions: x64 i686 arm riscv32 riscv64 +//@ error-pattern: the `-Zfixed-x18` flag is not supported +//@ dont-check-compiler-stderr +// +//@ compile-flags: -Zfixed-x18 +//@ [x64] needs-llvm-components: x86 +//@ [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib +//@ [i686] needs-llvm-components: x86 +//@ [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib +//@ [arm] needs-llvm-components: arm +//@ [arm] compile-flags: --target=armv7-unknown-linux-gnueabihf --crate-type=rlib +//@ [riscv32] needs-llvm-components: riscv +//@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf --crate-type=rlib +//@ [riscv64] needs-llvm-components: riscv +//@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf --crate-type=rlib + +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +trait Sized {} diff --git a/tests/ui/abi/stack-probes-lto.rs b/tests/ui/abi/stack-probes-lto.rs index 5451b72d9792..70343b0599ac 100644 --- a/tests/ui/abi/stack-probes-lto.rs +++ b/tests/ui/abi/stack-probes-lto.rs @@ -10,5 +10,9 @@ //@ compile-flags: -C lto //@ no-prefer-dynamic //@ ignore-nto Crash analysis impossible at SIGSEGV in QNX Neutrino +//@ ignore-ios Stack probes are enabled, but the SIGSEGV handler isn't +//@ ignore-tvos Stack probes are enabled, but the SIGSEGV handler isn't +//@ ignore-watchos Stack probes are enabled, but the SIGSEGV handler isn't +//@ ignore-visionos Stack probes are enabled, but the SIGSEGV handler isn't include!("stack-probes.rs"); diff --git a/tests/ui/abi/stack-probes.rs b/tests/ui/abi/stack-probes.rs index 32d4d6cd31e6..22304257593f 100644 --- a/tests/ui/abi/stack-probes.rs +++ b/tests/ui/abi/stack-probes.rs @@ -8,6 +8,10 @@ //@ ignore-sgx no processes //@ ignore-fuchsia no exception handler registered for segfault //@ ignore-nto Crash analysis impossible at SIGSEGV in QNX Neutrino +//@ ignore-ios Stack probes are enabled, but the SIGSEGV handler isn't +//@ ignore-tvos Stack probes are enabled, but the SIGSEGV handler isn't +//@ ignore-watchos Stack probes are enabled, but the SIGSEGV handler isn't +//@ ignore-visionos Stack probes are enabled, but the SIGSEGV handler isn't use std::env; use std::mem::MaybeUninit; diff --git a/tests/ui/allow-non-lint-warnings.rs b/tests/ui/allow-non-lint-warnings.rs new file mode 100644 index 000000000000..f8f5a78ebff2 --- /dev/null +++ b/tests/ui/allow-non-lint-warnings.rs @@ -0,0 +1,8 @@ +//@ compile-flags: -Awarnings +//@ check-pass + +#[derive()] +#[derive(Copy, Clone)] +pub struct Foo; + +pub fn main() {} diff --git a/tests/ui/empty_global_asm.rs b/tests/ui/asm/empty_global_asm.rs similarity index 100% rename from tests/ui/empty_global_asm.rs rename to tests/ui/asm/empty_global_asm.rs diff --git a/tests/ui/simple_global_asm.rs b/tests/ui/asm/simple_global_asm.rs similarity index 100% rename from tests/ui/simple_global_asm.rs rename to tests/ui/asm/simple_global_asm.rs diff --git a/tests/ui/asm/x86_64/type-check-3.stderr b/tests/ui/asm/x86_64/type-check-3.stderr index 34bfcd71caca..202b97ca5c0e 100644 --- a/tests/ui/asm/x86_64/type-check-3.stderr +++ b/tests/ui/asm/x86_64/type-check-3.stderr @@ -4,7 +4,7 @@ error: type `i128` cannot be used with this register class LL | asm!("{}", in(reg) 0i128); | ^^^^^ | - = note: register class `reg` supports these types: i16, i32, i64, f32, f64 + = note: register class `reg` supports these types: i16, i32, i64, f16, f32, f64 error: type `__m128` cannot be used with this register class --> $DIR/type-check-3.rs:16:28 @@ -12,7 +12,7 @@ error: type `__m128` cannot be used with this register class LL | asm!("{}", in(reg) _mm_setzero_ps()); | ^^^^^^^^^^^^^^^^ | - = note: register class `reg` supports these types: i16, i32, i64, f32, f64 + = note: register class `reg` supports these types: i16, i32, i64, f16, f32, f64 error: type `__m256` cannot be used with this register class --> $DIR/type-check-3.rs:18:28 @@ -20,7 +20,7 @@ error: type `__m256` cannot be used with this register class LL | asm!("{}", in(reg) _mm256_setzero_ps()); | ^^^^^^^^^^^^^^^^^^^ | - = note: register class `reg` supports these types: i16, i32, i64, f32, f64 + = note: register class `reg` supports these types: i16, i32, i64, f16, f32, f64 error: type `u8` cannot be used with this register class --> $DIR/type-check-3.rs:20:32 @@ -28,7 +28,7 @@ error: type `u8` cannot be used with this register class LL | asm!("{}", in(xmm_reg) 0u8); | ^^^ | - = note: register class `xmm_reg` supports these types: i32, i64, f32, f64, i8x16, i16x8, i32x4, i64x2, f32x4, f64x2 + = note: register class `xmm_reg` supports these types: i32, i64, f16, f32, f64, f128, i8x16, i16x8, i32x4, i64x2, f16x8, f32x4, f64x2 error: `avx512bw` target feature is not enabled --> $DIR/type-check-3.rs:29:29 @@ -81,7 +81,7 @@ error: type `i8` cannot be used with this register class LL | asm!("{}", in(reg) 0i8); | ^^^ | - = note: register class `reg` supports these types: i16, i32, i64, f32, f64 + = note: register class `reg` supports these types: i16, i32, i64, f16, f32, f64 = help: consider using the `reg_byte` register class instead error: incompatible types for asm inout argument diff --git a/tests/ui/associated-consts/double-elided.rs b/tests/ui/associated-consts/double-elided.rs index fd0317781bb1..6831b599374a 100644 --- a/tests/ui/associated-consts/double-elided.rs +++ b/tests/ui/associated-consts/double-elided.rs @@ -1,12 +1,10 @@ +//@ check-pass + struct S; impl S { const C: &&str = &""; - //~^ WARN `&` without an explicit lifetime name cannot be used here - //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - //~| WARN `&` without an explicit lifetime name cannot be used here - //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - //~| ERROR in type `&&str`, reference has a longer lifetime than the data it references + // Now resolves to `&'static &'static str`. } fn main() {} diff --git a/tests/ui/associated-consts/double-elided.stderr b/tests/ui/associated-consts/double-elided.stderr deleted file mode 100644 index 67834788ccd4..000000000000 --- a/tests/ui/associated-consts/double-elided.stderr +++ /dev/null @@ -1,47 +0,0 @@ -warning: `&` without an explicit lifetime name cannot be used here - --> $DIR/double-elided.rs:4:14 - | -LL | const C: &&str = &""; - | ^ - | - = warning: 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 #115010 - = note: `#[warn(elided_lifetimes_in_associated_constant)]` on by default -help: use the `'static` lifetime - | -LL | const C: &'static &str = &""; - | +++++++ - -warning: `&` without an explicit lifetime name cannot be used here - --> $DIR/double-elided.rs:4:15 - | -LL | const C: &&str = &""; - | ^ - | - = warning: 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 #115010 -help: use the `'static` lifetime - | -LL | const C: &&'static str = &""; - | +++++++ - -error[E0491]: in type `&&str`, reference has a longer lifetime than the data it references - --> $DIR/double-elided.rs:4:5 - | -LL | const C: &&str = &""; - | ^^^^^^^^^^^^^^^^^^^^^ - | -note: the pointer is valid for the anonymous lifetime as defined here - --> $DIR/double-elided.rs:4:14 - | -LL | const C: &&str = &""; - | ^ -note: but the referenced data is only valid for the anonymous lifetime as defined here - --> $DIR/double-elided.rs:4:14 - | -LL | const C: &&str = &""; - | ^ - -error: aborting due to 1 previous error; 2 warnings emitted - -For more information about this error, try `rustc --explain E0491`. diff --git a/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.rs b/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.rs index 1e0b77b0d3bd..40896c32e111 100644 --- a/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.rs +++ b/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.rs @@ -5,8 +5,6 @@ trait Trait { impl Trait for () { const ASSOC: &dyn Fn(_) = 1i32; //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated constants - //~| WARN `&` without an explicit lifetime name cannot be used here - //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } fn main() {} diff --git a/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.stderr b/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.stderr index e946491a0848..6b8ba9af7a07 100644 --- a/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.stderr +++ b/tests/ui/associated-consts/infer-placeholder-in-non-suggestable-pos.stderr @@ -1,23 +1,9 @@ -warning: `&` without an explicit lifetime name cannot be used here - --> $DIR/infer-placeholder-in-non-suggestable-pos.rs:6:18 - | -LL | const ASSOC: &dyn Fn(_) = 1i32; - | ^ - | - = warning: 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 #115010 - = note: `#[warn(elided_lifetimes_in_associated_constant)]` on by default -help: use the `'static` lifetime - | -LL | const ASSOC: &'static dyn Fn(_) = 1i32; - | +++++++ - error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants --> $DIR/infer-placeholder-in-non-suggestable-pos.rs:6:26 | LL | const ASSOC: &dyn Fn(_) = 1i32; | ^ not allowed in type signatures -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0121`. diff --git a/tests/ui/associated-consts/issue-102335-const.rs b/tests/ui/associated-consts/issue-102335-const.rs index 969c2c43b716..fd922cd0f1d8 100644 --- a/tests/ui/associated-consts/issue-102335-const.rs +++ b/tests/ui/associated-consts/issue-102335-const.rs @@ -2,8 +2,8 @@ trait T { type A: S = 34>; - //~^ ERROR associated type bindings are not allowed here - //~| ERROR associated type bindings are not allowed here + //~^ ERROR associated item constraints are not allowed here + //~| ERROR associated item constraints are not allowed here } trait S { diff --git a/tests/ui/associated-consts/issue-102335-const.stderr b/tests/ui/associated-consts/issue-102335-const.stderr index 905d7c75c200..dc1631220e2a 100644 --- a/tests/ui/associated-consts/issue-102335-const.stderr +++ b/tests/ui/associated-consts/issue-102335-const.stderr @@ -1,22 +1,22 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-102335-const.rs:4:17 | LL | type A: S = 34>; - | ^^^^^^^^ associated type not allowed here + | ^^^^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | LL | type A: S = 34>; | ~~~~~~~~~~ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-102335-const.rs:4:17 | LL | type A: S = 34>; - | ^^^^^^^^ associated type not allowed here + | ^^^^^^^^ associated item constraint not allowed here | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider removing this type binding +help: consider removing this associated item binding | LL | type A: S = 34>; | ~~~~~~~~~~ diff --git a/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg.rs b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg.rs new file mode 100644 index 000000000000..e2fc2961a448 --- /dev/null +++ b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg.rs @@ -0,0 +1,10 @@ +struct Fail; +//~^ ERROR: type parameter `T` is never used + +impl Fail { + const C: () = (); +} + +fn main() { + Fail::<()>::C +} diff --git a/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg.stderr b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg.stderr new file mode 100644 index 000000000000..f0a6ccf243a9 --- /dev/null +++ b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg.stderr @@ -0,0 +1,12 @@ +error[E0392]: type parameter `T` is never used + --> $DIR/wrong-projection-self-ty-invalid-bivariant-arg.rs:1:13 + | +LL | struct Fail; + | ^ unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0392`. diff --git a/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.rs b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.rs new file mode 100644 index 000000000000..cb53d902ba18 --- /dev/null +++ b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.rs @@ -0,0 +1,17 @@ +trait Proj { + type Assoc; +} +impl Proj for T { + type Assoc = T; +} + +struct Fail, U>(T); + +impl Fail { + const C: () = (); +} + +fn main() { + Fail::::C + //~^ ERROR: type mismatch +} diff --git a/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.stderr b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.stderr new file mode 100644 index 000000000000..0d63b7f5b293 --- /dev/null +++ b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.stderr @@ -0,0 +1,20 @@ +error[E0271]: type mismatch resolving `::Assoc == u32` + --> $DIR/wrong-projection-self-ty-invalid-bivariant-arg2.rs:15:5 + | +LL | Fail::::C + | ^^^^^^^^^^^^^^^^ type mismatch resolving `::Assoc == u32` + | +note: expected this to be `u32` + --> $DIR/wrong-projection-self-ty-invalid-bivariant-arg2.rs:5:18 + | +LL | type Assoc = T; + | ^ +note: required by a bound in `Fail` + --> $DIR/wrong-projection-self-ty-invalid-bivariant-arg2.rs:8:21 + | +LL | struct Fail, U>(T); + | ^^^^^^^^^ required by this bound in `Fail` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/associated-inherent-types/assoc-inherent-private.stderr b/tests/ui/associated-inherent-types/assoc-inherent-private.stderr index d67b45dae3fb..e3802a9f5bc4 100644 --- a/tests/ui/associated-inherent-types/assoc-inherent-private.stderr +++ b/tests/ui/associated-inherent-types/assoc-inherent-private.stderr @@ -2,7 +2,7 @@ error[E0624]: associated type `P` is private --> $DIR/assoc-inherent-private.rs:10:10 | LL | type P = (); - | ------ associated type defined here + | ------ the associated type is defined here ... LL | type U = m::T::P; | ^^^^^^^ private associated type @@ -11,7 +11,7 @@ error[E0624]: associated type `P` is private --> $DIR/assoc-inherent-private.rs:21:10 | LL | pub(super) type P = bool; - | ----------------- associated type defined here + | ----------------- the associated type is defined here ... LL | type V = n::n::T::P; | ^^^^^^^^^^ private associated type diff --git a/tests/ui/associated-inherent-types/issue-109071.rs b/tests/ui/associated-inherent-types/issue-109071.rs index 29eef081a327..97803ae7191e 100644 --- a/tests/ui/associated-inherent-types/issue-109071.rs +++ b/tests/ui/associated-inherent-types/issue-109071.rs @@ -13,7 +13,7 @@ impl Windows { //~ ERROR: missing generics for struct `Windows` impl Windows { fn T() -> Option {} - //~^ ERROR: ambiguous associated type + //[no_gate]~^ ERROR: ambiguous associated type } fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-109071.with_gate.stderr b/tests/ui/associated-inherent-types/issue-109071.with_gate.stderr index a7d17e2d5ebb..1324cb9bb9b5 100644 --- a/tests/ui/associated-inherent-types/issue-109071.with_gate.stderr +++ b/tests/ui/associated-inherent-types/issue-109071.with_gate.stderr @@ -20,20 +20,7 @@ help: add missing generic argument LL | impl Windows { | +++ -error[E0223]: ambiguous associated type - --> $DIR/issue-109071.rs:15:22 - | -LL | fn T() -> Option {} - | ^^^^^^^^^^ - | -help: use fully-qualified syntax - | -LL | fn T() -> Option< as IntoAsyncIterator>::Item> {} - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | fn T() -> Option< as IntoIterator>::Item> {} - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +error: aborting due to 2 previous errors -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0107, E0223, E0637. +Some errors have detailed explanations: E0107, E0637. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/associated-inherent-types/issue-109299.rs b/tests/ui/associated-inherent-types/issue-109299.rs index b6c010c34e44..84e4f9e72527 100644 --- a/tests/ui/associated-inherent-types/issue-109299.rs +++ b/tests/ui/associated-inherent-types/issue-109299.rs @@ -8,6 +8,5 @@ impl Lexer<'d> { //~ ERROR use of undeclared lifetime name `'d` } fn test(_: Lexer::Cursor) {} -//~^ ERROR: lifetime may not live long enough fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-109299.stderr b/tests/ui/associated-inherent-types/issue-109299.stderr index f108a52b92c0..1e11c0e8c2af 100644 --- a/tests/ui/associated-inherent-types/issue-109299.stderr +++ b/tests/ui/associated-inherent-types/issue-109299.stderr @@ -6,15 +6,6 @@ LL | impl Lexer<'d> { | | | help: consider introducing lifetime `'d` here: `<'d>` -error: lifetime may not live long enough - --> $DIR/issue-109299.rs:10:1 - | -LL | fn test(_: Lexer::Cursor) {} - | ^^^^^^^^-^^^^^^^^^^^^^^^^ - | | | - | | has type `Lexer<'1>::Cursor` - | requires that `'1` must outlive `'static` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0261`. diff --git a/tests/ui/associated-type-bounds/issue-102335-ty.rs b/tests/ui/associated-type-bounds/issue-102335-ty.rs index b2df68b18ae4..86bde6367a4c 100644 --- a/tests/ui/associated-type-bounds/issue-102335-ty.rs +++ b/tests/ui/associated-type-bounds/issue-102335-ty.rs @@ -1,13 +1,13 @@ trait T { type A: S = ()>; // Just one erroneous equality constraint - //~^ ERROR associated type bindings are not allowed here - //~| ERROR associated type bindings are not allowed here + //~^ ERROR associated item constraints are not allowed here + //~| ERROR associated item constraints are not allowed here } trait T2 { type A: S = ()>; // More than one erroneous equality constraints - //~^ ERROR associated type bindings are not allowed here - //~| ERROR associated type bindings are not allowed here + //~^ ERROR associated item constraints are not allowed here + //~| ERROR associated item constraints are not allowed here } trait Q {} diff --git a/tests/ui/associated-type-bounds/issue-102335-ty.stderr b/tests/ui/associated-type-bounds/issue-102335-ty.stderr index cf30b0a4f6ca..cd585f7f7d8f 100644 --- a/tests/ui/associated-type-bounds/issue-102335-ty.stderr +++ b/tests/ui/associated-type-bounds/issue-102335-ty.stderr @@ -1,45 +1,45 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-102335-ty.rs:2:17 | LL | type A: S = ()>; // Just one erroneous equality constraint - | ^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | LL | type A: S = ()>; // Just one erroneous equality constraint | ~~~~~~~~~~~ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-102335-ty.rs:2:17 | LL | type A: S = ()>; // Just one erroneous equality constraint - | ^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^ associated item constraint not allowed here | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider removing this type binding +help: consider removing this associated item binding | LL | type A: S = ()>; // Just one erroneous equality constraint | ~~~~~~~~~~~ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-102335-ty.rs:8:17 | LL | type A: S = ()>; // More than one erroneous equality constraints - | ^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | LL | type A: S = ()>; // More than one erroneous equality constraints | ~~~~~~~~~~ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-102335-ty.rs:8:17 | LL | type A: S = ()>; // More than one erroneous equality constraints - | ^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^ associated item constraint not allowed here | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider removing this type binding +help: consider removing this associated item binding | LL | type A: S = ()>; // More than one erroneous equality constraints | ~~~~~~~~~~ diff --git a/tests/ui/associated-type-bounds/no-gat-position.rs b/tests/ui/associated-type-bounds/no-gat-position.rs index 5005c5027f42..ec9214897fa8 100644 --- a/tests/ui/associated-type-bounds/no-gat-position.rs +++ b/tests/ui/associated-type-bounds/no-gat-position.rs @@ -4,7 +4,8 @@ pub trait Iter { type Item<'a>: 'a where Self: 'a; fn next<'a>(&'a mut self) -> Option>; - //~^ ERROR associated type bindings are not allowed here + //~^ ERROR associated item constraints are not allowed here + //~| HELP consider removing this associated item constraint } impl Iter for () { diff --git a/tests/ui/associated-type-bounds/no-gat-position.stderr b/tests/ui/associated-type-bounds/no-gat-position.stderr index c348d33c3a9a..39a2f89f9acf 100644 --- a/tests/ui/associated-type-bounds/no-gat-position.stderr +++ b/tests/ui/associated-type-bounds/no-gat-position.stderr @@ -1,8 +1,13 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/no-gat-position.rs:6:56 | LL | fn next<'a>(&'a mut self) -> Option>; - | ^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^ associated item constraint not allowed here + | +help: consider removing this associated item constraint + | +LL | fn next<'a>(&'a mut self) -> Option>; + | ~~~~~~~~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/associated-types/associated-types-eq-2.rs b/tests/ui/associated-types/associated-types-eq-2.rs index d71697e9a832..88eb29810610 100644 --- a/tests/ui/associated-types/associated-types-eq-2.rs +++ b/tests/ui/associated-types/associated-types-eq-2.rs @@ -4,7 +4,7 @@ struct Bar; struct Qux; -// Tests for a a non generic trait +// Tests for a non generic trait pub trait Tr1 { type A; fn boo(&self) -> ::A; @@ -18,14 +18,14 @@ impl Tr1 for isize { // Test for when the assoc type is // specified as an equality constraint impl Tr1 for usize { -//~^ ERROR associated type bindings are not allowed here +//~^ ERROR associated item constraints are not allowed here //~| ERROR not all trait items implemented, missing: `A` fn boo(&self) -> usize { 42 } } // Test for a wronngly used equality constraint in a func arg fn baz(_x: &>::A) {} -//~^ ERROR associated type bindings are not allowed here +//~^ ERROR associated item constraints are not allowed here @@ -38,28 +38,28 @@ trait Tr2 { // (Note: E0229 is emitted only for the first erroneous equality // constraint (T2) not for any subequent ones (e.g. T3)) impl Tr2 for Bar { -//~^ ERROR associated type bindings are not allowed here +//~^ ERROR associated item constraints are not allowed here //~| ERROR trait takes 3 generic arguments but 1 generic argument was supplied } // Test for when equality constraint's ident matches a // generic param's ident but has different case impl Tr2 for Qux { -//~^ ERROR associated type bindings are not allowed here +//~^ ERROR associated item constraints are not allowed here //~| ERROR trait takes 3 generic arguments but 1 generic argument was supplied } // Test for when equality constraint's ident // matches none of the generic param idents impl Tr2 for Bar { -//~^ ERROR associated type bindings are not allowed here +//~^ ERROR associated item constraints are not allowed here //~| ERROR trait takes 3 generic arguments but 1 generic argument was supplied } // Test for when the term in equality constraint is itself generic struct GenericTerm { _t: T } impl Tr2> for Bar { -//~^ ERROR associated type bindings are not allowed here +//~^ ERROR associated item constraints are not allowed here //~| ERROR trait takes 3 generic arguments but 2 generic arguments were supplied } @@ -74,16 +74,15 @@ trait Tr3 { // (Deliberately spread over multiple lines to test that // our suggestion spans are kosher in the face of such formatting) impl Tr3 for Bar { } // Test for when equality constraint's ident // matches the const param's ident but has a different case impl Tr3 for Qux { -//~^ ERROR associated type bindings are not allowed here +//~^ ERROR associated item constraints are not allowed here //~| ERROR associated const equality is incomplete //~| ERROR trait takes 3 generic arguments but 0 generic arguments were supplied } @@ -91,14 +90,13 @@ impl Tr3 for Qux { // Test for when equality constraint's ident // matches the const param ident but the constraint is a type arg impl Tr3 for Bar { -//~^ ERROR associated type bindings are not allowed here -//~| ERROR trait takes 3 generic arguments but 0 generic arguments were supplied +//~^ ERROR associated item constraints are not allowed here } // Test for when equality constraint's ident // matches a type param ident but the constraint is a const arg impl Tr3<42, T2 = 42, T3 = usize> for Bar { -//~^ ERROR associated type bindings are not allowed here +//~^ ERROR associated item constraints are not allowed here //~| ERROR associated const equality is incomplete //~| ERROR trait takes 3 generic arguments but 1 generic argument was supplied } @@ -106,7 +104,7 @@ impl Tr3<42, T2 = 42, T3 = usize> for Bar { // Test for when equality constraint's ident // matches none of the param idents impl Tr3 for Bar { -//~^ ERROR associated type bindings are not allowed here +//~^ ERROR associated item constraints are not allowed here //~| ERROR associated const equality is incomplete //~| ERROR trait takes 3 generic arguments but 0 generic arguments were supplied } @@ -117,7 +115,7 @@ impl Tr3 for Bar { struct St<'a, T> { v: &'a T } impl<'a, T> St<'a , T = Qux> { -//~^ ERROR associated type bindings are not allowed here +//~^ ERROR associated item constraints are not allowed here //~| ERROR struct takes 1 generic argument but 0 generic arguments were supplied } diff --git a/tests/ui/associated-types/associated-types-eq-2.stderr b/tests/ui/associated-types/associated-types-eq-2.stderr index b68c82f590c1..69b1b533450c 100644 --- a/tests/ui/associated-types/associated-types-eq-2.stderr +++ b/tests/ui/associated-types/associated-types-eq-2.stderr @@ -5,7 +5,6 @@ LL | impl Tr3 for Bar { | |____^ | @@ -14,7 +13,7 @@ LL | | = 42, T2 = Qux, T3 = usize> for Bar { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: associated const equality is incomplete - --> $DIR/associated-types-eq-2.rs:85:10 + --> $DIR/associated-types-eq-2.rs:84:10 | LL | impl Tr3 for Qux { | ^^^^^^ @@ -24,7 +23,7 @@ LL | impl Tr3 for Qux { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: associated const equality is incomplete - --> $DIR/associated-types-eq-2.rs:100:14 + --> $DIR/associated-types-eq-2.rs:98:14 | LL | impl Tr3<42, T2 = 42, T3 = usize> for Bar { | ^^^^^^^ @@ -34,7 +33,7 @@ LL | impl Tr3<42, T2 = 42, T3 = usize> for Bar { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: associated const equality is incomplete - --> $DIR/associated-types-eq-2.rs:108:10 + --> $DIR/associated-types-eq-2.rs:106:10 | LL | impl Tr3 for Bar { | ^^^^^^ @@ -43,13 +42,13 @@ LL | impl Tr3 for Bar { = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/associated-types-eq-2.rs:20:10 | LL | impl Tr1 for usize { - | ^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | LL | impl Tr1 for usize { | ~~~~~~~~~~~ @@ -63,13 +62,13 @@ LL | type A; LL | impl Tr1 for usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `A` in implementation -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/associated-types-eq-2.rs:27:31 | LL | fn baz(_x: &>::A) {} - | ^^^^^ associated type not allowed here + | ^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | LL | fn baz(_x: &>::A) {} | ~~~~~~~ @@ -92,11 +91,11 @@ help: add missing generic arguments LL | impl Tr2 for Bar { | ++++++++ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/associated-types-eq-2.rs:40:15 | LL | impl Tr2 for Bar { - | ^^^^^^^^ associated type not allowed here + | ^^^^^^^^ associated item constraint not allowed here | help: to use `Qux` as a generic argument specify it directly | @@ -121,13 +120,13 @@ help: add missing generic arguments LL | impl Tr2 for Qux { | ++++++++ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/associated-types-eq-2.rs:47:15 | LL | impl Tr2 for Qux { - | ^^^^^^^^ associated type not allowed here + | ^^^^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | LL | impl Tr2 for Qux { | ~~~~~~~~~~ @@ -150,13 +149,13 @@ help: add missing generic arguments LL | impl Tr2 for Bar { | ++++++++ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/associated-types-eq-2.rs:54:15 | LL | impl Tr2 for Bar { - | ^^^^^^^ associated type not allowed here + | ^^^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | LL | impl Tr2 for Bar { | ~~~~~~~~~ @@ -179,43 +178,26 @@ help: add missing generic argument LL | impl Tr2> for Bar { | ++++ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/associated-types-eq-2.rs:61:20 | LL | impl Tr2> for Bar { - | ^^^^^^^^^^^^^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^^^^^^^^^^^^^ associated item constraint not allowed here | help: to use `GenericTerm` as a generic argument specify it directly | LL | impl Tr2> for Bar { | ~~~~~~~~~~~~~~~~ -error[E0107]: trait takes 3 generic arguments but 0 generic arguments were supplied - --> $DIR/associated-types-eq-2.rs:76:6 - | -LL | impl Tr3 $DIR/associated-types-eq-2.rs:69:7 - | -LL | trait Tr3 { - | ^^^ ------------ -- -- -help: add missing generic arguments - | -LL | impl Tr3 $DIR/associated-types-eq-2.rs:76:10 | LL | impl Tr3 for Bar { - | |____^ associated type not allowed here + | |____^ associated item constraint not allowed here | help: to use `42` as a generic argument specify it directly | @@ -223,7 +205,7 @@ LL | impl Tr3<42, T2 = Qux, T3 = usize> for Bar { | ~~ error[E0107]: trait takes 3 generic arguments but 0 generic arguments were supplied - --> $DIR/associated-types-eq-2.rs:85:6 + --> $DIR/associated-types-eq-2.rs:84:6 | LL | impl Tr3 for Qux { | ^^^ expected 3 generic arguments @@ -238,46 +220,30 @@ help: add missing generic arguments LL | impl Tr3 for Qux { | ++++++++++ -error[E0229]: associated type bindings are not allowed here - --> $DIR/associated-types-eq-2.rs:85:10 +error[E0229]: associated item constraints are not allowed here + --> $DIR/associated-types-eq-2.rs:84:10 | LL | impl Tr3 for Qux { - | ^^^^^^ associated type not allowed here + | ^^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | LL | impl Tr3 for Qux { | ~~~~~~~ -error[E0107]: trait takes 3 generic arguments but 0 generic arguments were supplied - --> $DIR/associated-types-eq-2.rs:93:6 +error[E0229]: associated item constraints are not allowed here + --> $DIR/associated-types-eq-2.rs:92:10 | LL | impl Tr3 for Bar { - | ^^^ expected 3 generic arguments + | ^^^^^^^ associated item constraint not allowed here | -note: trait defined here, with 3 generic parameters: `N`, `T2`, `T3` - --> $DIR/associated-types-eq-2.rs:69:7 - | -LL | trait Tr3 { - | ^^^ ------------ -- -- -help: add missing generic arguments - | -LL | impl Tr3 for Bar { - | ++++++++++ - -error[E0229]: associated type bindings are not allowed here - --> $DIR/associated-types-eq-2.rs:93:10 - | -LL | impl Tr3 for Bar { - | ^^^^^^^ associated type not allowed here - | -help: consider removing this type binding +help: consider removing this associated item binding | LL | impl Tr3 for Bar { | ~~~~~~~~ error[E0107]: trait takes 3 generic arguments but 1 generic argument was supplied - --> $DIR/associated-types-eq-2.rs:100:6 + --> $DIR/associated-types-eq-2.rs:98:6 | LL | impl Tr3<42, T2 = 42, T3 = usize> for Bar { | ^^^ -- supplied 1 generic argument @@ -294,19 +260,19 @@ help: add missing generic arguments LL | impl Tr3<42, T2, T3, T2 = 42, T3 = usize> for Bar { | ++++++++ -error[E0229]: associated type bindings are not allowed here - --> $DIR/associated-types-eq-2.rs:100:14 +error[E0229]: associated item constraints are not allowed here + --> $DIR/associated-types-eq-2.rs:98:14 | LL | impl Tr3<42, T2 = 42, T3 = usize> for Bar { - | ^^^^^^^ associated type not allowed here + | ^^^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | LL | impl Tr3<42, T2 = 42, T3 = usize> for Bar { | ~~~~~~~~~ error[E0107]: trait takes 3 generic arguments but 0 generic arguments were supplied - --> $DIR/associated-types-eq-2.rs:108:6 + --> $DIR/associated-types-eq-2.rs:106:6 | LL | impl Tr3 for Bar { | ^^^ expected 3 generic arguments @@ -321,25 +287,25 @@ help: add missing generic arguments LL | impl Tr3 for Bar { | ++++++++++ -error[E0229]: associated type bindings are not allowed here - --> $DIR/associated-types-eq-2.rs:108:10 +error[E0229]: associated item constraints are not allowed here + --> $DIR/associated-types-eq-2.rs:106:10 | LL | impl Tr3 for Bar { - | ^^^^^^ associated type not allowed here + | ^^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | LL | impl Tr3 for Bar { | ~~~~~~~ error[E0107]: struct takes 1 generic argument but 0 generic arguments were supplied - --> $DIR/associated-types-eq-2.rs:119:13 + --> $DIR/associated-types-eq-2.rs:117:13 | LL | impl<'a, T> St<'a , T = Qux> { | ^^ expected 1 generic argument | note: struct defined here, with 1 generic parameter: `T` - --> $DIR/associated-types-eq-2.rs:117:8 + --> $DIR/associated-types-eq-2.rs:115:8 | LL | struct St<'a, T> { v: &'a T } | ^^ - @@ -348,18 +314,18 @@ help: add missing generic argument LL | impl<'a, T> St<'a, T , T = Qux> { | +++ -error[E0229]: associated type bindings are not allowed here - --> $DIR/associated-types-eq-2.rs:119:21 +error[E0229]: associated item constraints are not allowed here + --> $DIR/associated-types-eq-2.rs:117:21 | LL | impl<'a, T> St<'a , T = Qux> { - | ^^^^^^^ associated type not allowed here + | ^^^^^^^ associated item constraint not allowed here | help: to use `Qux` as a generic argument specify it directly | LL | impl<'a, T> St<'a , Qux> { | ~~~ -error: aborting due to 27 previous errors +error: aborting due to 25 previous errors Some errors have detailed explanations: E0046, E0107, E0229, E0658. For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/associated-types/associated-types-eq-expr-path.rs b/tests/ui/associated-types/associated-types-eq-expr-path.rs index 143992f29f19..4561e596c665 100644 --- a/tests/ui/associated-types/associated-types-eq-expr-path.rs +++ b/tests/ui/associated-types/associated-types-eq-expr-path.rs @@ -11,6 +11,6 @@ impl Foo for isize { } pub fn main() { - let x: isize = Foo::::bar(); - //~^ ERROR associated type bindings are not allowed here + let x: isize = Foo::::bar(); + //~^ ERROR associated item constraints are not allowed here } diff --git a/tests/ui/associated-types/associated-types-eq-expr-path.stderr b/tests/ui/associated-types/associated-types-eq-expr-path.stderr index 7559f3b7c2e5..3d0e3e61eca9 100644 --- a/tests/ui/associated-types/associated-types-eq-expr-path.stderr +++ b/tests/ui/associated-types/associated-types-eq-expr-path.stderr @@ -1,8 +1,8 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/associated-types-eq-expr-path.rs:14:26 | -LL | let x: isize = Foo::::bar(); - | ^^^^^^^ associated type not allowed here +LL | let x: isize = Foo::::bar(); + | ^^^^^^^^^ associated item constraint not allowed here error: aborting due to 1 previous error diff --git a/tests/ui/associated-types/defaults-suitability.stderr b/tests/ui/associated-types/defaults-suitability.current.stderr similarity index 88% rename from tests/ui/associated-types/defaults-suitability.stderr rename to tests/ui/associated-types/defaults-suitability.current.stderr index 82b35a486378..3cdeaa93a340 100644 --- a/tests/ui/associated-types/defaults-suitability.stderr +++ b/tests/ui/associated-types/defaults-suitability.current.stderr @@ -1,11 +1,11 @@ error[E0277]: the trait bound `NotClone: Clone` is not satisfied - --> $DIR/defaults-suitability.rs:13:22 + --> $DIR/defaults-suitability.rs:16:22 | LL | type Ty: Clone = NotClone; | ^^^^^^^^ the trait `Clone` is not implemented for `NotClone` | note: required by a bound in `Tr::Ty` - --> $DIR/defaults-suitability.rs:13:14 + --> $DIR/defaults-suitability.rs:16:14 | LL | type Ty: Clone = NotClone; | ^^^^^ required by this bound in `Tr::Ty` @@ -16,13 +16,13 @@ LL | struct NotClone; | error[E0277]: the trait bound `NotClone: Clone` is not satisfied - --> $DIR/defaults-suitability.rs:22:15 + --> $DIR/defaults-suitability.rs:25:15 | LL | type Ty = NotClone; | ^^^^^^^^ the trait `Clone` is not implemented for `NotClone` | note: required by a bound in `Tr2::Ty` - --> $DIR/defaults-suitability.rs:20:15 + --> $DIR/defaults-suitability.rs:23:15 | LL | Self::Ty: Clone, | ^^^^^ required by this bound in `Tr2::Ty` @@ -36,14 +36,14 @@ LL | struct NotClone; | error[E0277]: the trait bound `T: Clone` is not satisfied - --> $DIR/defaults-suitability.rs:28:23 + --> $DIR/defaults-suitability.rs:31:23 | LL | type Bar: Clone = Vec; | ^^^^^^ the trait `Clone` is not implemented for `T`, which is required by `Vec: Clone` | = note: required for `Vec` to implement `Clone` note: required by a bound in `Foo::Bar` - --> $DIR/defaults-suitability.rs:28:15 + --> $DIR/defaults-suitability.rs:31:15 | LL | type Bar: Clone = Vec; | ^^^^^ required by this bound in `Foo::Bar` @@ -53,30 +53,30 @@ LL | trait Foo { | +++++++++++++++++++ error[E0277]: the trait bound `(): Foo` is not satisfied - --> $DIR/defaults-suitability.rs:34:29 + --> $DIR/defaults-suitability.rs:37:29 | LL | type Assoc: Foo = (); | ^^ the trait `Foo` is not implemented for `()` | help: this trait has no implementations, consider adding one - --> $DIR/defaults-suitability.rs:27:1 + --> $DIR/defaults-suitability.rs:30:1 | LL | trait Foo { | ^^^^^^^^^^^^ note: required by a bound in `Bar::Assoc` - --> $DIR/defaults-suitability.rs:34:17 + --> $DIR/defaults-suitability.rs:37:17 | LL | type Assoc: Foo = (); | ^^^^^^^^^ required by this bound in `Bar::Assoc` error[E0277]: the trait bound `NotClone: IsU8` is not satisfied - --> $DIR/defaults-suitability.rs:56:18 + --> $DIR/defaults-suitability.rs:59:18 | LL | type Assoc = NotClone; | ^^^^^^^^ the trait `IsU8` is not implemented for `NotClone` | note: required by a bound in `D::Assoc` - --> $DIR/defaults-suitability.rs:53:18 + --> $DIR/defaults-suitability.rs:56:18 | LL | Self::Assoc: IsU8, | ^^^^^^^^^^^^^^^^^ required by this bound in `D::Assoc` @@ -85,14 +85,14 @@ LL | type Assoc = NotClone; | ----- required by a bound in this associated type error[E0277]: the trait bound `>::Baz: Clone` is not satisfied - --> $DIR/defaults-suitability.rs:65:23 + --> $DIR/defaults-suitability.rs:68:23 | LL | type Bar: Clone = Vec; | ^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `>::Baz`, which is required by `Vec<>::Baz>: Clone` | = note: required for `Vec<>::Baz>` to implement `Clone` note: required by a bound in `Foo2::Bar` - --> $DIR/defaults-suitability.rs:65:15 + --> $DIR/defaults-suitability.rs:68:15 | LL | type Bar: Clone = Vec; | ^^^^^ required by this bound in `Foo2::Bar` @@ -102,14 +102,14 @@ LL | trait Foo2 where >::Baz: Clone { | +++++++++++++++++++++++++++++++++++ error[E0277]: the trait bound `>::Baz: Clone` is not satisfied - --> $DIR/defaults-suitability.rs:74:23 + --> $DIR/defaults-suitability.rs:77:23 | LL | type Bar: Clone = Vec; | ^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `>::Baz`, which is required by `Vec<>::Baz>: Clone` | = note: required for `Vec<>::Baz>` to implement `Clone` note: required by a bound in `Foo25::Bar` - --> $DIR/defaults-suitability.rs:74:15 + --> $DIR/defaults-suitability.rs:77:15 | LL | type Bar: Clone = Vec; | ^^^^^ required by this bound in `Foo25::Bar` @@ -119,13 +119,13 @@ LL | trait Foo25 where >::Baz: Clone { | ++++++++++++++++++++++++++++++++++++ error[E0277]: the trait bound `T: Clone` is not satisfied - --> $DIR/defaults-suitability.rs:87:16 + --> $DIR/defaults-suitability.rs:90:16 | LL | type Baz = T; | ^ the trait `Clone` is not implemented for `T` | note: required by a bound in `Foo3::Baz` - --> $DIR/defaults-suitability.rs:84:16 + --> $DIR/defaults-suitability.rs:87:16 | LL | Self::Baz: Clone, | ^^^^^ required by this bound in `Foo3::Baz` diff --git a/tests/ui/associated-types/defaults-suitability.next.stderr b/tests/ui/associated-types/defaults-suitability.next.stderr new file mode 100644 index 000000000000..3cdeaa93a340 --- /dev/null +++ b/tests/ui/associated-types/defaults-suitability.next.stderr @@ -0,0 +1,142 @@ +error[E0277]: the trait bound `NotClone: Clone` is not satisfied + --> $DIR/defaults-suitability.rs:16:22 + | +LL | type Ty: Clone = NotClone; + | ^^^^^^^^ the trait `Clone` is not implemented for `NotClone` + | +note: required by a bound in `Tr::Ty` + --> $DIR/defaults-suitability.rs:16:14 + | +LL | type Ty: Clone = NotClone; + | ^^^^^ required by this bound in `Tr::Ty` +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClone; + | + +error[E0277]: the trait bound `NotClone: Clone` is not satisfied + --> $DIR/defaults-suitability.rs:25:15 + | +LL | type Ty = NotClone; + | ^^^^^^^^ the trait `Clone` is not implemented for `NotClone` + | +note: required by a bound in `Tr2::Ty` + --> $DIR/defaults-suitability.rs:23:15 + | +LL | Self::Ty: Clone, + | ^^^^^ required by this bound in `Tr2::Ty` +LL | { +LL | type Ty = NotClone; + | -- required by a bound in this associated type +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClone; + | + +error[E0277]: the trait bound `T: Clone` is not satisfied + --> $DIR/defaults-suitability.rs:31:23 + | +LL | type Bar: Clone = Vec; + | ^^^^^^ the trait `Clone` is not implemented for `T`, which is required by `Vec: Clone` + | + = note: required for `Vec` to implement `Clone` +note: required by a bound in `Foo::Bar` + --> $DIR/defaults-suitability.rs:31:15 + | +LL | type Bar: Clone = Vec; + | ^^^^^ required by this bound in `Foo::Bar` +help: consider restricting type parameter `T` + | +LL | trait Foo { + | +++++++++++++++++++ + +error[E0277]: the trait bound `(): Foo` is not satisfied + --> $DIR/defaults-suitability.rs:37:29 + | +LL | type Assoc: Foo = (); + | ^^ the trait `Foo` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/defaults-suitability.rs:30:1 + | +LL | trait Foo { + | ^^^^^^^^^^^^ +note: required by a bound in `Bar::Assoc` + --> $DIR/defaults-suitability.rs:37:17 + | +LL | type Assoc: Foo = (); + | ^^^^^^^^^ required by this bound in `Bar::Assoc` + +error[E0277]: the trait bound `NotClone: IsU8` is not satisfied + --> $DIR/defaults-suitability.rs:59:18 + | +LL | type Assoc = NotClone; + | ^^^^^^^^ the trait `IsU8` is not implemented for `NotClone` + | +note: required by a bound in `D::Assoc` + --> $DIR/defaults-suitability.rs:56:18 + | +LL | Self::Assoc: IsU8, + | ^^^^^^^^^^^^^^^^^ required by this bound in `D::Assoc` +... +LL | type Assoc = NotClone; + | ----- required by a bound in this associated type + +error[E0277]: the trait bound `>::Baz: Clone` is not satisfied + --> $DIR/defaults-suitability.rs:68:23 + | +LL | type Bar: Clone = Vec; + | ^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `>::Baz`, which is required by `Vec<>::Baz>: Clone` + | + = note: required for `Vec<>::Baz>` to implement `Clone` +note: required by a bound in `Foo2::Bar` + --> $DIR/defaults-suitability.rs:68:15 + | +LL | type Bar: Clone = Vec; + | ^^^^^ required by this bound in `Foo2::Bar` +help: consider further restricting the associated type + | +LL | trait Foo2 where >::Baz: Clone { + | +++++++++++++++++++++++++++++++++++ + +error[E0277]: the trait bound `>::Baz: Clone` is not satisfied + --> $DIR/defaults-suitability.rs:77:23 + | +LL | type Bar: Clone = Vec; + | ^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `>::Baz`, which is required by `Vec<>::Baz>: Clone` + | + = note: required for `Vec<>::Baz>` to implement `Clone` +note: required by a bound in `Foo25::Bar` + --> $DIR/defaults-suitability.rs:77:15 + | +LL | type Bar: Clone = Vec; + | ^^^^^ required by this bound in `Foo25::Bar` +help: consider further restricting the associated type + | +LL | trait Foo25 where >::Baz: Clone { + | ++++++++++++++++++++++++++++++++++++ + +error[E0277]: the trait bound `T: Clone` is not satisfied + --> $DIR/defaults-suitability.rs:90:16 + | +LL | type Baz = T; + | ^ the trait `Clone` is not implemented for `T` + | +note: required by a bound in `Foo3::Baz` + --> $DIR/defaults-suitability.rs:87:16 + | +LL | Self::Baz: Clone, + | ^^^^^ required by this bound in `Foo3::Baz` +... +LL | type Baz = T; + | --- required by a bound in this associated type +help: consider further restricting type parameter `T` + | +LL | Self::Baz: Clone, T: std::clone::Clone + | ~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-types/defaults-suitability.rs b/tests/ui/associated-types/defaults-suitability.rs index 504c957d9875..bab2f004ac75 100644 --- a/tests/ui/associated-types/defaults-suitability.rs +++ b/tests/ui/associated-types/defaults-suitability.rs @@ -1,3 +1,6 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver //! Checks that associated type defaults are properly validated. //! //! This means: diff --git a/tests/ui/associated-types/defaults-unsound-62211-1.current.stderr b/tests/ui/associated-types/defaults-unsound-62211-1.current.stderr index 9d52e923aded..8b6f0a47aed9 100644 --- a/tests/ui/associated-types/defaults-unsound-62211-1.current.stderr +++ b/tests/ui/associated-types/defaults-unsound-62211-1.current.stderr @@ -1,12 +1,12 @@ error[E0277]: `Self` doesn't implement `std::fmt::Display` - --> $DIR/defaults-unsound-62211-1.rs:26:96 + --> $DIR/defaults-unsound-62211-1.rs:24:96 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^ `Self` cannot be formatted with the default formatter | = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead note: required by a bound in `UncheckedCopy::Output` - --> $DIR/defaults-unsound-62211-1.rs:26:86 + --> $DIR/defaults-unsound-62211-1.rs:24:86 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^^^^ required by this bound in `UncheckedCopy::Output` @@ -16,13 +16,13 @@ LL | trait UncheckedCopy: Sized + std::fmt::Display { | +++++++++++++++++++ error[E0277]: cannot add-assign `&'static str` to `Self` - --> $DIR/defaults-unsound-62211-1.rs:26:96 + --> $DIR/defaults-unsound-62211-1.rs:24:96 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^ no implementation for `Self += &'static str` | note: required by a bound in `UncheckedCopy::Output` - --> $DIR/defaults-unsound-62211-1.rs:26:47 + --> $DIR/defaults-unsound-62211-1.rs:24:47 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `UncheckedCopy::Output` @@ -32,13 +32,13 @@ LL | trait UncheckedCopy: Sized + AddAssign<&'static str> { | +++++++++++++++++++++++++ error[E0277]: the trait bound `Self: Deref` is not satisfied - --> $DIR/defaults-unsound-62211-1.rs:26:96 + --> $DIR/defaults-unsound-62211-1.rs:24:96 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^ the trait `Deref` is not implemented for `Self` | note: required by a bound in `UncheckedCopy::Output` - --> $DIR/defaults-unsound-62211-1.rs:26:25 + --> $DIR/defaults-unsound-62211-1.rs:24:25 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^^^^^^^^^^^^^^^^ required by this bound in `UncheckedCopy::Output` @@ -48,13 +48,13 @@ LL | trait UncheckedCopy: Sized + Deref { | +++++++ error[E0277]: the trait bound `Self: Copy` is not satisfied - --> $DIR/defaults-unsound-62211-1.rs:26:96 + --> $DIR/defaults-unsound-62211-1.rs:24:96 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^ the trait `Copy` is not implemented for `Self` | note: required by a bound in `UncheckedCopy::Output` - --> $DIR/defaults-unsound-62211-1.rs:26:18 + --> $DIR/defaults-unsound-62211-1.rs:24:18 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^ required by this bound in `UncheckedCopy::Output` diff --git a/tests/ui/associated-types/defaults-unsound-62211-1.next.stderr b/tests/ui/associated-types/defaults-unsound-62211-1.next.stderr index 834ae00a8d85..010f51df15ad 100644 --- a/tests/ui/associated-types/defaults-unsound-62211-1.next.stderr +++ b/tests/ui/associated-types/defaults-unsound-62211-1.next.stderr @@ -1,13 +1,68 @@ -warning: calls to `std::mem::drop` with a value that implements `Copy` does nothing - --> $DIR/defaults-unsound-62211-1.rs:52:5 +error[E0277]: `Self` doesn't implement `std::fmt::Display` + --> $DIR/defaults-unsound-62211-1.rs:24:96 | -LL | drop(origin); - | ^^^^^------^ - | | - | argument has type `::Output` +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^ `Self` cannot be formatted with the default formatter | - = note: use `let _ = ...` to ignore the expression or result - = note: `#[warn(dropping_copy_types)]` on by default + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `UncheckedCopy::Output` + --> $DIR/defaults-unsound-62211-1.rs:24:86 + | +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^^^^ required by this bound in `UncheckedCopy::Output` +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + std::fmt::Display { + | +++++++++++++++++++ -warning: 1 warning emitted +error[E0277]: cannot add-assign `&'static str` to `Self` + --> $DIR/defaults-unsound-62211-1.rs:24:96 + | +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^ no implementation for `Self += &'static str` + | +note: required by a bound in `UncheckedCopy::Output` + --> $DIR/defaults-unsound-62211-1.rs:24:47 + | +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `UncheckedCopy::Output` +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + AddAssign<&'static str> { + | +++++++++++++++++++++++++ +error[E0277]: the trait bound `Self: Deref` is not satisfied + --> $DIR/defaults-unsound-62211-1.rs:24:96 + | +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^ the trait `Deref` is not implemented for `Self` + | +note: required by a bound in `UncheckedCopy::Output` + --> $DIR/defaults-unsound-62211-1.rs:24:31 + | +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^^^^^^^^^ required by this bound in `UncheckedCopy::Output` +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + Deref { + | +++++++ + +error[E0277]: the trait bound `Self: Copy` is not satisfied + --> $DIR/defaults-unsound-62211-1.rs:24:96 + | +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^ the trait `Copy` is not implemented for `Self` + | +note: required by a bound in `UncheckedCopy::Output` + --> $DIR/defaults-unsound-62211-1.rs:24:18 + | +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^ required by this bound in `UncheckedCopy::Output` +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + Copy { + | ++++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-types/defaults-unsound-62211-1.rs b/tests/ui/associated-types/defaults-unsound-62211-1.rs index d9cf5aa97acd..124bac0ea721 100644 --- a/tests/ui/associated-types/defaults-unsound-62211-1.rs +++ b/tests/ui/associated-types/defaults-unsound-62211-1.rs @@ -1,8 +1,6 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver //@ ignore-compare-mode-next-solver (explicit revisions) -//@[next] known-bug: rust-lang/trait-system-refactor-initiative#46 -//@[next] check-pass //! Regression test for https://github.com/rust-lang/rust/issues/62211 //! @@ -24,10 +22,10 @@ trait UncheckedCopy: Sized { // This Output is said to be Copy. Yet we default to Self // and it's accepted, not knowing if Self ineed is Copy type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; - //[current]~^ ERROR the trait bound `Self: Copy` is not satisfied - //[current]~| ERROR the trait bound `Self: Deref` is not satisfied - //[current]~| ERROR cannot add-assign `&'static str` to `Self` - //[current]~| ERROR `Self` doesn't implement `std::fmt::Display` + //~^ ERROR the trait bound `Self: Copy` is not satisfied + //~| ERROR the trait bound `Self: Deref` is not satisfied + //~| ERROR cannot add-assign `&'static str` to `Self` + //~| ERROR `Self` doesn't implement `std::fmt::Display` // We said the Output type was Copy, so we can Copy it freely! fn unchecked_copy(other: &Self::Output) -> Self::Output { diff --git a/tests/ui/associated-types/defaults-unsound-62211-2.current.stderr b/tests/ui/associated-types/defaults-unsound-62211-2.current.stderr index 4fd2ca6408a1..7552b0891333 100644 --- a/tests/ui/associated-types/defaults-unsound-62211-2.current.stderr +++ b/tests/ui/associated-types/defaults-unsound-62211-2.current.stderr @@ -1,12 +1,12 @@ error[E0277]: `Self` doesn't implement `std::fmt::Display` - --> $DIR/defaults-unsound-62211-2.rs:26:96 + --> $DIR/defaults-unsound-62211-2.rs:24:96 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^ `Self` cannot be formatted with the default formatter | = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead note: required by a bound in `UncheckedCopy::Output` - --> $DIR/defaults-unsound-62211-2.rs:26:86 + --> $DIR/defaults-unsound-62211-2.rs:24:86 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^^^^ required by this bound in `UncheckedCopy::Output` @@ -16,13 +16,13 @@ LL | trait UncheckedCopy: Sized + std::fmt::Display { | +++++++++++++++++++ error[E0277]: cannot add-assign `&'static str` to `Self` - --> $DIR/defaults-unsound-62211-2.rs:26:96 + --> $DIR/defaults-unsound-62211-2.rs:24:96 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^ no implementation for `Self += &'static str` | note: required by a bound in `UncheckedCopy::Output` - --> $DIR/defaults-unsound-62211-2.rs:26:47 + --> $DIR/defaults-unsound-62211-2.rs:24:47 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `UncheckedCopy::Output` @@ -32,13 +32,13 @@ LL | trait UncheckedCopy: Sized + AddAssign<&'static str> { | +++++++++++++++++++++++++ error[E0277]: the trait bound `Self: Deref` is not satisfied - --> $DIR/defaults-unsound-62211-2.rs:26:96 + --> $DIR/defaults-unsound-62211-2.rs:24:96 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^ the trait `Deref` is not implemented for `Self` | note: required by a bound in `UncheckedCopy::Output` - --> $DIR/defaults-unsound-62211-2.rs:26:25 + --> $DIR/defaults-unsound-62211-2.rs:24:25 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^^^^^^^^^^^^^^^^ required by this bound in `UncheckedCopy::Output` @@ -48,13 +48,13 @@ LL | trait UncheckedCopy: Sized + Deref { | +++++++ error[E0277]: the trait bound `Self: Copy` is not satisfied - --> $DIR/defaults-unsound-62211-2.rs:26:96 + --> $DIR/defaults-unsound-62211-2.rs:24:96 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^ the trait `Copy` is not implemented for `Self` | note: required by a bound in `UncheckedCopy::Output` - --> $DIR/defaults-unsound-62211-2.rs:26:18 + --> $DIR/defaults-unsound-62211-2.rs:24:18 | LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; | ^^^^ required by this bound in `UncheckedCopy::Output` diff --git a/tests/ui/associated-types/defaults-unsound-62211-2.next.stderr b/tests/ui/associated-types/defaults-unsound-62211-2.next.stderr index 0f944a18ed58..934789465707 100644 --- a/tests/ui/associated-types/defaults-unsound-62211-2.next.stderr +++ b/tests/ui/associated-types/defaults-unsound-62211-2.next.stderr @@ -1,13 +1,68 @@ -warning: calls to `std::mem::drop` with a value that implements `Copy` does nothing - --> $DIR/defaults-unsound-62211-2.rs:52:5 +error[E0277]: `Self` doesn't implement `std::fmt::Display` + --> $DIR/defaults-unsound-62211-2.rs:24:96 | -LL | drop(origin); - | ^^^^^------^ - | | - | argument has type `::Output` +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^ `Self` cannot be formatted with the default formatter | - = note: use `let _ = ...` to ignore the expression or result - = note: `#[warn(dropping_copy_types)]` on by default + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +note: required by a bound in `UncheckedCopy::Output` + --> $DIR/defaults-unsound-62211-2.rs:24:86 + | +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^^^^ required by this bound in `UncheckedCopy::Output` +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + std::fmt::Display { + | +++++++++++++++++++ -warning: 1 warning emitted +error[E0277]: cannot add-assign `&'static str` to `Self` + --> $DIR/defaults-unsound-62211-2.rs:24:96 + | +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^ no implementation for `Self += &'static str` + | +note: required by a bound in `UncheckedCopy::Output` + --> $DIR/defaults-unsound-62211-2.rs:24:47 + | +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `UncheckedCopy::Output` +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + AddAssign<&'static str> { + | +++++++++++++++++++++++++ +error[E0277]: the trait bound `Self: Deref` is not satisfied + --> $DIR/defaults-unsound-62211-2.rs:24:96 + | +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^ the trait `Deref` is not implemented for `Self` + | +note: required by a bound in `UncheckedCopy::Output` + --> $DIR/defaults-unsound-62211-2.rs:24:31 + | +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^^^^^^^^^ required by this bound in `UncheckedCopy::Output` +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + Deref { + | +++++++ + +error[E0277]: the trait bound `Self: Copy` is not satisfied + --> $DIR/defaults-unsound-62211-2.rs:24:96 + | +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^ the trait `Copy` is not implemented for `Self` + | +note: required by a bound in `UncheckedCopy::Output` + --> $DIR/defaults-unsound-62211-2.rs:24:18 + | +LL | type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; + | ^^^^ required by this bound in `UncheckedCopy::Output` +help: consider further restricting `Self` + | +LL | trait UncheckedCopy: Sized + Copy { + | ++++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-types/defaults-unsound-62211-2.rs b/tests/ui/associated-types/defaults-unsound-62211-2.rs index 6cbac1bf2364..998c39b5bad3 100644 --- a/tests/ui/associated-types/defaults-unsound-62211-2.rs +++ b/tests/ui/associated-types/defaults-unsound-62211-2.rs @@ -1,8 +1,6 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver //@ ignore-compare-mode-next-solver (explicit revisions) -//@[next] known-bug: rust-lang/trait-system-refactor-initiative#46 -//@[next] check-pass //! Regression test for https://github.com/rust-lang/rust/issues/62211 //! @@ -24,10 +22,10 @@ trait UncheckedCopy: Sized { // This Output is said to be Copy. Yet we default to Self // and it's accepted, not knowing if Self ineed is Copy type Output: Copy + Deref + AddAssign<&'static str> + From + Display = Self; - //[current]~^ ERROR the trait bound `Self: Copy` is not satisfied - //[current]~| ERROR the trait bound `Self: Deref` is not satisfied - //[current]~| ERROR cannot add-assign `&'static str` to `Self` - //[current]~| ERROR `Self` doesn't implement `std::fmt::Display` + //~^ ERROR the trait bound `Self: Copy` is not satisfied + //~| ERROR the trait bound `Self: Deref` is not satisfied + //~| ERROR cannot add-assign `&'static str` to `Self` + //~| ERROR `Self` doesn't implement `std::fmt::Display` // We said the Output type was Copy, so we can Copy it freely! fn unchecked_copy(other: &Self::Output) -> Self::Output { diff --git a/tests/ui/associated-types/issue-54108.stderr b/tests/ui/associated-types/issue-54108.current.stderr similarity index 93% rename from tests/ui/associated-types/issue-54108.stderr rename to tests/ui/associated-types/issue-54108.current.stderr index f300208fcc87..8850b4548e33 100644 --- a/tests/ui/associated-types/issue-54108.stderr +++ b/tests/ui/associated-types/issue-54108.current.stderr @@ -1,12 +1,12 @@ error[E0277]: cannot add `::ActualSize` to `::ActualSize` - --> $DIR/issue-54108.rs:19:17 + --> $DIR/issue-54108.rs:23:17 | LL | type Size = ::ActualSize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `::ActualSize + ::ActualSize` | = help: the trait `Add` is not implemented for `::ActualSize` note: required by a bound in `Encoder::Size` - --> $DIR/issue-54108.rs:4:16 + --> $DIR/issue-54108.rs:8:16 | LL | type Size: Add; | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Encoder::Size` diff --git a/tests/ui/associated-types/issue-54108.next.stderr b/tests/ui/associated-types/issue-54108.next.stderr new file mode 100644 index 000000000000..5e2fa551afe3 --- /dev/null +++ b/tests/ui/associated-types/issue-54108.next.stderr @@ -0,0 +1,20 @@ +error[E0277]: cannot add `::ActualSize` to `::ActualSize` + --> $DIR/issue-54108.rs:23:17 + | +LL | type Size = ::ActualSize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `::ActualSize + ::ActualSize` + | + = help: the trait `Add` is not implemented for `::ActualSize` +note: required by a bound in `Encoder::Size` + --> $DIR/issue-54108.rs:8:20 + | +LL | type Size: Add; + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `Encoder::Size` +help: consider further restricting the associated type + | +LL | T: SubEncoder, ::ActualSize: Add + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-types/issue-54108.rs b/tests/ui/associated-types/issue-54108.rs index 87f67ce4b527..bcc54f333621 100644 --- a/tests/ui/associated-types/issue-54108.rs +++ b/tests/ui/associated-types/issue-54108.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + use std::ops::Add; pub trait Encoder { diff --git a/tests/ui/associated-types/issue-63593.stderr b/tests/ui/associated-types/issue-63593.current.stderr similarity index 89% rename from tests/ui/associated-types/issue-63593.stderr rename to tests/ui/associated-types/issue-63593.current.stderr index 67151431a67a..76fdefeb4e52 100644 --- a/tests/ui/associated-types/issue-63593.stderr +++ b/tests/ui/associated-types/issue-63593.current.stderr @@ -1,11 +1,11 @@ error[E0277]: the size for values of type `Self` cannot be known at compilation time - --> $DIR/issue-63593.rs:9:17 + --> $DIR/issue-63593.rs:13:17 | LL | type This = Self; | ^^^^ doesn't have a size known at compile-time | note: required by a bound in `MyTrait::This` - --> $DIR/issue-63593.rs:9:5 + --> $DIR/issue-63593.rs:13:5 | LL | type This = Self; | ^^^^^^^^^^^^^^^^^ required by this bound in `MyTrait::This` diff --git a/tests/ui/associated-types/issue-63593.next.stderr b/tests/ui/associated-types/issue-63593.next.stderr new file mode 100644 index 000000000000..76fdefeb4e52 --- /dev/null +++ b/tests/ui/associated-types/issue-63593.next.stderr @@ -0,0 +1,19 @@ +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/issue-63593.rs:13:17 + | +LL | type This = Self; + | ^^^^ doesn't have a size known at compile-time + | +note: required by a bound in `MyTrait::This` + --> $DIR/issue-63593.rs:13:5 + | +LL | type This = Self; + | ^^^^^^^^^^^^^^^^^ required by this bound in `MyTrait::This` +help: consider further restricting `Self` + | +LL | trait MyTrait: Sized { + | +++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-types/issue-63593.rs b/tests/ui/associated-types/issue-63593.rs index 8dbc24c06732..dea81f729b4e 100644 --- a/tests/ui/associated-types/issue-63593.rs +++ b/tests/ui/associated-types/issue-63593.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + #![feature(associated_type_defaults)] // Tests that `Self` is not assumed to implement `Sized` when used as an diff --git a/tests/ui/async-await/async-closures/force-move-due-to-actually-fnonce.rs b/tests/ui/async-await/async-closures/force-move-due-to-actually-fnonce.rs new file mode 100644 index 000000000000..ce49f55e3e30 --- /dev/null +++ b/tests/ui/async-await/async-closures/force-move-due-to-actually-fnonce.rs @@ -0,0 +1,27 @@ +//@ aux-build:block-on.rs +//@ edition:2021 +//@ check-pass + +#![feature(async_closure)] + +extern crate block_on; + +fn consume(_: String) {} + +fn main() { + block_on::block_on(async { + let x = 1i32; + let s = String::new(); + // `consume(s)` pulls the closure's kind down to `FnOnce`, + // which means that we don't treat the borrow of `x` as a + // self-borrow (with `'env` lifetime). This leads to a lifetime + // error which is solved by forcing the inner coroutine to + // be `move` as well, so that it moves `x`. + let c = async move || { + println!("{x}"); + // This makes the closure FnOnce... + consume(s); + }; + c().await; + }); +} diff --git a/tests/ui/async-await/async-closures/force-move-due-to-inferred-kind.rs b/tests/ui/async-await/async-closures/force-move-due-to-inferred-kind.rs new file mode 100644 index 000000000000..803c990ef93f --- /dev/null +++ b/tests/ui/async-await/async-closures/force-move-due-to-inferred-kind.rs @@ -0,0 +1,24 @@ +//@ aux-build:block-on.rs +//@ edition:2021 +//@ check-pass + +#![feature(async_closure)] + +extern crate block_on; + +fn force_fnonce(t: T) -> T { t } + +fn main() { + block_on::block_on(async { + let x = 1i32; + // `force_fnonce` pulls the closure's kind down to `FnOnce`, + // which means that we don't treat the borrow of `x` as a + // self-borrow (with `'env` lifetime). This leads to a lifetime + // error which is solved by forcing the inner coroutine to + // be `move` as well, so that it moves `x`. + let c = force_fnonce(async move || { + println!("{x}"); + }); + c().await; + }); +} diff --git a/tests/ui/async-await/async-closures/implements-fnmut.rs b/tests/ui/async-await/async-closures/implements-fnmut.rs new file mode 100644 index 000000000000..1ed326cd0618 --- /dev/null +++ b/tests/ui/async-await/async-closures/implements-fnmut.rs @@ -0,0 +1,23 @@ +//@ check-pass +//@ edition: 2021 + +// Demonstrates that an async closure may implement `FnMut` (not just `async FnMut`!) +// if it has no self-borrows. In this case, `&Ty` is not borrowed from the closure env, +// since it's fine to reborrow it with its original lifetime. See the doc comment on +// `should_reborrow_from_env_of_parent_coroutine_closure` for more detail for when we +// must borrow from the closure env. + +#![feature(async_closure)] + +fn main() {} + +fn needs_fn_mut(x: impl FnMut() -> T) {} + +fn hello(x: &Ty) { + needs_fn_mut(async || { x.hello(); }); +} + +struct Ty; +impl Ty { + fn hello(&self) {} +} diff --git a/tests/ui/async-await/async-closures/wrong-fn-kind.stderr b/tests/ui/async-await/async-closures/wrong-fn-kind.stderr index e56389b32027..6f07a6feed35 100644 --- a/tests/ui/async-await/async-closures/wrong-fn-kind.stderr +++ b/tests/ui/async-await/async-closures/wrong-fn-kind.stderr @@ -20,15 +20,16 @@ LL | fn needs_async_fn(_: impl async Fn()) {} | ^^^^^^^^^^ required by this bound in `needs_async_fn` error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure - --> $DIR/wrong-fn-kind.rs:9:29 + --> $DIR/wrong-fn-kind.rs:9:20 | LL | fn needs_async_fn(_: impl async Fn()) {} | --------------- change this to accept `FnMut` instead of `Fn` ... LL | needs_async_fn(async || { - | _____--------------_--------_^ - | | | | - | | | in this closure + | -------------- ^------- + | | | + | _____|______________in this closure + | | | | | expects `Fn` instead of `FnMut` LL | | LL | | x += 1; diff --git a/tests/ui/async-await/async-drop.rs b/tests/ui/async-await/async-drop.rs index 6d02dcebc0b6..12f120a0b121 100644 --- a/tests/ui/async-await/async-drop.rs +++ b/tests/ui/async-await/async-drop.rs @@ -1,6 +1,10 @@ //@ run-pass //@ check-run-results +// WARNING: If you would ever want to modify this test, +// please consider modifying miri's async drop test at +// `src/tools/miri/tests/pass/async-drop.rs`. + #![feature(async_drop, impl_trait_in_assoc_type, noop_waker, async_closure)] #![allow(incomplete_features, dead_code)] @@ -13,9 +17,21 @@ use core::mem::{self, ManuallyDrop}; use core::pin::{pin, Pin}; use core::task::{Context, Poll, Waker}; -async fn test_async_drop(x: T) { +async fn test_async_drop(x: T, _size: usize) { let mut x = mem::MaybeUninit::new(x); let dtor = pin!(unsafe { async_drop_in_place(x.as_mut_ptr()) }); + + // FIXME(zetanumbers): This check fully depends on the layout of + // the coroutine state, since async destructor combinators are just + // async functions. + #[cfg(target_pointer_width = "64")] + assert_eq!( + mem::size_of_val(&*dtor), + _size, + "sizes did not match for async destructor of type {}", + core::any::type_name::(), + ); + test_idempotency(dtor).await; } @@ -36,51 +52,58 @@ fn main() { let i = 13; let fut = pin!(async { - test_async_drop(Int(0)).await; - test_async_drop(AsyncInt(0)).await; - test_async_drop([AsyncInt(1), AsyncInt(2)]).await; - test_async_drop((AsyncInt(3), AsyncInt(4))).await; - test_async_drop(5).await; + test_async_drop(Int(0), 0).await; + test_async_drop(AsyncInt(0), 104).await; + test_async_drop([AsyncInt(1), AsyncInt(2)], 152).await; + test_async_drop((AsyncInt(3), AsyncInt(4)), 488).await; + test_async_drop(5, 0).await; let j = 42; - test_async_drop(&i).await; - test_async_drop(&j).await; - test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }).await; - test_async_drop(ManuallyDrop::new(AsyncInt(9))).await; + test_async_drop(&i, 0).await; + test_async_drop(&j, 0).await; + test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }, 1688).await; + test_async_drop(ManuallyDrop::new(AsyncInt(9)), 0).await; let foo = AsyncInt(10); - test_async_drop(AsyncReference { foo: &foo }).await; + test_async_drop(AsyncReference { foo: &foo }, 104).await; let foo = AsyncInt(11); - test_async_drop(|| { - black_box(foo); - let foo = AsyncInt(10); - foo - }).await; + test_async_drop( + || { + black_box(foo); + let foo = AsyncInt(10); + foo + }, + 120, + ) + .await; - test_async_drop(AsyncEnum::A(AsyncInt(12))).await; - test_async_drop(AsyncEnum::B(SyncInt(13))).await; + test_async_drop(AsyncEnum::A(AsyncInt(12)), 680).await; + test_async_drop(AsyncEnum::B(SyncInt(13)), 680).await; - test_async_drop(SyncInt(14)).await; - test_async_drop(SyncThenAsync { - i: 15, - a: AsyncInt(16), - b: SyncInt(17), - c: AsyncInt(18), - }).await; + test_async_drop(SyncInt(14), 16).await; + test_async_drop( + SyncThenAsync { i: 15, a: AsyncInt(16), b: SyncInt(17), c: AsyncInt(18) }, + 3064, + ) + .await; let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19))); test_idempotency(async_drop_fut).await; let foo = AsyncInt(20); - test_async_drop(async || { - black_box(foo); - let foo = AsyncInt(19); - // Await point there, but this is async closure so it's fine - black_box(core::future::ready(())).await; - foo - }).await; + test_async_drop( + async || { + black_box(foo); + let foo = AsyncInt(19); + // Await point there, but this is async closure so it's fine + black_box(core::future::ready(())).await; + foo + }, + 120, + ) + .await; - test_async_drop(AsyncUnion { signed: 21 }).await; + test_async_drop(AsyncUnion { signed: 21 }, 32).await; }); let res = fut.poll(&mut cx); assert_eq!(res, Poll::Ready(())); diff --git a/tests/ui/async-await/async-fn/impl-header.stderr b/tests/ui/async-await/async-fn/impl-header.stderr index 2fb862af04e4..64a98aab17b2 100644 --- a/tests/ui/async-await/async-fn/impl-header.stderr +++ b/tests/ui/async-await/async-fn/impl-header.stderr @@ -28,7 +28,7 @@ error[E0277]: expected a `FnMut()` closure, found `F` LL | impl async Fn<()> for F {} | ^ expected an `FnMut()` closure, found `F` | - = help: the trait `FnMut<()>` is not implemented for `F` + = help: the trait `FnMut()` is not implemented for `F` = note: wrap the `F` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `Fn` --> $SRC_DIR/core/src/ops/function.rs:LL:COL diff --git a/tests/ui/async-await/async-is-unwindsafe.rs b/tests/ui/async-await/async-is-unwindsafe.rs index d0202f72f008..53009b6e7410 100644 --- a/tests/ui/async-await/async-is-unwindsafe.rs +++ b/tests/ui/async-await/async-is-unwindsafe.rs @@ -11,7 +11,6 @@ fn main() { is_unwindsafe(async { //~^ ERROR the type `&mut Context<'_>` may not be safely transferred across an unwind boundary - //~| ERROR the type `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary use std::ptr::null; use std::task::{Context, RawWaker, RawWakerVTable, Waker}; let waker = unsafe { diff --git a/tests/ui/async-await/async-is-unwindsafe.stderr b/tests/ui/async-await/async-is-unwindsafe.stderr index 6bb06df9f39e..5d87fc747682 100644 --- a/tests/ui/async-await/async-is-unwindsafe.stderr +++ b/tests/ui/async-await/async-is-unwindsafe.stderr @@ -6,18 +6,19 @@ LL | is_unwindsafe(async { | |_____| | || LL | || -LL | || LL | || use std::ptr::null; +LL | || use std::task::{Context, RawWaker, RawWakerVTable, Waker}; ... || LL | || drop(cx_ref); LL | || }); | ||_____-^ `&mut Context<'_>` may not be safely transferred across an unwind boundary | |_____| - | within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}` + | within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}` | - = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`, the trait `UnwindSafe` is not implemented for `&mut Context<'_>`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}: UnwindSafe` + = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}`, the trait `UnwindSafe` is not implemented for `&mut Context<'_>`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6}: UnwindSafe` + = note: `UnwindSafe` is implemented for `&Context<'_>`, but not for `&mut Context<'_>` note: future does not implement `UnwindSafe` as this value is used across an await - --> $DIR/async-is-unwindsafe.rs:26:18 + --> $DIR/async-is-unwindsafe.rs:25:18 | LL | let cx_ref = &mut cx; | ------ has type `&mut Context<'_>` which does not implement `UnwindSafe` @@ -30,38 +31,6 @@ note: required by a bound in `is_unwindsafe` LL | fn is_unwindsafe(_: impl std::panic::UnwindSafe) {} | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_unwindsafe` -error[E0277]: the type `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary - --> $DIR/async-is-unwindsafe.rs:12:5 - | -LL | is_unwindsafe(async { - | _____^_____________- - | |_____| - | || -LL | || -LL | || -LL | || use std::ptr::null; -... || -LL | || drop(cx_ref); -LL | || }); - | ||_____-^ `&mut (dyn Any + 'static)` may not be safely transferred across an unwind boundary - | |_____| - | within this `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}` - | - = help: within `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}`, the trait `UnwindSafe` is not implemented for `&mut (dyn Any + 'static)`, which is required by `{async block@$DIR/async-is-unwindsafe.rs:12:19: 30:6}: UnwindSafe` -note: future does not implement `UnwindSafe` as this value is used across an await - --> $DIR/async-is-unwindsafe.rs:26:18 - | -LL | let mut cx = Context::from_waker(&waker); - | ------ has type `Context<'_>` which does not implement `UnwindSafe` -... -LL | async {}.await; // this needs an inner await point - | ^^^^^ await occurs here, with `mut cx` maybe used later -note: required by a bound in `is_unwindsafe` - --> $DIR/async-is-unwindsafe.rs:3:26 - | -LL | fn is_unwindsafe(_: impl std::panic::UnwindSafe) {} - | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_unwindsafe` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/context-is-sorta-unwindsafe.rs b/tests/ui/async-await/context-is-sorta-unwindsafe.rs new file mode 100644 index 000000000000..278476ea2379 --- /dev/null +++ b/tests/ui/async-await/context-is-sorta-unwindsafe.rs @@ -0,0 +1,14 @@ +//@ run-pass +// Tests against a regression surfaced by crater in https://github.com/rust-lang/rust/issues/125193 +// Unwind Safety is not a very coherent concept, but we'd prefer no regressions until we kibosh it +// and this is an unstable feature anyways sooo... + +use std::panic::UnwindSafe; +use std::task::Context; + +fn unwind_safe() {} + +fn main() { + unwind_safe::>(); // test UnwindSafe + unwind_safe::<&Context<'_>>(); // test RefUnwindSafe +} diff --git a/tests/ui/async-await/coroutine-desc.stderr b/tests/ui/async-await/coroutine-desc.stderr index 1f1e303ea4c6..4c165ff619a3 100644 --- a/tests/ui/async-await/coroutine-desc.stderr +++ b/tests/ui/async-await/coroutine-desc.stderr @@ -10,6 +10,8 @@ LL | fun(async {}, async {}); | = note: expected `async` block `{async block@$DIR/coroutine-desc.rs:10:9: 10:17}` found `async` block `{async block@$DIR/coroutine-desc.rs:10:19: 10:27}` + = note: no two async blocks, even if identical, have the same type + = help: consider pinning your async block and casting it to a trait object note: function defined here --> $DIR/coroutine-desc.rs:8:4 | @@ -51,6 +53,8 @@ LL | fun((async || {})(), (async || {})()); | = note: expected `async` closure body `{async closure body@$DIR/coroutine-desc.rs:14:19: 14:21}` found `async` closure body `{async closure body@$DIR/coroutine-desc.rs:14:36: 14:38}` + = note: no two async blocks, even if identical, have the same type + = help: consider pinning your async block and casting it to a trait object note: function defined here --> $DIR/coroutine-desc.rs:8:4 | diff --git a/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr index 70cd9f924ac5..649a868faa5e 100644 --- a/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr +++ b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr @@ -11,6 +11,9 @@ LL | | } error[E0308]: mismatched types --> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:12:9 | +LL | call(|| -> Option<()> { + | ---------- expected `Option<()>` because of return type +... LL | true | ^^^^ expected `Option<()>`, found `bool` | diff --git a/tests/ui/async-await/no-async-const.rs b/tests/ui/async-await/no-async-const.rs index c5485ebc9b62..38a5df3576bb 100644 --- a/tests/ui/async-await/no-async-const.rs +++ b/tests/ui/async-await/no-async-const.rs @@ -2,5 +2,5 @@ //@ compile-flags: --crate-type lib pub async const fn x() {} -//~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `const` +//~^ ERROR expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `const` //~| ERROR functions cannot be both `const` and `async` diff --git a/tests/ui/async-await/no-async-const.stderr b/tests/ui/async-await/no-async-const.stderr index 524d778c09b8..d692ba8f4737 100644 --- a/tests/ui/async-await/no-async-const.stderr +++ b/tests/ui/async-await/no-async-const.stderr @@ -1,10 +1,10 @@ -error: expected one of `extern`, `fn`, or `unsafe`, found keyword `const` +error: expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `const` --> $DIR/no-async-const.rs:4:11 | LL | pub async const fn x() {} | ------^^^^^ | | | - | | expected one of `extern`, `fn`, or `unsafe` + | | expected one of `extern`, `fn`, `safe`, or `unsafe` | help: `const` must come before `async`: `const async` | = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` diff --git a/tests/ui/async-await/pin-needed-to-poll-3.rs b/tests/ui/async-await/pin-needed-to-poll-3.rs new file mode 100644 index 000000000000..11ba7d29abaf --- /dev/null +++ b/tests/ui/async-await/pin-needed-to-poll-3.rs @@ -0,0 +1,25 @@ +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + + +struct FutureWrapper { + fut: F, +} + +impl Future for FutureWrapper +where + F: Future, +{ + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let res = self.fut.poll(cx); + //~^ ERROR no method named `poll` found for type parameter `F` in the current scope + res + } +} + +fn main() {} diff --git a/tests/ui/async-await/pin-needed-to-poll-3.stderr b/tests/ui/async-await/pin-needed-to-poll-3.stderr new file mode 100644 index 000000000000..e4fbef69bacb --- /dev/null +++ b/tests/ui/async-await/pin-needed-to-poll-3.stderr @@ -0,0 +1,18 @@ +error[E0599]: no method named `poll` found for type parameter `F` in the current scope + --> $DIR/pin-needed-to-poll-3.rs:19:28 + | +LL | impl Future for FutureWrapper + | - method `poll` not found for this type parameter +... +LL | let res = self.fut.poll(cx); + | ^^^^ method not found in `F` + | +help: consider pinning the expression + | +LL ~ let mut pinned = std::pin::pin!(self.fut); +LL ~ let res = pinned.as_mut().poll(cx); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.rs b/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.rs index 68a750778ada..637678692bd1 100644 --- a/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.rs +++ b/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.rs @@ -8,7 +8,7 @@ trait Super1<'a> { } impl Super1<'_, bar(): Send> for () {} -//~^ ERROR associated type bindings are not allowed here +//~^ ERROR associated item constraints are not allowed here //~| ERROR not all trait items implemented fn main() {} diff --git a/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.stderr b/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.stderr index d925c7316b6e..54960ae60bcc 100644 --- a/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.stderr +++ b/tests/ui/async-await/return-type-notation/rtn-in-impl-signature.stderr @@ -7,11 +7,16 @@ LL | #![feature(return_type_notation)] = note: see issue #109417 for more information = note: `#[warn(incomplete_features)]` on by default -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/rtn-in-impl-signature.rs:10:17 | LL | impl Super1<'_, bar(): Send> for () {} - | ^^^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^^^ associated item constraint not allowed here + | +help: consider removing this associated item constraint + | +LL | impl Super1<'_, bar(): Send> for () {} + | ~~~~~~~~~~~~~ error[E0046]: not all trait items implemented, missing: `bar` --> $DIR/rtn-in-impl-signature.rs:10:1 diff --git a/tests/ui/attributes/key-value-expansion-scope.rs b/tests/ui/attributes/key-value-expansion-scope.rs new file mode 100644 index 000000000000..b84fe4873c37 --- /dev/null +++ b/tests/ui/attributes/key-value-expansion-scope.rs @@ -0,0 +1,64 @@ +#![doc = in_root!()] // FIXME, this is a bug +#![doc = in_mod!()] //~ ERROR cannot find macro `in_mod` in this scope +#![doc = in_mod_escape!()] // FIXME, this is a bug +#![doc = in_block!()] //~ ERROR cannot find macro `in_block` in this scope + +#[doc = in_root!()] //~ ERROR cannot find macro `in_root` in this scope +#[doc = in_mod!()] //~ ERROR cannot find macro `in_mod` in this scope +#[doc = in_mod_escape!()] //~ ERROR cannot find macro `in_mod_escape` in this scope +#[doc = in_block!()] //~ ERROR cannot find macro `in_block` in this scope +fn before() { + #![doc = in_root!()] //~ ERROR cannot find macro `in_root` in this scope + #![doc = in_mod!()] //~ ERROR cannot find macro `in_mod` in this scope + #![doc = in_mod_escape!()] //~ ERROR cannot find macro `in_mod_escape` in this scope + #![doc = in_block!()] //~ ERROR cannot find macro `in_block` in this scope +} + +macro_rules! in_root { () => { "" } } + +mod macros_stay { + #![doc = in_mod!()] // FIXME, this is a bug + + macro_rules! in_mod { () => { "" } } + + #[doc = in_mod!()] // OK + fn f() { + #![doc = in_mod!()] // OK + } +} + +#[macro_use] +mod macros_escape { + #![doc = in_mod_escape!()] // FIXME, this is a bug + + macro_rules! in_mod_escape { () => { "" } } + + #[doc = in_mod_escape!()] // OK + fn f() { + #![doc = in_mod_escape!()] // OK + } +} + +fn block() { + #![doc = in_block!()] //~ ERROR cannot find macro `in_block` in this scope + + macro_rules! in_block { () => { "" } } + + #[doc = in_block!()] // OK + fn f() { + #![doc = in_block!()] // OK + } +} + +#[doc = in_root!()] // OK +#[doc = in_mod!()] //~ ERROR cannot find macro `in_mod` in this scope +#[doc = in_mod_escape!()] // OK +#[doc = in_block!()] //~ ERROR cannot find macro `in_block` in this scope +fn after() { + #![doc = in_root!()] // OK + #![doc = in_mod!()] //~ ERROR cannot find macro `in_mod` in this scope + #![doc = in_mod_escape!()] // OK + #![doc = in_block!()] //~ ERROR cannot find macro `in_block` in this scope +} + +fn main() {} diff --git a/tests/ui/attributes/key-value-expansion-scope.stderr b/tests/ui/attributes/key-value-expansion-scope.stderr new file mode 100644 index 000000000000..a66ee9b17fb9 --- /dev/null +++ b/tests/ui/attributes/key-value-expansion-scope.stderr @@ -0,0 +1,122 @@ +error: cannot find macro `in_mod` in this scope + --> $DIR/key-value-expansion-scope.rs:2:10 + | +LL | #![doc = in_mod!()] + | ^^^^^^ + | + = help: have you added the `#[macro_use]` on the module/import? + +error: cannot find macro `in_block` in this scope + --> $DIR/key-value-expansion-scope.rs:4:10 + | +LL | #![doc = in_block!()] + | ^^^^^^^^ + | + = help: have you added the `#[macro_use]` on the module/import? + +error: cannot find macro `in_root` in this scope + --> $DIR/key-value-expansion-scope.rs:6:9 + | +LL | #[doc = in_root!()] + | ^^^^^^^ + | + = help: have you added the `#[macro_use]` on the module/import? + +error: cannot find macro `in_mod` in this scope + --> $DIR/key-value-expansion-scope.rs:7:9 + | +LL | #[doc = in_mod!()] + | ^^^^^^ + | + = help: have you added the `#[macro_use]` on the module/import? + +error: cannot find macro `in_mod_escape` in this scope + --> $DIR/key-value-expansion-scope.rs:8:9 + | +LL | #[doc = in_mod_escape!()] + | ^^^^^^^^^^^^^ + | + = help: have you added the `#[macro_use]` on the module/import? + +error: cannot find macro `in_block` in this scope + --> $DIR/key-value-expansion-scope.rs:9:9 + | +LL | #[doc = in_block!()] + | ^^^^^^^^ + | + = help: have you added the `#[macro_use]` on the module/import? + +error: cannot find macro `in_root` in this scope + --> $DIR/key-value-expansion-scope.rs:11:14 + | +LL | #![doc = in_root!()] + | ^^^^^^^ + | + = help: have you added the `#[macro_use]` on the module/import? + +error: cannot find macro `in_mod` in this scope + --> $DIR/key-value-expansion-scope.rs:12:14 + | +LL | #![doc = in_mod!()] + | ^^^^^^ + | + = help: have you added the `#[macro_use]` on the module/import? + +error: cannot find macro `in_mod_escape` in this scope + --> $DIR/key-value-expansion-scope.rs:13:14 + | +LL | #![doc = in_mod_escape!()] + | ^^^^^^^^^^^^^ + | + = help: have you added the `#[macro_use]` on the module/import? + +error: cannot find macro `in_block` in this scope + --> $DIR/key-value-expansion-scope.rs:14:14 + | +LL | #![doc = in_block!()] + | ^^^^^^^^ + | + = help: have you added the `#[macro_use]` on the module/import? + +error: cannot find macro `in_block` in this scope + --> $DIR/key-value-expansion-scope.rs:43:14 + | +LL | #![doc = in_block!()] + | ^^^^^^^^ + | + = help: have you added the `#[macro_use]` on the module/import? + +error: cannot find macro `in_mod` in this scope + --> $DIR/key-value-expansion-scope.rs:54:9 + | +LL | #[doc = in_mod!()] + | ^^^^^^ + | + = help: have you added the `#[macro_use]` on the module/import? + +error: cannot find macro `in_block` in this scope + --> $DIR/key-value-expansion-scope.rs:56:9 + | +LL | #[doc = in_block!()] + | ^^^^^^^^ + | + = help: have you added the `#[macro_use]` on the module/import? + +error: cannot find macro `in_mod` in this scope + --> $DIR/key-value-expansion-scope.rs:59:14 + | +LL | #![doc = in_mod!()] + | ^^^^^^ + | + = help: have you added the `#[macro_use]` on the module/import? + +error: cannot find macro `in_block` in this scope + --> $DIR/key-value-expansion-scope.rs:61:14 + | +LL | #![doc = in_block!()] + | ^^^^^^^^ + | + = help: have you added the `#[macro_use]` on the module/import? + +error: aborting due to 15 previous errors + diff --git a/tests/ui/attributes/rustc_confusables_std_cases.stderr b/tests/ui/attributes/rustc_confusables_std_cases.stderr index 45d571f435cb..f4b6947ccd94 100644 --- a/tests/ui/attributes/rustc_confusables_std_cases.stderr +++ b/tests/ui/attributes/rustc_confusables_std_cases.stderr @@ -26,6 +26,14 @@ error[E0599]: no method named `push` found for struct `VecDeque` in the current LL | x.push(1); | ^^^^ method not found in `VecDeque<_>` | +note: there's an earlier shadowed binding `x` of type `Vec<_>` that has method `push` available + --> $DIR/rustc_confusables_std_cases.rs:8:9 + | +LL | let mut x = Vec::new(); + | ^^^^^ `x` of type `Vec<_>` that has method `push` defined earlier here +... +LL | let mut x = VecDeque::new(); + | ----- earlier `x` shadowed here with type `VecDeque` help: you might have meant to use `push_back` | LL | x.push_back(1); diff --git a/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs b/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs new file mode 100644 index 000000000000..ce365d1a8b1c --- /dev/null +++ b/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs @@ -0,0 +1,7 @@ +//@ build-pass +#![feature(unsafe_attributes)] + +#[cfg_attr(all(), unsafe(no_mangle))] +fn a() {} + +fn main() {} diff --git a/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs b/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs new file mode 100644 index 000000000000..774ce86c0960 --- /dev/null +++ b/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs @@ -0,0 +1,6 @@ +#![feature(unsafe_attributes)] + +#[derive(unsafe(Debug))] //~ ERROR: traits in `#[derive(...)]` don't accept `unsafe(...)` +struct Foo; + +fn main() {} diff --git a/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr new file mode 100644 index 000000000000..fc0daf16790a --- /dev/null +++ b/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr @@ -0,0 +1,8 @@ +error: traits in `#[derive(...)]` don't accept `unsafe(...)` + --> $DIR/derive-unsafe-attributes.rs:3:10 + | +LL | #[derive(unsafe(Debug))] + | ^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/attributes/unsafe/double-unsafe-attributes.rs b/tests/ui/attributes/unsafe/double-unsafe-attributes.rs new file mode 100644 index 000000000000..a6c0ea578f25 --- /dev/null +++ b/tests/ui/attributes/unsafe/double-unsafe-attributes.rs @@ -0,0 +1,9 @@ +#![feature(unsafe_attributes)] + +#[unsafe(unsafe(no_mangle))] +//~^ ERROR expected identifier, found keyword `unsafe` +//~| ERROR cannot find attribute `r#unsafe` in this scope +//~| ERROR `r#unsafe` is not an unsafe attribute +fn a() {} + +fn main() {} diff --git a/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr new file mode 100644 index 000000000000..1c07a5bf8baa --- /dev/null +++ b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr @@ -0,0 +1,27 @@ +error: expected identifier, found keyword `unsafe` + --> $DIR/double-unsafe-attributes.rs:3:10 + | +LL | #[unsafe(unsafe(no_mangle))] + | ^^^^^^ expected identifier, found keyword + | +help: escape `unsafe` to use it as an identifier + | +LL | #[unsafe(r#unsafe(no_mangle))] + | ++ + +error: cannot find attribute `r#unsafe` in this scope + --> $DIR/double-unsafe-attributes.rs:3:10 + | +LL | #[unsafe(unsafe(no_mangle))] + | ^^^^^^ + +error: `r#unsafe` is not an unsafe attribute + --> $DIR/double-unsafe-attributes.rs:3:3 + | +LL | #[unsafe(unsafe(no_mangle))] + | ^^^^^^ + | + = note: extraneous unsafe is not allowed in attributes + +error: aborting due to 3 previous errors + diff --git a/tests/ui/attributes/unsafe/unsafe-attributes.rs b/tests/ui/attributes/unsafe/unsafe-attributes.rs new file mode 100644 index 000000000000..e7620a18048e --- /dev/null +++ b/tests/ui/attributes/unsafe/unsafe-attributes.rs @@ -0,0 +1,7 @@ +//@ build-pass +#![feature(unsafe_attributes)] + +#[unsafe(no_mangle)] +fn a() {} + +fn main() {} diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute.rs b/tests/ui/attributes/unsafe/unsafe-safe-attribute.rs new file mode 100644 index 000000000000..67db36afd2e6 --- /dev/null +++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute.rs @@ -0,0 +1,6 @@ +#![feature(unsafe_attributes)] + +#[unsafe(repr(C))] //~ ERROR: is not an unsafe attribute +struct Foo {} + +fn main() {} diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr b/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr new file mode 100644 index 000000000000..0602af34e4f6 --- /dev/null +++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr @@ -0,0 +1,10 @@ +error: `repr` is not an unsafe attribute + --> $DIR/unsafe-safe-attribute.rs:3:3 + | +LL | #[unsafe(repr(C))] + | ^^^^^^ + | + = note: extraneous unsafe is not allowed in attributes + +error: aborting due to 1 previous error + diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs new file mode 100644 index 000000000000..ff2eb61b4053 --- /dev/null +++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs @@ -0,0 +1,8 @@ +#![feature(unsafe_attributes)] + +#[unsafe(diagnostic::on_unimplemented( //~ ERROR: is not an unsafe attribute + message = "testing", +))] +trait Foo {} + +fn main() {} diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr new file mode 100644 index 000000000000..584dacf4d8c9 --- /dev/null +++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr @@ -0,0 +1,10 @@ +error: `diagnostic::on_unimplemented` is not an unsafe attribute + --> $DIR/unsafe-safe-attribute_diagnostic.rs:3:3 + | +LL | #[unsafe(diagnostic::on_unimplemented( + | ^^^^^^ + | + = note: extraneous unsafe is not allowed in attributes + +error: aborting due to 1 previous error + diff --git a/tests/ui/auto-traits/typeck-default-trait-impl-precedence.stderr b/tests/ui/auto-traits/typeck-default-trait-impl-precedence.stderr index 47bb1a059be5..4773ac4ccf72 100644 --- a/tests/ui/auto-traits/typeck-default-trait-impl-precedence.stderr +++ b/tests/ui/auto-traits/typeck-default-trait-impl-precedence.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `&'static u32: Defaulted` is not satisfied --> $DIR/typeck-default-trait-impl-precedence.rs:19:20 | LL | is_defaulted::<&'static u32>(); - | ^^^^^^^^^^^^ the trait `Signed` is not implemented for `&'static u32`, which is required by `&'static u32: Defaulted` + | ^^^^^^^^^^^^ the trait `Signed` is not implemented for `u32`, which is required by `&'static u32: Defaulted` | note: required for `&'static u32` to implement `Defaulted` --> $DIR/typeck-default-trait-impl-precedence.rs:10:19 diff --git a/tests/ui/backtrace-apple-no-dsymutil.rs b/tests/ui/backtrace/apple-no-dsymutil.rs similarity index 98% rename from tests/ui/backtrace-apple-no-dsymutil.rs rename to tests/ui/backtrace/apple-no-dsymutil.rs index 9924cd13b0a7..e5aeced25ca3 100644 --- a/tests/ui/backtrace-apple-no-dsymutil.rs +++ b/tests/ui/backtrace/apple-no-dsymutil.rs @@ -2,7 +2,7 @@ //@ compile-flags:-Cstrip=none //@ compile-flags:-g -Csplit-debuginfo=unpacked -//@ only-macos +//@ only-apple use std::process::Command; use std::str; diff --git a/tests/ui/debuginfo/auxiliary/dylib-dep-helper-aux.rs b/tests/ui/backtrace/auxiliary/dylib-dep-helper-aux.rs similarity index 100% rename from tests/ui/debuginfo/auxiliary/dylib-dep-helper-aux.rs rename to tests/ui/backtrace/auxiliary/dylib-dep-helper-aux.rs diff --git a/tests/ui/debuginfo/auxiliary/dylib-dep-helper.rs b/tests/ui/backtrace/auxiliary/dylib-dep-helper.rs similarity index 100% rename from tests/ui/debuginfo/auxiliary/dylib-dep-helper.rs rename to tests/ui/backtrace/auxiliary/dylib-dep-helper.rs diff --git a/tests/ui/debuginfo/auxiliary/line-tables-only-helper.rs b/tests/ui/backtrace/auxiliary/line-tables-only-helper.rs similarity index 100% rename from tests/ui/debuginfo/auxiliary/line-tables-only-helper.rs rename to tests/ui/backtrace/auxiliary/line-tables-only-helper.rs diff --git a/tests/ui/backtrace.rs b/tests/ui/backtrace/backtrace.rs similarity index 100% rename from tests/ui/backtrace.rs rename to tests/ui/backtrace/backtrace.rs diff --git a/tests/ui/debuginfo/backtrace-dylib-dep.rs b/tests/ui/backtrace/dylib-dep.rs similarity index 100% rename from tests/ui/debuginfo/backtrace-dylib-dep.rs rename to tests/ui/backtrace/dylib-dep.rs diff --git a/tests/ui/debuginfo/backtrace-line-tables-only.rs b/tests/ui/backtrace/line-tables-only.rs similarity index 100% rename from tests/ui/debuginfo/backtrace-line-tables-only.rs rename to tests/ui/backtrace/line-tables-only.rs diff --git a/tests/ui/std-backtrace.rs b/tests/ui/backtrace/std-backtrace.rs similarity index 100% rename from tests/ui/std-backtrace.rs rename to tests/ui/backtrace/std-backtrace.rs diff --git a/tests/ui/binop/binary-op-suggest-deref.stderr b/tests/ui/binop/binary-op-suggest-deref.stderr index 47af51e2106b..ec17074e3050 100644 --- a/tests/ui/binop/binary-op-suggest-deref.stderr +++ b/tests/ui/binop/binary-op-suggest-deref.stderr @@ -303,10 +303,10 @@ LL | let _ = FOO & (*"Sized".to_string().into_boxed_str()); | = help: the trait `BitAnd` is not implemented for `i32` = help: the following other types implement trait `BitAnd`: - <&'a i32 as BitAnd> - <&i32 as BitAnd<&i32>> - > - + `&'a i32` implements `BitAnd` + `&i32` implements `BitAnd<&i32>` + `i32` implements `BitAnd<&i32>` + `i32` implements `BitAnd` error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/binary-op-suggest-deref.rs:78:17 diff --git a/tests/ui/binop/binop-mul-i32-f32.stderr b/tests/ui/binop/binop-mul-i32-f32.stderr index 29e1ff918340..33d8fba172ce 100644 --- a/tests/ui/binop/binop-mul-i32-f32.stderr +++ b/tests/ui/binop/binop-mul-i32-f32.stderr @@ -6,10 +6,10 @@ LL | x * y | = help: the trait `Mul` is not implemented for `i32` = help: the following other types implement trait `Mul`: - <&'a i32 as Mul> - <&i32 as Mul<&i32>> - > - + `&'a i32` implements `Mul` + `&i32` implements `Mul<&i32>` + `i32` implements `Mul<&i32>` + `i32` implements `Mul` error: aborting due to 1 previous error diff --git a/tests/ui/binop/nested-assignment-may-be-deref.rs b/tests/ui/binop/nested-assignment-may-be-deref.rs new file mode 100644 index 000000000000..f675ab2e9183 --- /dev/null +++ b/tests/ui/binop/nested-assignment-may-be-deref.rs @@ -0,0 +1,14 @@ +pub fn bad(x: &mut bool) { + if true + *x = true {} + //~^ ERROR cannot multiply `bool` by `&mut bool` +} + +pub fn bad2(x: &mut bool) { + let y: bool; + y = true + *x = true; + //~^ ERROR cannot multiply `bool` by `&mut bool` +} + +fn main() {} diff --git a/tests/ui/binop/nested-assignment-may-be-deref.stderr b/tests/ui/binop/nested-assignment-may-be-deref.stderr new file mode 100644 index 000000000000..95b2db2b26c8 --- /dev/null +++ b/tests/ui/binop/nested-assignment-may-be-deref.stderr @@ -0,0 +1,29 @@ +error[E0369]: cannot multiply `bool` by `&mut bool` + --> $DIR/nested-assignment-may-be-deref.rs:3:5 + | +LL | if true + | ---- bool +LL | *x = true {} + | ^- &mut bool + | +help: you might have meant to write a semicolon here + | +LL | if true; + | + + +error[E0369]: cannot multiply `bool` by `&mut bool` + --> $DIR/nested-assignment-may-be-deref.rs:10:5 + | +LL | y = true + | ---- bool +LL | *x = true; + | ^- &mut bool + | +help: you might have meant to write a semicolon here + | +LL | y = true; + | + + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/binop/shift-various-bad-types.stderr b/tests/ui/binop/shift-various-bad-types.stderr index 38db66f86b46..7313cb3fb84f 100644 --- a/tests/ui/binop/shift-various-bad-types.stderr +++ b/tests/ui/binop/shift-various-bad-types.stderr @@ -6,14 +6,14 @@ LL | 22 >> p.char; | = help: the trait `Shr` is not implemented for `{integer}` = help: the following other types implement trait `Shr`: - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` and 568 others error[E0277]: no implementation for `{integer} >> &str` @@ -24,14 +24,14 @@ LL | 22 >> p.str; | = help: the trait `Shr<&str>` is not implemented for `{integer}` = help: the following other types implement trait `Shr`: - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` and 568 others error[E0277]: no implementation for `{integer} >> &Panolpy` @@ -42,14 +42,14 @@ LL | 22 >> p; | = help: the trait `Shr<&Panolpy>` is not implemented for `{integer}` = help: the following other types implement trait `Shr`: - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> - <&'a i128 as Shr> + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` + `&'a i128` implements `Shr` and 568 others error[E0308]: mismatched types diff --git a/tests/ui/borrowck/generic_const_early_param.rs b/tests/ui/borrowck/generic_const_early_param.rs index f601e45d21fe..0d07b6869f12 100644 --- a/tests/ui/borrowck/generic_const_early_param.rs +++ b/tests/ui/borrowck/generic_const_early_param.rs @@ -5,7 +5,6 @@ struct DataWrapper<'static> { //~^ ERROR invalid lifetime parameter name: `'static` data: &'a [u8; Self::SIZE], //~^ ERROR use of undeclared lifetime name `'a` - //~^^ ERROR lifetime may not live long enough } impl DataWrapper<'a> { diff --git a/tests/ui/borrowck/generic_const_early_param.stderr b/tests/ui/borrowck/generic_const_early_param.stderr index a71ab09396e7..3f56d6a33251 100644 --- a/tests/ui/borrowck/generic_const_early_param.stderr +++ b/tests/ui/borrowck/generic_const_early_param.stderr @@ -14,7 +14,7 @@ LL | data: &'a [u8; Self::SIZE], | ^^ undeclared lifetime error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/generic_const_early_param.rs:11:18 + --> $DIR/generic_const_early_param.rs:10:18 | LL | impl DataWrapper<'a> { | - ^^ undeclared lifetime @@ -30,13 +30,7 @@ LL | #![feature(generic_const_exprs)] = note: see issue #76560 for more information = note: `#[warn(incomplete_features)]` on by default -error: lifetime may not live long enough - --> $DIR/generic_const_early_param.rs:6:20 - | -LL | data: &'a [u8; Self::SIZE], - | ^^^^^^^^^^ requires that `'_` must outlive `'static` - -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 3 previous errors; 1 warning emitted Some errors have detailed explanations: E0261, E0262. For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/borrowck/issue-64453.stderr b/tests/ui/borrowck/issue-64453.stderr index 0e4a8d42f6e8..e671817633be 100644 --- a/tests/ui/borrowck/issue-64453.stderr +++ b/tests/ui/borrowck/issue-64453.stderr @@ -14,7 +14,7 @@ LL | static settings_dir: String = format!(""); | ^^^^^^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0507]: cannot move out of static item `settings_dir` diff --git a/tests/ui/borrowck/uninitalized-in-match-arm-issue-126133.rs b/tests/ui/borrowck/uninitalized-in-match-arm-issue-126133.rs new file mode 100644 index 000000000000..1974bbf9fe7b --- /dev/null +++ b/tests/ui/borrowck/uninitalized-in-match-arm-issue-126133.rs @@ -0,0 +1,22 @@ +enum E { + A, + B, + C, +} + +fn foo(e: E) { + let bar; + + match e { + E::A if true => return, + E::A => return, + E::B => {} + E::C => { + bar = 5; + } + } + + let _baz = bar; //~ ERROR E0381 +} + +fn main() {} diff --git a/tests/ui/borrowck/uninitalized-in-match-arm-issue-126133.stderr b/tests/ui/borrowck/uninitalized-in-match-arm-issue-126133.stderr new file mode 100644 index 000000000000..eb7d0f8b2047 --- /dev/null +++ b/tests/ui/borrowck/uninitalized-in-match-arm-issue-126133.stderr @@ -0,0 +1,15 @@ +error[E0381]: used binding `bar` is possibly-uninitialized + --> $DIR/uninitalized-in-match-arm-issue-126133.rs:19:16 + | +LL | let bar; + | --- binding declared here but left uninitialized +... +LL | E::B => {} + | ---- if this pattern is matched, `bar` is not initialized +... +LL | let _baz = bar; + | ^^^ `bar` used here but it is possibly-uninitialized + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0381`. diff --git a/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.rs b/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.rs new file mode 100644 index 000000000000..637c47f29396 --- /dev/null +++ b/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.rs @@ -0,0 +1,14 @@ +// This test was triggering a `span_bug` crash, which was then fixed by +// downgrading it to a `span_delayed_bug`. + +pub struct MyStruct<'field> { + field: &'field [u32], +} + +impl MyStruct<'_> { + pub fn f(field: &[u32]) -> Self { //~ ERROR type arguments are not allowed on self type + Self { field } //~ ERROR lifetime may not live long enough + } +} + +fn main() {} diff --git a/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.stderr b/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.stderr new file mode 100644 index 000000000000..0ae301b2090a --- /dev/null +++ b/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.stderr @@ -0,0 +1,34 @@ +error[E0109]: type arguments are not allowed on self type + --> $DIR/unmatched-arg-and-hir-arg-issue-126385.rs:9:37 + | +LL | pub fn f(field: &[u32]) -> Self { + | ---- ^^^ type argument not allowed + | | + | not allowed on self type + | +note: `Self` is of type `MyStruct<'_>` + --> $DIR/unmatched-arg-and-hir-arg-issue-126385.rs:4:12 + | +LL | pub struct MyStruct<'field> { + | ^^^^^^^^ `Self` corresponds to this type +... +LL | impl MyStruct<'_> { + | ----------------- `Self` is on type `MyStruct` in this `impl` +help: the `Self` type doesn't accept type parameters, use the concrete type's name `MyStruct` instead if you want to specify its type parameters + | +LL | pub fn f(field: &[u32]) -> MyStruct { + | ~~~~~~~~ + +error: lifetime may not live long enough + --> $DIR/unmatched-arg-and-hir-arg-issue-126385.rs:10:9 + | +LL | pub fn f(field: &[u32]) -> Self { + | - --------- return type is MyStruct<'2> + | | + | let's call the lifetime of this reference `'1` +LL | Self { field } + | ^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0109`. diff --git a/tests/ui/c-variadic/variadic-ffi-no-fixed-args.rs b/tests/ui/c-variadic/variadic-ffi-no-fixed-args.rs index 588c15a18297..b8841e88c4f4 100644 --- a/tests/ui/c-variadic/variadic-ffi-no-fixed-args.rs +++ b/tests/ui/c-variadic/variadic-ffi-no-fixed-args.rs @@ -1,6 +1,9 @@ +//@ build-pass + +// Supported since C23 +// https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2975.pdf extern "C" { fn foo(...); -//~^ ERROR C-variadic function must be declared with at least one named argument } fn main() {} diff --git a/tests/ui/c-variadic/variadic-ffi-no-fixed-args.stderr b/tests/ui/c-variadic/variadic-ffi-no-fixed-args.stderr deleted file mode 100644 index e268ef3fa681..000000000000 --- a/tests/ui/c-variadic/variadic-ffi-no-fixed-args.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-no-fixed-args.rs:2:12 - | -LL | fn foo(...); - | ^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/cfg/future-compat-crate-attributes-using-cfg_attr.stderr b/tests/ui/cfg/future-compat-crate-attributes-using-cfg_attr.stderr index 75df314e0baa..82b2d7d1b1da 100644 --- a/tests/ui/cfg/future-compat-crate-attributes-using-cfg_attr.stderr +++ b/tests/ui/cfg/future-compat-crate-attributes-using-cfg_attr.stderr @@ -1,4 +1,4 @@ -error: `crate_type` within an `#![cfg_attr] attribute is deprecated` +error: `crate_type` within an `#![cfg_attr]` attribute is deprecated --> $DIR/future-compat-crate-attributes-using-cfg_attr.rs:4:18 | LL | #![cfg_attr(foo, crate_type="bin")] @@ -8,7 +8,7 @@ LL | #![cfg_attr(foo, crate_type="bin")] = note: for more information, see issue #91632 = note: `#[deny(deprecated_cfg_attr_crate_type_name)]` on by default -error: `crate_name` within an `#![cfg_attr] attribute is deprecated` +error: `crate_name` within an `#![cfg_attr]` attribute is deprecated --> $DIR/future-compat-crate-attributes-using-cfg_attr.rs:9:18 | LL | #![cfg_attr(foo, crate_name="bar")] @@ -17,7 +17,7 @@ LL | #![cfg_attr(foo, crate_name="bar")] = warning: 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 #91632 -error: `crate_type` within an `#![cfg_attr] attribute is deprecated` +error: `crate_type` within an `#![cfg_attr]` attribute is deprecated --> $DIR/future-compat-crate-attributes-using-cfg_attr.rs:4:18 | LL | #![cfg_attr(foo, crate_type="bin")] @@ -27,7 +27,7 @@ LL | #![cfg_attr(foo, crate_type="bin")] = note: for more information, see issue #91632 = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: `crate_name` within an `#![cfg_attr] attribute is deprecated` +error: `crate_name` within an `#![cfg_attr]` attribute is deprecated --> $DIR/future-compat-crate-attributes-using-cfg_attr.rs:9:18 | LL | #![cfg_attr(foo, crate_name="bar")] diff --git a/tests/ui/check-cfg/allow-same-level.stderr b/tests/ui/check-cfg/allow-same-level.stderr index ae4c1605f016..b311a80c8fd5 100644 --- a/tests/ui/check-cfg/allow-same-level.stderr +++ b/tests/ui/check-cfg/allow-same-level.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `FALSE` LL | #[cfg(FALSE)] | ^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` + = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(FALSE)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/cargo-build-script.rs b/tests/ui/check-cfg/cargo-build-script.rs new file mode 100644 index 000000000000..a3ba8791441e --- /dev/null +++ b/tests/ui/check-cfg/cargo-build-script.rs @@ -0,0 +1,22 @@ +// This test checks that when we are building a build script provided +// by Cargo we only suggest expecting the unexpected cfgs in the Cargo.toml. +// +//@ check-pass +//@ no-auto-check-cfg +//@ rustc-env:CARGO_CRATE_NAME=build_script_build +//@ compile-flags:--crate-name=build_script_build +//@ compile-flags:--check-cfg=cfg(has_bar) + +#[cfg(has_foo)] +//~^ WARNING unexpected `cfg` condition name +fn foo() {} + +#[cfg(has_foo = "yes")] +//~^ WARNING unexpected `cfg` condition name +fn foo() {} + +#[cfg(has_bar = "yes")] +//~^ WARNING unexpected `cfg` condition value +fn has_bar() {} + +fn main() {} diff --git a/tests/ui/check-cfg/cargo-build-script.stderr b/tests/ui/check-cfg/cargo-build-script.stderr new file mode 100644 index 000000000000..9ab3290ef22e --- /dev/null +++ b/tests/ui/check-cfg/cargo-build-script.stderr @@ -0,0 +1,43 @@ +warning: unexpected `cfg` condition name: `has_foo` + --> $DIR/cargo-build-script.rs:10:7 + | +LL | #[cfg(has_foo)] + | ^^^^^^^ + | + = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `has_bar`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` + = help: consider using a Cargo feature instead + = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: + [lints.rust] + unexpected_cfgs = { level = "warn", check-cfg = ['cfg(has_foo)'] } + = note: see for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default + +warning: unexpected `cfg` condition name: `has_foo` + --> $DIR/cargo-build-script.rs:14:7 + | +LL | #[cfg(has_foo = "yes")] + | ^^^^^^^^^^^^^^^ + | + = help: consider using a Cargo feature instead + = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: + [lints.rust] + unexpected_cfgs = { level = "warn", check-cfg = ['cfg(has_foo, values("yes"))'] } + = note: see for more information about checking conditional configuration + +warning: unexpected `cfg` condition value: `yes` + --> $DIR/cargo-build-script.rs:18:7 + | +LL | #[cfg(has_bar = "yes")] + | ^^^^^^^-------- + | | + | help: remove the value + | + = note: no expected value for `has_bar` + = help: consider using a Cargo feature instead + = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: + [lints.rust] + unexpected_cfgs = { level = "warn", check-cfg = ['cfg(has_bar, values("yes"))'] } + = note: see for more information about checking conditional configuration + +warning: 3 warnings emitted + diff --git a/tests/ui/check-cfg/cargo-feature.none.stderr b/tests/ui/check-cfg/cargo-feature.none.stderr index 627f03ddf552..9d3117ed54d1 100644 --- a/tests/ui/check-cfg/cargo-feature.none.stderr +++ b/tests/ui/check-cfg/cargo-feature.none.stderr @@ -6,7 +6,7 @@ LL | #[cfg(feature = "serde")] | = note: no expected values for `feature` = help: consider adding `serde` as a feature in `Cargo.toml` - = note: see for more information about checking conditional configuration + = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition value: (none) @@ -17,7 +17,7 @@ LL | #[cfg(feature)] | = note: no expected values for `feature` = help: consider defining some features in `Cargo.toml` - = note: see for more information about checking conditional configuration + = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `tokio_unstable` --> $DIR/cargo-feature.rs:22:7 @@ -25,9 +25,13 @@ warning: unexpected `cfg` condition name: `tokio_unstable` LL | #[cfg(tokio_unstable)] | ^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` - = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(tokio_unstable)");` to the top of the `build.rs` - = note: see for more information about checking conditional configuration + = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` + = help: consider using a Cargo feature instead + = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: + [lints.rust] + unexpected_cfgs = { level = "warn", check-cfg = ['cfg(tokio_unstable)'] } + = help: or consider adding `println!("cargo::rustc-check-cfg=cfg(tokio_unstable)");` to the top of the `build.rs` + = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `CONFIG_NVME` --> $DIR/cargo-feature.rs:26:7 @@ -35,8 +39,12 @@ warning: unexpected `cfg` condition name: `CONFIG_NVME` LL | #[cfg(CONFIG_NVME = "m")] | ^^^^^^^^^^^^^^^^^ | - = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(CONFIG_NVME, values(\"m\"))");` to the top of the `build.rs` - = note: see for more information about checking conditional configuration + = help: consider using a Cargo feature instead + = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: + [lints.rust] + unexpected_cfgs = { level = "warn", check-cfg = ['cfg(CONFIG_NVME, values("m"))'] } + = help: or consider adding `println!("cargo::rustc-check-cfg=cfg(CONFIG_NVME, values(\"m\"))");` to the top of the `build.rs` + = note: see for more information about checking conditional configuration warning: 4 warnings emitted diff --git a/tests/ui/check-cfg/cargo-feature.some.stderr b/tests/ui/check-cfg/cargo-feature.some.stderr index 9cc5fb6aca03..14e24cb1429a 100644 --- a/tests/ui/check-cfg/cargo-feature.some.stderr +++ b/tests/ui/check-cfg/cargo-feature.some.stderr @@ -6,7 +6,7 @@ LL | #[cfg(feature = "serde")] | = note: expected values for `feature` are: `bitcode` = help: consider adding `serde` as a feature in `Cargo.toml` - = note: see for more information about checking conditional configuration + = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition value: (none) @@ -17,7 +17,7 @@ LL | #[cfg(feature)] | = note: expected values for `feature` are: `bitcode` = help: consider defining some features in `Cargo.toml` - = note: see for more information about checking conditional configuration + = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `tokio_unstable` --> $DIR/cargo-feature.rs:22:7 @@ -25,9 +25,13 @@ warning: unexpected `cfg` condition name: `tokio_unstable` LL | #[cfg(tokio_unstable)] | ^^^^^^^^^^^^^^ | - = help: expected names are: `CONFIG_NVME`, `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` - = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(tokio_unstable)");` to the top of the `build.rs` - = note: see for more information about checking conditional configuration + = help: expected names are: `CONFIG_NVME`, `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` + = help: consider using a Cargo feature instead + = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: + [lints.rust] + unexpected_cfgs = { level = "warn", check-cfg = ['cfg(tokio_unstable)'] } + = help: or consider adding `println!("cargo::rustc-check-cfg=cfg(tokio_unstable)");` to the top of the `build.rs` + = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `m` --> $DIR/cargo-feature.rs:26:7 @@ -38,8 +42,12 @@ LL | #[cfg(CONFIG_NVME = "m")] | help: there is a expected value with a similar name: `"y"` | = note: expected values for `CONFIG_NVME` are: `y` - = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(CONFIG_NVME, values(\"m\"))");` to the top of the `build.rs` - = note: see for more information about checking conditional configuration + = help: consider using a Cargo feature instead + = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: + [lints.rust] + unexpected_cfgs = { level = "warn", check-cfg = ['cfg(CONFIG_NVME, values("m"))'] } + = help: or consider adding `println!("cargo::rustc-check-cfg=cfg(CONFIG_NVME, values(\"m\"))");` to the top of the `build.rs` + = note: see for more information about checking conditional configuration warning: 4 warnings emitted diff --git a/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr b/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr index 4975129802b6..08bd43832ea1 100644 --- a/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr +++ b/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `value` LL | #[cfg(value)] | ^^^^^ | - = help: expected names are: `bar`, `bee`, `clippy`, `cow`, `debug_assertions`, `doc`, `doctest`, `foo`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` + = help: expected names are: `bar`, `bee`, `clippy`, `cow`, `debug_assertions`, `doc`, `doctest`, `foo`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(value)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr b/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr index 6404556f46c4..6db1144eada4 100644 --- a/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr +++ b/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `my_value` LL | #[cfg(my_value)] | ^^^^^^^^ | - = help: expected names are: `bar`, `clippy`, `debug_assertions`, `doc`, `doctest`, `foo`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` + = help: expected names are: `bar`, `clippy`, `debug_assertions`, `doc`, `doctest`, `foo`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(my_value)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/cfg-value-for-cfg-name.stderr b/tests/ui/check-cfg/cfg-value-for-cfg-name.stderr index bda1a601410e..a5f8176343a4 100644 --- a/tests/ui/check-cfg/cfg-value-for-cfg-name.stderr +++ b/tests/ui/check-cfg/cfg-value-for-cfg-name.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `linux` LL | #[cfg(linux)] | ^^^^^ help: found config with similar value: `target_os = "linux"` | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` + = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(linux)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/compact-names.stderr b/tests/ui/check-cfg/compact-names.stderr index 079be8d08c29..6fecdb52362e 100644 --- a/tests/ui/check-cfg/compact-names.stderr +++ b/tests/ui/check-cfg/compact-names.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `target_architecture` LL | #[cfg(target(os = "linux", architecture = "arm"))] | ^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` + = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(target_architecture, values("arm"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/compact-values.stderr b/tests/ui/check-cfg/compact-values.stderr index 4fe921dd24b2..c8d14f8c02aa 100644 --- a/tests/ui/check-cfg/compact-values.stderr +++ b/tests/ui/check-cfg/compact-values.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition value: `X` LL | #[cfg(target(os = "linux", pointer_width = "X"))] | ^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_pointer_width` are: `16`, `32`, `64` + = note: expected values for `target_pointer_width` are: `16`, `32`, and `64` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/concat-values.stderr b/tests/ui/check-cfg/concat-values.stderr index a508c3976617..ec740952f573 100644 --- a/tests/ui/check-cfg/concat-values.stderr +++ b/tests/ui/check-cfg/concat-values.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition value: (none) LL | #[cfg(my_cfg)] | ^^^^^^ | - = note: expected values for `my_cfg` are: `bar`, `foo` + = note: expected values for `my_cfg` are: `bar` and `foo` = help: to expect this configuration use `--check-cfg=cfg(my_cfg)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default @@ -15,7 +15,7 @@ warning: unexpected `cfg` condition value: `unk` LL | #[cfg(my_cfg = "unk")] | ^^^^^^^^^^^^^^ | - = note: expected values for `my_cfg` are: `bar`, `foo` + = note: expected values for `my_cfg` are: `bar` and `foo` = help: to expect this configuration use `--check-cfg=cfg(my_cfg, values("unk"))` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/diagnotics.cargo.stderr b/tests/ui/check-cfg/diagnotics.cargo.stderr index 1b7505682da3..a440ccaaf584 100644 --- a/tests/ui/check-cfg/diagnotics.cargo.stderr +++ b/tests/ui/check-cfg/diagnotics.cargo.stderr @@ -1,62 +1,70 @@ warning: unexpected `cfg` condition name: `featur` - --> $DIR/diagnotics.rs:8:7 + --> $DIR/diagnotics.rs:9:7 | LL | #[cfg(featur)] | ^^^^^^ help: there is a config with a similar name: `feature` | = help: expected values for `feature` are: `foo` - = note: see for more information about checking conditional configuration + = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition name: `featur` - --> $DIR/diagnotics.rs:12:7 + --> $DIR/diagnotics.rs:13:7 | LL | #[cfg(featur = "foo")] | ^^^^^^^^^^^^^^ | - = note: see for more information about checking conditional configuration + = note: see for more information about checking conditional configuration help: there is a config with a similar name and value | LL | #[cfg(feature = "foo")] | ~~~~~~~ warning: unexpected `cfg` condition name: `featur` - --> $DIR/diagnotics.rs:16:7 + --> $DIR/diagnotics.rs:17:7 | LL | #[cfg(featur = "fo")] | ^^^^^^^^^^^^^ | = help: expected values for `feature` are: `foo` - = note: see for more information about checking conditional configuration + = note: see for more information about checking conditional configuration help: there is a config with a similar name and different values | LL | #[cfg(feature = "foo")] | ~~~~~~~~~~~~~~~ warning: unexpected `cfg` condition name: `no_value` - --> $DIR/diagnotics.rs:23:7 + --> $DIR/diagnotics.rs:24:7 | LL | #[cfg(no_value)] | ^^^^^^^^ help: there is a config with a similar name: `no_values` | - = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(no_value)");` to the top of the `build.rs` - = note: see for more information about checking conditional configuration + = help: consider using a Cargo feature instead + = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: + [lints.rust] + unexpected_cfgs = { level = "warn", check-cfg = ['cfg(no_value)'] } + = help: or consider adding `println!("cargo::rustc-check-cfg=cfg(no_value)");` to the top of the `build.rs` + = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `no_value` - --> $DIR/diagnotics.rs:27:7 + --> $DIR/diagnotics.rs:28:7 | LL | #[cfg(no_value = "foo")] | ^^^^^^^^^^^^^^^^ | - = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(no_value, values(\"foo\"))");` to the top of the `build.rs` - = note: see for more information about checking conditional configuration + = help: consider using a Cargo feature instead + = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: + [lints.rust] + unexpected_cfgs = { level = "warn", check-cfg = ['cfg(no_value, values("foo"))'] } + = help: or consider adding `println!("cargo::rustc-check-cfg=cfg(no_value, values(\"foo\"))");` to the top of the `build.rs` + = note: see for more information about checking conditional configuration help: there is a config with a similar name and no value | LL | #[cfg(no_values)] | ~~~~~~~~~ warning: unexpected `cfg` condition value: `bar` - --> $DIR/diagnotics.rs:31:7 + --> $DIR/diagnotics.rs:32:7 | LL | #[cfg(no_values = "bar")] | ^^^^^^^^^-------- @@ -64,8 +72,28 @@ LL | #[cfg(no_values = "bar")] | help: remove the value | = note: no expected value for `no_values` - = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(no_values, values(\"bar\"))");` to the top of the `build.rs` - = note: see for more information about checking conditional configuration + = help: consider using a Cargo feature instead + = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: + [lints.rust] + unexpected_cfgs = { level = "warn", check-cfg = ['cfg(no_values, values("bar"))'] } + = help: or consider adding `println!("cargo::rustc-check-cfg=cfg(no_values, values(\"bar\"))");` to the top of the `build.rs` + = note: see for more information about checking conditional configuration -warning: 6 warnings emitted +warning: unexpected `cfg` condition value: `quote"` + --> $DIR/diagnotics.rs:36:7 + | +LL | #[cfg(quote = "quote\"")] + | ^^^^^^^^--------- + | | + | help: there is a expected value with a similar name: `"quote"` + | + = note: expected values for `quote` are: `quote` + = help: consider using a Cargo feature instead + = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: + [lints.rust] + unexpected_cfgs = { level = "warn", check-cfg = ['cfg(quote, values("quote\""))'] } + = help: or consider adding `println!("cargo::rustc-check-cfg=cfg(quote, values(\"quote\\\"\"))");` to the top of the `build.rs` + = note: see for more information about checking conditional configuration + +warning: 7 warnings emitted diff --git a/tests/ui/check-cfg/diagnotics.rs b/tests/ui/check-cfg/diagnotics.rs index b8268ec56062..7155d7c81b98 100644 --- a/tests/ui/check-cfg/diagnotics.rs +++ b/tests/ui/check-cfg/diagnotics.rs @@ -4,6 +4,7 @@ //@ [rustc]unset-rustc-env:CARGO_CRATE_NAME //@ [cargo]rustc-env:CARGO_CRATE_NAME=foo //@ compile-flags: --check-cfg=cfg(feature,values("foo")) --check-cfg=cfg(no_values) +//@ compile-flags: --check-cfg=cfg(quote,values("quote")) #[cfg(featur)] //~^ WARNING unexpected `cfg` condition name @@ -32,4 +33,8 @@ fn no_values() {} //~^ WARNING unexpected `cfg` condition value fn no_values() {} +#[cfg(quote = "quote\"")] +//~^ WARNING unexpected `cfg` condition value +fn no_values() {} + fn main() {} diff --git a/tests/ui/check-cfg/diagnotics.rustc.stderr b/tests/ui/check-cfg/diagnotics.rustc.stderr index 0bd6ce156bb6..6868be482d87 100644 --- a/tests/ui/check-cfg/diagnotics.rustc.stderr +++ b/tests/ui/check-cfg/diagnotics.rustc.stderr @@ -1,5 +1,5 @@ warning: unexpected `cfg` condition name: `featur` - --> $DIR/diagnotics.rs:8:7 + --> $DIR/diagnotics.rs:9:7 | LL | #[cfg(featur)] | ^^^^^^ help: there is a config with a similar name: `feature` @@ -10,7 +10,7 @@ LL | #[cfg(featur)] = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition name: `featur` - --> $DIR/diagnotics.rs:12:7 + --> $DIR/diagnotics.rs:13:7 | LL | #[cfg(featur = "foo")] | ^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | #[cfg(feature = "foo")] | ~~~~~~~ warning: unexpected `cfg` condition name: `featur` - --> $DIR/diagnotics.rs:16:7 + --> $DIR/diagnotics.rs:17:7 | LL | #[cfg(featur = "fo")] | ^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | #[cfg(feature = "foo")] | ~~~~~~~~~~~~~~~ warning: unexpected `cfg` condition name: `no_value` - --> $DIR/diagnotics.rs:23:7 + --> $DIR/diagnotics.rs:24:7 | LL | #[cfg(no_value)] | ^^^^^^^^ help: there is a config with a similar name: `no_values` @@ -46,7 +46,7 @@ LL | #[cfg(no_value)] = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `no_value` - --> $DIR/diagnotics.rs:27:7 + --> $DIR/diagnotics.rs:28:7 | LL | #[cfg(no_value = "foo")] | ^^^^^^^^^^^^^^^^ @@ -59,7 +59,7 @@ LL | #[cfg(no_values)] | ~~~~~~~~~ warning: unexpected `cfg` condition value: `bar` - --> $DIR/diagnotics.rs:31:7 + --> $DIR/diagnotics.rs:32:7 | LL | #[cfg(no_values = "bar")] | ^^^^^^^^^-------- @@ -70,5 +70,17 @@ LL | #[cfg(no_values = "bar")] = help: to expect this configuration use `--check-cfg=cfg(no_values, values("bar"))` = note: see for more information about checking conditional configuration -warning: 6 warnings emitted +warning: unexpected `cfg` condition value: `quote"` + --> $DIR/diagnotics.rs:36:7 + | +LL | #[cfg(quote = "quote\"")] + | ^^^^^^^^--------- + | | + | help: there is a expected value with a similar name: `"quote"` + | + = note: expected values for `quote` are: `quote` + = help: to expect this configuration use `--check-cfg=cfg(quote, values("quote\""))` + = note: see for more information about checking conditional configuration + +warning: 7 warnings emitted diff --git a/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr index 755373d7b77a..2497864e87ec 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `unknown_key` LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` + = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(unknown_key, values("value"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr index 6344739ae767..a7d4c6d4df66 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `unknown_key` LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` + = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(unknown_key, values("value"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/exhaustive-names-values.full.stderr b/tests/ui/check-cfg/exhaustive-names-values.full.stderr index 6344739ae767..a7d4c6d4df66 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.full.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.full.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `unknown_key` LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` + = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(unknown_key, values("value"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/exhaustive-names.stderr b/tests/ui/check-cfg/exhaustive-names.stderr index 605825cd4e58..7ac3241db5f8 100644 --- a/tests/ui/check-cfg/exhaustive-names.stderr +++ b/tests/ui/check-cfg/exhaustive-names.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `unknown_key` LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` + = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(unknown_key, values("value"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/mix.stderr b/tests/ui/check-cfg/mix.stderr index 545916991453..b3d0046fc178 100644 --- a/tests/ui/check-cfg/mix.stderr +++ b/tests/ui/check-cfg/mix.stderr @@ -44,7 +44,7 @@ warning: unexpected `cfg` condition name: `uu` LL | #[cfg_attr(uu, test)] | ^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` + = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(uu)` = note: see for more information about checking conditional configuration @@ -251,7 +251,7 @@ warning: unexpected `cfg` condition value: `zebra` LL | cfg!(target_feature = "zebra"); | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2` and 188 more + = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2`, `bti`, and `bulk-memory` and 186 more = note: see for more information about checking conditional configuration warning: 27 warnings emitted diff --git a/tests/ui/check-cfg/stmt-no-ice.stderr b/tests/ui/check-cfg/stmt-no-ice.stderr index ab0deae428d1..e8b61d808fe5 100644 --- a/tests/ui/check-cfg/stmt-no-ice.stderr +++ b/tests/ui/check-cfg/stmt-no-ice.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `crossbeam_loom` LL | #[cfg(crossbeam_loom)] | ^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` + = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(crossbeam_loom)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/unexpected-cfg-value.stderr b/tests/ui/check-cfg/unexpected-cfg-value.stderr index efcf65bb707e..0fa0dde41b6d 100644 --- a/tests/ui/check-cfg/unexpected-cfg-value.stderr +++ b/tests/ui/check-cfg/unexpected-cfg-value.stderr @@ -6,7 +6,7 @@ LL | #[cfg(feature = "sedre")] | | | help: there is a expected value with a similar name: `"serde"` | - = note: expected values for `feature` are: `full`, `serde` + = note: expected values for `feature` are: `full` and `serde` = help: to expect this configuration use `--check-cfg=cfg(feature, values("sedre"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default @@ -17,7 +17,7 @@ warning: unexpected `cfg` condition value: `rand` LL | #[cfg(feature = "rand")] | ^^^^^^^^^^^^^^^^ | - = note: expected values for `feature` are: `full`, `serde` + = note: expected values for `feature` are: `full` and `serde` = help: to expect this configuration use `--check-cfg=cfg(feature, values("rand"))` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/well-known-names.stderr b/tests/ui/check-cfg/well-known-names.stderr index 2ce6fe80ef20..41130210df1e 100644 --- a/tests/ui/check-cfg/well-known-names.stderr +++ b/tests/ui/check-cfg/well-known-names.stderr @@ -18,7 +18,7 @@ warning: unexpected `cfg` condition name: `features` LL | #[cfg(features = "foo")] | ^^^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` + = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(features, values("foo"))` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index db4a5fd49e6a..d2026a68450e 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -71,7 +71,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | panic = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `panic` are: `abort`, `unwind` + = note: expected values for `panic` are: `abort` and `unwind` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -91,7 +91,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | relocation_model = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `relocation_model` are: `dynamic-no-pic`, `pic`, `pie`, `ropi`, `ropi-rwpi`, `rwpi`, `static` + = note: expected values for `relocation_model` are: `dynamic-no-pic`, `pic`, `pie`, `ropi`, `ropi-rwpi`, `rwpi`, and `static` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -111,7 +111,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | sanitize = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `sanitize` are: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, `thread` + = note: expected values for `sanitize` are: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, and `thread` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -120,7 +120,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_abi = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `elf`, `fortanix`, `ilp32`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, `x32` + = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `elf`, `fortanix`, `ilp32`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -129,7 +129,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_arch = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_arch` are: `aarch64`, `arm`, `arm64ec`, `avr`, `bpf`, `csky`, `hexagon`, `loongarch64`, `m68k`, `mips`, `mips32r6`, `mips64`, `mips64r6`, `msp430`, `nvptx64`, `powerpc`, `powerpc64`, `riscv32`, `riscv64`, `s390x`, `sparc`, `sparc64`, `wasm32`, `wasm64`, `x86`, `x86_64` + = note: expected values for `target_arch` are: `aarch64`, `arm`, `arm64ec`, `avr`, `bpf`, `csky`, `hexagon`, `loongarch64`, `m68k`, `mips`, `mips32r6`, `mips64`, `mips64r6`, `msp430`, `nvptx64`, `powerpc`, `powerpc64`, `riscv32`, `riscv64`, `s390x`, `sparc`, `sparc64`, `wasm32`, `wasm64`, `x86`, `x86_64`, and `xtensa` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -138,7 +138,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_endian = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_endian` are: `big`, `little` + = note: expected values for `target_endian` are: `big` and `little` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -147,7 +147,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_env = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_env` are: ``, `gnu`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `ohos`, `p2`, `psx`, `relibc`, `sgx`, `uclibc` + = note: expected values for `target_env` are: ``, `gnu`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `ohos`, `p1`, `p2`, `psx`, `relibc`, `sgx`, and `uclibc` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -156,7 +156,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_family = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_family` are: `unix`, `wasm`, `windows` + = note: expected values for `target_family` are: `unix`, `wasm`, and `windows` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -165,7 +165,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_feature = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2`, `bti`, `bulk-memory`, `c`, `cache`, `cmpxchg16b`, `crc`, `crt-static`, `d`, `d32`, `dit`, `doloop`, `dotprod`, `dpb`, `dpb2`, `dsp`, `dsp1e2`, `dspe60`, `e`, `e1`, `e2`, `edsp`, `elrw`, `ermsb`, `exception-handling`, `extended-const`, `f`, `f16c`, `f32mm`, `f64mm`, `fcma`, `fdivdu`, `fhm`, `flagm`, `float1e2`, `float1e3`, `float3e4`, `float7e60`, `floate1`, `fma`, `fp-armv8`, `fp16`, `fp64`, `fpuv2_df`, `fpuv2_sf`, `fpuv3_df`, `fpuv3_hf`, `fpuv3_hi`, `fpuv3_sf`, `frecipe`, `frintts`, `fxsr`, `gfni`, `hard-float`, `hard-float-abi`, `hard-tp`, `high-registers`, `hvx`, `hvx-length128b`, `hwdiv`, `i8mm`, `jsconv`, `lahfsahf`, `lasx`, `lbt`, `lor`, `lse`, `lsx`, `lvz`, `lzcnt`, `m`, `mclass`, `movbe`, `mp`, `mp1e2`, `msa`, `mte`, `multivalue`, `mutable-globals`, `neon`, `nontrapping-fptoint`, `nvic`, `paca`, `pacg`, `pan`, `pclmulqdq`, `pmuv3`, `popcnt`, `power10-vector`, `power8-altivec`, `power8-vector`, `power9-altivec`, `power9-vector`, `prfchw`, `rand`, `ras`, `rclass`, `rcpc`, `rcpc2`, `rdm`, `rdrand`, `rdseed`, `reference-types`, `relax`, `relaxed-simd`, `rtm`, `sb`, `sha`, `sha2`, `sha3`, `sign-ext`, `simd128`, `sm4`, `spe`, `ssbs`, `sse`, `sse2`, `sse3`, `sse4.1`, `sse4.2`, `sse4a`, `ssse3`, `sve`, `sve2`, `sve2-aes`, `sve2-bitperm`, `sve2-sha3`, `sve2-sm4`, `tbm`, `thumb-mode`, `thumb2`, `tme`, `trust`, `trustzone`, `ual`, `unaligned-scalar-mem`, `v`, `v5te`, `v6`, `v6k`, `v6t2`, `v7`, `v8`, `v8.1a`, `v8.2a`, `v8.3a`, `v8.4a`, `v8.5a`, `v8.6a`, `v8.7a`, `vaes`, `vdsp2e60f`, `vdspv1`, `vdspv2`, `vfp2`, `vfp3`, `vfp4`, `vh`, `virt`, `virtualization`, `vpclmulqdq`, `vsx`, `xsave`, `xsavec`, `xsaveopt`, `xsaves`, `zba`, `zbb`, `zbc`, `zbkb`, `zbkc`, `zbkx`, `zbs`, `zdinx`, `zfh`, `zfhmin`, `zfinx`, `zhinx`, `zhinxmin`, `zk`, `zkn`, `zknd`, `zkne`, `zknh`, `zkr`, `zks`, `zksed`, `zksh`, `zkt` + = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2`, `bti`, `bulk-memory`, `c`, `cache`, `cmpxchg16b`, `crc`, `crt-static`, `d`, `d32`, `dit`, `doloop`, `dotprod`, `dpb`, `dpb2`, `dsp`, `dsp1e2`, `dspe60`, `e`, `e1`, `e2`, `edsp`, `elrw`, `ermsb`, `exception-handling`, `extended-const`, `f`, `f16c`, `f32mm`, `f64mm`, `fcma`, `fdivdu`, `fhm`, `flagm`, `float1e2`, `float1e3`, `float3e4`, `float7e60`, `floate1`, `fma`, `fp-armv8`, `fp16`, `fp64`, `fpuv2_df`, `fpuv2_sf`, `fpuv3_df`, `fpuv3_hf`, `fpuv3_hi`, `fpuv3_sf`, `frecipe`, `frintts`, `fxsr`, `gfni`, `hard-float`, `hard-float-abi`, `hard-tp`, `high-registers`, `hvx`, `hvx-length128b`, `hwdiv`, `i8mm`, `jsconv`, `lahfsahf`, `lasx`, `lbt`, `lor`, `lse`, `lsx`, `lvz`, `lzcnt`, `m`, `mclass`, `movbe`, `mp`, `mp1e2`, `msa`, `mte`, `multivalue`, `mutable-globals`, `neon`, `nontrapping-fptoint`, `nvic`, `paca`, `pacg`, `pan`, `pclmulqdq`, `pmuv3`, `popcnt`, `power10-vector`, `power8-altivec`, `power8-vector`, `power9-altivec`, `power9-vector`, `prfchw`, `rand`, `ras`, `rclass`, `rcpc`, `rcpc2`, `rdm`, `rdrand`, `rdseed`, `reference-types`, `relax`, `relaxed-simd`, `rtm`, `sb`, `sha`, `sha2`, `sha3`, `sign-ext`, `simd128`, `sm4`, `spe`, `ssbs`, `sse`, `sse2`, `sse3`, `sse4.1`, `sse4.2`, `sse4a`, `ssse3`, `sve`, `sve2`, `sve2-aes`, `sve2-bitperm`, `sve2-sha3`, `sve2-sm4`, `tbm`, `thumb-mode`, `thumb2`, `tme`, `trust`, `trustzone`, `ual`, `unaligned-scalar-mem`, `v`, `v5te`, `v6`, `v6k`, `v6t2`, `v7`, `v8`, `v8.1a`, `v8.2a`, `v8.3a`, `v8.4a`, `v8.5a`, `v8.6a`, `v8.7a`, `vaes`, `vdsp2e60f`, `vdspv1`, `vdspv2`, `vfp2`, `vfp3`, `vfp4`, `vh`, `virt`, `virtualization`, `vpclmulqdq`, `vsx`, `xsave`, `xsavec`, `xsaveopt`, `xsaves`, `zba`, `zbb`, `zbc`, `zbkb`, `zbkc`, `zbkx`, `zbs`, `zdinx`, `zfh`, `zfhmin`, `zfinx`, `zhinx`, `zhinxmin`, `zk`, `zkn`, `zknd`, `zkne`, `zknh`, `zkr`, `zks`, `zksed`, `zksh`, and `zkt` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -174,7 +174,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_has_atomic = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_has_atomic` are: (none), `128`, `16`, `32`, `64`, `8`, `ptr` + = note: expected values for `target_has_atomic` are: (none), `128`, `16`, `32`, `64`, `8`, and `ptr` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -183,7 +183,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_has_atomic_equal_alignment = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_has_atomic_equal_alignment` are: (none), `128`, `16`, `32`, `64`, `8`, `ptr` + = note: expected values for `target_has_atomic_equal_alignment` are: (none), `128`, `16`, `32`, `64`, `8`, and `ptr` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -192,7 +192,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_has_atomic_load_store = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_has_atomic_load_store` are: (none), `128`, `16`, `32`, `64`, `8`, `ptr` + = note: expected values for `target_has_atomic_load_store` are: (none), `128`, `16`, `32`, `64`, `8`, and `ptr` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -201,7 +201,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_os = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `openbsd`, `psp`, `redox`, `solaris`, `solid_asp3`, `teeos`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, `zkvm` + = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `openbsd`, `psp`, `redox`, `solaris`, `solid_asp3`, `teeos`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -210,7 +210,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_pointer_width = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_pointer_width` are: `16`, `32`, `64` + = note: expected values for `target_pointer_width` are: `16`, `32`, and `64` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -230,7 +230,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_vendor = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, `wrs` + = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -285,7 +285,7 @@ LL | #[cfg(target_os = "linuz")] // testing that we suggest `linux` | | | help: there is a expected value with a similar name: `"linux"` | - = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `openbsd`, `psp`, `redox`, `solaris`, `solid_asp3`, `teeos`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, `zkvm` + = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `openbsd`, `psp`, `redox`, `solaris`, `solid_asp3`, `teeos`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see for more information about checking conditional configuration warning: 29 warnings emitted diff --git a/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs b/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs index 840eda0513f8..a0e8e614d8ee 100644 --- a/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs +++ b/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs @@ -2,8 +2,7 @@ //@ edition:2021 const PATTERN_REF: &str = "Hello World"; -const NUMBER: i32 = 30; -const NUMBER_POINTER: *const i32 = &NUMBER; +const NUMBER_POINTER: *const i32 = 30 as *const i32; pub fn edge_case_ref(event: &str) { let _ = || { @@ -26,8 +25,7 @@ pub fn edge_case_str(event: String) { pub fn edge_case_raw_ptr(event: *const i32) { let _ = || { match event { - NUMBER_POINTER => (), //~WARN behave unpredictably - //~| previously accepted + NUMBER_POINTER => (), _ => (), }; }; diff --git a/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_1.stderr b/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_1.stderr deleted file mode 100644 index 8a2aaade6650..000000000000 --- a/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_1.stderr +++ /dev/null @@ -1,23 +0,0 @@ -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/match-edge-cases_1.rs:29:13 - | -LL | NUMBER_POINTER => (), - | ^^^^^^^^^^^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -warning: 1 warning emitted - -Future incompatibility report: Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/match-edge-cases_1.rs:29:13 - | -LL | NUMBER_POINTER => (), - | ^^^^^^^^^^^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - diff --git a/tests/ui/closures/closure-expected.stderr b/tests/ui/closures/closure-expected.stderr index 6b309d70bdf1..53a93e1e84dd 100644 --- a/tests/ui/closures/closure-expected.stderr +++ b/tests/ui/closures/closure-expected.stderr @@ -6,7 +6,7 @@ LL | let y = x.or_else(4); | | | required by a bound introduced by this call | - = help: the trait `FnOnce<()>` is not implemented for `{integer}` + = help: the trait `FnOnce()` is not implemented for `{integer}` = note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `Option::::or_else` --> $SRC_DIR/core/src/option.rs:LL:COL diff --git a/tests/ui/closures/closure-return-type-mismatch.stderr b/tests/ui/closures/closure-return-type-mismatch.stderr index 3a89d30a05d2..3a2f098d1efb 100644 --- a/tests/ui/closures/closure-return-type-mismatch.stderr +++ b/tests/ui/closures/closure-return-type-mismatch.stderr @@ -13,6 +13,9 @@ LL | return "test"; error[E0308]: mismatched types --> $DIR/closure-return-type-mismatch.rs:12:20 | +LL | || -> bool { + | ---- expected `bool` because of return type +LL | if false { LL | return "hello" | ^^^^^^^ expected `bool`, found `&str` diff --git a/tests/ui/closures/coerce-unsafe-to-closure.stderr b/tests/ui/closures/coerce-unsafe-to-closure.stderr index 4841ff32e701..cb718ca160f8 100644 --- a/tests/ui/closures/coerce-unsafe-to-closure.stderr +++ b/tests/ui/closures/coerce-unsafe-to-closure.stderr @@ -6,7 +6,7 @@ LL | let x: Option<&[u8]> = Some("foo").map(std::mem::transmute); | | | required by a bound introduced by this call | - = help: the trait `FnOnce<(&str,)>` is not implemented for fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` + = help: the trait `FnOnce(&str)` is not implemented for fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` = note: unsafe function cannot be called generically without an unsafe block note: required by a bound in `Option::::map` --> $SRC_DIR/core/src/option.rs:LL:COL diff --git a/tests/ui/closures/wrong-closure-arg-suggestion-125325.rs b/tests/ui/closures/wrong-closure-arg-suggestion-125325.rs new file mode 100644 index 000000000000..ce575697cf68 --- /dev/null +++ b/tests/ui/closures/wrong-closure-arg-suggestion-125325.rs @@ -0,0 +1,29 @@ +// Regression test for #125325 + +// Tests that we suggest changing an `impl Fn` param +// to `impl FnMut` when the provided closure arg +// is trying to mutate the closure env. +// Ensures that it works that way for both +// functions and methods + +struct S; + +impl S { + fn assoc_func(&self, _f: impl Fn()) -> usize { + 0 + } +} + +fn func(_f: impl Fn()) -> usize { + 0 +} + +fn test_func(s: &S) -> usize { + let mut x = (); + s.assoc_func(|| x = ()); + //~^ cannot assign to `x`, as it is a captured variable in a `Fn` closure + func(|| x = ()) + //~^ cannot assign to `x`, as it is a captured variable in a `Fn` closure +} + +fn main() {} diff --git a/tests/ui/closures/wrong-closure-arg-suggestion-125325.stderr b/tests/ui/closures/wrong-closure-arg-suggestion-125325.stderr new file mode 100644 index 000000000000..e0cce8c4b314 --- /dev/null +++ b/tests/ui/closures/wrong-closure-arg-suggestion-125325.stderr @@ -0,0 +1,28 @@ +error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure + --> $DIR/wrong-closure-arg-suggestion-125325.rs:23:21 + | +LL | fn assoc_func(&self, _f: impl Fn()) -> usize { + | --------- change this to accept `FnMut` instead of `Fn` +... +LL | s.assoc_func(|| x = ()); + | --------------^^^^^^- + | | | | + | | | cannot assign + | | in this closure + | expects `Fn` instead of `FnMut` + +error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure + --> $DIR/wrong-closure-arg-suggestion-125325.rs:25:13 + | +LL | fn func(_f: impl Fn()) -> usize { + | --------- change this to accept `FnMut` instead of `Fn` +... +LL | func(|| x = ()) + | ---- -- ^^^^^^ cannot assign + | | | + | | in this closure + | expects `Fn` instead of `FnMut` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0594`. diff --git a/tests/ui/codegen/mono-impossible-drop.rs b/tests/ui/codegen/mono-impossible-drop.rs new file mode 100644 index 000000000000..dec013cfe54b --- /dev/null +++ b/tests/ui/codegen/mono-impossible-drop.rs @@ -0,0 +1,18 @@ +//@ compile-flags: -Clink-dead-code=on --crate-type=lib +//@ build-pass + +#![feature(trivial_bounds)] +#![allow(trivial_bounds)] + +// Make sure we don't monomorphize the drop impl for `Baz`, since it has predicates +// that don't hold under a reveal-all param env. + +trait Foo { + type Assoc; +} + +struct Bar; + +struct Baz(::Assoc) +where + Bar: Foo; diff --git a/tests/ui/coercion/coerce-issue-49593-box-never-windows.nofallback.stderr b/tests/ui/coercion/coerce-issue-49593-box-never-windows.nofallback.stderr deleted file mode 100644 index b976f70acf76..000000000000 --- a/tests/ui/coercion/coerce-issue-49593-box-never-windows.nofallback.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: the trait bound `(): std::error::Error` is not satisfied - --> $DIR/coerce-issue-49593-box-never-windows.rs:18:53 - | -LL | /* *mut $0 is coerced to Box here */ Box::<_ /* ! */>::new(x) - | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()` - | - = note: required for the cast from `Box<()>` to `Box<(dyn std::error::Error + 'static)>` - -error[E0277]: the trait bound `(): std::error::Error` is not satisfied - --> $DIR/coerce-issue-49593-box-never-windows.rs:23:49 - | -LL | /* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()` - | - = note: required for the cast from `*mut ()` to `*mut (dyn std::error::Error + 'static)` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coercion/coerce-issue-49593-box-never-windows.rs b/tests/ui/coercion/coerce-issue-49593-box-never-windows.rs deleted file mode 100644 index b317841ab6e7..000000000000 --- a/tests/ui/coercion/coerce-issue-49593-box-never-windows.rs +++ /dev/null @@ -1,58 +0,0 @@ -//@ revisions: nofallback fallback -//@ only-windows - the number of `Error` impls is platform-dependent -//@[fallback] check-pass -//@[nofallback] check-fail - -#![feature(never_type)] -#![cfg_attr(fallback, feature(never_type_fallback))] -#![allow(unreachable_code)] - -use std::error::Error; -use std::mem; - -fn raw_ptr_box(t: T) -> *mut T { - panic!() -} - -fn foo(x: !) -> Box { - /* *mut $0 is coerced to Box here */ Box::<_ /* ! */>::new(x) - //[nofallback]~^ ERROR trait bound `(): std::error::Error` is not satisfied -} - -fn foo_raw_ptr(x: !) -> *mut dyn Error { - /* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x) - //[nofallback]~^ ERROR trait bound `(): std::error::Error` is not satisfied -} - -fn no_coercion(d: *mut dyn Error) -> *mut dyn Error { - /* an unsize coercion won't compile here, and it is indeed not used - because there is nothing requiring the _ to be Sized */ - d as *mut _ -} - -trait Xyz {} -struct S; -struct T; -impl Xyz for S {} -impl Xyz for T {} - -fn foo_no_never() { - let mut x /* : Option */ = None; - let mut first_iter = false; - loop { - if !first_iter { - let y: Box - = /* Box<$0> is coerced to Box here */ Box::new(x.unwrap()); - } - - x = Some(S); - first_iter = true; - } - - let mut y : Option = None; - // assert types are equal - mem::swap(&mut x, &mut y); -} - -fn main() { -} diff --git a/tests/ui/coercion/coerce-issue-49593-box-never.fallback.stderr b/tests/ui/coercion/coerce-issue-49593-box-never.fallback.stderr new file mode 100644 index 000000000000..ef5633f71348 --- /dev/null +++ b/tests/ui/coercion/coerce-issue-49593-box-never.fallback.stderr @@ -0,0 +1,11 @@ +error[E0277]: the trait bound `(): std::error::Error` is not satisfied + --> $DIR/coerce-issue-49593-box-never.rs:18:5 + | +LL | Box::<_ /* ! */>::new(x) + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()` + | + = note: required for the cast from `Box<()>` to `Box<(dyn std::error::Error + 'static)>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr b/tests/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr index 0d98fa93e5a4..7222af43b013 100644 --- a/tests/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr +++ b/tests/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr @@ -1,16 +1,16 @@ error[E0277]: the trait bound `(): std::error::Error` is not satisfied - --> $DIR/coerce-issue-49593-box-never.rs:18:53 + --> $DIR/coerce-issue-49593-box-never.rs:18:5 | -LL | /* *mut $0 is coerced to Box here */ Box::<_ /* ! */>::new(x) - | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()` +LL | Box::<_ /* ! */>::new(x) + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()` | = note: required for the cast from `Box<()>` to `Box<(dyn std::error::Error + 'static)>` error[E0277]: the trait bound `(): std::error::Error` is not satisfied - --> $DIR/coerce-issue-49593-box-never.rs:23:49 + --> $DIR/coerce-issue-49593-box-never.rs:24:5 | -LL | /* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()` +LL | raw_ptr_box::<_ /* ! */>(x) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()` | = note: required for the cast from `*mut ()` to `*mut (dyn std::error::Error + 'static)` diff --git a/tests/ui/coercion/coerce-issue-49593-box-never.rs b/tests/ui/coercion/coerce-issue-49593-box-never.rs index 19a2c036fbcb..53071be47dcb 100644 --- a/tests/ui/coercion/coerce-issue-49593-box-never.rs +++ b/tests/ui/coercion/coerce-issue-49593-box-never.rs @@ -1,7 +1,5 @@ //@ revisions: nofallback fallback -//@ ignore-windows - the number of `Error` impls is platform-dependent -//@[fallback] check-pass -//@[nofallback] check-fail +//@check-fail #![feature(never_type)] #![cfg_attr(fallback, feature(never_type_fallback))] @@ -15,18 +13,21 @@ fn raw_ptr_box(t: T) -> *mut T { } fn foo(x: !) -> Box { - /* *mut $0 is coerced to Box here */ Box::<_ /* ! */>::new(x) - //[nofallback]~^ ERROR trait bound `(): std::error::Error` is not satisfied + // Subtyping during method resolution will generate new inference vars and + // subtype them. Thus fallback will not fall back to `!`, but `()` instead. + Box::<_ /* ! */>::new(x) + //~^ ERROR trait bound `(): std::error::Error` is not satisfied } fn foo_raw_ptr(x: !) -> *mut dyn Error { - /* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x) + /* *mut $0 is coerced to *mut Error here */ + raw_ptr_box::<_ /* ! */>(x) //[nofallback]~^ ERROR trait bound `(): std::error::Error` is not satisfied } fn no_coercion(d: *mut dyn Error) -> *mut dyn Error { /* an unsize coercion won't compile here, and it is indeed not used - because there is nothing requiring the _ to be Sized */ + because there is nothing requiring the _ to be Sized */ d as *mut _ } @@ -49,10 +50,9 @@ fn foo_no_never() { first_iter = true; } - let mut y : Option = None; + let mut y: Option = None; // assert types are equal mem::swap(&mut x, &mut y); } -fn main() { -} +fn main() {} diff --git a/tests/ui/coherence/associated-type2.rs b/tests/ui/coherence/associated-type2.rs index 2aadfb04af05..68e98b62953a 100644 --- a/tests/ui/coherence/associated-type2.rs +++ b/tests/ui/coherence/associated-type2.rs @@ -1,6 +1,6 @@ //! A regression test for #120343. The overlap error was previously //! silenced in coherence because projecting `<() as ToUnit>::Unit` -//! failed. Then then silenced the missing items error in the `ToUnit` +//! failed. Then silenced the missing items error in the `ToUnit` //! impl, causing us to not emit any errors and ICEing due to a //! `span_delay_bug`. diff --git a/tests/ui/auxiliary/orphan-check-diagnostics.rs b/tests/ui/coherence/auxiliary/orphan-check-diagnostics.rs similarity index 100% rename from tests/ui/auxiliary/orphan-check-diagnostics.rs rename to tests/ui/coherence/auxiliary/orphan-check-diagnostics.rs diff --git a/tests/ui/coherence/coherence-impls-copy.stderr b/tests/ui/coherence/coherence-impls-copy.stderr index 2d2c5064043a..f529a056b0fd 100644 --- a/tests/ui/coherence/coherence-impls-copy.stderr +++ b/tests/ui/coherence/coherence-impls-copy.stderr @@ -1,14 +1,3 @@ -error[E0117]: only traits defined in the current crate can be implemented for primitive types - --> $DIR/coherence-impls-copy.rs:5:1 - | -LL | impl Copy for i32 {} - | ^^^^^^^^^^^^^^--- - | | | - | | `i32` is not defined in the current crate - | impl doesn't use only types from inside the current crate - | - = note: define and implement a trait or new type instead - error[E0119]: conflicting implementations of trait `Copy` for type `&NotSync` --> $DIR/coherence-impls-copy.rs:28:1 | @@ -30,6 +19,17 @@ LL | impl Copy for &'static [NotSync] {} | = note: define and implement a trait or new type instead +error[E0117]: only traits defined in the current crate can be implemented for primitive types + --> $DIR/coherence-impls-copy.rs:5:1 + | +LL | impl Copy for i32 {} + | ^^^^^^^^^^^^^^--- + | | | + | | `i32` is not defined in the current crate + | impl doesn't use only types from inside the current crate + | + = note: define and implement a trait or new type instead + error[E0206]: the trait `Copy` cannot be implemented for this type --> $DIR/coherence-impls-copy.rs:21:15 | diff --git a/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.next.stderr b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.next.stderr index 49b236f9d2aa..781ab0fcbf76 100644 --- a/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.next.stderr +++ b/tests/ui/coherence/coherence-overlap-unnormalizable-projection-1.next.stderr @@ -12,7 +12,7 @@ LL | impl Trait for Box {} | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>` | = note: downstream crates may implement trait `WithAssoc<'a>` for type `std::boxed::Box<_>` - = note: downstream crates may implement trait `WhereBound` for type `std::boxed::Box< as WithAssoc<'a>>::Assoc>` + = note: downstream crates may implement trait `WhereBound` for type `std::boxed::Box<_>` error: aborting due to 1 previous error diff --git a/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.next.stderr b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.next.stderr index 6e41561f1a7b..b6636d4de86e 100644 --- a/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.next.stderr +++ b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.next.stderr @@ -1,8 +1,8 @@ -error[E0284]: type annotations needed: cannot satisfy ` as Object>::Output == T` +error[E0284]: type annotations needed: cannot normalize ` as Object>::Output` --> $DIR/indirect-impl-for-trait-obj-coherence.rs:25:41 | LL | foo::, U>(x) - | ^ cannot satisfy ` as Object>::Output == T` + | ^ cannot normalize ` as Object>::Output` error: aborting due to 1 previous error diff --git a/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs index 0b66a6e78303..abfd51c2008b 100644 --- a/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs +++ b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs @@ -23,7 +23,7 @@ fn foo(x: >::Output) -> U { #[allow(dead_code)] fn transmute(x: T) -> U { foo::, U>(x) - //[next]~^ ERROR type annotations needed: cannot satisfy ` as Object>::Output == T` + //[next]~^ ERROR type annotations needed: cannot normalize ` as Object>::Output` } fn main() {} diff --git a/tests/ui/coherence/negative-coherence/generic_const_type_mismatch.rs b/tests/ui/coherence/negative-coherence/generic_const_type_mismatch.rs new file mode 100644 index 000000000000..fa0b0fdc136a --- /dev/null +++ b/tests/ui/coherence/negative-coherence/generic_const_type_mismatch.rs @@ -0,0 +1,13 @@ +//! This test used to ICE (#119381), because relating the `u8` and `i8` generic +//! const with the array length of the `Self` type was succeeding under the +//! assumption that an error had already been reported. + +#![feature(with_negative_coherence)] +trait Trait {} +impl Trait for [(); N] {} +//~^ ERROR: mismatched types +impl Trait for [(); N] {} +//~^ ERROR: mismatched types +//~| ERROR: conflicting implementations of trait `Trait` + +fn main() {} diff --git a/tests/ui/coherence/negative-coherence/generic_const_type_mismatch.stderr b/tests/ui/coherence/negative-coherence/generic_const_type_mismatch.stderr new file mode 100644 index 000000000000..d65450845bc1 --- /dev/null +++ b/tests/ui/coherence/negative-coherence/generic_const_type_mismatch.stderr @@ -0,0 +1,25 @@ +error[E0119]: conflicting implementations of trait `Trait` for type `[(); _]` + --> $DIR/generic_const_type_mismatch.rs:9:1 + | +LL | impl Trait for [(); N] {} + | ----------------------------------- first implementation here +LL | +LL | impl Trait for [(); N] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `[(); _]` + +error[E0308]: mismatched types + --> $DIR/generic_const_type_mismatch.rs:7:34 + | +LL | impl Trait for [(); N] {} + | ^ expected `usize`, found `u8` + +error[E0308]: mismatched types + --> $DIR/generic_const_type_mismatch.rs:9:34 + | +LL | impl Trait for [(); N] {} + | ^ expected `usize`, found `i8` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0119, E0308. +For more information about an error, try `rustc --explain E0119`. diff --git a/tests/ui/coherence/occurs-check/associated-type.next.stderr b/tests/ui/coherence/occurs-check/associated-type.next.stderr index 7443459b8300..4f15be4c7c88 100644 --- a/tests/ui/coherence/occurs-check/associated-type.next.stderr +++ b/tests/ui/coherence/occurs-check/associated-type.next.stderr @@ -1,7 +1,7 @@ -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } error[E0119]: conflicting implementations of trait `Overlap fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())` --> $DIR/associated-type.rs:31:1 | @@ -16,11 +16,11 @@ LL | | for<'a> *const T: ToUnit<'a>, | = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details -error[E0284]: type annotations needed: cannot satisfy ` fn(&'a (), ()) as Overlap fn(&'a (), ())>>::Assoc == usize` +error[E0284]: type annotations needed: cannot normalize ` fn(&'a (), ()) as Overlap fn(&'a (), ())>>::Assoc` --> $DIR/associated-type.rs:44:59 | LL | foo:: fn(&'a (), ()), for<'a> fn(&'a (), ())>(3usize); - | ^^^^^^ cannot satisfy ` fn(&'a (), ()) as Overlap fn(&'a (), ())>>::Assoc == usize` + | ^^^^^^ cannot normalize ` fn(&'a (), ()) as Overlap fn(&'a (), ())>>::Assoc` error: aborting due to 2 previous errors diff --git a/tests/ui/coherence/occurs-check/associated-type.old.stderr b/tests/ui/coherence/occurs-check/associated-type.old.stderr index 38a02c906d4f..329086ab7dfd 100644 --- a/tests/ui/coherence/occurs-check/associated-type.old.stderr +++ b/tests/ui/coherence/occurs-check/associated-type.old.stderr @@ -1,11 +1,11 @@ -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) } error[E0119]: conflicting implementations of trait `Overlap fn(&'a (), _)>` for type `for<'a> fn(&'a (), _)` --> $DIR/associated-type.rs:31:1 | diff --git a/tests/ui/coherence/occurs-check/associated-type.rs b/tests/ui/coherence/occurs-check/associated-type.rs index ac1236c554af..df03d5f60a02 100644 --- a/tests/ui/coherence/occurs-check/associated-type.rs +++ b/tests/ui/coherence/occurs-check/associated-type.rs @@ -42,5 +42,5 @@ fn foo, U>(x: T::Assoc) -> T::Assoc { fn main() { foo:: fn(&'a (), ()), for<'a> fn(&'a (), ())>(3usize); - //[next]~^ ERROR: cannot satisfy + //[next]~^ ERROR: cannot normalize } diff --git a/tests/ui/coherence/occurs-check/opaques.next.stderr b/tests/ui/coherence/occurs-check/opaques.next.stderr index f6c5255a1869..11d1edcca2f9 100644 --- a/tests/ui/coherence/occurs-check/opaques.next.stderr +++ b/tests/ui/coherence/occurs-check/opaques.next.stderr @@ -11,7 +11,7 @@ error[E0282]: type annotations needed --> $DIR/opaques.rs:13:20 | LL | pub fn cast(x: Container, T>) -> Container { - | ^ cannot infer type for associated type `>::Assoc` + | ^ cannot infer type error: aborting due to 2 previous errors diff --git a/tests/ui/orphan-check-diagnostics.rs b/tests/ui/coherence/orphan-check-diagnostics.rs similarity index 100% rename from tests/ui/orphan-check-diagnostics.rs rename to tests/ui/coherence/orphan-check-diagnostics.rs diff --git a/tests/ui/orphan-check-diagnostics.stderr b/tests/ui/coherence/orphan-check-diagnostics.stderr similarity index 100% rename from tests/ui/orphan-check-diagnostics.stderr rename to tests/ui/coherence/orphan-check-diagnostics.stderr diff --git a/tests/ui/coherence/orphan-check-opaque-types-not-covering.classic.stderr b/tests/ui/coherence/orphan-check-opaque-types-not-covering.classic.stderr new file mode 100644 index 000000000000..44f76f321cf1 --- /dev/null +++ b/tests/ui/coherence/orphan-check-opaque-types-not-covering.classic.stderr @@ -0,0 +1,21 @@ +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-opaque-types-not-covering.rs:17:6 + | +LL | impl foreign::Trait0 for Identity {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-opaque-types-not-covering.rs:26:6 + | +LL | impl foreign::Trait1 for Opaque {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0210`. diff --git a/tests/ui/coherence/orphan-check-opaque-types-not-covering.next.stderr b/tests/ui/coherence/orphan-check-opaque-types-not-covering.next.stderr new file mode 100644 index 000000000000..44f76f321cf1 --- /dev/null +++ b/tests/ui/coherence/orphan-check-opaque-types-not-covering.next.stderr @@ -0,0 +1,21 @@ +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-opaque-types-not-covering.rs:17:6 + | +LL | impl foreign::Trait0 for Identity {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + --> $DIR/orphan-check-opaque-types-not-covering.rs:26:6 + | +LL | impl foreign::Trait1 for Opaque {} + | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0210`. diff --git a/tests/ui/coherence/orphan-check-opaque-types-not-covering.rs b/tests/ui/coherence/orphan-check-opaque-types-not-covering.rs new file mode 100644 index 000000000000..8dc02b081c51 --- /dev/null +++ b/tests/ui/coherence/orphan-check-opaque-types-not-covering.rs @@ -0,0 +1,31 @@ +// Opaque types never cover type parameters. + +//@ revisions: classic next +//@[next] compile-flags: -Znext-solver + +//@ aux-crate:foreign=parametrized-trait.rs +//@ edition:2021 + +#![feature(type_alias_impl_trait)] + +type Identity = impl Sized; + +fn define_identity(x: T) -> Identity { + x +} + +impl foreign::Trait0 for Identity {} +//~^ ERROR type parameter `T` must be covered by another type + +type Opaque = impl Sized; + +fn define_local() -> Opaque { + Local +} + +impl foreign::Trait1 for Opaque {} +//~^ ERROR type parameter `T` must be covered by another type + +struct Local; + +fn main() {} diff --git a/tests/ui/coherence/re-rebalance-coherence.rs b/tests/ui/coherence/re-rebalance-coherence.rs index 9c176d5b1b12..5383a634617f 100644 --- a/tests/ui/coherence/re-rebalance-coherence.rs +++ b/tests/ui/coherence/re-rebalance-coherence.rs @@ -4,6 +4,7 @@ extern crate re_rebalance_coherence_lib as lib; use lib::*; +#[allow(dead_code)] struct Oracle; impl Backend for Oracle {} impl<'a, T:'a, Tab> QueryFragment for BatchInsert<'a, T, Tab> {} diff --git a/tests/crashes/114456.rs b/tests/ui/const-generics/adt_const_params/alias_const_param_ty-1.rs similarity index 69% rename from tests/crashes/114456.rs rename to tests/ui/const-generics/adt_const_params/alias_const_param_ty-1.rs index e347327e7386..8035fce09147 100644 --- a/tests/crashes/114456.rs +++ b/tests/ui/const-generics/adt_const_params/alias_const_param_ty-1.rs @@ -1,5 +1,7 @@ -//@ known-bug: #114456 +//@ check-pass #![feature(adt_const_params, lazy_type_alias)] +//~^ WARN: the feature `adt_const_params` is incomplete +//~| WARN: the feature `lazy_type_alias` is incomplete pub type Matrix = [usize; 1]; const EMPTY_MATRIX: Matrix = [0; 1]; diff --git a/tests/ui/const-generics/adt_const_params/alias_const_param_ty-1.stderr b/tests/ui/const-generics/adt_const_params/alias_const_param_ty-1.stderr new file mode 100644 index 000000000000..5c6981077b2d --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/alias_const_param_ty-1.stderr @@ -0,0 +1,19 @@ +warning: the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/alias_const_param_ty-1.rs:2:12 + | +LL | #![feature(adt_const_params, lazy_type_alias)] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #95174 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: the feature `lazy_type_alias` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/alias_const_param_ty-1.rs:2:30 + | +LL | #![feature(adt_const_params, lazy_type_alias)] + | ^^^^^^^^^^^^^^^ + | + = note: see issue #112792 for more information + +warning: 2 warnings emitted + diff --git a/tests/crashes/114456-2.rs b/tests/ui/const-generics/adt_const_params/alias_const_param_ty-2.rs similarity index 64% rename from tests/crashes/114456-2.rs rename to tests/ui/const-generics/adt_const_params/alias_const_param_ty-2.rs index eca27febb968..a576b75341cf 100644 --- a/tests/crashes/114456-2.rs +++ b/tests/ui/const-generics/adt_const_params/alias_const_param_ty-2.rs @@ -1,5 +1,6 @@ -//@ known-bug: #114456 +//@ check-pass #![feature(adt_const_params)] +//~^ WARN: the feature `adt_const_params` is incomplete const EMPTY_MATRIX: ::Matrix = [0; 1]; @@ -12,8 +13,12 @@ impl Walk { } pub enum Type {} -pub trait Trait { type Matrix; } -impl Trait for Type { type Matrix = [usize; 1]; } +pub trait Trait { + type Matrix; +} +impl Trait for Type { + type Matrix = [usize; 1]; +} fn main() { let _ = Walk::new(); diff --git a/tests/ui/const-generics/adt_const_params/alias_const_param_ty-2.stderr b/tests/ui/const-generics/adt_const_params/alias_const_param_ty-2.stderr new file mode 100644 index 000000000000..dbc8ab716365 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/alias_const_param_ty-2.stderr @@ -0,0 +1,11 @@ +warning: the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/alias_const_param_ty-2.rs:2:12 + | +LL | #![feature(adt_const_params)] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #95174 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/const-generics/adt_const_params/opaque_type_with_non-universal_region_substs_ice-111911.rs b/tests/ui/const-generics/adt_const_params/opaque_type_with_non-universal_region_substs_ice-111911.rs index 05bd0d91168a..c55f3dcec68c 100644 --- a/tests/ui/const-generics/adt_const_params/opaque_type_with_non-universal_region_substs_ice-111911.rs +++ b/tests/ui/const-generics/adt_const_params/opaque_type_with_non-universal_region_substs_ice-111911.rs @@ -1,4 +1,5 @@ //@ edition:2021 +//@ check-pass // issues rust-lang/rust#111911 // test for ICE opaque type with non-universal region substs @@ -6,8 +7,6 @@ #![allow(incomplete_features)] pub async fn foo() {} -//~^ ERROR const parameter `X` is part of concrete type but not used in parameter list for the `impl Trait` type alias -//~| ERROR const parameter `X` is part of concrete type but not used in parameter list for the `impl Trait` type alias fn bar() -> impl Sized {} pub fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/opaque_type_with_non-universal_region_substs_ice-111911.stderr b/tests/ui/const-generics/adt_const_params/opaque_type_with_non-universal_region_substs_ice-111911.stderr deleted file mode 100644 index 1bdb9cd9501a..000000000000 --- a/tests/ui/const-generics/adt_const_params/opaque_type_with_non-universal_region_substs_ice-111911.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: const parameter `X` is part of concrete type but not used in parameter list for the `impl Trait` type alias - --> $DIR/opaque_type_with_non-universal_region_substs_ice-111911.rs:8:43 - | -LL | pub async fn foo() {} - | ^^ - -error: const parameter `X` is part of concrete type but not used in parameter list for the `impl Trait` type alias - --> $DIR/opaque_type_with_non-universal_region_substs_ice-111911.rs:8:43 - | -LL | pub async fn foo() {} - | ^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors - diff --git a/tests/ui/const-generics/adt_const_params/transmutable-ice-110969.rs b/tests/ui/const-generics/adt_const_params/transmutable-ice-110969.rs index 68b8b4898168..569e57fa3262 100644 --- a/tests/ui/const-generics/adt_const_params/transmutable-ice-110969.rs +++ b/tests/ui/const-generics/adt_const_params/transmutable-ice-110969.rs @@ -24,7 +24,6 @@ fn via_associated_const() { trait Trait { const FALSE: bool = assert::is_transmutable::(); //~^ ERROR mismatched types - //~| ERROR `Src` cannot be safely transmuted into `Dst` //~| ERROR mismatched types } } diff --git a/tests/ui/const-generics/adt_const_params/transmutable-ice-110969.stderr b/tests/ui/const-generics/adt_const_params/transmutable-ice-110969.stderr index 1dbacaee3c2d..a8fc742e89f5 100644 --- a/tests/ui/const-generics/adt_const_params/transmutable-ice-110969.stderr +++ b/tests/ui/const-generics/adt_const_params/transmutable-ice-110969.stderr @@ -12,28 +12,13 @@ error[E0308]: mismatched types LL | const FALSE: bool = assert::is_transmutable::(); | ^^ expected `Assume`, found `()` -error[E0277]: `Src` cannot be safely transmuted into `Dst` - --> $DIR/transmutable-ice-110969.rs:25:60 - | -LL | const FALSE: bool = assert::is_transmutable::(); - | ^^^ `Dst` may carry safety invariants - | -note: required by a bound in `is_transmutable` - --> $DIR/transmutable-ice-110969.rs:11:14 - | -LL | pub fn is_transmutable() - | --------------- required by a bound in this function -LL | where -LL | Dst: BikeshedIntrinsicFrom, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` - error[E0308]: mismatched types --> $DIR/transmutable-ice-110969.rs:25:29 | LL | const FALSE: bool = assert::is_transmutable::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0107, E0277, E0308. +Some errors have detailed explanations: E0107, E0308. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/const-generics/bad-subst-const-kind.rs b/tests/ui/const-generics/bad-subst-const-kind.rs index ca5522a2ddf3..d5913879191e 100644 --- a/tests/ui/const-generics/bad-subst-const-kind.rs +++ b/tests/ui/const-generics/bad-subst-const-kind.rs @@ -10,5 +10,7 @@ impl Q for [u8; N] { const ASSOC: usize = 1; } -pub fn test() -> [u8; <[u8; 13] as Q>::ASSOC] { todo!() } -//~^ ERROR: the constant `13` is not of type `u64` +pub fn test() -> [u8; <[u8; 13] as Q>::ASSOC] { + //~^ ERROR: the constant `13` is not of type `u64` + todo!() +} diff --git a/tests/ui/const-generics/bad-subst-const-kind.stderr b/tests/ui/const-generics/bad-subst-const-kind.stderr index c04a05573bed..6725f6762e45 100644 --- a/tests/ui/const-generics/bad-subst-const-kind.stderr +++ b/tests/ui/const-generics/bad-subst-const-kind.stderr @@ -1,7 +1,7 @@ error: the constant `13` is not of type `u64` --> $DIR/bad-subst-const-kind.rs:13:24 | -LL | pub fn test() -> [u8; <[u8; 13] as Q>::ASSOC] { todo!() } +LL | pub fn test() -> [u8; <[u8; 13] as Q>::ASSOC] { | ^^^^^^^^ expected `u64`, found `usize` | note: required for `[u8; 13]` to implement `Q` diff --git a/tests/ui/const-generics/const-param-type-depends-on-type-param.full.stderr b/tests/ui/const-generics/const-param-type-depends-on-type-param.full.stderr index 2bfa604fc567..49cf42813235 100644 --- a/tests/ui/const-generics/const-param-type-depends-on-type-param.full.stderr +++ b/tests/ui/const-generics/const-param-type-depends-on-type-param.full.stderr @@ -6,6 +6,16 @@ LL | pub struct Dependent([(); X]); | = note: type parameters may not be used in the type of const parameters -error: aborting due to 1 previous error +error[E0392]: type parameter `T` is never used + --> $DIR/const-param-type-depends-on-type-param.rs:11:22 + | +LL | pub struct Dependent([(); X]); + | ^ unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead -For more information about this error, try `rustc --explain E0770`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0392, E0770. +For more information about an error, try `rustc --explain E0392`. diff --git a/tests/ui/const-generics/const-param-type-depends-on-type-param.min.stderr b/tests/ui/const-generics/const-param-type-depends-on-type-param.min.stderr index 2bfa604fc567..49cf42813235 100644 --- a/tests/ui/const-generics/const-param-type-depends-on-type-param.min.stderr +++ b/tests/ui/const-generics/const-param-type-depends-on-type-param.min.stderr @@ -6,6 +6,16 @@ LL | pub struct Dependent([(); X]); | = note: type parameters may not be used in the type of const parameters -error: aborting due to 1 previous error +error[E0392]: type parameter `T` is never used + --> $DIR/const-param-type-depends-on-type-param.rs:11:22 + | +LL | pub struct Dependent([(); X]); + | ^ unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead -For more information about this error, try `rustc --explain E0770`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0392, E0770. +For more information about an error, try `rustc --explain E0392`. diff --git a/tests/ui/const-generics/const-param-type-depends-on-type-param.rs b/tests/ui/const-generics/const-param-type-depends-on-type-param.rs index 1583fc4ee6c8..5503b08930b0 100644 --- a/tests/ui/const-generics/const-param-type-depends-on-type-param.rs +++ b/tests/ui/const-generics/const-param-type-depends-on-type-param.rs @@ -10,5 +10,6 @@ pub struct Dependent([(); X]); //~^ ERROR: the type of const parameters must not depend on other generic parameters +//~| ERROR: type parameter `T` is never used fn main() {} diff --git a/tests/ui/const-generics/defaults/doesnt_infer.rs b/tests/ui/const-generics/defaults/doesnt_infer.rs index e14c08fc1487..016685eee9df 100644 --- a/tests/ui/const-generics/defaults/doesnt_infer.rs +++ b/tests/ui/const-generics/defaults/doesnt_infer.rs @@ -3,7 +3,9 @@ struct Foo; impl Foo { - fn foo() -> Self { loop {} } + fn foo() -> Self { + loop {} + } } fn main() { diff --git a/tests/ui/const-generics/defaults/doesnt_infer.stderr b/tests/ui/const-generics/defaults/doesnt_infer.stderr index 93d586033971..1e779f75ce08 100644 --- a/tests/ui/const-generics/defaults/doesnt_infer.stderr +++ b/tests/ui/const-generics/defaults/doesnt_infer.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed for `Foo<_>` - --> $DIR/doesnt_infer.rs:11:9 + --> $DIR/doesnt_infer.rs:13:9 | LL | let foo = Foo::foo(); | ^^^ diff --git a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs index c23187598bce..4bf2fa761eae 100644 --- a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs +++ b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs @@ -2,6 +2,7 @@ //@ run-pass +#[allow(dead_code)] #[repr(C)] pub struct Loaf { head: [T; N], diff --git a/tests/ui/const-generics/exhaustive-value.stderr b/tests/ui/const-generics/exhaustive-value.stderr index acaa0cf1a508..791bb19ffe86 100644 --- a/tests/ui/const-generics/exhaustive-value.stderr +++ b/tests/ui/const-generics/exhaustive-value.stderr @@ -5,14 +5,14 @@ LL | <() as Foo>::test() | ^^ the trait `Foo` is not implemented for `()` | = help: the following other types implement trait `Foo`: - <() as Foo<0>> - <() as Foo<100>> - <() as Foo<101>> - <() as Foo<102>> - <() as Foo<103>> - <() as Foo<104>> - <() as Foo<105>> - <() as Foo<106>> + `()` implements `Foo<0>` + `()` implements `Foo<100>` + `()` implements `Foo<101>` + `()` implements `Foo<102>` + `()` implements `Foo<103>` + `()` implements `Foo<104>` + `()` implements `Foo<105>` + `()` implements `Foo<106>` and 248 others error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/generic_arg_infer/issue-91614.rs b/tests/ui/const-generics/generic_arg_infer/issue-91614.rs index b45e2cbc7372..cfbc5faecd9c 100644 --- a/tests/ui/const-generics/generic_arg_infer/issue-91614.rs +++ b/tests/ui/const-generics/generic_arg_infer/issue-91614.rs @@ -4,5 +4,5 @@ use std::simd::Mask; fn main() { let y = Mask::<_, _>::splat(false); - //~^ ERROR: type annotations needed for + //~^ ERROR: type annotations needed } diff --git a/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr b/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr index 5ee42c19dd3d..563406ad5eaa 100644 --- a/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr +++ b/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr @@ -15,7 +15,7 @@ note: required by a bound in `Mask::::splat` --> $SRC_DIR/core/src/../../portable-simd/crates/core_simd/src/masks.rs:LL:COL help: consider giving `y` an explicit type, where the type for type parameter `T` is specified | -LL | let y: Mask<_, N> = Mask::<_, _>::splat(false); +LL | let y: Mask = Mask::<_, _>::splat(false); | ++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/generic_const_exprs/associated-consts.rs b/tests/ui/const-generics/generic_const_exprs/associated-consts.rs index 5d2198f50ad9..50a6102c605a 100644 --- a/tests/ui/const-generics/generic_const_exprs/associated-consts.rs +++ b/tests/ui/const-generics/generic_const_exprs/associated-consts.rs @@ -16,7 +16,8 @@ impl BlockCipher for BarCipher { const BLOCK_SIZE: usize = 32; } -pub struct Block(#[allow(dead_code)] C); +#[allow(dead_code)] +pub struct Block(C); pub fn test() where diff --git a/tests/ui/const-generics/generic_const_exprs/double-opaque-parent-predicates.rs b/tests/ui/const-generics/generic_const_exprs/double-opaque-parent-predicates.rs new file mode 100644 index 000000000000..e48d559aa327 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/double-opaque-parent-predicates.rs @@ -0,0 +1,13 @@ +//@ check-pass + +#![feature(generic_const_exprs)] +//~^ WARN the feature `generic_const_exprs` is incomplete and may not be safe to use + +pub fn y<'a, U: 'a>() -> impl IntoIterator + 'a> { + [[[1, 2, 3]]] +} +// Make sure that the `predicates_of` for `{ 1 + 2 }` don't mention the duplicated lifetimes of +// the *outer* iterator. Whether they should mention the duplicated lifetimes of the *inner* +// iterator are another question, but not really something we need to answer immediately. + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/double-opaque-parent-predicates.stderr b/tests/ui/const-generics/generic_const_exprs/double-opaque-parent-predicates.stderr new file mode 100644 index 000000000000..faaede13e6b6 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/double-opaque-parent-predicates.stderr @@ -0,0 +1,11 @@ +warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/double-opaque-parent-predicates.rs:3:12 + | +LL | #![feature(generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #76560 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/const-generics/generic_const_exprs/error_in_ty.rs b/tests/ui/const-generics/generic_const_exprs/error_in_ty.rs new file mode 100644 index 000000000000..29ad935c0149 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/error_in_ty.rs @@ -0,0 +1,21 @@ +//@ compile-flags: -Znext-solver=coherence + +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +pub struct A {} +//~^ ERROR: cannot find value `x` in this scope +//~| ERROR: `[usize; x]` is forbidden as the type of a const generic parameter + +impl A<2> { + //~^ ERROR: mismatched types + pub const fn B() {} + //~^ ERROR: duplicate definitions +} + +impl A<2> { + //~^ ERROR: mismatched types + pub const fn B() {} +} + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/error_in_ty.stderr b/tests/ui/const-generics/generic_const_exprs/error_in_ty.stderr new file mode 100644 index 000000000000..0e40255bcf59 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/error_in_ty.stderr @@ -0,0 +1,45 @@ +error[E0425]: cannot find value `x` in this scope + --> $DIR/error_in_ty.rs:6:31 + | +LL | pub struct A {} + | - ^ help: a const parameter with a similar name exists: `z` + | | + | similarly named const parameter `z` defined here + +error: `[usize; x]` is forbidden as the type of a const generic parameter + --> $DIR/error_in_ty.rs:6:23 + | +LL | pub struct A {} + | ^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` +help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types + | +LL + #![feature(adt_const_params)] + | + +error[E0308]: mismatched types + --> $DIR/error_in_ty.rs:10:8 + | +LL | impl A<2> { + | ^ expected `[usize; x]`, found integer + +error[E0308]: mismatched types + --> $DIR/error_in_ty.rs:16:8 + | +LL | impl A<2> { + | ^ expected `[usize; x]`, found integer + +error[E0592]: duplicate definitions with name `B` + --> $DIR/error_in_ty.rs:12:5 + | +LL | pub const fn B() {} + | ^^^^^^^^^^^^^^^^ duplicate definitions for `B` +... +LL | pub const fn B() {} + | ---------------- other definition for `B` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0308, E0425, E0592. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/generic_const_exprs/eval-privacy.rs b/tests/ui/const-generics/generic_const_exprs/eval-privacy.rs index 8023b998a409..b662939c27eb 100644 --- a/tests/ui/const-generics/generic_const_exprs/eval-privacy.rs +++ b/tests/ui/const-generics/generic_const_exprs/eval-privacy.rs @@ -9,12 +9,14 @@ pub trait Trait { fn assoc_fn() -> Self::AssocTy; } -impl Trait for Const // OK, trait impl predicates +impl Trait for Const where - Const<{ my_const_fn(U) }>: , + // OK, trait impl predicates + Const<{ my_const_fn(U) }>:, { type AssocTy = Const<{ my_const_fn(U) }>; //~^ ERROR private type + //~| ERROR private type fn assoc_fn() -> Self::AssocTy { Const } diff --git a/tests/ui/const-generics/generic_const_exprs/eval-privacy.stderr b/tests/ui/const-generics/generic_const_exprs/eval-privacy.stderr index 043fa34d605a..98dac313be41 100644 --- a/tests/ui/const-generics/generic_const_exprs/eval-privacy.stderr +++ b/tests/ui/const-generics/generic_const_exprs/eval-privacy.stderr @@ -1,5 +1,5 @@ error[E0446]: private type `fn(u8) -> u8 {my_const_fn}` in public interface - --> $DIR/eval-privacy.rs:16:5 + --> $DIR/eval-privacy.rs:17:5 | LL | type AssocTy = Const<{ my_const_fn(U) }>; | ^^^^^^^^^^^^ can't leak private type @@ -7,6 +7,17 @@ LL | type AssocTy = Const<{ my_const_fn(U) }>; LL | const fn my_const_fn(val: u8) -> u8 { | ----------------------------------- `fn(u8) -> u8 {my_const_fn}` declared as private -error: aborting due to 1 previous error +error[E0446]: private type `fn(u8) -> u8 {my_const_fn}` in public interface + --> $DIR/eval-privacy.rs:17:5 + | +LL | type AssocTy = Const<{ my_const_fn(U) }>; + | ^^^^^^^^^^^^ can't leak private type +... +LL | const fn my_const_fn(val: u8) -> u8 { + | ----------------------------------- `fn(u8) -> u8 {my_const_fn}` declared as private + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0446`. diff --git a/tests/ui/const-generics/generic_const_exprs/object-safety-err-where-bounds.rs b/tests/ui/const-generics/generic_const_exprs/object-safety-err-where-bounds.rs index 42c1cc507b5c..b3bbb8426386 100644 --- a/tests/ui/const-generics/generic_const_exprs/object-safety-err-where-bounds.rs +++ b/tests/ui/const-generics/generic_const_exprs/object-safety-err-where-bounds.rs @@ -1,14 +1,11 @@ #![feature(generic_const_exprs)] #![allow(incomplete_features)] -#![deny(where_clauses_object_safety)] const fn bar() -> usize { 7 } trait Foo { fn test(&self) where [u8; bar::()]: Sized; - //~^ ERROR the trait `Foo` cannot be made into an object - //~| WARN this was previously accepted by the compiler but is being phased out } impl Foo for () { @@ -16,7 +13,9 @@ impl Foo for () { } fn use_dyn(v: &dyn Foo) { + //~^ ERROR the trait `Foo` cannot be made into an object v.test(); + //~^ ERROR the trait `Foo` cannot be made into an object } fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/object-safety-err-where-bounds.stderr b/tests/ui/const-generics/generic_const_exprs/object-safety-err-where-bounds.stderr index 9e480ce9b856..fde5d3ce7725 100644 --- a/tests/ui/const-generics/generic_const_exprs/object-safety-err-where-bounds.stderr +++ b/tests/ui/const-generics/generic_const_exprs/object-safety-err-where-bounds.stderr @@ -1,24 +1,35 @@ -error: the trait `Foo` cannot be made into an object - --> $DIR/object-safety-err-where-bounds.rs:9:8 +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/object-safety-err-where-bounds.rs:15:16 | -LL | fn test(&self) where [u8; bar::()]: Sized; - | ^^^^ +LL | fn use_dyn(v: &dyn Foo) { + | ^^^^^^^ `Foo` cannot be made into an object | - = warning: 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 #51443 note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/object-safety-err-where-bounds.rs:9:8 + --> $DIR/object-safety-err-where-bounds.rs:8:8 | LL | trait Foo { | --- this trait cannot be made into an object... LL | fn test(&self) where [u8; bar::()]: Sized; | ^^^^ ...because method `test` references the `Self` type in its `where` clause = help: consider moving `test` to another trait -note: the lint level is defined here - --> $DIR/object-safety-err-where-bounds.rs:3:9 + = help: only type `()` implements the trait, consider using it directly instead + +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/object-safety-err-where-bounds.rs:17:5 | -LL | #![deny(where_clauses_object_safety)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | v.test(); + | ^^^^^^^^ `Foo` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/object-safety-err-where-bounds.rs:8:8 + | +LL | trait Foo { + | --- this trait cannot be made into an object... +LL | fn test(&self) where [u8; bar::()]: Sized; + | ^^^^ ...because method `test` references the `Self` type in its `where` clause + = help: consider moving `test` to another trait + = help: only type `()` implements the trait, consider using it directly instead -error: aborting due to 1 previous error +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/const-generics/issues/issue-105821.rs b/tests/ui/const-generics/issues/issue-105821.rs index a0a98103b2cd..ecbae4d9f35c 100644 --- a/tests/ui/const-generics/issues/issue-105821.rs +++ b/tests/ui/const-generics/issues/issue-105821.rs @@ -1,4 +1,7 @@ //@ check-pass +// If this test starts failing because it ICEs due to not being able to convert a `ReErased` to +// something then feel free to just convert this to a known-bug. I'm pretty sure this is still +// a failing test, we just started masking the bug. #![allow(incomplete_features)] #![feature(adt_const_params, generic_const_exprs)] diff --git a/tests/ui/const-generics/issues/issue-71381.full.stderr b/tests/ui/const-generics/issues/issue-71381.full.stderr index 5d7800746961..05e847cf4c84 100644 --- a/tests/ui/const-generics/issues/issue-71381.full.stderr +++ b/tests/ui/const-generics/issues/issue-71381.full.stderr @@ -7,13 +7,25 @@ LL | pub fn call_me $DIR/issue-71381.rs:22:40 + --> $DIR/issue-71381.rs:24:40 | LL | const FN: unsafe extern "C" fn(Args), | ^^^^ the type must not depend on the parameter `Args` | = note: type parameters may not be used in the type of const parameters -error: aborting due to 2 previous errors +error[E0594]: cannot assign to `self.0`, which is behind a `&` reference + --> $DIR/issue-71381.rs:17:9 + | +LL | self.0 = Self::trampiline:: as _ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be written + | +help: consider changing this to be a mutable reference + | +LL | pub fn call_me(&mut self) { + | ~~~~~~~~~ -For more information about this error, try `rustc --explain E0770`. +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0594, E0770. +For more information about an error, try `rustc --explain E0594`. diff --git a/tests/ui/const-generics/issues/issue-71381.min.stderr b/tests/ui/const-generics/issues/issue-71381.min.stderr index 5d7800746961..1c30e885d1b7 100644 --- a/tests/ui/const-generics/issues/issue-71381.min.stderr +++ b/tests/ui/const-generics/issues/issue-71381.min.stderr @@ -7,13 +7,41 @@ LL | pub fn call_me $DIR/issue-71381.rs:22:40 + --> $DIR/issue-71381.rs:24:40 | LL | const FN: unsafe extern "C" fn(Args), | ^^^^ the type must not depend on the parameter `Args` | = note: type parameters may not be used in the type of const parameters -error: aborting due to 2 previous errors +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-71381.rs:14:61 + | +LL | pub fn call_me(&self) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` -For more information about this error, try `rustc --explain E0770`. +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-71381.rs:24:19 + | +LL | const FN: unsafe extern "C" fn(Args), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + +error[E0594]: cannot assign to `self.0`, which is behind a `&` reference + --> $DIR/issue-71381.rs:17:9 + | +LL | self.0 = Self::trampiline:: as _ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be written + | +help: consider changing this to be a mutable reference + | +LL | pub fn call_me(&mut self) { + | ~~~~~~~~~ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0594, E0770. +For more information about an error, try `rustc --explain E0594`. diff --git a/tests/ui/const-generics/issues/issue-71381.rs b/tests/ui/const-generics/issues/issue-71381.rs index 7f2e14944e29..166b724a7a33 100644 --- a/tests/ui/const-generics/issues/issue-71381.rs +++ b/tests/ui/const-generics/issues/issue-71381.rs @@ -13,7 +13,9 @@ unsafe extern "C" fn pass(args: PassArg) { impl Test { pub fn call_me(&self) { //~^ ERROR: the type of const parameters must not depend on other generic parameters + //[min]~^^ ERROR: using function pointers as const generic parameters is forbidden self.0 = Self::trampiline:: as _ + //~^ ERROR: cannot assign to `self.0` } unsafe extern "C" fn trampiline< @@ -21,6 +23,7 @@ impl Test { const IDX: usize, const FN: unsafe extern "C" fn(Args), //~^ ERROR: the type of const parameters must not depend on other generic parameters + //[min]~^^ ERROR: using function pointers as const generic parameters is forbidden >( args: Args, ) { diff --git a/tests/ui/const-generics/issues/issue-71611.min.stderr b/tests/ui/const-generics/issues/issue-71611.min.stderr index 6f6a9fc21a69..b01936f4d251 100644 --- a/tests/ui/const-generics/issues/issue-71611.min.stderr +++ b/tests/ui/const-generics/issues/issue-71611.min.stderr @@ -6,6 +6,14 @@ LL | fn func(outer: A) { | = note: type parameters may not be used in the type of const parameters -error: aborting due to 1 previous error +error: using function pointers as const generic parameters is forbidden + --> $DIR/issue-71611.rs:5:21 + | +LL | fn func(outer: A) { + | ^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/issues/issue-71611.rs b/tests/ui/const-generics/issues/issue-71611.rs index 0e0c08146b2e..c6c1e267171f 100644 --- a/tests/ui/const-generics/issues/issue-71611.rs +++ b/tests/ui/const-generics/issues/issue-71611.rs @@ -4,6 +4,7 @@ fn func(outer: A) { //~^ ERROR: the type of const parameters must not depend on other generic parameters + //[min]~| ERROR: using function pointers as const generic parameters is forbidden F(outer); } diff --git a/tests/ui/const-generics/issues/issue-82956.stderr b/tests/ui/const-generics/issues/issue-82956.stderr index d0fc7112426a..a956fc741f4d 100644 --- a/tests/ui/const-generics/issues/issue-82956.stderr +++ b/tests/ui/const-generics/issues/issue-82956.stderr @@ -4,7 +4,7 @@ error[E0433]: failed to resolve: use of undeclared type `IntoIter` LL | let mut iter = IntoIter::new(self); | ^^^^^^^^ use of undeclared type `IntoIter` | -help: consider importing one of these items +help: consider importing one of these structs | LL + use std::array::IntoIter; | diff --git a/tests/ui/const-generics/not_wf_param_in_rpitit.rs b/tests/ui/const-generics/not_wf_param_in_rpitit.rs new file mode 100644 index 000000000000..5471dc9022f9 --- /dev/null +++ b/tests/ui/const-generics/not_wf_param_in_rpitit.rs @@ -0,0 +1,14 @@ +//@ edition:2021 + +trait Trait { + //~^ ERROR: cannot find value `bar` in this scope + //~| ERROR: cycle detected when computing type of `Trait::N` + //~| ERROR: the trait `Trait` cannot be made into an object + //~| ERROR: the trait `Trait` cannot be made into an object + //~| ERROR: the trait `Trait` cannot be made into an object + //~| ERROR: `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter + //~| ERROR: trait objects must include the `dyn` keyword + async fn a() {} +} + +fn main() {} diff --git a/tests/ui/const-generics/not_wf_param_in_rpitit.stderr b/tests/ui/const-generics/not_wf_param_in_rpitit.stderr new file mode 100644 index 000000000000..9095948d22b8 --- /dev/null +++ b/tests/ui/const-generics/not_wf_param_in_rpitit.stderr @@ -0,0 +1,113 @@ +error[E0425]: cannot find value `bar` in this scope + --> $DIR/not_wf_param_in_rpitit.rs:3:30 + | +LL | trait Trait { + | ^^^ not found in this scope + +error[E0391]: cycle detected when computing type of `Trait::N` + --> $DIR/not_wf_param_in_rpitit.rs:3:22 + | +LL | trait Trait { + | ^^^^^ + | + = note: ...which immediately requires computing type of `Trait::N` again +note: cycle used when computing explicit predicates of trait `Trait` + --> $DIR/not_wf_param_in_rpitit.rs:3:1 + | +LL | trait Trait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error[E0038]: the trait `Trait` cannot be made into an object + --> $DIR/not_wf_param_in_rpitit.rs:3:22 + | +LL | trait Trait { + | ^^^^^ `Trait` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/not_wf_param_in_rpitit.rs:11:14 + | +LL | trait Trait { + | ----- this trait cannot be made into an object... +... +LL | async fn a() {} + | ^ ...because associated function `a` has no `self` parameter +help: consider turning `a` into a method by giving it a `&self` argument + | +LL | async fn a(&self) {} + | +++++ +help: alternatively, consider constraining `a` so it does not apply to trait objects + | +LL | async fn a() where Self: Sized {} + | +++++++++++++++++ + +error[E0038]: the trait `Trait` cannot be made into an object + --> $DIR/not_wf_param_in_rpitit.rs:3:13 + | +LL | trait Trait { + | ^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/not_wf_param_in_rpitit.rs:11:14 + | +LL | trait Trait { + | ----- this trait cannot be made into an object... +... +LL | async fn a() {} + | ^ ...because associated function `a` has no `self` parameter +help: consider turning `a` into a method by giving it a `&self` argument + | +LL | async fn a(&self) {} + | +++++ +help: alternatively, consider constraining `a` so it does not apply to trait objects + | +LL | async fn a() where Self: Sized {} + | +++++++++++++++++ + +error: `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter + --> $DIR/not_wf_param_in_rpitit.rs:3:22 + | +LL | trait Trait { + | ^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + +error[E0038]: the trait `Trait` cannot be made into an object + --> $DIR/not_wf_param_in_rpitit.rs:3:13 + | +LL | trait Trait { + | ^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/not_wf_param_in_rpitit.rs:11:14 + | +LL | trait Trait { + | ----- this trait cannot be made into an object... +... +LL | async fn a() {} + | ^ ...because associated function `a` has no `self` parameter + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider turning `a` into a method by giving it a `&self` argument + | +LL | async fn a(&self) {} + | +++++ +help: alternatively, consider constraining `a` so it does not apply to trait objects + | +LL | async fn a() where Self: Sized {} + | +++++++++++++++++ + +error[E0782]: trait objects must include the `dyn` keyword + --> $DIR/not_wf_param_in_rpitit.rs:3:22 + | +LL | trait Trait { + | ^^^^^ + | +help: add `dyn` keyword before this trait + | +LL | trait Trait { + | +++ + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0038, E0391, E0425, E0782. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/const-generics/opaque_types.rs b/tests/ui/const-generics/opaque_types.rs index ccf70f4fb37b..2c7170c889fb 100644 --- a/tests/ui/const-generics/opaque_types.rs +++ b/tests/ui/const-generics/opaque_types.rs @@ -6,6 +6,7 @@ type Foo = impl Sized; fn foo() {} //~^ ERROR: `Foo` is forbidden as the type of a const generic parameter +//~| ERROR: item does not constrain fn main() { foo::<42>(); diff --git a/tests/ui/const-generics/opaque_types.stderr b/tests/ui/const-generics/opaque_types.stderr index f03bca69a8bb..2c7384984c69 100644 --- a/tests/ui/const-generics/opaque_types.stderr +++ b/tests/ui/const-generics/opaque_types.stderr @@ -1,5 +1,18 @@ +error: item does not constrain `Foo::{opaque#0}`, but has it in its signature + --> $DIR/opaque_types.rs:7:4 + | +LL | fn foo() {} + | ^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/opaque_types.rs:3:12 + | +LL | type Foo = impl Sized; + | ^^^^^^^^^^ + error[E0308]: mismatched types - --> $DIR/opaque_types.rs:11:11 + --> $DIR/opaque_types.rs:12:11 | LL | type Foo = impl Sized; | ---------- the expected opaque type @@ -22,27 +35,27 @@ note: ...which requires computing type of opaque `Foo::{opaque#0}`... LL | type Foo = impl Sized; | ^^^^^^^^^^ note: ...which requires type-checking `main`... - --> $DIR/opaque_types.rs:10:1 + --> $DIR/opaque_types.rs:11:1 | LL | fn main() { | ^^^^^^^^^ note: ...which requires evaluating type-level constant... - --> $DIR/opaque_types.rs:11:11 + --> $DIR/opaque_types.rs:12:11 | LL | foo::<42>(); | ^^ note: ...which requires const-evaluating + checking `main::{constant#0}`... - --> $DIR/opaque_types.rs:11:11 + --> $DIR/opaque_types.rs:12:11 | LL | foo::<42>(); | ^^ note: ...which requires caching mir of `main::{constant#0}` for CTFE... - --> $DIR/opaque_types.rs:11:11 + --> $DIR/opaque_types.rs:12:11 | LL | foo::<42>(); | ^^ note: ...which requires elaborating drops for `main::{constant#0}`... - --> $DIR/opaque_types.rs:11:11 + --> $DIR/opaque_types.rs:12:11 | LL | foo::<42>(); | ^^ @@ -70,42 +83,42 @@ LL | type Foo = impl Sized; | ^^^^^^^^^^ | note: ...which requires type-checking `main`... - --> $DIR/opaque_types.rs:10:1 + --> $DIR/opaque_types.rs:11:1 | LL | fn main() { | ^^^^^^^^^ note: ...which requires evaluating type-level constant... - --> $DIR/opaque_types.rs:11:11 + --> $DIR/opaque_types.rs:12:11 | LL | foo::<42>(); | ^^ note: ...which requires const-evaluating + checking `main::{constant#0}`... - --> $DIR/opaque_types.rs:11:11 + --> $DIR/opaque_types.rs:12:11 | LL | foo::<42>(); | ^^ note: ...which requires caching mir of `main::{constant#0}` for CTFE... - --> $DIR/opaque_types.rs:11:11 + --> $DIR/opaque_types.rs:12:11 | LL | foo::<42>(); | ^^ note: ...which requires elaborating drops for `main::{constant#0}`... - --> $DIR/opaque_types.rs:11:11 + --> $DIR/opaque_types.rs:12:11 | LL | foo::<42>(); | ^^ note: ...which requires borrow-checking `main::{constant#0}`... - --> $DIR/opaque_types.rs:11:11 + --> $DIR/opaque_types.rs:12:11 | LL | foo::<42>(); | ^^ note: ...which requires promoting constants in MIR for `main::{constant#0}`... - --> $DIR/opaque_types.rs:11:11 + --> $DIR/opaque_types.rs:12:11 | LL | foo::<42>(); | ^^ note: ...which requires const checking `main::{constant#0}`... - --> $DIR/opaque_types.rs:11:11 + --> $DIR/opaque_types.rs:12:11 | LL | foo::<42>(); | ^^ @@ -119,7 +132,7 @@ LL | type Foo = impl Sized; | ^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0308, E0391. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/parser-error-recovery/issue-89013-no-kw.rs b/tests/ui/const-generics/parser-error-recovery/issue-89013-no-kw.rs index 79743abe4093..c15482f92dca 100644 --- a/tests/ui/const-generics/parser-error-recovery/issue-89013-no-kw.rs +++ b/tests/ui/const-generics/parser-error-recovery/issue-89013-no-kw.rs @@ -7,8 +7,7 @@ struct Bar; const T: usize = 42; impl Foo for Bar { -//~^ ERROR trait takes 1 generic argument but 0 generic arguments were supplied -//~| ERROR associated type bindings are not allowed here +//~^ ERROR associated item constraints are not allowed here //~| ERROR associated const equality is incomplete fn do_x(&self) -> [u8; 3] { [0u8; 3] diff --git a/tests/ui/const-generics/parser-error-recovery/issue-89013-no-kw.stderr b/tests/ui/const-generics/parser-error-recovery/issue-89013-no-kw.stderr index 941764a575e6..78cddcc234c6 100644 --- a/tests/ui/const-generics/parser-error-recovery/issue-89013-no-kw.stderr +++ b/tests/ui/const-generics/parser-error-recovery/issue-89013-no-kw.stderr @@ -8,34 +8,18 @@ LL | impl Foo for Bar { = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0107]: trait takes 1 generic argument but 0 generic arguments were supplied - --> $DIR/issue-89013-no-kw.rs:9:6 - | -LL | impl Foo for Bar { - | ^^^ expected 1 generic argument - | -note: trait defined here, with 1 generic parameter: `N` - --> $DIR/issue-89013-no-kw.rs:1:7 - | -LL | trait Foo { - | ^^^ -------------- -help: add missing generic argument - | -LL | impl Foo for Bar { - | ++ - -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-89013-no-kw.rs:9:10 | LL | impl Foo for Bar { - | ^^^^^ associated type not allowed here + | ^^^^^ associated item constraint not allowed here | help: to use `3` as a generic argument specify it directly | LL | impl Foo<3> for Bar { | ~ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0107, E0229, E0658. -For more information about an error, try `rustc --explain E0107`. +Some errors have detailed explanations: E0229, E0658. +For more information about an error, try `rustc --explain E0229`. diff --git a/tests/ui/const-generics/parser-error-recovery/issue-89013.rs b/tests/ui/const-generics/parser-error-recovery/issue-89013.rs index 335d0d94e83c..6302f7f2ad5d 100644 --- a/tests/ui/const-generics/parser-error-recovery/issue-89013.rs +++ b/tests/ui/const-generics/parser-error-recovery/issue-89013.rs @@ -8,8 +8,7 @@ const T: usize = 42; impl Foo for Bar { //~^ ERROR expected lifetime, type, or constant, found keyword `const` -//~| ERROR trait takes 1 generic -//~| ERROR associated type bindings are not allowed here +//~| ERROR associated item constraints are not allowed here //~| ERROR associated const equality is incomplete fn do_x(&self) -> [u8; 3] { [0u8; 3] diff --git a/tests/ui/const-generics/parser-error-recovery/issue-89013.stderr b/tests/ui/const-generics/parser-error-recovery/issue-89013.stderr index a4c9e065c15c..387eb226e70e 100644 --- a/tests/ui/const-generics/parser-error-recovery/issue-89013.stderr +++ b/tests/ui/const-generics/parser-error-recovery/issue-89013.stderr @@ -20,34 +20,18 @@ LL | impl Foo for Bar { = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0107]: trait takes 1 generic argument but 0 generic arguments were supplied - --> $DIR/issue-89013.rs:9:6 - | -LL | impl Foo for Bar { - | ^^^ expected 1 generic argument - | -note: trait defined here, with 1 generic parameter: `N` - --> $DIR/issue-89013.rs:1:7 - | -LL | trait Foo { - | ^^^ -------------- -help: add missing generic argument - | -LL | impl Foo for Bar { - | ++ - -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-89013.rs:9:10 | LL | impl Foo for Bar { - | ^^^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^^^ associated item constraint not allowed here | help: to use `3` as a generic argument specify it directly | LL | impl Foo<3> for Bar { | ~ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0107, E0229, E0658. -For more information about an error, try `rustc --explain E0107`. +Some errors have detailed explanations: E0229, E0658. +For more information about an error, try `rustc --explain E0229`. diff --git a/tests/ui/const-generics/repeat_expr_hack_gives_right_generics.rs b/tests/ui/const-generics/repeat_expr_hack_gives_right_generics.rs new file mode 100644 index 000000000000..899db191ae7d --- /dev/null +++ b/tests/ui/const-generics/repeat_expr_hack_gives_right_generics.rs @@ -0,0 +1,20 @@ +// Given an anon const `a`: `{ N }` and some anon const `b` which references the +// first anon const: `{ [1; a] }`. `b` should not have any generics as it is not +// a simple `N` argument nor is it a repeat expr count. +// +// On the other hand `b` *is* a repeat expr count and so it should inherit its +// parents generics as part of the `const_evaluatable_unchecked` fcw (#76200). +// +// In this specific case however `b`'s parent should be `a` and so it should wind +// up not having any generics after all. If `a` were to inherit its generics from +// the enclosing item then the reference to `a` from `b` would contain generic +// parameters not usable by `b` which would cause us to ICE. + +fn bar() {} + +fn foo() { + bar::<{ [1; N] }>(); + //~^ ERROR: generic parameters may not be used in const operations +} + +fn main() {} diff --git a/tests/ui/const-generics/repeat_expr_hack_gives_right_generics.stderr b/tests/ui/const-generics/repeat_expr_hack_gives_right_generics.stderr new file mode 100644 index 000000000000..64548cc5a301 --- /dev/null +++ b/tests/ui/const-generics/repeat_expr_hack_gives_right_generics.stderr @@ -0,0 +1,11 @@ +error: generic parameters may not be used in const operations + --> $DIR/repeat_expr_hack_gives_right_generics.rs:16:17 + | +LL | bar::<{ [1; N] }>(); + | ^ cannot perform const operation using `N` + | + = help: const parameters may only be used as standalone arguments, i.e. `N` + = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/transmute-fail.stderr b/tests/ui/const-generics/transmute-fail.stderr index 1b0d1ea50d08..b76ec10bd3f2 100644 --- a/tests/ui/const-generics/transmute-fail.stderr +++ b/tests/ui/const-generics/transmute-fail.stderr @@ -13,7 +13,7 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | std::mem::transmute(v) | ^^^^^^^^^^^^^^^^^^^ | - = note: source type: `[[u32; H]; W]` (this type does not have a fixed size) + = note: source type: `[[u32; H]; W]` (size can vary because of [u32; H]) = note: target type: `[[u32; W]; H]` (size can vary because of [u32; W]) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types diff --git a/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs b/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs index 419d605d0c87..35c41ae46152 100644 --- a/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs +++ b/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs @@ -6,6 +6,7 @@ use std::mem::MaybeUninit; +#[allow(dead_code)] #[repr(transparent)] pub struct MaybeUninitWrapper(MaybeUninit<[u64; N]>); diff --git a/tests/ui/const-generics/type_not_in_scope.rs b/tests/ui/const-generics/type_not_in_scope.rs index 917abaed15e0..1eb265d74d1c 100644 --- a/tests/ui/const-generics/type_not_in_scope.rs +++ b/tests/ui/const-generics/type_not_in_scope.rs @@ -5,6 +5,7 @@ impl X { } } fn getn() -> [u8; N] {} -//~^ ERROR expected type, found built-in attribute `cfg_attr` +//~^ ERROR: expected type, found built-in attribute `cfg_attr` +//~| ERROR: mismatched types fn main() {} diff --git a/tests/ui/const-generics/type_not_in_scope.stderr b/tests/ui/const-generics/type_not_in_scope.stderr index 5eb81ca05224..5f45550a6279 100644 --- a/tests/ui/const-generics/type_not_in_scope.stderr +++ b/tests/ui/const-generics/type_not_in_scope.stderr @@ -10,7 +10,15 @@ error[E0573]: expected type, found built-in attribute `cfg_attr` LL | fn getn() -> [u8; N] {} | ^^^^^^^^ not a type -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/type_not_in_scope.rs:7:33 + | +LL | fn getn() -> [u8; N] {} + | ---- ^^^^^^^ expected `[u8; N]`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression -Some errors have detailed explanations: E0412, E0573. -For more information about an error, try `rustc --explain E0412`. +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0412, E0573. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/const-ptr/forbidden_slices.rs b/tests/ui/const-ptr/forbidden_slices.rs index 85baeedf2215..2550a3a01096 100644 --- a/tests/ui/const-ptr/forbidden_slices.rs +++ b/tests/ui/const-ptr/forbidden_slices.rs @@ -12,7 +12,7 @@ use std::{ slice::{from_ptr_range, from_raw_parts}, }; -// Null is never valid for reads +// Null is never valid for references pub static S0: &[u32] = unsafe { from_raw_parts(ptr::null(), 0) }; //~^ ERROR: it is undefined behavior to use this value pub static S1: &[()] = unsafe { from_raw_parts(ptr::null(), 0) }; @@ -46,10 +46,11 @@ pub static S8: &[u64] = unsafe { }; pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; -pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; +//~^ ERROR it is undefined behavior to use this value +pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; // errors inside libcore pub static R2: &[u32] = unsafe { let ptr = &D0 as *const u32; - from_ptr_range(ptr..ptr.add(2)) + from_ptr_range(ptr..ptr.add(2)) // errors inside libcore }; pub static R4: &[u8] = unsafe { //~^ ERROR: it is undefined behavior to use this value diff --git a/tests/ui/const-ptr/forbidden_slices.stderr b/tests/ui/const-ptr/forbidden_slices.stderr index 250366da2a6a..eb41a25c818a 100644 --- a/tests/ui/const-ptr/forbidden_slices.stderr +++ b/tests/ui/const-ptr/forbidden_slices.stderr @@ -88,20 +88,16 @@ LL | pub static S8: &[u64] = unsafe { HEX_DUMP } -error[E0080]: could not evaluate static initializer - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | - = note: out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance) - | -note: inside `std::ptr::const_ptr::::sub_ptr` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL -note: inside `from_ptr_range::<'_, u32>` - --> $SRC_DIR/core/src/slice/raw.rs:LL:COL -note: inside `R0` - --> $DIR/forbidden_slices.rs:48:34 +error[E0080]: it is undefined behavior to use this value + --> $DIR/forbidden_slices.rs:48:1 | LL | pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null reference + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { + HEX_DUMP + } error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -113,9 +109,9 @@ note: inside `std::ptr::const_ptr::::sub_ptr` note: inside `from_ptr_range::<'_, ()>` --> $SRC_DIR/core/src/slice/raw.rs:LL:COL note: inside `R1` - --> $DIR/forbidden_slices.rs:49:33 + --> $DIR/forbidden_slices.rs:50:33 | -LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; +LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; // errors inside libcore | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -127,13 +123,13 @@ error[E0080]: could not evaluate static initializer note: inside `std::ptr::const_ptr::::add` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL note: inside `R2` - --> $DIR/forbidden_slices.rs:52:25 + --> $DIR/forbidden_slices.rs:53:25 | -LL | from_ptr_range(ptr..ptr.add(2)) +LL | from_ptr_range(ptr..ptr.add(2)) // errors inside libcore | ^^^^^^^^^^ error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:54:1 + --> $DIR/forbidden_slices.rs:55:1 | LL | pub static R4: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered uninitialized memory, but expected an integer @@ -144,7 +140,7 @@ LL | pub static R4: &[u8] = unsafe { } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:59:1 + --> $DIR/forbidden_slices.rs:60:1 | LL | pub static R5: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered a pointer, but expected an integer @@ -157,7 +153,7 @@ LL | pub static R5: &[u8] = unsafe { = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:64:1 + --> $DIR/forbidden_slices.rs:65:1 | LL | pub static R6: &[bool] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered 0x11, but expected a boolean @@ -168,7 +164,7 @@ LL | pub static R6: &[bool] = unsafe { } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:69:1 + --> $DIR/forbidden_slices.rs:70:1 | LL | pub static R7: &[u16] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1) @@ -186,7 +182,7 @@ error[E0080]: could not evaluate static initializer note: inside `std::ptr::const_ptr::::add` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL note: inside `R8` - --> $DIR/forbidden_slices.rs:76:25 + --> $DIR/forbidden_slices.rs:77:25 | LL | from_ptr_range(ptr..ptr.add(1)) | ^^^^^^^^^^ @@ -201,7 +197,7 @@ note: inside `std::ptr::const_ptr::::sub_ptr` note: inside `from_ptr_range::<'_, u32>` --> $SRC_DIR/core/src/slice/raw.rs:LL:COL note: inside `R9` - --> $DIR/forbidden_slices.rs:81:34 + --> $DIR/forbidden_slices.rs:82:34 | LL | pub static R9: &[u32] = unsafe { from_ptr_range(&D0..(&D0 as *const u32).add(1)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -216,7 +212,7 @@ note: inside `std::ptr::const_ptr::::sub_ptr` note: inside `from_ptr_range::<'_, u32>` --> $SRC_DIR/core/src/slice/raw.rs:LL:COL note: inside `R10` - --> $DIR/forbidden_slices.rs:82:35 + --> $DIR/forbidden_slices.rs:83:35 | LL | pub static R10: &[u32] = unsafe { from_ptr_range(&D0..&D0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/const_prop/ice-type-mismatch-when-copying-112824.rs b/tests/ui/const_prop/ice-type-mismatch-when-copying-112824.rs index a41a159c1fd7..09f7e2ba5b1d 100644 --- a/tests/ui/const_prop/ice-type-mismatch-when-copying-112824.rs +++ b/tests/ui/const_prop/ice-type-mismatch-when-copying-112824.rs @@ -13,6 +13,7 @@ impl Opcode2 { pub fn example2(msg_type: Opcode2) -> impl FnMut(&[u8]) { move |i| match msg_type { Opcode2::OP2 => unimplemented!(), + //~^ ERROR: could not evaluate constant pattern } } diff --git a/tests/ui/const_prop/ice-type-mismatch-when-copying-112824.stderr b/tests/ui/const_prop/ice-type-mismatch-when-copying-112824.stderr index d95a8861230e..9442eac0cf54 100644 --- a/tests/ui/const_prop/ice-type-mismatch-when-copying-112824.stderr +++ b/tests/ui/const_prop/ice-type-mismatch-when-copying-112824.stderr @@ -17,7 +17,13 @@ help: you might be missing a type parameter LL | pub struct Opcode2(&'a S); | +++ -error: aborting due to 2 previous errors +error: could not evaluate constant pattern + --> $DIR/ice-type-mismatch-when-copying-112824.rs:15:9 + | +LL | Opcode2::OP2 => unimplemented!(), + | ^^^^^^^^^^^^ + +error: aborting due to 3 previous errors Some errors have detailed explanations: E0261, E0412. For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/consts/assoc-const-elided-lifetime.stderr b/tests/ui/consts/assoc-const-elided-lifetime.stderr index a1eeaff4ba87..3e847298c356 100644 --- a/tests/ui/consts/assoc-const-elided-lifetime.stderr +++ b/tests/ui/consts/assoc-const-elided-lifetime.stderr @@ -6,6 +6,11 @@ LL | const FOO: Foo<'_> = Foo { x: PhantomData::<&()> }; | = warning: 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 #115010 +note: cannot automatically infer `'static` because of other lifetimes in scope + --> $DIR/assoc-const-elided-lifetime.rs:9:6 + | +LL | impl<'a> Foo<'a> { + | ^^ note: the lint level is defined here --> $DIR/assoc-const-elided-lifetime.rs:1:9 | @@ -24,6 +29,13 @@ LL | const BAR: &() = &(); | = warning: 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 #115010 +note: cannot automatically infer `'static` because of other lifetimes in scope + --> $DIR/assoc-const-elided-lifetime.rs:9:6 + | +LL | impl<'a> Foo<'a> { + | ^^ +LL | const FOO: Foo<'_> = Foo { x: PhantomData::<&()> }; + | ^^ help: use the `'static` lifetime | LL | const BAR: &'static () = &(); diff --git a/tests/ui/consts/const-compare-bytes-ub.rs b/tests/ui/consts/const-compare-bytes-ub.rs index b357bab96a4d..903ba15e6229 100644 --- a/tests/ui/consts/const-compare-bytes-ub.rs +++ b/tests/ui/consts/const-compare-bytes-ub.rs @@ -7,11 +7,11 @@ use std::mem::MaybeUninit; fn main() { const LHS_NULL: i32 = unsafe { - compare_bytes(0 as *const u8, 2 as *const u8, 0) + compare_bytes(0 as *const u8, 2 as *const u8, 1) //~^ ERROR evaluation of constant value failed }; const RHS_NULL: i32 = unsafe { - compare_bytes(1 as *const u8, 0 as *const u8, 0) + compare_bytes(1 as *const u8, 0 as *const u8, 1) //~^ ERROR evaluation of constant value failed }; const DANGLING_PTR_NON_ZERO_LENGTH: i32 = unsafe { diff --git a/tests/ui/consts/const-compare-bytes-ub.stderr b/tests/ui/consts/const-compare-bytes-ub.stderr index d8971eb9969f..9e49706c4c83 100644 --- a/tests/ui/consts/const-compare-bytes-ub.stderr +++ b/tests/ui/consts/const-compare-bytes-ub.stderr @@ -1,14 +1,14 @@ error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:10:9 | -LL | compare_bytes(0 as *const u8, 2 as *const u8, 0) +LL | compare_bytes(0 as *const u8, 2 as *const u8, 1) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:14:9 | -LL | compare_bytes(1 as *const u8, 0 as *const u8, 0) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) +LL | compare_bytes(1 as *const u8, 0 as *const u8, 1) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: 0x1[noalloc] is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:18:9 diff --git a/tests/ui/consts/const-eval/const-eval-overflow-3b.stderr b/tests/ui/consts/const-eval/const-eval-overflow-3b.stderr index 05f33c33946a..0d9b718cd067 100644 --- a/tests/ui/consts/const-eval/const-eval-overflow-3b.stderr +++ b/tests/ui/consts/const-eval/const-eval-overflow-3b.stderr @@ -12,10 +12,10 @@ LL | = [0; (i8::MAX + 1u8) as usize]; | = help: the trait `Add` is not implemented for `i8` = help: the following other types implement trait `Add`: - <&'a i8 as Add> - <&i8 as Add<&i8>> - > - + `&'a i8` implements `Add` + `&i8` implements `Add<&i8>` + `i8` implements `Add<&i8>` + `i8` implements `Add` error: aborting due to 2 previous errors diff --git a/tests/ui/consts/const-eval/const-eval-overflow-4b.stderr b/tests/ui/consts/const-eval/const-eval-overflow-4b.stderr index d019f5920b51..32fe30dc8824 100644 --- a/tests/ui/consts/const-eval/const-eval-overflow-4b.stderr +++ b/tests/ui/consts/const-eval/const-eval-overflow-4b.stderr @@ -12,10 +12,10 @@ LL | : [u32; (i8::MAX as i8 + 1u8) as usize] | = help: the trait `Add` is not implemented for `i8` = help: the following other types implement trait `Add`: - <&'a i8 as Add> - <&i8 as Add<&i8>> - > - + `&'a i8` implements `Add` + `&i8` implements `Add<&i8>` + `i8` implements `Add<&i8>` + `i8` implements `Add` error[E0604]: only `u8` can be cast as `char`, not `i8` --> $DIR/const-eval-overflow-4b.rs:22:13 diff --git a/tests/ui/consts/const-eval/format.rs b/tests/ui/consts/const-eval/format.rs index 5bdb2bf19543..b12df824a337 100644 --- a/tests/ui/consts/const-eval/format.rs +++ b/tests/ui/consts/const-eval/format.rs @@ -1,13 +1,13 @@ const fn failure() { panic!("{:?}", 0); //~^ ERROR cannot call non-const formatting macro in constant functions - //~| ERROR cannot call non-const fn `Arguments::<'_>::new_v1` in constant functions + //~| ERROR cannot call non-const fn `Arguments::<'_>::new_v1::<1, 1>` in constant functions } const fn print() { println!("{:?}", 0); //~^ ERROR cannot call non-const formatting macro in constant functions - //~| ERROR cannot call non-const fn `Arguments::<'_>::new_v1` in constant functions + //~| ERROR cannot call non-const fn `Arguments::<'_>::new_v1::<2, 1>` in constant functions //~| ERROR cannot call non-const fn `_print` in constant functions } diff --git a/tests/ui/consts/const-eval/format.stderr b/tests/ui/consts/const-eval/format.stderr index 434b07443044..ce3f9f2190e5 100644 --- a/tests/ui/consts/const-eval/format.stderr +++ b/tests/ui/consts/const-eval/format.stderr @@ -7,7 +7,7 @@ LL | panic!("{:?}", 0); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0015]: cannot call non-const fn `Arguments::<'_>::new_v1` in constant functions +error[E0015]: cannot call non-const fn `Arguments::<'_>::new_v1::<1, 1>` in constant functions --> $DIR/format.rs:2:5 | LL | panic!("{:?}", 0); @@ -25,7 +25,7 @@ LL | println!("{:?}", 0); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0015]: cannot call non-const fn `Arguments::<'_>::new_v1` in constant functions +error[E0015]: cannot call non-const fn `Arguments::<'_>::new_v1::<2, 1>` in constant functions --> $DIR/format.rs:8:5 | LL | println!("{:?}", 0); diff --git a/tests/ui/consts/const-eval/ub-invalid-values.rs b/tests/ui/consts/const-eval/ub-invalid-values.rs new file mode 100644 index 000000000000..1724a88dd3d9 --- /dev/null +++ b/tests/ui/consts/const-eval/ub-invalid-values.rs @@ -0,0 +1,11 @@ +const fn bool_cast(ptr: *const bool) { unsafe { + let _val = *ptr as u32; //~ERROR: evaluation of constant value failed + //~^ interpreting an invalid 8-bit value as a bool +}} + +const _: () = { + let v = 3_u8; + bool_cast(&v as *const u8 as *const bool); +}; + +fn main() {} diff --git a/tests/ui/consts/const-eval/ub-invalid-values.stderr b/tests/ui/consts/const-eval/ub-invalid-values.stderr new file mode 100644 index 000000000000..edf72f731e5a --- /dev/null +++ b/tests/ui/consts/const-eval/ub-invalid-values.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/ub-invalid-values.rs:2:16 + | +LL | let _val = *ptr as u32; + | ^^^^^^^^^^^ interpreting an invalid 8-bit value as a bool: 0x03 + | +note: inside `bool_cast` + --> $DIR/ub-invalid-values.rs:2:16 + | +LL | let _val = *ptr as u32; + | ^^^^^^^^^^^ +note: inside `_` + --> $DIR/ub-invalid-values.rs:8:5 + | +LL | bool_cast(&v as *const u8 as *const bool); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-fn-cycle.rs b/tests/ui/consts/const-fn-cycle.rs new file mode 100644 index 000000000000..5175296a53e5 --- /dev/null +++ b/tests/ui/consts/const-fn-cycle.rs @@ -0,0 +1,28 @@ +/// Discovered in https://github.com/rust-lang/rust/issues/112602. +/// This caused a cycle error, which made no sense. +/// Removing the `const` part of the `many` function would make the +/// test pass again. +/// The issue was that we were running const qualif checks on +/// `const fn`s, but never using them. During const qualif checks we tend +/// to end up revealing opaque types (the RPIT in `many`'s return type), +/// which can quickly lead to cycles. + +pub struct Parser(H); + +impl Parser +where + H: for<'a> Fn(&'a str) -> T, +{ + pub const fn new(handler: H) -> Parser { + Parser(handler) + } + + pub const fn many<'s>(&'s self) -> Parser Fn(&'a str) -> Vec + 's> { + //~^ ERROR: cycle detected + Parser::new(|_| unimplemented!()) + } +} + +fn main() { + println!("Hello, world!"); +} diff --git a/tests/ui/consts/const-fn-cycle.stderr b/tests/ui/consts/const-fn-cycle.stderr new file mode 100644 index 000000000000..c851f7342be9 --- /dev/null +++ b/tests/ui/consts/const-fn-cycle.stderr @@ -0,0 +1,34 @@ +error[E0391]: cycle detected when computing type of opaque `::many::{opaque#0}` + --> $DIR/const-fn-cycle.rs:20:47 + | +LL | pub const fn many<'s>(&'s self) -> Parser Fn(&'a str) -> Vec + 's> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires borrow-checking `::many`... + --> $DIR/const-fn-cycle.rs:20:5 + | +LL | pub const fn many<'s>(&'s self) -> Parser Fn(&'a str) -> Vec + 's> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires promoting constants in MIR for `::many`... + --> $DIR/const-fn-cycle.rs:20:5 + | +LL | pub const fn many<'s>(&'s self) -> Parser Fn(&'a str) -> Vec + 's> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires const checking `::many`... + --> $DIR/const-fn-cycle.rs:20:5 + | +LL | pub const fn many<'s>(&'s self) -> Parser Fn(&'a str) -> Vec + 's> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which requires computing whether `Parser<::many::{opaque#0}>` is freeze... + = note: ...which requires evaluating trait selection obligation `Parser<::many::{opaque#0}>: core::marker::Freeze`... + = note: ...which again requires computing type of opaque `::many::{opaque#0}`, completing the cycle +note: cycle used when computing type of `::many::{opaque#0}` + --> $DIR/const-fn-cycle.rs:20:47 + | +LL | pub const fn many<'s>(&'s self) -> Parser Fn(&'a str) -> Vec + 's> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/consts/const-int-unchecked.stderr b/tests/ui/consts/const-int-unchecked.stderr index 84b222972a13..3130603231e1 100644 --- a/tests/ui/consts/const-int-unchecked.stderr +++ b/tests/ui/consts/const-int-unchecked.stderr @@ -242,19 +242,19 @@ error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:123:25 | LL | const _: u16 = unsafe { std::intrinsics::unchecked_add(40000u16, 30000) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_add` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_add` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:126:25 | LL | const _: u32 = unsafe { std::intrinsics::unchecked_sub(14u32, 22) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_sub` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_sub` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:129:25 | LL | const _: u16 = unsafe { std::intrinsics::unchecked_mul(300u16, 250u16) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_mul` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_mul` error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:132:25 diff --git a/tests/ui/consts/const-promoted-opaque.atomic.stderr b/tests/ui/consts/const-promoted-opaque.atomic.stderr new file mode 100644 index 000000000000..a0459f4040ec --- /dev/null +++ b/tests/ui/consts/const-promoted-opaque.atomic.stderr @@ -0,0 +1,71 @@ +error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability + --> $DIR/const-promoted-opaque.rs:29:25 + | +LL | let _: &'static _ = &FOO; + | ^^^^ + | + = note: see issue #80384 for more information + = help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0493]: destructor of `helper::Foo` cannot be evaluated at compile-time + --> $DIR/const-promoted-opaque.rs:29:26 + | +LL | let _: &'static _ = &FOO; + | ^^^ the destructor for this type cannot be evaluated in constants +... +LL | }; + | - value is dropped here + +error[E0492]: constants cannot refer to interior mutable data + --> $DIR/const-promoted-opaque.rs:34:19 + | +LL | const BAZ: &Foo = &FOO; + | ^^^^ this borrow of an interior mutable value may end up in the final value + +error[E0716]: temporary value dropped while borrowed + --> $DIR/const-promoted-opaque.rs:38:26 + | +LL | let _: &'static _ = &FOO; + | ---------- ^^^ creates a temporary value which is freed while still in use + | | + | type annotation requires that borrow lasts for `'static` +LL | +LL | } + | - temporary value is freed at the end of this statement + +error[E0391]: cycle detected when computing type of opaque `helper::Foo::{opaque#0}` + --> $DIR/const-promoted-opaque.rs:14:20 + | +LL | pub type Foo = impl Sized; + | ^^^^^^^^^^ + | +note: ...which requires borrow-checking `helper::FOO`... + --> $DIR/const-promoted-opaque.rs:21:5 + | +LL | pub const FOO: Foo = std::sync::atomic::AtomicU8::new(42); + | ^^^^^^^^^^^^^^^^^^ +note: ...which requires promoting constants in MIR for `helper::FOO`... + --> $DIR/const-promoted-opaque.rs:21:5 + | +LL | pub const FOO: Foo = std::sync::atomic::AtomicU8::new(42); + | ^^^^^^^^^^^^^^^^^^ +note: ...which requires const checking `helper::FOO`... + --> $DIR/const-promoted-opaque.rs:21:5 + | +LL | pub const FOO: Foo = std::sync::atomic::AtomicU8::new(42); + | ^^^^^^^^^^^^^^^^^^ + = note: ...which requires computing whether `helper::Foo` is freeze... + = note: ...which requires evaluating trait selection obligation `helper::Foo: core::marker::Freeze`... + = note: ...which again requires computing type of opaque `helper::Foo::{opaque#0}`, completing the cycle +note: cycle used when computing type of `helper::Foo::{opaque#0}` + --> $DIR/const-promoted-opaque.rs:14:20 + | +LL | pub type Foo = impl Sized; + | ^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0391, E0492, E0493, E0658, E0716. +For more information about an error, try `rustc --explain E0391`. diff --git a/tests/ui/consts/const-promoted-opaque.rs b/tests/ui/consts/const-promoted-opaque.rs new file mode 100644 index 000000000000..e20823527f48 --- /dev/null +++ b/tests/ui/consts/const-promoted-opaque.rs @@ -0,0 +1,40 @@ +//@revisions: string unit atomic +#![feature(type_alias_impl_trait)] + +//! Check that we do not cause cycle errors when trying to +//! obtain information about interior mutability of an opaque type. +//! This used to happen, because when the body-analysis failed, we +//! checked the type instead, but the constant was also defining the +//! hidden type of the opaque type. Thus we ended up relying on the +//! result of our analysis to compute the result of our analysis. + +//@[unit] check-pass + +mod helper { + pub type Foo = impl Sized; + //[string,atomic]~^ ERROR cycle detected + + #[cfg(string)] + pub const FOO: Foo = String::new(); + + #[cfg(atomic)] + pub const FOO: Foo = std::sync::atomic::AtomicU8::new(42); + + #[cfg(unit)] + pub const FOO: Foo = (); +} +use helper::*; + +const BAR: () = { + let _: &'static _ = &FOO; + //[string,atomic]~^ ERROR: destructor of `helper::Foo` cannot be evaluated at compile-time + //[string,atomic]~| ERROR: cannot borrow here +}; + +const BAZ: &Foo = &FOO; +//[string,atomic]~^ ERROR: constants cannot refer to interior mutable data + +fn main() { + let _: &'static _ = &FOO; + //[string,atomic]~^ ERROR: temporary value dropped while borrowed +} diff --git a/tests/ui/consts/const-promoted-opaque.string.stderr b/tests/ui/consts/const-promoted-opaque.string.stderr new file mode 100644 index 000000000000..a613d517e68e --- /dev/null +++ b/tests/ui/consts/const-promoted-opaque.string.stderr @@ -0,0 +1,71 @@ +error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability + --> $DIR/const-promoted-opaque.rs:29:25 + | +LL | let _: &'static _ = &FOO; + | ^^^^ + | + = note: see issue #80384 for more information + = help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0493]: destructor of `helper::Foo` cannot be evaluated at compile-time + --> $DIR/const-promoted-opaque.rs:29:26 + | +LL | let _: &'static _ = &FOO; + | ^^^ the destructor for this type cannot be evaluated in constants +... +LL | }; + | - value is dropped here + +error[E0492]: constants cannot refer to interior mutable data + --> $DIR/const-promoted-opaque.rs:34:19 + | +LL | const BAZ: &Foo = &FOO; + | ^^^^ this borrow of an interior mutable value may end up in the final value + +error[E0716]: temporary value dropped while borrowed + --> $DIR/const-promoted-opaque.rs:38:26 + | +LL | let _: &'static _ = &FOO; + | ---------- ^^^ creates a temporary value which is freed while still in use + | | + | type annotation requires that borrow lasts for `'static` +LL | +LL | } + | - temporary value is freed at the end of this statement + +error[E0391]: cycle detected when computing type of opaque `helper::Foo::{opaque#0}` + --> $DIR/const-promoted-opaque.rs:14:20 + | +LL | pub type Foo = impl Sized; + | ^^^^^^^^^^ + | +note: ...which requires borrow-checking `helper::FOO`... + --> $DIR/const-promoted-opaque.rs:18:5 + | +LL | pub const FOO: Foo = String::new(); + | ^^^^^^^^^^^^^^^^^^ +note: ...which requires promoting constants in MIR for `helper::FOO`... + --> $DIR/const-promoted-opaque.rs:18:5 + | +LL | pub const FOO: Foo = String::new(); + | ^^^^^^^^^^^^^^^^^^ +note: ...which requires const checking `helper::FOO`... + --> $DIR/const-promoted-opaque.rs:18:5 + | +LL | pub const FOO: Foo = String::new(); + | ^^^^^^^^^^^^^^^^^^ + = note: ...which requires computing whether `helper::Foo` is freeze... + = note: ...which requires evaluating trait selection obligation `helper::Foo: core::marker::Freeze`... + = note: ...which again requires computing type of opaque `helper::Foo::{opaque#0}`, completing the cycle +note: cycle used when computing type of `helper::Foo::{opaque#0}` + --> $DIR/const-promoted-opaque.rs:14:20 + | +LL | pub type Foo = impl Sized; + | ^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0391, E0492, E0493, E0658, E0716. +For more information about an error, try `rustc --explain E0391`. diff --git a/tests/ui/consts/const_in_pattern/accept_structural.rs b/tests/ui/consts/const_in_pattern/accept_structural.rs index 31d3b6e73312..78d92abd88b4 100644 --- a/tests/ui/consts/const_in_pattern/accept_structural.rs +++ b/tests/ui/consts/const_in_pattern/accept_structural.rs @@ -1,7 +1,6 @@ //@ run-pass #![allow(non_local_definitions)] -#![warn(indirect_structural_match)] // This test is checking our logic for structural match checking by enumerating // the different kinds of const expressions. This test is collecting cases where diff --git a/tests/ui/consts/const_in_pattern/cross-crate-fail.rs b/tests/ui/consts/const_in_pattern/cross-crate-fail.rs index d8df2847c440..163a47f43336 100644 --- a/tests/ui/consts/const_in_pattern/cross-crate-fail.rs +++ b/tests/ui/consts/const_in_pattern/cross-crate-fail.rs @@ -1,7 +1,5 @@ //@ aux-build:consts.rs -#![warn(indirect_structural_match)] - extern crate consts; struct Defaulted; diff --git a/tests/ui/consts/const_in_pattern/cross-crate-fail.stderr b/tests/ui/consts/const_in_pattern/cross-crate-fail.stderr index 067f2b6e6747..e0f97a9abd21 100644 --- a/tests/ui/consts/const_in_pattern/cross-crate-fail.stderr +++ b/tests/ui/consts/const_in_pattern/cross-crate-fail.stderr @@ -1,5 +1,5 @@ error: to use a constant of type `CustomEq` in a pattern, `CustomEq` must be annotated with `#[derive(PartialEq)]` - --> $DIR/cross-crate-fail.rs:13:9 + --> $DIR/cross-crate-fail.rs:11:9 | LL | consts::SOME => panic!(), | ^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | consts::SOME => panic!(), = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details error: to use a constant of type `CustomEq` in a pattern, `CustomEq` must be annotated with `#[derive(PartialEq)]` - --> $DIR/cross-crate-fail.rs:20:9 + --> $DIR/cross-crate-fail.rs:18:9 | LL | ::SOME => panic!(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/const_in_pattern/cross-crate-pass.rs b/tests/ui/consts/const_in_pattern/cross-crate-pass.rs index c18a30b3495b..2a121a23dfc8 100644 --- a/tests/ui/consts/const_in_pattern/cross-crate-pass.rs +++ b/tests/ui/consts/const_in_pattern/cross-crate-pass.rs @@ -1,8 +1,6 @@ //@ run-pass //@ aux-build:consts.rs -#![warn(indirect_structural_match)] - extern crate consts; use consts::CustomEq; diff --git a/tests/ui/consts/const_in_pattern/custom-eq-branch-pass.rs b/tests/ui/consts/const_in_pattern/custom-eq-branch-pass.rs index 605f4e760cd2..2e7061e7c4b4 100644 --- a/tests/ui/consts/const_in_pattern/custom-eq-branch-pass.rs +++ b/tests/ui/consts/const_in_pattern/custom-eq-branch-pass.rs @@ -1,7 +1,5 @@ //@ run-pass -#![warn(indirect_structural_match)] - struct CustomEq; impl Eq for CustomEq {} diff --git a/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.rs b/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.rs index 6285427f59cb..e7c6101a3e06 100644 --- a/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.rs +++ b/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.rs @@ -1,4 +1,3 @@ -#![deny(pointer_structural_match)] #![allow(dead_code)] const C: *const u8 = &0; @@ -8,7 +7,6 @@ const C_INNER: (*const u8, u8) = (C, 0); fn foo(x: *const u8) { match x { C => {} //~ERROR: behave unpredictably - //~| previously accepted _ => {} } } @@ -16,7 +14,6 @@ fn foo(x: *const u8) { fn foo2(x: *const u8) { match (x, 1) { C_INNER => {} //~ERROR: behave unpredictably - //~| previously accepted _ => {} } } @@ -28,13 +25,11 @@ const STR: *const str = "abcd"; fn main() { match D { D => {} //~ERROR: behave unpredictably - //~| previously accepted _ => {} } match STR { STR => {} //~ERROR: behave unpredictably - //~| previously accepted _ => {} } } diff --git a/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.stderr b/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.stderr index bc1015c17342..aa208341c131 100644 --- a/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.stderr +++ b/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.stderr @@ -1,103 +1,26 @@ error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:10:9 + --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:9:9 | LL | C => {} | ^ - | - = warning: 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 #120362 -note: the lint level is defined here - --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:1:9 - | -LL | #![deny(pointer_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:18:9 + --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:16:9 | LL | C_INNER => {} | ^^^^^^^ - | - = warning: 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 #120362 error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:30:9 + --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:27:9 | LL | D => {} | ^ - | - = warning: 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 #120362 error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:36:9 + --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:32:9 | LL | STR => {} | ^^^ - | - = warning: 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 #120362 error: aborting due to 4 previous errors -Future incompatibility report: Future breakage diagnostic: -error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:10:9 - | -LL | C => {} - | ^ - | - = warning: 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 #120362 -note: the lint level is defined here - --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:1:9 - | -LL | #![deny(pointer_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:18:9 - | -LL | C_INNER => {} - | ^^^^^^^ - | - = warning: 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 #120362 -note: the lint level is defined here - --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:1:9 - | -LL | #![deny(pointer_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:30:9 - | -LL | D => {} - | ^ - | - = warning: 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 #120362 -note: the lint level is defined here - --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:1:9 - | -LL | #![deny(pointer_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:36:9 - | -LL | STR => {} - | ^^^ - | - = warning: 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 #120362 -note: the lint level is defined here - --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:1:9 - | -LL | #![deny(pointer_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - diff --git a/tests/ui/consts/const_in_pattern/issue-44333.rs b/tests/ui/consts/const_in_pattern/issue-44333.rs index 9adf02cbfcef..8a70aae1d509 100644 --- a/tests/ui/consts/const_in_pattern/issue-44333.rs +++ b/tests/ui/consts/const_in_pattern/issue-44333.rs @@ -1,7 +1,3 @@ -//@ run-pass - -#![warn(pointer_structural_match)] - type Func = fn(usize, usize) -> usize; fn foo(a: usize, b: usize) -> usize { a + b } @@ -16,10 +12,8 @@ const BAR: Func = bar; fn main() { match test(std::env::consts::ARCH.len()) { - FOO => println!("foo"), //~ WARN behave unpredictably - //~^ WARN will become a hard error - BAR => println!("bar"), //~ WARN behave unpredictably - //~^ WARN will become a hard error + FOO => println!("foo"), //~ ERROR behave unpredictably + BAR => println!("bar"), //~ ERROR behave unpredictably _ => unreachable!(), } } diff --git a/tests/ui/consts/const_in_pattern/issue-44333.stderr b/tests/ui/consts/const_in_pattern/issue-44333.stderr index f5931f0cad08..d377bfd95f9c 100644 --- a/tests/ui/consts/const_in_pattern/issue-44333.stderr +++ b/tests/ui/consts/const_in_pattern/issue-44333.stderr @@ -1,55 +1,14 @@ -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-44333.rs:19:9 +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/issue-44333.rs:15:9 | LL | FOO => println!("foo"), | ^^^ - | - = warning: 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 #120362 -note: the lint level is defined here - --> $DIR/issue-44333.rs:3:9 - | -LL | #![warn(pointer_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-44333.rs:21:9 +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/issue-44333.rs:16:9 | LL | BAR => println!("bar"), | ^^^ - | - = warning: 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 #120362 -warning: 2 warnings emitted - -Future incompatibility report: Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-44333.rs:19:9 - | -LL | FOO => println!("foo"), - | ^^^ - | - = warning: 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 #120362 -note: the lint level is defined here - --> $DIR/issue-44333.rs:3:9 - | -LL | #![warn(pointer_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-44333.rs:21:9 - | -LL | BAR => println!("bar"), - | ^^^ - | - = warning: 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 #120362 -note: the lint level is defined here - --> $DIR/issue-44333.rs:3:9 - | -LL | #![warn(pointer_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ +error: aborting due to 2 previous errors diff --git a/tests/ui/consts/const_in_pattern/no-eq-branch-fail.rs b/tests/ui/consts/const_in_pattern/no-eq-branch-fail.rs index 141d87d9b703..cf013c1a7906 100644 --- a/tests/ui/consts/const_in_pattern/no-eq-branch-fail.rs +++ b/tests/ui/consts/const_in_pattern/no-eq-branch-fail.rs @@ -1,5 +1,3 @@ -#![warn(indirect_structural_match)] - struct NoEq; enum Foo { diff --git a/tests/ui/consts/const_in_pattern/no-eq-branch-fail.stderr b/tests/ui/consts/const_in_pattern/no-eq-branch-fail.stderr index b29f959de972..7766c6ce683d 100644 --- a/tests/ui/consts/const_in_pattern/no-eq-branch-fail.stderr +++ b/tests/ui/consts/const_in_pattern/no-eq-branch-fail.stderr @@ -1,5 +1,5 @@ error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq)]` - --> $DIR/no-eq-branch-fail.rs:21:9 + --> $DIR/no-eq-branch-fail.rs:19:9 | LL | BAR_BAZ => panic!(), | ^^^^^^^ diff --git a/tests/ui/consts/const_in_pattern/reject_non_structural.rs b/tests/ui/consts/const_in_pattern/reject_non_structural.rs index a9b0aa5507e3..e3dcecec960a 100644 --- a/tests/ui/consts/const_in_pattern/reject_non_structural.rs +++ b/tests/ui/consts/const_in_pattern/reject_non_structural.rs @@ -11,8 +11,6 @@ // See also RFC 1445 #![feature(type_ascription)] -#![warn(indirect_structural_match)] -//~^ NOTE lint level is defined here #[derive(Copy, Clone, Debug)] struct NoPartialEq; @@ -96,9 +94,7 @@ fn main() { const ADDR_OF: &OND = &Some(NoDerive); match &Some(NoDerive) { ADDR_OF => dbg!(ADDR_OF), _ => panic!("whoops"), }; - //~^ WARN must be annotated with `#[derive(PartialEq)]` + //~^ ERROR must be annotated with `#[derive(PartialEq)]` //~| NOTE the traits must be derived //~| NOTE StructuralPartialEq.html for details - //~| WARN previously accepted by the compiler but is being phased out - //~| NOTE for more information, see } diff --git a/tests/ui/consts/const_in_pattern/reject_non_structural.stderr b/tests/ui/consts/const_in_pattern/reject_non_structural.stderr index 2c7aaf89aa78..c068db42e4d9 100644 --- a/tests/ui/consts/const_in_pattern/reject_non_structural.stderr +++ b/tests/ui/consts/const_in_pattern/reject_non_structural.stderr @@ -1,5 +1,5 @@ error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/reject_non_structural.rs:42:36 + --> $DIR/reject_non_structural.rs:40:36 | LL | match Derive::Some(NoDerive) { ENUM => dbg!(ENUM), _ => panic!("whoops"), }; | ^^^^ @@ -8,7 +8,7 @@ LL | match Derive::Some(NoDerive) { ENUM => dbg!(ENUM), _ => panic!("whoops" = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/reject_non_structural.rs:48:28 + --> $DIR/reject_non_structural.rs:46:28 | LL | match Some(NoDerive) { FIELD => dbg!(FIELD), _ => panic!("whoops"), }; | ^^^^^ @@ -17,7 +17,7 @@ LL | match Some(NoDerive) { FIELD => dbg!(FIELD), _ => panic!("whoops"), }; = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/reject_non_structural.rs:55:27 + --> $DIR/reject_non_structural.rs:53:27 | LL | match Some(NoDerive) {INDIRECT => dbg!(INDIRECT), _ => panic!("whoops"), }; | ^^^^^^^^ @@ -26,7 +26,7 @@ LL | match Some(NoDerive) {INDIRECT => dbg!(INDIRECT), _ => panic!("whoops") = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/reject_non_structural.rs:61:36 + --> $DIR/reject_non_structural.rs:59:36 | LL | match (None, Some(NoDerive)) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), }; | ^^^^^ @@ -35,7 +35,7 @@ LL | match (None, Some(NoDerive)) { TUPLE => dbg!(TUPLE), _ => panic!("whoop = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/reject_non_structural.rs:67:28 + --> $DIR/reject_non_structural.rs:65:28 | LL | match Some(NoDerive) { TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => panic!("whoops"), }; | ^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | match Some(NoDerive) { TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => p = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/reject_non_structural.rs:73:36 + --> $DIR/reject_non_structural.rs:71:36 | LL | match [None, Some(NoDerive)] { ARRAY => dbg!(ARRAY), _ => panic!("whoops"), }; | ^^^^^ @@ -53,7 +53,7 @@ LL | match [None, Some(NoDerive)] { ARRAY => dbg!(ARRAY), _ => panic!("whoop = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/reject_non_structural.rs:79:33 + --> $DIR/reject_non_structural.rs:77:33 | LL | match [Some(NoDerive); 2] { REPEAT => dbg!(REPEAT), _ => panic!("whoops"), }; | ^^^^^^ @@ -62,7 +62,7 @@ LL | match [Some(NoDerive); 2] { REPEAT => dbg!(REPEAT), _ => panic!("whoops = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/reject_non_structural.rs:86:28 + --> $DIR/reject_non_structural.rs:84:28 | LL | match Some(NoDerive) { NoDerive::ASSOC => dbg!(NoDerive::ASSOC), _ => panic!("whoops"), }; | ^^^^^^^^^^^^^^^ @@ -71,7 +71,7 @@ LL | match Some(NoDerive) { NoDerive::ASSOC => dbg!(NoDerive::ASSOC), _ => p = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/reject_non_structural.rs:92:28 + --> $DIR/reject_non_structural.rs:90:28 | LL | match Some(NoDerive) { BLOCK => dbg!(BLOCK), _ => panic!("whoops"), }; | ^^^^^ @@ -79,38 +79,14 @@ LL | match Some(NoDerive) { BLOCK => dbg!(BLOCK), _ => panic!("whoops"), }; = note: the traits must be derived, manual `impl`s are not sufficient = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/reject_non_structural.rs:98:29 +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` + --> $DIR/reject_non_structural.rs:96:29 | LL | match &Some(NoDerive) { ADDR_OF => dbg!(ADDR_OF), _ => panic!("whoops"), }; | ^^^^^^^ | - = warning: 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 #120362 = note: the traits must be derived, manual `impl`s are not sufficient = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -note: the lint level is defined here - --> $DIR/reject_non_structural.rs:14:9 - | -LL | #![warn(indirect_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 9 previous errors; 1 warning emitted - -Future incompatibility report: Future breakage diagnostic: -warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/reject_non_structural.rs:98:29 - | -LL | match &Some(NoDerive) { ADDR_OF => dbg!(ADDR_OF), _ => panic!("whoops"), }; - | ^^^^^^^ - | - = warning: 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 #120362 - = note: the traits must be derived, manual `impl`s are not sufficient - = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -note: the lint level is defined here - --> $DIR/reject_non_structural.rs:14:9 - | -LL | #![warn(indirect_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +error: aborting due to 10 previous errors diff --git a/tests/ui/consts/const_refs_to_static-ice-121413.rs b/tests/ui/consts/const_refs_to_static-ice-121413.rs index 8a24fb799b6b..8fc3912efd08 100644 --- a/tests/ui/consts/const_refs_to_static-ice-121413.rs +++ b/tests/ui/consts/const_refs_to_static-ice-121413.rs @@ -5,12 +5,16 @@ // ignore-tidy-linelength #![feature(const_refs_to_static)] const REF_INTERIOR_MUT: &usize = { + //~^ HELP consider importing this struct static FOO: Sync = AtomicUsize::new(0); //~^ ERROR failed to resolve: use of undeclared type `AtomicUsize` //~| WARN trait objects without an explicit `dyn` are deprecated //~| ERROR the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time //~| ERROR the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + //~| HELP if this is an object-safe trait, use `dyn` + //~| HELP the trait `Sized` is not implemented for `(dyn Sync + 'static)` + //~| HELP the trait `Sized` is not implemented for `(dyn Sync + 'static)` unsafe { &*(&FOO as *const _ as *const usize) } }; pub fn main() {} diff --git a/tests/ui/consts/const_refs_to_static-ice-121413.stderr b/tests/ui/consts/const_refs_to_static-ice-121413.stderr index c977c698a92e..fbe32a70293a 100644 --- a/tests/ui/consts/const_refs_to_static-ice-121413.stderr +++ b/tests/ui/consts/const_refs_to_static-ice-121413.stderr @@ -1,5 +1,5 @@ error[E0433]: failed to resolve: use of undeclared type `AtomicUsize` - --> $DIR/const_refs_to_static-ice-121413.rs:8:24 + --> $DIR/const_refs_to_static-ice-121413.rs:9:24 | LL | static FOO: Sync = AtomicUsize::new(0); | ^^^^^^^^^^^ use of undeclared type `AtomicUsize` @@ -10,7 +10,7 @@ LL + use std::sync::atomic::AtomicUsize; | warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/const_refs_to_static-ice-121413.rs:8:17 + --> $DIR/const_refs_to_static-ice-121413.rs:9:17 | LL | static FOO: Sync = AtomicUsize::new(0); | ^^^^ @@ -24,7 +24,7 @@ LL | static FOO: dyn Sync = AtomicUsize::new(0); | +++ error[E0277]: the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time - --> $DIR/const_refs_to_static-ice-121413.rs:8:17 + --> $DIR/const_refs_to_static-ice-121413.rs:9:17 | LL | static FOO: Sync = AtomicUsize::new(0); | ^^^^ doesn't have a size known at compile-time @@ -32,7 +32,7 @@ LL | static FOO: Sync = AtomicUsize::new(0); = help: the trait `Sized` is not implemented for `(dyn Sync + 'static)` error[E0277]: the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time - --> $DIR/const_refs_to_static-ice-121413.rs:8:24 + --> $DIR/const_refs_to_static-ice-121413.rs:9:24 | LL | static FOO: Sync = AtomicUsize::new(0); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time diff --git a/tests/ui/consts/copy-intrinsic.rs b/tests/ui/consts/copy-intrinsic.rs index 94d7bdc6bae9..4183dc0fcd6d 100644 --- a/tests/ui/consts/copy-intrinsic.rs +++ b/tests/ui/consts/copy-intrinsic.rs @@ -23,16 +23,20 @@ const COPY_ZERO: () = unsafe { const COPY_OOB_1: () = unsafe { let mut x = 0i32; let dangle = (&mut x as *mut i32).wrapping_add(10); - // Even if the first ptr is an int ptr and this is a ZST copy, we should detect dangling 2nd ptrs. - copy_nonoverlapping(0x100 as *const i32, dangle, 0); //~ ERROR evaluation of constant value failed [E0080] - //~| pointer at offset 40 is out-of-bounds + // Zero-sized copy is fine. + copy_nonoverlapping(0x100 as *const i32, dangle, 0); + // Non-zero-sized copy is not. + copy_nonoverlapping(0x100 as *const i32, dangle, 1); //~ ERROR evaluation of constant value failed [E0080] + //~| 0x100[noalloc] is a dangling pointer }; const COPY_OOB_2: () = unsafe { let x = 0i32; let dangle = (&x as *const i32).wrapping_add(10); - // Even if the second ptr is an int ptr and this is a ZST copy, we should detect dangling 1st ptrs. - copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); //~ ERROR evaluation of constant value failed [E0080] - //~| pointer at offset 40 is out-of-bounds + // Zero-sized copy is fine. + copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); + // Non-zero-sized copy is not. + copy_nonoverlapping(dangle, 0x100 as *mut i32, 1); //~ ERROR evaluation of constant value failed [E0080] + //~| offset 40 is out-of-bounds }; const COPY_SIZE_OVERFLOW: () = unsafe { diff --git a/tests/ui/consts/copy-intrinsic.stderr b/tests/ui/consts/copy-intrinsic.stderr index 0e4e6a6ad6c4..d34e61cd9627 100644 --- a/tests/ui/consts/copy-intrinsic.stderr +++ b/tests/ui/consts/copy-intrinsic.stderr @@ -1,23 +1,23 @@ error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:27:5 + --> $DIR/copy-intrinsic.rs:29:5 | -LL | copy_nonoverlapping(0x100 as *const i32, dangle, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC0 has size 4, so pointer at offset 40 is out-of-bounds +LL | copy_nonoverlapping(0x100 as *const i32, dangle, 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: 0x100[noalloc] is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:34:5 + --> $DIR/copy-intrinsic.rs:38:5 | -LL | copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC1 has size 4, so pointer at offset 40 is out-of-bounds +LL | copy_nonoverlapping(dangle, 0x100 as *mut i32, 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC0 has size 4, so pointer to 4 bytes starting at offset 40 is out-of-bounds error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:41:5 + --> $DIR/copy-intrinsic.rs:45:5 | LL | copy(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy` error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:47:5 + --> $DIR/copy-intrinsic.rs:51:5 | LL | copy_nonoverlapping(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy_nonoverlapping` diff --git a/tests/ui/consts/dangling-zst-ice-issue-126393.rs b/tests/ui/consts/dangling-zst-ice-issue-126393.rs new file mode 100644 index 000000000000..917aa0572fc0 --- /dev/null +++ b/tests/ui/consts/dangling-zst-ice-issue-126393.rs @@ -0,0 +1,15 @@ +// Strip out raw byte dumps to make comparison platform-independent: +//@ normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" +//@ normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +//@ normalize-stderr-test "HEX_DUMP\s*\n\s*HEX_DUMP" -> "HEX_DUMP" + +pub struct Wrapper; +pub static MAGIC_FFI_REF: &'static Wrapper = unsafe { +//~^ERROR: it is undefined behavior to use this value + std::mem::transmute(&{ + let y = 42; + y + }) +}; + +fn main() {} diff --git a/tests/ui/consts/dangling-zst-ice-issue-126393.stderr b/tests/ui/consts/dangling-zst-ice-issue-126393.stderr new file mode 100644 index 000000000000..d32b427f14ef --- /dev/null +++ b/tests/ui/consts/dangling-zst-ice-issue-126393.stderr @@ -0,0 +1,14 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/dangling-zst-ice-issue-126393.rs:7:1 + | +LL | pub static MAGIC_FFI_REF: &'static Wrapper = unsafe { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (use-after-free) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { + HEX_DUMP + } + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/erroneous_type_in_const_return_value.rs b/tests/ui/consts/erroneous_type_in_const_return_value.rs new file mode 100644 index 000000000000..304211f77da7 --- /dev/null +++ b/tests/ui/consts/erroneous_type_in_const_return_value.rs @@ -0,0 +1,12 @@ +//! ICE test #124348 +//! We should not be running const eval if the layout has errors. + +enum Eek { + TheConst, + UnusedByTheConst(Sum), + //~^ ERROR cannot find type `Sum` in this scope +} + +const EEK_ZERO: &[Eek] = &[]; + +fn main() {} diff --git a/tests/ui/consts/erroneous_type_in_const_return_value.stderr b/tests/ui/consts/erroneous_type_in_const_return_value.stderr new file mode 100644 index 000000000000..453f5b640973 --- /dev/null +++ b/tests/ui/consts/erroneous_type_in_const_return_value.stderr @@ -0,0 +1,14 @@ +error[E0412]: cannot find type `Sum` in this scope + --> $DIR/erroneous_type_in_const_return_value.rs:6:22 + | +LL | UnusedByTheConst(Sum), + | ^^^ not found in this scope + | +help: consider importing this trait + | +LL + use std::iter::Sum; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/consts/erroneous_type_in_promoted.rs b/tests/ui/consts/erroneous_type_in_promoted.rs new file mode 100644 index 000000000000..32e33c2030fb --- /dev/null +++ b/tests/ui/consts/erroneous_type_in_promoted.rs @@ -0,0 +1,14 @@ +//! ICE test #124348 +//! We should not be running const eval if the layout has errors. + +enum Eek { + TheConst, + UnusedByTheConst(Sum), + //~^ ERROR cannot find type `Sum` in this scope +} + +const fn foo() { + let x: &'static [Eek] = &[]; +} + +fn main() {} diff --git a/tests/ui/consts/erroneous_type_in_promoted.stderr b/tests/ui/consts/erroneous_type_in_promoted.stderr new file mode 100644 index 000000000000..0601fc9ebe7f --- /dev/null +++ b/tests/ui/consts/erroneous_type_in_promoted.stderr @@ -0,0 +1,14 @@ +error[E0412]: cannot find type `Sum` in this scope + --> $DIR/erroneous_type_in_promoted.rs:6:22 + | +LL | UnusedByTheConst(Sum), + | ^^^ not found in this scope + | +help: consider importing this trait + | +LL + use std::iter::Sum; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/consts/fn_trait_refs.stderr b/tests/ui/consts/fn_trait_refs.stderr index aad0ae64e858..4fb82c0683dc 100644 --- a/tests/ui/consts/fn_trait_refs.stderr +++ b/tests/ui/consts/fn_trait_refs.stderr @@ -107,8 +107,8 @@ LL | f() = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants help: consider further restricting this bound | -LL | T: ~const Fn<()> + ~const Destruct + ~const std::ops::Fn<()>, - | +++++++++++++++++++++++++ +LL | T: ~const Fn<()> + ~const Destruct + ~const Fn(), + | +++++++++++++ help: add `#![feature(effects)]` to the crate attributes to enable | LL + #![feature(effects)] @@ -132,8 +132,8 @@ LL | f() = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants help: consider further restricting this bound | -LL | T: ~const FnMut<()> + ~const Destruct + ~const std::ops::FnMut<()>, - | ++++++++++++++++++++++++++++ +LL | T: ~const FnMut<()> + ~const Destruct + ~const FnMut(), + | ++++++++++++++++ help: add `#![feature(effects)]` to the crate attributes to enable | LL + #![feature(effects)] @@ -157,8 +157,8 @@ LL | f() = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants help: consider further restricting this bound | -LL | T: ~const FnOnce<()> + ~const std::ops::FnOnce<()>, - | +++++++++++++++++++++++++++++ +LL | T: ~const FnOnce<()> + ~const FnOnce(), + | +++++++++++++++++ help: add `#![feature(effects)]` to the crate attributes to enable | LL + #![feature(effects)] diff --git a/tests/ui/consts/issue-103790.rs b/tests/ui/consts/issue-103790.rs index 5d130821dc8e..d19115ede74b 100644 --- a/tests/ui/consts/issue-103790.rs +++ b/tests/ui/consts/issue-103790.rs @@ -5,7 +5,7 @@ struct S; //~^ ERROR the name `S` is already used for a generic parameter in this item's generic parameters //~| ERROR missing generics for struct `S` //~| ERROR cycle detected when computing type of `S::S` -//~| ERROR cycle detected when computing type of `S` //~| ERROR `()` is forbidden as the type of a const generic parameter +//~| ERROR `S<{const error}, {const error}>` is forbidden as the type of a const generic parameter fn main() {} diff --git a/tests/ui/consts/issue-103790.stderr b/tests/ui/consts/issue-103790.stderr index eecaf5ff63a3..c671f078cb5c 100644 --- a/tests/ui/consts/issue-103790.stderr +++ b/tests/ui/consts/issue-103790.stderr @@ -29,26 +29,7 @@ LL | struct S; | ^ | = note: ...which immediately requires computing type of `S::S` again -note: cycle used when computing type of `S` - --> $DIR/issue-103790.rs:4:1 - | -LL | struct S; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error[E0391]: cycle detected when computing type of `S` - --> $DIR/issue-103790.rs:4:1 - | -LL | struct S; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...which requires computing type of `S::S`... - --> $DIR/issue-103790.rs:4:32 - | -LL | struct S; - | ^ - = note: ...which again requires computing type of `S`, completing the cycle -note: cycle used when checking that `S` is well-formed +note: cycle used when computing explicit predicates of `S` --> $DIR/issue-103790.rs:4:1 | LL | struct S; @@ -67,6 +48,18 @@ help: add `#![feature(adt_const_params)]` to the crate attributes to enable more LL + #![feature(adt_const_params)] | +error: `S<{const error}, {const error}>` is forbidden as the type of a const generic parameter + --> $DIR/issue-103790.rs:4:32 + | +LL | struct S; + | ^ + | + = note: the only supported types are integers, `bool` and `char` +help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types + | +LL + #![feature(adt_const_params)] + | + error: aborting due to 5 previous errors Some errors have detailed explanations: E0107, E0391, E0403. diff --git a/tests/ui/consts/issue-16538.stderr b/tests/ui/consts/issue-16538.stderr index 3981b4ada491..c4f5364b4d78 100644 --- a/tests/ui/consts/issue-16538.stderr +++ b/tests/ui/consts/issue-16538.stderr @@ -5,7 +5,7 @@ LL | static foo: &Y::X = &*Y::foo(Y::x as *const Y::X); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block --> $DIR/issue-16538.rs:11:22 diff --git a/tests/ui/consts/issue-32829-2.stderr b/tests/ui/consts/issue-32829-2.stderr index 0fec3581873e..bd0b8c15b558 100644 --- a/tests/ui/consts/issue-32829-2.stderr +++ b/tests/ui/consts/issue-32829-2.stderr @@ -13,7 +13,7 @@ LL | invalid(); | ^^^^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` error[E0015]: cannot call non-const fn `invalid` in statics --> $DIR/issue-32829-2.rs:54:9 @@ -22,7 +22,7 @@ LL | invalid(); | ^^^^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` error: aborting due to 3 previous errors diff --git a/tests/ui/consts/issue-89088.rs b/tests/ui/consts/issue-89088.rs index d0782963dfc8..02a786e74651 100644 --- a/tests/ui/consts/issue-89088.rs +++ b/tests/ui/consts/issue-89088.rs @@ -1,8 +1,5 @@ // Regression test for the ICE described in #89088. -//@ check-pass - -#![allow(indirect_structural_match)] use std::borrow::Cow; const FOO: &A = &A::Field(Cow::Borrowed("foo")); @@ -16,7 +13,7 @@ fn main() { let var = A::Field(Cow::Borrowed("bar")); match &var { - FOO => todo!(), + FOO => todo!(), //~ERROR derive(PartialEq) _ => todo!() } } diff --git a/tests/ui/consts/issue-89088.stderr b/tests/ui/consts/issue-89088.stderr index 7cb85d5279d0..362c63a2a456 100644 --- a/tests/ui/consts/issue-89088.stderr +++ b/tests/ui/consts/issue-89088.stderr @@ -1,12 +1,11 @@ -Future incompatibility report: Future breakage diagnostic: -warning: to use a constant of type `Cow<'_, str>` in a pattern, `Cow<'_, str>` must be annotated with `#[derive(PartialEq)]` - --> $DIR/issue-89088.rs:19:9 +error: to use a constant of type `Cow<'_, str>` in a pattern, `Cow<'_, str>` must be annotated with `#[derive(PartialEq)]` + --> $DIR/issue-89088.rs:16:9 | LL | FOO => todo!(), | ^^^ | - = warning: 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 #120362 = note: the traits must be derived, manual `impl`s are not sufficient = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +error: aborting due to 1 previous error + diff --git a/tests/ui/consts/mir_check_nonconst.stderr b/tests/ui/consts/mir_check_nonconst.stderr index ea6a8b8ee4a5..95d64622ad7a 100644 --- a/tests/ui/consts/mir_check_nonconst.stderr +++ b/tests/ui/consts/mir_check_nonconst.stderr @@ -5,7 +5,7 @@ LL | static foo: Foo = bar(); | ^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` error: aborting due to 1 previous error diff --git a/tests/ui/consts/offset_from_ub.rs b/tests/ui/consts/offset_from_ub.rs index e0dd27079156..57767e965962 100644 --- a/tests/ui/consts/offset_from_ub.rs +++ b/tests/ui/consts/offset_from_ub.rs @@ -34,8 +34,8 @@ pub const NOT_MULTIPLE_OF_SIZE: isize = { pub const OFFSET_FROM_NULL: isize = { let ptr = 0 as *const u8; - unsafe { ptr_offset_from(ptr, ptr) } //~ERROR evaluation of constant value failed - //~| null pointer is a dangling pointer + // Null isn't special for zero-sized "accesses" (i.e., the range between the two pointers) + unsafe { ptr_offset_from(ptr, ptr) } }; pub const DIFFERENT_INT: isize = { // offset_from with two different integers: like DIFFERENT_ALLOC @@ -67,8 +67,8 @@ const OUT_OF_BOUNDS_SAME: isize = { let start_ptr = &4 as *const _ as *const u8; let length = 10; let end_ptr = (start_ptr).wrapping_add(length); - unsafe { ptr_offset_from(end_ptr, end_ptr) } //~ERROR evaluation of constant value failed - //~| pointer at offset 10 is out-of-bounds + // Out-of-bounds is fine as long as the range between the pointers is empty. + unsafe { ptr_offset_from(end_ptr, end_ptr) } }; pub const DIFFERENT_ALLOC_UNSIGNED: usize = { diff --git a/tests/ui/consts/offset_from_ub.stderr b/tests/ui/consts/offset_from_ub.stderr index e3bac8d5e31a..65f75a6e0587 100644 --- a/tests/ui/consts/offset_from_ub.stderr +++ b/tests/ui/consts/offset_from_ub.stderr @@ -23,12 +23,6 @@ error[E0080]: evaluation of constant value failed LL | unsafe { ptr_offset_from(field_ptr, base_ptr as *const u16) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ exact_div: 1_isize cannot be divided by 2_isize without remainder -error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:37:14 - | -LL | unsafe { ptr_offset_from(ptr, ptr) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance) - error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:44:14 | @@ -47,12 +41,6 @@ error[E0080]: evaluation of constant value failed LL | unsafe { ptr_offset_from(start_ptr, end_ptr) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC1 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds -error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:70:14 - | -LL | unsafe { ptr_offset_from(end_ptr, end_ptr) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC2 has size 4, so pointer at offset 10 is out-of-bounds - error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:79:14 | @@ -109,6 +97,6 @@ note: inside `OFFSET_VERY_FAR2` LL | unsafe { ptr1.offset_from(ptr2.wrapping_offset(1)) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error: aborting due to 13 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/offset_ub.rs b/tests/ui/consts/offset_ub.rs index 920ecb687cf5..36e4ff1281a1 100644 --- a/tests/ui/consts/offset_ub.rs +++ b/tests/ui/consts/offset_ub.rs @@ -17,10 +17,10 @@ pub const NEGATIVE_OFFSET: *const u8 = unsafe { [0u8; 1].as_ptr().wrapping_offse pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; //~NOTE pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ptr().offset(4) }; //~NOTE -// Right now, a zero offset from null is UB -pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; //~NOTE - // Make sure that we don't panic when computing abs(offset*size_of::()) pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; //~NOTE +// Offset-by-zero is allowed. +pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; + fn main() {} diff --git a/tests/ui/consts/offset_ub.stderr b/tests/ui/consts/offset_ub.stderr index b398b20393fc..89371f06d9d6 100644 --- a/tests/ui/consts/offset_ub.stderr +++ b/tests/ui/consts/offset_ub.stderr @@ -128,19 +128,6 @@ note: inside `DANGLING` LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ptr().offset(4) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0080]: evaluation of constant value failed - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | - = note: out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance) - | -note: inside `std::ptr::const_ptr::::offset` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL -note: inside `NULL_OFFSET_ZERO` - --> $DIR/offset_ub.rs:21:50 - | -LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | @@ -149,11 +136,11 @@ error[E0080]: evaluation of constant value failed note: inside `std::ptr::const_ptr::::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL note: inside `UNDERFLOW_ABS` - --> $DIR/offset_ub.rs:24:47 + --> $DIR/offset_ub.rs:21:47 | LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/static-default-lifetime/elided-lifetime.rs b/tests/ui/consts/static-default-lifetime/elided-lifetime.rs new file mode 100644 index 000000000000..95d59f9b894e --- /dev/null +++ b/tests/ui/consts/static-default-lifetime/elided-lifetime.rs @@ -0,0 +1,22 @@ +#![deny(elided_lifetimes_in_associated_constant)] + +struct Foo<'a>(&'a ()); + +impl Foo<'_> { + const STATIC: &str = ""; + //~^ ERROR `&` without an explicit lifetime name cannot be used here + //~| WARN this was previously accepted by the compiler but is being phased out +} + +trait Bar { + const STATIC: &str; +} + +impl Bar for Foo<'_> { + const STATIC: &str = ""; + //~^ ERROR `&` without an explicit lifetime name cannot be used here + //~| WARN this was previously accepted by the compiler but is being phased out + //~| ERROR const not compatible with trait +} + +fn main() {} diff --git a/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr b/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr new file mode 100644 index 000000000000..d4fc17175389 --- /dev/null +++ b/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr @@ -0,0 +1,59 @@ +error: `&` without an explicit lifetime name cannot be used here + --> $DIR/elided-lifetime.rs:6:19 + | +LL | const STATIC: &str = ""; + | ^ + | + = warning: 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 #115010 +note: cannot automatically infer `'static` because of other lifetimes in scope + --> $DIR/elided-lifetime.rs:5:10 + | +LL | impl Foo<'_> { + | ^^ +note: the lint level is defined here + --> $DIR/elided-lifetime.rs:1:9 + | +LL | #![deny(elided_lifetimes_in_associated_constant)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: use the `'static` lifetime + | +LL | const STATIC: &'static str = ""; + | +++++++ + +error: `&` without an explicit lifetime name cannot be used here + --> $DIR/elided-lifetime.rs:16:19 + | +LL | const STATIC: &str = ""; + | ^ + | + = warning: 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 #115010 +note: cannot automatically infer `'static` because of other lifetimes in scope + --> $DIR/elided-lifetime.rs:15:18 + | +LL | impl Bar for Foo<'_> { + | ^^ +help: use the `'static` lifetime + | +LL | const STATIC: &'static str = ""; + | +++++++ + +error[E0308]: const not compatible with trait + --> $DIR/elided-lifetime.rs:16:5 + | +LL | const STATIC: &str = ""; + | ^^^^^^^^^^^^^^^^^^ lifetime mismatch + | + = note: expected reference `&'static _` + found reference `&_` +note: the anonymous lifetime as defined here... + --> $DIR/elided-lifetime.rs:15:18 + | +LL | impl Bar for Foo<'_> { + | ^^ + = note: ...does not necessarily outlive the static lifetime + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/consts/static-default-lifetime/generic-associated-const.rs b/tests/ui/consts/static-default-lifetime/generic-associated-const.rs new file mode 100644 index 000000000000..721920bd810c --- /dev/null +++ b/tests/ui/consts/static-default-lifetime/generic-associated-const.rs @@ -0,0 +1,19 @@ +#![deny(elided_lifetimes_in_associated_constant)] +#![feature(generic_const_items)] +//~^ WARN the feature `generic_const_items` is incomplete + +struct A; +impl A { + const GAC_TYPE: &str = ""; + const GAC_LIFETIME<'a>: &str = ""; + //~^ ERROR `&` without an explicit lifetime name cannot be used here + //~| WARN this was previously accepted by the compiler but is being phased out +} + +trait Trait { + const GAC_TYPE: &str = ""; + const GAC_LIFETIME<'a>: &str = ""; + //~^ ERROR missing lifetime specifier +} + +fn main() {} diff --git a/tests/ui/consts/static-default-lifetime/generic-associated-const.stderr b/tests/ui/consts/static-default-lifetime/generic-associated-const.stderr new file mode 100644 index 000000000000..2ecab3442a96 --- /dev/null +++ b/tests/ui/consts/static-default-lifetime/generic-associated-const.stderr @@ -0,0 +1,46 @@ +error[E0106]: missing lifetime specifier + --> $DIR/generic-associated-const.rs:15:29 + | +LL | const GAC_LIFETIME<'a>: &str = ""; + | ^ expected named lifetime parameter + | +help: consider using the `'a` lifetime + | +LL | const GAC_LIFETIME<'a>: &'a str = ""; + | ++ + +warning: the feature `generic_const_items` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/generic-associated-const.rs:2:12 + | +LL | #![feature(generic_const_items)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #113521 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: `&` without an explicit lifetime name cannot be used here + --> $DIR/generic-associated-const.rs:8:29 + | +LL | const GAC_LIFETIME<'a>: &str = ""; + | ^ + | + = warning: 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 #115010 +note: cannot automatically infer `'static` because of other lifetimes in scope + --> $DIR/generic-associated-const.rs:8:24 + | +LL | const GAC_LIFETIME<'a>: &str = ""; + | ^^ +note: the lint level is defined here + --> $DIR/generic-associated-const.rs:1:9 + | +LL | #![deny(elided_lifetimes_in_associated_constant)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: use the `'static` lifetime + | +LL | const GAC_LIFETIME<'a>: &'static str = ""; + | +++++++ + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/consts/static-default-lifetime/inner-item.rs b/tests/ui/consts/static-default-lifetime/inner-item.rs new file mode 100644 index 000000000000..061b240a40d8 --- /dev/null +++ b/tests/ui/consts/static-default-lifetime/inner-item.rs @@ -0,0 +1,21 @@ +//@ check-pass + +struct Foo<'a>(&'a ()); + +impl<'a> Foo<'a> { + fn hello(self) { + const INNER: &str = ""; + } +} + +impl Foo<'_> { + fn implicit(self) { + const INNER: &str = ""; + } + + fn fn_lifetime(&self) { + const INNER: &str = ""; + } +} + +fn main() {} diff --git a/tests/ui/consts/static-default-lifetime/static-trait-impl.rs b/tests/ui/consts/static-default-lifetime/static-trait-impl.rs new file mode 100644 index 000000000000..025fda4df581 --- /dev/null +++ b/tests/ui/consts/static-default-lifetime/static-trait-impl.rs @@ -0,0 +1,20 @@ +#![deny(elided_lifetimes_in_associated_constant)] + +trait Bar<'a> { + const STATIC: &'a str; +} + +struct A; +impl Bar<'_> for A { + const STATIC: &str = ""; + //~^ ERROR `&` without an explicit lifetime name cannot be used here + //~| WARN this was previously accepted by the compiler but is being phased out + //~| ERROR const not compatible with trait +} + +struct B; +impl Bar<'static> for B { + const STATIC: &str = ""; +} + +fn main() {} diff --git a/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr b/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr new file mode 100644 index 000000000000..8e4c27875ab3 --- /dev/null +++ b/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr @@ -0,0 +1,45 @@ +error: `&` without an explicit lifetime name cannot be used here + --> $DIR/static-trait-impl.rs:9:19 + | +LL | const STATIC: &str = ""; + | ^ + | + = warning: 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 #115010 +note: cannot automatically infer `'static` because of other lifetimes in scope + --> $DIR/static-trait-impl.rs:8:10 + | +LL | impl Bar<'_> for A { + | ^^ +note: the lint level is defined here + --> $DIR/static-trait-impl.rs:1:9 + | +LL | #![deny(elided_lifetimes_in_associated_constant)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: use the `'static` lifetime + | +LL | const STATIC: &'static str = ""; + | +++++++ + +error[E0308]: const not compatible with trait + --> $DIR/static-trait-impl.rs:9:5 + | +LL | const STATIC: &str = ""; + | ^^^^^^^^^^^^^^^^^^ lifetime mismatch + | + = note: expected reference `&_` + found reference `&_` +note: the anonymous lifetime as defined here... + --> $DIR/static-trait-impl.rs:8:10 + | +LL | impl Bar<'_> for A { + | ^^ +note: ...does not necessarily outlive the anonymous lifetime as defined here + --> $DIR/static-trait-impl.rs:8:10 + | +LL | impl Bar<'_> for A { + | ^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/consts/too_generic_eval_ice.stderr b/tests/ui/consts/too_generic_eval_ice.stderr index 54dffa3befcf..58a61b938d6c 100644 --- a/tests/ui/consts/too_generic_eval_ice.stderr +++ b/tests/ui/consts/too_generic_eval_ice.stderr @@ -22,14 +22,14 @@ LL | [5; Self::HOST_SIZE] == [6; 0] | = help: the trait `PartialEq<[{integer}; 0]>` is not implemented for `[{integer}; Self::HOST_SIZE]` = help: the following other types implement trait `PartialEq`: - <&[T] as PartialEq>> - <&[T] as PartialEq<[U; N]>> - <&mut [T] as PartialEq>> - <&mut [T] as PartialEq<[U; N]>> - <[T; N] as PartialEq<&[U]>> - <[T; N] as PartialEq<&mut [U]>> - <[T; N] as PartialEq<[U; N]>> - <[T; N] as PartialEq<[U]>> + `&[T]` implements `PartialEq>` + `&[T]` implements `PartialEq<[U; N]>` + `&mut [T]` implements `PartialEq>` + `&mut [T]` implements `PartialEq<[U; N]>` + `[T; N]` implements `PartialEq<&[U]>` + `[T; N]` implements `PartialEq<&mut [U]>` + `[T; N]` implements `PartialEq<[U; N]>` + `[T; N]` implements `PartialEq<[U]>` and 3 others error: aborting due to 3 previous errors diff --git a/tests/ui/consts/unstable-const-fn-in-libcore.stderr b/tests/ui/consts/unstable-const-fn-in-libcore.stderr index 9590b3372e32..6c83eff4de01 100644 --- a/tests/ui/consts/unstable-const-fn-in-libcore.stderr +++ b/tests/ui/consts/unstable-const-fn-in-libcore.stderr @@ -13,8 +13,8 @@ LL | Opt::None => f(), = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants help: consider further restricting this bound | -LL | const fn unwrap_or_else T + ~const std::ops::FnOnce<()>>(self, f: F) -> T { - | +++++++++++++++++++++++++++++ +LL | const fn unwrap_or_else T + ~const FnOnce()>(self, f: F) -> T { + | +++++++++++++++++ help: add `#![feature(effects)]` to the crate attributes to enable | LL + #![feature(effects)] diff --git a/tests/ui/coroutine/async_gen_fn.none.stderr b/tests/ui/coroutine/async_gen_fn.none.stderr index 7950251a75da..047f4d82486d 100644 --- a/tests/ui/coroutine/async_gen_fn.none.stderr +++ b/tests/ui/coroutine/async_gen_fn.none.stderr @@ -7,11 +7,11 @@ LL | async gen fn foo() {} = help: pass `--edition 2021` to `rustc` = note: for more on editions, read https://doc.rust-lang.org/edition-guide -error: expected one of `extern`, `fn`, or `unsafe`, found `gen` +error: expected one of `extern`, `fn`, `safe`, or `unsafe`, found `gen` --> $DIR/async_gen_fn.rs:4:7 | LL | async gen fn foo() {} - | ^^^ expected one of `extern`, `fn`, or `unsafe` + | ^^^ expected one of `extern`, `fn`, `safe`, or `unsafe` error: aborting due to 2 previous errors diff --git a/tests/ui/coroutine/async_gen_fn.rs b/tests/ui/coroutine/async_gen_fn.rs index 9e96ecf3ea69..e8be0434b9ef 100644 --- a/tests/ui/coroutine/async_gen_fn.rs +++ b/tests/ui/coroutine/async_gen_fn.rs @@ -3,7 +3,7 @@ async gen fn foo() {} //[none]~^ ERROR: `async fn` is not permitted in Rust 2015 -//[none]~| ERROR: expected one of `extern`, `fn`, or `unsafe`, found `gen` +//[none]~| ERROR: expected one of `extern`, `fn`, `safe`, or `unsafe`, found `gen` //[e2024]~^^^ ERROR: gen blocks are experimental fn main() {} diff --git a/tests/ui/coroutine/break-inside-coroutine-issue-124495.rs b/tests/ui/coroutine/break-inside-coroutine-issue-124495.rs index 5d93db56722b..97c3d06c023e 100644 --- a/tests/ui/coroutine/break-inside-coroutine-issue-124495.rs +++ b/tests/ui/coroutine/break-inside-coroutine-issue-124495.rs @@ -18,6 +18,7 @@ async gen fn async_gen_fn() { fn main() { let _ = async { break; }; //~ ERROR `break` inside `async` block + let _ = async || { break; }; //~ ERROR `break` inside `async` closure let _ = gen { break; }; //~ ERROR `break` inside `gen` block diff --git a/tests/ui/coroutine/break-inside-coroutine-issue-124495.stderr b/tests/ui/coroutine/break-inside-coroutine-issue-124495.stderr index a7f37fad35ea..f030961b7edd 100644 --- a/tests/ui/coroutine/break-inside-coroutine-issue-124495.stderr +++ b/tests/ui/coroutine/break-inside-coroutine-issue-124495.stderr @@ -38,16 +38,16 @@ LL | let _ = async { break; }; | enclosing `async` block error[E0267]: `break` inside `async` closure - --> $DIR/break-inside-coroutine-issue-124495.rs:21:24 + --> $DIR/break-inside-coroutine-issue-124495.rs:22:24 | LL | let _ = async || { break; }; - | --^^^^^--- - | | | - | | cannot `break` inside `async` closure - | enclosing `async` closure + | -----------^^^^^--- + | | | + | | cannot `break` inside `async` closure + | enclosing `async` closure error[E0267]: `break` inside `gen` block - --> $DIR/break-inside-coroutine-issue-124495.rs:23:19 + --> $DIR/break-inside-coroutine-issue-124495.rs:24:19 | LL | let _ = gen { break; }; | ------^^^^^--- @@ -56,7 +56,7 @@ LL | let _ = gen { break; }; | enclosing `gen` block error[E0267]: `break` inside `async gen` block - --> $DIR/break-inside-coroutine-issue-124495.rs:25:25 + --> $DIR/break-inside-coroutine-issue-124495.rs:26:25 | LL | let _ = async gen { break; }; | ------------^^^^^--- diff --git a/tests/ui/coroutine/gen_fn.none.stderr b/tests/ui/coroutine/gen_fn.none.stderr index c5342ee22e62..590210641aed 100644 --- a/tests/ui/coroutine/gen_fn.none.stderr +++ b/tests/ui/coroutine/gen_fn.none.stderr @@ -1,8 +1,8 @@ -error: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen` +error: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` --> $DIR/gen_fn.rs:4:1 | LL | gen fn foo() {} - | ^^^ expected one of 9 possible tokens + | ^^^ expected one of 10 possible tokens error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/gen_fn.rs b/tests/ui/coroutine/gen_fn.rs index 3228650f4152..d47b7e576d00 100644 --- a/tests/ui/coroutine/gen_fn.rs +++ b/tests/ui/coroutine/gen_fn.rs @@ -2,7 +2,7 @@ //@[e2024] compile-flags: --edition 2024 -Zunstable-options gen fn foo() {} -//[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen` +//[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` //[e2024]~^^ ERROR: gen blocks are experimental fn main() {} diff --git a/tests/ui/coroutine/layout-error.rs b/tests/ui/coroutine/layout-error.rs index 70a0248eabcc..3e26cf17d29e 100644 --- a/tests/ui/coroutine/layout-error.rs +++ b/tests/ui/coroutine/layout-error.rs @@ -16,13 +16,22 @@ impl Task { } } -fn main() { - async fn cb() { - let a = Foo; //~ ERROR cannot find value `Foo` in this scope - } +mod helper { + use super::*; + pub type F = impl Future; + fn foo() + where + F:, + { + async fn cb() { + let a = Foo; //~ ERROR cannot find value `Foo` in this scope + } - type F = impl Future; - // Check that statics are inhabited computes they layout. - static POOL: Task = Task::new(); - Task::spawn(&POOL, || cb()); + Task::spawn(&POOL, || cb()); + } } + +// Check that statics are inhabited computes they layout. +static POOL: Task = Task::new(); + +fn main() {} diff --git a/tests/ui/coroutine/layout-error.stderr b/tests/ui/coroutine/layout-error.stderr index 249a99e9b85c..ceadb62c9998 100644 --- a/tests/ui/coroutine/layout-error.stderr +++ b/tests/ui/coroutine/layout-error.stderr @@ -1,8 +1,8 @@ error[E0425]: cannot find value `Foo` in this scope - --> $DIR/layout-error.rs:21:17 + --> $DIR/layout-error.rs:27:21 | -LL | let a = Foo; - | ^^^ not found in this scope +LL | let a = Foo; + | ^^^ not found in this scope error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/metadata-sufficient-for-layout.rs b/tests/ui/coroutine/metadata-sufficient-for-layout.rs index 23937e12c521..9c3a7e4378e3 100644 --- a/tests/ui/coroutine/metadata-sufficient-for-layout.rs +++ b/tests/ui/coroutine/metadata-sufficient-for-layout.rs @@ -4,22 +4,23 @@ // Regression test for #80998. // //@ aux-build:metadata-sufficient-for-layout.rs +//@ check-pass #![feature(type_alias_impl_trait, rustc_attrs)] #![feature(coroutine_trait)] extern crate metadata_sufficient_for_layout; -use std::ops::Coroutine; +mod helper { + use std::ops::Coroutine; + pub type F = impl Coroutine<(), Yield = (), Return = ()>; -type F = impl Coroutine<(), Yield = (), Return = ()>; - -// Static queries the layout of the coroutine. -static A: Option = None; - -fn f() -> F { - metadata_sufficient_for_layout::g() + fn f() -> F { + metadata_sufficient_for_layout::g() + } } -#[rustc_error] -fn main() {} //~ ERROR +// Static queries the layout of the coroutine. +static A: Option = None; + +fn main() {} diff --git a/tests/ui/coroutine/metadata-sufficient-for-layout.stderr b/tests/ui/coroutine/metadata-sufficient-for-layout.stderr deleted file mode 100644 index c7860fde2a9f..000000000000 --- a/tests/ui/coroutine/metadata-sufficient-for-layout.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: fatal error triggered by #[rustc_error] - --> $DIR/metadata-sufficient-for-layout.rs:25:1 - | -LL | fn main() {} - | ^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/coroutine/static-move-suggestion.fixed b/tests/ui/coroutine/static-move-suggestion.fixed new file mode 100644 index 000000000000..56445be4715f --- /dev/null +++ b/tests/ui/coroutine/static-move-suggestion.fixed @@ -0,0 +1,19 @@ +//@ run-rustfix + +// check to make sure that we suggest adding `move` after `static` + +#![feature(coroutines)] + +fn check() -> impl Sized { + let x = 0; + #[coroutine] + static move || { + //~^ ERROR E0373 + yield; + x + } +} + +fn main() { + check(); +} diff --git a/tests/ui/coroutine/static-move-suggestion.rs b/tests/ui/coroutine/static-move-suggestion.rs new file mode 100644 index 000000000000..1d6e4a628831 --- /dev/null +++ b/tests/ui/coroutine/static-move-suggestion.rs @@ -0,0 +1,19 @@ +//@ run-rustfix + +// check to make sure that we suggest adding `move` after `static` + +#![feature(coroutines)] + +fn check() -> impl Sized { + let x = 0; + #[coroutine] + static || { + //~^ ERROR E0373 + yield; + x + } +} + +fn main() { + check(); +} diff --git a/tests/ui/coroutine/static-move-suggestion.stderr b/tests/ui/coroutine/static-move-suggestion.stderr new file mode 100644 index 000000000000..6d890468b322 --- /dev/null +++ b/tests/ui/coroutine/static-move-suggestion.stderr @@ -0,0 +1,26 @@ +error[E0373]: coroutine may outlive the current function, but it borrows `x`, which is owned by the current function + --> $DIR/static-move-suggestion.rs:10:5 + | +LL | static || { + | ^^^^^^^^^ may outlive borrowed value `x` +... +LL | x + | - `x` is borrowed here + | +note: coroutine is returned here + --> $DIR/static-move-suggestion.rs:10:5 + | +LL | / static || { +LL | | +LL | | yield; +LL | | x +LL | | } + | |_____^ +help: to force the coroutine to take ownership of `x` (and any other referenced variables), use the `move` keyword + | +LL | static move || { + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0373`. diff --git a/tests/ui/coverage-attr/bad-syntax.rs b/tests/ui/coverage-attr/bad-syntax.rs new file mode 100644 index 000000000000..8783714992b7 --- /dev/null +++ b/tests/ui/coverage-attr/bad-syntax.rs @@ -0,0 +1,58 @@ +#![feature(coverage_attribute)] + +// Tests the error messages produced (or not produced) by various unusual +// uses of the `#[coverage(..)]` attribute. + +// FIXME(#84605): Multiple coverage attributes with the same value are useless, +// and should probably produce a diagnostic. +#[coverage(off)] +#[coverage(off)] +fn multiple_consistent() {} + +// FIXME(#84605): When there are multiple inconsistent coverage attributes, +// it's unclear which one will prevail. +#[coverage(off)] +#[coverage(on)] +fn multiple_inconsistent() {} + +#[coverage] //~ ERROR expected `coverage(off)` or `coverage(on)` +fn bare_word() {} + +// FIXME(#84605): This shows as multiple different errors, one of which suggests +// writing bare `#[coverage]`, which is not allowed. +#[coverage = true] +//~^ ERROR expected `coverage(off)` or `coverage(on)` +//~| ERROR malformed `coverage` attribute input +//~| HELP the following are the possible correct uses +//~| SUGGESTION #[coverage(on|off)] +fn key_value() {} + +#[coverage()] //~ ERROR expected `coverage(off)` or `coverage(on)` +fn list_empty() {} + +#[coverage(off, off)] //~ ERROR expected `coverage(off)` or `coverage(on)` +fn list_consistent() {} + +#[coverage(off, on)] //~ ERROR expected `coverage(off)` or `coverage(on)` +fn list_inconsistent() {} + +#[coverage(bogus)] //~ ERROR expected `coverage(off)` or `coverage(on)` +fn bogus_word() {} + +#[coverage(bogus, off)] //~ ERROR expected `coverage(off)` or `coverage(on)` +fn bogus_word_before() {} + +#[coverage(off, bogus)] //~ ERROR expected `coverage(off)` or `coverage(on)` +fn bogus_word_after() {} + +#[coverage(off,)] +fn comma_after() {} + +// FIXME(#84605): This shows as multiple different errors. +#[coverage(,off)] +//~^ ERROR expected identifier, found `,` +//~| HELP remove this comma +//~| ERROR expected `coverage(off)` or `coverage(on)` +fn comma_before() {} + +fn main() {} diff --git a/tests/ui/coverage-attr/bad-syntax.stderr b/tests/ui/coverage-attr/bad-syntax.stderr new file mode 100644 index 000000000000..f6181d12a946 --- /dev/null +++ b/tests/ui/coverage-attr/bad-syntax.stderr @@ -0,0 +1,78 @@ +error: malformed `coverage` attribute input + --> $DIR/bad-syntax.rs:23:1 + | +LL | #[coverage = true] + | ^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[coverage(on|off)] + | +LL | #[coverage] + | + +error: expected identifier, found `,` + --> $DIR/bad-syntax.rs:52:12 + | +LL | #[coverage(,off)] + | ^ + | | + | expected identifier + | help: remove this comma + +error: expected `coverage(off)` or `coverage(on)` + --> $DIR/bad-syntax.rs:18:1 + | +LL | #[coverage] + | ^^^^^^^^^^^ + +error: expected `coverage(off)` or `coverage(on)` + --> $DIR/bad-syntax.rs:23:1 + | +LL | #[coverage = true] + | ^^^^^^^^^^^^^^^^^^ + +error: expected `coverage(off)` or `coverage(on)` + --> $DIR/bad-syntax.rs:30:1 + | +LL | #[coverage()] + | ^^^^^^^^^^^^^ + +error: expected `coverage(off)` or `coverage(on)` + --> $DIR/bad-syntax.rs:33:1 + | +LL | #[coverage(off, off)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: expected `coverage(off)` or `coverage(on)` + --> $DIR/bad-syntax.rs:36:1 + | +LL | #[coverage(off, on)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: expected `coverage(off)` or `coverage(on)` + --> $DIR/bad-syntax.rs:39:1 + | +LL | #[coverage(bogus)] + | ^^^^^^^^^^^^^^^^^^ + +error: expected `coverage(off)` or `coverage(on)` + --> $DIR/bad-syntax.rs:42:1 + | +LL | #[coverage(bogus, off)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected `coverage(off)` or `coverage(on)` + --> $DIR/bad-syntax.rs:45:1 + | +LL | #[coverage(off, bogus)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected `coverage(off)` or `coverage(on)` + --> $DIR/bad-syntax.rs:52:1 + | +LL | #[coverage(,off)] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors + diff --git a/tests/ui/lint/no-coverage.rs b/tests/ui/coverage-attr/no-coverage.rs similarity index 100% rename from tests/ui/lint/no-coverage.rs rename to tests/ui/coverage-attr/no-coverage.rs diff --git a/tests/ui/lint/no-coverage.stderr b/tests/ui/coverage-attr/no-coverage.stderr similarity index 100% rename from tests/ui/lint/no-coverage.stderr rename to tests/ui/coverage-attr/no-coverage.stderr diff --git a/tests/ui/cross-crate/xcrate-address-insignificant.rs b/tests/ui/cross-crate/address-insignificant.rs similarity index 100% rename from tests/ui/cross-crate/xcrate-address-insignificant.rs rename to tests/ui/cross-crate/address-insignificant.rs diff --git a/tests/ui/cross-crate/xcrate-associated-type-defaults.rs b/tests/ui/cross-crate/associated-type-defaults.rs similarity index 100% rename from tests/ui/cross-crate/xcrate-associated-type-defaults.rs rename to tests/ui/cross-crate/associated-type-defaults.rs diff --git a/tests/ui/cross-crate/auxiliary/static_init_aux.rs b/tests/ui/cross-crate/auxiliary/static_init_aux.rs index dca708733b92..832cee8d4d4e 100644 --- a/tests/ui/cross-crate/auxiliary/static_init_aux.rs +++ b/tests/ui/cross-crate/auxiliary/static_init_aux.rs @@ -3,6 +3,12 @@ pub static F: fn() = f; pub static G: fn() = G0; pub static H: &(dyn Fn() + Sync) = &h; pub static I: fn() = Helper(j).mk(); +pub static K: fn() -> fn() = { + #[inline(never)] + fn k() {} + #[inline(always)] + || -> fn() { k } +}; static X: u32 = 42; static G0: fn() = g; diff --git a/tests/ui/xcrate/auxiliary/static_priv_by_default.rs b/tests/ui/cross-crate/auxiliary/static_priv_by_default.rs similarity index 100% rename from tests/ui/xcrate/auxiliary/static_priv_by_default.rs rename to tests/ui/cross-crate/auxiliary/static_priv_by_default.rs diff --git a/tests/ui/xcrate/auxiliary/xcrate_unit_struct.rs b/tests/ui/cross-crate/auxiliary/xcrate_unit_struct.rs similarity index 100% rename from tests/ui/xcrate/auxiliary/xcrate_unit_struct.rs rename to tests/ui/cross-crate/auxiliary/xcrate_unit_struct.rs diff --git a/tests/ui/cross-crate/xcrate_generic_fn_nested_return.rs b/tests/ui/cross-crate/generic_fn_nested_return.rs similarity index 100% rename from tests/ui/cross-crate/xcrate_generic_fn_nested_return.rs rename to tests/ui/cross-crate/generic_fn_nested_return.rs diff --git a/tests/ui/xcrate/xcrate-private-by-default.rs b/tests/ui/cross-crate/private-by-default.rs similarity index 100% rename from tests/ui/xcrate/xcrate-private-by-default.rs rename to tests/ui/cross-crate/private-by-default.rs diff --git a/tests/ui/xcrate/xcrate-private-by-default.stderr b/tests/ui/cross-crate/private-by-default.stderr similarity index 88% rename from tests/ui/xcrate/xcrate-private-by-default.stderr rename to tests/ui/cross-crate/private-by-default.stderr index 25bbbf5f62aa..398c9088aae2 100644 --- a/tests/ui/xcrate/xcrate-private-by-default.stderr +++ b/tests/ui/cross-crate/private-by-default.stderr @@ -1,5 +1,5 @@ error[E0603]: static `j` is private - --> $DIR/xcrate-private-by-default.rs:23:29 + --> $DIR/private-by-default.rs:23:29 | LL | static_priv_by_default::j; | ^ private static @@ -11,7 +11,7 @@ LL | static j: isize = 0; | ^^^^^^^^^^^^^^^ error[E0603]: function `k` is private - --> $DIR/xcrate-private-by-default.rs:25:29 + --> $DIR/private-by-default.rs:25:29 | LL | static_priv_by_default::k; | ^ private function @@ -23,7 +23,7 @@ LL | fn k() {} | ^^^^^^ error[E0603]: unit struct `l` is private - --> $DIR/xcrate-private-by-default.rs:27:29 + --> $DIR/private-by-default.rs:27:29 | LL | static_priv_by_default::l; | ^ private unit struct @@ -35,7 +35,7 @@ LL | struct l; | ^^^^^^^^ error[E0603]: enum `m` is private - --> $DIR/xcrate-private-by-default.rs:29:35 + --> $DIR/private-by-default.rs:29:35 | LL | foo::(); | ^ private enum @@ -47,7 +47,7 @@ LL | enum m {} | ^^^^^^ error[E0603]: type alias `n` is private - --> $DIR/xcrate-private-by-default.rs:31:35 + --> $DIR/private-by-default.rs:31:35 | LL | foo::(); | ^ private type alias @@ -59,7 +59,7 @@ LL | type n = isize; | ^^^^^^ error[E0603]: module `foo` is private - --> $DIR/xcrate-private-by-default.rs:35:29 + --> $DIR/private-by-default.rs:35:29 | LL | static_priv_by_default::foo::a; | ^^^ - static `a` is not publicly re-exported @@ -73,7 +73,7 @@ LL | mod foo { | ^^^^^^^ error[E0603]: module `foo` is private - --> $DIR/xcrate-private-by-default.rs:37:29 + --> $DIR/private-by-default.rs:37:29 | LL | static_priv_by_default::foo::b; | ^^^ - function `b` is not publicly re-exported @@ -87,7 +87,7 @@ LL | mod foo { | ^^^^^^^ error[E0603]: module `foo` is private - --> $DIR/xcrate-private-by-default.rs:39:29 + --> $DIR/private-by-default.rs:39:29 | LL | static_priv_by_default::foo::c; | ^^^ - unit struct `c` is not publicly re-exported @@ -101,7 +101,7 @@ LL | mod foo { | ^^^^^^^ error[E0603]: module `foo` is private - --> $DIR/xcrate-private-by-default.rs:41:35 + --> $DIR/private-by-default.rs:41:35 | LL | foo::(); | ^^^ - enum `d` is not publicly re-exported @@ -115,7 +115,7 @@ LL | mod foo { | ^^^^^^^ error[E0603]: module `foo` is private - --> $DIR/xcrate-private-by-default.rs:43:35 + --> $DIR/private-by-default.rs:43:35 | LL | foo::(); | ^^^ - type alias `e` is not publicly re-exported diff --git a/tests/ui/cross-crate/xcrate-static-addresses.rs b/tests/ui/cross-crate/static-addresses.rs similarity index 100% rename from tests/ui/cross-crate/xcrate-static-addresses.rs rename to tests/ui/cross-crate/static-addresses.rs diff --git a/tests/ui/cross-crate/static-init.rs b/tests/ui/cross-crate/static-init.rs index c4697a1d010c..f8003856c5c4 100644 --- a/tests/ui/cross-crate/static-init.rs +++ b/tests/ui/cross-crate/static-init.rs @@ -8,6 +8,7 @@ static F: fn() = aux::F; static G: fn() = aux::G; static H: &(dyn Fn() + Sync) = aux::H; static I: fn() = aux::I; +static K: fn() -> fn() = aux::K; fn v() -> *const u32 { V @@ -19,4 +20,5 @@ fn main() { G(); H(); I(); + K()(); } diff --git a/tests/ui/cross-crate/xcrate-trait-lifetime-param.rs b/tests/ui/cross-crate/trait-lifetime-param.rs similarity index 100% rename from tests/ui/cross-crate/xcrate-trait-lifetime-param.rs rename to tests/ui/cross-crate/trait-lifetime-param.rs diff --git a/tests/ui/xcrate/xcrate-unit-struct-2.rs b/tests/ui/cross-crate/unit-struct-2.rs similarity index 100% rename from tests/ui/xcrate/xcrate-unit-struct-2.rs rename to tests/ui/cross-crate/unit-struct-2.rs diff --git a/tests/ui/xcrate/xcrate-unit-struct.rs b/tests/ui/cross-crate/unit-struct.rs similarity index 100% rename from tests/ui/xcrate/xcrate-unit-struct.rs rename to tests/ui/cross-crate/unit-struct.rs diff --git a/tests/ui/xcrate/xcrate-unit-struct.stderr b/tests/ui/cross-crate/unit-struct.stderr similarity index 92% rename from tests/ui/xcrate/xcrate-unit-struct.stderr rename to tests/ui/cross-crate/unit-struct.stderr index 7365170b69ea..a7e3e4685a99 100644 --- a/tests/ui/xcrate/xcrate-unit-struct.stderr +++ b/tests/ui/cross-crate/unit-struct.stderr @@ -1,5 +1,5 @@ error[E0423]: expected value, found struct `xcrate_unit_struct::StructWithFields` - --> $DIR/xcrate-unit-struct.rs:9:13 + --> $DIR/unit-struct.rs:9:13 | LL | let _ = xcrate_unit_struct::StructWithFields; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `xcrate_unit_struct::StructWithFields { foo: val }` @@ -10,7 +10,7 @@ LL | pub struct StructWithFields { | --------------------------- `xcrate_unit_struct::StructWithFields` defined here error[E0423]: expected value, found struct `xcrate_unit_struct::StructWithPrivFields` - --> $DIR/xcrate-unit-struct.rs:11:13 + --> $DIR/unit-struct.rs:11:13 | LL | let _ = xcrate_unit_struct::StructWithPrivFields; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/debuginfo/debuginfo-emit-llvm-ir-and-split-debuginfo.rs b/tests/ui/debuginfo/debuginfo-emit-llvm-ir-and-split-debuginfo.rs index 861179650164..3322660d1a85 100644 --- a/tests/ui/debuginfo/debuginfo-emit-llvm-ir-and-split-debuginfo.rs +++ b/tests/ui/debuginfo/debuginfo-emit-llvm-ir-and-split-debuginfo.rs @@ -1,5 +1,6 @@ //@ build-pass //@ only-linux +//@ ignore-riscv64 On this platform `-Csplit-debuginfo=unpacked` is unstable, see #120518 // //@ compile-flags: -g --emit=llvm-ir -Csplit-debuginfo=unpacked // diff --git a/tests/ui/delegation/body-identity-glob.rs b/tests/ui/delegation/body-identity-glob.rs new file mode 100644 index 000000000000..58b644f46d61 --- /dev/null +++ b/tests/ui/delegation/body-identity-glob.rs @@ -0,0 +1,32 @@ +//@ check-pass + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn foo(&self) {} + fn bar(&self) {} +} + +impl Trait for u8 {} + +struct S(u8); + +mod to_import { + pub fn check(arg: &u8) -> &u8 { arg } +} + +impl Trait for S { + reuse Trait::* { + use to_import::check; + + let _arr = Some(self.0).map(|x| [x * 2; 3]); + check(&self.0) + } +} + +fn main() { + let s = S(0); + s.foo(); + s.bar(); +} diff --git a/tests/ui/delegation/empty-glob.rs b/tests/ui/delegation/empty-glob.rs new file mode 100644 index 000000000000..d98579d89725 --- /dev/null +++ b/tests/ui/delegation/empty-glob.rs @@ -0,0 +1,11 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait {} + +struct S; +impl S { + reuse Trait::*; //~ ERROR empty glob delegation is not supported +} + +fn main() {} diff --git a/tests/ui/delegation/empty-glob.stderr b/tests/ui/delegation/empty-glob.stderr new file mode 100644 index 000000000000..f4d282f6a0f6 --- /dev/null +++ b/tests/ui/delegation/empty-glob.stderr @@ -0,0 +1,8 @@ +error: empty glob delegation is not supported + --> $DIR/empty-glob.rs:8:5 + | +LL | reuse Trait::*; + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/delegation/glob-bad-path.rs b/tests/ui/delegation/glob-bad-path.rs new file mode 100644 index 000000000000..7bc4f0153a30 --- /dev/null +++ b/tests/ui/delegation/glob-bad-path.rs @@ -0,0 +1,12 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait {} +struct S; + +impl Trait for u8 { + reuse unresolved::*; //~ ERROR failed to resolve: use of undeclared crate or module `unresolved` + reuse S::*; //~ ERROR expected trait, found struct `S` +} + +fn main() {} diff --git a/tests/ui/delegation/glob-bad-path.stderr b/tests/ui/delegation/glob-bad-path.stderr new file mode 100644 index 000000000000..0c06364b3f0a --- /dev/null +++ b/tests/ui/delegation/glob-bad-path.stderr @@ -0,0 +1,15 @@ +error: expected trait, found struct `S` + --> $DIR/glob-bad-path.rs:9:11 + | +LL | reuse S::*; + | ^ not a trait + +error[E0433]: failed to resolve: use of undeclared crate or module `unresolved` + --> $DIR/glob-bad-path.rs:8:11 + | +LL | reuse unresolved::*; + | ^^^^^^^^^^ use of undeclared crate or module `unresolved` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/delegation/glob-glob-conflict.rs b/tests/ui/delegation/glob-glob-conflict.rs new file mode 100644 index 000000000000..2843bf8c4934 --- /dev/null +++ b/tests/ui/delegation/glob-glob-conflict.rs @@ -0,0 +1,33 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait1 { + fn method(&self) -> u8; +} +trait Trait2 { + fn method(&self) -> u8; +} +trait Trait { + fn method(&self) -> u8; +} + +impl Trait1 for u8 { + fn method(&self) -> u8 { 0 } +} +impl Trait1 for u16 { + fn method(&self) -> u8 { 1 } +} +impl Trait2 for u8 { + fn method(&self) -> u8 { 2 } +} + +impl Trait for u8 { + reuse Trait1::*; + reuse Trait2::*; //~ ERROR duplicate definitions with name `method` +} +impl Trait for u16 { + reuse Trait1::*; + reuse Trait1::*; //~ ERROR duplicate definitions with name `method` +} + +fn main() {} diff --git a/tests/ui/delegation/glob-glob-conflict.stderr b/tests/ui/delegation/glob-glob-conflict.stderr new file mode 100644 index 000000000000..8c7e5a4b023c --- /dev/null +++ b/tests/ui/delegation/glob-glob-conflict.stderr @@ -0,0 +1,25 @@ +error[E0201]: duplicate definitions with name `method`: + --> $DIR/glob-glob-conflict.rs:26:5 + | +LL | fn method(&self) -> u8; + | ----------------------- item in trait +... +LL | reuse Trait1::*; + | ---------------- previous definition here +LL | reuse Trait2::*; + | ^^^^^^^^^^^^^^^^ duplicate definition + +error[E0201]: duplicate definitions with name `method`: + --> $DIR/glob-glob-conflict.rs:30:5 + | +LL | fn method(&self) -> u8; + | ----------------------- item in trait +... +LL | reuse Trait1::*; + | ---------------- previous definition here +LL | reuse Trait1::*; + | ^^^^^^^^^^^^^^^^ duplicate definition + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0201`. diff --git a/tests/ui/delegation/glob-glob.rs b/tests/ui/delegation/glob-glob.rs new file mode 100644 index 000000000000..ef7f9a15e195 --- /dev/null +++ b/tests/ui/delegation/glob-glob.rs @@ -0,0 +1,36 @@ +//@ check-pass + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +mod inner { + pub trait TraitFoo { + fn foo(&self) -> u8; + } + pub trait TraitBar { + fn bar(&self) -> u8; + } + + impl TraitFoo for u8 { + fn foo(&self) -> u8 { 0 } + } + impl TraitBar for u8 { + fn bar(&self) -> u8 { 1 } + } +} + +trait Trait { + fn foo(&self) -> u8; + fn bar(&self) -> u8; +} + +impl Trait for u8 { + reuse inner::TraitFoo::*; + reuse inner::TraitBar::*; +} + +fn main() { + let u = 0u8; + u.foo(); + u.bar(); +} diff --git a/tests/ui/delegation/glob-non-fn.rs b/tests/ui/delegation/glob-non-fn.rs new file mode 100644 index 000000000000..ab312d51f498 --- /dev/null +++ b/tests/ui/delegation/glob-non-fn.rs @@ -0,0 +1,38 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn method(&self); + const CONST: u8; + type Type; + #[allow(non_camel_case_types)] + type method; +} + +impl Trait for u8 { + fn method(&self) {} + const CONST: u8 = 0; + type Type = u8; + type method = u8; +} + +struct Good(u8); +impl Trait for Good { + reuse Trait::* { &self.0 } + // Explicit definitions for non-delegatable items. + const CONST: u8 = 0; + type Type = u8; + type method = u8; +} + +struct Bad(u8); +impl Trait for Bad { //~ ERROR not all trait items implemented, missing: `CONST`, `Type`, `method` + reuse Trait::* { &self.0 } + //~^ ERROR item `CONST` is an associated method, which doesn't match its trait `Trait` + //~| ERROR item `Type` is an associated method, which doesn't match its trait `Trait` + //~| ERROR duplicate definitions with name `method` + //~| ERROR expected function, found associated constant `Trait::CONST` + //~| ERROR expected function, found associated type `Trait::Type` +} + +fn main() {} diff --git a/tests/ui/delegation/glob-non-fn.stderr b/tests/ui/delegation/glob-non-fn.stderr new file mode 100644 index 000000000000..4b918c53b848 --- /dev/null +++ b/tests/ui/delegation/glob-non-fn.stderr @@ -0,0 +1,62 @@ +error[E0324]: item `CONST` is an associated method, which doesn't match its trait `Trait` + --> $DIR/glob-non-fn.rs:30:5 + | +LL | const CONST: u8; + | ---------------- item in trait +... +LL | reuse Trait::* { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait + +error[E0324]: item `Type` is an associated method, which doesn't match its trait `Trait` + --> $DIR/glob-non-fn.rs:30:5 + | +LL | type Type; + | ---------- item in trait +... +LL | reuse Trait::* { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait + +error[E0201]: duplicate definitions with name `method`: + --> $DIR/glob-non-fn.rs:30:5 + | +LL | fn method(&self); + | ----------------- item in trait +... +LL | reuse Trait::* { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | duplicate definition + | previous definition here + +error[E0423]: expected function, found associated constant `Trait::CONST` + --> $DIR/glob-non-fn.rs:30:11 + | +LL | reuse Trait::* { &self.0 } + | ^^^^^ not a function + +error[E0423]: expected function, found associated type `Trait::Type` + --> $DIR/glob-non-fn.rs:30:11 + | +LL | reuse Trait::* { &self.0 } + | ^^^^^ + | + = note: can't use a type alias as a constructor + +error[E0046]: not all trait items implemented, missing: `CONST`, `Type`, `method` + --> $DIR/glob-non-fn.rs:29:1 + | +LL | const CONST: u8; + | --------------- `CONST` from trait +LL | type Type; + | --------- `Type` from trait +LL | #[allow(non_camel_case_types)] +LL | type method; + | ----------- `method` from trait +... +LL | impl Trait for Bad { + | ^^^^^^^^^^^^^^^^^^ missing `CONST`, `Type`, `method` in implementation + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0046, E0201, E0324, E0423. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/delegation/glob-non-impl.rs b/tests/ui/delegation/glob-non-impl.rs new file mode 100644 index 000000000000..d523731eeb35 --- /dev/null +++ b/tests/ui/delegation/glob-non-impl.rs @@ -0,0 +1,20 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn method() {} +} + +reuse Trait::*; //~ ERROR glob delegation is only supported in impls + +trait OtherTrait { + reuse Trait::*; //~ ERROR glob delegation is only supported in impls +} + +extern { + reuse Trait::*; //~ ERROR delegation is not supported in `extern` blocks +} + +fn main() { + reuse Trait::*; //~ ERROR glob delegation is only supported in impls +} diff --git a/tests/ui/delegation/glob-non-impl.stderr b/tests/ui/delegation/glob-non-impl.stderr new file mode 100644 index 000000000000..ea458fd5e90f --- /dev/null +++ b/tests/ui/delegation/glob-non-impl.stderr @@ -0,0 +1,26 @@ +error: delegation is not supported in `extern` blocks + --> $DIR/glob-non-impl.rs:15:5 + | +LL | reuse Trait::*; + | ^^^^^^^^^^^^^^^ + +error: glob delegation is only supported in impls + --> $DIR/glob-non-impl.rs:8:1 + | +LL | reuse Trait::*; + | ^^^^^^^^^^^^^^^ + +error: glob delegation is only supported in impls + --> $DIR/glob-non-impl.rs:11:5 + | +LL | reuse Trait::*; + | ^^^^^^^^^^^^^^^ + +error: glob delegation is only supported in impls + --> $DIR/glob-non-impl.rs:19:5 + | +LL | reuse Trait::*; + | ^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/delegation/glob-override.rs b/tests/ui/delegation/glob-override.rs new file mode 100644 index 000000000000..1d0dcf1df6e6 --- /dev/null +++ b/tests/ui/delegation/glob-override.rs @@ -0,0 +1,37 @@ +//@ check-pass + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn foo(&self) -> u8; + fn bar(&self) -> u8; +} + +impl Trait for u8 { + fn foo(&self) -> u8 { 0 } + fn bar(&self) -> u8 { 1 } +} + +struct S(u8); +struct Z(u8); + +impl Trait for S { + reuse Trait::* { &self.0 } + fn bar(&self) -> u8 { 2 } +} + +impl Trait for Z { + reuse Trait::* { &self.0 } + reuse Trait::bar { &self.0 } +} + +fn main() { + let s = S(2); + s.foo(); + s.bar(); + + let z = Z(2); + z.foo(); + z.bar(); +} diff --git a/tests/ui/delegation/glob.rs b/tests/ui/delegation/glob.rs new file mode 100644 index 000000000000..5bc80c166489 --- /dev/null +++ b/tests/ui/delegation/glob.rs @@ -0,0 +1,35 @@ +//@ check-pass + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn foo(&self) -> u8; + fn bar(&self) -> u8; +} + +impl Trait for u8 { + fn foo(&self) -> u8 { 0 } + fn bar(&self) -> u8 { 1 } +} + +struct S(u8); +struct Z(u8); + +impl Trait for S { + reuse Trait::* { &self.0 } +} + +impl Trait for Z { + reuse ::* { &self.0 } +} + +fn main() { + let s = S(2); + s.foo(); + s.bar(); + + let z = Z(3); + z.foo(); + z.bar(); +} diff --git a/tests/ui/delegation/ice-issue-124342.rs b/tests/ui/delegation/ice-issue-124342.rs new file mode 100644 index 000000000000..ad62f6bd54af --- /dev/null +++ b/tests/ui/delegation/ice-issue-124342.rs @@ -0,0 +1,12 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +mod to_reuse {} + +trait Trait { + reuse to_reuse::foo { foo } + //~^ ERROR cannot find function `foo` in module `to_reuse` + //~| ERROR cannot find value `foo` in this scope +} + +fn main() {} diff --git a/tests/ui/delegation/ice-issue-124342.stderr b/tests/ui/delegation/ice-issue-124342.stderr new file mode 100644 index 000000000000..f9a3684d6493 --- /dev/null +++ b/tests/ui/delegation/ice-issue-124342.stderr @@ -0,0 +1,20 @@ +error[E0425]: cannot find function `foo` in module `to_reuse` + --> $DIR/ice-issue-124342.rs:7:21 + | +LL | reuse to_reuse::foo { foo } + | ^^^ not found in `to_reuse` + +error[E0425]: cannot find value `foo` in this scope + --> $DIR/ice-issue-124342.rs:7:27 + | +LL | reuse to_reuse::foo { foo } + | ^^^ + | +help: you might have meant to refer to the associated function + | +LL | reuse to_reuse::foo { Self::foo } + | ++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/delegation/ice-issue-124347.rs b/tests/ui/delegation/ice-issue-124347.rs new file mode 100644 index 000000000000..82a96055099c --- /dev/null +++ b/tests/ui/delegation/ice-issue-124347.rs @@ -0,0 +1,12 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + reuse Trait::foo { &self.0 } + //~^ ERROR recursive delegation is not supported yet +} + +reuse foo; +//~^ ERROR recursive delegation is not supported yet + +fn main() {} diff --git a/tests/ui/delegation/ice-issue-124347.stderr b/tests/ui/delegation/ice-issue-124347.stderr new file mode 100644 index 000000000000..5a3f4525d29e --- /dev/null +++ b/tests/ui/delegation/ice-issue-124347.stderr @@ -0,0 +1,14 @@ +error: recursive delegation is not supported yet + --> $DIR/ice-issue-124347.rs:5:18 + | +LL | reuse Trait::foo { &self.0 } + | ^^^ callee defined here + +error: recursive delegation is not supported yet + --> $DIR/ice-issue-124347.rs:9:7 + | +LL | reuse foo; + | ^^^ callee defined here + +error: aborting due to 2 previous errors + diff --git a/tests/ui/delegation/macro-inside-glob.rs b/tests/ui/delegation/macro-inside-glob.rs new file mode 100644 index 000000000000..1d529341c5b4 --- /dev/null +++ b/tests/ui/delegation/macro-inside-glob.rs @@ -0,0 +1,26 @@ +//@ check-pass + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn foo(&self) -> u8 { 0 } + fn bar(&self) -> u8 { 1 } +} + +impl Trait for u8 {} + +struct S(u8); + +// Macro expansion works inside delegation items. +macro_rules! u8 { () => { u8 } } +macro_rules! self_0 { ($self:ident) => { &$self.0 } } +impl Trait for S { + reuse ::* { self_0!(self) } +} + +fn main() { + let s = S(2); + s.foo(); + s.bar(); +} diff --git a/tests/ui/delegation/macro-inside-list.rs b/tests/ui/delegation/macro-inside-list.rs index 16c74d396ef0..d07a4e47dd4c 100644 --- a/tests/ui/delegation/macro-inside-list.rs +++ b/tests/ui/delegation/macro-inside-list.rs @@ -14,9 +14,9 @@ struct S(u8); // Macro expansion works inside delegation items. macro_rules! u8 { () => { u8 } } -macro_rules! self_0 { () => { &self.0 } } +macro_rules! self_0 { ($self:ident) => { &$self.0 } } impl Trait for S { - reuse ::{foo, bar} { self_0!() } + reuse ::{foo, bar} { self_0!(self) } } fn main() { diff --git a/tests/ui/delegation/not-supported.rs b/tests/ui/delegation/not-supported.rs index 5f78de97638b..25d7a4cb8955 100644 --- a/tests/ui/delegation/not-supported.rs +++ b/tests/ui/delegation/not-supported.rs @@ -70,12 +70,16 @@ mod opaque { pub fn opaque_arg(_: impl Trait) -> i32 { 0 } pub fn opaque_ret() -> impl Trait { unimplemented!() } + //~^ warn: this function depends on never type fallback being `()` + //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } reuse to_reuse::opaque_arg; //~^ ERROR delegation with early bound generics is not supported yet trait ToReuse { fn opaque_ret() -> impl Trait { unimplemented!() } + //~^ warn: this function depends on never type fallback being `()` + //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } // FIXME: Inherited `impl Trait`s create query cycles when used inside trait impls. diff --git a/tests/ui/delegation/not-supported.stderr b/tests/ui/delegation/not-supported.stderr index e2cb04f977b1..4ce01fd5d882 100644 --- a/tests/ui/delegation/not-supported.stderr +++ b/tests/ui/delegation/not-supported.stderr @@ -107,7 +107,7 @@ LL | reuse Trait::foo2 { &self.0 } | ^^^^ error: delegation with early bound generics is not supported yet - --> $DIR/not-supported.rs:74:21 + --> $DIR/not-supported.rs:76:21 | LL | pub fn opaque_arg(_: impl Trait) -> i32 { 0 } | --------------------------------------- callee defined here @@ -115,46 +115,77 @@ LL | pub fn opaque_arg(_: impl Trait) -> i32 { 0 } LL | reuse to_reuse::opaque_arg; | ^^^^^^^^^^ -error[E0391]: cycle detected when computing type of `opaque::::{synthetic#0}` - --> $DIR/not-supported.rs:83:25 +warning: this function depends on never type fallback being `()` + --> $DIR/not-supported.rs:80:9 + | +LL | fn opaque_ret() -> impl Trait { unimplemented!() } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: 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 #123748 + = help: specify the types explicitly +note: in edition 2024, the requirement `!: opaque::Trait` will fail + --> $DIR/not-supported.rs:80:28 + | +LL | fn opaque_ret() -> impl Trait { unimplemented!() } + | ^^^^^^^^^^ + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +error[E0391]: cycle detected when computing type of `opaque::::{synthetic#0}` + --> $DIR/not-supported.rs:87:25 | LL | reuse to_reuse::opaque_ret; | ^^^^^^^^^^ | note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process... - --> $DIR/not-supported.rs:83:25 + --> $DIR/not-supported.rs:87:25 | LL | reuse to_reuse::opaque_ret; | ^^^^^^^^^^ - = note: ...which again requires computing type of `opaque::::{synthetic#0}`, completing the cycle -note: cycle used when checking that `opaque::` is well-formed - --> $DIR/not-supported.rs:82:5 + = note: ...which again requires computing type of `opaque::::{synthetic#0}`, completing the cycle +note: cycle used when checking that `opaque::` is well-formed + --> $DIR/not-supported.rs:86:5 | LL | impl ToReuse for u8 { | ^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0391]: cycle detected when computing type of `opaque::::{synthetic#0}` - --> $DIR/not-supported.rs:86:24 +warning: this function depends on never type fallback being `()` + --> $DIR/not-supported.rs:72:9 + | +LL | pub fn opaque_ret() -> impl Trait { unimplemented!() } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: 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 #123748 + = help: specify the types explicitly +note: in edition 2024, the requirement `!: opaque::Trait` will fail + --> $DIR/not-supported.rs:72:32 + | +LL | pub fn opaque_ret() -> impl Trait { unimplemented!() } + | ^^^^^^^^^^ + +error[E0391]: cycle detected when computing type of `opaque::::{synthetic#0}` + --> $DIR/not-supported.rs:90:24 | LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ | note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process... - --> $DIR/not-supported.rs:86:24 + --> $DIR/not-supported.rs:90:24 | LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ - = note: ...which again requires computing type of `opaque::::{synthetic#0}`, completing the cycle -note: cycle used when checking that `opaque::` is well-formed - --> $DIR/not-supported.rs:85:5 + = note: ...which again requires computing type of `opaque::::{synthetic#0}`, completing the cycle +note: cycle used when checking that `opaque::` is well-formed + --> $DIR/not-supported.rs:89:5 | LL | impl ToReuse for u16 { | ^^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: recursive delegation is not supported yet - --> $DIR/not-supported.rs:99:22 + --> $DIR/not-supported.rs:103:22 | LL | pub reuse to_reuse2::foo; | --- callee defined here @@ -162,7 +193,7 @@ LL | pub reuse to_reuse2::foo; LL | reuse to_reuse1::foo; | ^^^ -error: aborting due to 16 previous errors +error: aborting due to 16 previous errors; 2 warnings emitted Some errors have detailed explanations: E0049, E0195, E0391. For more information about an error, try `rustc --explain E0049`. diff --git a/tests/ui/delegation/self-hygiene.rs b/tests/ui/delegation/self-hygiene.rs new file mode 100644 index 000000000000..dac6c319416a --- /dev/null +++ b/tests/ui/delegation/self-hygiene.rs @@ -0,0 +1,20 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +macro_rules! emit_self { () => { self } } +//~^ ERROR expected value, found module `self` +//~| ERROR expected value, found module `self` + +struct S; +impl S { + fn method(self) { + emit_self!(); + } +} + +fn foo(arg: u8) {} +reuse foo as bar { + emit_self!() +} + +fn main() {} diff --git a/tests/ui/delegation/self-hygiene.stderr b/tests/ui/delegation/self-hygiene.stderr new file mode 100644 index 000000000000..fa64b7d1d7f7 --- /dev/null +++ b/tests/ui/delegation/self-hygiene.stderr @@ -0,0 +1,31 @@ +error[E0424]: expected value, found module `self` + --> $DIR/self-hygiene.rs:4:34 + | +LL | macro_rules! emit_self { () => { self } } + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter +... +LL | / fn method(self) { +LL | | emit_self!(); + | | ------------ in this macro invocation +LL | | } + | |_____- this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters + | + = note: this error originates in the macro `emit_self` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0424]: expected value, found module `self` + --> $DIR/self-hygiene.rs:4:34 + | +LL | macro_rules! emit_self { () => { self } } + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter +... +LL | / reuse foo as bar { +LL | | emit_self!() + | | ------------ in this macro invocation +LL | | } + | |_- delegation supports a `self` parameter, but a macro invocation can only access identifiers it receives from parameters + | + = note: this error originates in the macro `emit_self` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0424`. diff --git a/tests/ui/deployment-target/macos-target.rs b/tests/ui/deployment-target/macos-target.rs index be2c32e28141..197edd024746 100644 --- a/tests/ui/deployment-target/macos-target.rs +++ b/tests/ui/deployment-target/macos-target.rs @@ -1,4 +1,4 @@ -//@ only-macos +//@ only-apple //@ compile-flags: --print deployment-target //@ normalize-stdout-test: "\d+\." -> "$$CURRENT_MAJOR_VERSION." //@ normalize-stdout-test: "\d+" -> "$$CURRENT_MINOR_VERSION" diff --git a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs index 6ab1fb7b039b..885dacc727af 100644 --- a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs +++ b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs @@ -1,9 +1,9 @@ #![forbid(dead_code)] #[derive(Debug)] -pub struct Whatever { +pub struct Whatever { //~ ERROR struct `Whatever` is never constructed pub field0: (), - field1: (), //~ ERROR fields `field1`, `field2`, `field3`, and `field4` are never read + field1: (), field2: (), field3: (), field4: (), diff --git a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr index e9b757b6bae7..e10d28ad03a4 100644 --- a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr +++ b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr @@ -1,19 +1,9 @@ -error: fields `field1`, `field2`, `field3`, and `field4` are never read - --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:6:5 +error: struct `Whatever` is never constructed + --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:4:12 | LL | pub struct Whatever { - | -------- fields in this struct -LL | pub field0: (), -LL | field1: (), - | ^^^^^^ -LL | field2: (), - | ^^^^^^ -LL | field3: (), - | ^^^^^^ -LL | field4: (), - | ^^^^^^ + | ^^^^^^^^ | - = note: `Whatever` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis note: the lint level is defined here --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:1:11 | diff --git a/tests/ui/derives/derive-hrtb-for-bare-fn-field-with-lifetime.rs b/tests/ui/derives/derive-hrtb-for-bare-fn-field-with-lifetime.rs new file mode 100644 index 000000000000..b8a42be6832d --- /dev/null +++ b/tests/ui/derives/derive-hrtb-for-bare-fn-field-with-lifetime.rs @@ -0,0 +1,13 @@ +//@ run-pass +// Issue #122622: `#[derive(Clone)]` should work for HRTB function type taking an associated type +#![allow(dead_code)] +trait SomeTrait { + type SomeType<'a>; +} + +#[derive(Clone)] +struct Foo { + x: for<'a> fn(T::SomeType<'a>) +} + +fn main() {} diff --git a/tests/ui/derives/deriving-with-repr-packed-2.stderr b/tests/ui/derives/deriving-with-repr-packed-2.stderr index 96f51a4e7a2b..b62c67d9a9da 100644 --- a/tests/ui/derives/deriving-with-repr-packed-2.stderr +++ b/tests/ui/derives/deriving-with-repr-packed-2.stderr @@ -10,6 +10,14 @@ LL | struct NonCopy; LL | _ = x.clone(); | ^^^^^ method cannot be called on `Foo` due to unsatisfied trait bounds | +note: there's an earlier shadowed binding `x` of type `Foo` that has method `clone` available + --> $DIR/deriving-with-repr-packed-2.rs:13:9 + | +LL | let x: Foo = Foo(1, 2, 3); + | ^ `x` of type `Foo` that has method `clone` defined earlier here +... +LL | let x: Foo = Foo(NonCopy, NonCopy, NonCopy); + | - earlier `x` shadowed here with type `Foo` note: the following trait bounds were not satisfied: `NonCopy: Clone` `NonCopy: Copy` diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr new file mode 100644 index 000000000000..41f222e46a77 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `&str: AsExpression` is not satisfied + --> $DIR/as_expression.rs:57:15 + | +LL | SelectInt.check("bar"); + | ^^^^^ the trait `AsExpression` is not implemented for `&str` + | + = help: the trait `AsExpression` is implemented for `&str` + = help: for that trait implementation, expected `Text`, found `Integer` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr new file mode 100644 index 000000000000..d189d2dbdedc --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr @@ -0,0 +1,37 @@ +error[E0277]: the trait bound `&str: AsExpression<::SqlType>` is not satisfied + --> $DIR/as_expression.rs:57:21 + | +LL | SelectInt.check("bar"); + | ----- ^^^^^ the trait `AsExpression<::SqlType>` is not implemented for `&str` + | | + | required by a bound introduced by this call + | + = help: the trait `AsExpression` is implemented for `&str` +note: required by a bound in `Foo::check` + --> $DIR/as_expression.rs:48:12 + | +LL | fn check(&self, _: T) -> ::SqlType>>::Expression + | ----- required by a bound in this associated function +LL | where +LL | T: AsExpression, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check` + +error[E0277]: the trait bound `&str: AsExpression` is not satisfied + --> $DIR/as_expression.rs:57:15 + | +LL | SelectInt.check("bar"); + | ^^^^^ the trait `AsExpression` is not implemented for `&str` + | + = help: the trait `AsExpression` is implemented for `&str` + = help: for that trait implementation, expected `Text`, found `Integer` + +error[E0271]: type mismatch resolving `::SqlType == Text` + --> $DIR/as_expression.rs:57:5 + | +LL | SelectInt.check("bar"); + | ^^^^^^^^^^^^^^^^^^^^^^ types differ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0271, E0277. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs new file mode 100644 index 000000000000..37b4429f694c --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs @@ -0,0 +1,61 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +#![feature(do_not_recommend)] + +pub trait Expression { + type SqlType; +} + +pub trait AsExpression { + type Expression: Expression; +} + +pub struct Text; +pub struct Integer; + +pub struct Bound(T); +pub struct SelectInt; + +impl Expression for SelectInt { + type SqlType = Integer; +} + +impl Expression for Bound { + type SqlType = T; +} + +#[diagnostic::do_not_recommend] +impl AsExpression for T +where + T: Expression, +{ + type Expression = T; +} + +impl AsExpression for i32 { + type Expression = Bound; +} + +impl AsExpression for &'_ str { + type Expression = Bound; +} + +trait Foo: Expression + Sized { + fn check(&self, _: T) -> ::SqlType>>::Expression + where + T: AsExpression, + { + todo!() + } +} + +impl Foo for T where T: Expression {} + +fn main() { + SelectInt.check("bar"); + //~^ ERROR the trait bound `&str: AsExpression` is not satisfied + //[next]~| the trait bound `&str: AsExpression<::SqlType>` is not satisfied + //[next]~| type mismatch +} diff --git a/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.rs b/tests/ui/diagnostic_namespace/do_not_recommend/feature-gate-do_not_recommend.rs similarity index 56% rename from tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.rs rename to tests/ui/diagnostic_namespace/do_not_recommend/feature-gate-do_not_recommend.rs index c9dc1c6e6498..5a26d28188c2 100644 --- a/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.rs +++ b/tests/ui/diagnostic_namespace/do_not_recommend/feature-gate-do_not_recommend.rs @@ -1,17 +1,13 @@ #![feature(do_not_recommend)] -pub trait Foo { -} +pub trait Foo {} -impl Foo for i32 { -} +impl Foo for i32 {} -pub trait Bar { -} +pub trait Bar {} -#[do_not_recommend] -impl Bar for T { -} +#[diagnostic::do_not_recommend] +impl Bar for T {} fn stuff(_: T) {} diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/feature-gate-do_not_recommend.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/feature-gate-do_not_recommend.stderr new file mode 100644 index 000000000000..3951231fa2e7 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/feature-gate-do_not_recommend.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `u8: Bar` is not satisfied + --> $DIR/feature-gate-do_not_recommend.rs:15:11 + | +LL | stuff(1u8); + | ^^^ the trait `Bar` is not implemented for `u8` + | +note: required by a bound in `stuff` + --> $DIR/feature-gate-do_not_recommend.rs:12:13 + | +LL | fn stuff(_: T) {} + | ^^^ required by this bound in `stuff` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.rs b/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.rs new file mode 100644 index 000000000000..400ef83873e1 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.rs @@ -0,0 +1,39 @@ +//@ check-pass +#![feature(do_not_recommend)] + +#[diagnostic::do_not_recommend] +//~^WARN `#[diagnostic::do_not_recommend]` can only be placed +const CONST: () = (); + +#[diagnostic::do_not_recommend] +//~^WARN `#[diagnostic::do_not_recommend]` can only be placed +static STATIC: () = (); + +#[diagnostic::do_not_recommend] +//~^WARN `#[diagnostic::do_not_recommend]` can only be placed +type Type = (); + +#[diagnostic::do_not_recommend] +//~^WARN `#[diagnostic::do_not_recommend]` can only be placed +enum Enum {} + +#[diagnostic::do_not_recommend] +//~^WARN `#[diagnostic::do_not_recommend]` can only be placed +extern "C" {} + +#[diagnostic::do_not_recommend] +//~^WARN `#[diagnostic::do_not_recommend]` can only be placed +fn fun() {} + +#[diagnostic::do_not_recommend] +//~^WARN `#[diagnostic::do_not_recommend]` can only be placed +struct Struct {} + +#[diagnostic::do_not_recommend] +//~^WARN `#[diagnostic::do_not_recommend]` can only be placed +trait Trait {} + +#[diagnostic::do_not_recommend] +impl Trait for i32 {} + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.stderr new file mode 100644 index 000000000000..c83fd46db584 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.stderr @@ -0,0 +1,52 @@ +warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations + --> $DIR/incorrect-locations.rs:4:1 + | +LL | #[diagnostic::do_not_recommend] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default + +warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations + --> $DIR/incorrect-locations.rs:8:1 + | +LL | #[diagnostic::do_not_recommend] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations + --> $DIR/incorrect-locations.rs:12:1 + | +LL | #[diagnostic::do_not_recommend] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations + --> $DIR/incorrect-locations.rs:16:1 + | +LL | #[diagnostic::do_not_recommend] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations + --> $DIR/incorrect-locations.rs:20:1 + | +LL | #[diagnostic::do_not_recommend] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations + --> $DIR/incorrect-locations.rs:24:1 + | +LL | #[diagnostic::do_not_recommend] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations + --> $DIR/incorrect-locations.rs:28:1 + | +LL | #[diagnostic::do_not_recommend] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations + --> $DIR/incorrect-locations.rs:32:1 + | +LL | #[diagnostic::do_not_recommend] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: 8 warnings emitted + diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/simple.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/simple.current.stderr index a4d4b7b359ec..729cb5694e21 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/simple.current.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/simple.current.stderr @@ -1,16 +1,11 @@ error[E0277]: the trait bound `*mut (): Foo` is not satisfied - --> $DIR/simple.rs:19:17 + --> $DIR/simple.rs:17:17 | LL | needs_foo::<*mut ()>(); - | ^^^^^^^ the trait `Send` is not implemented for `*mut ()`, which is required by `*mut (): Foo` + | ^^^^^^^ the trait `Foo` is not implemented for `*mut ()` | -note: required for `*mut ()` to implement `Foo` - --> $DIR/simple.rs:10:9 - | -LL | impl Foo for T where T: Send {} - | ^^^ ^ ---- unsatisfied trait bound introduced here note: required by a bound in `needs_foo` - --> $DIR/simple.rs:14:17 + --> $DIR/simple.rs:12:17 | LL | fn needs_foo() {} | ^^^ required by this bound in `needs_foo` diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/simple.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/simple.next.stderr index 1341ca8175af..729cb5694e21 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/simple.next.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/simple.next.stderr @@ -1,11 +1,11 @@ error[E0277]: the trait bound `*mut (): Foo` is not satisfied - --> $DIR/simple.rs:19:17 + --> $DIR/simple.rs:17:17 | LL | needs_foo::<*mut ()>(); | ^^^^^^^ the trait `Foo` is not implemented for `*mut ()` | note: required by a bound in `needs_foo` - --> $DIR/simple.rs:14:17 + --> $DIR/simple.rs:12:17 | LL | fn needs_foo() {} | ^^^ required by this bound in `needs_foo` diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/simple.rs b/tests/ui/diagnostic_namespace/do_not_recommend/simple.rs index 15ff80ae4d95..780649b009c6 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/simple.rs +++ b/tests/ui/diagnostic_namespace/do_not_recommend/simple.rs @@ -6,10 +6,8 @@ trait Foo {} -#[do_not_recommend] +#[diagnostic::do_not_recommend] impl Foo for T where T: Send {} -//[current]~^ NOTE required for `*mut ()` to implement `Foo` -//[current]~| NOTE unsatisfied trait bound introduced here fn needs_foo() {} //~^ NOTE required by a bound in `needs_foo` @@ -18,6 +16,5 @@ fn needs_foo() {} fn main() { needs_foo::<*mut ()>(); //~^ ERROR the trait bound `*mut (): Foo` is not satisfied - //[current]~| NOTE the trait `Send` is not implemented for `*mut ()` - //[next]~| NOTE the trait `Foo` is not implemented for `*mut ()` + //~| NOTE the trait `Foo` is not implemented for `*mut ()` } diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/stacked.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/stacked.current.stderr new file mode 100644 index 000000000000..41a10a61e1df --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/stacked.current.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `(): Root` is not satisfied + --> $DIR/stacked.rs:19:18 + | +LL | needs_root::<()>(); + | ^^ the trait `Root` is not implemented for `()` + | +note: required by a bound in `needs_root` + --> $DIR/stacked.rs:16:18 + | +LL | fn needs_root() {} + | ^^^^ required by this bound in `needs_root` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/stacked.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/stacked.next.stderr new file mode 100644 index 000000000000..41a10a61e1df --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/stacked.next.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `(): Root` is not satisfied + --> $DIR/stacked.rs:19:18 + | +LL | needs_root::<()>(); + | ^^ the trait `Root` is not implemented for `()` + | +note: required by a bound in `needs_root` + --> $DIR/stacked.rs:16:18 + | +LL | fn needs_root() {} + | ^^^^ required by this bound in `needs_root` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/stacked.rs b/tests/ui/diagnostic_namespace/do_not_recommend/stacked.rs new file mode 100644 index 000000000000..fc355bdc4e20 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/stacked.rs @@ -0,0 +1,21 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +#![feature(do_not_recommend)] + +trait Root {} +trait DontRecommend {} +trait Other {} + +#[diagnostic::do_not_recommend] +impl Root for T where T: DontRecommend {} + +impl DontRecommend for T where T: Other {} + +fn needs_root() {} + +fn main() { + needs_root::<()>(); + //~^ ERROR the trait bound `(): Root` is not satisfied +} diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/type_mismatch.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/type_mismatch.current.stderr new file mode 100644 index 000000000000..bcede8a255f2 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/type_mismatch.current.stderr @@ -0,0 +1,15 @@ +error[E0277]: Very important message! + --> $DIR/type_mismatch.rs:25:14 + | +LL | verify::(); + | ^^ the trait `TheImportantOne` is not implemented for `u8` + | +note: required by a bound in `verify` + --> $DIR/type_mismatch.rs:22:14 + | +LL | fn verify() {} + | ^^^^^^^^^^^^^^^ required by this bound in `verify` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/type_mismatch.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/type_mismatch.next.stderr new file mode 100644 index 000000000000..bcede8a255f2 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/type_mismatch.next.stderr @@ -0,0 +1,15 @@ +error[E0277]: Very important message! + --> $DIR/type_mismatch.rs:25:14 + | +LL | verify::(); + | ^^ the trait `TheImportantOne` is not implemented for `u8` + | +note: required by a bound in `verify` + --> $DIR/type_mismatch.rs:22:14 + | +LL | fn verify() {} + | ^^^^^^^^^^^^^^^ required by this bound in `verify` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/type_mismatch.rs b/tests/ui/diagnostic_namespace/do_not_recommend/type_mismatch.rs new file mode 100644 index 000000000000..d6721ccc848f --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/type_mismatch.rs @@ -0,0 +1,27 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +#![feature(do_not_recommend)] + +#[diagnostic::on_unimplemented(message = "Very important message!")] +trait TheImportantOne {} + +trait ImplementationDetail { + type Restriction; +} + +#[diagnostic::do_not_recommend] +impl> TheImportantOne for T {} + +// Comment out this `impl` to show the expected error message. +impl ImplementationDetail for u8 { + type Restriction = u8; +} + +fn verify() {} + +pub fn main() { + verify::(); + //~^ERROR: Very important message! [E0277] +} diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/unstable-feature.rs b/tests/ui/diagnostic_namespace/do_not_recommend/unstable-feature.rs new file mode 100644 index 000000000000..ccc687aa5b36 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/unstable-feature.rs @@ -0,0 +1,8 @@ +#![deny(unknown_or_malformed_diagnostic_attributes)] +trait Foo {} + +#[diagnostic::do_not_recommend] +//~^ ERROR unknown diagnostic attribute [unknown_or_malformed_diagnostic_attributes] +impl Foo for i32 {} + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/unstable-feature.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/unstable-feature.stderr new file mode 100644 index 000000000000..d8332229d4fc --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/unstable-feature.stderr @@ -0,0 +1,14 @@ +error: unknown diagnostic attribute + --> $DIR/unstable-feature.rs:4:15 + | +LL | #[diagnostic::do_not_recommend] + | ^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/unstable-feature.rs:1:9 + | +LL | #![deny(unknown_or_malformed_diagnostic_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.stderr b/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.stderr index 9cbce93c8e00..9d335b391eb1 100644 --- a/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.stderr +++ b/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.stderr @@ -7,8 +7,8 @@ LL | f1.foo(1usize); | required by a bound introduced by this call | = help: the following other types implement trait `Foo`: - > - > + `Bar` implements `Foo` + `Bar` implements `Foo` error: aborting due to 1 previous error diff --git a/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.stderr b/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.stderr index a2b7d804cb8c..f9d718079601 100644 --- a/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.stderr +++ b/tests/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.stderr @@ -7,12 +7,12 @@ LL | f1.foo(1usize); | required by a bound introduced by this call | = help: the following other types implement trait `Foo`: - > - > - > - > - > - > + `Bar` implements `Foo` + `Bar` implements `Foo` + `Bar` implements `Foo` + `Bar` implements `Foo` + `Bar` implements `Foo` + `Bar` implements `Foo` error: aborting due to 1 previous error diff --git a/tests/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr b/tests/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr index 7229b9ac986a..b50203044395 100644 --- a/tests/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr +++ b/tests/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr @@ -7,11 +7,11 @@ LL | Foo::::bar(&1i8); | required by a bound introduced by this call | = help: the following other types implement trait `Foo`: - > - > - > - > - > + `i8` implements `Foo` + `i8` implements `Foo` + `i8` implements `Foo` + `i8` implements `Foo` + `i8` implements `Foo` error[E0277]: the trait bound `u8: Foo` is not satisfied --> $DIR/issue-39802-show-5-trait-impls.rs:25:21 @@ -22,10 +22,10 @@ LL | Foo::::bar(&1u8); | required by a bound introduced by this call | = help: the following other types implement trait `Foo`: - > - > - > - > + `u8` implements `Foo` + `u8` implements `Foo` + `u8` implements `Foo` + `u8` implements `Foo` error[E0277]: the trait bound `bool: Foo` is not satisfied --> $DIR/issue-39802-show-5-trait-impls.rs:26:21 @@ -36,12 +36,12 @@ LL | Foo::::bar(&true); | required by a bound introduced by this call | = help: the following other types implement trait `Foo`: - > - > - > - > - > - > + `bool` implements `Foo` + `bool` implements `Foo` + `bool` implements `Foo` + `bool` implements `Foo` + `bool` implements `Foo` + `bool` implements `Foo` error: aborting due to 3 previous errors diff --git a/tests/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr b/tests/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr index 5c610f36322d..b1a15b8594a0 100644 --- a/tests/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr +++ b/tests/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr @@ -22,7 +22,7 @@ error[E0425]: cannot find value `Set` in this scope LL | fn setup() -> Set { Set } | ^^^ not found in this scope | -help: consider importing one of these items +help: consider importing one of these unit variants | LL + use AffixHeart::Set; | diff --git a/tests/ui/drop/auxiliary/edition-2021-macros.rs b/tests/ui/drop/auxiliary/edition-2021-macros.rs new file mode 100644 index 000000000000..8a6444f8614d --- /dev/null +++ b/tests/ui/drop/auxiliary/edition-2021-macros.rs @@ -0,0 +1,8 @@ +//@ edition:2021 + +#[macro_export] +macro_rules! edition_2021_block { + ($($c:tt)*) => { + { $($c)* } + } +} diff --git a/tests/ui/drop/auxiliary/edition-2024-macros.rs b/tests/ui/drop/auxiliary/edition-2024-macros.rs new file mode 100644 index 000000000000..236340bfed4f --- /dev/null +++ b/tests/ui/drop/auxiliary/edition-2024-macros.rs @@ -0,0 +1,9 @@ +//@ edition:2024 +//@ compile-flags: -Zunstable-options + +#[macro_export] +macro_rules! edition_2024_block { + ($($c:tt)*) => { + { $($c)* } + } +} diff --git a/tests/ui/drop/tail-expr-drop-order-negative.edition2024.stderr b/tests/ui/drop/tail-expr-drop-order-negative.edition2024.stderr new file mode 100644 index 000000000000..75fc34e409b5 --- /dev/null +++ b/tests/ui/drop/tail-expr-drop-order-negative.edition2024.stderr @@ -0,0 +1,16 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/tail-expr-drop-order-negative.rs:11:15 + | +LL | x.replace(std::cell::RefCell::new(123).borrow()).is_some() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement + | | + | creates a temporary value which is freed while still in use +LL | +LL | } + | - borrow might be used here, when `x` is dropped and runs the destructor for type `Option>` + | + = note: consider using a `let` binding to create a longer lived value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0716`. diff --git a/tests/ui/drop/tail-expr-drop-order-negative.rs b/tests/ui/drop/tail-expr-drop-order-negative.rs new file mode 100644 index 000000000000..c570b3a1ee23 --- /dev/null +++ b/tests/ui/drop/tail-expr-drop-order-negative.rs @@ -0,0 +1,17 @@ +//@ revisions: edition2021 edition2024 +//@ [edition2024] compile-flags: -Zunstable-options +//@ [edition2024] edition: 2024 +//@ [edition2021] check-pass + +#![feature(shorter_tail_lifetimes)] + +fn why_would_you_do_this() -> bool { + let mut x = None; + // Make a temporary `RefCell` and put a `Ref` that borrows it in `x`. + x.replace(std::cell::RefCell::new(123).borrow()).is_some() + //[edition2024]~^ ERROR: temporary value dropped while borrowed +} + +fn main() { + why_would_you_do_this(); +} diff --git a/tests/ui/drop/tail-expr-drop-order.rs b/tests/ui/drop/tail-expr-drop-order.rs new file mode 100644 index 000000000000..5d87f980b156 --- /dev/null +++ b/tests/ui/drop/tail-expr-drop-order.rs @@ -0,0 +1,108 @@ +//@ aux-build:edition-2021-macros.rs +//@ aux-build:edition-2024-macros.rs +//@ compile-flags: -Z validate-mir -Zunstable-options +//@ edition: 2024 +//@ run-pass + +#![feature(shorter_tail_lifetimes)] +#![allow(unused_imports)] +#![allow(dead_code)] +#![allow(unused_variables)] + +#[macro_use] +extern crate edition_2021_macros; +#[macro_use] +extern crate edition_2024_macros; +use std::cell::RefCell; +use std::convert::TryInto; + +#[derive(Default)] +struct DropOrderCollector(RefCell>); + +struct LoudDrop<'a>(&'a DropOrderCollector, u32); + +impl Drop for LoudDrop<'_> { + fn drop(&mut self) { + println!("{}", self.1); + self.0.0.borrow_mut().push(self.1); + } +} + +impl DropOrderCollector { + fn option_loud_drop(&self, n: u32) -> Option { + Some(LoudDrop(self, n)) + } + + fn loud_drop(&self, n: u32) -> LoudDrop { + LoudDrop(self, n) + } + + fn assert_sorted(&self, expected: usize) { + let result = self.0.borrow(); + assert_eq!(result.len(), expected); + for i in 1..result.len() { + assert!( + result[i - 1] < result[i], + "inversion at {} ({} followed by {})", + i - 1, + result[i - 1], + result[i] + ); + } + } +} + +fn edition_2021_around_2021() { + let c = DropOrderCollector::default(); + let _ = edition_2021_block! { + let a = c.loud_drop(1); + edition_2021_block! { + let b = c.loud_drop(0); + c.loud_drop(2).1 + } + }; + c.assert_sorted(3); +} + +fn edition_2021_around_2024() { + let c = DropOrderCollector::default(); + let _ = edition_2021_block! { + let a = c.loud_drop(2); + edition_2024_block! { + let b = c.loud_drop(1); + c.loud_drop(0).1 + } + }; + c.assert_sorted(3); +} + +fn edition_2024_around_2021() { + let c = DropOrderCollector::default(); + let _ = edition_2024_block! { + let a = c.loud_drop(2); + edition_2021_block! { + let b = c.loud_drop(0); + c.loud_drop(1).1 + } + }; + c.assert_sorted(3); +} + +fn edition_2024_around_2024() { + let c = DropOrderCollector::default(); + let _ = edition_2024_block! { + let a = c.loud_drop(2); + edition_2024_block! { + let b = c.loud_drop(1); + c.loud_drop(0).1 + } + }; + c.assert_sorted(3); +} + +fn main() { + edition_2021_around_2021(); + edition_2021_around_2024(); + edition_2024_around_2021(); + edition_2024_around_2024(); +} diff --git a/tests/ui/duplicate_entry_error.rs b/tests/ui/duplicate_entry_error.rs index 7ebbab470955..8e49f57a3df9 100644 --- a/tests/ui/duplicate_entry_error.rs +++ b/tests/ui/duplicate_entry_error.rs @@ -5,7 +5,9 @@ #![feature(lang_items)] -use std::panic::PanicInfo; +extern crate core; + +use core::panic::PanicInfo; #[lang = "panic_impl"] fn panic_impl(info: &PanicInfo) -> ! { diff --git a/tests/ui/duplicate_entry_error.stderr b/tests/ui/duplicate_entry_error.stderr index 3b5998df353c..958e9c7527d8 100644 --- a/tests/ui/duplicate_entry_error.stderr +++ b/tests/ui/duplicate_entry_error.stderr @@ -1,5 +1,5 @@ error[E0152]: found duplicate lang item `panic_impl` - --> $DIR/duplicate_entry_error.rs:11:1 + --> $DIR/duplicate_entry_error.rs:13:1 | LL | / fn panic_impl(info: &PanicInfo) -> ! { LL | | diff --git a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr new file mode 100644 index 000000000000..134fd098b7e4 --- /dev/null +++ b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr @@ -0,0 +1,33 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/never-type-fallback-breaking.rs:15:1 + | +LL | fn m() { + | ^^^^^^ + | + = warning: 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 #123748 + = help: specify the types explicitly +note: in edition 2024, the requirement `!: Default` will fail + --> $DIR/never-type-fallback-breaking.rs:19:17 + | +LL | true => Default::default(), + | ^^^^^^^^^^^^^^^^^^ + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: this function depends on never type fallback being `()` + --> $DIR/never-type-fallback-breaking.rs:27:1 + | +LL | fn q() -> Option<()> { + | ^^^^^^^^^^^^^^^^^^^^ + | + = warning: 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 #123748 + = help: specify the types explicitly +note: in edition 2024, the requirement `!: Default` will fail + --> $DIR/never-type-fallback-breaking.rs:34:5 + | +LL | deserialize()?; + | ^^^^^^^^^^^^^ + +warning: 2 warnings emitted + diff --git a/tests/ui/editions/never-type-fallback-breaking.e2024.stderr b/tests/ui/editions/never-type-fallback-breaking.e2024.stderr new file mode 100644 index 000000000000..461e4ae0bdf6 --- /dev/null +++ b/tests/ui/editions/never-type-fallback-breaking.e2024.stderr @@ -0,0 +1,26 @@ +error[E0277]: the trait bound `!: Default` is not satisfied + --> $DIR/never-type-fallback-breaking.rs:19:17 + | +LL | true => Default::default(), + | ^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `!` + | + = note: this error might have been caused by changes to Rust's type-inference algorithm (see issue #48950 for more information) + = help: did you intend to use the type `()` here instead? + +error[E0277]: the trait bound `!: Default` is not satisfied + --> $DIR/never-type-fallback-breaking.rs:34:5 + | +LL | deserialize()?; + | ^^^^^^^^^^^^^ the trait `Default` is not implemented for `!` + | + = note: this error might have been caused by changes to Rust's type-inference algorithm (see issue #48950 for more information) + = help: did you intend to use the type `()` here instead? +note: required by a bound in `deserialize` + --> $DIR/never-type-fallback-breaking.rs:30:23 + | +LL | fn deserialize() -> Option { + | ^^^^^^^ required by this bound in `deserialize` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/editions/never-type-fallback-breaking.rs b/tests/ui/editions/never-type-fallback-breaking.rs new file mode 100644 index 000000000000..7b4a1b1de048 --- /dev/null +++ b/tests/ui/editions/never-type-fallback-breaking.rs @@ -0,0 +1,38 @@ +//@ revisions: e2021 e2024 +// +//@[e2021] edition: 2021 +//@[e2024] edition: 2024 +//@[e2024] compile-flags: -Zunstable-options +// +//@[e2021] run-pass +//@[e2024] check-fail + +fn main() { + m(); + q(); +} + +fn m() { + //[e2021]~^ this function depends on never type fallback being `()` + //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + let x = match true { + true => Default::default(), + //[e2024]~^ error: the trait bound `!: Default` is not satisfied + false => panic!("..."), + }; + + dbg!(x); +} + +fn q() -> Option<()> { + //[e2021]~^ this function depends on never type fallback being `()` + //[e2021]~| this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + fn deserialize() -> Option { + Some(T::default()) + } + + deserialize()?; + //[e2024]~^ error: the trait bound `!: Default` is not satisfied + + None +} diff --git a/tests/ui/editions/never-type-fallback.e2021.run.stdout b/tests/ui/editions/never-type-fallback.e2021.run.stdout new file mode 100644 index 000000000000..4122f7ac1eed --- /dev/null +++ b/tests/ui/editions/never-type-fallback.e2021.run.stdout @@ -0,0 +1 @@ +return type = () diff --git a/tests/ui/editions/never-type-fallback.e2024.run.stdout b/tests/ui/editions/never-type-fallback.e2024.run.stdout new file mode 100644 index 000000000000..df2eeae60b4c --- /dev/null +++ b/tests/ui/editions/never-type-fallback.e2024.run.stdout @@ -0,0 +1 @@ +return type = ! diff --git a/tests/ui/editions/never-type-fallback.rs b/tests/ui/editions/never-type-fallback.rs new file mode 100644 index 000000000000..a5b75219295c --- /dev/null +++ b/tests/ui/editions/never-type-fallback.rs @@ -0,0 +1,16 @@ +//@ revisions: e2021 e2024 +// +//@[e2021] edition: 2021 +//@[e2024] edition: 2024 +//@[e2024] compile-flags: -Zunstable-options +// +//@ run-pass +//@ check-run-results + +fn main() { + print_return_type_of(|| panic!()); +} + +fn print_return_type_of(_: impl FnOnce() -> R) { + println!("return type = {}", std::any::type_name::()); +} diff --git a/tests/ui/error-codes/E0229.rs b/tests/ui/error-codes/E0229.rs index 558baae37f76..da2758dfba8f 100644 --- a/tests/ui/error-codes/E0229.rs +++ b/tests/ui/error-codes/E0229.rs @@ -10,10 +10,10 @@ impl Foo for isize { fn boo(&self) -> usize { 42 } } -fn baz(x: &>::A) {} -//~^ ERROR associated type bindings are not allowed here [E0229] -//~| ERROR associated type bindings are not allowed here [E0229] -//~| ERROR associated type bindings are not allowed here [E0229] +fn baz(x: &>::A) {} +//~^ ERROR associated item constraints are not allowed here [E0229] +//~| ERROR associated item constraints are not allowed here [E0229] +//~| ERROR associated item constraints are not allowed here [E0229] //~| ERROR the trait bound `I: Foo` is not satisfied //~| ERROR the trait bound `I: Foo` is not satisfied diff --git a/tests/ui/error-codes/E0229.stderr b/tests/ui/error-codes/E0229.stderr index ae7dc9ac2659..7d9cedc3bdc4 100644 --- a/tests/ui/error-codes/E0229.stderr +++ b/tests/ui/error-codes/E0229.stderr @@ -1,58 +1,58 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/E0229.rs:13:25 | -LL | fn baz(x: &>::A) {} - | ^^^^^ associated type not allowed here +LL | fn baz(x: &>::A) {} + | ^^^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | -LL | fn baz(x: &>::A) {} - | ~~~~~~~ +LL | fn baz(x: &>::A) {} + | ~~~~~~~~~ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/E0229.rs:13:25 | -LL | fn baz(x: &>::A) {} - | ^^^^^ associated type not allowed here +LL | fn baz(x: &>::A) {} + | ^^^^^^^ associated item constraint not allowed here | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider removing this type binding +help: consider removing this associated item binding | -LL | fn baz(x: &>::A) {} - | ~~~~~~~ +LL | fn baz(x: &>::A) {} + | ~~~~~~~~~ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/E0229.rs:13:25 | -LL | fn baz(x: &>::A) {} - | ^^^^^ associated type not allowed here +LL | fn baz(x: &>::A) {} + | ^^^^^^^ associated item constraint not allowed here | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider removing this type binding +help: consider removing this associated item binding | -LL | fn baz(x: &>::A) {} - | ~~~~~~~ +LL | fn baz(x: &>::A) {} + | ~~~~~~~~~ error[E0277]: the trait bound `I: Foo` is not satisfied --> $DIR/E0229.rs:13:15 | -LL | fn baz(x: &>::A) {} - | ^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `I` +LL | fn baz(x: &>::A) {} + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `I` | help: consider restricting type parameter `I` | -LL | fn baz(x: &>::A) {} +LL | fn baz(x: &>::A) {} | +++++ error[E0277]: the trait bound `I: Foo` is not satisfied - --> $DIR/E0229.rs:13:37 + --> $DIR/E0229.rs:13:39 | -LL | fn baz(x: &>::A) {} - | ^^ the trait `Foo` is not implemented for `I` +LL | fn baz(x: &>::A) {} + | ^^ the trait `Foo` is not implemented for `I` | help: consider restricting type parameter `I` | -LL | fn baz(x: &>::A) {} +LL | fn baz(x: &>::A) {} | +++++ error: aborting due to 5 previous errors diff --git a/tests/ui/error-codes/E0259.rs b/tests/ui/error-codes/E0259.rs index e7e94d58635c..78e56deccb4c 100644 --- a/tests/ui/error-codes/E0259.rs +++ b/tests/ui/error-codes/E0259.rs @@ -1,8 +1,8 @@ -#![feature(rustc_private)] +#![feature(test)] extern crate alloc; -extern crate libc as alloc; +extern crate test as alloc; //~^ ERROR E0259 fn main() {} diff --git a/tests/ui/error-codes/E0259.stderr b/tests/ui/error-codes/E0259.stderr index 6e086268fc6d..975d1a161a01 100644 --- a/tests/ui/error-codes/E0259.stderr +++ b/tests/ui/error-codes/E0259.stderr @@ -4,13 +4,13 @@ error[E0259]: the name `alloc` is defined multiple times LL | extern crate alloc; | ------------------- previous import of the extern crate `alloc` here LL | -LL | extern crate libc as alloc; +LL | extern crate test as alloc; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `alloc` reimported here | = note: `alloc` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | extern crate libc as other_alloc; +LL | extern crate test as other_alloc; | error: aborting due to 1 previous error diff --git a/tests/ui/errors/pic-linker.rs b/tests/ui/errors/pic-linker.rs new file mode 100644 index 000000000000..d90989903048 --- /dev/null +++ b/tests/ui/errors/pic-linker.rs @@ -0,0 +1,12 @@ +// `-z text` caused the linker to error if there were any non-position-independent +// code (PIC) sections. This test checks that this no longer happens. +// See https://github.com/rust-lang/rust/pull/39803 + +//@ ignore-windows +//@ ignore-macos +//@ ignore-cross-compile + +//@ compile-flags: -Clink-args=-Wl,-z,text +//@ run-pass + +fn main() {} diff --git a/tests/ui/expr-block-fn.rs b/tests/ui/expr/block-fn.rs similarity index 100% rename from tests/ui/expr-block-fn.rs rename to tests/ui/expr/block-fn.rs diff --git a/tests/ui/expr-block-generic.rs b/tests/ui/expr/block-generic.rs similarity index 100% rename from tests/ui/expr-block-generic.rs rename to tests/ui/expr/block-generic.rs diff --git a/tests/ui/expr-block.rs b/tests/ui/expr/block.rs similarity index 100% rename from tests/ui/expr-block.rs rename to tests/ui/expr/block.rs diff --git a/tests/ui/expr-copy.rs b/tests/ui/expr/copy.rs similarity index 100% rename from tests/ui/expr-copy.rs rename to tests/ui/expr/copy.rs diff --git a/tests/ui/expr-if-generic.rs b/tests/ui/expr/if-generic.rs similarity index 100% rename from tests/ui/expr-if-generic.rs rename to tests/ui/expr/if-generic.rs diff --git a/tests/ui/expr-if-panic-all.rs b/tests/ui/expr/if-panic-all.rs similarity index 100% rename from tests/ui/expr-if-panic-all.rs rename to tests/ui/expr/if-panic-all.rs diff --git a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr index 1264d9693425..c4c1c830afa6 100644 --- a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr +++ b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr @@ -14,7 +14,7 @@ LL | || } LL | | }); | |______^ expected an `FnOnce(&bool)` closure, found `bool` | - = help: the trait `for<'a> FnOnce<(&'a bool,)>` is not implemented for `bool` + = help: the trait `for<'a> FnOnce(&'a bool)` is not implemented for `bool` note: required by a bound in `Option::::filter` --> $SRC_DIR/core/src/option.rs:LL:COL help: you might have meant to create the closure instead of a block diff --git a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr index a7ed9f5880b6..54d3c6727f77 100644 --- a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr +++ b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr @@ -11,7 +11,7 @@ LL | | Some(x * 2) LL | | }); | |_____^ expected an `FnOnce({integer})` closure, found `Option` | - = help: the trait `FnOnce<({integer},)>` is not implemented for `Option` + = help: the trait `FnOnce({integer})` is not implemented for `Option` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL help: you might have meant to open the closure body instead of placing a closure within a block diff --git a/tests/ui/expr-scope.rs b/tests/ui/expr/scope.rs similarity index 100% rename from tests/ui/expr-scope.rs rename to tests/ui/expr/scope.rs diff --git a/tests/ui/extern-flag/no-nounused.rs b/tests/ui/extern-flag/no-nounused.rs index bed8006542ca..30b663398763 100644 --- a/tests/ui/extern-flag/no-nounused.rs +++ b/tests/ui/extern-flag/no-nounused.rs @@ -2,5 +2,5 @@ //@ compile-flags: -Zunstable-options -Dunused-crate-dependencies //@ edition:2018 -fn main() { //~ ERROR external crate `somedep` unused in `no_nounused` +fn main() { //~ ERROR extern crate `somedep` is unused in crate `no_nounused` } diff --git a/tests/ui/extern-flag/no-nounused.stderr b/tests/ui/extern-flag/no-nounused.stderr index 171079287f0b..2b42e851561a 100644 --- a/tests/ui/extern-flag/no-nounused.stderr +++ b/tests/ui/extern-flag/no-nounused.stderr @@ -1,9 +1,10 @@ -error: external crate `somedep` unused in `no_nounused`: remove the dependency or add `use somedep as _;` +error: extern crate `somedep` is unused in crate `no_nounused` --> $DIR/no-nounused.rs:5:1 | LL | fn main() { | ^ | + = help: remove the dependency or add `use somedep as _;` to the crate root = note: requested on the command line with `-D unused-crate-dependencies` error: aborting due to 1 previous error diff --git a/tests/ui/extern/extern-wrong-value-type.stderr b/tests/ui/extern/extern-wrong-value-type.stderr index 1c08aa1717f4..692a66011711 100644 --- a/tests/ui/extern/extern-wrong-value-type.stderr +++ b/tests/ui/extern/extern-wrong-value-type.stderr @@ -6,7 +6,7 @@ LL | is_fn(f); | | | required by a bound introduced by this call | - = help: the trait `Fn<()>` is not implemented for fn item `extern "C" fn() {f}` + = help: the trait `Fn()` is not implemented for fn item `extern "C" fn() {f}` = note: wrap the `extern "C" fn() {f}` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `is_fn` --> $DIR/extern-wrong-value-type.rs:4:28 diff --git a/tests/ui/feature-gates/feature-gate-extern_prelude.rs b/tests/ui/feature-gates/feature-gate-extern_prelude.rs deleted file mode 100644 index 237099e79010..000000000000 --- a/tests/ui/feature-gates/feature-gate-extern_prelude.rs +++ /dev/null @@ -1 +0,0 @@ -can-only-test-this-in-run-make-fulldeps //~ ERROR expected one of `!` or `::`, found `-` diff --git a/tests/ui/feature-gates/feature-gate-extern_prelude.stderr b/tests/ui/feature-gates/feature-gate-extern_prelude.stderr deleted file mode 100644 index 3b0ffae86965..000000000000 --- a/tests/ui/feature-gates/feature-gate-extern_prelude.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: expected one of `!` or `::`, found `-` - --> $DIR/feature-gate-extern_prelude.rs:1:4 - | -LL | can-only-test-this-in-run-make-fulldeps - | ^ expected one of `!` or `::` - -error: aborting due to 1 previous error - diff --git a/tests/ui/feature-gates/feature-gate-global-registration.rs b/tests/ui/feature-gates/feature-gate-global-registration.rs new file mode 100644 index 000000000000..6ac367120903 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-global-registration.rs @@ -0,0 +1,3 @@ +todo!(); //~ ERROR + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-global-registration.stderr b/tests/ui/feature-gates/feature-gate-global-registration.stderr new file mode 100644 index 000000000000..70538ae6f317 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-global-registration.stderr @@ -0,0 +1,13 @@ +error: expected one of `!` or `::`, found `(` + --> $DIR/feature-gate-global-registration.rs:1:1 + | +LL | todo!(); + | ^^^^^^^ + | | + | expected one of `!` or `::` + | in this macro invocation + | + = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + diff --git a/tests/ui/feature-gates/feature-gate-macro-metavar-expr-concat.rs b/tests/ui/feature-gates/feature-gate-macro-metavar-expr-concat.rs new file mode 100644 index 000000000000..e700999ae4bd --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-macro-metavar-expr-concat.rs @@ -0,0 +1,9 @@ +macro_rules! join { + ($lhs:ident, $rhs:ident) => { + let ${concat($lhs, $rhs)}: () = (); + //~^ ERROR the `concat` meta-variable expression is unstable + }; +} + +fn main() { +} diff --git a/tests/ui/feature-gates/feature-gate-macro-metavar-expr-concat.stderr b/tests/ui/feature-gates/feature-gate-macro-metavar-expr-concat.stderr new file mode 100644 index 000000000000..5b2589d8c89b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-macro-metavar-expr-concat.stderr @@ -0,0 +1,13 @@ +error[E0658]: the `concat` meta-variable expression is unstable + --> $DIR/feature-gate-macro-metavar-expr-concat.rs:3:14 + | +LL | let ${concat($lhs, $rhs)}: () = (); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #124225 for more information + = help: add `#![feature(macro_metavar_expr_concat)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-offset-of-slice.rs b/tests/ui/feature-gates/feature-gate-offset-of-slice.rs new file mode 100644 index 000000000000..23e8668bc685 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-offset-of-slice.rs @@ -0,0 +1,26 @@ +use std::mem::offset_of; + +struct S { + a: u8, + b: (u8, u8), + c: [i32], +} + +struct T { + x: i32, + y: S, +} + +type Tup = (i32, [i32]); + +fn main() { + offset_of!(S, c); //~ ERROR size for values of type `[i32]` cannot be known at compilation time +} + +fn other() { + offset_of!(T, y); //~ ERROR size for values of type `[i32]` cannot be known at compilation time +} + +fn tuple() { + offset_of!(Tup, 1); //~ ERROR size for values of type `[i32]` cannot be known at compilation time +} diff --git a/tests/ui/feature-gates/feature-gate-offset-of-slice.stderr b/tests/ui/feature-gates/feature-gate-offset-of-slice.stderr new file mode 100644 index 000000000000..5cf34a897f4d --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-offset-of-slice.stderr @@ -0,0 +1,35 @@ +error[E0277]: the size for values of type `[i32]` cannot be known at compilation time + --> $DIR/feature-gate-offset-of-slice.rs:17:5 + | +LL | offset_of!(S, c); + | ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[i32]` + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[i32]` cannot be known at compilation time + --> $DIR/feature-gate-offset-of-slice.rs:21:5 + | +LL | offset_of!(T, y); + | ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: within `S`, the trait `Sized` is not implemented for `[i32]`, which is required by `S: Sized` +note: required because it appears within the type `S` + --> $DIR/feature-gate-offset-of-slice.rs:3:8 + | +LL | struct S { + | ^ + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `[i32]` cannot be known at compilation time + --> $DIR/feature-gate-offset-of-slice.rs:25:5 + | +LL | offset_of!(Tup, 1); + | ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[i32]` + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr index 9bab366f7fef..815013733a98 100644 --- a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr +++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr @@ -1,13 +1,3 @@ -error[E0658]: the `#[optimize]` attribute is an experimental feature - --> $DIR/feature-gate-optimize_attribute.rs:4:1 - | -LL | #[optimize(size)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #54882 for more information - = help: add `#![feature(optimize_attribute)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - error[E0658]: the `#[optimize]` attribute is an experimental feature --> $DIR/feature-gate-optimize_attribute.rs:7:1 | @@ -38,6 +28,16 @@ LL | #[optimize(banana)] = help: add `#![feature(optimize_attribute)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: the `#[optimize]` attribute is an experimental feature + --> $DIR/feature-gate-optimize_attribute.rs:4:1 + | +LL | #[optimize(size)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #54882 for more information + = help: add `#![feature(optimize_attribute)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: the `#[optimize]` attribute is an experimental feature --> $DIR/feature-gate-optimize_attribute.rs:2:1 | diff --git a/tests/ui/feature-gates/feature-gate-precise-capturing.rs b/tests/ui/feature-gates/feature-gate-precise-capturing.rs index 0c3b49776230..47a21539d377 100644 --- a/tests/ui/feature-gates/feature-gate-precise-capturing.rs +++ b/tests/ui/feature-gates/feature-gate-precise-capturing.rs @@ -1,4 +1,4 @@ -fn hello() -> impl use<> Sized {} +fn hello() -> impl Sized + use<> {} //~^ ERROR precise captures on `impl Trait` are experimental fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-precise-capturing.stderr b/tests/ui/feature-gates/feature-gate-precise-capturing.stderr index 102b39148f9f..04365408880d 100644 --- a/tests/ui/feature-gates/feature-gate-precise-capturing.stderr +++ b/tests/ui/feature-gates/feature-gate-precise-capturing.stderr @@ -1,8 +1,8 @@ error[E0658]: precise captures on `impl Trait` are experimental - --> $DIR/feature-gate-precise-capturing.rs:1:20 + --> $DIR/feature-gate-precise-capturing.rs:1:28 | -LL | fn hello() -> impl use<> Sized {} - | ^^^ +LL | fn hello() -> impl Sized + use<> {} + | ^^^ | = note: see issue #123432 for more information = help: add `#![feature(precise_capturing)]` to the crate attributes to enable diff --git a/tests/ui/feature-gates/feature-gate-print-check-cfg.rs b/tests/ui/feature-gates/feature-gate-print-check-cfg.rs new file mode 100644 index 000000000000..304e0c132e50 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-print-check-cfg.rs @@ -0,0 +1,3 @@ +//@ compile-flags: --print=check-cfg + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-print-check-cfg.stderr b/tests/ui/feature-gates/feature-gate-print-check-cfg.stderr new file mode 100644 index 000000000000..62ee44b94a48 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-print-check-cfg.stderr @@ -0,0 +1,2 @@ +error: the `-Z unstable-options` flag must also be passed to enable the check-cfg print option + diff --git a/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.rs b/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.rs deleted file mode 100644 index ed5db56e0e83..000000000000 --- a/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.rs +++ /dev/null @@ -1,14 +0,0 @@ -pub fn main() { - if let Some(Some(&x)) = &Some(&Some(0)) { - //~^ ERROR: mismatched types [E0308] - let _: u32 = x; - } - if let Some(&Some(x)) = &Some(Some(0)) { - //~^ ERROR: mismatched types [E0308] - let _: u32 = x; - } - if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { - //~^ ERROR: mismatched types [E0308] - let _: u32 = x; - } -} diff --git a/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.stderr b/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.stderr deleted file mode 100644 index 0f0051325cdf..000000000000 --- a/tests/ui/feature-gates/feature-gate-ref_pat_everywhere.stderr +++ /dev/null @@ -1,49 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/feature-gate-ref_pat_everywhere.rs:2:22 - | -LL | if let Some(Some(&x)) = &Some(&Some(0)) { - | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` - | | - | expected integer, found `&_` - | - = note: expected type `{integer}` - found reference `&_` -help: consider removing `&` from the pattern - | -LL | if let Some(Some(x)) = &Some(&Some(0)) { - | ~ - -error[E0308]: mismatched types - --> $DIR/feature-gate-ref_pat_everywhere.rs:6:17 - | -LL | if let Some(&Some(x)) = &Some(Some(0)) { - | ^^^^^^^^ -------------- this expression has type `&Option>` - | | - | expected `Option<{integer}>`, found `&_` - | - = note: expected enum `Option<{integer}>` - found reference `&_` - -error[E0308]: mismatched types - --> $DIR/feature-gate-ref_pat_everywhere.rs:10:22 - | -LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { - | ^^^^^^ ----------------------- this expression has type `&mut Option<&mut Option<{integer}>>` - | | - | expected integer, found `&mut _` - | - = note: expected type `{integer}` - found mutable reference `&mut _` -note: to declare a mutable binding use: `mut x` - --> $DIR/feature-gate-ref_pat_everywhere.rs:10:22 - | -LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { - | ^^^^^^ -help: consider removing `&mut` from the pattern - | -LL | if let Some(Some(x)) = &mut Some(&mut Some(0)) { - | ~ - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/feature-gates/feature-gate-shorter_tail_lifetimes.rs b/tests/ui/feature-gates/feature-gate-shorter_tail_lifetimes.rs new file mode 100644 index 000000000000..5292c44bb2d4 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-shorter_tail_lifetimes.rs @@ -0,0 +1,8 @@ +fn f() -> usize { + let c = std::cell::RefCell::new(".."); + c.borrow().len() //~ ERROR: `c` does not live long enough +} + +fn main() { + let _ = f(); +} diff --git a/tests/ui/feature-gates/feature-gate-shorter_tail_lifetimes.stderr b/tests/ui/feature-gates/feature-gate-shorter_tail_lifetimes.stderr new file mode 100644 index 000000000000..648c3d5daa1c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-shorter_tail_lifetimes.stderr @@ -0,0 +1,26 @@ +error[E0597]: `c` does not live long enough + --> $DIR/feature-gate-shorter_tail_lifetimes.rs:3:5 + | +LL | let c = std::cell::RefCell::new(".."); + | - binding `c` declared here +LL | c.borrow().len() + | ^--------- + | | + | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... +LL | } + | - + | | + | `c` dropped here while still borrowed + | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, &str>` + | + = note: the temporary is part of an expression at the end of a block; + consider forcing this temporary to be dropped sooner, before the block's local variables are dropped +help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block + | +LL | let x = c.borrow().len(); x + | +++++++ +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs index b8ce9c85b727..ff528274c59b 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs @@ -16,7 +16,7 @@ impl Fn<()> for Foo { } struct Foo1; impl FnOnce() for Foo1 { - //~^ ERROR associated type bindings are not allowed here + //~^ ERROR associated item constraints are not allowed here //~| ERROR manual implementations of `FnOnce` are experimental //~| ERROR not all trait items implemented extern "rust-call" fn call_once(self, args: ()) -> () {} diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr index c3f161469e30..1b9febd431d6 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr @@ -80,7 +80,7 @@ error[E0277]: expected a `FnMut()` closure, found `Foo` LL | impl Fn<()> for Foo { | ^^^ expected an `FnMut()` closure, found `Foo` | - = help: the trait `FnMut<()>` is not implemented for `Foo` + = help: the trait `FnMut()` is not implemented for `Foo` = note: wrap the `Foo` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `Fn` --> $SRC_DIR/core/src/ops/function.rs:LL:COL @@ -105,11 +105,11 @@ LL | impl FnOnce() for Foo1 { | = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:18:6 | LL | impl FnOnce() for Foo1 { - | ^^^^^^^^ associated type not allowed here + | ^^^^^^^^ associated item constraint not allowed here | help: parenthesized trait syntax expands to `FnOnce<(), Output=()>` --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:18:6 @@ -149,7 +149,7 @@ error[E0277]: expected a `FnOnce()` closure, found `Bar` LL | impl FnMut<()> for Bar { | ^^^ expected an `FnOnce()` closure, found `Bar` | - = help: the trait `FnOnce<()>` is not implemented for `Bar` + = help: the trait `FnOnce()` is not implemented for `Bar` = note: wrap the `Bar` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `FnMut` --> $SRC_DIR/core/src/ops/function.rs:LL:COL diff --git a/tests/ui/feature-gates/feature-gate-unsafe-attributes.rs b/tests/ui/feature-gates/feature-gate-unsafe-attributes.rs new file mode 100644 index 000000000000..9eba415dda0e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unsafe-attributes.rs @@ -0,0 +1,8 @@ +#[unsafe(no_mangle)] //~ ERROR [E0658] +extern "C" fn foo() { + +} + +fn main() { + foo(); +} diff --git a/tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr b/tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr new file mode 100644 index 000000000000..dfcea756b02b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr @@ -0,0 +1,13 @@ +error[E0658]: `#[unsafe()]` markers for attributes are experimental + --> $DIR/feature-gate-unsafe-attributes.rs:1:3 + | +LL | #[unsafe(no_mangle)] + | ^^^^^^ + | + = note: see issue #123757 for more information + = help: add `#![feature(unsafe_attributes)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.rs b/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.rs new file mode 100644 index 000000000000..eab134a4a4de --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.rs @@ -0,0 +1,5 @@ +unsafe extern "C" { + //~^ ERROR extern block cannot be declared unsafe +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.stderr b/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.stderr new file mode 100644 index 000000000000..7e9b199a2db5 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unsafe-extern-blocks.stderr @@ -0,0 +1,8 @@ +error: extern block cannot be declared unsafe + --> $DIR/feature-gate-unsafe-extern-blocks.rs:1:1 + | +LL | unsafe extern "C" { + | ^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/feature-gates/feature-gate-vectorcall.stderr b/tests/ui/feature-gates/feature-gate-vectorcall.stderr index df93e8812c1e..b20e41887b9b 100644 --- a/tests/ui/feature-gates/feature-gate-vectorcall.stderr +++ b/tests/ui/feature-gates/feature-gate-vectorcall.stderr @@ -4,6 +4,7 @@ error[E0658]: vectorcall is experimental and subject to change LL | extern "vectorcall" fn f() {} | ^^^^^^^^^^^^ | + = note: see issue #124485 for more information = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date @@ -13,6 +14,7 @@ error[E0658]: vectorcall is experimental and subject to change LL | extern "vectorcall" fn m(); | ^^^^^^^^^^^^ | + = note: see issue #124485 for more information = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date @@ -22,6 +24,7 @@ error[E0658]: vectorcall is experimental and subject to change LL | extern "vectorcall" fn dm() {} | ^^^^^^^^^^^^ | + = note: see issue #124485 for more information = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date @@ -31,6 +34,7 @@ error[E0658]: vectorcall is experimental and subject to change LL | extern "vectorcall" fn m() {} | ^^^^^^^^^^^^ | + = note: see issue #124485 for more information = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date @@ -40,6 +44,7 @@ error[E0658]: vectorcall is experimental and subject to change LL | extern "vectorcall" fn im() {} | ^^^^^^^^^^^^ | + = note: see issue #124485 for more information = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date @@ -49,6 +54,7 @@ error[E0658]: vectorcall is experimental and subject to change LL | type TA = extern "vectorcall" fn(); | ^^^^^^^^^^^^ | + = note: see issue #124485 for more information = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date @@ -58,6 +64,7 @@ error[E0658]: vectorcall is experimental and subject to change LL | extern "vectorcall" {} | ^^^^^^^^^^^^ | + = note: see issue #124485 for more information = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs index bd3b69c2b4a6..ca60bc62b518 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs @@ -45,7 +45,7 @@ mod inline { //~| NOTE not a function or closure #[inline = "2100"] fn f() { } - //~^ ERROR attribute must be of the form + //~^ ERROR valid forms for the attribute are //~| WARN this was previously accepted //~| NOTE #[deny(ill_formed_attribute_input)]` on by default //~| NOTE for more information, see issue #57571 diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr index 89fa2abffc2f..ac2bf78157d2 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr @@ -7,7 +7,7 @@ LL | #![rustc_main] = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: attribute must be of the form `#[inline]` or `#[inline(always|never)]` +error: valid forms for the attribute are `#[inline]` and `#[inline(always|never)]` --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:47:5 | LL | #[inline = "2100"] fn f() { } diff --git a/tests/ui/feature-gates/issue-43106-gating-of-stable.stderr b/tests/ui/feature-gates/issue-43106-gating-of-stable.stderr index e4cc088e2cd3..677fef3a926b 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-stable.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-stable.stderr @@ -1,9 +1,3 @@ -error[E0734]: stability attributes may not be used outside of the standard library - --> $DIR/issue-43106-gating-of-stable.rs:10:1 - | -LL | #[stable()] - | ^^^^^^^^^^^ - error[E0734]: stability attributes may not be used outside of the standard library --> $DIR/issue-43106-gating-of-stable.rs:14:9 | @@ -34,6 +28,12 @@ error[E0734]: stability attributes may not be used outside of the standard libra LL | #[stable()] | ^^^^^^^^^^^ +error[E0734]: stability attributes may not be used outside of the standard library + --> $DIR/issue-43106-gating-of-stable.rs:10:1 + | +LL | #[stable()] + | ^^^^^^^^^^^ + error[E0734]: stability attributes may not be used outside of the standard library --> $DIR/issue-43106-gating-of-stable.rs:7:1 | diff --git a/tests/ui/feature-gates/issue-43106-gating-of-unstable.stderr b/tests/ui/feature-gates/issue-43106-gating-of-unstable.stderr index f7c6e631cd17..a2f361878c6d 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-unstable.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-unstable.stderr @@ -1,9 +1,3 @@ -error[E0734]: stability attributes may not be used outside of the standard library - --> $DIR/issue-43106-gating-of-unstable.rs:10:1 - | -LL | #[unstable()] - | ^^^^^^^^^^^^^ - error[E0734]: stability attributes may not be used outside of the standard library --> $DIR/issue-43106-gating-of-unstable.rs:14:9 | @@ -34,6 +28,12 @@ error[E0734]: stability attributes may not be used outside of the standard libra LL | #[unstable()] | ^^^^^^^^^^^^^ +error[E0734]: stability attributes may not be used outside of the standard library + --> $DIR/issue-43106-gating-of-unstable.rs:10:1 + | +LL | #[unstable()] + | ^^^^^^^^^^^^^ + error[E0734]: stability attributes may not be used outside of the standard library --> $DIR/issue-43106-gating-of-unstable.rs:7:1 | diff --git a/tests/ui/feature-gates/rustc-private.rs b/tests/ui/feature-gates/rustc-private.rs index 7b8944bb0a0b..aa44f790c8ae 100644 --- a/tests/ui/feature-gates/rustc-private.rs +++ b/tests/ui/feature-gates/rustc-private.rs @@ -1,5 +1,5 @@ // gate-test-rustc_private -extern crate libc; //~ ERROR use of unstable library feature 'rustc_private' +extern crate cfg_if; //~ ERROR use of unstable library feature 'rustc_private' fn main() {} diff --git a/tests/ui/feature-gates/rustc-private.stderr b/tests/ui/feature-gates/rustc-private.stderr index 03397cba763d..96cc98619fd7 100644 --- a/tests/ui/feature-gates/rustc-private.stderr +++ b/tests/ui/feature-gates/rustc-private.stderr @@ -1,8 +1,8 @@ error[E0658]: use of unstable library feature 'rustc_private': this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead? --> $DIR/rustc-private.rs:3:1 | -LL | extern crate libc; - | ^^^^^^^^^^^^^^^^^^ +LL | extern crate cfg_if; + | ^^^^^^^^^^^^^^^^^^^^ | = note: see issue #27812 for more information = help: add `#![feature(rustc_private)]` to the crate attributes to enable diff --git a/tests/ui/stmt_expr_attrs_no_feature.rs b/tests/ui/feature-gates/stmt_expr_attrs_no_feature.rs similarity index 100% rename from tests/ui/stmt_expr_attrs_no_feature.rs rename to tests/ui/feature-gates/stmt_expr_attrs_no_feature.rs diff --git a/tests/ui/stmt_expr_attrs_no_feature.stderr b/tests/ui/feature-gates/stmt_expr_attrs_no_feature.stderr similarity index 100% rename from tests/ui/stmt_expr_attrs_no_feature.stderr rename to tests/ui/feature-gates/stmt_expr_attrs_no_feature.stderr diff --git a/tests/ui/ffi_const.rs b/tests/ui/ffi-attrs/ffi_const.rs similarity index 100% rename from tests/ui/ffi_const.rs rename to tests/ui/ffi-attrs/ffi_const.rs diff --git a/tests/ui/ffi_const.stderr b/tests/ui/ffi-attrs/ffi_const.stderr similarity index 100% rename from tests/ui/ffi_const.stderr rename to tests/ui/ffi-attrs/ffi_const.stderr diff --git a/tests/ui/ffi_const2.rs b/tests/ui/ffi-attrs/ffi_const2.rs similarity index 100% rename from tests/ui/ffi_const2.rs rename to tests/ui/ffi-attrs/ffi_const2.rs diff --git a/tests/ui/ffi_const2.stderr b/tests/ui/ffi-attrs/ffi_const2.stderr similarity index 100% rename from tests/ui/ffi_const2.stderr rename to tests/ui/ffi-attrs/ffi_const2.stderr diff --git a/tests/ui/ffi_pure.rs b/tests/ui/ffi-attrs/ffi_pure.rs similarity index 100% rename from tests/ui/ffi_pure.rs rename to tests/ui/ffi-attrs/ffi_pure.rs diff --git a/tests/ui/ffi_pure.stderr b/tests/ui/ffi-attrs/ffi_pure.stderr similarity index 100% rename from tests/ui/ffi_pure.stderr rename to tests/ui/ffi-attrs/ffi_pure.stderr diff --git a/tests/ui/main-wrong-location.rs b/tests/ui/fn-main/wrong-location.rs similarity index 100% rename from tests/ui/main-wrong-location.rs rename to tests/ui/fn-main/wrong-location.rs diff --git a/tests/ui/main-wrong-location.stderr b/tests/ui/fn-main/wrong-location.stderr similarity index 70% rename from tests/ui/main-wrong-location.stderr rename to tests/ui/fn-main/wrong-location.stderr index 9486a9405628..c47092bc47e9 100644 --- a/tests/ui/main-wrong-location.stderr +++ b/tests/ui/fn-main/wrong-location.stderr @@ -1,11 +1,11 @@ -error[E0601]: `main` function not found in crate `main_wrong_location` - --> $DIR/main-wrong-location.rs:5:2 +error[E0601]: `main` function not found in crate `wrong_location` + --> $DIR/wrong-location.rs:5:2 | LL | } - | ^ the main function must be defined at the crate level (in `$DIR/main-wrong-location.rs`) + | ^ the main function must be defined at the crate level (in `$DIR/wrong-location.rs`) | note: here is a function named `main` - --> $DIR/main-wrong-location.rs:4:5 + --> $DIR/wrong-location.rs:4:5 | LL | fn main() { } | ^^^^^^^^^ diff --git a/tests/ui/main-wrong-type.rs b/tests/ui/fn-main/wrong-type.rs similarity index 100% rename from tests/ui/main-wrong-type.rs rename to tests/ui/fn-main/wrong-type.rs diff --git a/tests/ui/main-wrong-type.stderr b/tests/ui/fn-main/wrong-type.stderr similarity index 90% rename from tests/ui/main-wrong-type.stderr rename to tests/ui/fn-main/wrong-type.stderr index d07fc09064f2..2b0096431dba 100644 --- a/tests/ui/main-wrong-type.stderr +++ b/tests/ui/fn-main/wrong-type.stderr @@ -1,5 +1,5 @@ error[E0580]: `main` function has wrong type - --> $DIR/main-wrong-type.rs:6:1 + --> $DIR/wrong-type.rs:6:1 | LL | fn main(foo: S) { | ^^^^^^^^^^^^^^^ incorrect number of function parameters diff --git a/tests/ui/fn/fn-trait-formatting.stderr b/tests/ui/fn/fn-trait-formatting.stderr index c5e2f41691fa..9fdef49c5ef9 100644 --- a/tests/ui/fn/fn-trait-formatting.stderr +++ b/tests/ui/fn/fn-trait-formatting.stderr @@ -47,7 +47,7 @@ LL | needs_fn(1); | | | required by a bound introduced by this call | - = help: the trait `Fn<(isize,)>` is not implemented for `{integer}` + = help: the trait `Fn(isize)` is not implemented for `{integer}` note: required by a bound in `needs_fn` --> $DIR/fn-trait-formatting.rs:1:31 | diff --git a/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.rs b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.rs new file mode 100644 index 000000000000..5250e3a3d93e --- /dev/null +++ b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.rs @@ -0,0 +1,57 @@ +//! Test that coercing between function items of different functions works, +//! as long as their signatures match. The resulting value is a function pointer. + +#![feature(type_alias_impl_trait)] + +fn foo(t: T) -> T { + t +} + +fn bar(t: T) -> T { + t +} + +type F = impl Sized; + +fn f(a: F) { + let mut x = bar::; + x = foo::<()>; //~ ERROR: mismatched types + x(a); + x(()); +} + +type I = impl Sized; + +fn i(a: I) { + let mut x = bar::<()>; + x = foo::; //~ ERROR: mismatched types + x(a); + x(()); +} + +type J = impl Sized; + +fn j(a: J) { + let x = match true { + true => bar::, + false => foo::<()>, + }; + x(a); + x(()); +} + +fn k() -> impl Sized { + fn bind T>(_: T, f: F) -> F { + f + } + let x = match true { + true => { + let f = foo; + bind(k(), f) + } + false => bar::<()>, + }; + todo!() +} + +fn main() {} diff --git a/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr new file mode 100644 index 000000000000..0b3331b040d8 --- /dev/null +++ b/tests/ui/fn/fn_def_opaque_coercion_to_fn_ptr.stderr @@ -0,0 +1,38 @@ +error[E0308]: mismatched types + --> $DIR/fn_def_opaque_coercion_to_fn_ptr.rs:18:9 + | +LL | type F = impl Sized; + | ---------- the expected opaque type +... +LL | let mut x = bar::; + | -------- expected due to this value +LL | x = foo::<()>; + | ^^^^^^^^^ expected fn item, found a different fn item + | + = note: expected fn item `fn(F) -> F {bar::}` + found fn item `fn(()) {foo::<()>}` + +error[E0308]: mismatched types + --> $DIR/fn_def_opaque_coercion_to_fn_ptr.rs:27:9 + | +LL | fn foo(t: T) -> T { + | -------------------- function `foo` defined here +... +LL | type I = impl Sized; + | ---------- the found opaque type +... +LL | let mut x = bar::<()>; + | --------- expected due to this value +LL | x = foo::; + | ^^^^^^^^ expected fn item, found a different fn item + | + = note: expected fn item `fn(()) {bar::<()>}` + found fn item `fn(I) -> I {foo::}` +help: use parentheses to call this function + | +LL | x = foo::(/* I */); + | +++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/fn/issue-39259.rs b/tests/ui/fn/issue-39259.rs index 16983b652fc8..d4569a9094b2 100644 --- a/tests/ui/fn/issue-39259.rs +++ b/tests/ui/fn/issue-39259.rs @@ -4,7 +4,7 @@ struct S; impl Fn(u32) -> u32 for S { - //~^ ERROR associated type bindings are not allowed here [E0229] + //~^ ERROR associated item constraints are not allowed here [E0229] //~| ERROR expected a `FnMut(u32)` closure, found `S` fn call(&self) -> u32 { //~^ ERROR method `call` has 1 parameter but the declaration in trait `call` has 2 diff --git a/tests/ui/fn/issue-39259.stderr b/tests/ui/fn/issue-39259.stderr index 47150a3c155c..a923d7b26efa 100644 --- a/tests/ui/fn/issue-39259.stderr +++ b/tests/ui/fn/issue-39259.stderr @@ -1,8 +1,8 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-39259.rs:6:17 | LL | impl Fn(u32) -> u32 for S { - | ^^^ associated type not allowed here + | ^^^ associated item constraint not allowed here | help: parenthesized trait syntax expands to `Fn<(u32,), Output=u32>` --> $DIR/issue-39259.rs:6:6 @@ -16,7 +16,7 @@ error[E0277]: expected a `FnMut(u32)` closure, found `S` LL | impl Fn(u32) -> u32 for S { | ^ expected an `FnMut(u32)` closure, found `S` | - = help: the trait `FnMut<(u32,)>` is not implemented for `S` + = help: the trait `FnMut(u32)` is not implemented for `S` note: required by a bound in `Fn` --> $SRC_DIR/core/src/ops/function.rs:LL:COL diff --git a/tests/ui/foreign/foreign-fn-linkname.rs b/tests/ui/foreign/foreign-fn-linkname.rs index 47edf6fc7bba..7ced34e73fa7 100644 --- a/tests/ui/foreign/foreign-fn-linkname.rs +++ b/tests/ui/foreign/foreign-fn-linkname.rs @@ -1,20 +1,14 @@ //@ run-pass //@ ignore-sgx no libc -// Ensure no false positive on "unused extern crate" lint -#![deny(unused_extern_crates)] - -#![feature(rustc_private)] - -extern crate libc; use std::ffi::CString; mod mlibc { - use libc::{c_char, size_t}; + use std::ffi::c_char; extern "C" { #[link_name = "strlen"] - pub fn my_strlen(str: *const c_char) -> size_t; + pub fn my_strlen(str: *const c_char) -> usize; } } diff --git a/tests/ui/foreign-fn-return-lifetime.rs b/tests/ui/foreign/foreign-fn-return-lifetime.rs similarity index 100% rename from tests/ui/foreign-fn-return-lifetime.rs rename to tests/ui/foreign/foreign-fn-return-lifetime.rs diff --git a/tests/ui/foreign-fn-return-lifetime.stderr b/tests/ui/foreign/foreign-fn-return-lifetime.stderr similarity index 100% rename from tests/ui/foreign-fn-return-lifetime.stderr rename to tests/ui/foreign/foreign-fn-return-lifetime.stderr diff --git a/tests/ui/foreign/foreign2.rs b/tests/ui/foreign/foreign2.rs index eb24df35033c..178a04255cce 100644 --- a/tests/ui/foreign/foreign2.rs +++ b/tests/ui/foreign/foreign2.rs @@ -1,9 +1,8 @@ //@ run-pass -#![allow(dead_code)] //@ pretty-expanded FIXME #23616 -#![feature(rustc_private)] -extern crate libc; +#![allow(dead_code)] +#![feature(rustc_private)] mod bar { extern "C" {} @@ -13,14 +12,37 @@ mod zed { extern "C" {} } +#[cfg(not(windows))] mod mlibc { - use libc::{c_int, c_void, size_t, ssize_t}; + extern crate libc; + use self::libc::{c_int, c_void, size_t, ssize_t}; extern "C" { pub fn write(fd: c_int, buf: *const c_void, count: size_t) -> ssize_t; } } +#[cfg(windows)] +mod mlibc { + #![allow(non_snake_case)] + + use std::ffi::c_void; + + pub type BOOL = i32; + pub type HANDLE = *mut c_void; + + #[link(name = "ntdll")] + extern "system" { + pub fn WriteFile( + hfile: HANDLE, + lpbuffer: *const u8, + nnumberofbytestowrite: u32, + lpnumberofbyteswritten: *mut u32, + lpoverlapped: *mut c_void, + ) -> BOOL; + } +} + mod baz { extern "C" {} } diff --git a/tests/ui/optimization-fuel-0.rs b/tests/ui/fuel/optimization-fuel-0.rs similarity index 100% rename from tests/ui/optimization-fuel-0.rs rename to tests/ui/fuel/optimization-fuel-0.rs diff --git a/tests/ui/optimization-fuel-0.stderr b/tests/ui/fuel/optimization-fuel-0.stderr similarity index 100% rename from tests/ui/optimization-fuel-0.stderr rename to tests/ui/fuel/optimization-fuel-0.stderr diff --git a/tests/ui/optimization-fuel-1.rs b/tests/ui/fuel/optimization-fuel-1.rs similarity index 100% rename from tests/ui/optimization-fuel-1.rs rename to tests/ui/fuel/optimization-fuel-1.rs diff --git a/tests/ui/optimization-fuel-1.stderr b/tests/ui/fuel/optimization-fuel-1.stderr similarity index 100% rename from tests/ui/optimization-fuel-1.stderr rename to tests/ui/fuel/optimization-fuel-1.stderr diff --git a/tests/ui/print-fuel/print-fuel.rs b/tests/ui/fuel/print-fuel.rs similarity index 100% rename from tests/ui/print-fuel/print-fuel.rs rename to tests/ui/fuel/print-fuel.rs diff --git a/tests/ui/print-fuel/print-fuel.stderr b/tests/ui/fuel/print-fuel.stderr similarity index 100% rename from tests/ui/print-fuel/print-fuel.stderr rename to tests/ui/fuel/print-fuel.stderr diff --git a/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.current.stderr b/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.current.stderr index 2097115af009..c5c4f2c4d231 100644 --- a/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.current.stderr +++ b/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.current.stderr @@ -1,16 +1,16 @@ error[E0277]: the trait bound `::Bar<()>: Eq` is not satisfied - --> $DIR/assume-gat-normalization-for-nested-goals.rs:10:30 + --> $DIR/assume-gat-normalization-for-nested-goals.rs:9:30 | LL | type Bar: Baz = i32; | ^^^ the trait `Eq` is not implemented for `::Bar<()>`, which is required by `i32: Baz` | note: required for `i32` to implement `Baz` - --> $DIR/assume-gat-normalization-for-nested-goals.rs:17:23 + --> $DIR/assume-gat-normalization-for-nested-goals.rs:16:23 | LL | impl Baz for i32 where T::Bar<()>: Eq {} | ^^^^^^ ^^^ ------- unsatisfied trait bound introduced here note: required by a bound in `Foo::Bar` - --> $DIR/assume-gat-normalization-for-nested-goals.rs:10:18 + --> $DIR/assume-gat-normalization-for-nested-goals.rs:9:18 | LL | type Bar: Baz = i32; | ^^^^^^^^^ required by this bound in `Foo::Bar` diff --git a/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.next.stderr b/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.next.stderr new file mode 100644 index 000000000000..9b5d84b5b090 --- /dev/null +++ b/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.next.stderr @@ -0,0 +1,24 @@ +error[E0277]: the trait bound `i32: Baz` is not satisfied + --> $DIR/assume-gat-normalization-for-nested-goals.rs:9:30 + | +LL | type Bar: Baz = i32; + | ^^^ the trait `Eq` is not implemented for `::Bar<()>`, which is required by `i32: Baz` + | +note: required for `i32` to implement `Baz` + --> $DIR/assume-gat-normalization-for-nested-goals.rs:16:23 + | +LL | impl Baz for i32 where T::Bar<()>: Eq {} + | ^^^^^^ ^^^ ------- unsatisfied trait bound introduced here +note: required by a bound in `Foo::Bar` + --> $DIR/assume-gat-normalization-for-nested-goals.rs:9:18 + | +LL | type Bar: Baz = i32; + | ^^^^^^^^^ required by this bound in `Foo::Bar` +help: consider further restricting the associated type + | +LL | trait Foo where ::Bar<()>: Eq { + | +++++++++++++++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.rs b/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.rs index 56b50594e523..4050b6fc4258 100644 --- a/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.rs +++ b/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.rs @@ -1,8 +1,7 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver //@ ignore-compare-mode-next-solver (explicit revisions) -//@[current] known-bug: #117606 -//@[next] check-pass +//@ known-bug: #117606 #![feature(associated_type_defaults)] diff --git a/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.rs b/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.rs index 671d17f36f13..285493132b64 100644 --- a/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.rs +++ b/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.rs @@ -9,7 +9,6 @@ impl X for T { //~ ERROR: not all trait items implemented //~^ ERROR missing generics for associated type //~^^ ERROR missing generics for associated type //~| ERROR method `foo` has 1 type parameter but its trait declaration has 0 type parameters - //~| ERROR may not live long enough t } } diff --git a/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.stderr b/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.stderr index 65854ed71587..6a600aee11f2 100644 --- a/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.stderr +++ b/tests/ui/generic-associated-types/gat-trait-path-missing-lifetime.stderr @@ -51,16 +51,7 @@ help: add missing lifetime argument LL | fn foo<'a, T1: X = T1>>(t : T1) -> T1::Y<'a> { | ++++ -error: lifetime may not live long enough - --> $DIR/gat-trait-path-missing-lifetime.rs:8:3 - | -LL | fn foo<'a, T1: X>(t : T1) -> T1::Y<'a> { - | ^^^^^^^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | | - | | lifetime `'a` defined here - | requires that `'a` must outlive `'static` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0046, E0049, E0107. For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/generic-associated-types/issue-102335-gat.rs b/tests/ui/generic-associated-types/issue-102335-gat.rs index 3a4a0c107715..0be6ee3930f1 100644 --- a/tests/ui/generic-associated-types/issue-102335-gat.rs +++ b/tests/ui/generic-associated-types/issue-102335-gat.rs @@ -1,7 +1,7 @@ trait T { type A: S = ()>; - //~^ ERROR associated type bindings are not allowed here - //~| ERROR associated type bindings are not allowed here + //~^ ERROR associated item constraints are not allowed here + //~| ERROR associated item constraints are not allowed here } trait Q {} diff --git a/tests/ui/generic-associated-types/issue-102335-gat.stderr b/tests/ui/generic-associated-types/issue-102335-gat.stderr index 23b114a3a55e..b4772486e6e4 100644 --- a/tests/ui/generic-associated-types/issue-102335-gat.stderr +++ b/tests/ui/generic-associated-types/issue-102335-gat.stderr @@ -1,22 +1,22 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-102335-gat.rs:2:21 | LL | type A: S = ()>; - | ^^^^^^^^ associated type not allowed here + | ^^^^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | LL | type A: S = ()>; | ~~~~~~~~~~ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-102335-gat.rs:2:21 | LL | type A: S = ()>; - | ^^^^^^^^ associated type not allowed here + | ^^^^^^^^ associated item constraint not allowed here | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider removing this type binding +help: consider removing this associated item binding | LL | type A: S = ()>; | ~~~~~~~~~~ diff --git a/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr b/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr index 6bf832bb9e2a..3929e66a25a3 100644 --- a/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr +++ b/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr @@ -12,8 +12,8 @@ LL | type F<'a>: Fn() -> u32; | ^^^^^^^^^^^ required by this bound in `Fun::F` help: consider restricting type parameter `T` | -LL | impl> Fun for T { - | ++++++++++++++++++ +LL | impl Fun for T { + | ++++++ error: aborting due to 1 previous error diff --git a/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr b/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr index d9f26ee6c291..662726b89935 100644 --- a/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr +++ b/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr @@ -12,8 +12,8 @@ LL | type F<'a>: Fn() -> u32; | ^^^^^^^^^^^ required by this bound in `Fun::F` help: consider restricting type parameter `T` | -LL | impl> Fun for T { - | ++++++++++++++++++ +LL | impl Fun for T { + | ++++++ error: aborting due to 1 previous error diff --git a/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr b/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr index 3dc9ff10243f..34278249e35d 100644 --- a/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr +++ b/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr @@ -12,8 +12,8 @@ LL | type F<'a>: Fn() -> u32; | ^^^^^^^^^^^ required by this bound in `Fun::F` help: consider restricting type parameter `T` | -LL | impl> Fun for T { - | ++++++++++++++++++ +LL | impl Fun for T { + | ++++++ error: aborting due to 1 previous error diff --git a/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr b/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr index 45fb65f6cf1e..dafe1c1d3950 100644 --- a/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr +++ b/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr @@ -12,8 +12,8 @@ LL | type F<'a>: Fn() -> u32; | ^^^^^^^^^^^ required by this bound in `Fun::F` help: consider restricting type parameter `T` | -LL | impl> Fun for T { - | ++++++++++++++++++ +LL | impl Fun for T { + | ++++++ error: aborting due to 1 previous error diff --git a/tests/ui/generic-associated-types/issue-70304.rs b/tests/ui/generic-associated-types/issue-70304.rs index 935d3f7a4ba8..8898d4c7d135 100644 --- a/tests/ui/generic-associated-types/issue-70304.rs +++ b/tests/ui/generic-associated-types/issue-70304.rs @@ -52,5 +52,4 @@ fn create_doc() -> impl Document = DocCursorImpl<'_>> { pub fn main() { let doc = create_doc(); let lexer: Lexer<'_, DocCursorImpl<'_>> = Lexer::from(&doc); - //~^ ERROR: `doc` does not live long enough } diff --git a/tests/ui/generic-associated-types/issue-70304.stderr b/tests/ui/generic-associated-types/issue-70304.stderr index 8e012cc6d936..9b02c1b07683 100644 --- a/tests/ui/generic-associated-types/issue-70304.stderr +++ b/tests/ui/generic-associated-types/issue-70304.stderr @@ -27,21 +27,7 @@ LL | type Cursor<'a>: DocCursor<'a>; = note: this bound is currently required to ensure that impls have maximum flexibility = note: we are soliciting feedback, see issue #87479 for more information -error[E0597]: `doc` does not live long enough - --> $DIR/issue-70304.rs:54:59 - | -LL | let doc = create_doc(); - | --- binding `doc` declared here -LL | let lexer: Lexer<'_, DocCursorImpl<'_>> = Lexer::from(&doc); - | ------------^^^^- - | | | - | | borrowed value does not live long enough - | argument requires that `doc` is borrowed for `'static` -LL | -LL | } - | - `doc` dropped here while still borrowed +error: aborting due to 3 previous errors -error: aborting due to 4 previous errors - -Some errors have detailed explanations: E0106, E0597, E0637. +Some errors have detailed explanations: E0106, E0637. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/generic-associated-types/issue-71176.rs b/tests/ui/generic-associated-types/issue-71176.rs index f0e162d825f9..e58b6f6091e7 100644 --- a/tests/ui/generic-associated-types/issue-71176.rs +++ b/tests/ui/generic-associated-types/issue-71176.rs @@ -9,6 +9,9 @@ impl Provider for () { struct Holder { inner: Box>, //~^ ERROR: missing generics for associated type + //~| ERROR: missing generics for associated type + //~| ERROR: missing generics for associated type + //~| ERROR: the trait `Provider` cannot be made into an object } fn main() { diff --git a/tests/ui/generic-associated-types/issue-71176.stderr b/tests/ui/generic-associated-types/issue-71176.stderr index ed837f347533..a1913bb618bc 100644 --- a/tests/ui/generic-associated-types/issue-71176.stderr +++ b/tests/ui/generic-associated-types/issue-71176.stderr @@ -14,6 +14,57 @@ help: add missing lifetime argument LL | inner: Box = B>>, | ++++ -error: aborting due to 1 previous error +error[E0107]: missing generics for associated type `Provider::A` + --> $DIR/issue-71176.rs:10:27 + | +LL | inner: Box>, + | ^ expected 1 lifetime argument + | +note: associated type defined here, with 1 lifetime parameter: `'a` + --> $DIR/issue-71176.rs:2:10 + | +LL | type A<'a>; + | ^ -- + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing lifetime argument + | +LL | inner: Box = B>>, + | ++++ -For more information about this error, try `rustc --explain E0107`. +error[E0107]: missing generics for associated type `Provider::A` + --> $DIR/issue-71176.rs:10:27 + | +LL | inner: Box>, + | ^ expected 1 lifetime argument + | +note: associated type defined here, with 1 lifetime parameter: `'a` + --> $DIR/issue-71176.rs:2:10 + | +LL | type A<'a>; + | ^ -- + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing lifetime argument + | +LL | inner: Box = B>>, + | ++++ + +error[E0038]: the trait `Provider` cannot be made into an object + --> $DIR/issue-71176.rs:10:14 + | +LL | inner: Box>, + | ^^^^^^^^^^^^^^^^^^^ `Provider` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/issue-71176.rs:2:10 + | +LL | trait Provider { + | -------- this trait cannot be made into an object... +LL | type A<'a>; + | ^ ...because it contains the generic associated type `A` + = help: consider moving `A` to another trait + = help: only type `()` implements the trait, consider using it directly instead + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0038, E0107. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-74816.stderr b/tests/ui/generic-associated-types/issue-74816.current.stderr similarity index 89% rename from tests/ui/generic-associated-types/issue-74816.stderr rename to tests/ui/generic-associated-types/issue-74816.current.stderr index 45018e6976cf..335486c6538c 100644 --- a/tests/ui/generic-associated-types/issue-74816.stderr +++ b/tests/ui/generic-associated-types/issue-74816.current.stderr @@ -1,11 +1,11 @@ error[E0277]: the trait bound `Self: Trait1` is not satisfied - --> $DIR/issue-74816.rs:8:31 + --> $DIR/issue-74816.rs:12:31 | LL | type Associated: Trait1 = Self; | ^^^^ the trait `Trait1` is not implemented for `Self` | note: required by a bound in `Trait2::Associated` - --> $DIR/issue-74816.rs:8:22 + --> $DIR/issue-74816.rs:12:22 | LL | type Associated: Trait1 = Self; | ^^^^^^ required by this bound in `Trait2::Associated` @@ -15,13 +15,13 @@ LL | trait Trait2: Trait1 { | ++++++++ error[E0277]: the size for values of type `Self` cannot be known at compilation time - --> $DIR/issue-74816.rs:8:31 + --> $DIR/issue-74816.rs:12:31 | LL | type Associated: Trait1 = Self; | ^^^^ doesn't have a size known at compile-time | note: required by a bound in `Trait2::Associated` - --> $DIR/issue-74816.rs:8:5 + --> $DIR/issue-74816.rs:12:5 | LL | type Associated: Trait1 = Self; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Trait2::Associated` diff --git a/tests/ui/generic-associated-types/issue-74816.next.stderr b/tests/ui/generic-associated-types/issue-74816.next.stderr new file mode 100644 index 000000000000..335486c6538c --- /dev/null +++ b/tests/ui/generic-associated-types/issue-74816.next.stderr @@ -0,0 +1,35 @@ +error[E0277]: the trait bound `Self: Trait1` is not satisfied + --> $DIR/issue-74816.rs:12:31 + | +LL | type Associated: Trait1 = Self; + | ^^^^ the trait `Trait1` is not implemented for `Self` + | +note: required by a bound in `Trait2::Associated` + --> $DIR/issue-74816.rs:12:22 + | +LL | type Associated: Trait1 = Self; + | ^^^^^^ required by this bound in `Trait2::Associated` +help: consider further restricting `Self` + | +LL | trait Trait2: Trait1 { + | ++++++++ + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/issue-74816.rs:12:31 + | +LL | type Associated: Trait1 = Self; + | ^^^^ doesn't have a size known at compile-time + | +note: required by a bound in `Trait2::Associated` + --> $DIR/issue-74816.rs:12:5 + | +LL | type Associated: Trait1 = Self; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Trait2::Associated` +help: consider further restricting `Self` + | +LL | trait Trait2: Sized { + | +++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/generic-associated-types/issue-74816.rs b/tests/ui/generic-associated-types/issue-74816.rs index 344afb87f99c..e2f4ddc7485c 100644 --- a/tests/ui/generic-associated-types/issue-74816.rs +++ b/tests/ui/generic-associated-types/issue-74816.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + #![feature(associated_type_defaults)] trait Trait1 { diff --git a/tests/ui/generic-associated-types/issue-74824.stderr b/tests/ui/generic-associated-types/issue-74824.current.stderr similarity index 90% rename from tests/ui/generic-associated-types/issue-74824.stderr rename to tests/ui/generic-associated-types/issue-74824.current.stderr index 942d9583be1b..b06c7f127ac0 100644 --- a/tests/ui/generic-associated-types/issue-74824.stderr +++ b/tests/ui/generic-associated-types/issue-74824.current.stderr @@ -1,17 +1,17 @@ error[E0277]: the trait bound `Box: Copy` is not satisfied - --> $DIR/issue-74824.rs:6:26 + --> $DIR/issue-74824.rs:10:26 | LL | type Copy: Copy = Box; | ^^^^^^ the trait `Copy` is not implemented for `Box` | note: required by a bound in `UnsafeCopy::Copy` - --> $DIR/issue-74824.rs:6:19 + --> $DIR/issue-74824.rs:10:19 | LL | type Copy: Copy = Box; | ^^^^ required by this bound in `UnsafeCopy::Copy` error[E0277]: the trait bound `T: Clone` is not satisfied - --> $DIR/issue-74824.rs:6:26 + --> $DIR/issue-74824.rs:10:26 | LL | type Copy: Copy = Box; | ^^^^^^ the trait `Clone` is not implemented for `T`, which is required by `::Copy: Copy` @@ -19,7 +19,7 @@ LL | type Copy: Copy = Box; = note: required for `Box` to implement `Clone` = note: required for `::Copy` to implement `Copy` note: required by a bound in `UnsafeCopy::Copy` - --> $DIR/issue-74824.rs:6:19 + --> $DIR/issue-74824.rs:10:19 | LL | type Copy: Copy = Box; | ^^^^ required by this bound in `UnsafeCopy::Copy` diff --git a/tests/ui/generic-associated-types/issue-74824.next.stderr b/tests/ui/generic-associated-types/issue-74824.next.stderr new file mode 100644 index 000000000000..b06c7f127ac0 --- /dev/null +++ b/tests/ui/generic-associated-types/issue-74824.next.stderr @@ -0,0 +1,33 @@ +error[E0277]: the trait bound `Box: Copy` is not satisfied + --> $DIR/issue-74824.rs:10:26 + | +LL | type Copy: Copy = Box; + | ^^^^^^ the trait `Copy` is not implemented for `Box` + | +note: required by a bound in `UnsafeCopy::Copy` + --> $DIR/issue-74824.rs:10:19 + | +LL | type Copy: Copy = Box; + | ^^^^ required by this bound in `UnsafeCopy::Copy` + +error[E0277]: the trait bound `T: Clone` is not satisfied + --> $DIR/issue-74824.rs:10:26 + | +LL | type Copy: Copy = Box; + | ^^^^^^ the trait `Clone` is not implemented for `T`, which is required by `::Copy: Copy` + | + = note: required for `Box` to implement `Clone` + = note: required for `::Copy` to implement `Copy` +note: required by a bound in `UnsafeCopy::Copy` + --> $DIR/issue-74824.rs:10:19 + | +LL | type Copy: Copy = Box; + | ^^^^ required by this bound in `UnsafeCopy::Copy` +help: consider restricting type parameter `T` + | +LL | type Copy: Copy = Box; + | +++++++++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/generic-associated-types/issue-74824.rs b/tests/ui/generic-associated-types/issue-74824.rs index 10c45d133642..7cfb862abed0 100644 --- a/tests/ui/generic-associated-types/issue-74824.rs +++ b/tests/ui/generic-associated-types/issue-74824.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + #![feature(associated_type_defaults)] use std::ops::Deref; diff --git a/tests/ui/generic-associated-types/issue-80433.rs b/tests/ui/generic-associated-types/issue-80433.rs index 530575424402..bdba78c2ccd2 100644 --- a/tests/ui/generic-associated-types/issue-80433.rs +++ b/tests/ui/generic-associated-types/issue-80433.rs @@ -22,8 +22,7 @@ fn test_simpler<'a>(dst: &'a mut impl TestMut) //~^ ERROR missing generics for associated type { for n in 0i16..100 { - *dst.test_mut() = n.into(); //~ ERROR: cannot borrow - //~^ ERROR: borrowed data escapes outside of function + *dst.test_mut() = n.into(); } } diff --git a/tests/ui/generic-associated-types/issue-80433.stderr b/tests/ui/generic-associated-types/issue-80433.stderr index a9a14d3f51ce..8ab6fdcb8157 100644 --- a/tests/ui/generic-associated-types/issue-80433.stderr +++ b/tests/ui/generic-associated-types/issue-80433.stderr @@ -25,30 +25,6 @@ help: add missing lifetime argument LL | fn test_simpler<'a>(dst: &'a mut impl TestMut = &'a mut f32>) | ++++ -error[E0499]: cannot borrow `*dst` as mutable more than once at a time - --> $DIR/issue-80433.rs:25:10 - | -LL | *dst.test_mut() = n.into(); - | ^^^----------- - | | - | `*dst` was mutably borrowed here in the previous iteration of the loop - | argument requires that `*dst` is borrowed for `'static` +error: aborting due to 2 previous errors -error[E0521]: borrowed data escapes outside of function - --> $DIR/issue-80433.rs:25:10 - | -LL | fn test_simpler<'a>(dst: &'a mut impl TestMut) - | -- --- `dst` is a reference that is only valid in the function body - | | - | lifetime `'a` defined here -... -LL | *dst.test_mut() = n.into(); - | ^^^^^^^^^^^^^^ - | | - | `dst` escapes the function body here - | argument requires that `'a` must outlive `'static` - -error: aborting due to 4 previous errors - -Some errors have detailed explanations: E0107, E0499, E0521. -For more information about an error, try `rustc --explain E0107`. +For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/generic-associated-types/static-lifetime-tip-with-default-type.rs b/tests/ui/generic-associated-types/static-lifetime-tip-with-default-type.rs new file mode 100644 index 000000000000..68879a0af292 --- /dev/null +++ b/tests/ui/generic-associated-types/static-lifetime-tip-with-default-type.rs @@ -0,0 +1,26 @@ +//@ error-pattern: r#T: 'static +//@ error-pattern: r#K: 'static +//@ error-pattern: T: 'static= + +// https://github.com/rust-lang/rust/issues/124785 + +struct Foo(&'static T, &'static K); +//~^ ERROR: the parameter type `T` may not live long enough +//~| ERROR: the parameter type `K` may not live long enough + +struct Bar(&'static r#T, &'static r#K); +//~^ ERROR: the parameter type `T` may not live long enough +//~| ERROR: the parameter type `K` may not live long enough + +struct Boo(&'static T); +//~^ ERROR: the parameter type `T` may not live long enough + +struct Far(&'static T); +//~^ ERROR: the parameter type `T` may not live long enough + +struct S<'a, K: 'a = i32>(&'static K); +//~^ ERROR: lifetime parameter `'a` is never used +//~| ERROR: the parameter type `K` may not live long enough + +fn main() {} diff --git a/tests/ui/generic-associated-types/static-lifetime-tip-with-default-type.stderr b/tests/ui/generic-associated-types/static-lifetime-tip-with-default-type.stderr new file mode 100644 index 000000000000..7d985a9013f1 --- /dev/null +++ b/tests/ui/generic-associated-types/static-lifetime-tip-with-default-type.stderr @@ -0,0 +1,110 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/static-lifetime-tip-with-default-type.rs:7:24 + | +LL | struct Foo(&'static T, &'static K); + | ^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the reference type `&'static T` does not outlive the data it points at + | +help: consider adding an explicit lifetime bound + | +LL | struct Foo(&'static T, &'static K); + | +++++++++ + +error[E0310]: the parameter type `K` may not live long enough + --> $DIR/static-lifetime-tip-with-default-type.rs:7:36 + | +LL | struct Foo(&'static T, &'static K); + | ^^^^^^^^^^ + | | + | the parameter type `K` must be valid for the static lifetime... + | ...so that the reference type `&'static K` does not outlive the data it points at + | +help: consider adding an explicit lifetime bound + | +LL | struct Foo(&'static T, &'static K); + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/static-lifetime-tip-with-default-type.rs:11:28 + | +LL | struct Bar(&'static r#T, &'static r#K); + | ^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the reference type `&'static T` does not outlive the data it points at + | +help: consider adding an explicit lifetime bound + | +LL | struct Bar(&'static r#T, &'static r#K); + | +++++++++ + +error[E0310]: the parameter type `K` may not live long enough + --> $DIR/static-lifetime-tip-with-default-type.rs:11:42 + | +LL | struct Bar(&'static r#T, &'static r#K); + | ^^^^^^^^^^^^ + | | + | the parameter type `K` must be valid for the static lifetime... + | ...so that the reference type `&'static K` does not outlive the data it points at + | +help: consider adding an explicit lifetime bound + | +LL | struct Bar(&'static r#T, &'static r#K); + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/static-lifetime-tip-with-default-type.rs:15:20 + | +LL | struct Boo(&'static T); + | ^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the reference type `&'static T` does not outlive the data it points at + | +help: consider adding an explicit lifetime bound + | +LL | struct Boo(&'static T); + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/static-lifetime-tip-with-default-type.rs:19:8 + | +LL | = i32>(&'static T); + | ^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the reference type `&'static T` does not outlive the data it points at + | +help: consider adding an explicit lifetime bound + | +LL | struct Far $DIR/static-lifetime-tip-with-default-type.rs:22:27 + | +LL | struct S<'a, K: 'a = i32>(&'static K); + | ^^^^^^^^^^ + | | + | the parameter type `K` must be valid for the static lifetime... + | ...so that the reference type `&'static K` does not outlive the data it points at + | +help: consider adding an explicit lifetime bound + | +LL | struct S<'a, K: 'a + 'static = i32>(&'static K); + | +++++++++ + +error[E0392]: lifetime parameter `'a` is never used + --> $DIR/static-lifetime-tip-with-default-type.rs:22:10 + | +LL | struct S<'a, K: 'a = i32>(&'static K); + | ^^ unused lifetime parameter + | + = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0310, E0392. +For more information about an error, try `rustc --explain E0310`. diff --git a/tests/ui/generic-const-items/assoc-const-missing-type.rs b/tests/ui/generic-const-items/assoc-const-missing-type.rs new file mode 100644 index 000000000000..93160f0b5758 --- /dev/null +++ b/tests/ui/generic-const-items/assoc-const-missing-type.rs @@ -0,0 +1,21 @@ +// Ensure that we properly deal with missing/placeholder types inside GACs. +// issue: rust-lang/rust#124833 +#![feature(generic_const_items)] +#![allow(incomplete_features)] + +trait Trait { + const K: T; + const Q<'a>: &'a str; +} + +impl Trait for () { + const K = (); + //~^ ERROR missing type for `const` item + //~| ERROR mismatched types + //~| ERROR mismatched types + const Q = ""; + //~^ ERROR missing type for `const` item + //~| ERROR lifetime parameters or bounds on const `Q` do not match the trait declaration +} + +fn main() {} diff --git a/tests/ui/generic-const-items/assoc-const-missing-type.stderr b/tests/ui/generic-const-items/assoc-const-missing-type.stderr new file mode 100644 index 000000000000..6f35c0958d45 --- /dev/null +++ b/tests/ui/generic-const-items/assoc-const-missing-type.stderr @@ -0,0 +1,48 @@ +error[E0308]: mismatched types + --> $DIR/assoc-const-missing-type.rs:12:18 + | +LL | const K = (); + | - ^^ expected type parameter `T`, found `()` + | | + | expected this type parameter + | + = note: expected type parameter `T` + found unit type `()` + +error: missing type for `const` item + --> $DIR/assoc-const-missing-type.rs:12:15 + | +LL | const K = (); + | ^ help: provide a type for the associated constant: `()` + +error[E0195]: lifetime parameters or bounds on const `Q` do not match the trait declaration + --> $DIR/assoc-const-missing-type.rs:16:12 + | +LL | const Q<'a>: &'a str; + | ---- lifetimes in impl do not match this const in trait +... +LL | const Q = ""; + | ^ lifetimes do not match const in trait + +error: missing type for `const` item + --> $DIR/assoc-const-missing-type.rs:16:12 + | +LL | const Q = ""; + | ^ help: provide a type for the associated constant: `: &str` + +error[E0308]: mismatched types + --> $DIR/assoc-const-missing-type.rs:12:18 + | +LL | const K = (); + | - ^^ expected type parameter `T`, found `()` + | | + | expected this type parameter + | + = note: expected type parameter `T` + found unit type `()` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0195, E0308. +For more information about an error, try `rustc --explain E0195`. diff --git a/tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.rs b/tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.rs new file mode 100644 index 000000000000..b2ac332b4f08 --- /dev/null +++ b/tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.rs @@ -0,0 +1,28 @@ +#![allow(dead_code)] + +trait Trait1 + where T: for<'a> Trait1 + 'b { } //~ ERROR use of undeclared lifetime name `'b` + +trait Trait2 +where + T: B<'b> + for<'a> A<'a>, //~ ERROR use of undeclared lifetime name `'b` +{ +} + +trait Trait3 +where + T: B<'b> + for<'a> A<'a> + 'c {} + //~^ ERROR use of undeclared lifetime name `'b` + //~| ERROR use of undeclared lifetime name `'c` + +trait Trait4 +where + T: for<'a> A<'a> + 'x + for<'b> B<'b>, //~ ERROR use of undeclared lifetime name `'x` +{ +} + +trait A<'a> {} +trait B<'a> {} + + +fn main() {} diff --git a/tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.stderr b/tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.stderr new file mode 100644 index 000000000000..40f0769964f0 --- /dev/null +++ b/tests/ui/generics/generic-higher-ranked-lifetime-issue-122714.stderr @@ -0,0 +1,92 @@ +error[E0261]: use of undeclared lifetime name `'b` + --> $DIR/generic-higher-ranked-lifetime-issue-122714.rs:4:32 + | +LL | where T: for<'a> Trait1 + 'b { } + | ^^ undeclared lifetime + | + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'b` lifetime + | +LL - where T: for<'a> Trait1 + 'b { } +LL + where for<'b, 'a> T: Trait1 + 'b { } + | +help: consider introducing lifetime `'b` here + | +LL | trait Trait1<'b, T> + | +++ + +error[E0261]: use of undeclared lifetime name `'b` + --> $DIR/generic-higher-ranked-lifetime-issue-122714.rs:8:10 + | +LL | T: B<'b> + for<'a> A<'a>, + | ^^ undeclared lifetime + | +help: consider making the bound lifetime-generic with a new `'b` lifetime + | +LL | T: for<'b> B<'b> + for<'a> A<'a>, + | +++++++ +help: consider making the bound lifetime-generic with a new `'b` lifetime + | +LL - T: B<'b> + for<'a> A<'a>, +LL + for<'b, 'a> T: B<'b> + A<'a>, + | +help: consider introducing lifetime `'b` here + | +LL | trait Trait2<'b, T> + | +++ + +error[E0261]: use of undeclared lifetime name `'b` + --> $DIR/generic-higher-ranked-lifetime-issue-122714.rs:14:10 + | +LL | T: B<'b> + for<'a> A<'a> + 'c {} + | ^^ undeclared lifetime + | +help: consider making the bound lifetime-generic with a new `'b` lifetime + | +LL | T: for<'b> B<'b> + for<'a> A<'a> + 'c {} + | +++++++ +help: consider making the bound lifetime-generic with a new `'b` lifetime + | +LL - T: B<'b> + for<'a> A<'a> + 'c {} +LL + for<'b, 'a> T: B<'b> + A<'a> + 'c {} + | +help: consider introducing lifetime `'b` here + | +LL | trait Trait3<'b, T> + | +++ + +error[E0261]: use of undeclared lifetime name `'c` + --> $DIR/generic-higher-ranked-lifetime-issue-122714.rs:14:32 + | +LL | T: B<'b> + for<'a> A<'a> + 'c {} + | ^^ undeclared lifetime + | +help: consider making the bound lifetime-generic with a new `'c` lifetime + | +LL - T: B<'b> + for<'a> A<'a> + 'c {} +LL + for<'c, 'a> T: B<'b> + A<'a> + 'c {} + | +help: consider introducing lifetime `'c` here + | +LL | trait Trait3<'c, T> + | +++ + +error[E0261]: use of undeclared lifetime name `'x` + --> $DIR/generic-higher-ranked-lifetime-issue-122714.rs:20:24 + | +LL | T: for<'a> A<'a> + 'x + for<'b> B<'b>, + | ^^ undeclared lifetime + | +help: consider making the bound lifetime-generic with a new `'x` lifetime + | +LL - T: for<'a> A<'a> + 'x + for<'b> B<'b>, +LL + for<'x, 'a, 'b> T: A<'a> + 'x + B<'b>, + | +help: consider introducing lifetime `'x` here + | +LL | trait Trait4<'x, T> + | +++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0261`. diff --git a/tests/ui/generics/impl-block-params-declared-in-wrong-spot-issue-113073.rs b/tests/ui/generics/impl-block-params-declared-in-wrong-spot-issue-113073.rs new file mode 100644 index 000000000000..3a5b60e5763b --- /dev/null +++ b/tests/ui/generics/impl-block-params-declared-in-wrong-spot-issue-113073.rs @@ -0,0 +1,33 @@ +trait Foo {} + +impl Foo for String {} +//~^ ERROR associated item constraints are not allowed here +//~| HELP declare the type parameter right after the `impl` keyword + +impl Foo for u8 {} +//~^ ERROR associated item constraints are not allowed here +//~| HELP declare the type parameter right after the `impl` keyword +//~| ERROR use of undeclared lifetime name `'a` +//~| HELP consider introducing lifetime `'a` here + +impl Foo for u16 {} +//~^ ERROR associated item constraints are not allowed here +//~| HELP declare the type parameter right after the `impl` keyword + +impl<'a> Foo for u32 {} +//~^ ERROR associated item constraints are not allowed here +//~| HELP declare the type parameter right after the `impl` keyword + +trait Bar {} + +impl Bar for String {} +//~^ ERROR associated item constraints are not allowed here +//~| HELP declare the type parameter right after the `impl` keyword + +impl Bar for u8 {} +//~^ ERROR trait takes 2 generic arguments but 1 generic argument was supplied +//~| HELP add missing generic argument +//~| ERROR associated item constraints are not allowed here +//~| HELP declare the type parameter right after the `impl` keyword + +fn main() {} diff --git a/tests/ui/generics/impl-block-params-declared-in-wrong-spot-issue-113073.stderr b/tests/ui/generics/impl-block-params-declared-in-wrong-spot-issue-113073.stderr new file mode 100644 index 000000000000..dfc6761e17e7 --- /dev/null +++ b/tests/ui/generics/impl-block-params-declared-in-wrong-spot-issue-113073.stderr @@ -0,0 +1,96 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/impl-block-params-declared-in-wrong-spot-issue-113073.rs:7:13 + | +LL | impl Foo for u8 {} + | - ^^ undeclared lifetime + | | + | help: consider introducing lifetime `'a` here: `<'a>` + +error[E0229]: associated item constraints are not allowed here + --> $DIR/impl-block-params-declared-in-wrong-spot-issue-113073.rs:3:10 + | +LL | impl Foo for String {} + | ^^^^^^^^^^ associated item constraint not allowed here + | +help: declare the type parameter right after the `impl` keyword + | +LL | impl Foo for String {} + | ++++++++++++ ~ + +error[E0229]: associated item constraints are not allowed here + --> $DIR/impl-block-params-declared-in-wrong-spot-issue-113073.rs:7:10 + | +LL | impl Foo for u8 {} + | ^^^^^^^^^^^^^^^ associated item constraint not allowed here + | +help: declare the type parameter right after the `impl` keyword + | +LL | impl<'a, T: 'a + Default> Foo for u8 {} + | +++++++++++++++++++++ ~ + +error[E0229]: associated item constraints are not allowed here + --> $DIR/impl-block-params-declared-in-wrong-spot-issue-113073.rs:13:13 + | +LL | impl Foo for u16 {} + | ^^^^^^^^^^ associated item constraint not allowed here + | +help: declare the type parameter right after the `impl` keyword + | +LL | impl Foo for u16 {} + | ++++++++++++ ~ + +error[E0229]: associated item constraints are not allowed here + --> $DIR/impl-block-params-declared-in-wrong-spot-issue-113073.rs:17:14 + | +LL | impl<'a> Foo for u32 {} + | ^^^^^^^^^^^^^^^ associated item constraint not allowed here + | +help: declare the type parameter right after the `impl` keyword + | +LL | impl<'a, 'a, T: 'a + Default> Foo for u32 {} + | +++++++++++++++++++++ ~ + +error[E0229]: associated item constraints are not allowed here + --> $DIR/impl-block-params-declared-in-wrong-spot-issue-113073.rs:23:10 + | +LL | impl Bar for String {} + | ^^^^^^^^^^ associated item constraint not allowed here + | +help: declare the type parameter right after the `impl` keyword + | +LL | impl Bar for String {} + | ++++++++++++ ~ + +error[E0107]: trait takes 2 generic arguments but 1 generic argument was supplied + --> $DIR/impl-block-params-declared-in-wrong-spot-issue-113073.rs:27:9 + | +LL | impl Bar for u8 {} + | ^^^ - supplied 1 generic argument + | | + | expected 2 generic arguments + | +note: trait defined here, with 2 generic parameters: `T`, `K` + --> $DIR/impl-block-params-declared-in-wrong-spot-issue-113073.rs:21:7 + | +LL | trait Bar {} + | ^^^ - - +help: add missing generic argument + | +LL | impl Bar for u8 {} + | +++ + +error[E0229]: associated item constraints are not allowed here + --> $DIR/impl-block-params-declared-in-wrong-spot-issue-113073.rs:27:16 + | +LL | impl Bar for u8 {} + | ^^^^^^^^^^ associated item constraint not allowed here + | +help: declare the type parameter right after the `impl` keyword + | +LL | impl Bar for u8 {} + | ++++++++++++ ~ + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0107, E0229, E0261. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs index 2f53bd019b78..e0d2e44e6e7d 100644 --- a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs @@ -49,7 +49,7 @@ fn function3>() { // Trying to normalize the type `for<'a> fn(>::Assoc)` // only gets to `>::Assoc` once `'a` has been already // instantiated, causing us to prefer the where-bound over the impl - // resulting in a placeholder error. Even if were were to also use the + // resulting in a placeholder error. Even if we were to also use the // leak check during candidate selection for normalization, this // case would still not compile. let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs new file mode 100644 index 000000000000..beda719ac208 --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs @@ -0,0 +1,28 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// The new trait solver does not return region constraints if the goal +// is still ambiguous. This causes the following test to fail with ambiguity, +// even though `(): LeakCheckFailure<'!a, V>` would return `'!a: 'static` +// which would have caused a leak check failure. + +trait Ambig {} +impl Ambig for u32 {} +impl Ambig for u16 {} + +trait Id {} +impl Id for u32 {} +impl Id for u16 {} + + +trait LeakCheckFailure<'a, V: ?Sized> {} +impl LeakCheckFailure<'static, V> for () {} + +trait Trait {} +impl Trait for () where for<'a> (): LeakCheckFailure<'a, V> {} +impl Trait for () {} +fn impls_trait, U: Id, V>() {} +fn main() { + impls_trait::<(), _, _>() +} diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.next.stderr b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.next.stderr new file mode 100644 index 000000000000..a12e3312230a --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.next.stderr @@ -0,0 +1,22 @@ +error[E0283]: type annotations needed + --> $DIR/leak-check-in-selection-6-ambig-unify.rs:30:5 + | +LL | impls_trait::<(), _, _>() + | ^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `impls_trait` + | +note: multiple `impl`s satisfying `(): Trait<_, _>` found + --> $DIR/leak-check-in-selection-6-ambig-unify.rs:26:1 + | +LL | impl Trait for () where for<'b> (): LeakCheckFailure<'static, 'b, V> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Trait for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `impls_trait` + --> $DIR/leak-check-in-selection-6-ambig-unify.rs:28:19 + | +LL | fn impls_trait, U: Id, V>() {} + | ^^^^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.old.stderr b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.old.stderr new file mode 100644 index 000000000000..a12e3312230a --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.old.stderr @@ -0,0 +1,22 @@ +error[E0283]: type annotations needed + --> $DIR/leak-check-in-selection-6-ambig-unify.rs:30:5 + | +LL | impls_trait::<(), _, _>() + | ^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `impls_trait` + | +note: multiple `impl`s satisfying `(): Trait<_, _>` found + --> $DIR/leak-check-in-selection-6-ambig-unify.rs:26:1 + | +LL | impl Trait for () where for<'b> (): LeakCheckFailure<'static, 'b, V> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Trait for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `impls_trait` + --> $DIR/leak-check-in-selection-6-ambig-unify.rs:28:19 + | +LL | fn impls_trait, U: Id, V>() {} + | ^^^^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs new file mode 100644 index 000000000000..592d581695e5 --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs @@ -0,0 +1,32 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver + +// The new trait solver does not return region constraints if the goal +// is still ambiguous. This should cause the following test to fail with +// ambiguity as even if `(): LeakCheckFailure<'static, '!b, V>` unifies +// `'!b` with `'static`, we erase all region constraints. +// +// However, we do still unify the var_value for `'b` with `'static`, +// causing us to return this requirement via the `var_values` even if +// we don't return any region constraints. This is a bit inconsistent +// but isn't something we should really worry about imo. +trait Ambig {} +impl Ambig for u32 {} +impl Ambig for u16 {} + +trait Id {} +impl Id for u32 {} +impl Id for u16 {} + + +trait LeakCheckFailure<'a, 'b, V: ?Sized> {} +impl<'a, 'b: 'a, V: ?Sized + Ambig> LeakCheckFailure<'a, 'b, V> for () {} + +trait Trait {} +impl Trait for () where for<'b> (): LeakCheckFailure<'static, 'b, V> {} +impl Trait for () {} +fn impls_trait, U: Id, V>() {} +fn main() { + impls_trait::<(), _, _>() + //~^ ERROR type annotations needed +} diff --git a/tests/ui/higher-ranked/structually-relate-aliases.stderr b/tests/ui/higher-ranked/structually-relate-aliases.stderr index 59fab52b221e..2f1dfd19c483 100644 --- a/tests/ui/higher-ranked/structually-relate-aliases.stderr +++ b/tests/ui/higher-ranked/structually-relate-aliases.stderr @@ -1,5 +1,5 @@ -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, !2_0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, !2_0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } error[E0277]: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied --> $DIR/structually-relate-aliases.rs:13:36 | diff --git a/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.next.stderr b/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.next.stderr new file mode 100644 index 000000000000..31d74d1c022a --- /dev/null +++ b/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.next.stderr @@ -0,0 +1,9 @@ +error[E0284]: type annotations needed: cannot satisfy `for<'a> <_ as Trait<'a>>::Assoc normalizes-to >::Assoc` + --> $DIR/rigid-equate-projections-in-higher-ranked-fn-signature.rs:27:50 + | +LL | let _: for<'a> fn(<_ as Trait<'a>>::Assoc) = foo::(); + | ^^^^^^^^^^ cannot satisfy `for<'a> <_ as Trait<'a>>::Assoc normalizes-to >::Assoc` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.rs b/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.rs new file mode 100644 index 000000000000..10dbc2a4a825 --- /dev/null +++ b/tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.rs @@ -0,0 +1,30 @@ +//@ revisions: current next +//@[current] check-pass +//@[next] compile-flags: -Znext-solver +//@[next] check-fail +//@ ignore-compare-mode-next-solver (explicit revisions) + +/// This triggers an ICE with (and without) `--emit metadata` using the old +/// trait solver: +/// ``` +/// rustc +nightly-2023-01-09 \ +/// tests/ui/higher-ranked/trait-bounds/rigid-equate-projections-in-higher-ranked-fn-signature.rs +/// ``` +/// The ICE was unknowingly fixed by +/// in `nightly-2023-01-10`. +/// This is a regression test for that fixed ICE. For the next solver we simply +/// make sure there is a compiler error. + +trait Trait<'a> { + type Assoc; +} + +fn foo Trait<'a>>() -> for<'a> fn(>::Assoc) { + todo!() +} + +fn bar Trait<'a>>() { + let _: for<'a> fn(<_ as Trait<'a>>::Assoc) = foo::(); //[next]~ ERROR type annotations needed +} + +fn main() {} diff --git a/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr b/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr index 78e6376bca2e..cc229764ad3f 100644 --- a/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr +++ b/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr @@ -24,8 +24,7 @@ LL | fn f() { my_core::mem::drop(0); } LL | a!(); | ---- in this macro invocation | - = help: consider importing one of these items: - core::mem + = help: consider importing this module: std::mem = note: this error originates in the macro `a` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -35,9 +34,7 @@ error[E0433]: failed to resolve: use of undeclared crate or module `my_core` LL | fn f() { my_core::mem::drop(0); } | ^^^^^^^ use of undeclared crate or module `my_core` | -help: consider importing one of these items - | -LL + use core::mem; +help: consider importing this module | LL + use std::mem; | diff --git a/tests/ui/impl-header-lifetime-elision/assoc-type.rs b/tests/ui/impl-header-lifetime-elision/assoc-type.rs index b0089a37aa05..db3c416540fc 100644 --- a/tests/ui/impl-header-lifetime-elision/assoc-type.rs +++ b/tests/ui/impl-header-lifetime-elision/assoc-type.rs @@ -9,7 +9,7 @@ trait MyTrait { impl MyTrait for &i32 { type Output = &i32; - //~^ ERROR `&` without an explicit lifetime name cannot be used here + //~^ ERROR 11:19: 11:20: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type } impl MyTrait for &u32 { diff --git a/tests/ui/impl-header-lifetime-elision/assoc-type.stderr b/tests/ui/impl-header-lifetime-elision/assoc-type.stderr index c4f27e0b80e4..e650eeca48ad 100644 --- a/tests/ui/impl-header-lifetime-elision/assoc-type.stderr +++ b/tests/ui/impl-header-lifetime-elision/assoc-type.stderr @@ -1,8 +1,8 @@ -error[E0637]: `&` without an explicit lifetime name cannot be used here +error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type --> $DIR/assoc-type.rs:11:19 | LL | type Output = &i32; - | ^ explicit lifetime name needed here + | ^ this lifetime must come from the implemented type error[E0637]: `'_` cannot be used here --> $DIR/assoc-type.rs:16:20 diff --git a/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.rs b/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.rs new file mode 100644 index 000000000000..65e90c1ca53c --- /dev/null +++ b/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.rs @@ -0,0 +1,38 @@ +// This test should never pass! + +#![feature(type_alias_impl_trait)] + +pub trait Captures<'a> {} +impl Captures<'_> for T {} + +pub struct MyTy<'a, 'b>(Option<*mut &'a &'b ()>); +unsafe impl Send for MyTy<'_, 'static> {} + +pub mod step1 { + use super::*; + pub type Step1<'a, 'b: 'a> = impl Sized + Captures<'b> + 'a; + pub fn step1<'a, 'b: 'a>() -> Step1<'a, 'b> { + MyTy::<'a, 'b>(None) + } +} + +pub mod step2 { + pub type Step2<'a> = impl Send + 'a; + + // Although `Step2` is WF at the definition site, it's not WF in its + // declaration site (above). We check this in `check_opaque_meets_bounds`, + // which must remain sound. + pub fn step2<'a, 'b: 'a>() -> Step2<'a> + where crate::step1::Step1<'a, 'b>: Send + { + crate::step1::step1::<'a, 'b>() + //~^ ERROR hidden type for `Step2<'a>` captures lifetime that does not appear in bounds + } +} + +fn step3<'a, 'b>() { + fn is_send() {} + is_send::>(); +} + +fn main() {} diff --git a/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.stderr b/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.stderr new file mode 100644 index 000000000000..58d7f9959d34 --- /dev/null +++ b/tests/ui/impl-trait/alias-liveness/tait-hidden-erased-unsoundness-2.stderr @@ -0,0 +1,15 @@ +error[E0700]: hidden type for `Step2<'a>` captures lifetime that does not appear in bounds + --> $DIR/tait-hidden-erased-unsoundness-2.rs:28:9 + | +LL | pub type Step2<'a> = impl Send + 'a; + | -------------- opaque type defined here +... +LL | pub fn step2<'a, 'b: 'a>() -> Step2<'a> + | -- hidden type `Step1<'a, 'b>` captures the lifetime `'b` as defined here +... +LL | crate::step1::step1::<'a, 'b>() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/impl-trait/associated-type-undefine.rs b/tests/ui/impl-trait/associated-type-undefine.rs new file mode 100644 index 000000000000..c8f07021fbf1 --- /dev/null +++ b/tests/ui/impl-trait/associated-type-undefine.rs @@ -0,0 +1,28 @@ +#![feature(impl_trait_in_assoc_type)] + +trait Foo: Sized { + type Bar; + type Gat; + fn foo(self) -> (::Gat, ::Gat); +} + +impl Foo for u32 { + type Bar = (); + type Gat = (); + fn foo(self) -> (::Gat, ::Gat) { + ((), ()) + } +} + +impl Foo for () { + type Bar = impl Sized; + type Gat = ::Bar; + // Because we encounter `Gat` first, we never walk into another `Gat` + // again, thus missing the opaque type that we could be defining. + fn foo(self) -> (::Gat, ::Gat) { + ((), ()) + //~^ ERROR: mismatched types + } +} + +fn main() {} diff --git a/tests/ui/impl-trait/associated-type-undefine.stderr b/tests/ui/impl-trait/associated-type-undefine.stderr new file mode 100644 index 000000000000..5d9d525eb93b --- /dev/null +++ b/tests/ui/impl-trait/associated-type-undefine.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/associated-type-undefine.rs:23:14 + | +LL | type Bar = impl Sized; + | ---------- the expected opaque type +... +LL | ((), ()) + | ^^ expected opaque type, found `()` + | + = note: expected opaque type `<() as Foo>::Bar` + found unit type `()` +note: this item must have the opaque type in its signature in order to be able to register hidden types + --> $DIR/associated-type-undefine.rs:22:8 + | +LL | fn foo(self) -> (::Gat, ::Gat) { + | ^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/impl-trait/call_method_ambiguous.next.stderr b/tests/ui/impl-trait/call_method_ambiguous.next.stderr new file mode 100644 index 000000000000..a1f9a8b40a89 --- /dev/null +++ b/tests/ui/impl-trait/call_method_ambiguous.next.stderr @@ -0,0 +1,17 @@ +error[E0282]: type annotations needed + --> $DIR/call_method_ambiguous.rs:28:13 + | +LL | let mut iter = foo(n - 1, m); + | ^^^^^^^^ +LL | +LL | assert_eq!(iter.get(), 1); + | ---- type must be known at this point + | +help: consider giving `iter` an explicit type + | +LL | let mut iter: /* Type */ = foo(n - 1, m); + | ++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/call_method_ambiguous.rs b/tests/ui/impl-trait/call_method_ambiguous.rs new file mode 100644 index 000000000000..4dac605d6b81 --- /dev/null +++ b/tests/ui/impl-trait/call_method_ambiguous.rs @@ -0,0 +1,38 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@[current] run-pass + +#![feature(precise_capturing)] + +trait Get { + fn get(&mut self) -> u32; +} + +impl Get for () { + fn get(&mut self) -> u32 { + 0 + } +} + +impl Get for &mut T +where + T: Get, +{ + fn get(&mut self) -> u32 { + T::get(self) + 1 + } +} + +fn foo(n: usize, m: &mut ()) -> impl Get + use<'_> { + if n > 0 { + let mut iter = foo(n - 1, m); + //[next]~^ type annotations needed + assert_eq!(iter.get(), 1); + } + m +} + +fn main() { + let g = foo(1, &mut ()).get(); + assert_eq!(g, 1); +} diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl.next.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl.next.stderr new file mode 100644 index 000000000000..271051f120ab --- /dev/null +++ b/tests/ui/impl-trait/call_method_on_inherent_impl.next.stderr @@ -0,0 +1,17 @@ +error[E0282]: type annotations needed + --> $DIR/call_method_on_inherent_impl.rs:18:13 + | +LL | let x = my_foo(); + | ^ +LL | +LL | x.my_debug(); + | - type must be known at this point + | +help: consider giving `x` an explicit type + | +LL | let x: /* Type */ = my_foo(); + | ++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl.rs b/tests/ui/impl-trait/call_method_on_inherent_impl.rs new file mode 100644 index 000000000000..17f7cad660db --- /dev/null +++ b/tests/ui/impl-trait/call_method_on_inherent_impl.rs @@ -0,0 +1,25 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@[current] check-pass + +trait MyDebug { + fn my_debug(&self); +} + +impl MyDebug for T +where + T: std::fmt::Debug, +{ + fn my_debug(&self) {} +} + +fn my_foo() -> impl std::fmt::Debug { + if false { + let x = my_foo(); + //[next]~^ type annotations needed + x.my_debug(); + } + () +} + +fn main() {} diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.current.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.current.stderr new file mode 100644 index 000000000000..6ecb2b05fc56 --- /dev/null +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.current.stderr @@ -0,0 +1,16 @@ +error[E0599]: no method named `my_debug` found for reference `&impl Debug` in the current scope + --> $DIR/call_method_on_inherent_impl_on_rigid_type.rs:16:11 + | +LL | x.my_debug(); + | ^^^^^^^^ method not found in `&impl Debug` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `MyDebug` defines an item `my_debug`, perhaps you need to implement it + --> $DIR/call_method_on_inherent_impl_on_rigid_type.rs:4:1 + | +LL | trait MyDebug { + | ^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.next.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.next.stderr new file mode 100644 index 000000000000..5fb0b8f1d14b --- /dev/null +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.next.stderr @@ -0,0 +1,17 @@ +error[E0282]: type annotations needed for `&_` + --> $DIR/call_method_on_inherent_impl_on_rigid_type.rs:14:13 + | +LL | let x = &my_foo(); + | ^ +LL | +LL | x.my_debug(); + | -------- type must be known at this point + | +help: consider giving `x` an explicit type, where the placeholders `_` are specified + | +LL | let x: &_ = &my_foo(); + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.rs b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.rs new file mode 100644 index 000000000000..7fb2ff3b2bcc --- /dev/null +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_on_rigid_type.rs @@ -0,0 +1,22 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +trait MyDebug { + fn my_debug(&self); +} + +impl MyDebug for &() { + fn my_debug(&self) {} +} + +fn my_foo() -> impl std::fmt::Debug { + if false { + let x = &my_foo(); + //[next]~^ ERROR: type annotations needed + x.my_debug(); + //[current]~^ ERROR: no method named `my_debug` + } + () +} + +fn main() {} diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_ref.current.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_ref.current.stderr new file mode 100644 index 000000000000..fe6e166cb4fa --- /dev/null +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_ref.current.stderr @@ -0,0 +1,40 @@ +error[E0599]: no method named `my_debug` found for opaque type `impl Debug` in the current scope + --> $DIR/call_method_on_inherent_impl_ref.rs:20:11 + | +LL | fn my_debug(&self); + | -------- the method is available for `&impl Debug` here +... +LL | x.my_debug(); + | ^^^^^^^^ method not found in `impl Debug` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `MyDebug` defines an item `my_debug`, perhaps you need to implement it + --> $DIR/call_method_on_inherent_impl_ref.rs:4:1 + | +LL | trait MyDebug { + | ^^^^^^^^^^^^^ + +error[E0391]: cycle detected when computing type of opaque `my_foo::{opaque#0}` + --> $DIR/call_method_on_inherent_impl_ref.rs:15:16 + | +LL | fn my_foo() -> impl std::fmt::Debug { + | ^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires type-checking `my_foo`... + --> $DIR/call_method_on_inherent_impl_ref.rs:20:9 + | +LL | x.my_debug(); + | ^ + = note: ...which requires evaluating trait selection obligation `my_foo::{opaque#0}: core::marker::Unpin`... + = note: ...which again requires computing type of opaque `my_foo::{opaque#0}`, completing the cycle +note: cycle used when computing type of `my_foo::{opaque#0}` + --> $DIR/call_method_on_inherent_impl_ref.rs:15:16 + | +LL | fn my_foo() -> impl std::fmt::Debug { + | ^^^^^^^^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0391, E0599. +For more information about an error, try `rustc --explain E0391`. diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_ref.next.stderr b/tests/ui/impl-trait/call_method_on_inherent_impl_ref.next.stderr new file mode 100644 index 000000000000..327f6ca3450f --- /dev/null +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_ref.next.stderr @@ -0,0 +1,31 @@ +error[E0282]: type annotations needed + --> $DIR/call_method_on_inherent_impl_ref.rs:18:13 + | +LL | let x = my_foo(); + | ^ +LL | +LL | x.my_debug(); + | - type must be known at this point + | +help: consider giving `x` an explicit type + | +LL | let x: /* Type */ = my_foo(); + | ++++++++++++ + +error[E0282]: type annotations needed for `&_` + --> $DIR/call_method_on_inherent_impl_ref.rs:28:13 + | +LL | let x = &my_bar(); + | ^ +LL | +LL | x.my_debug(); + | -------- type must be known at this point + | +help: consider giving `x` an explicit type, where the placeholders `_` are specified + | +LL | let x: &_ = &my_bar(); + | ++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/call_method_on_inherent_impl_ref.rs b/tests/ui/impl-trait/call_method_on_inherent_impl_ref.rs new file mode 100644 index 000000000000..40ad21532a4e --- /dev/null +++ b/tests/ui/impl-trait/call_method_on_inherent_impl_ref.rs @@ -0,0 +1,35 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +trait MyDebug { + fn my_debug(&self); +} + +impl MyDebug for &T +where + T: std::fmt::Debug, +{ + fn my_debug(&self) {} +} + +fn my_foo() -> impl std::fmt::Debug { + //[current]~^ cycle + if false { + let x = my_foo(); + //[next]~^ type annotations needed + x.my_debug(); + //[current]~^ no method named `my_debug` found + } + () +} + +fn my_bar() -> impl std::fmt::Debug { + if false { + let x = &my_bar(); + //[next]~^ type annotations needed + x.my_debug(); + } + () +} + +fn main() {} diff --git a/tests/ui/impl-trait/call_method_without_import.no_import.stderr b/tests/ui/impl-trait/call_method_without_import.no_import.stderr new file mode 100644 index 000000000000..72982b695bbb --- /dev/null +++ b/tests/ui/impl-trait/call_method_without_import.no_import.stderr @@ -0,0 +1,37 @@ +error[E0599]: no method named `fmt` found for opaque type `impl Debug` in the current scope + --> $DIR/call_method_without_import.rs:17:11 + | +LL | x.fmt(f); + | ^^^ method not found in `impl Debug` + --> $SRC_DIR/core/src/fmt/mod.rs:LL:COL + | + = note: the method is available for `impl Debug` here + | + = help: items from traits can only be used if the trait is in scope +help: trait `Debug` which provides `fmt` is implemented but not in scope; perhaps you want to import it + | +LL + use std::fmt::Debug; + | + +error[E0599]: no method named `fmt` found for mutable reference `&mut impl Debug` in the current scope + --> $DIR/call_method_without_import.rs:26:11 + | +LL | x.fmt(f); + | ^^^ method not found in `&mut impl Debug` + | + = help: items from traits can only be used if the trait is in scope +help: the following traits which provide `fmt` are implemented but not in scope; perhaps you want to import one of them + | +LL + use std::fmt::Binary; + | +LL + use std::fmt::Debug; + | +LL + use std::fmt::Display; + | +LL + use std::fmt::LowerExp; + | + and 5 other candidates + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/impl-trait/call_method_without_import.rs b/tests/ui/impl-trait/call_method_without_import.rs new file mode 100644 index 000000000000..d62777ea2835 --- /dev/null +++ b/tests/ui/impl-trait/call_method_without_import.rs @@ -0,0 +1,42 @@ +//! Test that opaque types only pick up methods from traits in their bounds +//! if the trait is imported. +//! +//! FIXME: always look through the bounds of an opaque type to see if there are +//! methods that could be called on any of the bound traits, irrespective of +//! imported traits. + +//@ revisions: import no_import +//@[import] check-pass + +#[cfg(import)] +use std::fmt::Debug as _; + +fn foo(f: &mut std::fmt::Formatter<'_>) -> impl std::fmt::Debug { + if false { + let x = foo(f); + x.fmt(f); + //[no_import]~^ ERROR: no method named `fmt` found + } + () +} + +fn foo1(f: &mut std::fmt::Formatter<'_>) -> impl std::fmt::Debug { + if false { + let x = &mut foo(f); + x.fmt(f); + //[no_import]~^ ERROR: no method named `fmt` found + } + () +} + +// inconsistent with this +fn bar(t: impl std::fmt::Debug, f: &mut std::fmt::Formatter<'_>) { + t.fmt(f); +} + +// and the desugared version, of course +fn baz(t: T, f: &mut std::fmt::Formatter<'_>) { + t.fmt(f); +} + +fn main() {} diff --git a/tests/ui/impl-trait/equality.stderr b/tests/ui/impl-trait/equality.stderr index 69f4cbbbf429..12d886a00245 100644 --- a/tests/ui/impl-trait/equality.stderr +++ b/tests/ui/impl-trait/equality.stderr @@ -30,10 +30,10 @@ LL | n + sum_to(n - 1) | = help: the trait `Add` is not implemented for `u32` = help: the following other types implement trait `Add`: - <&'a u32 as Add> - <&u32 as Add<&u32>> - > - + `&'a u32` implements `Add` + `&u32` implements `Add<&u32>` + `u32` implements `Add<&u32>` + `u32` implements `Add` error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/impl-trait/future-no-bound-vars-ice-112347.rs b/tests/ui/impl-trait/future-no-bound-vars-ice-112347.rs index dc4dc9e220d5..af623dc7cd9d 100644 --- a/tests/ui/impl-trait/future-no-bound-vars-ice-112347.rs +++ b/tests/ui/impl-trait/future-no-bound-vars-ice-112347.rs @@ -7,11 +7,15 @@ use std::future::Future; -type Fut<'a> = impl Future + 'a; +mod foo { + use std::future::Future; + pub type Fut<'a> = impl Future + 'a; -fn foo<'a>(_: &()) -> Fut<'_> { - async {} + fn foo<'a>(_: &()) -> Fut<'_> { + async {} + } } +use foo::*; trait Test { fn hello(); diff --git a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit-2.rs b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit-2.rs index 5b3a4eb53ff1..14b1ebea8db2 100644 --- a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit-2.rs +++ b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit-2.rs @@ -1,11 +1,9 @@ // issue: 114146 - trait Foo { fn bar<'other: 'a>() -> impl Sized + 'a {} //~^ ERROR use of undeclared lifetime name `'a` //~| ERROR use of undeclared lifetime name `'a` - //~| ERROR expected generic lifetime parameter, found `'static` } fn main() {} diff --git a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit-2.stderr b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit-2.stderr index 8975578dabd8..f1b006da1db5 100644 --- a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit-2.stderr +++ b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit-2.stderr @@ -1,5 +1,5 @@ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/bad-item-bound-within-rpitit-2.rs:5:20 + --> $DIR/bad-item-bound-within-rpitit-2.rs:4:20 | LL | fn bar<'other: 'a>() -> impl Sized + 'a {} | ^^ undeclared lifetime @@ -14,7 +14,7 @@ LL | trait Foo<'a> { | ++++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/bad-item-bound-within-rpitit-2.rs:5:42 + --> $DIR/bad-item-bound-within-rpitit-2.rs:4:42 | LL | fn bar<'other: 'a>() -> impl Sized + 'a {} | ^^ undeclared lifetime @@ -28,15 +28,6 @@ help: consider introducing lifetime `'a` here LL | trait Foo<'a> { | ++++ -error[E0792]: expected generic lifetime parameter, found `'static` - --> $DIR/bad-item-bound-within-rpitit-2.rs:5:45 - | -LL | fn bar<'other: 'a>() -> impl Sized + 'a {} - | ------ ^^ - | | - | cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type +error: aborting due to 2 previous errors -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0261, E0792. -For more information about an error, try `rustc --explain E0261`. +For more information about this error, try `rustc --explain E0261`. diff --git a/tests/ui/impl-trait/issue-108592.rs b/tests/ui/impl-trait/issue-108592.rs index 624bb79006ea..7db2e31549c0 100644 --- a/tests/ui/impl-trait/issue-108592.rs +++ b/tests/ui/impl-trait/issue-108592.rs @@ -11,9 +11,13 @@ fn test_closure() { closure(&opaque()); } -type Opaque2 = impl Sized; -type Opaque<'a> = Opaque2; -fn define<'a>() -> Opaque<'a> {} +mod helper { + pub type Opaque2 = impl Sized; + pub type Opaque<'a> = Opaque2; + fn define<'a>() -> Opaque<'a> {} +} + +use helper::*; fn test_tait(_: &Opaque<'_>) { None::<&'static Opaque<'_>>; diff --git a/tests/ui/impl-trait/issue-99914.stderr b/tests/ui/impl-trait/issue-99914.stderr index 06e85e521d22..8adb211745a3 100644 --- a/tests/ui/impl-trait/issue-99914.stderr +++ b/tests/ui/impl-trait/issue-99914.stderr @@ -2,7 +2,9 @@ error[E0308]: mismatched types --> $DIR/issue-99914.rs:9:27 | LL | t.and_then(|t| -> _ { bar(t) }); - | ^^^^^^ expected `Result<_, Error>`, found future + | - ^^^^^^ expected `Result<_, Error>`, found future + | | + | expected `Result<_, Error>` because of return type | help: try wrapping the expression in `Ok` | diff --git a/tests/ui/impl-trait/issues/issue-67830.rs b/tests/ui/impl-trait/issues/issue-67830.rs index 939eca82a8f6..28772fa52720 100644 --- a/tests/ui/impl-trait/issues/issue-67830.rs +++ b/tests/ui/impl-trait/issues/issue-67830.rs @@ -7,7 +7,7 @@ struct Wrap(F); impl MyFn for Wrap where - F: Fn(A) -> B + F: Fn(A) -> B, { type Output = B; @@ -16,13 +16,10 @@ where } } - struct A; -fn test() -> impl for<'a> MyFn<&'a A, Output=impl Iterator + 'a> { +fn test() -> impl for<'a> MyFn<&'a A, Output = impl Iterator + 'a> { //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` Wrap(|a| Some(a).into_iter()) - //~^ ERROR implementation of `FnOnce` is not general enough - //~| ERROR implementation of `FnOnce` is not general enough } fn main() {} diff --git a/tests/ui/impl-trait/issues/issue-67830.stderr b/tests/ui/impl-trait/issues/issue-67830.stderr index ef513a40cf39..a7633c7f20b6 100644 --- a/tests/ui/impl-trait/issues/issue-67830.stderr +++ b/tests/ui/impl-trait/issues/issue-67830.stderr @@ -1,34 +1,15 @@ error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` - --> $DIR/issue-67830.rs:21:62 + --> $DIR/issue-67830.rs:20:64 | -LL | fn test() -> impl for<'a> MyFn<&'a A, Output=impl Iterator + 'a> { - | ^^ +LL | fn test() -> impl for<'a> MyFn<&'a A, Output = impl Iterator + 'a> { + | ^^ | note: lifetime declared here - --> $DIR/issue-67830.rs:21:23 + --> $DIR/issue-67830.rs:20:23 | -LL | fn test() -> impl for<'a> MyFn<&'a A, Output=impl Iterator + 'a> { +LL | fn test() -> impl for<'a> MyFn<&'a A, Output = impl Iterator + 'a> { | ^^ -error: implementation of `FnOnce` is not general enough - --> $DIR/issue-67830.rs:23:5 - | -LL | Wrap(|a| Some(a).into_iter()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough - | - = note: closure with signature `fn(&'2 A) -> std::option::IntoIter<&A>` must implement `FnOnce<(&'1 A,)>`, for any lifetime `'1`... - = note: ...but it actually implements `FnOnce<(&'2 A,)>`, for some specific lifetime `'2` - -error: implementation of `FnOnce` is not general enough - --> $DIR/issue-67830.rs:23:5 - | -LL | Wrap(|a| Some(a).into_iter()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough - | - = note: closure with signature `fn(&'2 A) -> std::option::IntoIter<&A>` must implement `FnOnce<(&'1 A,)>`, for any lifetime `'1`... - = note: ...but it actually implements `FnOnce<(&'2 A,)>`, for some specific lifetime `'2` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0657`. diff --git a/tests/ui/impl-trait/issues/issue-70877.rs b/tests/ui/impl-trait/issues/issue-70877.rs index df7722986744..6ced0bbba8b3 100644 --- a/tests/ui/impl-trait/issues/issue-70877.rs +++ b/tests/ui/impl-trait/issues/issue-70877.rs @@ -1,10 +1,8 @@ #![feature(type_alias_impl_trait)] type FooArg<'a> = &'a dyn ToString; -type FooRet = impl std::fmt::Debug; type FooItem = Box FooRet>; -type Foo = impl Iterator; #[repr(C)] struct Bar(u8); @@ -17,19 +15,26 @@ impl Iterator for Bar { } } -fn quux(st: FooArg) -> FooRet { - Some(st.to_string()) +mod ret { + pub type FooRet = impl std::fmt::Debug; + pub fn quux(st: super::FooArg) -> FooRet { + Some(st.to_string()) + } } - -fn ham() -> Foo { - Bar(1) -} - -fn oof(_: Foo) -> impl std::fmt::Debug { - let mut bar = ham(); - let func = bar.next().unwrap(); - return func(&"oof"); //~ ERROR opaque type's hidden type cannot be another opaque type +use ret::*; +mod foo { + pub type Foo = impl Iterator; + pub fn ham() -> Foo { + super::Bar(1) + } + pub fn oof(_: Foo) -> impl std::fmt::Debug { + //~^ ERROR: item does not constrain `Foo::{opaque#0}`, but has it in its signature + let mut bar = ham(); + let func = bar.next().unwrap(); + return func(&"oof"); + } } +use foo::*; fn main() { let _ = oof(ham()); diff --git a/tests/ui/impl-trait/issues/issue-70877.stderr b/tests/ui/impl-trait/issues/issue-70877.stderr index 274139f01d08..4b23a02aaee3 100644 --- a/tests/ui/impl-trait/issues/issue-70877.stderr +++ b/tests/ui/impl-trait/issues/issue-70877.stderr @@ -1,19 +1,15 @@ -error: opaque type's hidden type cannot be another opaque type from the same scope - --> $DIR/issue-70877.rs:31:12 +error: item does not constrain `Foo::{opaque#0}`, but has it in its signature + --> $DIR/issue-70877.rs:30:12 | -LL | return func(&"oof"); - | ^^^^^^^^^^^^ one of the two opaque types used here has to be outside its defining scope +LL | pub fn oof(_: Foo) -> impl std::fmt::Debug { + | ^^^ | -note: opaque type whose hidden type is being assigned - --> $DIR/issue-70877.rs:28:19 + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/issue-70877.rs:26:20 | -LL | fn oof(_: Foo) -> impl std::fmt::Debug { - | ^^^^^^^^^^^^^^^^^^^^ -note: opaque type being used as hidden type - --> $DIR/issue-70877.rs:4:15 - | -LL | type FooRet = impl std::fmt::Debug; - | ^^^^^^^^^^^^^^^^^^^^ +LL | pub type Foo = impl Iterator; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/issues/issue-77987.rs b/tests/ui/impl-trait/issues/issue-77987.rs index b77f993effce..a7e7b067d5ff 100644 --- a/tests/ui/impl-trait/issues/issue-77987.rs +++ b/tests/ui/impl-trait/issues/issue-77987.rs @@ -2,19 +2,20 @@ //@ check-pass -trait Foo {} +pub trait Foo {} impl Foo for U {} -type Scope = impl Foo<()>; +mod scope { + pub type Scope = impl super::Foo<()>; -#[allow(unused)] -fn infer_scope() -> Scope { - () + #[allow(unused)] + fn infer_scope() -> Scope { + () + } } #[allow(unused)] -fn ice() -> impl Foo -{ +fn ice() -> impl Foo { loop {} } diff --git a/tests/ui/impl-trait/issues/issue-86800.rs b/tests/ui/impl-trait/issues/issue-86800.rs index 172ab04f58df..5a6959ad7e6f 100644 --- a/tests/ui/impl-trait/issues/issue-86800.rs +++ b/tests/ui/impl-trait/issues/issue-86800.rs @@ -26,6 +26,7 @@ type TransactionFuture<'__, O> = impl '__ + Future //~^ ERROR unconstrained opaque type fn execute_transaction_fut<'f, F, O>( + //~^ ERROR: item does not constrain f: F, ) -> impl FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O> where @@ -37,10 +38,12 @@ where impl Context { async fn do_transaction( + //~^ ERROR: item does not constrain &self, f: impl FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O> ) -> TransactionResult { //~^ ERROR expected generic lifetime parameter, found `'_` + //~| ERROR: item does not constrain let mut conn = Connection {}; let mut transaction = TestTransaction { conn: &mut conn }; f(&mut transaction).await diff --git a/tests/ui/impl-trait/issues/issue-86800.stderr b/tests/ui/impl-trait/issues/issue-86800.stderr index 146d2f67942b..095f648143ca 100644 --- a/tests/ui/impl-trait/issues/issue-86800.stderr +++ b/tests/ui/impl-trait/issues/issue-86800.stderr @@ -1,3 +1,48 @@ +error: item does not constrain `TransactionFuture::{opaque#0}`, but has it in its signature + --> $DIR/issue-86800.rs:28:4 + | +LL | fn execute_transaction_fut<'f, F, O>( + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/issue-86800.rs:25:34 + | +LL | type TransactionFuture<'__, O> = impl '__ + Future>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: item does not constrain `TransactionFuture::{opaque#0}`, but has it in its signature + --> $DIR/issue-86800.rs:40:14 + | +LL | async fn do_transaction( + | ^^^^^^^^^^^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/issue-86800.rs:25:34 + | +LL | type TransactionFuture<'__, O> = impl '__ + Future>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: item does not constrain `TransactionFuture::{opaque#0}`, but has it in its signature + --> $DIR/issue-86800.rs:44:5 + | +LL | / { +LL | | +LL | | +LL | | let mut conn = Connection {}; +LL | | let mut transaction = TestTransaction { conn: &mut conn }; +LL | | f(&mut transaction).await +LL | | } + | |_____^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/issue-86800.rs:25:34 + | +LL | type TransactionFuture<'__, O> = impl '__ + Future>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: unconstrained opaque type --> $DIR/issue-86800.rs:25:34 | @@ -7,7 +52,7 @@ LL | type TransactionFuture<'__, O> = impl '__ + Future $DIR/issue-86800.rs:34:5 + --> $DIR/issue-86800.rs:35:5 | LL | type TransactionFuture<'__, O> = impl '__ + Future>; | --- this generic parameter must be used with a generic lifetime parameter @@ -16,19 +61,20 @@ LL | f | ^ error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/issue-86800.rs:42:5 + --> $DIR/issue-86800.rs:44:5 | LL | type TransactionFuture<'__, O> = impl '__ + Future>; | --- this generic parameter must be used with a generic lifetime parameter ... LL | / { LL | | +LL | | LL | | let mut conn = Connection {}; LL | | let mut transaction = TestTransaction { conn: &mut conn }; LL | | f(&mut transaction).await LL | | } | |_____^ -error: aborting due to 3 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/issues/issue-88236-2.rs b/tests/ui/impl-trait/issues/issue-88236-2.rs index 7ff08d8174f8..5005af46ee18 100644 --- a/tests/ui/impl-trait/issues/issue-88236-2.rs +++ b/tests/ui/impl-trait/issues/issue-88236-2.rs @@ -18,16 +18,11 @@ fn make_impl() -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {} fn make_weird_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> { //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` &() - //~^ ERROR implementation of `Hrtb` is not general enough - //~| ERROR implementation of `Hrtb` is not general enough } fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> { //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` x - //~^ ERROR implementation of `Hrtb` is not general enough - //~| ERROR implementation of `Hrtb` is not general enough - //~| ERROR lifetime may not live long enough } fn main() {} diff --git a/tests/ui/impl-trait/issues/issue-88236-2.stderr b/tests/ui/impl-trait/issues/issue-88236-2.stderr index 09fd58056a56..4ded9ed386fa 100644 --- a/tests/ui/impl-trait/issues/issue-88236-2.stderr +++ b/tests/ui/impl-trait/issues/issue-88236-2.stderr @@ -22,72 +22,18 @@ note: lifetime declared here LL | fn make_weird_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> { | ^^ -error: implementation of `Hrtb` is not general enough - --> $DIR/issue-88236-2.rs:20:5 - | -LL | &() - | ^^^ implementation of `Hrtb` is not general enough - | - = note: `Hrtb<'0>` would have to be implemented for the type `&()`, for any lifetime `'0`... - = note: ...but `Hrtb<'1>` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1` - -error: implementation of `Hrtb` is not general enough - --> $DIR/issue-88236-2.rs:20:5 - | -LL | &() - | ^^^ implementation of `Hrtb` is not general enough - | - = note: `Hrtb<'a>` would have to be implemented for the type `&()` - = note: ...but `Hrtb<'0>` is actually implemented for the type `&'0 ()`, for some specific lifetime `'0` - error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` - --> $DIR/issue-88236-2.rs:25:78 + --> $DIR/issue-88236-2.rs:23:78 | LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> { | ^^ | note: lifetime declared here - --> $DIR/issue-88236-2.rs:25:45 + --> $DIR/issue-88236-2.rs:23:45 | LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> { | ^^ -error: lifetime may not live long enough - --> $DIR/issue-88236-2.rs:27:5 - | -LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> { - | -- lifetime `'b` defined here -LL | -LL | x - | ^ returning this value requires that `'b` must outlive `'static` - | -help: to declare that `impl for<'a> Hrtb<'a, Assoc = impl Send + '_>` captures data from argument `x`, you can add an explicit `'b` lifetime bound - | -LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> + 'b { - | ++++ -help: to declare that `impl Send + 'a` captures data from argument `x`, you can add an explicit `'b` lifetime bound - | -LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a + 'b> { - | ++++ - -error: implementation of `Hrtb` is not general enough - --> $DIR/issue-88236-2.rs:27:5 - | -LL | x - | ^ implementation of `Hrtb` is not general enough - | - = note: `Hrtb<'0>` would have to be implemented for the type `&()`, for any lifetime `'0`... - = note: ...but `Hrtb<'1>` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1` - -error: implementation of `Hrtb` is not general enough - --> $DIR/issue-88236-2.rs:27:5 - | -LL | x - | ^ implementation of `Hrtb` is not general enough - | - = note: `Hrtb<'a>` would have to be implemented for the type `&()` - = note: ...but `Hrtb<'0>` is actually implemented for the type `&'0 ()`, for some specific lifetime `'0` - -error: aborting due to 8 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0657`. diff --git a/tests/ui/impl-trait/issues/issue-89312.rs b/tests/ui/impl-trait/issues/issue-89312.rs index 4304e3bc1b4e..3b0e976780be 100644 --- a/tests/ui/impl-trait/issues/issue-89312.rs +++ b/tests/ui/impl-trait/issues/issue-89312.rs @@ -2,18 +2,23 @@ //@ check-pass -trait T { type Item; } +mod helper { + pub trait T { + type Item; + } -type Alias<'a> = impl T; + pub type Alias<'a> = impl T; -struct S; -impl<'a> T for &'a S { - type Item = &'a (); -} - -fn filter_positive<'a>() -> Alias<'a> { - &S + struct S; + impl<'a> T for &'a S { + type Item = &'a (); + } + + pub fn filter_positive<'a>() -> Alias<'a> { + &S + } } +use helper::*; fn with_positive(fun: impl Fn(Alias<'_>)) { fun(filter_positive()); diff --git a/tests/ui/impl-trait/lazy_subtyping_of_opaques.rs b/tests/ui/impl-trait/lazy_subtyping_of_opaques.rs new file mode 100644 index 000000000000..8fd1f35645a8 --- /dev/null +++ b/tests/ui/impl-trait/lazy_subtyping_of_opaques.rs @@ -0,0 +1,24 @@ +//! This test checks that we allow subtyping predicates that contain opaque types. +//! No hidden types are being constrained in the subtyping predicate, but type and +//! lifetime variables get subtyped in the generic parameter list of the opaque. + +//@ check-pass + +fn foo() -> impl Default + Copy { + if false { + let x = Default::default(); + // add `Subtype(?x, ?y)` obligation + let y = x; + + // Make a tuple `(?x, ?y)` and equate it with `(impl Default, u32)`. + // For us to try and prove a `Subtype(impl Default, u32)` obligation, + // we have to instantiate both `?x` and `?y` without any + // `select_where_possible` calls inbetween. + let mut tup = &mut (x, y); + let assign_tup = &mut (foo(), 1u32); + tup = assign_tup; + } + 1u32 +} + +fn main() {} diff --git a/tests/ui/impl-trait/method-resolution.rs b/tests/ui/impl-trait/method-resolution.rs new file mode 100644 index 000000000000..60fbacd86462 --- /dev/null +++ b/tests/ui/impl-trait/method-resolution.rs @@ -0,0 +1,26 @@ +//! Since there is only one possible `bar` method, we invoke it and subsequently +//! constrain `foo`'s RPIT to `u32`. + +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ check-pass + +trait Trait {} + +impl Trait for u32 {} + +struct Bar(T); + +impl Bar { + fn bar(self) {} +} + +fn foo(x: bool) -> Bar { + if x { + let x = foo(false); + x.bar(); + } + todo!() +} + +fn main() {} diff --git a/tests/ui/impl-trait/method-resolution2.next.stderr b/tests/ui/impl-trait/method-resolution2.next.stderr new file mode 100644 index 000000000000..223430e1658b --- /dev/null +++ b/tests/ui/impl-trait/method-resolution2.next.stderr @@ -0,0 +1,20 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/method-resolution2.rs:25:11 + | +LL | x.bar(); + | ^^^ multiple `bar` found + | +note: candidate #1 is defined in an impl for the type `Bar` + --> $DIR/method-resolution2.rs:19:5 + | +LL | fn bar(self) {} + | ^^^^^^^^^^^^ +note: candidate #2 is defined in an impl for the type `Bar` + --> $DIR/method-resolution2.rs:15:5 + | +LL | fn bar(self) {} + | ^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0034`. diff --git a/tests/ui/impl-trait/method-resolution2.rs b/tests/ui/impl-trait/method-resolution2.rs new file mode 100644 index 000000000000..88d4f3d9896c --- /dev/null +++ b/tests/ui/impl-trait/method-resolution2.rs @@ -0,0 +1,31 @@ +//! Check that the method call does not constrain the RPIT to `i32`, even though +//! `i32` is the only type that satisfies the RPIT's trait bounds. + +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@[current] check-pass + +trait Trait {} + +impl Trait for i32 {} + +struct Bar(T); + +impl Bar { + fn bar(self) {} +} + +impl Bar { + fn bar(self) {} +} + +fn foo(x: bool) -> Bar { + if x { + let x = foo(false); + x.bar(); + //[next]~^ ERROR: multiple applicable items in scope + } + Bar(42_i32) +} + +fn main() {} diff --git a/tests/ui/impl-trait/method-resolution3.current.stderr b/tests/ui/impl-trait/method-resolution3.current.stderr new file mode 100644 index 000000000000..87dd862ef8f4 --- /dev/null +++ b/tests/ui/impl-trait/method-resolution3.current.stderr @@ -0,0 +1,20 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/method-resolution3.rs:21:11 + | +LL | x.bar(); + | ^^^ multiple `bar` found + | +note: candidate #1 is defined in an impl for the type `Bar` + --> $DIR/method-resolution3.rs:15:5 + | +LL | fn bar(self) {} + | ^^^^^^^^^^^^ +note: candidate #2 is defined in an impl for the type `Bar` + --> $DIR/method-resolution3.rs:11:5 + | +LL | fn bar(self) {} + | ^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0034`. diff --git a/tests/ui/impl-trait/method-resolution3.next.stderr b/tests/ui/impl-trait/method-resolution3.next.stderr new file mode 100644 index 000000000000..87dd862ef8f4 --- /dev/null +++ b/tests/ui/impl-trait/method-resolution3.next.stderr @@ -0,0 +1,20 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/method-resolution3.rs:21:11 + | +LL | x.bar(); + | ^^^ multiple `bar` found + | +note: candidate #1 is defined in an impl for the type `Bar` + --> $DIR/method-resolution3.rs:15:5 + | +LL | fn bar(self) {} + | ^^^^^^^^^^^^ +note: candidate #2 is defined in an impl for the type `Bar` + --> $DIR/method-resolution3.rs:11:5 + | +LL | fn bar(self) {} + | ^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0034`. diff --git a/tests/ui/impl-trait/method-resolution3.rs b/tests/ui/impl-trait/method-resolution3.rs new file mode 100644 index 000000000000..8c47ef4fc75c --- /dev/null +++ b/tests/ui/impl-trait/method-resolution3.rs @@ -0,0 +1,27 @@ +//! Check that we consider `Bar` to successfully unify +//! with both `Bar` and `Bar` (in isolation), so we bail +//! out with ambiguity. + +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +struct Bar(T); + +impl Bar { + fn bar(self) {} +} + +impl Bar { + fn bar(self) {} +} + +fn foo(x: bool) -> Bar { + if x { + let x = foo(false); + x.bar(); + //~^ ERROR: multiple applicable items in scope + } + todo!() +} + +fn main() {} diff --git a/tests/ui/impl-trait/method-resolution4.next.stderr b/tests/ui/impl-trait/method-resolution4.next.stderr new file mode 100644 index 000000000000..b48de0af3579 --- /dev/null +++ b/tests/ui/impl-trait/method-resolution4.next.stderr @@ -0,0 +1,22 @@ +error[E0282]: type annotations needed + --> $DIR/method-resolution4.rs:13:9 + | +LL | foo(false).next().unwrap(); + | ^^^^^^^^^^ cannot infer type + +error[E0308]: mismatched types + --> $DIR/method-resolution4.rs:16:5 + | +LL | fn foo(b: bool) -> impl Iterator { + | ------------------------ the expected opaque type +... +LL | std::iter::empty() + | ^^^^^^^^^^^^^^^^^^ types differ + | + = note: expected opaque type `impl Iterator` + found struct `std::iter::Empty<_>` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0282, E0308. +For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/method-resolution4.rs b/tests/ui/impl-trait/method-resolution4.rs new file mode 100644 index 000000000000..91884eb59fd6 --- /dev/null +++ b/tests/ui/impl-trait/method-resolution4.rs @@ -0,0 +1,20 @@ +//! The recursive method call yields the opaque type. The +//! `next` method call then constrains the hidden type to `&mut _` +//! because `next` takes `&mut self`. We never resolve the inference +//! variable, but get a type mismatch when comparing `&mut _` with +//! `std::iter::Empty`. + +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@[current] check-pass + +fn foo(b: bool) -> impl Iterator { + if b { + foo(false).next().unwrap(); + //[next]~^ type annotations needed + } + std::iter::empty() + //[next]~^ mismatched types +} + +fn main() {} diff --git a/tests/ui/impl-trait/nested-rpit-hrtb.rs b/tests/ui/impl-trait/nested-rpit-hrtb.rs index 9b18aceb4a73..11d79bcff737 100644 --- a/tests/ui/impl-trait/nested-rpit-hrtb.rs +++ b/tests/ui/impl-trait/nested-rpit-hrtb.rs @@ -31,7 +31,6 @@ fn one_hrtb_trait_param() -> impl for<'a> Foo<'a, Assoc = impl Qux<'a>> {} fn one_hrtb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'a> {} //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` -//~| ERROR implementation of `Bar` is not general enough fn one_hrtb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl Qux<'a>> {} //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` @@ -64,6 +63,5 @@ fn two_htrb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Qux< // `'b` is not in scope for the outlives bound. fn two_htrb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Sized + 'b> {} //~^ ERROR use of undeclared lifetime name `'b` [E0261] -//~| ERROR implementation of `Bar` is not general enough fn main() {} diff --git a/tests/ui/impl-trait/nested-rpit-hrtb.stderr b/tests/ui/impl-trait/nested-rpit-hrtb.stderr index 2fa036f35fab..d98de650d0d8 100644 --- a/tests/ui/impl-trait/nested-rpit-hrtb.stderr +++ b/tests/ui/impl-trait/nested-rpit-hrtb.stderr @@ -1,5 +1,5 @@ error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/nested-rpit-hrtb.rs:57:77 + --> $DIR/nested-rpit-hrtb.rs:56:77 | LL | fn two_htrb_outlives() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Sized + 'b> {} | ^^ undeclared lifetime @@ -15,7 +15,7 @@ LL | fn two_htrb_outlives<'b>() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Siz | ++++ error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/nested-rpit-hrtb.rs:65:82 + --> $DIR/nested-rpit-hrtb.rs:64:82 | LL | fn two_htrb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Sized + 'b> {} | ^^ undeclared lifetime @@ -65,29 +65,20 @@ note: lifetime declared here LL | fn one_hrtb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'a> {} | ^^ -error: implementation of `Bar` is not general enough - --> $DIR/nested-rpit-hrtb.rs:32:78 - | -LL | fn one_hrtb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'a> {} - | ^^ implementation of `Bar` is not general enough - | - = note: `()` must implement `Bar<'a>` - = note: ...but it actually implements `Bar<'0>`, for some specific lifetime `'0` - error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` - --> $DIR/nested-rpit-hrtb.rs:36:73 + --> $DIR/nested-rpit-hrtb.rs:35:73 | LL | fn one_hrtb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl Qux<'a>> {} | ^^ | note: lifetime declared here - --> $DIR/nested-rpit-hrtb.rs:36:44 + --> $DIR/nested-rpit-hrtb.rs:35:44 | LL | fn one_hrtb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl Qux<'a>> {} | ^^ error[E0277]: the trait bound `for<'a> &'a (): Qux<'b>` is not satisfied - --> $DIR/nested-rpit-hrtb.rs:46:79 + --> $DIR/nested-rpit-hrtb.rs:45:79 | LL | fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Qux<'b>> {} | ^^^^^^^^^^^^ the trait `for<'a> Qux<'b>` is not implemented for `&'a ()` @@ -96,7 +87,7 @@ LL | fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc = help: for that trait implementation, expected `()`, found `&'a ()` error: implementation of `Bar` is not general enough - --> $DIR/nested-rpit-hrtb.rs:50:93 + --> $DIR/nested-rpit-hrtb.rs:49:93 | LL | fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'b> {} | ^^ implementation of `Bar` is not general enough @@ -105,7 +96,7 @@ LL | fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = = note: ...but it actually implements `Bar<'0>`, for some specific lifetime `'0` error[E0277]: the trait bound `for<'a, 'b> &'a (): Qux<'b>` is not satisfied - --> $DIR/nested-rpit-hrtb.rs:61:64 + --> $DIR/nested-rpit-hrtb.rs:60:64 | LL | fn two_htrb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Qux<'b>> {} | ^^^^^^^^^^^^^^^^^^^^ the trait `for<'a, 'b> Qux<'b>` is not implemented for `&'a ()` @@ -113,16 +104,7 @@ LL | fn two_htrb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> = help: the trait `Qux<'_>` is implemented for `()` = help: for that trait implementation, expected `()`, found `&'a ()` -error: implementation of `Bar` is not general enough - --> $DIR/nested-rpit-hrtb.rs:65:86 - | -LL | fn two_htrb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Sized + 'b> {} - | ^^ implementation of `Bar` is not general enough - | - = note: `()` must implement `Bar<'a>` - = note: ...but it actually implements `Bar<'0>`, for some specific lifetime `'0` - -error: aborting due to 11 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0261, E0277, E0657. For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/impl-trait/nested_impl_trait.stderr b/tests/ui/impl-trait/nested_impl_trait.stderr index 1f9a2a5e9d60..a53312e5c0bd 100644 --- a/tests/ui/impl-trait/nested_impl_trait.stderr +++ b/tests/ui/impl-trait/nested_impl_trait.stderr @@ -46,7 +46,7 @@ error[E0277]: the trait bound `impl Into: Into` is not satisfie --> $DIR/nested_impl_trait.rs:6:46 | LL | fn bad_in_ret_position(x: impl Into) -> impl Into { x } - | ^^^^^^^^^^^^^^^^^^^^^ the trait `From>` is not implemented for `impl Into`, which is required by `impl Into: Into` + | ^^^^^^^^^^^^^^^^^^^^^ the trait `From>` is not implemented for `impl Debug`, which is required by `impl Into: Into` | = help: the trait `Into` is implemented for `T` = note: required for `impl Into` to implement `Into` @@ -55,7 +55,7 @@ error[E0277]: the trait bound `impl Into: Into` is not satisfie --> $DIR/nested_impl_trait.rs:19:34 | LL | fn bad(x: impl Into) -> impl Into { x } - | ^^^^^^^^^^^^^^^^^^^^^ the trait `From>` is not implemented for `impl Into`, which is required by `impl Into: Into` + | ^^^^^^^^^^^^^^^^^^^^^ the trait `From>` is not implemented for `impl Debug`, which is required by `impl Into: Into` | = help: the trait `Into` is implemented for `T` = note: required for `impl Into` to implement `Into` diff --git a/tests/ui/impl-trait/normalize-tait-in-const.rs b/tests/ui/impl-trait/normalize-tait-in-const.rs index 422c2e439cf0..fc90139d6409 100644 --- a/tests/ui/impl-trait/normalize-tait-in-const.rs +++ b/tests/ui/impl-trait/normalize-tait-in-const.rs @@ -6,20 +6,23 @@ use std::marker::Destruct; -trait T { - type Item; -} +mod foo { + trait T { + type Item; + } -type Alias<'a> = impl T; + pub type Alias<'a> = impl T; -struct S; -impl<'a> T for &'a S { - type Item = &'a (); -} + struct S; + impl<'a> T for &'a S { + type Item = &'a (); + } -const fn filter_positive<'a>() -> &'a Alias<'a> { - &&S + pub const fn filter_positive<'a>() -> &'a Alias<'a> { + &&S + } } +use foo::*; const fn with_positive Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { fun(filter_positive()); diff --git a/tests/ui/impl-trait/normalize-tait-in-const.stderr b/tests/ui/impl-trait/normalize-tait-in-const.stderr index fbd41b61730e..5e3c0ea054bd 100644 --- a/tests/ui/impl-trait/normalize-tait-in-const.stderr +++ b/tests/ui/impl-trait/normalize-tait-in-const.stderr @@ -1,11 +1,11 @@ error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/normalize-tait-in-const.rs:24:42 + --> $DIR/normalize-tait-in-const.rs:27:42 | LL | const fn with_positive Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { | ^^^^^^^^^^^^^^^^^ error[E0015]: cannot call non-const closure in constant functions - --> $DIR/normalize-tait-in-const.rs:25:5 + --> $DIR/normalize-tait-in-const.rs:28:5 | LL | fun(filter_positive()); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -13,15 +13,15 @@ LL | fun(filter_positive()); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants help: consider further restricting this bound | -LL | const fn with_positive Fn(&'a Alias<'a>) + ~const Destruct + ~const std::ops::Fn<(&Alias<'_>,)>>(fun: F) { - | ++++++++++++++++++++++++++++++++++++ +LL | const fn with_positive Fn(&'a Alias<'a>) + ~const Destruct + ~const Fn(&foo::Alias<'_>)>(fun: F) { + | ++++++++++++++++++++++++++++ help: add `#![feature(effects)]` to the crate attributes to enable | LL + #![feature(effects)] | error[E0493]: destructor of `F` cannot be evaluated at compile-time - --> $DIR/normalize-tait-in-const.rs:24:79 + --> $DIR/normalize-tait-in-const.rs:27:79 | LL | const fn with_positive Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { | ^^^ the destructor for this type cannot be evaluated in constant functions diff --git a/tests/ui/impl-trait/precise-capturing/apit.rs b/tests/ui/impl-trait/precise-capturing/apit.rs index efcac9ebb0b8..64c15d6df96c 100644 --- a/tests/ui/impl-trait/precise-capturing/apit.rs +++ b/tests/ui/impl-trait/precise-capturing/apit.rs @@ -1,7 +1,6 @@ #![feature(precise_capturing)] -//~^ WARN the feature `precise_capturing` is incomplete -fn hello(_: impl use<> Sized) {} -//~^ ERROR `use<...>` precise capturing syntax not allowed on argument-position `impl Trait` +fn hello(_: impl Sized + use<>) {} +//~^ ERROR `use<...>` precise capturing syntax not allowed in argument-position `impl Trait` fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/apit.stderr b/tests/ui/impl-trait/precise-capturing/apit.stderr index 96548f5732f5..1d6225a1ff8d 100644 --- a/tests/ui/impl-trait/precise-capturing/apit.stderr +++ b/tests/ui/impl-trait/precise-capturing/apit.stderr @@ -1,17 +1,8 @@ -warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/apit.rs:1:12 +error: `use<...>` precise capturing syntax not allowed in argument-position `impl Trait` + --> $DIR/apit.rs:3:26 | -LL | #![feature(precise_capturing)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #123432 for more information - = note: `#[warn(incomplete_features)]` on by default +LL | fn hello(_: impl Sized + use<>) {} + | ^^^^^ -error: `use<...>` precise capturing syntax not allowed on argument-position `impl Trait` - --> $DIR/apit.rs:4:18 - | -LL | fn hello(_: impl use<> Sized) {} - | ^^^^^ - -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs index 623063a8f502..d2d4570c570e 100644 --- a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs +++ b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs @@ -1,14 +1,13 @@ #![feature(precise_capturing)] -//~^ WARN the feature `precise_capturing` is incomplete -fn no_elided_lt() -> impl use<'_> Sized {} +fn no_elided_lt() -> impl Sized + use<'_> {} //~^ ERROR missing lifetime specifier //~| ERROR expected lifetime parameter in `use<...>` precise captures list, found `'_` -fn static_lt() -> impl use<'static> Sized {} +fn static_lt() -> impl Sized + use<'static> {} //~^ ERROR expected lifetime parameter in `use<...>` precise captures list, found `'static` -fn missing_lt() -> impl use<'missing> Sized {} +fn missing_lt() -> impl Sized + use<'missing> {} //~^ ERROR use of undeclared lifetime name `'missing` fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr index a926362c50c8..550996ab5e57 100644 --- a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr +++ b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr @@ -1,45 +1,36 @@ error[E0106]: missing lifetime specifier - --> $DIR/bad-lifetimes.rs:4:31 + --> $DIR/bad-lifetimes.rs:3:39 | -LL | fn no_elided_lt() -> impl use<'_> Sized {} - | ^^ expected named lifetime parameter +LL | fn no_elided_lt() -> impl Sized + use<'_> {} + | ^^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | -LL | fn no_elided_lt() -> impl use<'static> Sized {} - | ~~~~~~~ +LL | fn no_elided_lt() -> impl Sized + use<'static> {} + | ~~~~~~~ error[E0261]: use of undeclared lifetime name `'missing` - --> $DIR/bad-lifetimes.rs:11:29 + --> $DIR/bad-lifetimes.rs:10:37 | -LL | fn missing_lt() -> impl use<'missing> Sized {} - | - ^^^^^^^^ undeclared lifetime +LL | fn missing_lt() -> impl Sized + use<'missing> {} + | - ^^^^^^^^ undeclared lifetime | | | help: consider introducing lifetime `'missing` here: `<'missing>` -warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/bad-lifetimes.rs:1:12 - | -LL | #![feature(precise_capturing)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #123432 for more information - = note: `#[warn(incomplete_features)]` on by default - error: expected lifetime parameter in `use<...>` precise captures list, found `'_` - --> $DIR/bad-lifetimes.rs:4:31 + --> $DIR/bad-lifetimes.rs:3:39 | -LL | fn no_elided_lt() -> impl use<'_> Sized {} - | ^^ +LL | fn no_elided_lt() -> impl Sized + use<'_> {} + | ^^ error: expected lifetime parameter in `use<...>` precise captures list, found `'static` - --> $DIR/bad-lifetimes.rs:8:28 + --> $DIR/bad-lifetimes.rs:7:36 | -LL | fn static_lt() -> impl use<'static> Sized {} - | ^^^^^^^ +LL | fn static_lt() -> impl Sized + use<'static> {} + | ^^^^^^^ -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 4 previous errors Some errors have detailed explanations: E0106, E0261. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.rs b/tests/ui/impl-trait/precise-capturing/bad-params.rs index 7970d49bf7c0..08eee67c0e57 100644 --- a/tests/ui/impl-trait/precise-capturing/bad-params.rs +++ b/tests/ui/impl-trait/precise-capturing/bad-params.rs @@ -1,19 +1,18 @@ #![feature(precise_capturing)] -//~^ WARN the feature `precise_capturing` is incomplete -fn missing() -> impl use Sized {} +fn missing() -> impl Sized + use {} //~^ ERROR cannot find type `T` in this scope -fn missing_self() -> impl use Sized {} +fn missing_self() -> impl Sized + use {} //~^ ERROR cannot find type `Self` in this scope struct MyType; impl MyType { - fn self_is_not_param() -> impl use Sized {} + fn self_is_not_param() -> impl Sized + use {} //~^ ERROR `Self` can't be captured in `use<...>` precise captures list, since it is an alias } -fn hello() -> impl use Sized {} +fn hello() -> impl Sized + use {} //~^ ERROR expected type or const parameter in `use<...>` precise captures list, found function fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.stderr b/tests/ui/impl-trait/precise-capturing/bad-params.stderr index 27bf05302f9b..e104f115aa3f 100644 --- a/tests/ui/impl-trait/precise-capturing/bad-params.stderr +++ b/tests/ui/impl-trait/precise-capturing/bad-params.stderr @@ -1,46 +1,37 @@ error[E0412]: cannot find type `T` in this scope - --> $DIR/bad-params.rs:4:26 + --> $DIR/bad-params.rs:3:34 | -LL | fn missing() -> impl use Sized {} - | ^ not found in this scope +LL | fn missing() -> impl Sized + use {} + | ^ not found in this scope | help: you might be missing a type parameter | -LL | fn missing() -> impl use Sized {} +LL | fn missing() -> impl Sized + use {} | +++ error[E0411]: cannot find type `Self` in this scope - --> $DIR/bad-params.rs:7:31 + --> $DIR/bad-params.rs:6:39 | -LL | fn missing_self() -> impl use Sized {} - | ------------ ^^^^ `Self` is only available in impls, traits, and type definitions +LL | fn missing_self() -> impl Sized + use {} + | ------------ ^^^^ `Self` is only available in impls, traits, and type definitions | | | `Self` not allowed in a function -warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/bad-params.rs:1:12 - | -LL | #![feature(precise_capturing)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #123432 for more information - = note: `#[warn(incomplete_features)]` on by default - error: `Self` can't be captured in `use<...>` precise captures list, since it is an alias - --> $DIR/bad-params.rs:12:40 + --> $DIR/bad-params.rs:11:48 | LL | impl MyType { | ----------- `Self` is not a generic argument, but an alias to the type of the implementation -LL | fn self_is_not_param() -> impl use Sized {} - | ^^^^ +LL | fn self_is_not_param() -> impl Sized + use {} + | ^^^^ error: expected type or const parameter in `use<...>` precise captures list, found function - --> $DIR/bad-params.rs:16:24 + --> $DIR/bad-params.rs:15:32 | -LL | fn hello() -> impl use Sized {} - | ^^^^^ +LL | fn hello() -> impl Sized + use {} + | ^^^^^ -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 4 previous errors Some errors have detailed explanations: E0411, E0412. For more information about an error, try `rustc --explain E0411`. diff --git a/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs index 35b28d0e6fb5..82b953bfed4b 100644 --- a/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs +++ b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs @@ -1,5 +1,4 @@ #![feature(precise_capturing)] -//~^ WARN the feature `precise_capturing` is incomplete trait Tr { type Assoc; @@ -13,25 +12,25 @@ impl Tr for W<'_> { // The normal way of capturing `'a`... impl<'a> W<'a> { - fn good1() -> impl use<'a> Into< as Tr>::Assoc> {} + fn good1() -> impl Into< as Tr>::Assoc> + use<'a> {} } // This ensures that we don't error when we capture the *parent* copy of `'a`, // since the opaque captures that rather than the duplicated `'a` lifetime // synthesized from mentioning `'a` directly in the bounds. impl<'a> W<'a> { - fn good2() -> impl use<'a> Into<::Assoc> {} + fn good2() -> impl Into<::Assoc> + use<'a> {} } // The normal way of capturing `'a`... but not mentioned in the bounds. impl<'a> W<'a> { - fn bad1() -> impl use<> Into< as Tr>::Assoc> {} + fn bad1() -> impl Into< as Tr>::Assoc> + use<> {} //~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list } // But also make sure that we error here... impl<'a> W<'a> { - fn bad2() -> impl use<> Into<::Assoc> {} + fn bad2() -> impl Into<::Assoc> + use<> {} //~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list } diff --git a/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr index 13aaa9997073..b521ee0a9023 100644 --- a/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr +++ b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr @@ -1,27 +1,18 @@ -warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/capture-parent-arg.rs:1:12 - | -LL | #![feature(precise_capturing)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #123432 for more information - = note: `#[warn(incomplete_features)]` on by default - error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list - --> $DIR/capture-parent-arg.rs:28:37 + --> $DIR/capture-parent-arg.rs:27:31 | LL | impl<'a> W<'a> { | -- this lifetime parameter is captured -LL | fn bad1() -> impl use<> Into< as Tr>::Assoc> {} - | -------------------^^---------------- lifetime captured due to being mentioned in the bounds of the `impl Trait` +LL | fn bad1() -> impl Into< as Tr>::Assoc> + use<> {} + | -------------^^------------------------ lifetime captured due to being mentioned in the bounds of the `impl Trait` error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list - --> $DIR/capture-parent-arg.rs:34:18 + --> $DIR/capture-parent-arg.rs:33:18 | LL | impl<'a> W<'a> { | -- this lifetime parameter is captured -LL | fn bad2() -> impl use<> Into<::Assoc> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime captured due to being mentioned in the bounds of the `impl Trait` +LL | fn bad2() -> impl Into<::Assoc> + use<> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime captured due to being mentioned in the bounds of the `impl Trait` -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/precise-capturing/duplicated-use.real.stderr b/tests/ui/impl-trait/precise-capturing/duplicated-use.real.stderr new file mode 100644 index 000000000000..d8edd672b48c --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/duplicated-use.real.stderr @@ -0,0 +1,8 @@ +error: duplicate `use<...>` precise capturing syntax + --> $DIR/duplicated-use.rs:7:32 + | +LL | fn hello<'a>() -> impl Sized + use<'a> + use<'a> {} + | ^^^^^^^ ------- second `use<...>` here + +error: aborting due to 1 previous error + diff --git a/tests/ui/impl-trait/precise-capturing/duplicated-use.rs b/tests/ui/impl-trait/precise-capturing/duplicated-use.rs new file mode 100644 index 000000000000..bfbdcdbf311d --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/duplicated-use.rs @@ -0,0 +1,10 @@ +//@ revisions: real pre_expansion +//@[pre_expansion] check-pass + +#![feature(precise_capturing)] + +#[cfg(real)] +fn hello<'a>() -> impl Sized + use<'a> + use<'a> {} +//[real]~^ ERROR duplicate `use<...>` precise capturing syntax + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/dyn-use.rs b/tests/ui/impl-trait/precise-capturing/dyn-use.rs new file mode 100644 index 000000000000..ce7a0f3c7b21 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/dyn-use.rs @@ -0,0 +1,4 @@ +#![feature(precise_capturing)] + +fn dyn() -> &'static dyn use<> { &() } +//~^ ERROR expected one of `!`, `(`, `::`, `<`, `where`, or `{`, found keyword `use` diff --git a/tests/ui/impl-trait/precise-capturing/dyn-use.stderr b/tests/ui/impl-trait/precise-capturing/dyn-use.stderr new file mode 100644 index 000000000000..5519633de1f7 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/dyn-use.stderr @@ -0,0 +1,8 @@ +error: expected one of `!`, `(`, `::`, `<`, `where`, or `{`, found keyword `use` + --> $DIR/dyn-use.rs:3:26 + | +LL | fn dyn() -> &'static dyn use<> { &() } + | ^^^ expected one of `!`, `(`, `::`, `<`, `where`, or `{` + +error: aborting due to 1 previous error + diff --git a/tests/ui/impl-trait/precise-capturing/elided.rs b/tests/ui/impl-trait/precise-capturing/elided.rs index de80e8a5d581..34d1ba620dce 100644 --- a/tests/ui/impl-trait/precise-capturing/elided.rs +++ b/tests/ui/impl-trait/precise-capturing/elided.rs @@ -1,8 +1,7 @@ //@ check-pass #![feature(precise_capturing)] -//~^ WARN the feature `precise_capturing` is incomplete -fn elided(x: &()) -> impl use<'_> Sized { x } +fn elided(x: &()) -> impl Sized + use<'_> { x } fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/elided.stderr b/tests/ui/impl-trait/precise-capturing/elided.stderr deleted file mode 100644 index 38da0828de97..000000000000 --- a/tests/ui/impl-trait/precise-capturing/elided.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/elided.rs:3:12 - | -LL | #![feature(precise_capturing)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #123432 for more information - = note: `#[warn(incomplete_features)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs index 1b604e6c358b..26d29e456eaa 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs @@ -1,7 +1,6 @@ #![feature(precise_capturing)] -//~^ WARN the feature `precise_capturing` is incomplete -fn constant() -> impl use<> Sized {} +fn constant() -> impl Sized + use<> {} //~^ ERROR `impl Trait` must mention all const parameters in scope fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr index 3f78e7c56b6e..989ed136d4c3 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr @@ -1,21 +1,12 @@ -warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/forgot-to-capture-const.rs:1:12 - | -LL | #![feature(precise_capturing)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #123432 for more information - = note: `#[warn(incomplete_features)]` on by default - error: `impl Trait` must mention all const parameters in scope in `use<...>` - --> $DIR/forgot-to-capture-const.rs:4:34 + --> $DIR/forgot-to-capture-const.rs:3:34 | -LL | fn constant() -> impl use<> Sized {} - | -------------- ^^^^^^^^^^^^^^^^ +LL | fn constant() -> impl Sized + use<> {} + | -------------- ^^^^^^^^^^^^^^^^^^ | | | const parameter is implicitly captured by this `impl Trait` | = note: currently, all const parameters are required to be mentioned in the precise captures list -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs index cc86bf83107e..f18dbca6c5ef 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs @@ -1,10 +1,9 @@ #![feature(precise_capturing)] -//~^ WARN the feature `precise_capturing` is incomplete -fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x } +fn lifetime_in_bounds<'a>(x: &'a ()) -> impl Into<&'a ()> + use<> { x } //~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list -fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x } +fn lifetime_in_hidden<'a>(x: &'a ()) -> impl Sized + use<> { x } //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr index e472c898050a..6544837ba832 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr @@ -1,35 +1,26 @@ -warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/forgot-to-capture-lifetime.rs:1:12 - | -LL | #![feature(precise_capturing)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #123432 for more information - = note: `#[warn(incomplete_features)]` on by default - error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list - --> $DIR/forgot-to-capture-lifetime.rs:4:58 + --> $DIR/forgot-to-capture-lifetime.rs:3:52 | -LL | fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x } - | -- -----------------^^---- +LL | fn lifetime_in_bounds<'a>(x: &'a ()) -> impl Into<&'a ()> + use<> { x } + | -- -----------^^------------ | | | | | lifetime captured due to being mentioned in the bounds of the `impl Trait` | this lifetime parameter is captured error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds - --> $DIR/forgot-to-capture-lifetime.rs:7:60 + --> $DIR/forgot-to-capture-lifetime.rs:6:62 | -LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x } - | -- ---------------- ^ +LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl Sized + use<> { x } + | -- ------------------ ^ | | | | | opaque type defined here | hidden type `&'a ()` captures the lifetime `'a` as defined here | help: to declare that `impl Sized` captures `'a`, you can add an explicit `'a` lifetime bound | -LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized + 'a { x } - | ++++ +LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl Sized + use<> + 'a { x } + | ++++ -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs index d359ea5e26df..080149857839 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs @@ -1,11 +1,10 @@ #![feature(precise_capturing)] -//~^ WARN the feature `precise_capturing` is incomplete -fn type_param() -> impl use<> Sized {} +fn type_param() -> impl Sized + use<> {} //~^ ERROR `impl Trait` must mention all type parameters in scope trait Foo { - fn bar() -> impl use<> Sized; + fn bar() -> impl Sized + use<>; //~^ ERROR `impl Trait` must mention the `Self` type of the trait } diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr index 26994d0bdbf1..93b44a0c18c2 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr @@ -1,31 +1,22 @@ -warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/forgot-to-capture-type.rs:1:12 - | -LL | #![feature(precise_capturing)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #123432 for more information - = note: `#[warn(incomplete_features)]` on by default - error: `impl Trait` must mention all type parameters in scope in `use<...>` - --> $DIR/forgot-to-capture-type.rs:4:23 + --> $DIR/forgot-to-capture-type.rs:3:23 | -LL | fn type_param() -> impl use<> Sized {} - | - ^^^^^^^^^^^^^^^^ +LL | fn type_param() -> impl Sized + use<> {} + | - ^^^^^^^^^^^^^^^^^^ | | | type parameter is implicitly captured by this `impl Trait` | = note: currently, all type parameters are required to be mentioned in the precise captures list error: `impl Trait` must mention the `Self` type of the trait in `use<...>` - --> $DIR/forgot-to-capture-type.rs:8:17 + --> $DIR/forgot-to-capture-type.rs:7:17 | LL | trait Foo { | --------- `Self` type parameter is implicitly captured by this `impl Trait` -LL | fn bar() -> impl use<> Sized; - | ^^^^^^^^^^^^^^^^ +LL | fn bar() -> impl Sized + use<>; + | ^^^^^^^^^^^^^^^^^^ | = note: currently, all type parameters are required to be mentioned in the precise captures list -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/precise-capturing/higher-ranked.rs b/tests/ui/impl-trait/precise-capturing/higher-ranked.rs index 28fb1fa4b9ec..21ac19640bcd 100644 --- a/tests/ui/impl-trait/precise-capturing/higher-ranked.rs +++ b/tests/ui/impl-trait/precise-capturing/higher-ranked.rs @@ -3,7 +3,6 @@ // Show how precise captures allow us to skip capturing a higher-ranked lifetime #![feature(lifetime_capture_rules_2024, precise_capturing)] -//~^ WARN the feature `precise_capturing` is incomplete trait Trait<'a> { type Item; @@ -13,6 +12,6 @@ impl Trait<'_> for () { type Item = Vec<()>; } -fn hello() -> impl for<'a> Trait<'a, Item = impl use<> IntoIterator> {} +fn hello() -> impl for<'a> Trait<'a, Item = impl IntoIterator + use<>> {} fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/higher-ranked.stderr b/tests/ui/impl-trait/precise-capturing/higher-ranked.stderr deleted file mode 100644 index e48d6d42af07..000000000000 --- a/tests/ui/impl-trait/precise-capturing/higher-ranked.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/higher-ranked.rs:5:41 - | -LL | #![feature(lifetime_capture_rules_2024, precise_capturing)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #123432 for more information - = note: `#[warn(incomplete_features)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/impl-trait/precise-capturing/illegal-positions.real.stderr b/tests/ui/impl-trait/precise-capturing/illegal-positions.real.stderr new file mode 100644 index 000000000000..2b234bcb6a50 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/illegal-positions.real.stderr @@ -0,0 +1,57 @@ +error: `use<...>` precise capturing syntax not allowed in supertrait bounds + --> $DIR/illegal-positions.rs:8:12 + | +LL | trait Foo: use<> { + | ^^^^^ + +error: `use<...>` precise capturing syntax not allowed in bounds + --> $DIR/illegal-positions.rs:10:33 + | +LL | type Assoc: use<> where (): use<>; + | ^^^^^ + +error: `use<...>` precise capturing syntax not allowed in bounds + --> $DIR/illegal-positions.rs:10:17 + | +LL | type Assoc: use<> where (): use<>; + | ^^^^^ + +error: `use<...>` precise capturing syntax not allowed in bounds + --> $DIR/illegal-positions.rs:16:11 + | +LL | fn fun>(_: impl use<>) where (): use<> {} + | ^^^^^ + +error: `use<...>` precise capturing syntax not allowed in bounds + --> $DIR/illegal-positions.rs:16:43 + | +LL | fn fun>(_: impl use<>) where (): use<> {} + | ^^^^^ + +error: at least one trait must be specified + --> $DIR/illegal-positions.rs:16:21 + | +LL | fn fun>(_: impl use<>) where (): use<> {} + | ^^^^^^^^^^ + +error: `use<...>` precise capturing syntax not allowed in `dyn` trait object bounds + --> $DIR/illegal-positions.rs:23:25 + | +LL | fn dynamic() -> Box> {} + | ^^^^^ + +error: `use<...>` precise capturing syntax not allowed in argument-position `impl Trait` + --> $DIR/illegal-positions.rs:16:26 + | +LL | fn fun>(_: impl use<>) where (): use<> {} + | ^^^^^ + +error[E0224]: at least one trait is required for an object type + --> $DIR/illegal-positions.rs:23:21 + | +LL | fn dynamic() -> Box> {} + | ^^^^^^^^^ + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0224`. diff --git a/tests/ui/impl-trait/precise-capturing/illegal-positions.rs b/tests/ui/impl-trait/precise-capturing/illegal-positions.rs new file mode 100644 index 000000000000..681458e25f80 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/illegal-positions.rs @@ -0,0 +1,27 @@ +//@ revisions: real pre_expansion +//@[pre_expansion] check-pass +//@ edition: 2021 + +#![feature(precise_capturing)] + +#[cfg(real)] +trait Foo: use<> { + //[real]~^ ERROR `use<...>` precise capturing syntax not allowed + type Assoc: use<> where (): use<>; + //[real]~^ ERROR `use<...>` precise capturing syntax not allowed + //[real]~| ERROR `use<...>` precise capturing syntax not allowed +} + +#[cfg(real)] +fn fun>(_: impl use<>) where (): use<> {} +//[real]~^ ERROR `use<...>` precise capturing syntax not allowed +//[real]~| ERROR `use<...>` precise capturing syntax not allowed +//[real]~| ERROR `use<...>` precise capturing syntax not allowed +//[real]~| ERROR at least one trait must be specified + +#[cfg(real)] +fn dynamic() -> Box> {} +//[real]~^ ERROR `use<...>` precise capturing syntax not allowed in `dyn` trait object bounds +//[real]~| ERROR at least one trait is required for an object type [E0224] + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/ordering.rs b/tests/ui/impl-trait/precise-capturing/ordering.rs index 2bace798c570..eb570a120cc4 100644 --- a/tests/ui/impl-trait/precise-capturing/ordering.rs +++ b/tests/ui/impl-trait/precise-capturing/ordering.rs @@ -1,16 +1,15 @@ #![feature(precise_capturing)] -//~^ WARN the feature `precise_capturing` is incomplete -fn lt<'a>() -> impl use<'a, 'a> Sized {} +fn lt<'a>() -> impl Sized + use<'a, 'a> {} //~^ ERROR cannot capture parameter `'a` twice -fn ty() -> impl use Sized {} +fn ty() -> impl Sized + use {} //~^ ERROR cannot capture parameter `T` twice -fn ct() -> impl use Sized {} +fn ct() -> impl Sized + use {} //~^ ERROR cannot capture parameter `N` twice -fn ordering<'a, T>() -> impl use Sized {} +fn ordering<'a, T>() -> impl Sized + use {} //~^ ERROR lifetime parameter `'a` must be listed before non-lifetime parameters fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/ordering.stderr b/tests/ui/impl-trait/precise-capturing/ordering.stderr index 3f545108df56..ecd47159059b 100644 --- a/tests/ui/impl-trait/precise-capturing/ordering.stderr +++ b/tests/ui/impl-trait/precise-capturing/ordering.stderr @@ -1,37 +1,28 @@ -warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/ordering.rs:1:12 - | -LL | #![feature(precise_capturing)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #123432 for more information - = note: `#[warn(incomplete_features)]` on by default - error: cannot capture parameter `'a` twice - --> $DIR/ordering.rs:4:25 + --> $DIR/ordering.rs:3:33 | -LL | fn lt<'a>() -> impl use<'a, 'a> Sized {} - | ^^ -- parameter captured again here +LL | fn lt<'a>() -> impl Sized + use<'a, 'a> {} + | ^^ -- parameter captured again here error: cannot capture parameter `T` twice - --> $DIR/ordering.rs:7:24 + --> $DIR/ordering.rs:6:32 | -LL | fn ty() -> impl use Sized {} - | ^ - parameter captured again here +LL | fn ty() -> impl Sized + use {} + | ^ - parameter captured again here error: cannot capture parameter `N` twice - --> $DIR/ordering.rs:10:37 + --> $DIR/ordering.rs:9:45 | -LL | fn ct() -> impl use Sized {} - | ^ - parameter captured again here +LL | fn ct() -> impl Sized + use {} + | ^ - parameter captured again here error: lifetime parameter `'a` must be listed before non-lifetime parameters - --> $DIR/ordering.rs:13:37 + --> $DIR/ordering.rs:12:45 | -LL | fn ordering<'a, T>() -> impl use Sized {} - | - ^^ - | | - | move the lifetime before this parameter +LL | fn ordering<'a, T>() -> impl Sized + use {} + | - ^^ + | | + | move the lifetime before this parameter -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 4 previous errors diff --git a/tests/ui/impl-trait/precise-capturing/outlives.rs b/tests/ui/impl-trait/precise-capturing/outlives.rs index 71e6333934e7..26ac922b5b9d 100644 --- a/tests/ui/impl-trait/precise-capturing/outlives.rs +++ b/tests/ui/impl-trait/precise-capturing/outlives.rs @@ -3,9 +3,8 @@ // Show that precise captures allow us to skip a lifetime param for outlives #![feature(lifetime_capture_rules_2024, precise_capturing)] -//~^ WARN the feature `precise_capturing` is incomplete -fn hello<'a: 'a, 'b: 'b>() -> impl use<'a> Sized { } +fn hello<'a: 'a, 'b: 'b>() -> impl Sized + use<'a> { } fn outlives<'a, T: 'a>(_: T) {} diff --git a/tests/ui/impl-trait/precise-capturing/outlives.stderr b/tests/ui/impl-trait/precise-capturing/outlives.stderr deleted file mode 100644 index 405c09cccd94..000000000000 --- a/tests/ui/impl-trait/precise-capturing/outlives.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/outlives.rs:5:41 - | -LL | #![feature(lifetime_capture_rules_2024, precise_capturing)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #123432 for more information - = note: `#[warn(incomplete_features)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed index 014ab23e4ebc..5ac296a9cbdf 100644 --- a/tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed +++ b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed @@ -4,15 +4,15 @@ #![allow(unused, incomplete_features)] #![deny(impl_trait_overcaptures)] -fn named<'a>(x: &'a i32) -> impl use<> Sized { *x } +fn named<'a>(x: &'a i32) -> impl Sized + use<> { *x } //~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024 -fn implicit(x: &i32) -> impl use<> Sized { *x } +fn implicit(x: &i32) -> impl Sized + use<> { *x } //~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024 struct W; impl W { - fn hello(&self, x: &i32) -> impl use<'_> Sized + '_ { self } + fn hello(&self, x: &i32) -> impl Sized + '_ + use<'_> { self } //~^ ERROR `impl Sized + '_` will capture more lifetimes than possibly intended in edition 2024 } @@ -23,7 +23,7 @@ impl Higher<'_> for () { type Output = (); } -fn hrtb() -> impl for<'a> Higher<'a, Output = impl use<> Sized> {} +fn hrtb() -> impl for<'a> Higher<'a, Output = impl Sized + use<>> {} //~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024 fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr index 16cb8b7e94b1..f8bb7f099af9 100644 --- a/tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr +++ b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr @@ -17,8 +17,8 @@ LL | #![deny(impl_trait_overcaptures)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the precise capturing `use<...>` syntax to make the captures explicit | -LL | fn named<'a>(x: &'a i32) -> impl use<> Sized { *x } - | +++++ +LL | fn named<'a>(x: &'a i32) -> impl Sized + use<> { *x } + | +++++++ error: `impl Sized` will capture more lifetimes than possibly intended in edition 2024 --> $DIR/overcaptures-2024.rs:10:25 @@ -34,8 +34,8 @@ LL | fn implicit(x: &i32) -> impl Sized { *x } = note: all lifetimes in scope will be captured by `impl Trait`s in edition 2024 help: use the precise capturing `use<...>` syntax to make the captures explicit | -LL | fn implicit(x: &i32) -> impl use<> Sized { *x } - | +++++ +LL | fn implicit(x: &i32) -> impl Sized + use<> { *x } + | +++++++ error: `impl Sized + '_` will capture more lifetimes than possibly intended in edition 2024 --> $DIR/overcaptures-2024.rs:15:33 @@ -51,8 +51,8 @@ LL | fn hello(&self, x: &i32) -> impl Sized + '_ { self } = note: all lifetimes in scope will be captured by `impl Trait`s in edition 2024 help: use the precise capturing `use<...>` syntax to make the captures explicit | -LL | fn hello(&self, x: &i32) -> impl use<'_> Sized + '_ { self } - | +++++++ +LL | fn hello(&self, x: &i32) -> impl Sized + '_ + use<'_> { self } + | +++++++++ error: `impl Sized` will capture more lifetimes than possibly intended in edition 2024 --> $DIR/overcaptures-2024.rs:26:47 @@ -68,8 +68,8 @@ LL | fn hrtb() -> impl for<'a> Higher<'a, Output = impl Sized> {} = note: all lifetimes in scope will be captured by `impl Trait`s in edition 2024 help: use the precise capturing `use<...>` syntax to make the captures explicit | -LL | fn hrtb() -> impl for<'a> Higher<'a, Output = impl use<> Sized> {} - | +++++ +LL | fn hrtb() -> impl for<'a> Higher<'a, Output = impl Sized + use<>> {} + | +++++++ error: aborting due to 4 previous errors diff --git a/tests/ui/impl-trait/precise-capturing/redundant.rs b/tests/ui/impl-trait/precise-capturing/redundant.rs index 108a4cb64aa8..99c128fdc482 100644 --- a/tests/ui/impl-trait/precise-capturing/redundant.rs +++ b/tests/ui/impl-trait/precise-capturing/redundant.rs @@ -2,23 +2,22 @@ //@ check-pass #![feature(precise_capturing)] -//~^ WARN the feature `precise_capturing` is incomplete -fn hello<'a>() -> impl use<'a> Sized {} +fn hello<'a>() -> impl Sized + use<'a> {} //~^ WARN all possible in-scope parameters are already captured struct Inherent; impl Inherent { - fn inherent(&self) -> impl use<'_> Sized {} + fn inherent(&self) -> impl Sized + use<'_> {} //~^ WARN all possible in-scope parameters are already captured } trait Test<'a> { - fn in_trait() -> impl use<'a, Self> Sized; + fn in_trait() -> impl Sized + use<'a, Self>; //~^ WARN all possible in-scope parameters are already captured } impl<'a> Test<'a> for () { - fn in_trait() -> impl use<'a> Sized {} + fn in_trait() -> impl Sized + use<'a> {} //~^ WARN all possible in-scope parameters are already captured } diff --git a/tests/ui/impl-trait/precise-capturing/redundant.stderr b/tests/ui/impl-trait/precise-capturing/redundant.stderr index 325f04d3536a..274d9d2375f7 100644 --- a/tests/ui/impl-trait/precise-capturing/redundant.stderr +++ b/tests/ui/impl-trait/precise-capturing/redundant.stderr @@ -1,45 +1,36 @@ -warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/redundant.rs:4:12 - | -LL | #![feature(precise_capturing)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #123432 for more information - = note: `#[warn(incomplete_features)]` on by default - warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant - --> $DIR/redundant.rs:7:19 + --> $DIR/redundant.rs:6:19 | -LL | fn hello<'a>() -> impl use<'a> Sized {} - | ^^^^^-------^^^^^^ - | | - | help: remove the `use<...>` syntax +LL | fn hello<'a>() -> impl Sized + use<'a> {} + | ^^^^^^^^^^^^^------- + | | + | help: remove the `use<...>` syntax | = note: `#[warn(impl_trait_redundant_captures)]` on by default warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant - --> $DIR/redundant.rs:12:27 + --> $DIR/redundant.rs:11:27 | -LL | fn inherent(&self) -> impl use<'_> Sized {} - | ^^^^^-------^^^^^^ - | | - | help: remove the `use<...>` syntax +LL | fn inherent(&self) -> impl Sized + use<'_> {} + | ^^^^^^^^^^^^^------- + | | + | help: remove the `use<...>` syntax warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant - --> $DIR/redundant.rs:17:22 + --> $DIR/redundant.rs:16:22 | -LL | fn in_trait() -> impl use<'a, Self> Sized; - | ^^^^^-------------^^^^^^ - | | - | help: remove the `use<...>` syntax +LL | fn in_trait() -> impl Sized + use<'a, Self>; + | ^^^^^^^^^^^^^------------- + | | + | help: remove the `use<...>` syntax warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant - --> $DIR/redundant.rs:21:22 + --> $DIR/redundant.rs:20:22 | -LL | fn in_trait() -> impl use<'a> Sized {} - | ^^^^^-------^^^^^^ - | | - | help: remove the `use<...>` syntax +LL | fn in_trait() -> impl Sized + use<'a> {} + | ^^^^^^^^^^^^^------- + | | + | help: remove the `use<...>` syntax -warning: 5 warnings emitted +warning: 4 warnings emitted diff --git a/tests/ui/impl-trait/precise-capturing/self-capture.rs b/tests/ui/impl-trait/precise-capturing/self-capture.rs index ecbc388e27be..e0a4a8b658c5 100644 --- a/tests/ui/impl-trait/precise-capturing/self-capture.rs +++ b/tests/ui/impl-trait/precise-capturing/self-capture.rs @@ -1,10 +1,9 @@ //@ check-pass #![feature(precise_capturing)] -//~^ WARN the feature `precise_capturing` is incomplete trait Foo { - fn bar<'a>() -> impl use Sized; + fn bar<'a>() -> impl Sized + use; } fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/self-capture.stderr b/tests/ui/impl-trait/precise-capturing/self-capture.stderr deleted file mode 100644 index 5a058c6826d2..000000000000 --- a/tests/ui/impl-trait/precise-capturing/self-capture.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/self-capture.rs:3:12 - | -LL | #![feature(precise_capturing)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #123432 for more information - = note: `#[warn(incomplete_features)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/impl-trait/precise-capturing/unexpected-token.rs b/tests/ui/impl-trait/precise-capturing/unexpected-token.rs index 39c8c0def6bf..a1089fd7bfc5 100644 --- a/tests/ui/impl-trait/precise-capturing/unexpected-token.rs +++ b/tests/ui/impl-trait/precise-capturing/unexpected-token.rs @@ -2,8 +2,7 @@ // token due to a strange interaction between the sequence parsing code and the // param/lifetime parsing code. -fn hello() -> impl use<'a {}> Sized {} +fn hello() -> impl Sized + use<'a {}> {} //~^ ERROR expected one of `,` or `>`, found `{` -//~| ERROR expected item, found `>` fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/unexpected-token.stderr b/tests/ui/impl-trait/precise-capturing/unexpected-token.stderr index 989c479b2484..51e4f0f67754 100644 --- a/tests/ui/impl-trait/precise-capturing/unexpected-token.stderr +++ b/tests/ui/impl-trait/precise-capturing/unexpected-token.stderr @@ -1,16 +1,8 @@ error: expected one of `,` or `>`, found `{` - --> $DIR/unexpected-token.rs:5:27 + --> $DIR/unexpected-token.rs:5:35 | -LL | fn hello() -> impl use<'a {}> Sized {} - | ^ expected one of `,` or `>` +LL | fn hello() -> impl Sized + use<'a {}> {} + | ^ expected one of `,` or `>` -error: expected item, found `>` - --> $DIR/unexpected-token.rs:5:29 - | -LL | fn hello() -> impl use<'a {}> Sized {} - | ^ expected item - | - = note: for a full list of items that can appear in modules, see - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/recursive-bound-eval.next.stderr b/tests/ui/impl-trait/recursive-bound-eval.next.stderr new file mode 100644 index 000000000000..4bab290d71c3 --- /dev/null +++ b/tests/ui/impl-trait/recursive-bound-eval.next.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/recursive-bound-eval.rs:20:13 + | +LL | move || recursive_fn().parse() + | ^^^^^^^^^^^^^^ cannot infer type + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/recursive-bound-eval.rs b/tests/ui/impl-trait/recursive-bound-eval.rs new file mode 100644 index 000000000000..7859c8983fc8 --- /dev/null +++ b/tests/ui/impl-trait/recursive-bound-eval.rs @@ -0,0 +1,24 @@ +//! Test that we can evaluate nested obligations when invoking methods on recursive calls on +//! an RPIT. + +//@revisions: next current +//@[next] compile-flags: -Znext-solver + +//@[current] check-pass + +pub trait Parser { + fn parse(&self) -> E; +} + +impl E> Parser for T { + fn parse(&self) -> E { + self() + } +} + +pub fn recursive_fn() -> impl Parser { + move || recursive_fn().parse() + //[next]~^ ERROR: type annotations needed +} + +fn main() {} diff --git a/tests/ui/impl-trait/recursive-parent-trait-method-call.rs b/tests/ui/impl-trait/recursive-parent-trait-method-call.rs new file mode 100644 index 000000000000..4da9a06a4659 --- /dev/null +++ b/tests/ui/impl-trait/recursive-parent-trait-method-call.rs @@ -0,0 +1,42 @@ +//! This test checks that we can resolve the `boxed` method call to `FutureExt`, +//! because we know that the anonymous future does not implement `StreamExt`. + +//@ edition: 2021 +//@ check-pass + +use std::future::Future; +use std::pin::Pin; + +trait FutureExt: Future + Sized + Send + 'static { + fn boxed(self) -> Pin + Send + 'static>> { + Box::pin(self) + } +} + +trait StreamExt: Future + Sized + Send + 'static { + fn boxed(self) -> Pin + Send + 'static>> { + Box::pin(self) + } +} + +impl FutureExt for T {} + +fn go(i: usize) -> impl Future + Send + 'static { + async move { + if i != 0 { + spawn(async move { + let fut = go(i - 1).boxed(); + fut.await; + }) + .await; + } + } +} + +pub fn spawn( + _: impl Future + Send + 'static, +) -> impl Future + Send + 'static { + async move { todo!() } +} + +fn main() {} diff --git a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs index b369544782c7..0b29af5df5b5 100644 --- a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs +++ b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.rs @@ -2,7 +2,14 @@ //@ check-pass -type Foo = impl PartialEq<(Foo, i32)>; +mod foo { + pub type Foo = impl PartialEq<(Foo, i32)>; + + fn foo() -> Foo { + super::Bar + } +} +use foo::Foo; struct Bar; @@ -12,8 +19,4 @@ impl PartialEq<(Foo, i32)> for Bar { } } -fn foo() -> Foo { - Bar -} - fn main() {} diff --git a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.rs b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.rs index 91f1ed48133f..dc9424c3ca7a 100644 --- a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.rs +++ b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.rs @@ -9,6 +9,7 @@ mod a { impl PartialEq<(Bar, i32)> for Bar { fn eq(&self, _other: &(Foo, i32)) -> bool { //~^ ERROR: `eq` has an incompatible type for trait + //~| ERROR: item does not constrain `a::Foo::{opaque#0}` true } } diff --git a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr index 07ac1a37e75c..4352af1c0df7 100644 --- a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr +++ b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr @@ -1,3 +1,16 @@ +error: item does not constrain `a::Foo::{opaque#0}`, but has it in its signature + --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:10:12 + | +LL | fn eq(&self, _other: &(Foo, i32)) -> bool { + | ^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:4:16 + | +LL | type Foo = impl PartialEq<(Foo, i32)>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: unconstrained opaque type --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:4:16 | @@ -22,7 +35,7 @@ LL | fn eq(&self, _other: &(Foo, i32)) -> bool { found signature `fn(&a::Bar, &(a::Foo, _)) -> _` error: unconstrained opaque type - --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:18:16 + --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:19:16 | LL | type Foo = impl PartialEq<(Foo, i32)>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -30,7 +43,7 @@ LL | type Foo = impl PartialEq<(Foo, i32)>; = note: `Foo` must be used in combination with a concrete type within the same module error[E0053]: method `eq` has an incompatible type for trait - --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:24:30 + --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:25:30 | LL | type Foo = impl PartialEq<(Foo, i32)>; | -------------------------- the expected opaque type @@ -44,11 +57,11 @@ LL | fn eq(&self, _other: &(Bar, i32)) -> bool { = note: expected signature `fn(&b::Bar, &(b::Foo, _)) -> _` found signature `fn(&b::Bar, &(b::Bar, _)) -> _` note: this item must have the opaque type in its signature in order to be able to register hidden types - --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:24:12 + --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:25:12 | LL | fn eq(&self, _other: &(Bar, i32)) -> bool { | ^^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.current.stderr b/tests/ui/impl-trait/two_tait_defining_each_other2.current.stderr index 6f2b2d9ca042..b70676ece0e2 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other2.current.stderr +++ b/tests/ui/impl-trait/two_tait_defining_each_other2.current.stderr @@ -1,3 +1,16 @@ +error: item does not constrain `A::{opaque#0}`, but has it in its signature + --> $DIR/two_tait_defining_each_other2.rs:11:4 + | +LL | fn muh(x: A) -> B { + | ^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/two_tait_defining_each_other2.rs:6:10 + | +LL | type A = impl Foo; + | ^^^^^^^^ + error: unconstrained opaque type --> $DIR/two_tait_defining_each_other2.rs:6:10 | @@ -7,7 +20,7 @@ LL | type A = impl Foo; = note: `A` must be used in combination with a concrete type within the same module error: opaque type's hidden type cannot be another opaque type from the same scope - --> $DIR/two_tait_defining_each_other2.rs:13:5 + --> $DIR/two_tait_defining_each_other2.rs:14:5 | LL | x // B's hidden type is A (opaquely) | ^ one of the two opaque types used here has to be outside its defining scope @@ -23,5 +36,5 @@ note: opaque type being used as hidden type LL | type A = impl Foo; | ^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.rs b/tests/ui/impl-trait/two_tait_defining_each_other2.rs index e850736fdfaf..3311c5565681 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other2.rs +++ b/tests/ui/impl-trait/two_tait_defining_each_other2.rs @@ -9,7 +9,8 @@ type B = impl Foo; trait Foo {} fn muh(x: A) -> B { - //[next]~^ ERROR type annotations needed: cannot satisfy `_ == A` + //[current]~^ ERROR: item does not constrain `A::{opaque#0}` + //[next]~^^ ERROR: cannot satisfy `_ == A` x // B's hidden type is A (opaquely) //[current]~^ ERROR opaque type's hidden type cannot be another opaque type } diff --git a/tests/ui/impl-trait/type-alias-impl-trait-in-fn-body.rs b/tests/ui/impl-trait/type-alias-impl-trait-in-fn-body.rs index 926998315809..4879d2db40b8 100644 --- a/tests/ui/impl-trait/type-alias-impl-trait-in-fn-body.rs +++ b/tests/ui/impl-trait/type-alias-impl-trait-in-fn-body.rs @@ -1,10 +1,12 @@ -//@ build-pass (FIXME(62277): could be check-pass?) +//! Test that it is basically not possible to declare *and opaquely use* opaque types +//! in function bodies. This will work again once we have a `#[defines]` attribute #![feature(type_alias_impl_trait)] use std::fmt::Debug; fn main() { + //~^ ERROR: item does not constrain type Existential = impl Debug; fn f() -> Existential {} diff --git a/tests/ui/impl-trait/type-alias-impl-trait-in-fn-body.stderr b/tests/ui/impl-trait/type-alias-impl-trait-in-fn-body.stderr new file mode 100644 index 000000000000..7744fa2f2ae6 --- /dev/null +++ b/tests/ui/impl-trait/type-alias-impl-trait-in-fn-body.stderr @@ -0,0 +1,15 @@ +error: item does not constrain `Existential::{opaque#0}`, but has it in its signature + --> $DIR/type-alias-impl-trait-in-fn-body.rs:8:4 + | +LL | fn main() { + | ^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/type-alias-impl-trait-in-fn-body.rs:10:24 + | +LL | type Existential = impl Debug; + | ^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/impl-trait/unsize_adt.rs b/tests/ui/impl-trait/unsize_adt.rs new file mode 100644 index 000000000000..fce5094bd404 --- /dev/null +++ b/tests/ui/impl-trait/unsize_adt.rs @@ -0,0 +1,14 @@ +//! Test that we do not allow unsizing `Foo<[Opaque; N]>` to `Foo<[Concrete]>`. + +struct Foo(T); + +fn hello() -> Foo<[impl Sized; 2]> { + if false { + let x = hello(); + let _: &Foo<[i32]> = &x; + //~^ ERROR: mismatched types + } + todo!() +} + +fn main() {} diff --git a/tests/ui/impl-trait/unsize_adt.stderr b/tests/ui/impl-trait/unsize_adt.stderr new file mode 100644 index 000000000000..2b93b4a571b1 --- /dev/null +++ b/tests/ui/impl-trait/unsize_adt.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/unsize_adt.rs:8:30 + | +LL | fn hello() -> Foo<[impl Sized; 2]> { + | ---------- the found opaque type +... +LL | let _: &Foo<[i32]> = &x; + | ----------- ^^ expected `&Foo<[i32]>`, found `&Foo<[impl Sized; 2]>` + | | + | expected due to this + | + = note: expected reference `&Foo<[i32]>` + found reference `&Foo<[impl Sized; 2]>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/impl-trait/unsize_slice.rs b/tests/ui/impl-trait/unsize_slice.rs new file mode 100644 index 000000000000..ec0f16265640 --- /dev/null +++ b/tests/ui/impl-trait/unsize_slice.rs @@ -0,0 +1,12 @@ +//! Test that we do not allow unsizing `[Opaque; N]` to `[Concrete]`. + +fn hello() -> [impl Sized; 2] { + if false { + let x = hello(); + let _: &[i32] = &x; + //~^ ERROR: mismatched types + } + todo!() +} + +fn main() {} diff --git a/tests/ui/impl-trait/unsize_slice.stderr b/tests/ui/impl-trait/unsize_slice.stderr new file mode 100644 index 000000000000..6a7fdb46163b --- /dev/null +++ b/tests/ui/impl-trait/unsize_slice.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/unsize_slice.rs:6:25 + | +LL | fn hello() -> [impl Sized; 2] { + | ---------- the found opaque type +... +LL | let _: &[i32] = &x; + | ------ ^^ expected `&[i32]`, found `&[impl Sized; 2]` + | | + | expected due to this + | + = note: expected reference `&[i32]` + found reference `&[impl Sized; 2]` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/impl-trait/unsize_tuple.rs b/tests/ui/impl-trait/unsize_tuple.rs new file mode 100644 index 000000000000..630b8fd430f1 --- /dev/null +++ b/tests/ui/impl-trait/unsize_tuple.rs @@ -0,0 +1,14 @@ +//! Test that we do not allow unsizing `([Opaque; N],)` to `([Concrete],)`. + +#![feature(unsized_tuple_coercion)] + +fn hello() -> ([impl Sized; 2],) { + if false { + let x = hello(); + let _: &([i32],) = &x; + //~^ ERROR: mismatched types + } + todo!() +} + +fn main() {} diff --git a/tests/ui/impl-trait/unsize_tuple.stderr b/tests/ui/impl-trait/unsize_tuple.stderr new file mode 100644 index 000000000000..8d3cf15887c0 --- /dev/null +++ b/tests/ui/impl-trait/unsize_tuple.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/unsize_tuple.rs:8:28 + | +LL | fn hello() -> ([impl Sized; 2],) { + | ---------- the found opaque type +... +LL | let _: &([i32],) = &x; + | --------- ^^ expected `&([i32],)`, found `&([impl Sized; 2],)` + | | + | expected due to this + | + = note: expected reference `&([i32],)` + found reference `&([impl Sized; 2],)` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/imports/auxiliary/simple-dylib.rs b/tests/ui/imports/auxiliary/simple-dylib.rs new file mode 100644 index 000000000000..af64aa66f31b --- /dev/null +++ b/tests/ui/imports/auxiliary/simple-dylib.rs @@ -0,0 +1,4 @@ +//@ compile-flags: -Cprefer-dynamic + +#![crate_type = "dylib"] +pub fn bar() {} diff --git a/tests/ui/imports/auxiliary/simple-rlib.rs b/tests/ui/imports/auxiliary/simple-rlib.rs new file mode 100644 index 000000000000..28a6273840f6 --- /dev/null +++ b/tests/ui/imports/auxiliary/simple-rlib.rs @@ -0,0 +1,2 @@ +#![crate_type = "rlib"] +pub fn bar() {} diff --git a/tests/ui/imports/cycle-import-in-diff-module-0.rs b/tests/ui/imports/cycle-import-in-diff-module-0.rs new file mode 100644 index 000000000000..962603a89716 --- /dev/null +++ b/tests/ui/imports/cycle-import-in-diff-module-0.rs @@ -0,0 +1,14 @@ +//@ check-pass + +// https://github.com/rust-lang/rust/pull/124840#issuecomment-2098148587 + +mod a { + pub(crate) use crate::S; +} +mod b { + pub struct S; +} +use self::a::S; +use self::b::*; + +fn main() {} diff --git a/tests/ui/imports/cycle-import-in-diff-module-1.rs b/tests/ui/imports/cycle-import-in-diff-module-1.rs new file mode 100644 index 000000000000..8c67df376c3f --- /dev/null +++ b/tests/ui/imports/cycle-import-in-diff-module-1.rs @@ -0,0 +1,14 @@ +//@ check-pass + +// similar `cycle-import-in-diff-module-0.rs` + +mod a { + pub(crate) use crate::s; +} +mod b { + pub mod s {} +} +use self::b::*; +use self::a::s; + +fn main() {} diff --git a/tests/ui/imports/cycle-import-in-std-1.rs b/tests/ui/imports/cycle-import-in-std-1.rs new file mode 100644 index 000000000000..2fa492c155d7 --- /dev/null +++ b/tests/ui/imports/cycle-import-in-std-1.rs @@ -0,0 +1,9 @@ +//@ edition: 2018 + +// https://github.com/rust-lang/rust/issues/124490 + +use ops::{self as std}; +//~^ ERROR: unresolved import `ops` +use std::collections::{self as ops}; + +fn main() {} diff --git a/tests/ui/imports/cycle-import-in-std-1.stderr b/tests/ui/imports/cycle-import-in-std-1.stderr new file mode 100644 index 000000000000..a7dfc6231bac --- /dev/null +++ b/tests/ui/imports/cycle-import-in-std-1.stderr @@ -0,0 +1,12 @@ +error[E0432]: unresolved import `ops` + --> $DIR/cycle-import-in-std-1.rs:5:11 + | +LL | use ops::{self as std}; + | ^^^^^^^^^^^ no external crate `ops` + | + = help: consider importing this module instead: + std::ops + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/cycle-import-in-std-2.rs b/tests/ui/imports/cycle-import-in-std-2.rs new file mode 100644 index 000000000000..d73c57944bc2 --- /dev/null +++ b/tests/ui/imports/cycle-import-in-std-2.rs @@ -0,0 +1,9 @@ +//@ edition: 2018 + +// https://github.com/rust-lang/rust/issues/125013 + +use ops::{self as std}; +//~^ ERROR: unresolved import `ops` +use std::ops::Deref::{self as ops}; + +fn main() {} diff --git a/tests/ui/imports/cycle-import-in-std-2.stderr b/tests/ui/imports/cycle-import-in-std-2.stderr new file mode 100644 index 000000000000..8d94693cd51d --- /dev/null +++ b/tests/ui/imports/cycle-import-in-std-2.stderr @@ -0,0 +1,12 @@ +error[E0432]: unresolved import `ops` + --> $DIR/cycle-import-in-std-2.rs:5:11 + | +LL | use ops::{self as std}; + | ^^^^^^^^^^^ no external crate `ops` + | + = help: consider importing this module instead: + std::ops + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/import-alias-issue-121168.edition2018.stderr b/tests/ui/imports/import-alias-issue-121168.edition2018.stderr index b61a0e3edd52..e14e700c33d8 100644 --- a/tests/ui/imports/import-alias-issue-121168.edition2018.stderr +++ b/tests/ui/imports/import-alias-issue-121168.edition2018.stderr @@ -4,7 +4,7 @@ error[E0412]: cannot find type `Foo` in this scope LL | let _: Foo = todo!(); | ^^^ not found in this scope | -help: consider importing one of these items +help: consider importing one of these structs | LL + use crate::nice_crate_name::Foo; | diff --git a/tests/ui/imports/import-alias-issue-121168.edition2021.stderr b/tests/ui/imports/import-alias-issue-121168.edition2021.stderr index b61a0e3edd52..e14e700c33d8 100644 --- a/tests/ui/imports/import-alias-issue-121168.edition2021.stderr +++ b/tests/ui/imports/import-alias-issue-121168.edition2021.stderr @@ -4,7 +4,7 @@ error[E0412]: cannot find type `Foo` in this scope LL | let _: Foo = todo!(); | ^^^ not found in this scope | -help: consider importing one of these items +help: consider importing one of these structs | LL + use crate::nice_crate_name::Foo; | diff --git a/tests/ui/imports/import-from-missing-star-2.rs b/tests/ui/imports/import-from-missing-star-2.rs new file mode 100644 index 000000000000..cb341b0b0ca4 --- /dev/null +++ b/tests/ui/imports/import-from-missing-star-2.rs @@ -0,0 +1,12 @@ +mod foo { + use spam::*; //~ ERROR unresolved import `spam` [E0432] +} + +fn main() { + // Expect this to pass because the compiler knows there's a failed `*` import in `foo` that + // might have caused it. + foo::bar(); + // FIXME: these two should *fail* because they can't be fixed by fixing the glob import in `foo` + ham(); // should error but doesn't + eggs(); // should error but doesn't +} diff --git a/tests/ui/imports/import-from-missing-star-2.stderr b/tests/ui/imports/import-from-missing-star-2.stderr new file mode 100644 index 000000000000..ea3876248c93 --- /dev/null +++ b/tests/ui/imports/import-from-missing-star-2.stderr @@ -0,0 +1,11 @@ +error[E0432]: unresolved import `spam` + --> $DIR/import-from-missing-star-2.rs:2:9 + | +LL | use spam::*; + | ^^^^ maybe a missing crate `spam`? + | + = help: consider adding `extern crate spam` to use the `spam` crate + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/import-from-missing-star-3.rs b/tests/ui/imports/import-from-missing-star-3.rs new file mode 100644 index 000000000000..bec51fd47b37 --- /dev/null +++ b/tests/ui/imports/import-from-missing-star-3.rs @@ -0,0 +1,43 @@ +mod foo { + use spam::*; //~ ERROR unresolved import `spam` [E0432] + + fn x() { + // Expect these to pass because the compiler knows there's a failed `*` import that might + // fix it. + eggs(); + foo::bar(); + } +} + +mod bar { + fn z() {} + fn x() { + // Expect these to pass because the compiler knows there's a failed `*` import that might + // fix it. + foo::bar(); + z(); + // FIXME: should error but doesn't because as soon as there's a single glob import error, we + // silence all resolve errors. + eggs(); + } +} + +mod baz { + fn x() { + use spam::*; //~ ERROR unresolved import `spam` [E0432] + fn qux() {} + qux(); + // Expect this to pass because the compiler knows there's a local failed `*` import that + // might have caused it. + eggs(); + // Expect this to pass because the compiler knows there's a failed `*` import in `foo` that + // might have caused it. + foo::bar(); + } +} + +fn main() { + // FIXME: should error but doesn't because as soon as there's a single glob import error, we + // silence all resolve errors. + ham(); +} diff --git a/tests/ui/imports/import-from-missing-star-3.stderr b/tests/ui/imports/import-from-missing-star-3.stderr new file mode 100644 index 000000000000..1fe5d4f19a97 --- /dev/null +++ b/tests/ui/imports/import-from-missing-star-3.stderr @@ -0,0 +1,19 @@ +error[E0432]: unresolved import `spam` + --> $DIR/import-from-missing-star-3.rs:2:9 + | +LL | use spam::*; + | ^^^^ maybe a missing crate `spam`? + | + = help: consider adding `extern crate spam` to use the `spam` crate + +error[E0432]: unresolved import `spam` + --> $DIR/import-from-missing-star-3.rs:27:13 + | +LL | use spam::*; + | ^^^^ maybe a missing crate `spam`? + | + = help: consider adding `extern crate spam` to use the `spam` crate + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/import-from-missing-star.rs b/tests/ui/imports/import-from-missing-star.rs new file mode 100644 index 000000000000..cb21e16ba677 --- /dev/null +++ b/tests/ui/imports/import-from-missing-star.rs @@ -0,0 +1,10 @@ +use spam::*; //~ ERROR unresolved import `spam` [E0432] + +fn main() { + // Expect these to pass because the compiler knows there's a failed `*` import that might have + // caused it. + ham(); + eggs(); + // Even this case, as we might have expected `spam::foo` to exist. + foo::bar(); +} diff --git a/tests/ui/imports/import-from-missing-star.stderr b/tests/ui/imports/import-from-missing-star.stderr new file mode 100644 index 000000000000..f8e295078047 --- /dev/null +++ b/tests/ui/imports/import-from-missing-star.stderr @@ -0,0 +1,11 @@ +error[E0432]: unresolved import `spam` + --> $DIR/import-from-missing-star.rs:1:5 + | +LL | use spam::*; + | ^^^^ maybe a missing crate `spam`? + | + = help: consider adding `extern crate spam` to use the `spam` crate + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/issue-31212.rs b/tests/ui/imports/issue-31212.rs index 556f0d18f9f2..fe69c5af2706 100644 --- a/tests/ui/imports/issue-31212.rs +++ b/tests/ui/imports/issue-31212.rs @@ -6,5 +6,5 @@ mod foo { } fn main() { - foo::f(); //~ ERROR cannot find function `f` in module `foo` + foo::f(); // cannot find function `f` in module `foo`, but silenced } diff --git a/tests/ui/imports/issue-31212.stderr b/tests/ui/imports/issue-31212.stderr index 0bb56b361cbb..5bba791fd020 100644 --- a/tests/ui/imports/issue-31212.stderr +++ b/tests/ui/imports/issue-31212.stderr @@ -4,13 +4,6 @@ error[E0432]: unresolved import `self::*` LL | pub use self::*; | ^^^^^^^ cannot glob-import a module into itself -error[E0425]: cannot find function `f` in module `foo` - --> $DIR/issue-31212.rs:9:10 - | -LL | foo::f(); - | ^ not found in `foo` +error: aborting due to 1 previous error -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0425, E0432. -For more information about an error, try `rustc --explain E0425`. +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/issue-37887.rs b/tests/ui/imports/issue-37887.rs index 58f0c6b651ad..919f46d34c6d 100644 --- a/tests/ui/imports/issue-37887.rs +++ b/tests/ui/imports/issue-37887.rs @@ -1,4 +1,4 @@ fn main() { - extern crate libc; //~ ERROR use of unstable - use libc::*; //~ ERROR unresolved import + extern crate test; //~ ERROR use of unstable + use test::*; //~ ERROR unresolved import } diff --git a/tests/ui/imports/issue-37887.stderr b/tests/ui/imports/issue-37887.stderr index 6117fd21af18..e7792ac0d159 100644 --- a/tests/ui/imports/issue-37887.stderr +++ b/tests/ui/imports/issue-37887.stderr @@ -1,19 +1,19 @@ -error[E0432]: unresolved import `libc` +error[E0432]: unresolved import `test` --> $DIR/issue-37887.rs:3:9 | -LL | use libc::*; - | ^^^^ maybe a missing crate `libc`? +LL | use test::*; + | ^^^^ maybe a missing crate `test`? | - = help: consider adding `extern crate libc` to use the `libc` crate + = help: consider adding `extern crate test` to use the `test` crate -error[E0658]: use of unstable library feature 'rustc_private': this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead? +error[E0658]: use of unstable library feature 'test' --> $DIR/issue-37887.rs:2:5 | -LL | extern crate libc; +LL | extern crate test; | ^^^^^^^^^^^^^^^^^^ | - = note: see issue #27812 for more information - = help: add `#![feature(rustc_private)]` to the crate attributes to enable + = note: see issue #50297 for more information + = help: add `#![feature(test)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 2 previous errors diff --git a/tests/ui/imports/issue-56125.stderr b/tests/ui/imports/issue-56125.stderr index d2a0f436c42d..0c4a569c7ea7 100644 --- a/tests/ui/imports/issue-56125.stderr +++ b/tests/ui/imports/issue-56125.stderr @@ -4,16 +4,16 @@ error[E0432]: unresolved import `empty::issue_56125` LL | use empty::issue_56125; | ^^^^^^^^^^^^^^^^^^ no `issue_56125` in `m3::empty` | -help: consider importing one of these items instead +help: consider importing one of these modules instead | +LL | use crate::m3::last_segment::issue_56125; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | use crate::m3::non_last_segment::non_last_segment::issue_56125; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LL | use ::issue_56125::issue_56125; | ~~~~~~~~~~~~~~~~~~~~~~~~~~ LL | use ::issue_56125::last_segment::issue_56125; | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | use ::issue_56125::non_last_segment::non_last_segment::issue_56125; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | use crate::m3::last_segment::issue_56125; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ and 1 other candidate error[E0659]: `issue_56125` is ambiguous diff --git a/tests/ui/imports/shadow-glob-module-resolution-1.rs b/tests/ui/imports/shadow-glob-module-resolution-1.rs new file mode 100644 index 000000000000..ba1e65cddc65 --- /dev/null +++ b/tests/ui/imports/shadow-glob-module-resolution-1.rs @@ -0,0 +1,17 @@ +// https://github.com/rust-lang/rust/issues/124490 + +mod a { + pub mod b { + pub mod c {} + } +} + +use a::*; + +use b::c; +//~^ ERROR: cannot determine resolution for the import +//~| ERROR: cannot determine resolution for the import +//~| ERROR: unresolved import `b::c` +use c as b; + +fn main() {} diff --git a/tests/ui/imports/shadow-glob-module-resolution-1.stderr b/tests/ui/imports/shadow-glob-module-resolution-1.stderr new file mode 100644 index 000000000000..f9135963fe99 --- /dev/null +++ b/tests/ui/imports/shadow-glob-module-resolution-1.stderr @@ -0,0 +1,23 @@ +error: cannot determine resolution for the import + --> $DIR/shadow-glob-module-resolution-1.rs:11:5 + | +LL | use b::c; + | ^^^^ + +error: cannot determine resolution for the import + --> $DIR/shadow-glob-module-resolution-1.rs:11:5 + | +LL | use b::c; + | ^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0432]: unresolved import `b::c` + --> $DIR/shadow-glob-module-resolution-1.rs:11:5 + | +LL | use b::c; + | ^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/shadow-glob-module-resolution-2.rs b/tests/ui/imports/shadow-glob-module-resolution-2.rs new file mode 100644 index 000000000000..36bd72658ae8 --- /dev/null +++ b/tests/ui/imports/shadow-glob-module-resolution-2.rs @@ -0,0 +1,19 @@ +// https://github.com/rust-lang/rust/issues/125013 + +mod a { + pub mod b { + pub mod c { + pub trait D {} + } + } +} + +use a::*; + +use e as b; +//~^ ERROR: unresolved import `e` +use b::c::D as e; +//~^ ERROR: cannot determine resolution for the import +//~| ERROR: cannot determine resolution for the import + +fn main() { } diff --git a/tests/ui/imports/shadow-glob-module-resolution-2.stderr b/tests/ui/imports/shadow-glob-module-resolution-2.stderr new file mode 100644 index 000000000000..644fcb841628 --- /dev/null +++ b/tests/ui/imports/shadow-glob-module-resolution-2.stderr @@ -0,0 +1,26 @@ +error: cannot determine resolution for the import + --> $DIR/shadow-glob-module-resolution-2.rs:15:5 + | +LL | use b::c::D as e; + | ^^^^^^^^^^^^ + +error: cannot determine resolution for the import + --> $DIR/shadow-glob-module-resolution-2.rs:15:5 + | +LL | use b::c::D as e; + | ^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0432]: unresolved import `e` + --> $DIR/shadow-glob-module-resolution-2.rs:13:5 + | +LL | use e as b; + | -^^^^^ + | | + | no `e` in the root + | help: a similar name exists in the module: `a` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/shadow-glob-module-resolution-3.rs b/tests/ui/imports/shadow-glob-module-resolution-3.rs new file mode 100644 index 000000000000..f5a43373261b --- /dev/null +++ b/tests/ui/imports/shadow-glob-module-resolution-3.rs @@ -0,0 +1,19 @@ +// https://github.com/rust-lang/rust/issues/126389 + +mod a { + pub mod b { + pub mod c {} + } +} + +use a::*; + +use b::c; +//~^ ERROR: unresolved import `b::c` +//~| ERROR: cannot determine resolution for the import +//~| ERROR: cannot determine resolution for the import +use c as b; + +fn c() {} + +fn main() { } diff --git a/tests/ui/imports/shadow-glob-module-resolution-3.stderr b/tests/ui/imports/shadow-glob-module-resolution-3.stderr new file mode 100644 index 000000000000..ab853c715828 --- /dev/null +++ b/tests/ui/imports/shadow-glob-module-resolution-3.stderr @@ -0,0 +1,23 @@ +error: cannot determine resolution for the import + --> $DIR/shadow-glob-module-resolution-3.rs:11:5 + | +LL | use b::c; + | ^^^^ + +error: cannot determine resolution for the import + --> $DIR/shadow-glob-module-resolution-3.rs:11:5 + | +LL | use b::c; + | ^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0432]: unresolved import `b::c` + --> $DIR/shadow-glob-module-resolution-3.rs:11:5 + | +LL | use b::c; + | ^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/shadow-glob-module-resolution-4.rs b/tests/ui/imports/shadow-glob-module-resolution-4.rs new file mode 100644 index 000000000000..581cdc185d3f --- /dev/null +++ b/tests/ui/imports/shadow-glob-module-resolution-4.rs @@ -0,0 +1,20 @@ +// https://github.com/rust-lang/rust/issues/126376 + +mod a { + pub mod b { + pub trait C {} + } +} + +use a::*; + +use e as b; + +use b::C as e; +//~^ ERROR: unresolved import `b::C` +//~| ERROR: cannot determine resolution for the import +//~| ERROR: cannot determine resolution for the import + +fn e() {} + +fn main() { } diff --git a/tests/ui/imports/shadow-glob-module-resolution-4.stderr b/tests/ui/imports/shadow-glob-module-resolution-4.stderr new file mode 100644 index 000000000000..063beb612b13 --- /dev/null +++ b/tests/ui/imports/shadow-glob-module-resolution-4.stderr @@ -0,0 +1,23 @@ +error: cannot determine resolution for the import + --> $DIR/shadow-glob-module-resolution-4.rs:13:5 + | +LL | use b::C as e; + | ^^^^^^^^^ + +error: cannot determine resolution for the import + --> $DIR/shadow-glob-module-resolution-4.rs:13:5 + | +LL | use b::C as e; + | ^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0432]: unresolved import `b::C` + --> $DIR/shadow-glob-module-resolution-4.rs:13:5 + | +LL | use b::C as e; + | ^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/imports/simple-dylib-import.rs b/tests/ui/imports/simple-dylib-import.rs new file mode 100644 index 000000000000..d8ee3496b90d --- /dev/null +++ b/tests/ui/imports/simple-dylib-import.rs @@ -0,0 +1,12 @@ +// A simple test, where foo.rs has a dependency +// on the dynamic library simple-dylib.rs. If the test passes, +// dylibs can be built and linked into another file successfully.. + +//@ aux-crate:bar=simple-dylib.rs +//@ run-pass + +extern crate bar; + +fn main() { + bar::bar(); +} diff --git a/tests/ui/imports/simple-rlib-import.rs b/tests/ui/imports/simple-rlib-import.rs new file mode 100644 index 000000000000..6eab7ba04cfa --- /dev/null +++ b/tests/ui/imports/simple-rlib-import.rs @@ -0,0 +1,12 @@ +// A simple test, where foo.rs has a dependency +// on the rlib (a static Rust-specific library format) bar.rs. If the test passes, +// rlibs can be built and linked into another file successfully.. + +//@ aux-crate:bar=simple-rlib.rs +//@ run-pass + +extern crate bar; + +fn main() { + bar::bar(); +} diff --git a/tests/ui/indexing/index-bounds.rs b/tests/ui/indexing/index-bounds.rs new file mode 100644 index 000000000000..2b318beeeaa7 --- /dev/null +++ b/tests/ui/indexing/index-bounds.rs @@ -0,0 +1,10 @@ +//@ build-fail + +fn main() { + let _n = [64][200]; + //~^ ERROR this operation will panic at runtime [unconditional_panic] + + // issue #121126, test index value between 0xFFFF_FF00 and u32::MAX + let _n = [64][u32::MAX as usize - 1]; + //~^ ERROR this operation will panic at runtime [unconditional_panic] +} diff --git a/tests/ui/indexing/index-bounds.stderr b/tests/ui/indexing/index-bounds.stderr new file mode 100644 index 000000000000..51d8c7567a46 --- /dev/null +++ b/tests/ui/indexing/index-bounds.stderr @@ -0,0 +1,16 @@ +error: this operation will panic at runtime + --> $DIR/index-bounds.rs:4:14 + | +LL | let _n = [64][200]; + | ^^^^^^^^^ index out of bounds: the length is 1 but the index is 200 + | + = note: `#[deny(unconditional_panic)]` on by default + +error: this operation will panic at runtime + --> $DIR/index-bounds.rs:8:14 + | +LL | let _n = [64][u32::MAX as usize - 1]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ index out of bounds: the length is 1 but the index is 4294967294 + +error: aborting due to 2 previous errors + diff --git a/tests/ui/infer-fn-tail-expr.rs b/tests/ui/inference/infer-fn-tail-expr.rs similarity index 100% rename from tests/ui/infer-fn-tail-expr.rs rename to tests/ui/inference/infer-fn-tail-expr.rs diff --git a/tests/ui/inference/issue-83606.rs b/tests/ui/inference/issue-83606.rs index 4454b5e60f06..e6e291c3a920 100644 --- a/tests/ui/inference/issue-83606.rs +++ b/tests/ui/inference/issue-83606.rs @@ -6,5 +6,5 @@ fn foo(_: impl std::fmt::Display) -> [usize; N] { fn main() { let _ = foo("foo"); - //~^ ERROR type annotations needed for `[usize; _]` + //~^ ERROR type annotations needed } diff --git a/tests/ui/lambda-infer-unresolved.rs b/tests/ui/inference/lambda-infer-unresolved.rs similarity index 100% rename from tests/ui/lambda-infer-unresolved.rs rename to tests/ui/inference/lambda-infer-unresolved.rs diff --git a/tests/ui/inference/need_type_info/type-alias-indirect.stderr b/tests/ui/inference/need_type_info/type-alias-indirect.stderr index 535c0044aec0..5c5b4c149c17 100644 --- a/tests/ui/inference/need_type_info/type-alias-indirect.stderr +++ b/tests/ui/inference/need_type_info/type-alias-indirect.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/type-alias-indirect.rs:14:5 | LL | IndirectAlias::new(); - | ^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the type alias `IndirectAlias` + | ^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the type alias `IndirectAlias` error: aborting due to 1 previous error diff --git a/tests/ui/inference/need_type_info/type-alias.stderr b/tests/ui/inference/need_type_info/type-alias.stderr index 2c39a3f56466..fd9bcf2fe3f3 100644 --- a/tests/ui/inference/need_type_info/type-alias.stderr +++ b/tests/ui/inference/need_type_info/type-alias.stderr @@ -2,19 +2,19 @@ error[E0282]: type annotations needed --> $DIR/type-alias.rs:12:5 | LL | DirectAlias::new() - | ^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` + | ^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the type alias `DirectAlias` error[E0282]: type annotations needed --> $DIR/type-alias.rs:18:5 | LL | IndirectAlias::new(); - | ^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the type alias `IndirectAlias` + | ^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the type alias `IndirectAlias` error[E0282]: type annotations needed --> $DIR/type-alias.rs:32:5 | LL | DirectButWithDefaultAlias::new(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the type alias `DirectButWithDefaultAlias` error: aborting due to 3 previous errors diff --git a/tests/ui/inference/note-and-explain-ReVar-124973.rs b/tests/ui/inference/note-and-explain-ReVar-124973.rs new file mode 100644 index 000000000000..f1e246456369 --- /dev/null +++ b/tests/ui/inference/note-and-explain-ReVar-124973.rs @@ -0,0 +1,8 @@ +//@ edition:2018 + +#![feature(c_variadic)] + +async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {} +//~^ ERROR hidden type for `impl Future` captures lifetime that does not appear in bounds + +fn main() {} diff --git a/tests/ui/inference/note-and-explain-ReVar-124973.stderr b/tests/ui/inference/note-and-explain-ReVar-124973.stderr new file mode 100644 index 000000000000..574f6508e4c7 --- /dev/null +++ b/tests/ui/inference/note-and-explain-ReVar-124973.stderr @@ -0,0 +1,13 @@ +error[E0700]: hidden type for `impl Future` captures lifetime that does not appear in bounds + --> $DIR/note-and-explain-ReVar-124973.rs:5:73 + | +LL | async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {} + | ----------------------------------------------------------------------- ^^ + | | + | opaque type defined here + | + = note: hidden type `{async fn body of multiple_named_lifetimes<'a, 'b>()}` captures lifetime `'_` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/order-dependent-cast-inference.rs b/tests/ui/inference/order-dependent-cast-inference.rs similarity index 100% rename from tests/ui/order-dependent-cast-inference.rs rename to tests/ui/inference/order-dependent-cast-inference.rs diff --git a/tests/ui/order-dependent-cast-inference.stderr b/tests/ui/inference/order-dependent-cast-inference.stderr similarity index 100% rename from tests/ui/order-dependent-cast-inference.stderr rename to tests/ui/inference/order-dependent-cast-inference.stderr diff --git a/tests/ui/inference/str-as-char-butchered.rs b/tests/ui/inference/str-as-char-butchered.rs new file mode 100644 index 000000000000..6020cd3422fd --- /dev/null +++ b/tests/ui/inference/str-as-char-butchered.rs @@ -0,0 +1,7 @@ +// issue: rust-lang/rust#125081 + +fn main() { + let _: &str = 'β; + //~^ ERROR expected `while`, `for`, `loop` or `{` after a label + //~| ERROR mismatched types +} diff --git a/tests/ui/inference/str-as-char-butchered.stderr b/tests/ui/inference/str-as-char-butchered.stderr new file mode 100644 index 000000000000..ad465f979d4d --- /dev/null +++ b/tests/ui/inference/str-as-char-butchered.stderr @@ -0,0 +1,22 @@ +error: expected `while`, `for`, `loop` or `{` after a label + --> $DIR/str-as-char-butchered.rs:4:21 + | +LL | let _: &str = 'β; + | ^ expected `while`, `for`, `loop` or `{` after a label + | +help: add `'` to close the char literal + | +LL | let _: &str = 'β'; + | + + +error[E0308]: mismatched types + --> $DIR/str-as-char-butchered.rs:4:19 + | +LL | let _: &str = 'β; + | ---- ^^ expected `&str`, found `char` + | | + | expected due to this + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/inference/str-as-char-non-lit.rs b/tests/ui/inference/str-as-char-non-lit.rs new file mode 100644 index 000000000000..d38b46630dcf --- /dev/null +++ b/tests/ui/inference/str-as-char-non-lit.rs @@ -0,0 +1,9 @@ +// Don't suggest double quotes when encountering an expr of type `char` where a `&str` +// is expected if the expr is not a char literal. +// issue: rust-lang/rust#125595 + +fn main() { + let _: &str = ('a'); //~ ERROR mismatched types + let token = || 'a'; + let _: &str = token(); //~ ERROR mismatched types +} diff --git a/tests/ui/inference/str-as-char-non-lit.stderr b/tests/ui/inference/str-as-char-non-lit.stderr new file mode 100644 index 000000000000..7675fe013307 --- /dev/null +++ b/tests/ui/inference/str-as-char-non-lit.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/str-as-char-non-lit.rs:6:19 + | +LL | let _: &str = ('a'); + | ---- ^^^^^ expected `&str`, found `char` + | | + | expected due to this + +error[E0308]: mismatched types + --> $DIR/str-as-char-non-lit.rs:8:19 + | +LL | let _: &str = token(); + | ---- ^^^^^^^ expected `&str`, found `char` + | | + | expected due to this + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/inline-const/const_block_pat_liveness.rs b/tests/ui/inline-const/const_block_pat_liveness.rs new file mode 100644 index 000000000000..26393a4f65b8 --- /dev/null +++ b/tests/ui/inline-const/const_block_pat_liveness.rs @@ -0,0 +1,18 @@ +//! This test used to ICE because const blocks didn't have a body +//! anymore, making a lot of logic very fragile around handling the +//! HIR of a const block. +//! https://github.com/rust-lang/rust/issues/125846 + +//@ check-pass + +#![feature(inline_const_pat)] + +fn main() { + match 0 { + const { + let a = 10_usize; + *&a + } + | _ => {} + } +} diff --git a/tests/ui/inline-const/referencing_local_variables.rs b/tests/ui/inline-const/referencing_local_variables.rs new file mode 100644 index 000000000000..f9f0fef07f08 --- /dev/null +++ b/tests/ui/inline-const/referencing_local_variables.rs @@ -0,0 +1,6 @@ +const fn test_me(a: usize) -> usize { + const { a } + //~^ ERROR: attempt to use a non-constant value in a constant +} + +fn main() {} diff --git a/tests/ui/inline-const/referencing_local_variables.stderr b/tests/ui/inline-const/referencing_local_variables.stderr new file mode 100644 index 000000000000..4a0a54066024 --- /dev/null +++ b/tests/ui/inline-const/referencing_local_variables.stderr @@ -0,0 +1,11 @@ +error[E0435]: attempt to use a non-constant value in a constant + --> $DIR/referencing_local_variables.rs:2:13 + | +LL | const fn test_me(a: usize) -> usize { + | - this would need to be a `const` +LL | const { a } + | ^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0435`. diff --git a/tests/ui/inline-const/uninit_local.rs b/tests/ui/inline-const/uninit_local.rs new file mode 100644 index 000000000000..548c053affc7 --- /dev/null +++ b/tests/ui/inline-const/uninit_local.rs @@ -0,0 +1,6 @@ +fn main() { + let _my_usize = const { + let x: bool; + while x {} //~ ERROR: `x` isn't initialized + }; +} diff --git a/tests/ui/inline-const/uninit_local.stderr b/tests/ui/inline-const/uninit_local.stderr new file mode 100644 index 000000000000..37b78e337e7b --- /dev/null +++ b/tests/ui/inline-const/uninit_local.stderr @@ -0,0 +1,16 @@ +error[E0381]: used binding `x` isn't initialized + --> $DIR/uninit_local.rs:4:15 + | +LL | let x: bool; + | - binding declared here but left uninitialized +LL | while x {} + | ^ `x` used here but it isn't initialized + | +help: consider assigning a value + | +LL | let x: bool = false; + | +++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0381`. diff --git a/tests/ui/instrument-coverage/coverage-options.bad.stderr b/tests/ui/instrument-coverage/coverage-options.bad.stderr index 9e2c19e90d59..1a6b30dc8324 100644 --- a/tests/ui/instrument-coverage/coverage-options.bad.stderr +++ b/tests/ui/instrument-coverage/coverage-options.bad.stderr @@ -1,2 +1,2 @@ -error: incorrect value `bad` for unstable option `coverage-options` - `block` | `branch` | `mcdc` was expected +error: incorrect value `bad` for unstable option `coverage-options` - `block` | `branch` | `condition` | `mcdc` | `no-mir-spans` was expected diff --git a/tests/ui/instrument-coverage/coverage-options.rs b/tests/ui/instrument-coverage/coverage-options.rs index 2a80ce4ab2e9..7615a0fb2751 100644 --- a/tests/ui/instrument-coverage/coverage-options.rs +++ b/tests/ui/instrument-coverage/coverage-options.rs @@ -1,6 +1,5 @@ -//@ needs-profiler-support -//@ revisions: block branch mcdc bad -//@ compile-flags -Cinstrument-coverage +//@ revisions: block branch condition mcdc bad +//@ compile-flags -Cinstrument-coverage -Zno-profiler-runtime //@ [block] check-pass //@ [block] compile-flags: -Zcoverage-options=block @@ -8,6 +7,9 @@ //@ [branch] check-pass //@ [branch] compile-flags: -Zcoverage-options=branch +//@ [condition] check-pass +//@ [condition] compile-flags: -Zcoverage-options=condition + //@ [mcdc] check-pass //@ [mcdc] compile-flags: -Zcoverage-options=mcdc diff --git a/tests/ui/instrument-coverage/mcdc-condition-limit.bad.stderr b/tests/ui/instrument-coverage/mcdc-condition-limit.bad.stderr new file mode 100644 index 000000000000..5df6aaf98041 --- /dev/null +++ b/tests/ui/instrument-coverage/mcdc-condition-limit.bad.stderr @@ -0,0 +1,8 @@ +warning: Number of conditions in decision (7) exceeds limit (6). MC/DC analysis will not count this expression. + --> $DIR/mcdc-condition-limit.rs:29:8 + | +LL | if a && b && c && d && e && f && g { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/tests/ui/instrument-coverage/mcdc-condition-limit.rs b/tests/ui/instrument-coverage/mcdc-condition-limit.rs new file mode 100644 index 000000000000..de3770b5709d --- /dev/null +++ b/tests/ui/instrument-coverage/mcdc-condition-limit.rs @@ -0,0 +1,32 @@ +//@ edition: 2021 +//@ min-llvm-version: 18 +//@ revisions: good bad +//@ check-pass +//@ compile-flags: -Cinstrument-coverage -Zcoverage-options=mcdc -Zno-profiler-runtime + +// Check that we emit some kind of diagnostic when MC/DC instrumentation sees +// code that exceeds the limit of 6 conditions per decision, and falls back +// to only instrumenting that code for branch coverage. +// +// See also `tests/coverage/mcdc/condition-limit.rs`, which tests the actual +// effect on instrumentation. +// +// (The limit is enforced in `compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs`.) + +#[cfg(good)] +fn main() { + // 6 conditions is OK, so no diagnostic. + let [a, b, c, d, e, f] = <[bool; 6]>::default(); + if a && b && c && d && e && f { + core::hint::black_box("hello"); + } +} + +#[cfg(bad)] +fn main() { + // 7 conditions is too many, so issue a diagnostic. + let [a, b, c, d, e, f, g] = <[bool; 7]>::default(); + if a && b && c && d && e && f && g { //[bad]~ WARNING Number of conditions in decision + core::hint::black_box("hello"); + } +} diff --git a/tests/ui/instrument-coverage/on-values.rs b/tests/ui/instrument-coverage/on-values.rs index a6793b2c304f..53b149fd39cc 100644 --- a/tests/ui/instrument-coverage/on-values.rs +++ b/tests/ui/instrument-coverage/on-values.rs @@ -1,5 +1,5 @@ //@ check-pass -//@ needs-profiler-support +//@ compile-flags: -Zno-profiler-runtime //@ revisions: default y yes on true_ all //@ [default] compile-flags: -Cinstrument-coverage //@ [y] compile-flags: -Cinstrument-coverage=y diff --git a/tests/ui/intrinsics-always-extern.rs b/tests/ui/intrinsics/always-extern.rs similarity index 100% rename from tests/ui/intrinsics-always-extern.rs rename to tests/ui/intrinsics/always-extern.rs diff --git a/tests/ui/intrinsics-always-extern.stderr b/tests/ui/intrinsics/always-extern.stderr similarity index 83% rename from tests/ui/intrinsics-always-extern.stderr rename to tests/ui/intrinsics/always-extern.stderr index 1f7bb5a3b0de..44b889c6faac 100644 --- a/tests/ui/intrinsics-always-extern.stderr +++ b/tests/ui/intrinsics/always-extern.stderr @@ -1,11 +1,11 @@ error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/intrinsics-always-extern.rs:4:32 + --> $DIR/always-extern.rs:4:32 | LL | extern "rust-intrinsic" fn foo(&self); | ^^^ error[E0093]: unrecognized intrinsic function: `hello` - --> $DIR/intrinsics-always-extern.rs:12:28 + --> $DIR/always-extern.rs:12:28 | LL | extern "rust-intrinsic" fn hello() { | ^^^^^ unrecognized intrinsic @@ -13,7 +13,7 @@ LL | extern "rust-intrinsic" fn hello() { = help: if you're adding an intrinsic, be sure to update `check_intrinsic_type` error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/intrinsics-always-extern.rs:8:43 + --> $DIR/always-extern.rs:8:43 | LL | extern "rust-intrinsic" fn foo(&self) { | ___________________________________________^ @@ -21,7 +21,7 @@ LL | | } | |_____^ error: intrinsic must be in `extern "rust-intrinsic" { ... }` block - --> $DIR/intrinsics-always-extern.rs:12:36 + --> $DIR/always-extern.rs:12:36 | LL | extern "rust-intrinsic" fn hello() { | ____________________________________^ diff --git a/tests/ui/intrinsics/const-eval-select-bad.stderr b/tests/ui/intrinsics/const-eval-select-bad.stderr index 85e22178c4a6..50092edda4f7 100644 --- a/tests/ui/intrinsics/const-eval-select-bad.stderr +++ b/tests/ui/intrinsics/const-eval-select-bad.stderr @@ -24,7 +24,7 @@ LL | const_eval_select((), 42, 0xDEADBEEF); | | | required by a bound introduced by this call | - = help: the trait `FnOnce<()>` is not implemented for `{integer}` + = help: the trait `FnOnce()` is not implemented for `{integer}` = note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics.rs:LL:COL @@ -37,7 +37,7 @@ LL | const_eval_select((), 42, 0xDEADBEEF); | | | required by a bound introduced by this call | - = help: the trait `FnOnce<()>` is not implemented for `{integer}` + = help: the trait `FnOnce()` is not implemented for `{integer}` = note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics.rs:LL:COL diff --git a/tests/ui/intrinsics/intrinsic-alignment.rs b/tests/ui/intrinsics/intrinsic-alignment.rs index 4856da553a80..138273aadd2f 100644 --- a/tests/ui/intrinsics/intrinsic-alignment.rs +++ b/tests/ui/intrinsics/intrinsic-alignment.rs @@ -10,20 +10,21 @@ mod rusti { } } -#[cfg(any(target_os = "android", - target_os = "dragonfly", - target_os = "emscripten", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "vxworks", - target_os = "nto", +#[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "vxworks", + target_os = "nto", + target_vendor = "apple", ))] mod m { #[cfg(target_arch = "x86")] diff --git a/tests/ui/reify-intrinsic.rs b/tests/ui/intrinsics/reify-intrinsic.rs similarity index 100% rename from tests/ui/reify-intrinsic.rs rename to tests/ui/intrinsics/reify-intrinsic.rs diff --git a/tests/ui/reify-intrinsic.stderr b/tests/ui/intrinsics/reify-intrinsic.stderr similarity index 100% rename from tests/ui/reify-intrinsic.stderr rename to tests/ui/intrinsics/reify-intrinsic.stderr diff --git a/tests/ui/invalid-compile-flags/print.stderr b/tests/ui/invalid-compile-flags/print.stderr index 0a032aabdfe8..70b4a394dd02 100644 --- a/tests/ui/invalid-compile-flags/print.stderr +++ b/tests/ui/invalid-compile-flags/print.stderr @@ -1,4 +1,4 @@ error: unknown print request: `yyyy` | - = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models` + = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models` diff --git a/tests/ui/io-checks/non-ice-error-on-worker-io-fail.stderr b/tests/ui/io-checks/non-ice-error-on-worker-io-fail.stderr index 5775a4032c39..48cae82feb03 100644 --- a/tests/ui/io-checks/non-ice-error-on-worker-io-fail.stderr +++ b/tests/ui/io-checks/non-ice-error-on-worker-io-fail.stderr @@ -1,6 +1,4 @@ -warning: ignoring --out-dir flag due to -o flag - error: io error modifying ./does-not-exist/ -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-11771.stderr b/tests/ui/issues/issue-11771.stderr index d4a4647f6a10..8205ee0c38d5 100644 --- a/tests/ui/issues/issue-11771.stderr +++ b/tests/ui/issues/issue-11771.stderr @@ -6,14 +6,14 @@ LL | 1 + | = help: the trait `Add<()>` is not implemented for `{integer}` = help: the following other types implement trait `Add`: - <&'a f128 as Add> - <&'a f16 as Add> - <&'a f32 as Add> - <&'a f64 as Add> - <&'a i128 as Add> - <&'a i16 as Add> - <&'a i32 as Add> - <&'a i64 as Add> + `&'a f128` implements `Add` + `&'a f16` implements `Add` + `&'a f32` implements `Add` + `&'a f64` implements `Add` + `&'a i128` implements `Add` + `&'a i16` implements `Add` + `&'a i32` implements `Add` + `&'a i64` implements `Add` and 56 others error[E0277]: cannot add `()` to `{integer}` @@ -24,14 +24,14 @@ LL | 1 + | = help: the trait `Add<()>` is not implemented for `{integer}` = help: the following other types implement trait `Add`: - <&'a f128 as Add> - <&'a f16 as Add> - <&'a f32 as Add> - <&'a f64 as Add> - <&'a i128 as Add> - <&'a i16 as Add> - <&'a i32 as Add> - <&'a i64 as Add> + `&'a f128` implements `Add` + `&'a f16` implements `Add` + `&'a f32` implements `Add` + `&'a f64` implements `Add` + `&'a i128` implements `Add` + `&'a i16` implements `Add` + `&'a i32` implements `Add` + `&'a i64` implements `Add` and 56 others error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-22034.stderr b/tests/ui/issues/issue-22034.stderr index 75ddcd478992..68202085e77d 100644 --- a/tests/ui/issues/issue-22034.stderr +++ b/tests/ui/issues/issue-22034.stderr @@ -4,7 +4,7 @@ error[E0277]: expected a `Fn()` closure, found `()` LL | &mut *(ptr as *mut dyn Fn()) | ^^^ expected an `Fn()` closure, found `()` | - = help: the trait `Fn<()>` is not implemented for `()` + = help: the trait `Fn()` is not implemented for `()` = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` = note: required for the cast from `*mut ()` to `*mut dyn Fn()` diff --git a/tests/ui/issues/issue-23543.rs b/tests/ui/issues/issue-23543.rs index 843e1a8a83ae..248bf77a7083 100644 --- a/tests/ui/issues/issue-23543.rs +++ b/tests/ui/issues/issue-23543.rs @@ -5,7 +5,7 @@ struct Foo; pub trait D { fn f(self) where T: A; - //~^ ERROR associated type bindings are not allowed here [E0229] + //~^ ERROR associated item constraints are not allowed here [E0229] } fn main() {} diff --git a/tests/ui/issues/issue-23543.stderr b/tests/ui/issues/issue-23543.stderr index d917a4c51d59..17243aefbbcd 100644 --- a/tests/ui/issues/issue-23543.stderr +++ b/tests/ui/issues/issue-23543.stderr @@ -1,8 +1,8 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-23543.rs:7:17 | LL | where T: A; - | ^^^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^^^ associated item constraint not allowed here error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-23544.rs b/tests/ui/issues/issue-23544.rs index 6ad00b2fba4b..bbd05a59353d 100644 --- a/tests/ui/issues/issue-23544.rs +++ b/tests/ui/issues/issue-23544.rs @@ -3,7 +3,7 @@ pub trait A: Copy {} pub trait D { fn f(self) where T: A; - //~^ ERROR associated type bindings are not allowed here [E0229] + //~^ ERROR associated item constraints are not allowed here [E0229] } fn main() {} diff --git a/tests/ui/issues/issue-23544.stderr b/tests/ui/issues/issue-23544.stderr index 2a7e93f0eb71..8d652a9da279 100644 --- a/tests/ui/issues/issue-23544.stderr +++ b/tests/ui/issues/issue-23544.stderr @@ -1,8 +1,8 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-23544.rs:5:17 | LL | where T: A; - | ^^^^^^^^^^^^^^^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^^^^^^^^^^^^^^^ associated item constraint not allowed here error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-23966.stderr b/tests/ui/issues/issue-23966.stderr index c29e88614442..3f7a4fa312f1 100644 --- a/tests/ui/issues/issue-23966.stderr +++ b/tests/ui/issues/issue-23966.stderr @@ -6,7 +6,7 @@ LL | "".chars().fold(|_, _| (), ()); | | | required by a bound introduced by this call | - = help: the trait `FnMut<(_, char)>` is not implemented for `()` + = help: the trait `FnMut(_, char)` is not implemented for `()` note: required by a bound in `fold` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL diff --git a/tests/ui/issues/issue-24352.stderr b/tests/ui/issues/issue-24352.stderr index a9f886158e1d..2e7dc254d914 100644 --- a/tests/ui/issues/issue-24352.stderr +++ b/tests/ui/issues/issue-24352.stderr @@ -6,10 +6,10 @@ LL | 1.0f64 - 1 | = help: the trait `Sub<{integer}>` is not implemented for `f64` = help: the following other types implement trait `Sub`: - <&'a f64 as Sub> - <&f64 as Sub<&f64>> - > - + `&'a f64` implements `Sub` + `&f64` implements `Sub<&f64>` + `f64` implements `Sub<&f64>` + `f64` implements `Sub` help: consider using a floating-point literal by writing it with `.0` | LL | 1.0f64 - 1.0 diff --git a/tests/ui/issues/issue-24682.rs b/tests/ui/issues/issue-24682.rs index 0d1ab73417c0..1bd42f3cbe3e 100644 --- a/tests/ui/issues/issue-24682.rs +++ b/tests/ui/issues/issue-24682.rs @@ -2,17 +2,17 @@ trait A: Sized { type N; fn x() -> Self< - N= //~ ERROR associated type bindings are not allowed here + N= //~ ERROR associated item constraints are not allowed here Self::N> { loop {} } fn y(&self) -> std - //~ ERROR associated type bindings are not allowed here + //~ ERROR associated item constraints are not allowed here ::option::Option<()> { None } fn z(&self) -> - u32 //~ ERROR associated type bindings are not allowed here + u32 //~ ERROR associated item constraints are not allowed here { 42 } } diff --git a/tests/ui/issues/issue-24682.stderr b/tests/ui/issues/issue-24682.stderr index e1943bf4d683..a107e8b52cd5 100644 --- a/tests/ui/issues/issue-24682.stderr +++ b/tests/ui/issues/issue-24682.stderr @@ -1,21 +1,21 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-24682.rs:5:11 | LL | / N= LL | | Self::N> { - | |_________________^ associated type not allowed here + | |_________________^ associated item constraint not allowed here -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-24682.rs:11:13 | LL | - | ^^^^ associated type not allowed here + | ^^^^ associated item constraint not allowed here -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-24682.rs:15:13 | LL | u32 - | ^^^^ associated type not allowed here + | ^^^^ associated item constraint not allowed here error: aborting due to 3 previous errors diff --git a/tests/ui/issues/issue-25901.stderr b/tests/ui/issues/issue-25901.stderr index 5c19abffa029..3fedfd964170 100644 --- a/tests/ui/issues/issue-25901.stderr +++ b/tests/ui/issues/issue-25901.stderr @@ -16,7 +16,7 @@ note: impl defined here, but it is not `const` LL | impl Deref for A { | ^^^^^^^^^^^^^^^^ = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` help: add `#![feature(const_trait_impl)]` to the crate attributes to enable | LL + #![feature(const_trait_impl)] diff --git a/tests/ui/issues/issue-28344.stderr b/tests/ui/issues/issue-28344.stderr index ddc1027c16b8..d30fb3cfe58a 100644 --- a/tests/ui/issues/issue-28344.stderr +++ b/tests/ui/issues/issue-28344.stderr @@ -52,7 +52,7 @@ error[E0599]: no function or associated item named `bitor` found for trait objec LL | let g = BitXor::bitor; | ^^^^^ function or associated item not found in `dyn BitXor<_>` | -help: there is a method `bitxor` with a similar name, but with different arguments +help: there is a method `bitxor` with a similar name --> $SRC_DIR/core/src/ops/bit.rs:LL:COL error: aborting due to 4 previous errors; 2 warnings emitted diff --git a/tests/ui/issues/issue-39687.rs b/tests/ui/issues/issue-39687.rs index cbb721fbb57c..58f981b63d11 100644 --- a/tests/ui/issues/issue-39687.rs +++ b/tests/ui/issues/issue-39687.rs @@ -2,5 +2,5 @@ fn main() { ::call; - //~^ ERROR associated type bindings are not allowed here [E0229] + //~^ ERROR associated item constraints are not allowed here [E0229] } diff --git a/tests/ui/issues/issue-39687.stderr b/tests/ui/issues/issue-39687.stderr index f4742115a199..87e5fdc2d8f2 100644 --- a/tests/ui/issues/issue-39687.stderr +++ b/tests/ui/issues/issue-39687.stderr @@ -1,8 +1,8 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-39687.rs:4:14 | LL | ::call; - | ^^^^ associated type not allowed here + | ^^^^ associated item constraint not allowed here error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-43431.rs b/tests/ui/issues/issue-43431.rs index e7ec35105f75..0286336910e1 100644 --- a/tests/ui/issues/issue-43431.rs +++ b/tests/ui/issues/issue-43431.rs @@ -7,7 +7,7 @@ trait CallSingle { impl B> CallSingle for F { fn call(&self, a: A) -> B { B>::call(self, (a,)) - //~^ ERROR associated type bindings are not allowed here + //~^ ERROR associated item constraints are not allowed here } } diff --git a/tests/ui/issues/issue-43431.stderr b/tests/ui/issues/issue-43431.stderr index 6d47ba271624..27a720408e4b 100644 --- a/tests/ui/issues/issue-43431.stderr +++ b/tests/ui/issues/issue-43431.stderr @@ -1,8 +1,8 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-43431.rs:9:27 | LL | B>::call(self, (a,)) - | ^ associated type not allowed here + | ^ associated item constraint not allowed here error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-45731.rs b/tests/ui/issues/issue-45731.rs index 8e483d08cb51..49335362dd0f 100644 --- a/tests/ui/issues/issue-45731.rs +++ b/tests/ui/issues/issue-45731.rs @@ -2,10 +2,10 @@ #![allow(unused_variables)] //@ compile-flags:--test -g -#[cfg(target_os = "macos")] +#[cfg(target_vendor = "apple")] #[test] fn simple_test() { - use std::{env, panic, fs}; + use std::{env, fs, panic}; // Find our dSYM and replace the DWARF binary with an empty file let mut dsym_path = env::current_exe().unwrap(); @@ -13,8 +13,13 @@ fn simple_test() { assert!(dsym_path.pop()); // Pop executable dsym_path.push(format!("{}.dSYM/Contents/Resources/DWARF/{0}", executable_name)); { - let file = fs::OpenOptions::new().read(false).write(true).truncate(true).create(false) - .open(&dsym_path).unwrap(); + let file = fs::OpenOptions::new() + .read(false) + .write(true) + .truncate(true) + .create(false) + .open(&dsym_path) + .unwrap(); } env::set_var("RUST_BACKTRACE", "1"); diff --git a/tests/ui/issues/issue-50582.stderr b/tests/ui/issues/issue-50582.stderr index b765d2f087d0..7203fdeb0bbc 100644 --- a/tests/ui/issues/issue-50582.stderr +++ b/tests/ui/issues/issue-50582.stderr @@ -16,14 +16,14 @@ LL | Vec::<[(); 1 + for x in 0..1 {}]>::new(); | = help: the trait `Add<()>` is not implemented for `{integer}` = help: the following other types implement trait `Add`: - <&'a f128 as Add> - <&'a f16 as Add> - <&'a f32 as Add> - <&'a f64 as Add> - <&'a i128 as Add> - <&'a i16 as Add> - <&'a i32 as Add> - <&'a i64 as Add> + `&'a f128` implements `Add` + `&'a f16` implements `Add` + `&'a f32` implements `Add` + `&'a f64` implements `Add` + `&'a i128` implements `Add` + `&'a i16` implements `Add` + `&'a i32` implements `Add` + `&'a i64` implements `Add` and 56 others error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-50781.rs b/tests/ui/issues/issue-50781.rs index 3c5e5a9f69af..32253c3c2360 100644 --- a/tests/ui/issues/issue-50781.rs +++ b/tests/ui/issues/issue-50781.rs @@ -1,10 +1,7 @@ -#![deny(where_clauses_object_safety)] - trait Trait {} trait X { - fn foo(&self) where Self: Trait; //~ ERROR the trait `X` cannot be made into an object - //~^ WARN this was previously accepted by the compiler but is being phased out + fn foo(&self) where Self: Trait; } impl X for () { @@ -12,8 +9,11 @@ impl X for () { } impl Trait for dyn X {} +//~^ ERROR the trait `X` cannot be made into an object pub fn main() { // Check that this does not segfault. ::foo(&()); + //~^ ERROR the trait `X` cannot be made into an object + //~| ERROR the trait `X` cannot be made into an object } diff --git a/tests/ui/issues/issue-50781.stderr b/tests/ui/issues/issue-50781.stderr index beaea1e634ce..6b0b42ca53a6 100644 --- a/tests/ui/issues/issue-50781.stderr +++ b/tests/ui/issues/issue-50781.stderr @@ -1,24 +1,52 @@ -error: the trait `X` cannot be made into an object - --> $DIR/issue-50781.rs:6:8 +error[E0038]: the trait `X` cannot be made into an object + --> $DIR/issue-50781.rs:11:16 | -LL | fn foo(&self) where Self: Trait; - | ^^^ +LL | impl Trait for dyn X {} + | ^^^^^ `X` cannot be made into an object | - = warning: 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 #51443 note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/issue-50781.rs:6:8 + --> $DIR/issue-50781.rs:4:8 | LL | trait X { | - this trait cannot be made into an object... LL | fn foo(&self) where Self: Trait; | ^^^ ...because method `foo` references the `Self` type in its `where` clause = help: consider moving `foo` to another trait -note: the lint level is defined here - --> $DIR/issue-50781.rs:1:9 + = help: only type `()` implements the trait, consider using it directly instead + +error[E0038]: the trait `X` cannot be made into an object + --> $DIR/issue-50781.rs:16:23 | -LL | #![deny(where_clauses_object_safety)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ::foo(&()); + | ^^^ `X` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/issue-50781.rs:4:8 + | +LL | trait X { + | - this trait cannot be made into an object... +LL | fn foo(&self) where Self: Trait; + | ^^^ ...because method `foo` references the `Self` type in its `where` clause + = help: consider moving `foo` to another trait + = help: only type `()` implements the trait, consider using it directly instead + = note: required for the cast from `&()` to `&dyn X` -error: aborting due to 1 previous error +error[E0038]: the trait `X` cannot be made into an object + --> $DIR/issue-50781.rs:16:6 + | +LL | ::foo(&()); + | ^^^^^ `X` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/issue-50781.rs:4:8 + | +LL | trait X { + | - this trait cannot be made into an object... +LL | fn foo(&self) where Self: Trait; + | ^^^ ...because method `foo` references the `Self` type in its `where` clause + = help: consider moving `foo` to another trait + = help: only type `()` implements the trait, consider using it directly instead +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/issues/issue-53498.rs b/tests/ui/issues/issue-53498.rs deleted file mode 100644 index 9e0437c46f4b..000000000000 --- a/tests/ui/issues/issue-53498.rs +++ /dev/null @@ -1,17 +0,0 @@ -pub mod test { - pub struct A; - pub struct B; - pub struct Foo(T); - - impl Foo { - fn foo() {} - } - - impl Foo { - fn foo() {} - } -} - -fn main() { - test::Foo::::foo(); //~ ERROR associated function `foo` is private -} diff --git a/tests/ui/issues/issue-5708.rs b/tests/ui/issues/issue-5708.rs index ce9ef78ffcd9..89ea9fbdcd8f 100644 --- a/tests/ui/issues/issue-5708.rs +++ b/tests/ui/issues/issue-5708.rs @@ -44,6 +44,7 @@ pub trait MyTrait { fn dummy(&self, t: T) -> T { panic!() } } +#[allow(dead_code)] pub struct MyContainer<'a, T:'a> { foos: Vec<&'a (dyn MyTrait+'a)> , } diff --git a/tests/ui/issues/issue-66706.rs b/tests/ui/issues/issue-66706.rs index 6d1d9f5e3a99..87dc3437a57b 100644 --- a/tests/ui/issues/issue-66706.rs +++ b/tests/ui/issues/issue-66706.rs @@ -1,6 +1,6 @@ fn a() { [0; [|_: _ &_| ()].len()] - //~^ ERROR expected `,`, found `&` + //~^ ERROR expected one of `,` or `|`, found `&` //~| ERROR type annotations needed } @@ -11,7 +11,7 @@ fn b() { fn c() { [0; [|&_: _ &_| {}; 0 ].len()] - //~^ ERROR expected `,`, found `&` + //~^ ERROR expected one of `,` or `|`, found `&` //~| ERROR type annotations needed } diff --git a/tests/ui/issues/issue-66706.stderr b/tests/ui/issues/issue-66706.stderr index 0271db754bd3..dd1e07589f51 100644 --- a/tests/ui/issues/issue-66706.stderr +++ b/tests/ui/issues/issue-66706.stderr @@ -1,8 +1,8 @@ -error: expected `,`, found `&` +error: expected one of `,` or `|`, found `&` --> $DIR/issue-66706.rs:2:16 | LL | [0; [|_: _ &_| ()].len()] - | -^ expected `,` + | -^ expected one of `,` or `|` | | | help: missing `,` @@ -12,11 +12,11 @@ error: expected identifier, found reserved identifier `_` LL | [0; [|f @ &ref _| {} ; 0 ].len() ]; | ^ expected identifier, found reserved identifier -error: expected `,`, found `&` +error: expected one of `,` or `|`, found `&` --> $DIR/issue-66706.rs:13:17 | LL | [0; [|&_: _ &_| {}; 0 ].len()] - | -^ expected `,` + | -^ expected one of `,` or `|` | | | help: missing `,` diff --git a/tests/ui/issues/issue-7364.stderr b/tests/ui/issues/issue-7364.stderr index 3bf59a6d7114..d5b6dde1f10e 100644 --- a/tests/ui/issues/issue-7364.stderr +++ b/tests/ui/issues/issue-7364.stderr @@ -18,7 +18,7 @@ LL | static boxed: Box> = Box::new(RefCell::new(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` error: aborting due to 2 previous errors diff --git a/tests/ui/iterators/into-iter-on-arrays-2018.stderr b/tests/ui/iterators/into-iter-on-arrays-2018.stderr index 2378476e5d0e..9d6bbf06c36b 100644 --- a/tests/ui/iterators/into-iter-on-arrays-2018.stderr +++ b/tests/ui/iterators/into-iter-on-arrays-2018.stderr @@ -1,4 +1,4 @@ -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-2018.rs:14:34 | LL | let _: Iter<'_, i32> = array.into_iter(); @@ -16,7 +16,7 @@ help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicit LL | let _: Iter<'_, i32> = IntoIterator::into_iter(array); | ++++++++++++++++++++++++ ~ -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-2018.rs:18:44 | LL | let _: Iter<'_, i32> = Box::new(array).into_iter(); @@ -25,7 +25,7 @@ LL | let _: Iter<'_, i32> = Box::new(array).into_iter(); = warning: this changes meaning in Rust 2021 = note: for more information, see -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-2018.rs:22:43 | LL | let _: Iter<'_, i32> = Rc::new(array).into_iter(); @@ -34,7 +34,7 @@ LL | let _: Iter<'_, i32> = Rc::new(array).into_iter(); = warning: this changes meaning in Rust 2021 = note: for more information, see -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-2018.rs:25:41 | LL | let _: Iter<'_, i32> = Array(array).into_iter(); @@ -43,7 +43,7 @@ LL | let _: Iter<'_, i32> = Array(array).into_iter(); = warning: this changes meaning in Rust 2021 = note: for more information, see -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-2018.rs:32:24 | LL | for _ in [1, 2, 3].into_iter() {} diff --git a/tests/ui/iterators/into-iter-on-arrays-lint.stderr b/tests/ui/iterators/into-iter-on-arrays-lint.stderr index 2fde276faa3d..da388d6b848d 100644 --- a/tests/ui/iterators/into-iter-on-arrays-lint.stderr +++ b/tests/ui/iterators/into-iter-on-arrays-lint.stderr @@ -1,4 +1,4 @@ -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:11:11 | LL | small.into_iter(); @@ -16,7 +16,7 @@ help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicit LL | IntoIterator::into_iter(small); | ++++++++++++++++++++++++ ~ -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:14:12 | LL | [1, 2].into_iter(); @@ -33,7 +33,7 @@ help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicit LL | IntoIterator::into_iter([1, 2]); | ++++++++++++++++++++++++ ~ -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:17:9 | LL | big.into_iter(); @@ -50,7 +50,7 @@ help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicit LL | IntoIterator::into_iter(big); | ++++++++++++++++++++++++ ~ -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:20:15 | LL | [0u8; 33].into_iter(); @@ -67,7 +67,7 @@ help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicit LL | IntoIterator::into_iter([0u8; 33]); | ++++++++++++++++++++++++ ~ -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:24:21 | LL | Box::new(small).into_iter(); @@ -76,7 +76,7 @@ LL | Box::new(small).into_iter(); = warning: this changes meaning in Rust 2021 = note: for more information, see -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:27:22 | LL | Box::new([1, 2]).into_iter(); @@ -85,7 +85,7 @@ LL | Box::new([1, 2]).into_iter(); = warning: this changes meaning in Rust 2021 = note: for more information, see -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:30:19 | LL | Box::new(big).into_iter(); @@ -94,7 +94,7 @@ LL | Box::new(big).into_iter(); = warning: this changes meaning in Rust 2021 = note: for more information, see -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:33:25 | LL | Box::new([0u8; 33]).into_iter(); @@ -103,7 +103,7 @@ LL | Box::new([0u8; 33]).into_iter(); = warning: this changes meaning in Rust 2021 = note: for more information, see -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:37:31 | LL | Box::new(Box::new(small)).into_iter(); @@ -112,7 +112,7 @@ LL | Box::new(Box::new(small)).into_iter(); = warning: this changes meaning in Rust 2021 = note: for more information, see -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:40:32 | LL | Box::new(Box::new([1, 2])).into_iter(); @@ -121,7 +121,7 @@ LL | Box::new(Box::new([1, 2])).into_iter(); = warning: this changes meaning in Rust 2021 = note: for more information, see -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:43:29 | LL | Box::new(Box::new(big)).into_iter(); @@ -130,7 +130,7 @@ LL | Box::new(Box::new(big)).into_iter(); = warning: this changes meaning in Rust 2021 = note: for more information, see -warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <[T; N] as IntoIterator>::into_iter in Rust 2021 +warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:46:35 | LL | Box::new(Box::new([0u8; 33])).into_iter(); diff --git a/tests/ui/iterators/into-iter-on-boxed-slices-2021.rs b/tests/ui/iterators/into-iter-on-boxed-slices-2021.rs new file mode 100644 index 000000000000..3d9d131e59c2 --- /dev/null +++ b/tests/ui/iterators/into-iter-on-boxed-slices-2021.rs @@ -0,0 +1,46 @@ +//@ check-pass +//@ edition:2018 + +use std::ops::Deref; +use std::rc::Rc; +use std::slice::Iter; +use std::vec::IntoIter; + +fn main() { + let boxed_slice = vec![0; 10].into_boxed_slice(); + + // Before 2024, the method dispatched to `IntoIterator for Box<[T]>`, + // which we continue to support for compatibility. + let _: Iter<'_, i32> = boxed_slice.into_iter(); + //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` + //~| WARNING this changes meaning + + let _: Iter<'_, i32> = Box::new(boxed_slice.clone()).into_iter(); + //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` + //~| WARNING this changes meaning + + let _: Iter<'_, i32> = Rc::new(boxed_slice.clone()).into_iter(); + //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` + //~| WARNING this changes meaning + let _: Iter<'_, i32> = Array(boxed_slice.clone()).into_iter(); + //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` + //~| WARNING this changes meaning + + // But you can always use the trait method explicitly as an boxed_slice. + let _: IntoIter = IntoIterator::into_iter(boxed_slice); + + for _ in (Box::new([1, 2, 3]) as Box<[_]>).into_iter() {} + //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` + //~| WARNING this changes meaning +} + +/// User type that dereferences to a boxed slice. +struct Array(Box<[i32]>); + +impl Deref for Array { + type Target = Box<[i32]>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/tests/ui/iterators/into-iter-on-boxed-slices-2021.stderr b/tests/ui/iterators/into-iter-on-boxed-slices-2021.stderr new file mode 100644 index 000000000000..d7f38a23725b --- /dev/null +++ b/tests/ui/iterators/into-iter-on-boxed-slices-2021.stderr @@ -0,0 +1,60 @@ +warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to ` as IntoIterator>::into_iter` in Rust 2024 + --> $DIR/into-iter-on-boxed-slices-2021.rs:14:40 + | +LL | let _: Iter<'_, i32> = boxed_slice.into_iter(); + | ^^^^^^^^^ + | + = warning: this changes meaning in Rust 2024 + = note: `#[warn(boxed_slice_into_iter)]` on by default +help: use `.iter()` instead of `.into_iter()` to avoid ambiguity + | +LL | let _: Iter<'_, i32> = boxed_slice.iter(); + | ~~~~ +help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value + | +LL | let _: Iter<'_, i32> = IntoIterator::into_iter(boxed_slice); + | ++++++++++++++++++++++++ ~ + +warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to ` as IntoIterator>::into_iter` in Rust 2024 + --> $DIR/into-iter-on-boxed-slices-2021.rs:18:58 + | +LL | let _: Iter<'_, i32> = Box::new(boxed_slice.clone()).into_iter(); + | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter` + | + = warning: this changes meaning in Rust 2024 + +warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to ` as IntoIterator>::into_iter` in Rust 2024 + --> $DIR/into-iter-on-boxed-slices-2021.rs:22:57 + | +LL | let _: Iter<'_, i32> = Rc::new(boxed_slice.clone()).into_iter(); + | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter` + | + = warning: this changes meaning in Rust 2024 + +warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to ` as IntoIterator>::into_iter` in Rust 2024 + --> $DIR/into-iter-on-boxed-slices-2021.rs:25:55 + | +LL | let _: Iter<'_, i32> = Array(boxed_slice.clone()).into_iter(); + | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter` + | + = warning: this changes meaning in Rust 2024 + +warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to ` as IntoIterator>::into_iter` in Rust 2024 + --> $DIR/into-iter-on-boxed-slices-2021.rs:32:48 + | +LL | for _ in (Box::new([1, 2, 3]) as Box<[_]>).into_iter() {} + | ^^^^^^^^^ + | + = warning: this changes meaning in Rust 2024 +help: use `.iter()` instead of `.into_iter()` to avoid ambiguity + | +LL | for _ in (Box::new([1, 2, 3]) as Box<[_]>).iter() {} + | ~~~~ +help: or remove `.into_iter()` to iterate by value + | +LL - for _ in (Box::new([1, 2, 3]) as Box<[_]>).into_iter() {} +LL + for _ in (Box::new([1, 2, 3]) as Box<[_]>) {} + | + +warning: 5 warnings emitted + diff --git a/tests/ui/iterators/into-iter-on-boxed-slices-2024.rs b/tests/ui/iterators/into-iter-on-boxed-slices-2024.rs new file mode 100644 index 000000000000..ffd6f022bc6d --- /dev/null +++ b/tests/ui/iterators/into-iter-on-boxed-slices-2024.rs @@ -0,0 +1,20 @@ +//@ check-pass +//@ edition:2024 +//@ compile-flags: -Zunstable-options + +use std::ops::Deref; +use std::rc::Rc; +use std::vec::IntoIter; + +fn main() { + let boxed_slice = vec![0; 10].into_boxed_slice(); + + // In 2021, the method dispatches to `IntoIterator for [T; N]`. + let _: IntoIter = boxed_slice.clone().into_iter(); + + // And through other boxes. + let _: IntoIter = Box::new(boxed_slice.clone()).into_iter(); + + // You can always use the trait method explicitly as a boxed_slice. + let _: IntoIter = IntoIterator::into_iter(boxed_slice.clone()); +} diff --git a/tests/ui/iterators/into-iter-on-boxed-slices-lint.fixed b/tests/ui/iterators/into-iter-on-boxed-slices-lint.fixed new file mode 100644 index 000000000000..265a6c764d20 --- /dev/null +++ b/tests/ui/iterators/into-iter-on-boxed-slices-lint.fixed @@ -0,0 +1,30 @@ +//@ run-pass +//@ run-rustfix +//@ rustfix-only-machine-applicable + +#[allow(unused_must_use, unused_allocation)] +fn main() { + let boxed = vec![1, 2].into_boxed_slice(); + + // Expressions that should trigger the lint + boxed.iter(); + //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` + //~| WARNING this changes meaning + Box::new(boxed.clone()).iter(); + //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` + //~| WARNING this changes meaning + Box::new(Box::new(boxed.clone())).iter(); + //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` + //~| WARNING this changes meaning + + // Expressions that should not + (&boxed).into_iter(); + + for _ in &boxed {} + (&boxed as &[_]).into_iter(); + boxed[..].into_iter(); + std::iter::IntoIterator::into_iter(&boxed); + + #[allow(boxed_slice_into_iter)] + boxed.into_iter(); +} diff --git a/tests/ui/iterators/into-iter-on-boxed-slices-lint.rs b/tests/ui/iterators/into-iter-on-boxed-slices-lint.rs new file mode 100644 index 000000000000..dd78e481e0e9 --- /dev/null +++ b/tests/ui/iterators/into-iter-on-boxed-slices-lint.rs @@ -0,0 +1,30 @@ +//@ run-pass +//@ run-rustfix +//@ rustfix-only-machine-applicable + +#[allow(unused_must_use, unused_allocation)] +fn main() { + let boxed = vec![1, 2].into_boxed_slice(); + + // Expressions that should trigger the lint + boxed.into_iter(); + //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` + //~| WARNING this changes meaning + Box::new(boxed.clone()).into_iter(); + //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` + //~| WARNING this changes meaning + Box::new(Box::new(boxed.clone())).into_iter(); + //~^ WARNING this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` + //~| WARNING this changes meaning + + // Expressions that should not + (&boxed).into_iter(); + + for _ in &boxed {} + (&boxed as &[_]).into_iter(); + boxed[..].into_iter(); + std::iter::IntoIterator::into_iter(&boxed); + + #[allow(boxed_slice_into_iter)] + boxed.into_iter(); +} diff --git a/tests/ui/iterators/into-iter-on-boxed-slices-lint.stderr b/tests/ui/iterators/into-iter-on-boxed-slices-lint.stderr new file mode 100644 index 000000000000..b73faf0dbd3b --- /dev/null +++ b/tests/ui/iterators/into-iter-on-boxed-slices-lint.stderr @@ -0,0 +1,35 @@ +warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to ` as IntoIterator>::into_iter` in Rust 2024 + --> $DIR/into-iter-on-boxed-slices-lint.rs:10:11 + | +LL | boxed.into_iter(); + | ^^^^^^^^^ + | + = warning: this changes meaning in Rust 2024 + = note: `#[warn(boxed_slice_into_iter)]` on by default +help: use `.iter()` instead of `.into_iter()` to avoid ambiguity + | +LL | boxed.iter(); + | ~~~~ +help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value + | +LL | IntoIterator::into_iter(boxed); + | ++++++++++++++++++++++++ ~ + +warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to ` as IntoIterator>::into_iter` in Rust 2024 + --> $DIR/into-iter-on-boxed-slices-lint.rs:13:29 + | +LL | Box::new(boxed.clone()).into_iter(); + | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter` + | + = warning: this changes meaning in Rust 2024 + +warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to ` as IntoIterator>::into_iter` in Rust 2024 + --> $DIR/into-iter-on-boxed-slices-lint.rs:16:39 + | +LL | Box::new(Box::new(boxed.clone())).into_iter(); + | ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter` + | + = warning: this changes meaning in Rust 2024 + +warning: 3 warnings emitted + diff --git a/tests/ui/iterators/invalid-iterator-chain-fixable.stderr b/tests/ui/iterators/invalid-iterator-chain-fixable.stderr index 4d1e4207fdcb..a7685e4938d8 100644 --- a/tests/ui/iterators/invalid-iterator-chain-fixable.stderr +++ b/tests/ui/iterators/invalid-iterator-chain-fixable.stderr @@ -33,8 +33,8 @@ LL | println!("{}", scores.sum::()); | = help: the trait `Sum<()>` is not implemented for `i32` = help: the following other types implement trait `Sum`: - > - + `i32` implements `Sum<&'a i32>` + `i32` implements `Sum` note: the method call chain might not have had the expected associated types --> $DIR/invalid-iterator-chain-fixable.rs:14:10 | @@ -66,8 +66,8 @@ LL | .sum::(), | = help: the trait `Sum<()>` is not implemented for `i32` = help: the following other types implement trait `Sum`: - > - + `i32` implements `Sum<&'a i32>` + `i32` implements `Sum` note: the method call chain might not have had the expected associated types --> $DIR/invalid-iterator-chain-fixable.rs:23:14 | @@ -99,8 +99,8 @@ LL | println!("{}", vec![0, 1].iter().map(|x| { x; }).sum::()); | = help: the trait `Sum<()>` is not implemented for `i32` = help: the following other types implement trait `Sum`: - > - + `i32` implements `Sum<&'a i32>` + `i32` implements `Sum` note: the method call chain might not have had the expected associated types --> $DIR/invalid-iterator-chain-fixable.rs:27:38 | diff --git a/tests/ui/iterators/invalid-iterator-chain-with-int-infer.stderr b/tests/ui/iterators/invalid-iterator-chain-with-int-infer.stderr index 9381a98e0778..189f089ba51a 100644 --- a/tests/ui/iterators/invalid-iterator-chain-with-int-infer.stderr +++ b/tests/ui/iterators/invalid-iterator-chain-with-int-infer.stderr @@ -8,8 +8,8 @@ LL | let x = Some(()).iter().map(|()| 1).sum::(); | = help: the trait `Sum<{integer}>` is not implemented for `f32` = help: the following other types implement trait `Sum`: - > - + `f32` implements `Sum<&'a f32>` + `f32` implements `Sum` note: the method call chain might not have had the expected associated types --> $DIR/invalid-iterator-chain-with-int-infer.rs:2:29 | diff --git a/tests/ui/iterators/invalid-iterator-chain.stderr b/tests/ui/iterators/invalid-iterator-chain.stderr index 6129a724f447..f72a9f702dce 100644 --- a/tests/ui/iterators/invalid-iterator-chain.stderr +++ b/tests/ui/iterators/invalid-iterator-chain.stderr @@ -33,8 +33,8 @@ LL | println!("{}", scores.sum::()); | = help: the trait `Sum<()>` is not implemented for `i32` = help: the following other types implement trait `Sum`: - > - + `i32` implements `Sum<&'a i32>` + `i32` implements `Sum` note: the method call chain might not have had the expected associated types --> $DIR/invalid-iterator-chain.rs:12:10 | @@ -65,8 +65,8 @@ LL | .sum::(), | = help: the trait `Sum<()>` is not implemented for `i32` = help: the following other types implement trait `Sum`: - > - + `i32` implements `Sum<&'a i32>` + `i32` implements `Sum` note: the method call chain might not have had the expected associated types --> $DIR/invalid-iterator-chain.rs:25:14 | @@ -104,8 +104,8 @@ LL | .sum::(), | = help: the trait `Sum` is not implemented for `i32` = help: the following other types implement trait `Sum`: - > - + `i32` implements `Sum<&'a i32>` + `i32` implements `Sum` note: the method call chain might not have had the expected associated types --> $DIR/invalid-iterator-chain.rs:33:14 | @@ -134,8 +134,8 @@ LL | println!("{}", vec![0, 1].iter().map(|x| { x; }).sum::()); | = help: the trait `Sum<()>` is not implemented for `i32` = help: the following other types implement trait `Sum`: - > - + `i32` implements `Sum<&'a i32>` + `i32` implements `Sum` note: the method call chain might not have had the expected associated types --> $DIR/invalid-iterator-chain.rs:38:38 | @@ -162,8 +162,8 @@ LL | println!("{}", vec![(), ()].iter().sum::()); | = help: the trait `Sum<&()>` is not implemented for `i32` = help: the following other types implement trait `Sum`: - > - + `i32` implements `Sum<&'a i32>` + `i32` implements `Sum` note: the method call chain might not have had the expected associated types --> $DIR/invalid-iterator-chain.rs:39:33 | diff --git a/tests/ui/kindck/kindck-send-object.stderr b/tests/ui/kindck/kindck-send-object.stderr index 9f1ff4f3644c..7d0c711abc4c 100644 --- a/tests/ui/kindck/kindck-send-object.stderr +++ b/tests/ui/kindck/kindck-send-object.stderr @@ -4,7 +4,7 @@ error[E0277]: `&'static (dyn Dummy + 'static)` cannot be sent between threads sa LL | assert_send::<&'static (dyn Dummy + 'static)>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&'static (dyn Dummy + 'static)` cannot be sent between threads safely | - = help: the trait `Sync` is not implemented for `&'static (dyn Dummy + 'static)`, which is required by `&'static (dyn Dummy + 'static): Send` + = help: the trait `Sync` is not implemented for `(dyn Dummy + 'static)`, which is required by `&'static (dyn Dummy + 'static): Send` = note: required for `&'static (dyn Dummy + 'static)` to implement `Send` note: required by a bound in `assert_send` --> $DIR/kindck-send-object.rs:5:18 diff --git a/tests/ui/kindck/kindck-send-object1.stderr b/tests/ui/kindck/kindck-send-object1.stderr index f2aa814676fc..7f39dab2086a 100644 --- a/tests/ui/kindck/kindck-send-object1.stderr +++ b/tests/ui/kindck/kindck-send-object1.stderr @@ -4,7 +4,7 @@ error[E0277]: `&'a (dyn Dummy + 'a)` cannot be sent between threads safely LL | assert_send::<&'a dyn Dummy>(); | ^^^^^^^^^^^^^ `&'a (dyn Dummy + 'a)` cannot be sent between threads safely | - = help: the trait `Sync` is not implemented for `&'a (dyn Dummy + 'a)`, which is required by `&'a (dyn Dummy + 'a): Send` + = help: the trait `Sync` is not implemented for `(dyn Dummy + 'a)`, which is required by `&'a (dyn Dummy + 'a): Send` = note: required for `&'a (dyn Dummy + 'a)` to implement `Send` note: required by a bound in `assert_send` --> $DIR/kindck-send-object1.rs:5:18 diff --git a/tests/ui/kindck/kindck-send-object2.stderr b/tests/ui/kindck/kindck-send-object2.stderr index cd4d74360f86..a481a132ccef 100644 --- a/tests/ui/kindck/kindck-send-object2.stderr +++ b/tests/ui/kindck/kindck-send-object2.stderr @@ -4,7 +4,7 @@ error[E0277]: `&'static (dyn Dummy + 'static)` cannot be sent between threads sa LL | assert_send::<&'static dyn Dummy>(); | ^^^^^^^^^^^^^^^^^^ `&'static (dyn Dummy + 'static)` cannot be sent between threads safely | - = help: the trait `Sync` is not implemented for `&'static (dyn Dummy + 'static)`, which is required by `&'static (dyn Dummy + 'static): Send` + = help: the trait `Sync` is not implemented for `(dyn Dummy + 'static)`, which is required by `&'static (dyn Dummy + 'static): Send` = note: required for `&'static (dyn Dummy + 'static)` to implement `Send` note: required by a bound in `assert_send` --> $DIR/kindck-send-object2.rs:3:18 diff --git a/tests/ui/layout/issue-84108.rs b/tests/ui/layout/issue-84108.rs index 425da65b9905..974d5310f6bb 100644 --- a/tests/ui/layout/issue-84108.rs +++ b/tests/ui/layout/issue-84108.rs @@ -8,6 +8,9 @@ static FOO: (dyn AsRef, u8) = ("hello", 42); const BAR: (&Path, [u8], usize) = ("hello", [], 42); //~^ ERROR cannot find type `Path` in this scope +//~| ERROR the size for values of type `[u8]` cannot be known at compilation time +//~| ERROR the size for values of type `[u8]` cannot be known at compilation time +//~| ERROR mismatched types static BAZ: ([u8], usize) = ([], 0); //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time diff --git a/tests/ui/layout/issue-84108.stderr b/tests/ui/layout/issue-84108.stderr index 6c168cc5fa84..8ddce285e23f 100644 --- a/tests/ui/layout/issue-84108.stderr +++ b/tests/ui/layout/issue-84108.stderr @@ -21,7 +21,35 @@ LL + use std::path::Path; | error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/issue-84108.rs:12:13 + --> $DIR/issue-84108.rs:9:12 + | +LL | const BAR: (&Path, [u8], usize) = ("hello", [], 42); + | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: only the last element of a tuple may have a dynamically sized type + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/issue-84108.rs:9:12 + | +LL | const BAR: (&Path, [u8], usize) = ("hello", [], 42); + | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: only the last element of a tuple may have a dynamically sized type + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0308]: mismatched types + --> $DIR/issue-84108.rs:9:45 + | +LL | const BAR: (&Path, [u8], usize) = ("hello", [], 42); + | ^^ expected `[u8]`, found `[_; 0]` + | + = note: expected slice `[u8]` + found array `[_; 0]` + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/issue-84108.rs:15:13 | LL | static BAZ: ([u8], usize) = ([], 0); | ^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -30,7 +58,7 @@ LL | static BAZ: ([u8], usize) = ([], 0); = note: only the last element of a tuple may have a dynamically sized type error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/issue-84108.rs:12:13 + --> $DIR/issue-84108.rs:15:13 | LL | static BAZ: ([u8], usize) = ([], 0); | ^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -40,7 +68,7 @@ LL | static BAZ: ([u8], usize) = ([], 0); = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0308]: mismatched types - --> $DIR/issue-84108.rs:12:30 + --> $DIR/issue-84108.rs:15:30 | LL | static BAZ: ([u8], usize) = ([], 0); | ^^ expected `[u8]`, found `[_; 0]` @@ -48,7 +76,7 @@ LL | static BAZ: ([u8], usize) = ([], 0); = note: expected slice `[u8]` found array `[_; 0]` -error: aborting due to 5 previous errors +error: aborting due to 8 previous errors Some errors have detailed explanations: E0277, E0308, E0412. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/lazy-type-alias/trailing-where-clause.stderr b/tests/ui/lazy-type-alias/trailing-where-clause.stderr index 534df382eb6e..9fabbe91d25a 100644 --- a/tests/ui/lazy-type-alias/trailing-where-clause.stderr +++ b/tests/ui/lazy-type-alias/trailing-where-clause.stderr @@ -5,12 +5,12 @@ LL | let _: Alias<()>; | ^^ the trait `From<()>` is not implemented for `String` | = help: the following other types implement trait `From`: - > - > - > - >> - >> - > + `String` implements `From<&String>` + `String` implements `From<&mut str>` + `String` implements `From<&str>` + `String` implements `From>` + `String` implements `From>` + `String` implements `From` note: required by a bound in `Alias` --> $DIR/trailing-where-clause.rs:8:13 | diff --git a/tests/ui/lifetimes/issue-17728.stderr b/tests/ui/lifetimes/issue-17728.stderr index 23547f722a11..eb4381ea99ee 100644 --- a/tests/ui/lifetimes/issue-17728.stderr +++ b/tests/ui/lifetimes/issue-17728.stderr @@ -29,8 +29,8 @@ LL | Some(entry) => Ok(entry), | help: consider introducing a named lifetime parameter | -LL | fn attemptTraverse<'a>(&'a self, room: &'a Room, directionStr: &str) -> Result<&Room, &str> { - | ++++ ++ ++ +LL | fn attemptTraverse<'a>(&self, room: &'a Room, directionStr: &str) -> Result<&'a Room, &'a str> { + | ++++ ++ ++ ++ error: aborting due to 2 previous errors diff --git a/tests/ui/lifetimes/issue-69314.fixed b/tests/ui/lifetimes/issue-69314.fixed index 285d192b44c7..ee1675724fb1 100644 --- a/tests/ui/lifetimes/issue-69314.fixed +++ b/tests/ui/lifetimes/issue-69314.fixed @@ -11,7 +11,7 @@ impl A { } async fn f() { let mut buf = [0; 512]; - let m2 = &buf[..]; //~ ERROR `buf` does not live long enough + let m2 = &buf[..]; let m = Self::g(m2).await; Self::f2(m).await; } diff --git a/tests/ui/lifetimes/issue-69314.rs b/tests/ui/lifetimes/issue-69314.rs index 345f77850608..f9e5196b2c74 100644 --- a/tests/ui/lifetimes/issue-69314.rs +++ b/tests/ui/lifetimes/issue-69314.rs @@ -11,7 +11,7 @@ impl A { } async fn f() { let mut buf = [0; 512]; - let m2 = &buf[..]; //~ ERROR `buf` does not live long enough + let m2 = &buf[..]; let m = Self::g(m2).await; Self::f2(m).await; } diff --git a/tests/ui/lifetimes/issue-69314.stderr b/tests/ui/lifetimes/issue-69314.stderr index 3879f35505c2..67da54e8dcd7 100644 --- a/tests/ui/lifetimes/issue-69314.stderr +++ b/tests/ui/lifetimes/issue-69314.stderr @@ -9,20 +9,6 @@ help: indicate the anonymous lifetime LL | async fn f2(m: Msg<'_>) {} | ++++ -error[E0597]: `buf` does not live long enough - --> $DIR/issue-69314.rs:14:19 - | -LL | let mut buf = [0; 512]; - | ------- binding `buf` declared here -LL | let m2 = &buf[..]; - | ^^^ borrowed value does not live long enough -LL | let m = Self::g(m2).await; - | ----------- argument requires that `buf` is borrowed for `'static` -LL | Self::f2(m).await; -LL | } - | - `buf` dropped here while still borrowed +error: aborting due to 1 previous error -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0597, E0726. -For more information about an error, try `rustc --explain E0597`. +For more information about this error, try `rustc --explain E0726`. diff --git a/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr b/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr index e9f97d1d93bd..e8c3ab00226e 100644 --- a/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr +++ b/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr @@ -10,7 +10,7 @@ LL | | F:, LL | | for<'a> >::Output: Future + 'a, | |__________________________________________________________________________^ expected an `FnOnce(&'a mut i32)` closure, found `i32` | - = help: the trait `for<'a> FnOnce<(&'a mut i32,)>` is not implemented for `i32` + = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` --> $DIR/issue-76168-hr-outlives-3.rs:6:10 @@ -18,7 +18,7 @@ error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` LL | async fn wrapper(f: F) | ^^^^^^^ expected an `FnOnce(&'a mut i32)` closure, found `i32` | - = help: the trait `for<'a> FnOnce<(&'a mut i32,)>` is not implemented for `i32` + = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` --> $DIR/issue-76168-hr-outlives-3.rs:6:1 @@ -32,7 +32,7 @@ LL | | F:, LL | | for<'a> >::Output: Future + 'a, | |__________________________________________________________________________^ expected an `FnOnce(&'a mut i32)` closure, found `i32` | - = help: the trait `for<'a> FnOnce<(&'a mut i32,)>` is not implemented for `i32` + = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` --> $DIR/issue-76168-hr-outlives-3.rs:6:1 @@ -46,7 +46,7 @@ LL | | F:, LL | | for<'a> >::Output: Future + 'a, | |__________________________________________________________________________^ expected an `FnOnce(&'a mut i32)` closure, found `i32` | - = help: the trait `for<'a> FnOnce<(&'a mut i32,)>` is not implemented for `i32` + = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` @@ -59,7 +59,7 @@ LL | | &mut i; LL | | } | |_^ expected an `FnOnce(&'a mut i32)` closure, found `i32` | - = help: the trait `for<'a> FnOnce<(&'a mut i32,)>` is not implemented for `i32` + = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` error: aborting due to 5 previous errors diff --git a/tests/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs b/tests/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs index a47e71afcf0b..78069e682c13 100644 --- a/tests/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs +++ b/tests/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs @@ -3,6 +3,6 @@ struct Foo {} impl Foo { fn bar(foo: Foo) {} - //~^ associated type bindings are not allowed here + //~^ associated item constraints are not allowed here } fn main() {} diff --git a/tests/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.stderr b/tests/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.stderr index d6da842e6aba..f8d919fd68b6 100644 --- a/tests/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.stderr +++ b/tests/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.stderr @@ -1,10 +1,10 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-83753-invalid-associated-type-supertrait-hrtb.rs:5:21 | LL | fn bar(foo: Foo) {} - | ^^^^^^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | LL | fn bar(foo: Foo) {} | ~~~~~~~~~~~~~~~~ diff --git a/tests/ui/lifetimes/issue-90170-elision-mismatch.stderr b/tests/ui/lifetimes/issue-90170-elision-mismatch.stderr index 48fb3fb4a229..82511d07b0ef 100644 --- a/tests/ui/lifetimes/issue-90170-elision-mismatch.stderr +++ b/tests/ui/lifetimes/issue-90170-elision-mismatch.stderr @@ -35,7 +35,7 @@ LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&u8>, y: &u8) { x.push(y); } | | let's call the lifetime of this reference `'1` | let's call the lifetime of this reference `'2` | -help: consider introducing a named lifetime parameter +help: consider reusing a named lifetime parameter | LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } | ++ ++ diff --git a/tests/ui/lifetimes/issue-95023.rs b/tests/ui/lifetimes/issue-95023.rs index ee39a8c49c05..7a67297c7632 100644 --- a/tests/ui/lifetimes/issue-95023.rs +++ b/tests/ui/lifetimes/issue-95023.rs @@ -2,7 +2,7 @@ struct ErrorKind; struct Error(ErrorKind); impl Fn(&isize) for Error { //~^ ERROR manual implementations of `Fn` are experimental [E0183] - //~^^ ERROR associated type bindings are not allowed here [E0229] + //~^^ ERROR associated item constraints are not allowed here [E0229] //~| ERROR not all trait items implemented //~| ERROR expected a `FnMut(&isize)` closure, found `Error` fn foo(&self) -> Self::B<{ N }>; diff --git a/tests/ui/lifetimes/issue-95023.stderr b/tests/ui/lifetimes/issue-95023.stderr index c4285dbf4bdb..310dee514060 100644 --- a/tests/ui/lifetimes/issue-95023.stderr +++ b/tests/ui/lifetimes/issue-95023.stderr @@ -20,11 +20,11 @@ LL | impl Fn(&isize) for Error { | = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-95023.rs:3:6 | LL | impl Fn(&isize) for Error { - | ^^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^^ associated item constraint not allowed here | help: parenthesized trait syntax expands to `Fn<(&isize,), Output=()>` --> $DIR/issue-95023.rs:3:6 @@ -38,7 +38,7 @@ error[E0277]: expected a `FnMut(&isize)` closure, found `Error` LL | impl Fn(&isize) for Error { | ^^^^^ expected an `FnMut(&isize)` closure, found `Error` | - = help: the trait `FnMut<(&isize,)>` is not implemented for `Error` + = help: the trait `FnMut(&isize)` is not implemented for `Error` note: required by a bound in `Fn` --> $SRC_DIR/core/src/ops/function.rs:LL:COL diff --git a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.stderr b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.stderr index 5827b2fe695a..2caab3df6b85 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl.stderr @@ -8,6 +8,11 @@ LL | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 { LL | LL | if x > y { x } else { y } | ^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1` + | +help: consider reusing a named lifetime parameter and update trait if needed + | +LL | fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { + | ++ error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.fixed b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.fixed new file mode 100644 index 000000000000..40eee22fc321 --- /dev/null +++ b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.fixed @@ -0,0 +1,14 @@ +//@ run-rustfix +#![allow(dead_code)] +struct Foo { + field: i32, +} + +impl Foo { + fn foo<'a>(&self, x: &'a i32) -> &'a i32 { + x + //~^ ERROR lifetime may not live long enough + } +} + +fn main() {} diff --git a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.rs b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.rs index 49993aca3cad..c8dd958b37c6 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.rs +++ b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.rs @@ -1,15 +1,14 @@ +//@ run-rustfix +#![allow(dead_code)] struct Foo { - field: i32 + field: i32, } impl Foo { - fn foo<'a>(&self, x: &'a i32) -> &i32 { - - x - //~^ ERROR lifetime may not live long enough - - } - + fn foo<'a>(&self, x: &'a i32) -> &i32 { + x + //~^ ERROR lifetime may not live long enough + } } -fn main() { } +fn main() {} diff --git a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.stderr b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.stderr index da454b8fd863..c743351a6702 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.stderr @@ -1,13 +1,17 @@ error: lifetime may not live long enough - --> $DIR/ex1-return-one-existing-name-return-type-is-anon.rs:8:5 + --> $DIR/ex1-return-one-existing-name-return-type-is-anon.rs:9:9 | -LL | fn foo<'a>(&self, x: &'a i32) -> &i32 { - | -- - let's call the lifetime of this reference `'1` - | | - | lifetime `'a` defined here -LL | -LL | x - | ^ method was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a` +LL | fn foo<'a>(&self, x: &'a i32) -> &i32 { + | -- - let's call the lifetime of this reference `'1` + | | + | lifetime `'a` defined here +LL | x + | ^ method was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a` + | +help: consider reusing a named lifetime parameter and update trait if needed + | +LL | fn foo<'a>(&self, x: &'a i32) -> &'a i32 { + | ++ error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-self-is-anon.stderr b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-self-is-anon.stderr index a1dfff883a00..4ed6c3bc92d7 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-self-is-anon.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex1-return-one-existing-name-self-is-anon.stderr @@ -8,6 +8,11 @@ LL | fn foo<'a>(&self, x: &'a Foo) -> &'a Foo { LL | LL | if true { x } else { self } | ^^^^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1` + | +help: consider reusing a named lifetime parameter and update trait if needed + | +LL | fn foo<'a>(&'a self, x: &'a Foo) -> &'a Foo { + | ++ error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.stderr b/tests/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.stderr index df1d03d51703..6f7127d4c4c4 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.stderr @@ -7,6 +7,11 @@ LL | fn foo(x: &mut Vec>, y: Ref) { | has type `&mut Vec>` LL | x.push(y); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` + | +help: consider introducing a named lifetime parameter + | +LL | fn foo<'a>(x: &mut Vec>, y: Ref<'a, i32>) { + | ++++ +++ +++ error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-2.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-2.stderr index 4f1cb1e79722..f8ad923cce8a 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-2.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-2.stderr @@ -7,6 +7,11 @@ LL | fn foo(mut x: Ref, y: Ref) { | has type `Ref<'_, '2>` LL | x.b = y.b; | ^^^^^^^^^ assignment requires that `'1` must outlive `'2` + | +help: consider introducing a named lifetime parameter + | +LL | fn foo<'a>(mut x: Ref<'a, 'a>, y: Ref<'a, 'a>) { + | ++++ ++++++++ ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-3.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-3.stderr index 3a1947973b55..a1149779d2c6 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-3.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-3.stderr @@ -8,6 +8,11 @@ LL | fn foo(mut x: Ref) { | has type `Ref<'2, '_>` LL | x.a = x.b; | ^^^^^^^^^ assignment requires that `'1` must outlive `'2` + | +help: consider introducing a named lifetime parameter + | +LL | fn foo<'a>(mut x: Ref<'a, 'a>) { + | ++++ ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr index c778f77366ab..017bfa714631 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr @@ -7,6 +7,11 @@ LL | fn foo(mut x: Vec, y: Ref) { | has type `Vec>` LL | x.push(y); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` + | +help: consider introducing a named lifetime parameter + | +LL | fn foo<'a>(mut x: Vec>, y: Ref<'a>) { + | ++++ ++++ ++++ error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.stderr index bbd62902d9f4..0980de92d350 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.stderr @@ -7,6 +7,11 @@ LL | fn foo(mut x: Ref, y: &u32) { | has type `Ref<'_, '1>` LL | y = x.b; | ^^^^^^^ assignment requires that `'1` must outlive `'2` + | +help: consider introducing a named lifetime parameter + | +LL | fn foo<'a>(mut x: Ref<'a, 'a>, y: &'a u32) { + | ++++ ++++++++ ++ error[E0384]: cannot assign to immutable argument `y` --> $DIR/ex3-both-anon-regions-one-is-struct-2.rs:4:5 diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-3.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-3.stderr index 60ca41684551..d9c7530b64c5 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-3.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-3.stderr @@ -7,6 +7,11 @@ LL | fn foo(mut y: Ref, x: &u32) { | has type `Ref<'_, '2>` LL | y.b = x; | ^^^^^^^ assignment requires that `'1` must outlive `'2` + | +help: consider introducing a named lifetime parameter + | +LL | fn foo<'a>(mut y: Ref<'a, 'a>, x: &'a u32) { + | ++++ ++++++++ ++ error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-4.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-4.stderr index 98e490c3827b..d07b821444cc 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-4.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-4.stderr @@ -7,6 +7,11 @@ LL | fn foo(mut y: Ref, x: &u32) { | has type `Ref<'_, '2>` LL | y.b = x; | ^^^^^^^ assignment requires that `'1` must outlive `'2` + | +help: consider introducing a named lifetime parameter + | +LL | fn foo<'a>(mut y: Ref<'a, 'a>, x: &'a u32) { + | ++++ ++++++++ ++ error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct.stderr index ea1cc1f18a4d..ef28ddeacc5e 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct.stderr @@ -7,6 +7,11 @@ LL | fn foo(mut x: Ref, y: &u32) { | has type `Ref<'_, '2>` LL | x.b = y; | ^^^^^^^ assignment requires that `'1` must outlive `'2` + | +help: consider introducing a named lifetime parameter + | +LL | fn foo<'a>(mut x: Ref<'a, 'a>, y: &'a u32) { + | ++++ ++++++++ ++ error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.fixed b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.fixed new file mode 100644 index 000000000000..40eee22fc321 --- /dev/null +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.fixed @@ -0,0 +1,14 @@ +//@ run-rustfix +#![allow(dead_code)] +struct Foo { + field: i32, +} + +impl Foo { + fn foo<'a>(&self, x: &'a i32) -> &'a i32 { + x + //~^ ERROR lifetime may not live long enough + } +} + +fn main() {} diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.rs b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.rs index 3ffd7be4e73f..0225d8c79616 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.rs +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.rs @@ -1,12 +1,14 @@ +//@ run-rustfix +#![allow(dead_code)] struct Foo { - field: i32 + field: i32, } impl Foo { - fn foo<'a>(&self, x: &i32) -> &i32 { - x - //~^ ERROR lifetime may not live long enough - } + fn foo<'a>(&self, x: &i32) -> &i32 { + x + //~^ ERROR lifetime may not live long enough + } } -fn main() { } +fn main() {} diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.stderr index e934ce7c040e..673a87a4537c 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-return-type-is-anon.stderr @@ -1,17 +1,17 @@ error: lifetime may not live long enough - --> $DIR/ex3-both-anon-regions-return-type-is-anon.rs:7:5 + --> $DIR/ex3-both-anon-regions-return-type-is-anon.rs:9:9 | -LL | fn foo<'a>(&self, x: &i32) -> &i32 { - | - - let's call the lifetime of this reference `'1` - | | - | let's call the lifetime of this reference `'2` -LL | x - | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` +LL | fn foo<'a>(&self, x: &i32) -> &i32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | x + | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` | -help: consider introducing a named lifetime parameter and update trait if needed +help: consider reusing a named lifetime parameter and update trait if needed | -LL | fn foo<'a>(&'a self, x: &'a i32) -> &i32 { - | ++ ++ +LL | fn foo<'a>(&self, x: &'a i32) -> &'a i32 { + | ++ ++ error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-self-is-anon.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-self-is-anon.stderr index dde271d100c5..64bd448a0747 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-self-is-anon.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-self-is-anon.stderr @@ -8,10 +8,10 @@ LL | fn foo<'a>(&self, x: &Foo) -> &Foo { LL | if true { x } else { self } | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` | -help: consider introducing a named lifetime parameter and update trait if needed +help: consider reusing a named lifetime parameter and update trait if needed | -LL | fn foo<'a>(&'a self, x: &'a Foo) -> &Foo { - | ++ ++ +LL | fn foo<'a>(&self, x: &'a Foo) -> &'a Foo { + | ++ ++ error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/no_lending_iterators.rs b/tests/ui/lifetimes/no_lending_iterators.rs new file mode 100644 index 000000000000..21395475fb3d --- /dev/null +++ b/tests/ui/lifetimes/no_lending_iterators.rs @@ -0,0 +1,35 @@ +struct Data(String); + +impl Iterator for Data { + type Item = &str; + //~^ ERROR 4:17: 4:18: associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + + fn next(&mut self) -> Option { + Some(&self.0) + } +} + +trait Bar { + type Item; + fn poke(&mut self, item: Self::Item); +} + +impl Bar for usize { + type Item = &usize; + //~^ ERROR 18:17: 18:18: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + + fn poke(&mut self, item: Self::Item) { + self += *item; + } +} + +impl Bar for isize { + type Item<'a> = &'a isize; + //~^ ERROR 27:14: 27:18: lifetime parameters or bounds on type `Item` do not match the trait declaration [E0195] + + fn poke(&mut self, item: Self::Item) { + self += *item; + } +} + +fn main() {} diff --git a/tests/ui/lifetimes/no_lending_iterators.stderr b/tests/ui/lifetimes/no_lending_iterators.stderr new file mode 100644 index 000000000000..c3784770d792 --- /dev/null +++ b/tests/ui/lifetimes/no_lending_iterators.stderr @@ -0,0 +1,30 @@ +error: associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + --> $DIR/no_lending_iterators.rs:4:17 + | +LL | type Item = &str; + | ^ + | +note: you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type. + --> $DIR/no_lending_iterators.rs:3:19 + | +LL | impl Iterator for Data { + | ^^^^ + +error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + --> $DIR/no_lending_iterators.rs:18:17 + | +LL | type Item = &usize; + | ^ this lifetime must come from the implemented type + +error[E0195]: lifetime parameters or bounds on type `Item` do not match the trait declaration + --> $DIR/no_lending_iterators.rs:27:14 + | +LL | type Item; + | - lifetimes in impl do not match this type in trait +... +LL | type Item<'a> = &'a isize; + | ^^^^ lifetimes do not match type in trait + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0195`. diff --git a/tests/ui/lifetimes/noisy-follow-up-erro.rs b/tests/ui/lifetimes/noisy-follow-up-erro.rs new file mode 100644 index 000000000000..47a87068d8fb --- /dev/null +++ b/tests/ui/lifetimes/noisy-follow-up-erro.rs @@ -0,0 +1,23 @@ +struct Foo<'c, 'd>(&'c (), &'d ()); + +impl<'c, 'd> Foo<'c, 'd> { + fn acc(&mut self, _bar: &Bar) -> &'d () { + todo!() + } +} + +struct Bar; + +impl<'a> Bar { + fn boom(&self, foo: &mut Foo<'_, '_, 'a>) -> Result<(), &'a ()> { + //~^ ERROR: struct takes 2 lifetime arguments but 3 lifetime arguments were supplied + self.bar().map_err(|()| foo.acc(self))?; + //~^ ERROR: explicit lifetime required in the type of `foo` + Ok(()) + } + fn bar(&self) -> Result<(), &'a ()> { + todo!() + } +} + +fn main() {} diff --git a/tests/ui/lifetimes/noisy-follow-up-erro.stderr b/tests/ui/lifetimes/noisy-follow-up-erro.stderr new file mode 100644 index 000000000000..f549009a87c1 --- /dev/null +++ b/tests/ui/lifetimes/noisy-follow-up-erro.stderr @@ -0,0 +1,27 @@ +error[E0107]: struct takes 2 lifetime arguments but 3 lifetime arguments were supplied + --> $DIR/noisy-follow-up-erro.rs:12:30 + | +LL | fn boom(&self, foo: &mut Foo<'_, '_, 'a>) -> Result<(), &'a ()> { + | ^^^ -- help: remove this lifetime argument + | | + | expected 2 lifetime arguments + | +note: struct defined here, with 2 lifetime parameters: `'c`, `'d` + --> $DIR/noisy-follow-up-erro.rs:1:8 + | +LL | struct Foo<'c, 'd>(&'c (), &'d ()); + | ^^^ -- -- + +error[E0621]: explicit lifetime required in the type of `foo` + --> $DIR/noisy-follow-up-erro.rs:14:9 + | +LL | fn boom(&self, foo: &mut Foo<'_, '_, 'a>) -> Result<(), &'a ()> { + | -------------------- help: add explicit lifetime `'a` to the type of `foo`: `&mut Foo<'_, 'a>` +LL | +LL | self.bar().map_err(|()| foo.acc(self))?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'a` required + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0107, E0621. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/lifetimes/refcell-in-tail-expr.edition2021.stderr b/tests/ui/lifetimes/refcell-in-tail-expr.edition2021.stderr new file mode 100644 index 000000000000..858be42d5409 --- /dev/null +++ b/tests/ui/lifetimes/refcell-in-tail-expr.edition2021.stderr @@ -0,0 +1,26 @@ +error[E0597]: `cell` does not live long enough + --> $DIR/refcell-in-tail-expr.rs:12:27 + | +LL | let cell = std::cell::RefCell::new(0u8); + | ---- binding `cell` declared here +LL | +LL | if let Ok(mut byte) = cell.try_borrow_mut() { + | ^^^^----------------- + | | + | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... +... +LL | } + | - + | | + | `cell` dropped here while still borrowed + | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Result, BorrowMutError>` + | +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + | +LL | }; + | + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/lifetimes/refcell-in-tail-expr.rs b/tests/ui/lifetimes/refcell-in-tail-expr.rs new file mode 100644 index 000000000000..b1814c1e3271 --- /dev/null +++ b/tests/ui/lifetimes/refcell-in-tail-expr.rs @@ -0,0 +1,16 @@ +//@ revisions: edition2021 edition2024 +//@ [edition2021] edition: 2021 +//@ [edition2024] edition: 2024 +//@ [edition2024] compile-flags: -Zunstable-options +//@ [edition2024] check-pass + +#![cfg_attr(edition2024, feature(shorter_tail_lifetimes))] + +fn main() { + let cell = std::cell::RefCell::new(0u8); + + if let Ok(mut byte) = cell.try_borrow_mut() { + //[edition2021]~^ ERROR: `cell` does not live long enough + *byte = 1; + } +} diff --git a/tests/ui/lifetimes/shorter-tail-expr-lifetime.edition2021.stderr b/tests/ui/lifetimes/shorter-tail-expr-lifetime.edition2021.stderr new file mode 100644 index 000000000000..ad28ae2f80d6 --- /dev/null +++ b/tests/ui/lifetimes/shorter-tail-expr-lifetime.edition2021.stderr @@ -0,0 +1,26 @@ +error[E0597]: `c` does not live long enough + --> $DIR/shorter-tail-expr-lifetime.rs:10:5 + | +LL | let c = std::cell::RefCell::new(".."); + | - binding `c` declared here +LL | c.borrow().len() + | ^--------- + | | + | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... +LL | } + | - + | | + | `c` dropped here while still borrowed + | ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, &str>` + | + = note: the temporary is part of an expression at the end of a block; + consider forcing this temporary to be dropped sooner, before the block's local variables are dropped +help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block + | +LL | let x = c.borrow().len(); x + | +++++++ +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/lifetimes/shorter-tail-expr-lifetime.rs b/tests/ui/lifetimes/shorter-tail-expr-lifetime.rs new file mode 100644 index 000000000000..0392b6c6d9ad --- /dev/null +++ b/tests/ui/lifetimes/shorter-tail-expr-lifetime.rs @@ -0,0 +1,15 @@ +//@ revisions: edition2021 edition2024 +//@ [edition2024] compile-flags: -Zunstable-options +//@ [edition2024] edition: 2024 +//@ [edition2024] run-pass + +#![cfg_attr(edition2024, feature(shorter_tail_lifetimes))] + +fn f() -> usize { + let c = std::cell::RefCell::new(".."); + c.borrow().len() //[edition2021]~ ERROR: `c` does not live long enough +} + +fn main() { + let _ = f(); +} diff --git a/tests/ui/lifetimes/tail-expr-in-nested-expr.rs b/tests/ui/lifetimes/tail-expr-in-nested-expr.rs new file mode 100644 index 000000000000..a8989f22f4b5 --- /dev/null +++ b/tests/ui/lifetimes/tail-expr-in-nested-expr.rs @@ -0,0 +1,9 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options + +#![feature(shorter_tail_lifetimes)] + +fn main() { + let _ = { String::new().as_str() }.len(); + //~^ ERROR temporary value dropped while borrowed +} diff --git a/tests/ui/lifetimes/tail-expr-in-nested-expr.stderr b/tests/ui/lifetimes/tail-expr-in-nested-expr.stderr new file mode 100644 index 000000000000..f699d184bdb1 --- /dev/null +++ b/tests/ui/lifetimes/tail-expr-in-nested-expr.stderr @@ -0,0 +1,15 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/tail-expr-in-nested-expr.rs:7:15 + | +LL | let _ = { String::new().as_str() }.len(); + | ^^^^^^^^^^^^^--------- + | | | + | | temporary value is freed at the end of this statement + | creates a temporary value which is freed while still in use + | borrow later used here + | + = note: consider using a `let` binding to create a longer lived value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0716`. diff --git a/tests/ui/lifetimes/tail-expr-lock-poisoning.rs b/tests/ui/lifetimes/tail-expr-lock-poisoning.rs new file mode 100644 index 000000000000..69b8f286d774 --- /dev/null +++ b/tests/ui/lifetimes/tail-expr-lock-poisoning.rs @@ -0,0 +1,29 @@ +//@ revisions: edition2021 edition2024 +//@ ignore-wasm no panic or subprocess support +//@ [edition2024] compile-flags: -Zunstable-options +//@ [edition2024] edition: 2024 +//@ run-pass +#![cfg_attr(edition2024, feature(shorter_tail_lifetimes))] + +use std::sync::Mutex; + +struct PanicOnDrop; +impl Drop for PanicOnDrop { + fn drop(&mut self) { + panic!() + } +} + +fn f(m: &Mutex) -> i32 { + let _x = PanicOnDrop; + *m.lock().unwrap() +} + +fn main() { + let m = Mutex::new(0); + let _ = std::panic::catch_unwind(|| f(&m)); + #[cfg(edition2024)] + assert!(m.lock().is_ok()); + #[cfg(edition2021)] + assert!(m.lock().is_err()); +} diff --git a/tests/ui/lifetimes/unusual-rib-combinations.rs b/tests/ui/lifetimes/unusual-rib-combinations.rs index 3bc87b9d480b..0708a00d3715 100644 --- a/tests/ui/lifetimes/unusual-rib-combinations.rs +++ b/tests/ui/lifetimes/unusual-rib-combinations.rs @@ -2,9 +2,8 @@ struct S<'a>(&'a u8); fn foo() {} // Paren generic args in AnonConst -fn a() -> [u8; foo::()] { -//~^ ERROR parenthesized type parameters may only be used with a `Fn` trait -//~| ERROR mismatched types +fn a() -> [u8; foo()] { + //~^ ERROR mismatched types panic!() } @@ -26,5 +25,6 @@ fn d() {} trait Foo<'a> {} struct Bar Foo<'a>)>; //~^ ERROR the type of const parameters must not depend on other generic parameters +//~| ERROR `&dyn for<'a> Foo<'a>` is forbidden as the type of a const generic parameter fn main() {} diff --git a/tests/ui/lifetimes/unusual-rib-combinations.stderr b/tests/ui/lifetimes/unusual-rib-combinations.stderr index 2857fc72ea14..70f06b4be603 100644 --- a/tests/ui/lifetimes/unusual-rib-combinations.stderr +++ b/tests/ui/lifetimes/unusual-rib-combinations.stderr @@ -1,11 +1,11 @@ error[E0106]: missing lifetime specifier - --> $DIR/unusual-rib-combinations.rs:22:15 + --> $DIR/unusual-rib-combinations.rs:21:15 | LL | fn d() {} | ^ expected named lifetime parameter error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/unusual-rib-combinations.rs:27:22 + --> $DIR/unusual-rib-combinations.rs:26:22 | LL | struct Bar Foo<'a>)>; | ^^ the type must not depend on the parameter `'a` @@ -13,25 +13,19 @@ LL | struct Bar Foo<'a>)>; = note: lifetime parameters may not be used in the type of const parameters error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/unusual-rib-combinations.rs:5:16 - | -LL | fn a() -> [u8; foo::()] { - | ^^^^^^^ only `Fn` traits may use parentheses - -error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/unusual-rib-combinations.rs:12:15 + --> $DIR/unusual-rib-combinations.rs:11:15 | LL | fn b() {} | ^^^^ only `Fn` traits may use parentheses error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/unusual-rib-combinations.rs:16:10 + --> $DIR/unusual-rib-combinations.rs:15:10 | LL | fn c() {} | ^^^^ only `Fn` traits may use parentheses error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/unusual-rib-combinations.rs:16:6 + --> $DIR/unusual-rib-combinations.rs:15:6 | LL | fn c() {} | ^^^^^^^^ @@ -43,14 +37,11 @@ LL | fn c() {} error[E0308]: mismatched types --> $DIR/unusual-rib-combinations.rs:5:16 | -LL | fn a() -> [u8; foo::()] { - | ^^^^^^^ expected `usize`, found fn item - | - = note: expected type `usize` - found fn item `fn() {foo}` +LL | fn a() -> [u8; foo()] { + | ^^^^^ expected `usize`, found `()` error: `S<'_>` is forbidden as the type of a const generic parameter - --> $DIR/unusual-rib-combinations.rs:22:15 + --> $DIR/unusual-rib-combinations.rs:21:15 | LL | fn d() {} | ^ @@ -61,6 +52,18 @@ help: add `#![feature(adt_const_params)]` to the crate attributes to enable more LL + #![feature(adt_const_params)] | +error: `&dyn for<'a> Foo<'a>` is forbidden as the type of a const generic parameter + --> $DIR/unusual-rib-combinations.rs:26:21 + | +LL | struct Bar Foo<'a>)>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` +help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types + | +LL + #![feature(adt_const_params)] + | + error: aborting due to 8 previous errors Some errors have detailed explanations: E0106, E0214, E0308, E0770. diff --git a/tests/ui/link-section.rs b/tests/ui/link-section.rs index 9299b4d08b23..1a791b88ef95 100644 --- a/tests/ui/link-section.rs +++ b/tests/ui/link-section.rs @@ -1,32 +1,32 @@ //@ run-pass #![allow(non_upper_case_globals)] -#[cfg(not(target_os = "macos"))] -#[link_section=".moretext"] +#[cfg(not(target_vendor = "apple"))] +#[link_section = ".moretext"] fn i_live_in_more_text() -> &'static str { "knock knock" } -#[cfg(not(target_os = "macos"))] -#[link_section=".imm"] +#[cfg(not(target_vendor = "apple"))] +#[link_section = ".imm"] static magic: usize = 42; -#[cfg(not(target_os = "macos"))] -#[link_section=".mut"] +#[cfg(not(target_vendor = "apple"))] +#[link_section = ".mut"] static mut frobulator: usize = 0xdeadbeef; -#[cfg(target_os = "macos")] -#[link_section="__TEXT,__moretext"] +#[cfg(target_vendor = "apple")] +#[link_section = "__TEXT,__moretext"] fn i_live_in_more_text() -> &'static str { "knock knock" } -#[cfg(target_os = "macos")] -#[link_section="__RODATA,__imm"] +#[cfg(target_vendor = "apple")] +#[link_section = "__RODATA,__imm"] static magic: usize = 42; -#[cfg(target_os = "macos")] -#[link_section="__DATA,__mut"] +#[cfg(target_vendor = "apple")] +#[link_section = "__DATA,__mut"] static mut frobulator: usize = 0xdeadbeef; pub fn main() { diff --git a/tests/ui/linkage-attr/framework.rs b/tests/ui/linkage-attr/framework.rs index 824adf62206d..08f4394db21f 100644 --- a/tests/ui/linkage-attr/framework.rs +++ b/tests/ui/linkage-attr/framework.rs @@ -1,5 +1,5 @@ // Check that linking frameworks on Apple platforms works. -//@ only-macos +//@ only-apple //@ revisions: omit link weak both //@ [omit]build-fail //@ [link]run-pass diff --git a/tests/ui/osx-frameworks.rs b/tests/ui/linkage-attr/kind-framework.rs similarity index 58% rename from tests/ui/osx-frameworks.rs rename to tests/ui/linkage-attr/kind-framework.rs index b0d7a3a9c07a..c2f90809e03a 100644 --- a/tests/ui/osx-frameworks.rs +++ b/tests/ui/linkage-attr/kind-framework.rs @@ -1,4 +1,4 @@ -//@ ignore-macos this is supposed to succeed on osx +//@ ignore-apple this is supposed to succeed on Apple platforms (though it won't necessarily link) #[link(name = "foo", kind = "framework")] extern "C" {} diff --git a/tests/ui/osx-frameworks.stderr b/tests/ui/linkage-attr/kind-framework.stderr similarity index 89% rename from tests/ui/osx-frameworks.stderr rename to tests/ui/linkage-attr/kind-framework.stderr index 8582b8123bfe..93dacd68e294 100644 --- a/tests/ui/osx-frameworks.stderr +++ b/tests/ui/linkage-attr/kind-framework.stderr @@ -1,5 +1,5 @@ error[E0455]: link kind `framework` is only supported on Apple targets - --> $DIR/osx-frameworks.rs:3:29 + --> $DIR/kind-framework.rs:3:29 | LL | #[link(name = "foo", kind = "framework")] | ^^^^^^^^^^^ diff --git a/tests/ui/issues/issue-33992.rs b/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs similarity index 87% rename from tests/ui/issues/issue-33992.rs rename to tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs index 177ff234bb29..a169997927eb 100644 --- a/tests/ui/issues/issue-33992.rs +++ b/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs @@ -1,13 +1,10 @@ //@ run-pass //@ ignore-windows -//@ ignore-macos +//@ ignore-apple //@ ignore-wasm32 common linkage not implemented right now #![feature(linkage)] -#[linkage = "common"] -pub static mut TEST1: u32 = 0u32; - #[linkage = "external"] pub static TEST2: bool = true; diff --git a/tests/ui/linkage-attr/linkage-attr-mutable-static.rs b/tests/ui/linkage-attr/linkage-attr-mutable-static.rs new file mode 100644 index 000000000000..ed11947f59e7 --- /dev/null +++ b/tests/ui/linkage-attr/linkage-attr-mutable-static.rs @@ -0,0 +1,24 @@ +//! The symbols are resolved by the linker. It doesn't make sense to change +//! them at runtime, so deny mutable statics with #[linkage]. + +#![feature(linkage)] + +fn main() { + #[rustfmt::skip] + extern "C" { + #[linkage = "extern_weak"] //~ ERROR extern mutable statics are not allowed with `#[linkage]` + static mut EXTERN_WEAK: *const u8; + } + + unsafe { + assert_eq!(EXTERN_WEAK as usize, 0); + } + + // static mut is fine here as this is a definition rather than declaration. + #[linkage = "weak"] + static mut WEAK_DEF: u8 = 42; + + unsafe { + assert_eq!(WEAK_DEF, 0); + } +} diff --git a/tests/ui/linkage-attr/linkage-attr-mutable-static.stderr b/tests/ui/linkage-attr/linkage-attr-mutable-static.stderr new file mode 100644 index 000000000000..ad9997690475 --- /dev/null +++ b/tests/ui/linkage-attr/linkage-attr-mutable-static.stderr @@ -0,0 +1,10 @@ +error: extern mutable statics are not allowed with `#[linkage]` + --> $DIR/linkage-attr-mutable-static.rs:9:9 + | +LL | #[linkage = "extern_weak"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: marking the extern static mutable would allow changing which symbol the static references rather than make the target of the symbol mutable + +error: aborting due to 1 previous error + diff --git a/tests/ui/linkage-attr/linkage1.rs b/tests/ui/linkage-attr/linkage1.rs index 2edb80bf1b02..19bf601d9d20 100644 --- a/tests/ui/linkage-attr/linkage1.rs +++ b/tests/ui/linkage-attr/linkage1.rs @@ -1,6 +1,6 @@ //@ run-pass //@ ignore-windows -//@ ignore-macos +//@ ignore-apple //@ ignore-emscripten doesn't support this linkage //@ ignore-sgx weak linkage not permitted //@ aux-build:linkage1.rs diff --git a/tests/ui/issues/issue-18804/auxiliary/lib.rs b/tests/ui/linkage-attr/propagate-generic-issue-18804/auxiliary/lib.rs similarity index 100% rename from tests/ui/issues/issue-18804/auxiliary/lib.rs rename to tests/ui/linkage-attr/propagate-generic-issue-18804/auxiliary/lib.rs diff --git a/tests/ui/issues/issue-18804/main.rs b/tests/ui/linkage-attr/propagate-generic-issue-18804/main.rs similarity index 91% rename from tests/ui/issues/issue-18804/main.rs rename to tests/ui/linkage-attr/propagate-generic-issue-18804/main.rs index d83fe697470b..56a9358cce3c 100644 --- a/tests/ui/issues/issue-18804/main.rs +++ b/tests/ui/linkage-attr/propagate-generic-issue-18804/main.rs @@ -4,7 +4,7 @@ //@ ignore-emscripten no weak symbol support //@ ignore-windows no extern_weak linkage -//@ ignore-macos no extern_weak linkage +//@ ignore-apple no extern_weak linkage //@ aux-build:lib.rs diff --git a/tests/ui/lint/anonymous-reexport.stderr b/tests/ui/lint/anonymous-reexport.stderr index e3854a5459ec..c4ee18fe8da2 100644 --- a/tests/ui/lint/anonymous-reexport.stderr +++ b/tests/ui/lint/anonymous-reexport.stderr @@ -34,7 +34,7 @@ error: unused import: `Bar as _` LL | pub use self::my_mod::{Bar as _, Foo as _}; | ^^^^^^^^ -error: unused imports: `Bar as _`, `TyBar as _` +error: unused imports: `Bar as _` and `TyBar as _` --> $DIR/anonymous-reexport.rs:17:24 | LL | pub use self::my_mod::{Bar as _, TyBar as _}; diff --git a/tests/ui/lint/dead-code/allow-unconstructed-pub-struct.rs b/tests/ui/lint/dead-code/allow-unconstructed-pub-struct.rs new file mode 100644 index 000000000000..8cd1524045b1 --- /dev/null +++ b/tests/ui/lint/dead-code/allow-unconstructed-pub-struct.rs @@ -0,0 +1,33 @@ +//@ check-pass + +mod ffi { + use super::*; + + extern "C" { + pub fn DomPromise_AddRef(promise: *const Promise); + pub fn DomPromise_Release(promise: *const Promise); + } +} + +#[repr(C)] +#[allow(unused)] +pub struct Promise { + private: [u8; 0], + __nosync: ::std::marker::PhantomData<::std::rc::Rc>, +} + +pub unsafe trait RefCounted { + unsafe fn addref(&self); + unsafe fn release(&self); +} + +unsafe impl RefCounted for Promise { + unsafe fn addref(&self) { + ffi::DomPromise_AddRef(self) + } + unsafe fn release(&self) { + ffi::DomPromise_Release(self) + } +} + +fn main() {} diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.rs b/tests/ui/lint/dead-code/lint-dead-code-1.rs index ddcafedf7bc5..3386dfa47470 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.rs +++ b/tests/ui/lint/dead-code/lint-dead-code-1.rs @@ -46,11 +46,10 @@ struct SemiUsedStruct; impl SemiUsedStruct { fn la_la_la() {} } -struct StructUsedAsField; +struct StructUsedAsField; //~ ERROR struct `StructUsedAsField` is never constructed pub struct StructUsedInEnum; struct StructUsedInGeneric; -pub struct PubStruct2 { - #[allow(dead_code)] +pub struct PubStruct2 { //~ ERROR struct `PubStruct2` is never constructed struct_used_as_field: *const StructUsedAsField } diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.stderr b/tests/ui/lint/dead-code/lint-dead-code-1.stderr index eb728b5b9305..b0163df8855c 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.stderr +++ b/tests/ui/lint/dead-code/lint-dead-code-1.stderr @@ -22,14 +22,26 @@ error: struct `PrivStruct` is never constructed LL | struct PrivStruct; | ^^^^^^^^^^ +error: struct `StructUsedAsField` is never constructed + --> $DIR/lint-dead-code-1.rs:49:8 + | +LL | struct StructUsedAsField; + | ^^^^^^^^^^^^^^^^^ + +error: struct `PubStruct2` is never constructed + --> $DIR/lint-dead-code-1.rs:52:12 + | +LL | pub struct PubStruct2 { + | ^^^^^^^^^^ + error: enum `priv_enum` is never used - --> $DIR/lint-dead-code-1.rs:64:6 + --> $DIR/lint-dead-code-1.rs:63:6 | LL | enum priv_enum { foo2, bar2 } | ^^^^^^^^^ error: variant `bar3` is never constructed - --> $DIR/lint-dead-code-1.rs:67:5 + --> $DIR/lint-dead-code-1.rs:66:5 | LL | enum used_enum { | --------- variant in this enum @@ -38,25 +50,25 @@ LL | bar3 | ^^^^ error: function `priv_fn` is never used - --> $DIR/lint-dead-code-1.rs:88:4 + --> $DIR/lint-dead-code-1.rs:87:4 | LL | fn priv_fn() { | ^^^^^^^ error: function `foo` is never used - --> $DIR/lint-dead-code-1.rs:93:4 + --> $DIR/lint-dead-code-1.rs:92:4 | LL | fn foo() { | ^^^ error: function `bar` is never used - --> $DIR/lint-dead-code-1.rs:98:4 + --> $DIR/lint-dead-code-1.rs:97:4 | LL | fn bar() { | ^^^ error: function `baz` is never used - --> $DIR/lint-dead-code-1.rs:102:4 + --> $DIR/lint-dead-code-1.rs:101:4 | LL | fn baz() -> impl Copy { | ^^^ @@ -67,5 +79,5 @@ error: struct `Bar` is never constructed LL | pub struct Bar; | ^^^ -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/lint/dead-code/tuple-struct-field.rs b/tests/ui/lint/dead-code/tuple-struct-field.rs index d13fe029289a..ff3da4105000 100644 --- a/tests/ui/lint/dead-code/tuple-struct-field.rs +++ b/tests/ui/lint/dead-code/tuple-struct-field.rs @@ -5,15 +5,20 @@ use std::marker::PhantomData; const LEN: usize = 4; -struct SingleUnused(i32, [u8; LEN], String); -//~^ ERROR: field `1` is never read -//~| NOTE: field in this struct -//~| HELP: consider changing the field to be of unit type - -struct MultipleUnused(i32, f32, String, u8); -//~^ ERROR: fields `0`, `1`, `2`, and `3` are never read +struct UnusedAtTheEnd(i32, f32, [u8; LEN], String, u8); +//~^ ERROR:fields `1`, `2`, `3`, and `4` are never read //~| NOTE: fields in this struct -//~| HELP: consider changing the fields to be of unit type +//~| HELP: consider removing these fields + +struct UnusedJustOneField(i32); +//~^ ERROR: field `0` is never read +//~| NOTE: field in this struct +//~| HELP: consider removing this field + +struct UnusedInTheMiddle(i32, f32, String, u8, u32); +//~^ ERROR: fields `1`, `2`, and `4` are never read +//~| NOTE: fields in this struct +//~| HELP: consider changing the fields to be of unit type to suppress this warning while preserving the field numbering, or remove the fields struct GoodUnit(()); @@ -23,15 +28,19 @@ struct Void; struct GoodVoid(Void); fn main() { - let w = SingleUnused(42, [0, 1, 2, 3], "abc".to_string()); - let _ = w.0; - let _ = w.2; + let u1 = UnusedAtTheEnd(42, 3.14, [0, 1, 2, 3], "def".to_string(), 4u8); + let _ = u1.0; + + let _ = UnusedJustOneField(42); + + let u2 = UnusedInTheMiddle(42, 3.14, "def".to_string(), 4u8, 5); + let _ = u2.0; + let _ = u2.3; - let m = MultipleUnused(42, 3.14, "def".to_string(), 4u8); let gu = GoodUnit(()); let gp = GoodPhantom(PhantomData); let gv = GoodVoid(Void); - let _ = (gu, gp, gv, m); + let _ = (gu, gp, gv); } diff --git a/tests/ui/lint/dead-code/tuple-struct-field.stderr b/tests/ui/lint/dead-code/tuple-struct-field.stderr index 0154d5489f9f..434554d7ae5e 100644 --- a/tests/ui/lint/dead-code/tuple-struct-field.stderr +++ b/tests/ui/lint/dead-code/tuple-struct-field.stderr @@ -1,33 +1,40 @@ -error: field `1` is never read - --> $DIR/tuple-struct-field.rs:8:26 +error: fields `1`, `2`, `3`, and `4` are never read + --> $DIR/tuple-struct-field.rs:8:28 | -LL | struct SingleUnused(i32, [u8; LEN], String); - | ------------ ^^^^^^^^^ +LL | struct UnusedAtTheEnd(i32, f32, [u8; LEN], String, u8); + | -------------- ^^^ ^^^^^^^^^ ^^^^^^ ^^ | | - | field in this struct + | fields in this struct | + = help: consider removing these fields note: the lint level is defined here --> $DIR/tuple-struct-field.rs:1:9 | LL | #![deny(dead_code)] | ^^^^^^^^^ -help: consider changing the field to be of unit type to suppress this warning while preserving the field numbering, or remove the field - | -LL | struct SingleUnused(i32, (), String); - | ~~ -error: fields `0`, `1`, `2`, and `3` are never read - --> $DIR/tuple-struct-field.rs:13:23 +error: field `0` is never read + --> $DIR/tuple-struct-field.rs:13:27 | -LL | struct MultipleUnused(i32, f32, String, u8); - | -------------- ^^^ ^^^ ^^^^^^ ^^ +LL | struct UnusedJustOneField(i32); + | ------------------ ^^^ + | | + | field in this struct + | + = help: consider removing this field + +error: fields `1`, `2`, and `4` are never read + --> $DIR/tuple-struct-field.rs:18:31 + | +LL | struct UnusedInTheMiddle(i32, f32, String, u8, u32); + | ----------------- ^^^ ^^^^^^ ^^^ | | | fields in this struct | help: consider changing the fields to be of unit type to suppress this warning while preserving the field numbering, or remove the fields | -LL | struct MultipleUnused((), (), (), ()); - | ~~ ~~ ~~ ~~ +LL | struct UnusedInTheMiddle(i32, (), (), u8, ()); + | ~~ ~~ ~~ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs new file mode 100644 index 000000000000..bf2fc243e819 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs @@ -0,0 +1,52 @@ +#![deny(dead_code)] + +struct T1; //~ ERROR struct `T1` is never constructed +pub struct T2(i32); //~ ERROR struct `T2` is never constructed +struct T3; + +trait Trait1 { //~ ERROR trait `Trait1` is never used + const UNUSED: i32; + fn unused(&self) {} + fn construct_self() -> Self; +} + +pub trait Trait2 { + const USED: i32; + fn used(&self) {} +} + +pub trait Trait3 { + const USED: i32; + fn construct_self() -> Self; +} + +impl Trait1 for T1 { + const UNUSED: i32 = 0; + fn construct_self() -> Self { + Self + } +} + +impl Trait1 for T2 { + const UNUSED: i32 = 0; + fn construct_self() -> Self { + T2(0) + } +} + +impl Trait2 for T1 { + const USED: i32 = 0; +} + +impl Trait2 for T2 { + const USED: i32 = 0; +} + +impl Trait3 for T3 { + const USED: i32 = 0; + fn construct_self() -> Self { + Self + } +} + +fn main() {} diff --git a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr new file mode 100644 index 000000000000..174096d93988 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr @@ -0,0 +1,26 @@ +error: struct `T1` is never constructed + --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:3:8 + | +LL | struct T1; + | ^^ + | +note: the lint level is defined here + --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: struct `T2` is never constructed + --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:4:12 + | +LL | pub struct T2(i32); + | ^^ + +error: trait `Trait1` is never used + --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:7:7 + | +LL | trait Trait1 { + | ^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/lint/dead-code/unused-adt-impls-trait.rs b/tests/ui/lint/dead-code/unused-adt-impls-trait.rs new file mode 100644 index 000000000000..4714859afac4 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-adt-impls-trait.rs @@ -0,0 +1,34 @@ +#![deny(dead_code)] + +struct Used; +struct Unused; //~ ERROR struct `Unused` is never constructed + +pub trait PubTrait { + fn foo(&self) -> Self; +} + +impl PubTrait for Used { + fn foo(&self) -> Self { Used } +} + +impl PubTrait for Unused { + fn foo(&self) -> Self { Unused } +} + +trait PriTrait { + fn foo(&self) -> Self; +} + +impl PriTrait for Used { + fn foo(&self) -> Self { Used } +} + +impl PriTrait for Unused { + fn foo(&self) -> Self { Unused } +} + +fn main() { + let t = Used; + let _t = ::foo(&t); + let _t = ::foo(&t); +} diff --git a/tests/ui/lint/dead-code/unused-adt-impls-trait.stderr b/tests/ui/lint/dead-code/unused-adt-impls-trait.stderr new file mode 100644 index 000000000000..28bae5c2af09 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-adt-impls-trait.stderr @@ -0,0 +1,14 @@ +error: struct `Unused` is never constructed + --> $DIR/unused-adt-impls-trait.rs:4:8 + | +LL | struct Unused; + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-adt-impls-trait.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/dead-code/unused-assoc-const.rs b/tests/ui/lint/dead-code/unused-assoc-const.rs new file mode 100644 index 000000000000..36e8315ad360 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-assoc-const.rs @@ -0,0 +1,20 @@ +#![deny(dead_code)] + +trait Trait { + const UNUSED_CONST: i32; //~ ERROR associated constant `UNUSED_CONST` is never used + const USED_CONST: i32; + + fn foo(&self) {} +} + +pub struct T(()); + +impl Trait for T { + const UNUSED_CONST: i32 = 0; + const USED_CONST: i32 = 1; +} + +fn main() { + T(()).foo(); + T::USED_CONST; +} diff --git a/tests/ui/lint/dead-code/unused-assoc-const.stderr b/tests/ui/lint/dead-code/unused-assoc-const.stderr new file mode 100644 index 000000000000..78296d706638 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-assoc-const.stderr @@ -0,0 +1,16 @@ +error: associated constant `UNUSED_CONST` is never used + --> $DIR/unused-assoc-const.rs:4:11 + | +LL | trait Trait { + | ----- associated constant in this trait +LL | const UNUSED_CONST: i32; + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-assoc-const.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/dead-code/unused-pub-struct.rs b/tests/ui/lint/dead-code/unused-pub-struct.rs new file mode 100644 index 000000000000..aaf4dd612de4 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-pub-struct.rs @@ -0,0 +1,48 @@ +#![deny(dead_code)] + +pub struct NotLint1(()); +pub struct NotLint2(std::marker::PhantomData); + +pub struct NeverConstructed(i32); //~ ERROR struct `NeverConstructed` is never constructed + +impl NeverConstructed { + pub fn not_construct_self(&self) {} +} + +impl Clone for NeverConstructed { + fn clone(&self) -> NeverConstructed { + NeverConstructed(0) + } +} + +pub trait Trait { + fn not_construct_self(&self); +} + +impl Trait for NeverConstructed { + fn not_construct_self(&self) { + self.0; + } +} + +pub struct Constructed(i32); + +impl Constructed { + pub fn construct_self() -> Self { + Constructed(0) + } +} + +impl Clone for Constructed { + fn clone(&self) -> Constructed { + Constructed(0) + } +} + +impl Trait for Constructed { + fn not_construct_self(&self) { + self.0; + } +} + +fn main() {} diff --git a/tests/ui/lint/dead-code/unused-pub-struct.stderr b/tests/ui/lint/dead-code/unused-pub-struct.stderr new file mode 100644 index 000000000000..3667ddb97bd3 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-pub-struct.stderr @@ -0,0 +1,14 @@ +error: struct `NeverConstructed` is never constructed + --> $DIR/unused-pub-struct.rs:6:12 + | +LL | pub struct NeverConstructed(i32); + | ^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-pub-struct.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/dropping_copy_types-issue-125189-can-not-fixed.rs b/tests/ui/lint/dropping_copy_types-issue-125189-can-not-fixed.rs new file mode 100644 index 000000000000..12c984c932ac --- /dev/null +++ b/tests/ui/lint/dropping_copy_types-issue-125189-can-not-fixed.rs @@ -0,0 +1,14 @@ +//@ check-fail + +#![deny(dropping_copy_types)] + +fn main() { + let y = 1; + let z = 2; + match y { + 0 => drop(y), //~ ERROR calls to `std::mem::drop` + 1 => drop(z), //~ ERROR calls to `std::mem::drop` + 2 => drop(3), //~ ERROR calls to `std::mem::drop` + _ => {}, + } +} diff --git a/tests/ui/lint/dropping_copy_types-issue-125189-can-not-fixed.stderr b/tests/ui/lint/dropping_copy_types-issue-125189-can-not-fixed.stderr new file mode 100644 index 000000000000..6c73e5ea90b4 --- /dev/null +++ b/tests/ui/lint/dropping_copy_types-issue-125189-can-not-fixed.stderr @@ -0,0 +1,37 @@ +error: calls to `std::mem::drop` with a value that implements `Copy` does nothing + --> $DIR/dropping_copy_types-issue-125189-can-not-fixed.rs:9:14 + | +LL | 0 => drop(y), + | ^^^^^-^ + | | + | argument has type `i32` + | + = note: use `let _ = ...` to ignore the expression or result +note: the lint level is defined here + --> $DIR/dropping_copy_types-issue-125189-can-not-fixed.rs:3:9 + | +LL | #![deny(dropping_copy_types)] + | ^^^^^^^^^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a value that implements `Copy` does nothing + --> $DIR/dropping_copy_types-issue-125189-can-not-fixed.rs:10:14 + | +LL | 1 => drop(z), + | ^^^^^-^ + | | + | argument has type `i32` + | + = note: use `let _ = ...` to ignore the expression or result + +error: calls to `std::mem::drop` with a value that implements `Copy` does nothing + --> $DIR/dropping_copy_types-issue-125189-can-not-fixed.rs:11:14 + | +LL | 2 => drop(3), + | ^^^^^-^ + | | + | argument has type `i32` + | + = note: use `let _ = ...` to ignore the expression or result + +error: aborting due to 3 previous errors + diff --git a/tests/ui/lint/dropping_copy_types-issue-125189.fixed b/tests/ui/lint/dropping_copy_types-issue-125189.fixed new file mode 100644 index 000000000000..2dfd68d4cc16 --- /dev/null +++ b/tests/ui/lint/dropping_copy_types-issue-125189.fixed @@ -0,0 +1,10 @@ +//@ check-fail +//@ run-rustfix + +#![deny(dropping_copy_types)] + +fn main() { + let y = 1; + let _ = 3.2; //~ ERROR calls to `std::mem::drop` + let _ = y; //~ ERROR calls to `std::mem::drop` +} diff --git a/tests/ui/lint/dropping_copy_types-issue-125189.rs b/tests/ui/lint/dropping_copy_types-issue-125189.rs new file mode 100644 index 000000000000..1f42efabba9f --- /dev/null +++ b/tests/ui/lint/dropping_copy_types-issue-125189.rs @@ -0,0 +1,10 @@ +//@ check-fail +//@ run-rustfix + +#![deny(dropping_copy_types)] + +fn main() { + let y = 1; + drop(3.2); //~ ERROR calls to `std::mem::drop` + drop(y); //~ ERROR calls to `std::mem::drop` +} diff --git a/tests/ui/lint/dropping_copy_types-issue-125189.stderr b/tests/ui/lint/dropping_copy_types-issue-125189.stderr new file mode 100644 index 000000000000..ba3e36f5b6ec --- /dev/null +++ b/tests/ui/lint/dropping_copy_types-issue-125189.stderr @@ -0,0 +1,35 @@ +error: calls to `std::mem::drop` with a value that implements `Copy` does nothing + --> $DIR/dropping_copy_types-issue-125189.rs:8:5 + | +LL | drop(3.2); + | ^^^^^---^ + | | + | argument has type `f64` + | +note: the lint level is defined here + --> $DIR/dropping_copy_types-issue-125189.rs:4:9 + | +LL | #![deny(dropping_copy_types)] + | ^^^^^^^^^^^^^^^^^^^ +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(3.2); +LL + let _ = 3.2; + | + +error: calls to `std::mem::drop` with a value that implements `Copy` does nothing + --> $DIR/dropping_copy_types-issue-125189.rs:9:5 + | +LL | drop(y); + | ^^^^^-^ + | | + | argument has type `i32` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(y); +LL + let _ = y; + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/lint/dropping_copy_types.stderr b/tests/ui/lint/dropping_copy_types.stderr index b6291aa5ed63..41aa66a4efc6 100644 --- a/tests/ui/lint/dropping_copy_types.stderr +++ b/tests/ui/lint/dropping_copy_types.stderr @@ -6,12 +6,16 @@ LL | drop(s1); | | | argument has type `SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result note: the lint level is defined here --> $DIR/dropping_copy_types.rs:3:9 | LL | #![warn(dropping_copy_types)] | ^^^^^^^^^^^^^^^^^^^ +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(s1); +LL + let _ = s1; + | warning: calls to `std::mem::drop` with a value that implements `Copy` does nothing --> $DIR/dropping_copy_types.rs:35:5 @@ -21,7 +25,11 @@ LL | drop(s2); | | | argument has type `SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(s2); +LL + let _ = s2; + | warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing --> $DIR/dropping_copy_types.rs:36:5 @@ -31,8 +39,12 @@ LL | drop(s3); | | | argument has type `&SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result = note: `#[warn(dropping_references)]` on by default +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(s3); +LL + let _ = s3; + | warning: calls to `std::mem::drop` with a value that implements `Copy` does nothing --> $DIR/dropping_copy_types.rs:37:5 @@ -42,7 +54,11 @@ LL | drop(s4); | | | argument has type `SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(s4); +LL + let _ = s4; + | warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing --> $DIR/dropping_copy_types.rs:38:5 @@ -52,7 +68,11 @@ LL | drop(s5); | | | argument has type `&SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(s5); +LL + let _ = s5; + | warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing --> $DIR/dropping_copy_types.rs:50:5 @@ -62,7 +82,11 @@ LL | drop(a2); | | | argument has type `&AnotherStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(a2); +LL + let _ = a2; + | warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing --> $DIR/dropping_copy_types.rs:52:5 @@ -72,7 +96,11 @@ LL | drop(a4); | | | argument has type `&AnotherStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(a4); +LL + let _ = a4; + | warning: calls to `std::mem::drop` with a value that implements `Copy` does nothing --> $DIR/dropping_copy_types.rs:71:13 @@ -82,7 +110,11 @@ LL | drop(println_and(13)); | | | argument has type `i32` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(println_and(13)); +LL + let _ = println_and(13); + | warning: calls to `std::mem::drop` with a value that implements `Copy` does nothing --> $DIR/dropping_copy_types.rs:74:14 diff --git a/tests/ui/lint/dropping_references-can-fixed.fixed b/tests/ui/lint/dropping_references-can-fixed.fixed new file mode 100644 index 000000000000..f704d3c72df7 --- /dev/null +++ b/tests/ui/lint/dropping_references-can-fixed.fixed @@ -0,0 +1,31 @@ +//@ check-fail +//@ run-rustfix + +#![deny(dropping_references)] + +struct SomeStruct; + +fn main() { + let _ = &SomeStruct; //~ ERROR calls to `std::mem::drop` + + let mut owned1 = SomeStruct; + let _ = &owned1; //~ ERROR calls to `std::mem::drop` + let _ = &&owned1; //~ ERROR calls to `std::mem::drop` + let _ = &mut owned1; //~ ERROR calls to `std::mem::drop` + drop(owned1); + + let reference1 = &SomeStruct; + let _ = reference1; //~ ERROR calls to `std::mem::drop` + + let reference2 = &mut SomeStruct; + let _ = reference2; //~ ERROR calls to `std::mem::drop` + + let ref reference3 = SomeStruct; + let _ = reference3; //~ ERROR calls to `std::mem::drop` +} + +#[allow(dead_code)] +fn test_generic_fn_drop(val: T) { + let _ = &val; //~ ERROR calls to `std::mem::drop` + drop(val); +} diff --git a/tests/ui/lint/dropping_references-can-fixed.rs b/tests/ui/lint/dropping_references-can-fixed.rs new file mode 100644 index 000000000000..70d1c16d66b4 --- /dev/null +++ b/tests/ui/lint/dropping_references-can-fixed.rs @@ -0,0 +1,31 @@ +//@ check-fail +//@ run-rustfix + +#![deny(dropping_references)] + +struct SomeStruct; + +fn main() { + drop(&SomeStruct); //~ ERROR calls to `std::mem::drop` + + let mut owned1 = SomeStruct; + drop(&owned1); //~ ERROR calls to `std::mem::drop` + drop(&&owned1); //~ ERROR calls to `std::mem::drop` + drop(&mut owned1); //~ ERROR calls to `std::mem::drop` + drop(owned1); + + let reference1 = &SomeStruct; + drop(reference1); //~ ERROR calls to `std::mem::drop` + + let reference2 = &mut SomeStruct; + drop(reference2); //~ ERROR calls to `std::mem::drop` + + let ref reference3 = SomeStruct; + drop(reference3); //~ ERROR calls to `std::mem::drop` +} + +#[allow(dead_code)] +fn test_generic_fn_drop(val: T) { + drop(&val); //~ ERROR calls to `std::mem::drop` + drop(val); +} diff --git a/tests/ui/lint/dropping_references-can-fixed.stderr b/tests/ui/lint/dropping_references-can-fixed.stderr new file mode 100644 index 000000000000..42cdb81b524a --- /dev/null +++ b/tests/ui/lint/dropping_references-can-fixed.stderr @@ -0,0 +1,119 @@ +error: calls to `std::mem::drop` with a reference instead of an owned value does nothing + --> $DIR/dropping_references-can-fixed.rs:9:5 + | +LL | drop(&SomeStruct); + | ^^^^^-----------^ + | | + | argument has type `&SomeStruct` + | +note: the lint level is defined here + --> $DIR/dropping_references-can-fixed.rs:4:9 + | +LL | #![deny(dropping_references)] + | ^^^^^^^^^^^^^^^^^^^ +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(&SomeStruct); +LL + let _ = &SomeStruct; + | + +error: calls to `std::mem::drop` with a reference instead of an owned value does nothing + --> $DIR/dropping_references-can-fixed.rs:12:5 + | +LL | drop(&owned1); + | ^^^^^-------^ + | | + | argument has type `&SomeStruct` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(&owned1); +LL + let _ = &owned1; + | + +error: calls to `std::mem::drop` with a reference instead of an owned value does nothing + --> $DIR/dropping_references-can-fixed.rs:13:5 + | +LL | drop(&&owned1); + | ^^^^^--------^ + | | + | argument has type `&&SomeStruct` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(&&owned1); +LL + let _ = &&owned1; + | + +error: calls to `std::mem::drop` with a reference instead of an owned value does nothing + --> $DIR/dropping_references-can-fixed.rs:14:5 + | +LL | drop(&mut owned1); + | ^^^^^-----------^ + | | + | argument has type `&mut SomeStruct` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(&mut owned1); +LL + let _ = &mut owned1; + | + +error: calls to `std::mem::drop` with a reference instead of an owned value does nothing + --> $DIR/dropping_references-can-fixed.rs:18:5 + | +LL | drop(reference1); + | ^^^^^----------^ + | | + | argument has type `&SomeStruct` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(reference1); +LL + let _ = reference1; + | + +error: calls to `std::mem::drop` with a reference instead of an owned value does nothing + --> $DIR/dropping_references-can-fixed.rs:21:5 + | +LL | drop(reference2); + | ^^^^^----------^ + | | + | argument has type `&mut SomeStruct` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(reference2); +LL + let _ = reference2; + | + +error: calls to `std::mem::drop` with a reference instead of an owned value does nothing + --> $DIR/dropping_references-can-fixed.rs:24:5 + | +LL | drop(reference3); + | ^^^^^----------^ + | | + | argument has type `&SomeStruct` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(reference3); +LL + let _ = reference3; + | + +error: calls to `std::mem::drop` with a reference instead of an owned value does nothing + --> $DIR/dropping_references-can-fixed.rs:29:5 + | +LL | drop(&val); + | ^^^^^----^ + | | + | argument has type `&T` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(&val); +LL + let _ = &val; + | + +error: aborting due to 8 previous errors + diff --git a/tests/ui/lint/dropping_references.stderr b/tests/ui/lint/dropping_references.stderr index 7e25a46216ec..312334b82ad4 100644 --- a/tests/ui/lint/dropping_references.stderr +++ b/tests/ui/lint/dropping_references.stderr @@ -6,12 +6,16 @@ LL | drop(&SomeStruct); | | | argument has type `&SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result note: the lint level is defined here --> $DIR/dropping_references.rs:3:9 | LL | #![warn(dropping_references)] | ^^^^^^^^^^^^^^^^^^^ +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(&SomeStruct); +LL + let _ = &SomeStruct; + | warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing --> $DIR/dropping_references.rs:11:5 @@ -21,7 +25,11 @@ LL | drop(&owned1); | | | argument has type `&SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(&owned1); +LL + let _ = &owned1; + | warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing --> $DIR/dropping_references.rs:12:5 @@ -31,7 +39,11 @@ LL | drop(&&owned1); | | | argument has type `&&SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(&&owned1); +LL + let _ = &&owned1; + | warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing --> $DIR/dropping_references.rs:13:5 @@ -41,7 +53,11 @@ LL | drop(&mut owned1); | | | argument has type `&mut SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(&mut owned1); +LL + let _ = &mut owned1; + | warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing --> $DIR/dropping_references.rs:17:5 @@ -51,7 +67,11 @@ LL | drop(reference1); | | | argument has type `&SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(reference1); +LL + let _ = reference1; + | warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing --> $DIR/dropping_references.rs:20:5 @@ -61,7 +81,11 @@ LL | drop(reference2); | | | argument has type `&mut SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(reference2); +LL + let _ = reference2; + | warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing --> $DIR/dropping_references.rs:23:5 @@ -71,7 +95,11 @@ LL | drop(reference3); | | | argument has type `&SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(reference3); +LL + let _ = reference3; + | warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing --> $DIR/dropping_references.rs:28:5 @@ -81,7 +109,11 @@ LL | drop(&val); | | | argument has type `&T` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(&val); +LL + let _ = &val; + | warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing --> $DIR/dropping_references.rs:36:5 @@ -91,7 +123,11 @@ LL | std::mem::drop(&SomeStruct); | | | argument has type `&SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - std::mem::drop(&SomeStruct); +LL + let _ = &SomeStruct; + | warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing --> $DIR/dropping_references.rs:91:13 @@ -101,7 +137,11 @@ LL | drop(println_and(&13)); | | | argument has type `&i32` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - drop(println_and(&13)); +LL + let _ = println_and(&13); + | warning: calls to `std::mem::drop` with a reference instead of an owned value does nothing --> $DIR/dropping_references.rs:94:14 diff --git a/tests/ui/lint/for_loop_over_fallibles.rs b/tests/ui/lint/for_loop_over_fallibles.rs index 52c3b8f2aae5..89ac20c35217 100644 --- a/tests/ui/lint/for_loop_over_fallibles.rs +++ b/tests/ui/lint/for_loop_over_fallibles.rs @@ -41,3 +41,25 @@ fn _returns_result() -> Result<(), ()> { Ok(()) } + +fn _by_ref() { + // Shared refs + for _ in &Some(1) {} + //~^ WARN for loop over a `&Option`. This is more readably written as an `if let` statement + //~| HELP to check pattern in a loop use `while let` + //~| HELP consider using `if let` to clear intent + for _ in &Ok::<_, ()>(1) {} + //~^ WARN for loop over a `&Result`. This is more readably written as an `if let` statement + //~| HELP to check pattern in a loop use `while let` + //~| HELP consider using `if let` to clear intent + + // Mutable refs + for _ in &mut Some(1) {} + //~^ WARN for loop over a `&mut Option`. This is more readably written as an `if let` statement + //~| HELP to check pattern in a loop use `while let` + //~| HELP consider using `if let` to clear intent + for _ in &mut Ok::<_, ()>(1) {} + //~^ WARN for loop over a `&mut Result`. This is more readably written as an `if let` statement + //~| HELP to check pattern in a loop use `while let` + //~| HELP consider using `if let` to clear intent +} diff --git a/tests/ui/lint/for_loop_over_fallibles.stderr b/tests/ui/lint/for_loop_over_fallibles.stderr index 96efdf85c490..f695de082572 100644 --- a/tests/ui/lint/for_loop_over_fallibles.stderr +++ b/tests/ui/lint/for_loop_over_fallibles.stderr @@ -97,5 +97,65 @@ help: consider using `if let` to clear intent LL | if let Ok(_) = Ok::<_, ()>([0; 0]) {} | ~~~~~~~~~~ ~~~ -warning: 6 warnings emitted +warning: for loop over a `&Option`. This is more readably written as an `if let` statement + --> $DIR/for_loop_over_fallibles.rs:47:14 + | +LL | for _ in &Some(1) {} + | ^^^^^^^^ + | +help: to check pattern in a loop use `while let` + | +LL | while let Some(_) = &Some(1) {} + | ~~~~~~~~~~~~~~~ ~~~ +help: consider using `if let` to clear intent + | +LL | if let Some(_) = &Some(1) {} + | ~~~~~~~~~~~~ ~~~ + +warning: for loop over a `&Result`. This is more readably written as an `if let` statement + --> $DIR/for_loop_over_fallibles.rs:51:14 + | +LL | for _ in &Ok::<_, ()>(1) {} + | ^^^^^^^^^^^^^^^ + | +help: to check pattern in a loop use `while let` + | +LL | while let Ok(_) = &Ok::<_, ()>(1) {} + | ~~~~~~~~~~~~~ ~~~ +help: consider using `if let` to clear intent + | +LL | if let Ok(_) = &Ok::<_, ()>(1) {} + | ~~~~~~~~~~ ~~~ + +warning: for loop over a `&mut Option`. This is more readably written as an `if let` statement + --> $DIR/for_loop_over_fallibles.rs:57:14 + | +LL | for _ in &mut Some(1) {} + | ^^^^^^^^^^^^ + | +help: to check pattern in a loop use `while let` + | +LL | while let Some(_) = &mut Some(1) {} + | ~~~~~~~~~~~~~~~ ~~~ +help: consider using `if let` to clear intent + | +LL | if let Some(_) = &mut Some(1) {} + | ~~~~~~~~~~~~ ~~~ + +warning: for loop over a `&mut Result`. This is more readably written as an `if let` statement + --> $DIR/for_loop_over_fallibles.rs:61:14 + | +LL | for _ in &mut Ok::<_, ()>(1) {} + | ^^^^^^^^^^^^^^^^^^^ + | +help: to check pattern in a loop use `while let` + | +LL | while let Ok(_) = &mut Ok::<_, ()>(1) {} + | ~~~~~~~~~~~~~ ~~~ +help: consider using `if let` to clear intent + | +LL | if let Ok(_) = &mut Ok::<_, ()>(1) {} + | ~~~~~~~~~~ ~~~ + +warning: 10 warnings emitted diff --git a/tests/ui/lint/forgetting_copy_types-can-fixed.fixed b/tests/ui/lint/forgetting_copy_types-can-fixed.fixed new file mode 100644 index 000000000000..c5d65ecc1d42 --- /dev/null +++ b/tests/ui/lint/forgetting_copy_types-can-fixed.fixed @@ -0,0 +1,22 @@ +//@ check-fail +//@ run-rustfix + +#![deny(forgetting_copy_types)] +#![allow(unused_mut)] +#![allow(unused_imports)] + +use std::vec::Vec; +use std::mem::forget; + +#[derive(Copy, Clone)] +struct SomeStruct; + +fn main() { + let s1 = SomeStruct {}; + let s2 = s1; + let mut s3 = s1; + + let _ = s1; //~ ERROR calls to `std::mem::forget` + let _ = s2; //~ ERROR calls to `std::mem::forget` + let _ = s3; //~ ERROR calls to `std::mem::forget` +} diff --git a/tests/ui/lint/forgetting_copy_types-can-fixed.rs b/tests/ui/lint/forgetting_copy_types-can-fixed.rs new file mode 100644 index 000000000000..c55722942800 --- /dev/null +++ b/tests/ui/lint/forgetting_copy_types-can-fixed.rs @@ -0,0 +1,22 @@ +//@ check-fail +//@ run-rustfix + +#![deny(forgetting_copy_types)] +#![allow(unused_mut)] +#![allow(unused_imports)] + +use std::vec::Vec; +use std::mem::forget; + +#[derive(Copy, Clone)] +struct SomeStruct; + +fn main() { + let s1 = SomeStruct {}; + let s2 = s1; + let mut s3 = s1; + + forget(s1); //~ ERROR calls to `std::mem::forget` + forget(s2); //~ ERROR calls to `std::mem::forget` + forget(s3); //~ ERROR calls to `std::mem::forget` +} diff --git a/tests/ui/lint/forgetting_copy_types-can-fixed.stderr b/tests/ui/lint/forgetting_copy_types-can-fixed.stderr new file mode 100644 index 000000000000..cb7bbf02222e --- /dev/null +++ b/tests/ui/lint/forgetting_copy_types-can-fixed.stderr @@ -0,0 +1,49 @@ +error: calls to `std::mem::forget` with a value that implements `Copy` does nothing + --> $DIR/forgetting_copy_types-can-fixed.rs:19:5 + | +LL | forget(s1); + | ^^^^^^^--^ + | | + | argument has type `SomeStruct` + | +note: the lint level is defined here + --> $DIR/forgetting_copy_types-can-fixed.rs:4:9 + | +LL | #![deny(forgetting_copy_types)] + | ^^^^^^^^^^^^^^^^^^^^^ +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(s1); +LL + let _ = s1; + | + +error: calls to `std::mem::forget` with a value that implements `Copy` does nothing + --> $DIR/forgetting_copy_types-can-fixed.rs:20:5 + | +LL | forget(s2); + | ^^^^^^^--^ + | | + | argument has type `SomeStruct` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(s2); +LL + let _ = s2; + | + +error: calls to `std::mem::forget` with a value that implements `Copy` does nothing + --> $DIR/forgetting_copy_types-can-fixed.rs:21:5 + | +LL | forget(s3); + | ^^^^^^^--^ + | | + | argument has type `SomeStruct` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(s3); +LL + let _ = s3; + | + +error: aborting due to 3 previous errors + diff --git a/tests/ui/lint/forgetting_copy_types.stderr b/tests/ui/lint/forgetting_copy_types.stderr index 36d1ef5c53e9..980ee6caba25 100644 --- a/tests/ui/lint/forgetting_copy_types.stderr +++ b/tests/ui/lint/forgetting_copy_types.stderr @@ -6,12 +6,16 @@ LL | forget(s1); | | | argument has type `SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result note: the lint level is defined here --> $DIR/forgetting_copy_types.rs:3:9 | LL | #![warn(forgetting_copy_types)] | ^^^^^^^^^^^^^^^^^^^^^ +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(s1); +LL + let _ = s1; + | warning: calls to `std::mem::forget` with a value that implements `Copy` does nothing --> $DIR/forgetting_copy_types.rs:35:5 @@ -21,7 +25,11 @@ LL | forget(s2); | | | argument has type `SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(s2); +LL + let _ = s2; + | warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing --> $DIR/forgetting_copy_types.rs:36:5 @@ -31,8 +39,12 @@ LL | forget(s3); | | | argument has type `&SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result = note: `#[warn(forgetting_references)]` on by default +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(s3); +LL + let _ = s3; + | warning: calls to `std::mem::forget` with a value that implements `Copy` does nothing --> $DIR/forgetting_copy_types.rs:37:5 @@ -42,7 +54,11 @@ LL | forget(s4); | | | argument has type `SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(s4); +LL + let _ = s4; + | warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing --> $DIR/forgetting_copy_types.rs:38:5 @@ -52,7 +68,11 @@ LL | forget(s5); | | | argument has type `&SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(s5); +LL + let _ = s5; + | warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing --> $DIR/forgetting_copy_types.rs:50:5 @@ -62,7 +82,11 @@ LL | forget(a2); | | | argument has type `&AnotherStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(a2); +LL + let _ = a2; + | warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing --> $DIR/forgetting_copy_types.rs:52:5 @@ -72,7 +96,11 @@ LL | forget(a3); | | | argument has type `&AnotherStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(a3); +LL + let _ = a3; + | warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing --> $DIR/forgetting_copy_types.rs:53:5 @@ -82,7 +110,11 @@ LL | forget(a4); | | | argument has type `&AnotherStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(a4); +LL + let _ = a4; + | warning: 8 warnings emitted diff --git a/tests/ui/lint/forgetting_references-can-fixed.fixed b/tests/ui/lint/forgetting_references-can-fixed.fixed new file mode 100644 index 000000000000..64475f042840 --- /dev/null +++ b/tests/ui/lint/forgetting_references-can-fixed.fixed @@ -0,0 +1,40 @@ +//@ check-fail +//@ run-rustfix + +#![deny(forgetting_references)] + +use std::mem::forget; + +struct SomeStruct; + +fn main() { + let _ = &SomeStruct; //~ ERROR calls to `std::mem::forget` + + let mut owned = SomeStruct; + let _ = &owned; //~ ERROR calls to `std::mem::forget` + let _ = &&owned; //~ ERROR calls to `std::mem::forget` + let _ = &mut owned; //~ ERROR calls to `std::mem::forget` + forget(owned); + + let reference1 = &SomeStruct; + let _ = &*reference1; //~ ERROR calls to `std::mem::forget` + + let reference2 = &mut SomeStruct; + let _ = reference2; //~ ERROR calls to `std::mem::forget` + + let ref reference3 = SomeStruct; + let _ = reference3; //~ ERROR calls to `std::mem::forget` +} + +#[allow(dead_code)] +fn test_generic_fn_forget(val: T) { + let _ = &val; //~ ERROR calls to `std::mem::forget` + forget(val); +} + +#[allow(dead_code)] +fn test_similarly_named_function() { + fn forget(_val: T) {} + forget(&SomeStruct); //OK; call to unrelated function which happens to have the same name + let _ = &SomeStruct; //~ ERROR calls to `std::mem::forget` +} diff --git a/tests/ui/lint/forgetting_references-can-fixed.rs b/tests/ui/lint/forgetting_references-can-fixed.rs new file mode 100644 index 000000000000..4c9ef541d341 --- /dev/null +++ b/tests/ui/lint/forgetting_references-can-fixed.rs @@ -0,0 +1,40 @@ +//@ check-fail +//@ run-rustfix + +#![deny(forgetting_references)] + +use std::mem::forget; + +struct SomeStruct; + +fn main() { + forget(&SomeStruct); //~ ERROR calls to `std::mem::forget` + + let mut owned = SomeStruct; + forget(&owned); //~ ERROR calls to `std::mem::forget` + forget(&&owned); //~ ERROR calls to `std::mem::forget` + forget(&mut owned); //~ ERROR calls to `std::mem::forget` + forget(owned); + + let reference1 = &SomeStruct; + forget(&*reference1); //~ ERROR calls to `std::mem::forget` + + let reference2 = &mut SomeStruct; + forget(reference2); //~ ERROR calls to `std::mem::forget` + + let ref reference3 = SomeStruct; + forget(reference3); //~ ERROR calls to `std::mem::forget` +} + +#[allow(dead_code)] +fn test_generic_fn_forget(val: T) { + forget(&val); //~ ERROR calls to `std::mem::forget` + forget(val); +} + +#[allow(dead_code)] +fn test_similarly_named_function() { + fn forget(_val: T) {} + forget(&SomeStruct); //OK; call to unrelated function which happens to have the same name + std::mem::forget(&SomeStruct); //~ ERROR calls to `std::mem::forget` +} diff --git a/tests/ui/lint/forgetting_references-can-fixed.stderr b/tests/ui/lint/forgetting_references-can-fixed.stderr new file mode 100644 index 000000000000..05eb636ab459 --- /dev/null +++ b/tests/ui/lint/forgetting_references-can-fixed.stderr @@ -0,0 +1,133 @@ +error: calls to `std::mem::forget` with a reference instead of an owned value does nothing + --> $DIR/forgetting_references-can-fixed.rs:11:5 + | +LL | forget(&SomeStruct); + | ^^^^^^^-----------^ + | | + | argument has type `&SomeStruct` + | +note: the lint level is defined here + --> $DIR/forgetting_references-can-fixed.rs:4:9 + | +LL | #![deny(forgetting_references)] + | ^^^^^^^^^^^^^^^^^^^^^ +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(&SomeStruct); +LL + let _ = &SomeStruct; + | + +error: calls to `std::mem::forget` with a reference instead of an owned value does nothing + --> $DIR/forgetting_references-can-fixed.rs:14:5 + | +LL | forget(&owned); + | ^^^^^^^------^ + | | + | argument has type `&SomeStruct` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(&owned); +LL + let _ = &owned; + | + +error: calls to `std::mem::forget` with a reference instead of an owned value does nothing + --> $DIR/forgetting_references-can-fixed.rs:15:5 + | +LL | forget(&&owned); + | ^^^^^^^-------^ + | | + | argument has type `&&SomeStruct` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(&&owned); +LL + let _ = &&owned; + | + +error: calls to `std::mem::forget` with a reference instead of an owned value does nothing + --> $DIR/forgetting_references-can-fixed.rs:16:5 + | +LL | forget(&mut owned); + | ^^^^^^^----------^ + | | + | argument has type `&mut SomeStruct` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(&mut owned); +LL + let _ = &mut owned; + | + +error: calls to `std::mem::forget` with a reference instead of an owned value does nothing + --> $DIR/forgetting_references-can-fixed.rs:20:5 + | +LL | forget(&*reference1); + | ^^^^^^^------------^ + | | + | argument has type `&SomeStruct` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(&*reference1); +LL + let _ = &*reference1; + | + +error: calls to `std::mem::forget` with a reference instead of an owned value does nothing + --> $DIR/forgetting_references-can-fixed.rs:23:5 + | +LL | forget(reference2); + | ^^^^^^^----------^ + | | + | argument has type `&mut SomeStruct` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(reference2); +LL + let _ = reference2; + | + +error: calls to `std::mem::forget` with a reference instead of an owned value does nothing + --> $DIR/forgetting_references-can-fixed.rs:26:5 + | +LL | forget(reference3); + | ^^^^^^^----------^ + | | + | argument has type `&SomeStruct` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(reference3); +LL + let _ = reference3; + | + +error: calls to `std::mem::forget` with a reference instead of an owned value does nothing + --> $DIR/forgetting_references-can-fixed.rs:31:5 + | +LL | forget(&val); + | ^^^^^^^----^ + | | + | argument has type `&T` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(&val); +LL + let _ = &val; + | + +error: calls to `std::mem::forget` with a reference instead of an owned value does nothing + --> $DIR/forgetting_references-can-fixed.rs:39:5 + | +LL | std::mem::forget(&SomeStruct); + | ^^^^^^^^^^^^^^^^^-----------^ + | | + | argument has type `&SomeStruct` + | +help: use `let _ = ...` to ignore the expression or result + | +LL - std::mem::forget(&SomeStruct); +LL + let _ = &SomeStruct; + | + +error: aborting due to 9 previous errors + diff --git a/tests/ui/lint/forgetting_references.rs b/tests/ui/lint/forgetting_references.rs index ecfa23ee4970..d0ec2a77ab1c 100644 --- a/tests/ui/lint/forgetting_references.rs +++ b/tests/ui/lint/forgetting_references.rs @@ -23,6 +23,16 @@ fn main() { let ref reference3 = SomeStruct; forget(reference3); //~ WARN calls to `std::mem::forget` + + let ref reference4 = SomeStruct; + + let a = 1; + match a { + 1 => forget(&*reference1), //~ WARN calls to `std::mem::forget` + 2 => forget(reference3), //~ WARN calls to `std::mem::forget` + 3 => forget(reference4), //~ WARN calls to `std::mem::forget` + _ => {} + } } #[allow(dead_code)] diff --git a/tests/ui/lint/forgetting_references.stderr b/tests/ui/lint/forgetting_references.stderr index 5624b690789f..afd5030a6805 100644 --- a/tests/ui/lint/forgetting_references.stderr +++ b/tests/ui/lint/forgetting_references.stderr @@ -6,12 +6,16 @@ LL | forget(&SomeStruct); | | | argument has type `&SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result note: the lint level is defined here --> $DIR/forgetting_references.rs:3:9 | LL | #![warn(forgetting_references)] | ^^^^^^^^^^^^^^^^^^^^^ +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(&SomeStruct); +LL + let _ = &SomeStruct; + | warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing --> $DIR/forgetting_references.rs:13:5 @@ -21,7 +25,11 @@ LL | forget(&owned); | | | argument has type `&SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(&owned); +LL + let _ = &owned; + | warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing --> $DIR/forgetting_references.rs:14:5 @@ -31,7 +39,11 @@ LL | forget(&&owned); | | | argument has type `&&SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(&&owned); +LL + let _ = &&owned; + | warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing --> $DIR/forgetting_references.rs:15:5 @@ -41,7 +53,11 @@ LL | forget(&mut owned); | | | argument has type `&mut SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(&mut owned); +LL + let _ = &mut owned; + | warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing --> $DIR/forgetting_references.rs:19:5 @@ -51,7 +67,11 @@ LL | forget(&*reference1); | | | argument has type `&SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(&*reference1); +LL + let _ = &*reference1; + | warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing --> $DIR/forgetting_references.rs:22:5 @@ -61,7 +81,11 @@ LL | forget(reference2); | | | argument has type `&mut SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(reference2); +LL + let _ = reference2; + | warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing --> $DIR/forgetting_references.rs:25:5 @@ -71,27 +95,69 @@ LL | forget(reference3); | | | argument has type `&SomeStruct` | +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(reference3); +LL + let _ = reference3; + | + +warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing + --> $DIR/forgetting_references.rs:31:14 + | +LL | 1 => forget(&*reference1), + | ^^^^^^^------------^ + | | + | argument has type `&SomeStruct` + | = note: use `let _ = ...` to ignore the expression or result warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forgetting_references.rs:30:5 + --> $DIR/forgetting_references.rs:32:14 + | +LL | 2 => forget(reference3), + | ^^^^^^^----------^ + | | + | argument has type `&SomeStruct` + | + = note: use `let _ = ...` to ignore the expression or result + +warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing + --> $DIR/forgetting_references.rs:33:14 + | +LL | 3 => forget(reference4), + | ^^^^^^^----------^ + | | + | argument has type `&SomeStruct` + | + = note: use `let _ = ...` to ignore the expression or result + +warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing + --> $DIR/forgetting_references.rs:40:5 | LL | forget(&val); | ^^^^^^^----^ | | | argument has type `&T` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - forget(&val); +LL + let _ = &val; + | warning: calls to `std::mem::forget` with a reference instead of an owned value does nothing - --> $DIR/forgetting_references.rs:38:5 + --> $DIR/forgetting_references.rs:48:5 | LL | std::mem::forget(&SomeStruct); | ^^^^^^^^^^^^^^^^^-----------^ | | | argument has type `&SomeStruct` | - = note: use `let _ = ...` to ignore the expression or result +help: use `let _ = ...` to ignore the expression or result + | +LL - std::mem::forget(&SomeStruct); +LL + let _ = &SomeStruct; + | -warning: 9 warnings emitted +warning: 12 warnings emitted diff --git a/tests/ui/lint-group-denied-lint-allowed.rs b/tests/ui/lint/group-denied-lint-allowed.rs similarity index 100% rename from tests/ui/lint-group-denied-lint-allowed.rs rename to tests/ui/lint/group-denied-lint-allowed.rs diff --git a/tests/ui/lint-group-forbid-always-trumps-cli.rs b/tests/ui/lint/group-forbid-always-trumps-cli.rs similarity index 100% rename from tests/ui/lint-group-forbid-always-trumps-cli.rs rename to tests/ui/lint/group-forbid-always-trumps-cli.rs diff --git a/tests/ui/lint-group-forbid-always-trumps-cli.stderr b/tests/ui/lint/group-forbid-always-trumps-cli.stderr similarity index 84% rename from tests/ui/lint-group-forbid-always-trumps-cli.stderr rename to tests/ui/lint/group-forbid-always-trumps-cli.stderr index 04a0f56c1633..21674ebae9ce 100644 --- a/tests/ui/lint-group-forbid-always-trumps-cli.stderr +++ b/tests/ui/lint/group-forbid-always-trumps-cli.stderr @@ -1,5 +1,5 @@ error: unused variable: `x` - --> $DIR/lint-group-forbid-always-trumps-cli.rs:4:9 + --> $DIR/group-forbid-always-trumps-cli.rs:4:9 | LL | let x = 1; | ^ help: if this is intentional, prefix it with an underscore: `_x` diff --git a/tests/ui/lint/missing_copy_impl_trivial_bounds.rs b/tests/ui/lint/missing_copy_impl_trivial_bounds.rs new file mode 100644 index 000000000000..9b743417bd52 --- /dev/null +++ b/tests/ui/lint/missing_copy_impl_trivial_bounds.rs @@ -0,0 +1,21 @@ +//@ check-pass + +#![feature(trivial_bounds)] +#![allow(trivial_bounds)] + +// Make sure that we still use the where-clauses from the struct when checking +// if it may implement `Copy` unconditionally. +// Fix for . + +pub trait Foo { + type Assoc; +} + +pub struct Bar; + +// This needs to be public +pub struct Baz2(::Assoc) +where + Bar: Foo; + +fn main() {} diff --git a/tests/ui/lint/non-local-defs/cargo-update.stderr b/tests/ui/lint/non-local-defs/cargo-update.stderr index e9e33b9aa173..afd37d03a231 100644 --- a/tests/ui/lint/non-local-defs/cargo-update.stderr +++ b/tests/ui/lint/non-local-defs/cargo-update.stderr @@ -1,14 +1,19 @@ -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/cargo-update.rs:17:1 | LL | non_local_macro::non_local_impl!(LocalStruct); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `LocalStruct` is not local + | `Debug` is not local + | move the `impl` block outside of this constant `_IMPL_DEBUG` | - = help: move this `impl` block outside the of the current constant `_IMPL_DEBUG` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type - = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue + = note: the macro `non_local_macro::non_local_impl` defines the non-local `impl`, and may need to be changed = note: the macro `non_local_macro::non_local_impl` may come from an old version of the `non_local_macro` crate, try updating your dependency with `cargo update -p non_local_macro` + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` + = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration + = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue = note: `#[warn(non_local_definitions)]` on by default = note: this warning originates in the macro `non_local_macro::non_local_impl` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/lint/non-local-defs/consts.stderr b/tests/ui/lint/non-local-defs/consts.stderr index d15b452b004e..2756ea401387 100644 --- a/tests/ui/lint/non-local-defs/consts.stderr +++ b/tests/ui/lint/non-local-defs/consts.stderr @@ -1,102 +1,134 @@ -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/consts.rs:13:5 | LL | const Z: () = { - | - help: use a const-anon item to suppress this lint: `_` + | ----------- + | | | + | | help: use a const-anon item to suppress this lint: `_` + | move the `impl` block outside of this constant `Z` ... LL | impl Uto for &Test {} - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^---^^^^^----- + | | | + | | `&'_ Test` is not local + | `Uto` is not local | - = help: move this `impl` block outside the of the current constant `Z` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` + = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue = note: `#[warn(non_local_definitions)]` on by default -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/consts.rs:24:5 | +LL | static A: u32 = { + | ------------- move the `impl` block outside of this static `A` LL | impl Uto2 for Test {} - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^----^^^^^---- + | | | + | | `Test` is not local + | `Uto2` is not local | - = help: move this `impl` block outside the of the current static `A` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` + = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/consts.rs:32:5 | +LL | const B: u32 = { + | ------------ move the `impl` block outside of this constant `B` LL | impl Uto3 for Test {} - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^----^^^^^---- + | | | + | | `Test` is not local + | `Uto3` is not local | - = help: move this `impl` block outside the of the current constant `B` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` + = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/consts.rs:43:5 | -LL | / impl Test { -LL | | -LL | | fn foo() {} -LL | | } - | |_____^ +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +LL | impl Test { + | ^^^^^---- + | | + | `Test` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/consts.rs:50:9 | -LL | / impl Test { +LL | const { + | ___________- +LL | | impl Test { + | | ^^^^^---- + | | | + | | `Test` is not local LL | | LL | | fn hoo() {} -LL | | } - | |_________^ +... | +LL | | 1 +LL | | }; + | |_____- move the `impl` block outside of this inline constant `` and up 2 bodies | - = help: move this `impl` block outside the of the current inline constant `` and up 2 bodies - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/consts.rs:59:9 | -LL | / impl Test { -LL | | -LL | | fn foo2() {} -LL | | } - | |_________^ +LL | const _: u32 = { + | ------------ move the `impl` block outside of this constant `_` and up 2 bodies +LL | impl Test { + | ^^^^^---- + | | + | `Test` is not local | - = help: move this `impl` block outside the of the current constant `_` and up 2 bodies - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` + = note: items in an anonymous const item (`const _: () = { ... }`) are treated as in the same scope as the anonymous const's declaration = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/consts.rs:72:9 | +LL | let _a = || { + | -- move the `impl` block outside of this closure `` and up 2 bodies LL | impl Uto9 for Test {} - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^----^^^^^---- + | | | + | | `Test` is not local + | `Uto9` is not local | - = help: move this `impl` block outside the of the current closure `` and up 2 bodies - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/consts.rs:79:9 | -LL | impl Uto10 for Test {} - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | type A = [u32; { + | ____________________- +LL | | impl Uto10 for Test {} + | | ^^^^^-----^^^^^---- + | | | | + | | | `Test` is not local + | | `Uto10` is not local +LL | | +... | +LL | | }]; + | |_____- move the `impl` block outside of this constant expression `` and up 2 bodies | - = help: move this `impl` block outside the of the current constant expression `` and up 2 bodies - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: 8 warnings emitted diff --git a/tests/ui/lint/non-local-defs/exhaustive-trait.stderr b/tests/ui/lint/non-local-defs/exhaustive-trait.stderr index 8d58d4dd27c5..67df0e31d5bd 100644 --- a/tests/ui/lint/non-local-defs/exhaustive-trait.stderr +++ b/tests/ui/lint/non-local-defs/exhaustive-trait.stderr @@ -1,98 +1,97 @@ -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive-trait.rs:7:5 | -LL | / impl PartialEq<()> for Dog { -LL | | -LL | | fn eq(&self, _: &()) -> bool { -LL | | todo!() -LL | | } -LL | | } - | |_____^ +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +LL | impl PartialEq<()> for Dog { + | ^^^^^---------^^^^^^^^^--- + | | | + | | `Dog` is not local + | `PartialEq` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue = note: `#[warn(non_local_definitions)]` on by default -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive-trait.rs:14:5 | -LL | / impl PartialEq<()> for &Dog { -LL | | -LL | | fn eq(&self, _: &()) -> bool { -LL | | todo!() -LL | | } -LL | | } - | |_____^ +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... +LL | impl PartialEq<()> for &Dog { + | ^^^^^---------^^^^^^^^^---- + | | | + | | `&'_ Dog` is not local + | `PartialEq` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive-trait.rs:21:5 | -LL | / impl PartialEq for () { -LL | | -LL | | fn eq(&self, _: &Dog) -> bool { -LL | | todo!() -LL | | } -LL | | } - | |_____^ +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... +LL | impl PartialEq for () { + | ^^^^^---------^^^^^^^^^^-- + | | | + | | `()` is not local + | `PartialEq` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive-trait.rs:28:5 | -LL | / impl PartialEq<&Dog> for () { -LL | | -LL | | fn eq(&self, _: &&Dog) -> bool { -LL | | todo!() -LL | | } -LL | | } - | |_____^ +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... +LL | impl PartialEq<&Dog> for () { + | ^^^^^---------^^^^^^^^^^^-- + | | | + | | `()` is not local + | `PartialEq` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive-trait.rs:35:5 | -LL | / impl PartialEq for &Dog { -LL | | -LL | | fn eq(&self, _: &Dog) -> bool { -LL | | todo!() -LL | | } -LL | | } - | |_____^ +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... +LL | impl PartialEq for &Dog { + | ^^^^^---------^^^^^^^^^^---- + | | | + | | `&'_ Dog` is not local + | `PartialEq` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive-trait.rs:42:5 | -LL | / impl PartialEq<&Dog> for &Dog { -LL | | -LL | | fn eq(&self, _: &&Dog) -> bool { -LL | | todo!() -LL | | } -LL | | } - | |_____^ +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... +LL | impl PartialEq<&Dog> for &Dog { + | ^^^^^---------^^^^^^^^^^^---- + | | | + | | `&'_ Dog` is not local + | `PartialEq` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: 6 warnings emitted diff --git a/tests/ui/lint/non-local-defs/exhaustive.stderr b/tests/ui/lint/non-local-defs/exhaustive.stderr index b3697969c4f8..1e0d5caec383 100644 --- a/tests/ui/lint/non-local-defs/exhaustive.stderr +++ b/tests/ui/lint/non-local-defs/exhaustive.stderr @@ -1,238 +1,344 @@ -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:10:5 | -LL | / impl Test { -LL | | -LL | | fn foo() {} -LL | | } - | |_____^ +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +LL | impl Test { + | ^^^^^---- + | | + | `Test` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue = note: `#[warn(non_local_definitions)]` on by default -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:15:5 | -LL | / impl Display for Test { -LL | | -LL | | fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -LL | | todo!() -LL | | } -LL | | } - | |_____^ +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... +LL | impl Display for Test { + | ^^^^^-------^^^^^---- + | | | + | | `Test` is not local + | `Display` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:22:5 | +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... LL | impl dyn Trait {} - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^----- + | | + | `Trait` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:25:5 | +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... LL | impl Trait for Vec { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^-----^^^^^---^^^ + | | | + | | `Vec` is not local + | `Trait` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:28:5 | +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... LL | impl Trait for &dyn Trait {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-----^^^^^---------- + | | | + | | `&'_ dyn Trait` is not local + | `Trait` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:31:5 | +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... LL | impl Trait for *mut Test {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-----^^^^^--------- + | | | + | | `*mut Test` is not local + | `Trait` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:34:5 | +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... LL | impl Trait for *mut [Test] {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-----^^^^^----------- + | | | + | | `*mut [Test]` is not local + | `Trait` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:37:5 | +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... LL | impl Trait for [Test; 8] {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-----^^^^^--------- + | | | + | | `[Test; 8]` is not local + | `Trait` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:40:5 | +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... LL | impl Trait for (Test,) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-----^^^^^------- + | | | + | | `(Test,)` is not local + | `Trait` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:43:5 | +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... LL | impl Trait for fn(Test) -> () {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-----^^^^^-------------- + | | | + | | `fn(: Test) -> ()` is not local + | `Trait` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:46:5 | +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +... LL | impl Trait for fn() -> Test {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-----^^^^^------------ + | | | + | | `fn() -> Test` is not local + | `Trait` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:50:9 | +LL | let _a = || { + | -- move the `impl` block outside of this closure `` and up 2 bodies LL | impl Trait for Test {} - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-----^^^^^---- + | | | + | | `Test` is not local + | `Trait` is not local | - = help: move this `impl` block outside the of the current closure `` and up 2 bodies - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:58:5 | LL | impl Trait for *mut InsideMain {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-----^^^^^--------------- + | | | + | | `*mut InsideMain` is not local + | | help: remove `*mut ` to make the `impl` local + | `Trait` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `main` + --> $DIR/exhaustive.rs:9:1 + | +LL | fn main() { + | ^^^^^^^^^ +... +LL | struct InsideMain; + | ----------------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:60:5 | LL | impl Trait for *mut [InsideMain] {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-----^^^^^----------------- + | | | + | | `*mut [InsideMain]` is not local + | `Trait` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `main` + --> $DIR/exhaustive.rs:9:1 + | +LL | fn main() { + | ^^^^^^^^^ +... +LL | struct InsideMain; + | ----------------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:62:5 | LL | impl Trait for [InsideMain; 8] {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-----^^^^^--------------- + | | | + | | `[InsideMain; 8]` is not local + | `Trait` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `main` + --> $DIR/exhaustive.rs:9:1 + | +LL | fn main() { + | ^^^^^^^^^ +... +LL | struct InsideMain; + | ----------------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:64:5 | LL | impl Trait for (InsideMain,) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-----^^^^^------------- + | | | + | | `(InsideMain,)` is not local + | `Trait` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `main` + --> $DIR/exhaustive.rs:9:1 + | +LL | fn main() { + | ^^^^^^^^^ +... +LL | struct InsideMain; + | ----------------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:66:5 | LL | impl Trait for fn(InsideMain) -> () {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-----^^^^^-------------------- + | | | + | | `fn(: InsideMain) -> ()` is not local + | `Trait` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `main` + --> $DIR/exhaustive.rs:9:1 + | +LL | fn main() { + | ^^^^^^^^^ +... +LL | struct InsideMain; + | ----------------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:68:5 | LL | impl Trait for fn() -> InsideMain {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-----^^^^^------------------ + | | | + | | `fn() -> InsideMain` is not local + | `Trait` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `main` + --> $DIR/exhaustive.rs:9:1 + | +LL | fn main() { + | ^^^^^^^^^ +... +LL | struct InsideMain; + | ----------------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:72:9 | -LL | / impl Display for InsideMain { -LL | | -LL | | fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -LL | | todo!() -LL | | } -LL | | } - | |_________^ +LL | fn inside_inside() { + | ------------------ move the `impl` block outside of this function `inside_inside` and up 2 bodies +LL | impl Display for InsideMain { + | ^^^^^-------^^^^^---------- + | | | + | | `InsideMain` is not local + | `Display` is not local | - = help: move this `impl` block outside the of the current function `inside_inside` and up 2 bodies - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/exhaustive.rs:79:9 | -LL | / impl InsideMain { -LL | | -LL | | fn bar() {} -LL | | } - | |_________^ +LL | fn inside_inside() { + | ------------------ move the `impl` block outside of this function `inside_inside` and up 2 bodies +... +LL | impl InsideMain { + | ^^^^^---------- + | | + | `InsideMain` is not local | - = help: move this `impl` block outside the of the current function `inside_inside` and up 2 bodies - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: 20 warnings emitted diff --git a/tests/ui/lint/non-local-defs/from-local-for-global.stderr b/tests/ui/lint/non-local-defs/from-local-for-global.stderr index 0cd385049aa6..67fd937d134c 100644 --- a/tests/ui/lint/non-local-defs/from-local-for-global.stderr +++ b/tests/ui/lint/non-local-defs/from-local-for-global.stderr @@ -1,77 +1,99 @@ -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/from-local-for-global.rs:8:5 | -LL | / impl From for () { -LL | | -LL | | fn from(_: Cat) -> () { -LL | | todo!() -LL | | } -LL | | } - | |_____^ +LL | fn main() { + | --------- move the `impl` block outside of this function `main` +LL | impl From for () { + | ^^^^^----^^^^^^^^^^-- + | | | + | | `()` is not local + | `From` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue = note: `#[warn(non_local_definitions)]` on by default -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/from-local-for-global.rs:18:5 | -LL | / impl From>> for () { -LL | | -LL | | fn from(_: Wrap>) -> Self { -LL | | todo!() -LL | | } -LL | | } - | |_____^ +LL | impl From>> for () { + | ^^^^^----^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | | + | `From` is not local `()` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `main` + --> $DIR/from-local-for-global.rs:7:1 + | +LL | fn main() { + | ^^^^^^^^^ +... +LL | struct Elephant; + | --------------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/from-local-for-global.rs:32:5 | LL | impl StillNonLocal for &Foo {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^-------------^^^^^---- + | | | + | | `&'_ Foo` is not local + | | help: remove `&` to make the `impl` local + | `StillNonLocal` is not local | - = help: move this `impl` block outside the of the current function `only_global` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `only_global` + --> $DIR/from-local-for-global.rs:30:1 + | +LL | fn only_global() { + | ^^^^^^^^^^^^^^^^ +LL | struct Foo; + | ---------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/from-local-for-global.rs:40:5 | -LL | / impl From for GlobalSameFunction { -LL | | -LL | | fn from(x: Local1) -> GlobalSameFunction { -LL | | x.0 -LL | | } -LL | | } - | |_____^ +LL | impl From for GlobalSameFunction { + | ^^^^^----^^^^^^^^^^^^^------------------ + | | | + | | `GlobalSameFunction` is not local + | `From` is not local | - = help: move this `impl` block outside the of the current function `same_function` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `same_function` + --> $DIR/from-local-for-global.rs:38:1 + | +LL | fn same_function() { + | ^^^^^^^^^^^^^^^^^^ +LL | struct Local1(GlobalSameFunction); + | ------------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/from-local-for-global.rs:48:5 | -LL | / impl From for GlobalSameFunction { -LL | | -LL | | fn from(x: Local2) -> GlobalSameFunction { -LL | | x.0 -LL | | } -LL | | } - | |_____^ +LL | impl From for GlobalSameFunction { + | ^^^^^----^^^^^^^^^^^^^------------------ + | | | + | | `GlobalSameFunction` is not local + | `From` is not local | - = help: move this `impl` block outside the of the current function `same_function` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `same_function` + --> $DIR/from-local-for-global.rs:38:1 + | +LL | fn same_function() { + | ^^^^^^^^^^^^^^^^^^ +... +LL | struct Local2(GlobalSameFunction); + | ------------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: 5 warnings emitted diff --git a/tests/ui/lint/non-local-defs/generics.stderr b/tests/ui/lint/non-local-defs/generics.stderr index 681d9e45e7a0..ed2f87a4ed2d 100644 --- a/tests/ui/lint/non-local-defs/generics.stderr +++ b/tests/ui/lint/non-local-defs/generics.stderr @@ -1,113 +1,160 @@ -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/generics.rs:9:5 | LL | impl Global for Vec { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^------^^^^^---^^^ + | | | + | | `Vec` is not local + | `Global` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `main` + --> $DIR/generics.rs:6:1 + | +LL | fn main() { + | ^^^^^^^^^ +LL | trait Local {}; + | ----------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue = note: `#[warn(non_local_definitions)]` on by default -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/generics.rs:20:5 | LL | impl Uto7 for Test where Local: std::any::Any {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^----^^^^^---- + | | | + | | `Test` is not local + | `Uto7` is not local | - = help: move this `impl` block outside the of the current function `bad` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `bad` + --> $DIR/generics.rs:18:1 + | +LL | fn bad() { + | ^^^^^^^^ +LL | struct Local; + | ------------ may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/generics.rs:23:5 | +LL | fn bad() { + | -------- move the `impl` block outside of this function `bad` +... LL | impl Uto8 for T {} - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^----^^^^^- + | | | + | | `T` is not local + | `Uto8` is not local | - = help: move this `impl` block outside the of the current function `bad` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/generics.rs:32:5 | -LL | / impl Default for UwU { -LL | | -LL | | fn default() -> Self { -LL | | UwU(OwO) -LL | | } -LL | | } - | |_____^ +LL | impl Default for UwU { + | ^^^^^-------^^^^^---^^^^^ + | | | + | | `UwU` is not local + | `Default` is not local | - = help: move this `impl` block outside the of the current function `fun` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `fun` + --> $DIR/generics.rs:29:1 + | +LL | fn fun() { + | ^^^^^^^^ +LL | #[derive(Debug)] +LL | struct OwO; + | ---------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/generics.rs:43:5 | -LL | / impl AsRef for () { -LL | | -LL | | fn as_ref(&self) -> &Cat { &Cat } -LL | | } - | |_____^ +LL | impl AsRef for () { + | ^^^^^-----^^^^^^^^^^-- + | | | + | | `()` is not local + | `AsRef` is not local | - = help: move this `impl` block outside the of the current function `meow` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `meow` + --> $DIR/generics.rs:40:1 + | +LL | fn meow() { + | ^^^^^^^^^ +LL | #[derive(Debug)] +LL | struct Cat; + | ---------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/generics.rs:54:5 | -LL | / impl PartialEq for G { -LL | | -LL | | fn eq(&self, _: &B) -> bool { -LL | | true -LL | | } -LL | | } - | |_____^ +LL | impl PartialEq for G { + | ^^^^^---------^^^^^^^^- + | | | + | | `G` is not local + | `PartialEq` is not local | - = help: move this `impl` block outside the of the current function `fun2` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `fun2` + --> $DIR/generics.rs:51:1 + | +LL | fn fun2() { + | ^^^^^^^^^ +LL | #[derive(Debug, Default)] +LL | struct B; + | -------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/generics.rs:69:5 | -LL | / impl From>> for () { -LL | | -LL | | fn from(_: Wrap>) -> Self { -LL | | todo!() -LL | | } -LL | | } - | |_____^ +LL | impl From>> for () { + | ^^^^^----^^^^^^^^^^^^^^^^^^^^^^^-- + | | | + | `From` is not local `()` is not local | - = help: move this `impl` block outside the of the current function `rawr` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `rawr` + --> $DIR/generics.rs:66:1 + | +LL | fn rawr() { + | ^^^^^^^^^ +LL | struct Lion; + | ----------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/generics.rs:76:5 | -LL | / impl From<()> for Wrap { -LL | | -LL | | fn from(_: ()) -> Self { -LL | | todo!() -LL | | } -LL | | } - | |_____^ +LL | impl From<()> for Wrap { + | ^^^^^----^^^^^^^^^----^^^^^^ + | | | + | | `Wrap` is not local + | `From` is not local | - = help: move this `impl` block outside the of the current function `rawr` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `rawr` + --> $DIR/generics.rs:66:1 + | +LL | fn rawr() { + | ^^^^^^^^^ +LL | struct Lion; + | ----------- may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: 8 warnings emitted diff --git a/tests/ui/lint/non-local-defs/inside-macro_rules.stderr b/tests/ui/lint/non-local-defs/inside-macro_rules.stderr index 319682b973d5..fa9ba2cb785d 100644 --- a/tests/ui/lint/non-local-defs/inside-macro_rules.stderr +++ b/tests/ui/lint/non-local-defs/inside-macro_rules.stderr @@ -1,15 +1,20 @@ -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/inside-macro_rules.rs:9:13 | +LL | fn my_func() { + | ------------ move the `impl` block outside of this function `my_func` LL | impl MacroTrait for OutsideStruct {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^----------^^^^^------------- + | | | + | | `OutsideStruct` is not local + | `MacroTrait` is not local ... LL | m!(); | ---- in this macro invocation | - = help: move this `impl` block outside the of the current function `my_func` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: the macro `m` defines the non-local `impl`, and may need to be changed + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue = note: `#[warn(non_local_definitions)]` on by default = note: this warning originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/lint/non-local-defs/macro_rules.stderr b/tests/ui/lint/non-local-defs/macro_rules.stderr index 125d8e97d87f..4e86fc7b987e 100644 --- a/tests/ui/lint/non-local-defs/macro_rules.stderr +++ b/tests/ui/lint/non-local-defs/macro_rules.stderr @@ -1,4 +1,4 @@ -warning: non-local `macro_rules!` definition, they should be avoided as they go against expectation +warning: non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module --> $DIR/macro_rules.rs:10:5 | LL | macro_rules! m0 { () => { } }; @@ -6,11 +6,10 @@ LL | macro_rules! m0 { () => { } }; | = help: remove the `#[macro_export]` or move this `macro_rules!` outside the of the current constant `B` = note: a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue = note: `#[warn(non_local_definitions)]` on by default -warning: non-local `macro_rules!` definition, they should be avoided as they go against expectation +warning: non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module --> $DIR/macro_rules.rs:16:1 | LL | non_local_macro::non_local_macro_rules!(my_macro); @@ -18,12 +17,11 @@ LL | non_local_macro::non_local_macro_rules!(my_macro); | = help: remove the `#[macro_export]` or move this `macro_rules!` outside the of the current constant `_MACRO_EXPORT` = note: a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue = note: the macro `non_local_macro::non_local_macro_rules` may come from an old version of the `non_local_macro` crate, try updating your dependency with `cargo update -p non_local_macro` = note: this warning originates in the macro `non_local_macro::non_local_macro_rules` (in Nightly builds, run with -Z macro-backtrace for more info) -warning: non-local `macro_rules!` definition, they should be avoided as they go against expectation +warning: non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module --> $DIR/macro_rules.rs:21:5 | LL | macro_rules! m { () => { } }; @@ -31,10 +29,9 @@ LL | macro_rules! m { () => { } }; | = help: remove the `#[macro_export]` or move this `macro_rules!` outside the of the current function `main` = note: a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `macro_rules!` definition, they should be avoided as they go against expectation +warning: non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module --> $DIR/macro_rules.rs:29:13 | LL | macro_rules! m2 { () => { } }; @@ -42,7 +39,6 @@ LL | macro_rules! m2 { () => { } }; | = help: remove the `#[macro_export]` or move this `macro_rules!` outside the of the current associated function `bar` and up 2 bodies = note: a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: 4 warnings emitted diff --git a/tests/ui/lint/non-local-defs/suggest-moving-inner.rs b/tests/ui/lint/non-local-defs/suggest-moving-inner.rs new file mode 100644 index 000000000000..61b32e5bad9f --- /dev/null +++ b/tests/ui/lint/non-local-defs/suggest-moving-inner.rs @@ -0,0 +1,17 @@ +//@ check-pass + +trait Trait {} + +fn main() { + mod below { + pub struct Type(T); + } + struct InsideMain; + trait HasFoo {} + + impl Trait for &Vec> + //~^ WARN non-local `impl` definition + where + T: HasFoo + {} +} diff --git a/tests/ui/lint/non-local-defs/suggest-moving-inner.stderr b/tests/ui/lint/non-local-defs/suggest-moving-inner.stderr new file mode 100644 index 000000000000..f0de0f72e74f --- /dev/null +++ b/tests/ui/lint/non-local-defs/suggest-moving-inner.stderr @@ -0,0 +1,29 @@ +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item + --> $DIR/suggest-moving-inner.rs:12:5 + | +LL | impl Trait for &Vec> + | ^^^^^^^^-----^^^^^^^^^^^^^^^^^---------------------------------- + | | | + | | `&'_ Vec>` is not local + | `Trait` is not local + | + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `main` + --> $DIR/suggest-moving-inner.rs:5:1 + | +LL | fn main() { + | ^^^^^^^^^ +LL | mod below { +LL | pub struct Type(T); + | ------------------ may need to be moved as well +LL | } +LL | struct InsideMain; + | ----------------- may need to be moved as well +LL | trait HasFoo {} + | ------------ may need to be moved as well + = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue + = note: `#[warn(non_local_definitions)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.stderr b/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.stderr index 9a8ab810835c..80930ce1bcdf 100644 --- a/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.stderr +++ b/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.stderr @@ -1,12 +1,22 @@ -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/trait-solver-overflow-123573.rs:12:5 | LL | impl Test for &Local {} - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^----^^^^^------ + | | | + | | `&'_ Local` is not local + | | help: remove `&` to make the `impl` local + | `Test` is not local | - = help: move this `impl` block outside the of the current function `main` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` +help: move the `impl` block outside of this function `main` + --> $DIR/trait-solver-overflow-123573.rs:10:1 + | +LL | fn main() { + | ^^^^^^^^^ +LL | struct Local {} + | ------------ may need to be moved as well = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue = note: `#[warn(non_local_definitions)]` on by default diff --git a/tests/ui/lint/non-local-defs/weird-exprs.stderr b/tests/ui/lint/non-local-defs/weird-exprs.stderr index 015a0cce43b2..cd414d636d34 100644 --- a/tests/ui/lint/non-local-defs/weird-exprs.stderr +++ b/tests/ui/lint/non-local-defs/weird-exprs.stderr @@ -1,71 +1,116 @@ -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/weird-exprs.rs:8:5 | -LL | impl Uto for *mut Test {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | type A = [u32; { + | ________________- +LL | | impl Uto for *mut Test {} + | | ^^^^^---^^^^^--------- + | | | | + | | | `*mut Test` is not local + | | `Uto` is not local +LL | | +... | +LL | | }]; + | |_- move the `impl` block outside of this constant expression `` | - = help: move this `impl` block outside the of the current constant expression `` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue = note: `#[warn(non_local_definitions)]` on by default -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/weird-exprs.rs:16:9 | -LL | impl Uto for Test {} - | ^^^^^^^^^^^^^^^^^^^^ +LL | Discr = { + | _____________- +LL | | impl Uto for Test {} + | | ^^^^^---^^^^^---- + | | | | + | | | `Test` is not local + | | `Uto` is not local +LL | | +... | +LL | | } + | |_____- move the `impl` block outside of this constant expression `` | - = help: move this `impl` block outside the of the current constant expression `` - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/weird-exprs.rs:25:9 | -LL | / impl Test { +LL | let _array = [0i32; { + | _________________________- +LL | | impl Test { + | | ^^^^^---- + | | | + | | `Test` is not local LL | | LL | | fn bar() {} -LL | | } - | |_________^ +... | +LL | | 1 +LL | | }]; + | |_____- move the `impl` block outside of this constant expression `` and up 2 bodies | - = help: move this `impl` block outside the of the current constant expression `` and up 2 bodies - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/weird-exprs.rs:34:9 | -LL | impl Uto for &Test {} - | ^^^^^^^^^^^^^^^^^^^^^ +LL | type A = [u32; { + | ____________________- +LL | | impl Uto for &Test {} + | | ^^^^^---^^^^^----- + | | | | + | | | `&'_ Test` is not local + | | `Uto` is not local +LL | | +... | +LL | | }]; + | |_____- move the `impl` block outside of this constant expression `` and up 2 bodies | - = help: move this `impl` block outside the of the current constant expression `` and up 2 bodies - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/weird-exprs.rs:41:9 | -LL | impl Uto for &(Test,) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn a(_: [u32; { + | ___________________- +LL | | impl Uto for &(Test,) {} + | | ^^^^^---^^^^^-------- + | | | | + | | | `&'_ (Test,)` is not local + | | `Uto` is not local +LL | | +... | +LL | | }]) {} + | |_____- move the `impl` block outside of this constant expression `` and up 2 bodies | - = help: move this `impl` block outside the of the current constant expression `` and up 2 bodies - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue -warning: non-local `impl` definition, they should be avoided as they go against expectation +warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item --> $DIR/weird-exprs.rs:48:9 | -LL | impl Uto for &(Test,Test) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn b() -> [u32; { + | _____________________- +LL | | impl Uto for &(Test,Test) {} + | | ^^^^^---^^^^^------------ + | | | | + | | | `&'_ (Test, Test)` is not local + | | `Uto` is not local +LL | | +... | +LL | | }] { todo!() } + | |_____- move the `impl` block outside of this constant expression `` and up 2 bodies | - = help: move this `impl` block outside the of the current constant expression `` and up 2 bodies - = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue warning: 6 warnings emitted diff --git a/tests/ui/lint/issue-89469.rs b/tests/ui/lint/non-snake-case/allow-snake-case-field-destructuring-issue-89469.rs similarity index 100% rename from tests/ui/lint/issue-89469.rs rename to tests/ui/lint/non-snake-case/allow-snake-case-field-destructuring-issue-89469.rs diff --git a/tests/ui/lint/lint-non-snake-case-crate-bin.rs b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate-bin.rs similarity index 100% rename from tests/ui/lint/lint-non-snake-case-crate-bin.rs rename to tests/ui/lint/non-snake-case/lint-non-snake-case-crate-bin.rs diff --git a/tests/ui/lint/lint-non-snake-case-crate-bin2.rs b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate-bin2.rs similarity index 100% rename from tests/ui/lint/lint-non-snake-case-crate-bin2.rs rename to tests/ui/lint/non-snake-case/lint-non-snake-case-crate-bin2.rs diff --git a/tests/ui/lint/lint-non-snake-case-crate-bin3.rs b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate-bin3.rs similarity index 100% rename from tests/ui/lint/lint-non-snake-case-crate-bin3.rs rename to tests/ui/lint/non-snake-case/lint-non-snake-case-crate-bin3.rs diff --git a/tests/ui/lint/lint-non-snake-case-crate-cdylib.rs b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate-cdylib.rs similarity index 100% rename from tests/ui/lint/lint-non-snake-case-crate-cdylib.rs rename to tests/ui/lint/non-snake-case/lint-non-snake-case-crate-cdylib.rs diff --git a/tests/ui/lint/lint-non-snake-case-crate-cdylib.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate-cdylib.stderr similarity index 100% rename from tests/ui/lint/lint-non-snake-case-crate-cdylib.stderr rename to tests/ui/lint/non-snake-case/lint-non-snake-case-crate-cdylib.stderr diff --git a/tests/ui/lint/lint-non-snake-case-crate-dylib.rs b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate-dylib.rs similarity index 100% rename from tests/ui/lint/lint-non-snake-case-crate-dylib.rs rename to tests/ui/lint/non-snake-case/lint-non-snake-case-crate-dylib.rs diff --git a/tests/ui/lint/lint-non-snake-case-crate-dylib.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate-dylib.stderr similarity index 100% rename from tests/ui/lint/lint-non-snake-case-crate-dylib.stderr rename to tests/ui/lint/non-snake-case/lint-non-snake-case-crate-dylib.stderr diff --git a/tests/ui/lint/lint-non-snake-case-crate-lib.rs b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate-lib.rs similarity index 100% rename from tests/ui/lint/lint-non-snake-case-crate-lib.rs rename to tests/ui/lint/non-snake-case/lint-non-snake-case-crate-lib.rs diff --git a/tests/ui/lint/lint-non-snake-case-crate-lib.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate-lib.stderr similarity index 100% rename from tests/ui/lint/lint-non-snake-case-crate-lib.stderr rename to tests/ui/lint/non-snake-case/lint-non-snake-case-crate-lib.stderr diff --git a/tests/ui/lint/lint-non-snake-case-crate-proc-macro.rs b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate-proc-macro.rs similarity index 100% rename from tests/ui/lint/lint-non-snake-case-crate-proc-macro.rs rename to tests/ui/lint/non-snake-case/lint-non-snake-case-crate-proc-macro.rs diff --git a/tests/ui/lint/lint-non-snake-case-crate-proc-macro.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate-proc-macro.stderr similarity index 100% rename from tests/ui/lint/lint-non-snake-case-crate-proc-macro.stderr rename to tests/ui/lint/non-snake-case/lint-non-snake-case-crate-proc-macro.stderr diff --git a/tests/ui/lint/lint-non-snake-case-crate-rlib.rs b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate-rlib.rs similarity index 100% rename from tests/ui/lint/lint-non-snake-case-crate-rlib.rs rename to tests/ui/lint/non-snake-case/lint-non-snake-case-crate-rlib.rs diff --git a/tests/ui/lint/lint-non-snake-case-crate-rlib.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate-rlib.stderr similarity index 100% rename from tests/ui/lint/lint-non-snake-case-crate-rlib.stderr rename to tests/ui/lint/non-snake-case/lint-non-snake-case-crate-rlib.stderr diff --git a/tests/ui/lint/lint-non-snake-case-crate-staticlib.rs b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate-staticlib.rs similarity index 100% rename from tests/ui/lint/lint-non-snake-case-crate-staticlib.rs rename to tests/ui/lint/non-snake-case/lint-non-snake-case-crate-staticlib.rs diff --git a/tests/ui/lint/lint-non-snake-case-crate-staticlib.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-crate-staticlib.stderr similarity index 100% rename from tests/ui/lint/lint-non-snake-case-crate-staticlib.stderr rename to tests/ui/lint/non-snake-case/lint-non-snake-case-crate-staticlib.stderr diff --git a/tests/ui/lint/lint-non-snake-case-functions.rs b/tests/ui/lint/non-snake-case/lint-non-snake-case-functions.rs similarity index 100% rename from tests/ui/lint/lint-non-snake-case-functions.rs rename to tests/ui/lint/non-snake-case/lint-non-snake-case-functions.rs diff --git a/tests/ui/lint/lint-non-snake-case-functions.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-functions.stderr similarity index 100% rename from tests/ui/lint/lint-non-snake-case-functions.stderr rename to tests/ui/lint/non-snake-case/lint-non-snake-case-functions.stderr diff --git a/tests/ui/lint/lint-non-snake-case-identifiers-suggestion-reserved.rs b/tests/ui/lint/non-snake-case/lint-non-snake-case-identifiers-suggestion-reserved.rs similarity index 100% rename from tests/ui/lint/lint-non-snake-case-identifiers-suggestion-reserved.rs rename to tests/ui/lint/non-snake-case/lint-non-snake-case-identifiers-suggestion-reserved.rs diff --git a/tests/ui/lint/lint-non-snake-case-identifiers-suggestion-reserved.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-identifiers-suggestion-reserved.stderr similarity index 100% rename from tests/ui/lint/lint-non-snake-case-identifiers-suggestion-reserved.stderr rename to tests/ui/lint/non-snake-case/lint-non-snake-case-identifiers-suggestion-reserved.stderr diff --git a/tests/ui/lint/lint-non-snake-case-lifetimes.rs b/tests/ui/lint/non-snake-case/lint-non-snake-case-lifetimes.rs similarity index 100% rename from tests/ui/lint/lint-non-snake-case-lifetimes.rs rename to tests/ui/lint/non-snake-case/lint-non-snake-case-lifetimes.rs diff --git a/tests/ui/lint/lint-non-snake-case-lifetimes.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-lifetimes.stderr similarity index 100% rename from tests/ui/lint/lint-non-snake-case-lifetimes.stderr rename to tests/ui/lint/non-snake-case/lint-non-snake-case-lifetimes.stderr diff --git a/tests/ui/lint/lint-non-snake-case-modules.rs b/tests/ui/lint/non-snake-case/lint-non-snake-case-modules.rs similarity index 100% rename from tests/ui/lint/lint-non-snake-case-modules.rs rename to tests/ui/lint/non-snake-case/lint-non-snake-case-modules.rs diff --git a/tests/ui/lint/lint-non-snake-case-modules.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-modules.stderr similarity index 100% rename from tests/ui/lint/lint-non-snake-case-modules.stderr rename to tests/ui/lint/non-snake-case/lint-non-snake-case-modules.stderr diff --git a/tests/ui/lint/lint-non-snake-case-no-lowercase-equivalent.rs b/tests/ui/lint/non-snake-case/lint-non-snake-case-no-lowercase-equivalent.rs similarity index 100% rename from tests/ui/lint/lint-non-snake-case-no-lowercase-equivalent.rs rename to tests/ui/lint/non-snake-case/lint-non-snake-case-no-lowercase-equivalent.rs diff --git a/tests/ui/lint/lint-nonstandard-style-unicode-2.rs b/tests/ui/lint/non-snake-case/lint-nonstandard-style-unicode-2.rs similarity index 100% rename from tests/ui/lint/lint-nonstandard-style-unicode-2.rs rename to tests/ui/lint/non-snake-case/lint-nonstandard-style-unicode-2.rs diff --git a/tests/ui/lint/lint-nonstandard-style-unicode-2.stderr b/tests/ui/lint/non-snake-case/lint-nonstandard-style-unicode-2.stderr similarity index 100% rename from tests/ui/lint/lint-nonstandard-style-unicode-2.stderr rename to tests/ui/lint/non-snake-case/lint-nonstandard-style-unicode-2.stderr diff --git a/tests/ui/lint/lint-uppercase-variables.rs b/tests/ui/lint/non-snake-case/lint-uppercase-variables.rs similarity index 100% rename from tests/ui/lint/lint-uppercase-variables.rs rename to tests/ui/lint/non-snake-case/lint-uppercase-variables.rs diff --git a/tests/ui/lint/lint-uppercase-variables.stderr b/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr similarity index 100% rename from tests/ui/lint/lint-uppercase-variables.stderr rename to tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr diff --git a/tests/ui/lint/issue-66362-no-snake-case-warning-for-field-puns.rs b/tests/ui/lint/non-snake-case/no-snake-case-warning-for-field-puns-issue-66362.rs similarity index 100% rename from tests/ui/lint/issue-66362-no-snake-case-warning-for-field-puns.rs rename to tests/ui/lint/non-snake-case/no-snake-case-warning-for-field-puns-issue-66362.rs diff --git a/tests/ui/lint/issue-66362-no-snake-case-warning-for-field-puns.stderr b/tests/ui/lint/non-snake-case/no-snake-case-warning-for-field-puns-issue-66362.stderr similarity index 80% rename from tests/ui/lint/issue-66362-no-snake-case-warning-for-field-puns.stderr rename to tests/ui/lint/non-snake-case/no-snake-case-warning-for-field-puns-issue-66362.stderr index 09dc3640f99a..cbbcf9909185 100644 --- a/tests/ui/lint/issue-66362-no-snake-case-warning-for-field-puns.stderr +++ b/tests/ui/lint/non-snake-case/no-snake-case-warning-for-field-puns-issue-66362.stderr @@ -1,29 +1,29 @@ error: structure field `lowerCamelCaseName` should have a snake case name - --> $DIR/issue-66362-no-snake-case-warning-for-field-puns.rs:7:9 + --> $DIR/no-snake-case-warning-for-field-puns-issue-66362.rs:7:9 | LL | lowerCamelCaseName: bool, | ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `lower_camel_case_name` | note: the lint level is defined here - --> $DIR/issue-66362-no-snake-case-warning-for-field-puns.rs:1:9 + --> $DIR/no-snake-case-warning-for-field-puns-issue-66362.rs:1:9 | LL | #![deny(non_snake_case)] | ^^^^^^^^^^^^^^ error: variable `lowerCamelCaseBinding` should have a snake case name - --> $DIR/issue-66362-no-snake-case-warning-for-field-puns.rs:20:38 + --> $DIR/no-snake-case-warning-for-field-puns-issue-66362.rs:20:38 | LL | Foo::Good { snake_case_name: lowerCamelCaseBinding } => { } | ^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `lower_camel_case_binding` error: variable `anotherLowerCamelCaseBinding` should have a snake case name - --> $DIR/issue-66362-no-snake-case-warning-for-field-puns.rs:24:41 + --> $DIR/no-snake-case-warning-for-field-puns-issue-66362.rs:24:41 | LL | if let Foo::Good { snake_case_name: anotherLowerCamelCaseBinding } = b { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `another_lower_camel_case_binding` error: variable `yetAnotherLowerCamelCaseBinding` should have a snake case name - --> $DIR/issue-66362-no-snake-case-warning-for-field-puns.rs:27:43 + --> $DIR/no-snake-case-warning-for-field-puns-issue-66362.rs:27:43 | LL | if let Foo::Bad { lowerCamelCaseName: yetAnotherLowerCamelCaseBinding } = b { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `yet_another_lower_camel_case_binding` diff --git a/tests/ui/lint/issue-31924-non-snake-ffi.rs b/tests/ui/lint/non-snake-case/non-snake-ffi-issue-31924.rs similarity index 100% rename from tests/ui/lint/issue-31924-non-snake-ffi.rs rename to tests/ui/lint/non-snake-case/non-snake-ffi-issue-31924.rs diff --git a/tests/ui/lint/suspicious-double-ref-op.rs b/tests/ui/lint/suspicious-double-ref-op.rs index bc8c23c7b895..5aeb81dbd65a 100644 --- a/tests/ui/lint/suspicious-double-ref-op.rs +++ b/tests/ui/lint/suspicious-double-ref-op.rs @@ -1,4 +1,3 @@ -#![feature(lazy_cell)] #![deny(suspicious_double_ref_op, noop_method_call)] use std::borrow::Borrow; diff --git a/tests/ui/lint/suspicious-double-ref-op.stderr b/tests/ui/lint/suspicious-double-ref-op.stderr index f5a71d40fc13..c956843c5078 100644 --- a/tests/ui/lint/suspicious-double-ref-op.stderr +++ b/tests/ui/lint/suspicious-double-ref-op.stderr @@ -1,29 +1,29 @@ error: using `.clone()` on a double reference, which returns `&Vec` instead of cloning the inner type - --> $DIR/suspicious-double-ref-op.rs:15:23 + --> $DIR/suspicious-double-ref-op.rs:14:23 | LL | let z: &Vec<_> = y.clone(); | ^^^^^^^^ | note: the lint level is defined here - --> $DIR/suspicious-double-ref-op.rs:2:9 + --> $DIR/suspicious-double-ref-op.rs:1:9 | LL | #![deny(suspicious_double_ref_op, noop_method_call)] | ^^^^^^^^^^^^^^^^^^^^^^^^ error: using `.clone()` on a double reference, which returns `&CloneType` instead of cloning the inner type - --> $DIR/suspicious-double-ref-op.rs:33:63 + --> $DIR/suspicious-double-ref-op.rs:32:63 | LL | let clone_type_ref_clone: &CloneType = clone_type_ref.clone(); | ^^^^^^^^ error: using `.deref()` on a double reference, which returns `&PlainType` instead of dereferencing the inner type - --> $DIR/suspicious-double-ref-op.rs:37:63 + --> $DIR/suspicious-double-ref-op.rs:36:63 | LL | let non_deref_type_deref: &PlainType = non_deref_type.deref(); | ^^^^^^^^ error: using `.clone()` on a double reference, which returns `&str` instead of cloning the inner type - --> $DIR/suspicious-double-ref-op.rs:41:44 + --> $DIR/suspicious-double-ref-op.rs:40:44 | LL | let _v: Vec<&str> = xs.iter().map(|x| x.clone()).collect(); // could use `*x` instead | ^^^^^^^^ diff --git a/tests/ui/lint-unknown-lints-at-crate-level.rs b/tests/ui/lint/unknown-lints-at-crate-level.rs similarity index 100% rename from tests/ui/lint-unknown-lints-at-crate-level.rs rename to tests/ui/lint/unknown-lints-at-crate-level.rs diff --git a/tests/ui/lint/unnecessary-extern-crate.rs b/tests/ui/lint/unnecessary-extern-crate.rs index 6ca3b96758f1..7f97a4c469ed 100644 --- a/tests/ui/lint/unnecessary-extern-crate.rs +++ b/tests/ui/lint/unnecessary-extern-crate.rs @@ -1,12 +1,12 @@ //@ edition:2018 #![deny(unused_extern_crates)] -#![feature(test, rustc_private)] +#![feature(test)] -extern crate libc; +extern crate core; //~^ ERROR unused extern crate //~| HELP remove -extern crate libc as x; +extern crate core as x; //~^ ERROR unused extern crate //~| HELP remove @@ -28,11 +28,11 @@ mod foo { pub(super) extern crate alloc as d; - extern crate libc; + extern crate core; //~^ ERROR unused extern crate //~| HELP remove - extern crate libc as x; + extern crate core as x; //~^ ERROR unused extern crate //~| HELP remove @@ -41,11 +41,11 @@ mod foo { pub extern crate test as y; mod bar { - extern crate libc; + extern crate core; //~^ ERROR unused extern crate //~| HELP remove - extern crate libc as x; + extern crate core as x; //~^ ERROR unused extern crate //~| HELP remove diff --git a/tests/ui/lint/unnecessary-extern-crate.stderr b/tests/ui/lint/unnecessary-extern-crate.stderr index 14ba9d052f42..1fa4aa9c9a9c 100644 --- a/tests/ui/lint/unnecessary-extern-crate.stderr +++ b/tests/ui/lint/unnecessary-extern-crate.stderr @@ -1,7 +1,7 @@ error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:6:1 | -LL | extern crate libc; +LL | extern crate core; | ^^^^^^^^^^^^^^^^^^ help: remove it | note: the lint level is defined here @@ -13,31 +13,31 @@ LL | #![deny(unused_extern_crates)] error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:9:1 | -LL | extern crate libc as x; +LL | extern crate core as x; | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:31:5 | -LL | extern crate libc; +LL | extern crate core; | ^^^^^^^^^^^^^^^^^^ help: remove it error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:35:5 | -LL | extern crate libc as x; +LL | extern crate core as x; | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:44:9 | -LL | extern crate libc; +LL | extern crate core; | ^^^^^^^^^^^^^^^^^^ help: remove it error: unused extern crate --> $DIR/unnecessary-extern-crate.rs:48:9 | -LL | extern crate libc as x; +LL | extern crate core as x; | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove it error: aborting due to 6 previous errors diff --git a/tests/ui/lint/unreachable_pub.rs b/tests/ui/lint/unreachable_pub.rs index 22c091e112be..f21f6640342c 100644 --- a/tests/ui/lint/unreachable_pub.rs +++ b/tests/ui/lint/unreachable_pub.rs @@ -9,12 +9,16 @@ mod private_mod { pub use std::env::{Args}; // braced-use has different item spans than unbraced //~^ WARNING unreachable_pub + // we lint on struct definition pub struct Hydrogen { //~ WARNING unreachable_pub - // `pub` struct fields, too - pub neutrons: usize, //~ WARNING unreachable_pub - // (... but not more-restricted fields) + // but not on fields, even if they are `pub` as putting `pub(crate)` + // it would clutter the source code for little value + pub neutrons: usize, pub(crate) electrons: usize } + pub(crate) struct Calcium { + pub neutrons: usize, + } impl Hydrogen { // impls, too pub fn count_neutrons(&self) -> usize { self.neutrons } //~ WARNING unreachable_pub diff --git a/tests/ui/lint/unreachable_pub.stderr b/tests/ui/lint/unreachable_pub.stderr index 762834b97b90..705a537a3f1c 100644 --- a/tests/ui/lint/unreachable_pub.stderr +++ b/tests/ui/lint/unreachable_pub.stderr @@ -24,7 +24,7 @@ LL | pub use std::env::{Args}; // braced-use has different item spans than u = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:12:5 + --> $DIR/unreachable_pub.rs:13:5 | LL | pub struct Hydrogen { | ---^^^^^^^^^^^^^^^^ @@ -33,16 +33,8 @@ LL | pub struct Hydrogen { | = help: or consider exporting it for use by other crates -warning: unreachable `pub` field - --> $DIR/unreachable_pub.rs:14:9 - | -LL | pub neutrons: usize, - | ---^^^^^^^^^^^^^^^^ - | | - | help: consider restricting its visibility: `pub(crate)` - warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:20:9 + --> $DIR/unreachable_pub.rs:24:9 | LL | pub fn count_neutrons(&self) -> usize { self.neutrons } | ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -50,7 +42,7 @@ LL | pub fn count_neutrons(&self) -> usize { self.neutrons } | help: consider restricting its visibility: `pub(crate)` warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:29:5 + --> $DIR/unreachable_pub.rs:33:5 | LL | pub enum Helium {} | ---^^^^^^^^^^^^ @@ -60,7 +52,7 @@ LL | pub enum Helium {} = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:30:5 + --> $DIR/unreachable_pub.rs:34:5 | LL | pub union Lithium { c1: usize, c2: u8 } | ---^^^^^^^^^^^^^^ @@ -70,7 +62,7 @@ LL | pub union Lithium { c1: usize, c2: u8 } = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:31:5 + --> $DIR/unreachable_pub.rs:35:5 | LL | pub fn beryllium() {} | ---^^^^^^^^^^^^^^^ @@ -80,7 +72,7 @@ LL | pub fn beryllium() {} = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:32:5 + --> $DIR/unreachable_pub.rs:36:5 | LL | pub trait Boron {} | ---^^^^^^^^^^^^ @@ -90,7 +82,7 @@ LL | pub trait Boron {} = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:33:5 + --> $DIR/unreachable_pub.rs:37:5 | LL | pub const CARBON: usize = 1; | ---^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +92,7 @@ LL | pub const CARBON: usize = 1; = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:34:5 + --> $DIR/unreachable_pub.rs:38:5 | LL | pub static NITROGEN: usize = 2; | ---^^^^^^^^^^^^^^^^^^^^^^^ @@ -110,7 +102,7 @@ LL | pub static NITROGEN: usize = 2; = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:35:5 + --> $DIR/unreachable_pub.rs:39:5 | LL | pub type Oxygen = bool; | ---^^^^^^^^^^^^ @@ -120,7 +112,7 @@ LL | pub type Oxygen = bool; = help: or consider exporting it for use by other crates warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:38:47 + --> $DIR/unreachable_pub.rs:42:47 | LL | ($visibility: vis, $name: ident) => { $visibility struct $name {} } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -135,7 +127,7 @@ LL | define_empty_struct_with_visibility!(pub, Fluorine); = note: this warning originates in the macro `define_empty_struct_with_visibility` (in Nightly builds, run with -Z macro-backtrace for more info) warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:44:9 + --> $DIR/unreachable_pub.rs:48:9 | LL | pub fn catalyze() -> bool; | ---^^^^^^^^^^^^^^^^^^^^^^ @@ -144,5 +136,5 @@ LL | pub fn catalyze() -> bool; | = help: or consider exporting it for use by other crates -warning: 14 warnings emitted +warning: 13 warnings emitted diff --git a/tests/ui/lint/unused/import_remove_line.stderr b/tests/ui/lint/unused/import_remove_line.stderr index 0e8c5de3558e..b2a16669a939 100644 --- a/tests/ui/lint/unused/import_remove_line.stderr +++ b/tests/ui/lint/unused/import_remove_line.stderr @@ -1,4 +1,4 @@ -warning: unused imports: `Duration`, `Instant` +warning: unused imports: `Duration` and `Instant` --> $DIR/import_remove_line.rs:7:17 | LL | use std::time::{Duration, Instant}; diff --git a/tests/ui/lint/unused/lint-unused-imports.rs b/tests/ui/lint/unused/lint-unused-imports.rs index 88f2baa5da9b..710fb7a5ed12 100644 --- a/tests/ui/lint/unused/lint-unused-imports.rs +++ b/tests/ui/lint/unused/lint-unused-imports.rs @@ -10,7 +10,7 @@ use std::fmt::{}; // Should get errors for both 'Some' and 'None' use std::option::Option::{Some, None}; -//~^ ERROR unused imports: `None`, `Some` +//~^ ERROR unused imports: `None` and `Some` use test::A; //~ ERROR unused import: `test::A` // Be sure that if we just bring some methods into scope that they're also diff --git a/tests/ui/lint/unused/lint-unused-imports.stderr b/tests/ui/lint/unused/lint-unused-imports.stderr index 07684a84a64f..a848fb31ebab 100644 --- a/tests/ui/lint/unused/lint-unused-imports.stderr +++ b/tests/ui/lint/unused/lint-unused-imports.stderr @@ -10,7 +10,7 @@ note: the lint level is defined here LL | #![deny(unused_imports)] | ^^^^^^^^^^^^^^ -error: unused imports: `None`, `Some` +error: unused imports: `None` and `Some` --> $DIR/lint-unused-imports.rs:12:27 | LL | use std::option::Option::{Some, None}; diff --git a/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs b/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs index 0f9eac93930b..ba32fb566e83 100644 --- a/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs +++ b/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs @@ -20,10 +20,10 @@ fn doc_comment_between_if_else(num: u8) -> bool { } fn doc_comment_on_expr(num: u8) -> bool { - (/// useless doc comment + /// useless doc comment //~^ ERROR: attributes on expressions are experimental //~| ERROR: unused doc comment - num) == 3 + num == 3 } fn doc_comment_on_expr_field() -> bool { diff --git a/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr b/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr index 1ff5c8d88253..c07411745549 100644 --- a/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr +++ b/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr @@ -5,10 +5,10 @@ LL | else { | ^^^^ expected expression error[E0658]: attributes on expressions are experimental - --> $DIR/unused-doc-comments-edge-cases.rs:23:6 + --> $DIR/unused-doc-comments-edge-cases.rs:23:5 | -LL | (/// useless doc comment - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | /// useless doc comment + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #15701 for more information = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable @@ -32,12 +32,12 @@ LL | #![deny(unused_doc_comments)] | ^^^^^^^^^^^^^^^^^^^ error: unused doc comment - --> $DIR/unused-doc-comments-edge-cases.rs:23:6 + --> $DIR/unused-doc-comments-edge-cases.rs:23:5 | -LL | (/// useless doc comment - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | /// useless doc comment + | ^^^^^^^^^^^^^^^^^^^^^^^ ... -LL | num) == 3 +LL | num == 3 | --- rustdoc does not generate documentation for expressions | = help: use `//` for a plain comment diff --git a/tests/ui/lint/use_suggestion_json.stderr b/tests/ui/lint/use_suggestion_json.stderr index acc36550642d..4683e5dd8f3c 100644 --- a/tests/ui/lint/use_suggestion_json.stderr +++ b/tests/ui/lint/use_suggestion_json.stderr @@ -95,7 +95,7 @@ mod foo { ], "children": [ { - "message": "consider importing one of these items", + "message": "consider importing one of these structs", "code": null, "level": "help", "spans": [ @@ -386,7 +386,7 @@ mod foo { \u001b[0m\u001b[1m\u001b[38;5;12mLL\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m let x: Iter;\u001b[0m \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mnot found in this scope\u001b[0m \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m -\u001b[0m\u001b[1m\u001b[38;5;14mhelp\u001b[0m\u001b[0m: consider importing one of these items\u001b[0m +\u001b[0m\u001b[1m\u001b[38;5;14mhelp\u001b[0m\u001b[0m: consider importing one of these structs\u001b[0m \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m \u001b[0m\u001b[1m\u001b[38;5;12mLL\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[38;5;10m+ use std::collections::binary_heap::Iter;\u001b[0m \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m diff --git a/tests/ui/loops/loop-if-else-break-issue-123261.fixed b/tests/ui/loops/loop-if-else-break-issue-123261.fixed new file mode 100644 index 000000000000..f9e88c18ad02 --- /dev/null +++ b/tests/ui/loops/loop-if-else-break-issue-123261.fixed @@ -0,0 +1,31 @@ +//@ run-rustfix + +#![allow(unused)] + +fn main() { + let n = 1; + let m = 2; + let x = 'block: { + if n == 0 { + break 'block 1; //~ ERROR [E0268] + } else { + break 'block 2; + } + }; + + let y = 'block: { + if n == 0 { + break 'block 1; //~ ERROR [E0268] + } + break 'block 0; + }; + + let z = 'block: { + if n == 0 { + if m > 1 { + break 'block 3; //~ ERROR [E0268] + } + } + break 'block 1; + }; +} diff --git a/tests/ui/loops/loop-if-else-break-issue-123261.rs b/tests/ui/loops/loop-if-else-break-issue-123261.rs new file mode 100644 index 000000000000..a1f9dabe891f --- /dev/null +++ b/tests/ui/loops/loop-if-else-break-issue-123261.rs @@ -0,0 +1,31 @@ +//@ run-rustfix + +#![allow(unused)] + +fn main() { + let n = 1; + let m = 2; + let x = { + if n == 0 { + break 1; //~ ERROR [E0268] + } else { + break 2; + } + }; + + let y = { + if n == 0 { + break 1; //~ ERROR [E0268] + } + break 0; + }; + + let z = { + if n == 0 { + if m > 1 { + break 3; //~ ERROR [E0268] + } + } + break 1; + }; +} diff --git a/tests/ui/loops/loop-if-else-break-issue-123261.stderr b/tests/ui/loops/loop-if-else-break-issue-123261.stderr new file mode 100644 index 000000000000..7bd192fc00b0 --- /dev/null +++ b/tests/ui/loops/loop-if-else-break-issue-123261.stderr @@ -0,0 +1,59 @@ +error[E0268]: `break` outside of a loop or labeled block + --> $DIR/loop-if-else-break-issue-123261.rs:10:13 + | +LL | break 1; + | ^^^^^^^ cannot `break` outside of a loop or labeled block +LL | } else { +LL | break 2; + | ^^^^^^^ cannot `break` outside of a loop or labeled block + | +help: consider labeling this block to be able to break within it + | +LL ~ let x = 'block: { +LL | if n == 0 { +LL ~ break 'block 1; +LL | } else { +LL ~ break 'block 2; + | + +error[E0268]: `break` outside of a loop or labeled block + --> $DIR/loop-if-else-break-issue-123261.rs:18:13 + | +LL | break 1; + | ^^^^^^^ cannot `break` outside of a loop or labeled block +LL | } +LL | break 0; + | ^^^^^^^ cannot `break` outside of a loop or labeled block + | +help: consider labeling this block to be able to break within it + | +LL ~ let y = 'block: { +LL | if n == 0 { +LL ~ break 'block 1; +LL | } +LL ~ break 'block 0; + | + +error[E0268]: `break` outside of a loop or labeled block + --> $DIR/loop-if-else-break-issue-123261.rs:26:17 + | +LL | break 3; + | ^^^^^^^ cannot `break` outside of a loop or labeled block +... +LL | break 1; + | ^^^^^^^ cannot `break` outside of a loop or labeled block + | +help: consider labeling this block to be able to break within it + | +LL ~ let z = 'block: { +LL | if n == 0 { +LL | if m > 1 { +LL ~ break 'block 3; +LL | } +LL | } +LL ~ break 'block 1; + | + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0268`. diff --git a/tests/ui/compile_error_macro.rs b/tests/ui/macros/compile_error_macro.rs similarity index 100% rename from tests/ui/compile_error_macro.rs rename to tests/ui/macros/compile_error_macro.rs diff --git a/tests/ui/compile_error_macro.stderr b/tests/ui/macros/compile_error_macro.stderr similarity index 100% rename from tests/ui/compile_error_macro.stderr rename to tests/ui/macros/compile_error_macro.stderr diff --git a/tests/ui/macros/expand-full-no-resolution.stderr b/tests/ui/macros/expand-full-no-resolution.stderr index 2537a5032a92..df6f20332bfd 100644 --- a/tests/ui/macros/expand-full-no-resolution.stderr +++ b/tests/ui/macros/expand-full-no-resolution.stderr @@ -7,7 +7,7 @@ LL | macro_rules! _a { LL | format_args!(a!()); | ^ | -help: a macro with a similar name exists, consider renaming `_a` into `a` +help: the leading underscore in `_a` marks it as unused, consider renaming it to `a` | LL | macro_rules! a { | ~ @@ -21,7 +21,7 @@ LL | macro_rules! _a { LL | env!(a!()); | ^ | -help: a macro with a similar name exists, consider renaming `_a` into `a` +help: the leading underscore in `_a` marks it as unused, consider renaming it to `a` | LL | macro_rules! a { | ~ diff --git a/tests/ui/macros/issue-61053-missing-repetition.rs b/tests/ui/macros/issue-61053-missing-repetition.rs index 6b36c730b825..77c94f24171a 100644 --- a/tests/ui/macros/issue-61053-missing-repetition.rs +++ b/tests/ui/macros/issue-61053-missing-repetition.rs @@ -3,7 +3,7 @@ macro_rules! foo { () => {}; ($( $i:ident = $($j:ident),+ );*) => { $( $i = $j; )* }; - //~^ ERROR variable 'j' is still repeating + //~^ ERROR variable `j` is still repeating } macro_rules! bar { @@ -12,12 +12,12 @@ macro_rules! bar { macro_rules! nested { () => {}; ($( $i:ident = $($j:ident),+ );*) => { $( $i = $j; )* }; - //~^ ERROR variable 'j' is still repeating + //~^ ERROR variable `j` is still repeating } }; ( $( $i:ident = $($j:ident),+ );* ) => { $(macro_rules! $i { - () => { $j }; //~ ERROR variable 'j' is still repeating + () => { $j }; //~ ERROR variable `j` is still repeating })* }; } diff --git a/tests/ui/macros/issue-61053-missing-repetition.stderr b/tests/ui/macros/issue-61053-missing-repetition.stderr index 738f711f04e1..ba254401f17d 100644 --- a/tests/ui/macros/issue-61053-missing-repetition.stderr +++ b/tests/ui/macros/issue-61053-missing-repetition.stderr @@ -1,4 +1,4 @@ -error: variable 'j' is still repeating at this depth +error: variable `j` is still repeating at this depth --> $DIR/issue-61053-missing-repetition.rs:5:52 | LL | ($( $i:ident = $($j:ident),+ );*) => { $( $i = $j; )* }; @@ -12,7 +12,7 @@ note: the lint level is defined here LL | #![deny(meta_variable_misuse)] | ^^^^^^^^^^^^^^^^^^^^ -error: variable 'j' is still repeating at this depth +error: variable `j` is still repeating at this depth --> $DIR/issue-61053-missing-repetition.rs:14:60 | LL | ($( $i:ident = $($j:ident),+ );*) => { $( $i = $j; )* }; @@ -20,7 +20,7 @@ LL | ($( $i:ident = $($j:ident),+ );*) => { $( $i = $j; )* }; | | | expected repetition -error: variable 'j' is still repeating at this depth +error: variable `j` is still repeating at this depth --> $DIR/issue-61053-missing-repetition.rs:20:21 | LL | ( $( $i:ident = $($j:ident),+ );* ) => { diff --git a/tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs b/tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs new file mode 100644 index 000000000000..e44eeffb01be --- /dev/null +++ b/tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs @@ -0,0 +1,58 @@ +//@ run-pass + +#![allow(dead_code, non_camel_case_types, non_upper_case_globals)] +#![feature(macro_metavar_expr_concat)] + +macro_rules! create_things { + ($lhs:ident) => { + struct ${concat($lhs, _separated_idents_in_a_struct)} { + foo: i32, + ${concat($lhs, _separated_idents_in_a_field)}: i32, + } + + mod ${concat($lhs, _separated_idents_in_a_module)} { + pub const FOO: () = (); + } + + fn ${concat($lhs, _separated_idents_in_a_fn)}() {} + }; +} + +macro_rules! many_idents { + ($a:ident, $c:ident) => { + const ${concat($a, B, $c, D)}: i32 = 1; + }; +} + +macro_rules! valid_tts { + ($_0:tt, $_1:tt) => { + const ${concat($_0, $_1)}: i32 = 1; + } +} + +macro_rules! without_dollar_sign_is_an_ident { + ($ident:ident) => { + const ${concat(VAR, ident)}: i32 = 1; + const ${concat(VAR, $ident)}: i32 = 2; + }; +} + +fn main() { + create_things!(behold); + behold_separated_idents_in_a_fn(); + let _ = behold_separated_idents_in_a_module::FOO; + let _ = behold_separated_idents_in_a_struct { + foo: 1, + behold_separated_idents_in_a_field: 2, + }; + + many_idents!(A, C); + assert_eq!(ABCD, 1); + + valid_tts!(X, YZ); + assert_eq!(XYZ, 1); + + without_dollar_sign_is_an_ident!(_123); + assert_eq!(VARident, 1); + assert_eq!(VAR_123, 2); +} diff --git a/tests/ui/macros/macro-metavar-expr-concat/hygiene.rs b/tests/ui/macros/macro-metavar-expr-concat/hygiene.rs new file mode 100644 index 000000000000..24b0e36498a3 --- /dev/null +++ b/tests/ui/macros/macro-metavar-expr-concat/hygiene.rs @@ -0,0 +1,13 @@ +#![feature(macro_metavar_expr_concat)] + +macro_rules! join { + ($lhs:ident, $rhs:ident) => { + ${concat($lhs, $rhs)} + //~^ ERROR cannot find value `abcdef` in this scope + }; +} + +fn main() { + let abcdef = 1; + let _another = join!(abc, def); +} diff --git a/tests/ui/macros/macro-metavar-expr-concat/hygiene.stderr b/tests/ui/macros/macro-metavar-expr-concat/hygiene.stderr new file mode 100644 index 000000000000..ef2326dce857 --- /dev/null +++ b/tests/ui/macros/macro-metavar-expr-concat/hygiene.stderr @@ -0,0 +1,14 @@ +error[E0425]: cannot find value `abcdef` in this scope + --> $DIR/hygiene.rs:5:10 + | +LL | ${concat($lhs, $rhs)} + | ^^^^^^^^^^^^^^^^^^^^ not found in this scope +... +LL | let _another = join!(abc, def); + | --------------- in this macro invocation + | + = note: this error originates in the macro `join` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.rs b/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.rs new file mode 100644 index 000000000000..f72b9baca89f --- /dev/null +++ b/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.rs @@ -0,0 +1,81 @@ +#![feature(macro_metavar_expr_concat)] + +macro_rules! idents_01 { + ($rhs:ident) => { + let ${concat(abc, $rhs)}: () = (); + //~^ ERROR `${concat(..)}` currently does not support raw identifiers + }; +} + +macro_rules! idents_10 { + ($lhs:ident) => { + let ${concat($lhs, abc)}: () = (); + //~^ ERROR `${concat(..)}` currently does not support raw identifiers + }; +} + +macro_rules! idents_11 { + ($lhs:ident, $rhs:ident) => { + let ${concat($lhs, $rhs)}: () = (); + //~^ ERROR `${concat(..)}` currently does not support raw identifiers + //~| ERROR `${concat(..)}` currently does not support raw identifiers + //~| ERROR `${concat(..)}` currently does not support raw identifiers + }; +} + +macro_rules! no_params { + () => { + let ${concat(r#abc, abc)}: () = (); + //~^ ERROR `${concat(..)}` currently does not support raw identifiers + //~| ERROR expected pattern, found `$` + + let ${concat(abc, r#abc)}: () = (); + //~^ ERROR `${concat(..)}` currently does not support raw identifiers + + let ${concat(r#abc, r#abc)}: () = (); + //~^ ERROR `${concat(..)}` currently does not support raw identifiers + }; +} + +macro_rules! tts_01 { + ($rhs:tt) => { + let ${concat(abc, $rhs)}: () = (); + //~^ ERROR `${concat(..)}` currently does not support raw identifiers + }; +} + +macro_rules! tts_10 { + ($lhs:tt) => { + let ${concat($lhs, abc)}: () = (); + //~^ ERROR `${concat(..)}` currently does not support raw identifiers + }; +} + +macro_rules! tts_11 { + ($lhs:tt, $rhs:tt) => { + let ${concat($lhs, $rhs)}: () = (); + //~^ ERROR `${concat(..)}` currently does not support raw identifiers + //~| ERROR `${concat(..)}` currently does not support raw identifiers + //~| ERROR `${concat(..)}` currently does not support raw identifiers + }; +} + +fn main() { + idents_01!(r#_c); + + idents_10!(r#_c); + + idents_11!(r#_c, d); + idents_11!(_e, r#f); + idents_11!(r#_g, r#h); + + tts_01!(r#_c); + + tts_10!(r#_c); + + tts_11!(r#_c, d); + tts_11!(_e, r#f); + tts_11!(r#_g, r#h); + + no_params!(); +} diff --git a/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.stderr b/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.stderr new file mode 100644 index 000000000000..dd525cf0801b --- /dev/null +++ b/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.stderr @@ -0,0 +1,95 @@ +error: `${concat(..)}` currently does not support raw identifiers + --> $DIR/raw-identifiers.rs:28:22 + | +LL | let ${concat(r#abc, abc)}: () = (); + | ^^^^^ + +error: `${concat(..)}` currently does not support raw identifiers + --> $DIR/raw-identifiers.rs:32:27 + | +LL | let ${concat(abc, r#abc)}: () = (); + | ^^^^^ + +error: `${concat(..)}` currently does not support raw identifiers + --> $DIR/raw-identifiers.rs:35:22 + | +LL | let ${concat(r#abc, r#abc)}: () = (); + | ^^^^^ + +error: `${concat(..)}` currently does not support raw identifiers + --> $DIR/raw-identifiers.rs:5:28 + | +LL | let ${concat(abc, $rhs)}: () = (); + | ^^^ + +error: `${concat(..)}` currently does not support raw identifiers + --> $DIR/raw-identifiers.rs:12:23 + | +LL | let ${concat($lhs, abc)}: () = (); + | ^^^ + +error: `${concat(..)}` currently does not support raw identifiers + --> $DIR/raw-identifiers.rs:19:23 + | +LL | let ${concat($lhs, $rhs)}: () = (); + | ^^^ + +error: `${concat(..)}` currently does not support raw identifiers + --> $DIR/raw-identifiers.rs:19:29 + | +LL | let ${concat($lhs, $rhs)}: () = (); + | ^^^ + +error: `${concat(..)}` currently does not support raw identifiers + --> $DIR/raw-identifiers.rs:19:23 + | +LL | let ${concat($lhs, $rhs)}: () = (); + | ^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `${concat(..)}` currently does not support raw identifiers + --> $DIR/raw-identifiers.rs:42:28 + | +LL | let ${concat(abc, $rhs)}: () = (); + | ^^^ + +error: `${concat(..)}` currently does not support raw identifiers + --> $DIR/raw-identifiers.rs:49:23 + | +LL | let ${concat($lhs, abc)}: () = (); + | ^^^ + +error: `${concat(..)}` currently does not support raw identifiers + --> $DIR/raw-identifiers.rs:56:23 + | +LL | let ${concat($lhs, $rhs)}: () = (); + | ^^^ + +error: `${concat(..)}` currently does not support raw identifiers + --> $DIR/raw-identifiers.rs:56:29 + | +LL | let ${concat($lhs, $rhs)}: () = (); + | ^^^ + +error: `${concat(..)}` currently does not support raw identifiers + --> $DIR/raw-identifiers.rs:56:23 + | +LL | let ${concat($lhs, $rhs)}: () = (); + | ^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: expected pattern, found `$` + --> $DIR/raw-identifiers.rs:28:13 + | +LL | let ${concat(r#abc, abc)}: () = (); + | ^ expected pattern +... +LL | no_params!(); + | ------------ in this macro invocation + | + = note: this error originates in the macro `no_params` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 14 previous errors + diff --git a/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs b/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs new file mode 100644 index 000000000000..bf47442ea76f --- /dev/null +++ b/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs @@ -0,0 +1,50 @@ +#![feature(macro_metavar_expr_concat)] + +macro_rules! wrong_concat_declarations { + ($ex:expr) => { + ${concat()} + //~^ ERROR expected identifier + + ${concat(aaaa)} + //~^ ERROR `concat` must have at least two elements + + ${concat(aaaa,)} + //~^ ERROR expected identifier + + ${concat(aaaa, 1)} + //~^ ERROR expected identifier + + ${concat(_, aaaa)} + + ${concat(aaaa aaaa)} + //~^ ERROR expected comma + + ${concat($ex)} + //~^ ERROR `concat` must have at least two elements + + ${concat($ex, aaaa)} + //~^ ERROR `${concat(..)}` currently only accepts identifiers + + ${concat($ex, aaaa 123)} + //~^ ERROR expected comma + + ${concat($ex, aaaa,)} + //~^ ERROR expected identifier + + ${concat($ex, aaaa, 123)} + //~^ ERROR expected identifier + }; +} + +macro_rules! dollar_sign_without_referenced_ident { + ($ident:ident) => { + const ${concat(FOO, $foo)}: i32 = 2; + //~^ ERROR variable `foo` is not recognized in meta-variable expression + }; +} + +fn main() { + wrong_concat_declarations!(1); + + dollar_sign_without_referenced_ident!(VAR); +} diff --git a/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.stderr b/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.stderr new file mode 100644 index 000000000000..b216a86d59ab --- /dev/null +++ b/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.stderr @@ -0,0 +1,68 @@ +error: expected identifier + --> $DIR/syntax-errors.rs:5:10 + | +LL | ${concat()} + | ^^^^^^^^^^ + +error: `concat` must have at least two elements + --> $DIR/syntax-errors.rs:8:11 + | +LL | ${concat(aaaa)} + | ^^^^^^ + +error: expected identifier + --> $DIR/syntax-errors.rs:11:10 + | +LL | ${concat(aaaa,)} + | ^^^^^^^^^^^^^^^ + +error: expected identifier, found `1` + --> $DIR/syntax-errors.rs:14:24 + | +LL | ${concat(aaaa, 1)} + | ^ help: try removing `1` + +error: expected comma + --> $DIR/syntax-errors.rs:19:10 + | +LL | ${concat(aaaa aaaa)} + | ^^^^^^^^^^^^^^^^^^^ + +error: `concat` must have at least two elements + --> $DIR/syntax-errors.rs:22:11 + | +LL | ${concat($ex)} + | ^^^^^^ + +error: expected comma + --> $DIR/syntax-errors.rs:28:10 + | +LL | ${concat($ex, aaaa 123)} + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected identifier + --> $DIR/syntax-errors.rs:31:10 + | +LL | ${concat($ex, aaaa,)} + | ^^^^^^^^^^^^^^^^^^^^ + +error: expected identifier, found `123` + --> $DIR/syntax-errors.rs:34:29 + | +LL | ${concat($ex, aaaa, 123)} + | ^^^ help: try removing `123` + +error: `${concat(..)}` currently only accepts identifiers or meta-variables as parameters + --> $DIR/syntax-errors.rs:25:19 + | +LL | ${concat($ex, aaaa)} + | ^^ + +error: variable `foo` is not recognized in meta-variable expression + --> $DIR/syntax-errors.rs:41:30 + | +LL | const ${concat(FOO, $foo)}: i32 = 2; + | ^^^ + +error: aborting due to 11 previous errors + diff --git a/tests/ui/module-macro_use-arguments.rs b/tests/ui/macros/module-macro_use-arguments.rs similarity index 100% rename from tests/ui/module-macro_use-arguments.rs rename to tests/ui/macros/module-macro_use-arguments.rs diff --git a/tests/ui/module-macro_use-arguments.stderr b/tests/ui/macros/module-macro_use-arguments.stderr similarity index 100% rename from tests/ui/module-macro_use-arguments.stderr rename to tests/ui/macros/module-macro_use-arguments.stderr diff --git a/tests/ui/no-patterns-in-args-macro.rs b/tests/ui/macros/no-patterns-in-args-macro.rs similarity index 100% rename from tests/ui/no-patterns-in-args-macro.rs rename to tests/ui/macros/no-patterns-in-args-macro.rs diff --git a/tests/ui/no-patterns-in-args-macro.stderr b/tests/ui/macros/no-patterns-in-args-macro.stderr similarity index 100% rename from tests/ui/no-patterns-in-args-macro.stderr rename to tests/ui/macros/no-patterns-in-args-macro.stderr diff --git a/tests/ui/macros/proc_macro.rs b/tests/ui/macros/proc_macro.rs index ce2b041c26eb..8fea4ca282cf 100644 --- a/tests/ui/macros/proc_macro.rs +++ b/tests/ui/macros/proc_macro.rs @@ -1,6 +1,5 @@ //@ run-pass //@ aux-build:proc_macro_def.rs -//@ ignore-cross-compile extern crate proc_macro_def; diff --git a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs index 9fec546ecd65..1eda5f5bb6bc 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs +++ b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs @@ -35,7 +35,7 @@ macro_rules! no_curly__no_rhs_dollar__no_round { #[rustfmt::skip] // autoformatters can break a few of the error traces macro_rules! no_curly__rhs_dollar__round { ( $( $i:ident ),* ) => { count($i) }; - //~^ ERROR variable 'i' is still repeating at this depth + //~^ ERROR variable `i` is still repeating at this depth } #[rustfmt::skip] // autoformatters can break a few of the error traces diff --git a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr index e9cef5ec6cc9..8e4ba192d79f 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr +++ b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr @@ -191,10 +191,10 @@ LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } }; | ^^^^^^^^^^^^^^ help: supported expressions are count, ignore, index and len error: expected identifier - --> $DIR/syntax-errors.rs:118:31 + --> $DIR/syntax-errors.rs:118:33 | LL | ( $( $i:ident ),* ) => { ${ {} } }; - | ^^^^^^ + | ^^ error: `count` can not be placed inside the inner-most repetition --> $DIR/syntax-errors.rs:12:24 @@ -208,7 +208,7 @@ error: `count` can not be placed inside the inner-most repetition LL | ( $i:ident ) => { ${ count($i) } }; | ^^^^^^^^^^^^^ -error: variable 'i' is still repeating at this depth +error: variable `i` is still repeating at this depth --> $DIR/syntax-errors.rs:37:36 | LL | ( $( $i:ident ),* ) => { count($i) }; diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index 6b215ba525de..f06c1f99069a 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -37,25 +37,22 @@ macro_rules! vis { ($vis:vis) => { stringify!($vis) }; } // the same result (which is preferable.) macro_rules! c1 { ($frag:ident, [$($tt:tt)*], $s:literal) => { + // Prior to #125174: + // - the first of these two lines created a `TokenKind::Interpolated` + // that was printed by the AST pretty printer; + // - the second of these two lines created a token stream that was + // printed by the TokenStream pretty printer. + // + // Now they are both printed by the TokenStream pretty printer. But it + // doesn't hurt to keep both assertions to ensure this remains true. + // + // (This also explains the name `c1`. There used to be a `c2` macro for + // cases where the two pretty printers produced different output.) assert_eq!($frag!($($tt)*), $s); assert_eq!(stringify!($($tt)*), $s); }; } -// Use this when AST pretty-printing and TokenStream pretty-printing give -// different results. -// -// `c1` and `c2` could be in a single macro, but having them separate makes it -// easy to find the cases where the two pretty-printing approaches give -// different results. -macro_rules! c2 { - ($frag:ident, [$($tt:tt)*], $s1:literal, $s2:literal $(,)?) => { - assert_ne!($s1, $s2, "should use `c1!` instead"); - assert_eq!($frag!($($tt)*), $s1); - assert_eq!(stringify!($($tt)*), $s2); - }; -} - #[test] fn test_block() { c1!(block, [ {} ], "{}"); @@ -76,7 +73,7 @@ fn test_expr() { // ExprKind::Array c1!(expr, [ [] ], "[]"); c1!(expr, [ [true] ], "[true]"); - c2!(expr, [ [true,] ], "[true]", "[true,]"); + c1!(expr, [ [true,] ], "[true,]"); c1!(expr, [ [true, true] ], "[true, true]"); // ExprKind::ConstBlock @@ -85,11 +82,11 @@ fn test_expr() { // ExprKind::Call c1!(expr, [ f() ], "f()"); c1!(expr, [ f::() ], "f::()"); - c2!(expr, [ f :: < u8>( ) ], "f::()", "f :: < u8>()"); + c1!(expr, [ f :: < u8>( ) ], "f :: < u8>()"); c1!(expr, [ f::<1>() ], "f::<1>()"); c1!(expr, [ f::<'a, u8, 1>() ], "f::<'a, u8, 1>()"); c1!(expr, [ f(true) ], "f(true)"); - c2!(expr, [ f(true,) ], "f(true)", "f(true,)"); + c1!(expr, [ f(true,) ], "f(true,)"); c1!(expr, [ ()() ], "()()"); // ExprKind::MethodCall @@ -101,7 +98,7 @@ fn test_expr() { c1!(expr, [ () ], "()"); c1!(expr, [ (true,) ], "(true,)"); c1!(expr, [ (true, false) ], "(true, false)"); - c2!(expr, [ (true, false,) ], "(true, false)", "(true, false,)"); + c1!(expr, [ (true, false,) ], "(true, false,)"); // ExprKind::Binary c1!(expr, [ true || false ], "true || false"); @@ -131,16 +128,6 @@ fn test_expr() { c1!(expr, [ if let Some(a) = b { c } else { d } ], "if let Some(a) = b { c } else { d }"); c1!(expr, [ if let _ = true && false {} ], "if let _ = true && false {}"); c1!(expr, [ if let _ = (true && false) {} ], "if let _ = (true && false) {}"); - macro_rules! c2_if_let { - ($expr:expr, $expr_expected:expr, $tokens_expected:expr $(,)?) => { - c2!(expr, [ if let _ = $expr {} ], $expr_expected, $tokens_expected); - }; - } - c2_if_let!( - true && false, - "if let _ = (true && false) {}", - "if let _ = true && false {}", - ); c1!(expr, [ match () { _ if let _ = Struct {} => {} } ], "match () { _ if let _ = Struct {} => {} }" @@ -203,31 +190,6 @@ fn test_expr() { } ], "match self { Ok => 1, Err => 0, }" ); - macro_rules! c2_match_arm { - ([ $expr:expr ], $expr_expected:expr, $tokens_expected:expr $(,)?) => { - c2!(expr, [ match () { _ => $expr } ], $expr_expected, $tokens_expected); - }; - } - c2_match_arm!( - [ { 1 } - 1 ], - "match () { _ => ({ 1 }) - 1, }", - "match () { _ => { 1 } - 1 }", - ); - c2_match_arm!( - [ m!() - 1 ], - "match () { _ => m!() - 1, }", - "match () { _ => m!() - 1 }", - ); - c2_match_arm!( - [ m![] - 1 ], - "match () { _ => m![] - 1, }", - "match () { _ => m![] - 1 }", - ); - c2_match_arm!( - [ m! {} - 1 ], - "match () { _ => m! {} - 1, }", - "match () { _ => m! {} - 1 }", - ); // ExprKind::Closure c1!(expr, [ || {} ], "|| {}"); @@ -242,22 +204,19 @@ fn test_expr() { c1!(expr, [ static async || self ], "static async || self"); c1!(expr, [ static async move || self ], "static async move || self"); c1!(expr, [ || -> u8 { self } ], "|| -> u8 { self }"); - c2!(expr, [ 1 + || {} ], "1 + (|| {})", "1 + || {}"); // AST?? + c1!(expr, [ 1 + || {} ], "1 + || {}"); // ExprKind::Block c1!(expr, [ {} ], "{}"); c1!(expr, [ unsafe {} ], "unsafe {}"); c1!(expr, [ 'a: {} ], "'a: {}"); c1!(expr, [ #[attr] {} ], "#[attr] {}"); - c2!(expr, + c1!(expr, [ { #![attr] } ], - "{\n\ - \x20 #![attr]\n\ - }", "{ #![attr] }" ); @@ -289,7 +248,7 @@ fn test_expr() { c1!(expr, [ ..hi ], "..hi"); c1!(expr, [ lo.. ], "lo.."); c1!(expr, [ lo..hi ], "lo..hi"); - c2!(expr, [ lo .. hi ], "lo..hi", "lo .. hi"); + c1!(expr, [ lo .. hi ], "lo .. hi"); c1!(expr, [ ..=hi ], "..=hi"); c1!(expr, [ lo..=hi ], "lo..=hi"); c1!(expr, [ -2..=-1 ], "-2..=-1"); @@ -382,11 +341,7 @@ fn test_item() { c1!(item, [ pub extern crate self as std; ], "pub extern crate self as std;"); // ItemKind::Use - c2!(item, - [ pub use crate::{a, b::c}; ], - "pub use crate::{a, b::c};", - "pub use crate::{ a, b::c };" // FIXME - ); + c1!(item, [ pub use crate::{a, b::c}; ], "pub use crate::{ a, b::c };"); // FIXME c1!(item, [ pub use A::*; ], "pub use A::*;"); // ItemKind::Static @@ -418,24 +373,19 @@ fn test_item() { // ItemKind::ForeignMod c1!(item, [ extern "C" {} ], "extern \"C\" {}"); - c2!(item, - [ pub extern "C" {} ], - "extern \"C\" {}", // ?? - "pub extern \"C\" {}" - ); + c1!(item, [ pub extern "C" {} ], "pub extern \"C\" {}"); c1!(item, [ unsafe extern "C++" {} ], "unsafe extern \"C++\" {}"); // ItemKind::GlobalAsm: untestable because this test works pre-expansion. // ItemKind::TyAlias - c2!(item, + c1!(item, [ pub default type Type<'a>: Bound where Self: 'a, = T; ], - "pub default type Type<'a>: Bound where Self: 'a = T;", "pub default type Type<'a>: Bound where Self: 'a, = T;" ); @@ -451,7 +401,7 @@ fn test_item() { ], "enum Empty { Unit, Tuple(), Struct {}, }" ); - c2!(item, + c1!(item, [ enum Enum where @@ -462,13 +412,6 @@ fn test_item() { Struct { t: T }, } ], - "enum Enum where T: 'a {\n\ - \x20 Unit,\n\ - \x20 Tuple(T),\n\ - \x20 Struct {\n\ - \x20 t: T,\n\ - \x20 },\n\ - }", "enum Enum where T: 'a, { Unit, Tuple(T), Struct { t: T }, }" ); @@ -477,7 +420,7 @@ fn test_item() { c1!(item, [ struct Tuple(); ], "struct Tuple();"); c1!(item, [ struct Tuple(T); ], "struct Tuple(T);"); c1!(item, [ struct Struct {} ], "struct Struct {}"); - c2!(item, + c1!(item, [ struct Struct where @@ -486,29 +429,23 @@ fn test_item() { t: T, } ], - "struct Struct where T: 'a {\n\ - \x20 t: T,\n\ - }", "struct Struct where T: 'a, { t: T, }" ); // ItemKind::Union c1!(item, [ pub union Union {} ], "pub union Union {}"); - c2!(item, + c1!(item, [ union Union where T: 'a { t: T, } ], - "union Union where T: 'a {\n\ - \x20 t: T,\n\ - }", "union Union where T: 'a { t: T, }" ); // ItemKind::Trait c1!(item, [ pub unsafe auto trait Send {} ], "pub unsafe auto trait Send {}"); - c2!(item, + c1!(item, [ trait Trait<'a>: Sized where @@ -516,7 +453,6 @@ fn test_item() { { } ], - "trait Trait<'a>: Sized where Self: 'a {}", "trait Trait<'a>: Sized where Self: 'a, {}" ); @@ -547,11 +483,7 @@ fn test_item() { ], "macro_rules! stringify { () => {}; }" ); - c2!(item, - [ pub macro stringify() {} ], - "pub macro stringify { () => {} }", // ?? - "pub macro stringify() {}" - ); + c1!(item, [ pub macro stringify() {} ], "pub macro stringify() {}"); } #[test] @@ -577,7 +509,7 @@ fn test_pat() { // PatKind::Struct c1!(pat, [ Struct {} ], "Struct {}"); c1!(pat, [ Struct:: {} ], "Struct:: {}"); - c2!(pat, [ Struct ::< u8 > {} ], "Struct:: {}", "Struct ::< u8 > {}"); + c1!(pat, [ Struct ::< u8 > {} ], "Struct ::< u8 > {}"); c1!(pat, [ Struct::<'static> {} ], "Struct::<'static> {}"); c1!(pat, [ Struct { x } ], "Struct { x }"); c1!(pat, [ Struct { x: _x } ], "Struct { x: _x }"); @@ -597,8 +529,8 @@ fn test_pat() { // PatKind::Or c1!(pat, [ true | false ], "true | false"); - c2!(pat, [ | true ], "true", "| true"); - c2!(pat, [ |true| false ], "true | false", "|true| false"); + c1!(pat, [ | true ], "| true"); + c1!(pat, [ |true| false ], "|true| false"); // PatKind::Path c1!(pat, [ crate::Path ], "crate::Path"); @@ -631,7 +563,7 @@ fn test_pat() { // PatKind::Slice c1!(pat, [ [] ], "[]"); c1!(pat, [ [true] ], "[true]"); - c2!(pat, [ [true,] ], "[true]", "[true,]"); + c1!(pat, [ [true,] ], "[true,]"); c1!(pat, [ [true, false] ], "[true, false]"); // PatKind::Rest @@ -658,7 +590,7 @@ fn test_path() { c1!(path, [ crate::thing ], "crate::thing"); c1!(path, [ Self::thing ], "Self::thing"); c1!(path, [ Self<'static> ], "Self<'static>"); - c2!(path, [ Self::<'static> ], "Self<'static>", "Self::<'static>"); + c1!(path, [ Self::<'static> ], "Self::<'static>"); c1!(path, [ Self() ], "Self()"); c1!(path, [ Self() -> () ], "Self() -> ()"); } @@ -666,40 +598,12 @@ fn test_path() { #[test] fn test_stmt() { // StmtKind::Local - c2!(stmt, [ let _ ], "let _;", "let _"); - c2!(stmt, [ let x = true ], "let x = true;", "let x = true"); - c2!(stmt, [ let x: bool = true ], "let x: bool = true;", "let x: bool = true"); - c2!(stmt, [ let (a, b) = (1, 2) ], "let (a, b) = (1, 2);", "let (a, b) = (1, 2)"); - c2!(stmt, - [ let (a, b): (u32, u32) = (1, 2) ], - "let (a, b): (u32, u32) = (1, 2);", - "let (a, b): (u32, u32) = (1, 2)" - ); - c2!(stmt, - [ let _ = f() else { return; } ], - "let _ = f() else { return; };", - "let _ = f() else { return; }", - ); - macro_rules! c2_let_expr_minus_one { - ([ $expr:expr ], $stmt_expected:expr, $tokens_expected:expr $(,)?) => { - c2!(stmt, [ let _ = $expr - 1 ], $stmt_expected, $tokens_expected); - }; - } - c2_let_expr_minus_one!( - [ match void {} ], - "let _ = match void {} - 1;", - "let _ = match void {} - 1", - ); - macro_rules! c2_let_expr_else_return { - ([ $expr:expr ], $stmt_expected:expr, $tokens_expected:expr $(,)?) => { - c2!(stmt, [ let _ = $expr else { return; } ], $stmt_expected, $tokens_expected); - }; - } - c2_let_expr_else_return!( - [ f() ], - "let _ = f() else { return; };", - "let _ = f() else { return; }", - ); + c1!(stmt, [ let _ ], "let _"); + c1!(stmt, [ let x = true ], "let x = true"); + c1!(stmt, [ let x: bool = true ], "let x: bool = true"); + c1!(stmt, [ let (a, b) = (1, 2) ], "let (a, b) = (1, 2)"); + c1!(stmt, [ let (a, b): (u32, u32) = (1, 2) ], "let (a, b): (u32, u32) = (1, 2)"); + c1!(stmt, [ let _ = f() else { return; } ], "let _ = f() else { return; }"); // StmtKind::Item c1!(stmt, [ struct S; ], "struct S;"); @@ -709,62 +613,7 @@ fn test_stmt() { c1!(stmt, [ loop {} ], "loop {}"); // StmtKind::Semi - c2!(stmt, [ 1 + 1 ], "1 + 1;", "1 + 1"); - macro_rules! c2_expr_as_stmt { - // Parse as expr, then reparse as stmt. - // - // The c2_minus_one macro below can't directly call `c2!(stmt, ...)` - // because `$expr - 1` cannot be parsed directly as a stmt. A statement - // boundary occurs after the `match void {}`, after which the `-` token - // hits "no rules expected this token in macro call". - // - // The unwanted statement boundary is exactly why the pretty-printer is - // injecting parentheses around the subexpression, which is the behavior - // we are interested in testing. - ([ $expr:expr ], $stmt_expected:expr, $tokens_expected:expr $(,)?) => { - c2!(stmt, [ $expr ], $stmt_expected, $tokens_expected); - }; - } - macro_rules! c2_minus_one { - ([ $expr:expr ], $stmt_expected:expr, $tokens_expected:expr $(,)?) => { - c2_expr_as_stmt!([ $expr - 1 ], $stmt_expected, $tokens_expected); - }; - } - c2_minus_one!( - [ match void {} ], - "(match void {}) - 1;", - "match void {} - 1", - ); - c2_minus_one!( - [ match void {}() ], - "(match void {})() - 1;", - "match void {}() - 1", - ); - c2_minus_one!( - [ match void {}[0] ], - "(match void {})[0] - 1;", - "match void {}[0] - 1", - ); - c2_minus_one!( - [ loop { break 1; } ], - "(loop { break 1; }) - 1;", - "loop { break 1; } - 1", - ); - c2_minus_one!( - [ m!() ], - "m!() - 1;", - "m!() - 1" - ); - c2_minus_one!( - [ m![] ], - "m![] - 1;", - "m![] - 1" - ); - c2_minus_one!( - [ m! {} ], - "(m! {}) - 1;", - "m! {} - 1" - ); + c1!(stmt, [ 1 + 1 ], "1 + 1"); // StmtKind::Empty c1!(stmt, [ ; ], ";"); @@ -793,14 +642,14 @@ fn test_ty() { c1!(ty, [ &'a T ], "&'a T"); c1!(ty, [ &'a mut [T] ], "&'a mut [T]"); c1!(ty, [ &A>>> ], "&A>>>"); - c2!(ty, [ &A > > > ], "&A>>>", "&A > > >"); + c1!(ty, [ &A > > > ], "&A > > >"); // TyKind::BareFn c1!(ty, [ fn() ], "fn()"); c1!(ty, [ fn() -> () ], "fn() -> ()"); c1!(ty, [ fn(u8) ], "fn(u8)"); c1!(ty, [ fn(x: u8) ], "fn(x: u8)"); - c2!(ty, [ for<> fn() ], "fn()", "for<> fn()"); + c1!(ty, [ for<> fn() ], "for<> fn()"); c1!(ty, [ for<'a> fn() ], "for<'a> fn()"); // TyKind::Never @@ -819,7 +668,7 @@ fn test_ty() { c1!(ty, [ T ], "T"); c1!(ty, [ Ref<'a> ], "Ref<'a>"); c1!(ty, [ PhantomData ], "PhantomData"); - c2!(ty, [ PhantomData:: ], "PhantomData", "PhantomData::"); + c1!(ty, [ PhantomData:: ], "PhantomData::"); c1!(ty, [ Fn() -> ! ], "Fn() -> !"); c1!(ty, [ Fn(u8) -> ! ], "Fn(u8) -> !"); c1!(ty, [ ::Type ], "::Type"); @@ -864,23 +713,19 @@ fn test_ty() { #[test] fn test_vis() { // VisibilityKind::Public - c2!(vis, [ pub ], "pub ", "pub"); + c1!(vis, [ pub ], "pub"); // VisibilityKind::Restricted - c2!(vis, [ pub(crate) ], "pub(crate) ", "pub(crate)"); - c2!(vis, [ pub(self) ], "pub(self) ", "pub(self)"); - c2!(vis, [ pub(super) ], "pub(super) ", "pub(super)"); - c2!(vis, [ pub(in crate) ], "pub(in crate) ", "pub(in crate)"); - c2!(vis, [ pub(in self) ], "pub(in self) ", "pub(in self)"); - c2!(vis, [ pub(in super) ], "pub(in super) ", "pub(in super)"); - c2!(vis, [ pub(in path::to) ], "pub(in path::to) ", "pub(in path::to)"); - c2!(vis, [ pub(in ::path::to) ], "pub(in ::path::to) ", "pub(in ::path::to)"); - c2!(vis, [ pub(in self::path::to) ], "pub(in self::path::to) ", "pub(in self::path::to)"); - c2!(vis, - [ pub(in super::path::to) ], - "pub(in super::path::to) ", - "pub(in super::path::to)" - ); + c1!(vis, [ pub(crate) ], "pub(crate)"); + c1!(vis, [ pub(self) ], "pub(self)"); + c1!(vis, [ pub(super) ], "pub(super)"); + c1!(vis, [ pub(in crate) ], "pub(in crate)"); + c1!(vis, [ pub(in self) ], "pub(in self)"); + c1!(vis, [ pub(in super) ], "pub(in super)"); + c1!(vis, [ pub(in path::to) ], "pub(in path::to)"); + c1!(vis, [ pub(in ::path::to) ], "pub(in ::path::to)"); + c1!(vis, [ pub(in self::path::to) ], "pub(in self::path::to)"); + c1!(vis, [ pub(in super::path::to) ], "pub(in super::path::to)"); // VisibilityKind::Inherited // This one is different because directly calling `vis!` does not work. diff --git a/tests/ui/macros/trace_faulty_macros.rs b/tests/ui/macros/trace_faulty_macros.rs index ec1ce1a1f925..87036bb9c6f7 100644 --- a/tests/ui/macros/trace_faulty_macros.rs +++ b/tests/ui/macros/trace_faulty_macros.rs @@ -46,7 +46,7 @@ macro_rules! test { (let $p:pat = $e:expr) => {test!(($p,$e))}; // this should be expr // vvv - (($p:pat, $e:pat)) => {let $p = $e;}; //~ ERROR expected expression, found pattern `1 + 1` + (($p:pat, $e:pat)) => {let $p = $e;}; //~ ERROR expected expression, found pattern `1+1` } fn foo() { diff --git a/tests/ui/macros/trace_faulty_macros.stderr b/tests/ui/macros/trace_faulty_macros.stderr index 665dcc7d0913..66d7b76bb072 100644 --- a/tests/ui/macros/trace_faulty_macros.stderr +++ b/tests/ui/macros/trace_faulty_macros.stderr @@ -50,7 +50,7 @@ LL | my_recursive_macro!(); = note: expanding `my_recursive_macro! { }` = note: to `my_recursive_macro! ();` -error: expected expression, found pattern `A { a: a, b: 0, c: _, .. }` +error: expected expression, found pattern `A { a : a, b : 0, c : _, .. }` --> $DIR/trace_faulty_macros.rs:16:9 | LL | $a @@ -69,7 +69,7 @@ LL | #[derive(Debug)] LL | fn use_derive_macro_as_attr() {} | -------------------------------- not a `struct`, `enum` or `union` -error: expected expression, found pattern `1 + 1` +error: expected expression, found pattern `1+1` --> $DIR/trace_faulty_macros.rs:49:37 | LL | (let $p:pat = $e:expr) => {test!(($p,$e))}; @@ -96,7 +96,7 @@ LL | let a = pat_macro!(); = note: expanding `pat_macro! { }` = note: to `pat_macro! (A { a : a, b : 0, c : _, .. });` = note: expanding `pat_macro! { A { a : a, b : 0, c : _, .. } }` - = note: to `A { a: a, b: 0, c: _, .. }` + = note: to `A { a : a, b : 0, c : _, .. }` note: trace_macro --> $DIR/trace_faulty_macros.rs:53:5 @@ -105,9 +105,9 @@ LL | test!(let x = 1+1); | ^^^^^^^^^^^^^^^^^^ | = note: expanding `test! { let x = 1+1 }` - = note: to `test! ((x, 1 + 1))` - = note: expanding `test! { (x, 1 + 1) }` - = note: to `let x = 1 + 1;` + = note: to `test! ((x, 1+1))` + = note: expanding `test! { (x, 1+1) }` + = note: to `let x = 1+1;` error: aborting due to 5 previous errors diff --git a/tests/ui/malformed/do-not-ice-on-note_and_explain.rs b/tests/ui/malformed/do-not-ice-on-note_and_explain.rs index e65276fb738d..be0b18a00d28 100644 --- a/tests/ui/malformed/do-not-ice-on-note_and_explain.rs +++ b/tests/ui/malformed/do-not-ice-on-note_and_explain.rs @@ -1,7 +1,12 @@ struct A(B); -implA{fn d(){fn d(){Self(1)}}} -//~^ ERROR the size for values of type `B` cannot be known at compilation time -//~| ERROR the size for values of type `B` cannot be known at compilation time -//~| ERROR mismatched types -//~| ERROR mismatched types -//~| ERROR `main` function not found in crate + +impl A { + fn d() { + fn d() { + Self(1) + //~^ ERROR can't reference `Self` constructor from outer item + } + } +} + +fn main() {} diff --git a/tests/ui/malformed/do-not-ice-on-note_and_explain.stderr b/tests/ui/malformed/do-not-ice-on-note_and_explain.stderr index 41d0f17366b1..11a8c01e4909 100644 --- a/tests/ui/malformed/do-not-ice-on-note_and_explain.stderr +++ b/tests/ui/malformed/do-not-ice-on-note_and_explain.stderr @@ -1,79 +1,12 @@ -error[E0601]: `main` function not found in crate `do_not_ice_on_note_and_explain` - --> $DIR/do-not-ice-on-note_and_explain.rs:2:37 +error[E0401]: can't reference `Self` constructor from outer item + --> $DIR/do-not-ice-on-note_and_explain.rs:6:13 | -LL | implA{fn d(){fn d(){Self(1)}}} - | ^ consider adding a `main` function to `$DIR/do-not-ice-on-note_and_explain.rs` +LL | impl A { + | ------------ the inner item doesn't inherit generics from this impl, so `Self` is invalid to reference +... +LL | Self(1) + | ^^^^ help: replace `Self` with the actual type: `A` -error[E0277]: the size for values of type `B` cannot be known at compilation time - --> $DIR/do-not-ice-on-note_and_explain.rs:2:32 - | -LL | implA{fn d(){fn d(){Self(1)}}} - | - ---- ^ doesn't have a size known at compile-time - | | | - | | required by a bound introduced by this call - | this type parameter needs to be `Sized` - | -note: required by a bound in `A` - --> $DIR/do-not-ice-on-note_and_explain.rs:1:10 - | -LL | struct A(B); - | ^ required by this bound in `A` +error: aborting due to 1 previous error -error[E0308]: mismatched types - --> $DIR/do-not-ice-on-note_and_explain.rs:2:32 - | -LL | implA{fn d(){fn d(){Self(1)}}} - | ---- ^ expected type parameter `B`, found integer - | | - | arguments to this function are incorrect - | - = note: expected type parameter `B` - found type `{integer}` -note: tuple struct defined here - --> $DIR/do-not-ice-on-note_and_explain.rs:1:8 - | -LL | struct A(B); - | ^ - -error[E0308]: mismatched types - --> $DIR/do-not-ice-on-note_and_explain.rs:2:27 - | -LL | implA{fn d(){fn d(){Self(1)}}} - | ^^^^^^^ expected `()`, found `A` - | - = note: expected unit type `()` - found struct `A` -help: consider using a semicolon here - | -LL | implA{fn d(){fn d(){Self(1);}}} - | + -help: try adding a return type - | -LL | implA{fn d(){fn d() -> A{Self(1)}}} - | +++++++ - -error[E0277]: the size for values of type `B` cannot be known at compilation time - --> $DIR/do-not-ice-on-note_and_explain.rs:2:27 - | -LL | implA{fn d(){fn d(){Self(1)}}} - | - ^^^^^^^ doesn't have a size known at compile-time - | | - | this type parameter needs to be `Sized` - | -note: required by an implicit `Sized` bound in `A` - --> $DIR/do-not-ice-on-note_and_explain.rs:1:10 - | -LL | struct A(B); - | ^ required by the implicit `Sized` requirement on this type parameter in `A` -help: you could relax the implicit `Sized` bound on `B` if it were used through indirection like `&B` or `Box` - --> $DIR/do-not-ice-on-note_and_explain.rs:1:10 - | -LL | struct A(B); - | ^ - ...if indirection were used here: `Box` - | | - | this could be changed to `B: ?Sized`... - -error: aborting due to 5 previous errors - -Some errors have detailed explanations: E0277, E0308, E0601. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0401`. diff --git a/tests/ui/malformed/malformed-regressions.rs b/tests/ui/malformed/malformed-regressions.rs index ac1444bbaef4..f0a7aac59c1c 100644 --- a/tests/ui/malformed/malformed-regressions.rs +++ b/tests/ui/malformed/malformed-regressions.rs @@ -1,8 +1,8 @@ -#[doc] //~ ERROR attribute must be of the form +#[doc] //~ ERROR valid forms for the attribute are //~^ WARN this was previously accepted -#[ignore()] //~ ERROR attribute must be of the form +#[ignore()] //~ ERROR valid forms for the attribute are //~^ WARN this was previously accepted -#[inline = ""] //~ ERROR attribute must be of the form +#[inline = ""] //~ ERROR valid forms for the attribute are //~^ WARN this was previously accepted #[link] //~ ERROR attribute must be of the form //~^ WARN this was previously accepted diff --git a/tests/ui/malformed/malformed-regressions.stderr b/tests/ui/malformed/malformed-regressions.stderr index 9bfbe7ebafd7..e1dbdb9ab3c6 100644 --- a/tests/ui/malformed/malformed-regressions.stderr +++ b/tests/ui/malformed/malformed-regressions.stderr @@ -1,4 +1,4 @@ -error: attribute must be of the form `#[doc(hidden|inline|...)]` or `#[doc = "string"]` +error: valid forms for the attribute are `#[doc(hidden|inline|...)]` and `#[doc = "string"]` --> $DIR/malformed-regressions.rs:1:1 | LL | #[doc] @@ -8,7 +8,7 @@ LL | #[doc] = note: for more information, see issue #57571 = note: `#[deny(ill_formed_attribute_input)]` on by default -error: attribute must be of the form `#[ignore]` or `#[ignore = "reason"]` +error: valid forms for the attribute are `#[ignore]` and `#[ignore = "reason"]` --> $DIR/malformed-regressions.rs:3:1 | LL | #[ignore()] @@ -17,7 +17,7 @@ LL | #[ignore()] = warning: 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 #57571 -error: attribute must be of the form `#[inline]` or `#[inline(always|never)]` +error: valid forms for the attribute are `#[inline]` and `#[inline(always|never)]` --> $DIR/malformed-regressions.rs:5:1 | LL | #[inline = ""] diff --git a/tests/ui/manual/manual-link-framework.rs b/tests/ui/manual/manual-link-framework.rs index 06fd76f68df1..43cdda0a4e6a 100644 --- a/tests/ui/manual/manual-link-framework.rs +++ b/tests/ui/manual/manual-link-framework.rs @@ -1,7 +1,5 @@ -//@ ignore-macos -//@ ignore-ios +//@ ignore-apple //@ compile-flags:-l framework=foo //@ error-pattern: library kind `framework` is only supported on Apple targets -fn main() { -} +fn main() {} diff --git a/tests/ui/match/issue-82392.stdout b/tests/ui/match/issue-82392.stdout index 8a23d906757d..8949611ac12f 100644 --- a/tests/ui/match/issue-82392.stdout +++ b/tests/ui/match/issue-82392.stdout @@ -7,10 +7,10 @@ extern crate std; //@ check-pass fn main() ({ - (if (true as bool) - ({ } as - ()) else if (let Some(a) = - ((Some as - fn(i32) -> Option {Option::::Some})((3 as i32)) as - Option) as bool) ({ } as ()) as ()) - } as ()) + (if (true as bool) + ({ } as + ()) else if (let Some(a) = + ((Some as + fn(i32) -> Option {Option::::Some})((3 as i32)) as + Option) as bool) ({ } as ()) as ()) + } as ()) diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs index 62e4f82a3ffb..829b7f86e262 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs @@ -23,9 +23,6 @@ pub fn main() { if let Some(Some(&x)) = &Some(&Some(0)) { let _: u32 = x; } - if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { - let _: u32 = x; - } if let Some(&Some(&x)) = &mut Some(&Some(0)) { let _: u32 = x; } @@ -35,9 +32,6 @@ pub fn main() { if let Some(&Some(&mut ref x)) = Some(&Some(&mut 0)) { let _: &u32 = x; } - if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { - let _: &u32 = x; - } if let &Some(Some(x)) = &Some(&mut Some(0)) { let _: &u32 = x; } @@ -59,13 +53,4 @@ pub fn main() { if let Some(&Some(x)) = &mut Some(Some(0)) { let _: u32 = x; } - - let &mut x = &&mut 0; - let _: &u32 = x; - - let &mut x = &&&&&&&&&&&&&&&&&&&&&&&&&&&&mut 0; - let _: &u32 = x; - - let &mut &mut &mut &mut x = &mut &&&&mut &&&mut &mut 0; - let _: &u32 = x; } diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs index 96b4ff77ddb4..40e8293e2411 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs @@ -5,26 +5,26 @@ pub fn main() { if let Some(&mut Some(&_)) = &Some(&Some(0)) { - //~^ ERROR: mismatched types + //~^ ERROR: cannot match inherited `&` with `&mut` pattern } if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { - //~^ ERROR: mismatched types + //~^ ERROR: cannot match inherited `&` with `&mut` pattern } if let Some(&Some(x)) = &mut Some(&Some(0)) { let _: &mut u32 = x; //~^ ERROR: mismatched types } if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { - //~^ ERROR: mismatched types + //~^ ERROR: cannot match inherited `&` with `&mut` pattern } if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { - //~^ ERROR: mismatched types + //~^ ERROR: cannot match inherited `&` with `&mut` pattern } if let Some(&mut Some(x)) = &Some(Some(0)) { - //~^ ERROR: mismatched types + //~^ ERROR: cannot match inherited `&` with `&mut` pattern } if let Some(&mut Some(x)) = &Some(Some(0)) { - //~^ ERROR: mismatched types + //~^ ERROR: cannot match inherited `&` with `&mut` pattern } let &mut _ = &&0; @@ -32,4 +32,31 @@ pub fn main() { let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; //~^ ERROR: mismatched types + + if let Some(&mut Some(&_)) = &Some(&mut Some(0)) { + //~^ ERROR: cannot match inherited `&` with `&mut` pattern + } + + if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { + //~^ ERROR: cannot match inherited `&` with `&mut` pattern + } + + let &mut _ = &&mut 0; + //~^ ERROR: mismatched types + + let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&mut 0; + //~^ ERROR: mismatched types + + let &mut &mut &mut &mut _ = &mut &&&&mut &&&mut &mut 0; + //~^ ERROR: mismatched types + + struct Foo(u8); + + let Foo(mut a) = &Foo(0); + //~^ ERROR: binding cannot be both mutable and by-reference + a = &42; + + let Foo(mut a) = &mut Foo(0); + //~^ ERROR: binding cannot be both mutable and by-reference + a = &mut 42; } diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr index e06a645fc0d3..26317e43d023 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr +++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr @@ -1,24 +1,24 @@ -error[E0308]: mismatched types +error: cannot match inherited `&` with `&mut` pattern --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:7:17 | LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { - | ^^^^^^^^^^^^^ --------------- this expression has type `&Option<&Option<{integer}>>` - | | - | expected `Option<{integer}>`, found `&mut _` + | ^^^^^ | - = note: expected enum `Option<{integer}>` - found mutable reference `&mut _` +help: replace this `&mut` pattern with `&` + | +LL | if let Some(&Some(&_)) = &Some(&Some(0)) { + | ~ -error[E0308]: mismatched types +error: cannot match inherited `&` with `&mut` pattern --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:10:23 | LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { - | ^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` +help: replace this `&mut` pattern with `&` + | +LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { + | ~ error[E0308]: mismatched types --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:14:27 @@ -31,49 +31,49 @@ LL | let _: &mut u32 = x; = note: expected mutable reference `&mut u32` found reference `&{integer}` -error[E0308]: mismatched types +error: cannot match inherited `&` with `&mut` pattern --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:17:23 | LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { - | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` +help: replace this `&mut` pattern with `&` + | +LL | if let Some(&Some(&_)) = &mut Some(&Some(0)) { + | ~ -error[E0308]: mismatched types +error: cannot match inherited `&` with `&mut` pattern --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:20:29 | LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { - | ^^^^^^ ------------------------- this expression has type `&Option>>` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` +help: replace this `&mut` pattern with `&` + | +LL | if let Some(&Some(Some((&_)))) = &Some(Some(&mut Some(0))) { + | ~ -error[E0308]: mismatched types +error: cannot match inherited `&` with `&mut` pattern --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:23:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { - | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` - | | - | expected `Option<{integer}>`, found `&mut _` + | ^^^^^ | - = note: expected enum `Option<{integer}>` - found mutable reference `&mut _` +help: replace this `&mut` pattern with `&` + | +LL | if let Some(&Some(x)) = &Some(Some(0)) { + | ~ -error[E0308]: mismatched types +error: cannot match inherited `&` with `&mut` pattern --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:26:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { - | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` - | | - | expected `Option<{integer}>`, found `&mut _` + | ^^^^^ | - = note: expected enum `Option<{integer}>` - found mutable reference `&mut _` +help: replace this `&mut` pattern with `&` + | +LL | if let Some(&Some(x)) = &Some(Some(0)) { + | ~ error[E0308]: mismatched types --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:30:9 @@ -81,9 +81,9 @@ error[E0308]: mismatched types LL | let &mut _ = &&0; | ^^^^^^ --- this expression has type `&&{integer}` | | - | expected integer, found `&mut _` + | types differ in mutability | - = note: expected type `{integer}` + = note: expected reference `&&{integer}` found mutable reference `&mut _` error[E0308]: mismatched types @@ -92,11 +92,87 @@ error[E0308]: mismatched types LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; | ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` | | - | expected integer, found `&mut _` + | types differ in mutability | - = note: expected type `{integer}` + = note: expected reference `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` found mutable reference `&mut _` -error: aborting due to 9 previous errors +error: cannot match inherited `&` with `&mut` pattern + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:36:17 + | +LL | if let Some(&mut Some(&_)) = &Some(&mut Some(0)) { + | ^^^^^ + | +help: replace this `&mut` pattern with `&` + | +LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { + | ~ -For more information about this error, try `rustc --explain E0308`. +error: cannot match inherited `&` with `&mut` pattern + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:40:22 + | +LL | if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { + | ^^^^^ + | +help: replace this `&mut` pattern with `&` + | +LL | if let Some(Some(&x)) = &Some(Some(&mut 0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:44:9 + | +LL | let &mut _ = &&mut 0; + | ^^^^^^ ------- this expression has type `&&mut {integer}` + | | + | types differ in mutability + | + = note: expected reference `&&mut {integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:47:9 + | +LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&mut 0; + | ^^^^^^ --------------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&mut {integer}` + | | + | types differ in mutability + | + = note: expected reference `&&&&&&&&&&&&&&&&&&&&&&&&&&&&mut {integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:50:14 + | +LL | let &mut &mut &mut &mut _ = &mut &&&&mut &&&mut &mut 0; + | ^^^^^^^^^^^^^^^^ -------------------------- this expression has type `&mut &&&&mut &&&mut &mut {integer}` + | | + | types differ in mutability + | + = note: expected reference `&&&&mut &&&mut &mut {integer}` + found mutable reference `&mut _` + +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:55:13 + | +LL | let Foo(mut a) = &Foo(0); + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:59:13 + | +LL | let Foo(mut a) = &mut Foo(0); + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 16 previous errors + +Some errors have detailed explanations: E0308, E0658. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/match/ref_pat_everywhere-fail.rs b/tests/ui/match/ref_pat_everywhere-fail.rs deleted file mode 100644 index d1b1c04730d3..000000000000 --- a/tests/ui/match/ref_pat_everywhere-fail.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![allow(incomplete_features)] -#![feature(ref_pat_everywhere)] -pub fn main() { - if let Some(&x) = Some(0) { - //~^ ERROR: mismatched types [E0308] - let _: u32 = x; - } - if let Some(&mut x) = Some(&0) { - //~^ ERROR: mismatched types [E0308] - let _: u32 = x; - } -} diff --git a/tests/ui/match/ref_pat_everywhere-fail.stderr b/tests/ui/match/ref_pat_everywhere-fail.stderr deleted file mode 100644 index 25a01129f4a9..000000000000 --- a/tests/ui/match/ref_pat_everywhere-fail.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/ref_pat_everywhere-fail.rs:4:17 - | -LL | if let Some(&x) = Some(0) { - | ^^ ------- this expression has type `Option<{integer}>` - | | - | expected integer, found `&_` - | - = note: expected type `{integer}` - found reference `&_` -help: consider removing `&` from the pattern - | -LL | if let Some(x) = Some(0) { - | ~ - -error[E0308]: mismatched types - --> $DIR/ref_pat_everywhere-fail.rs:8:17 - | -LL | if let Some(&mut x) = Some(&0) { - | ^^^^^^ -------- this expression has type `Option<&{integer}>` - | | - | types differ in mutability - | - = note: expected reference `&{integer}` - found mutable reference `&mut _` -note: to declare a mutable binding use: `mut x` - --> $DIR/ref_pat_everywhere-fail.rs:8:17 - | -LL | if let Some(&mut x) = Some(&0) { - | ^^^^^^ -help: consider removing `&mut` from the pattern - | -LL | if let Some(x) = Some(&0) { - | ~ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/match/ref_pat_everywhere.rs b/tests/ui/match/ref_pat_everywhere.rs deleted file mode 100644 index 9a79c548475f..000000000000 --- a/tests/ui/match/ref_pat_everywhere.rs +++ /dev/null @@ -1,24 +0,0 @@ -//@ run-pass -#![allow(incomplete_features)] -#![feature(ref_pat_everywhere)] - -pub fn main() { - if let Some(Some(&x)) = &Some(&Some(0)) { - let _: u32 = x; - } - if let Some(&Some(x)) = &Some(Some(0)) { - let _: u32 = x; - } - if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { - let _: u32 = x; - } - if let Some(Some(&x)) = &Some(&mut Some(0)) { - let _: u32 = x; - } - if let &Some(x) = &mut Some(0) { - let _: u32 = x; - } - if let Some(&x) = &mut Some(0) { - let _: u32 = x; - } -} diff --git a/tests/ui/meta/no_std-extern-libc.rs b/tests/ui/meta/no_std-extern-libc.rs index 919caf9428f7..8b89e14382d1 100644 --- a/tests/ui/meta/no_std-extern-libc.rs +++ b/tests/ui/meta/no_std-extern-libc.rs @@ -1,5 +1,6 @@ // Test that `download-rustc` doesn't put duplicate copies of libc in the sysroot. //@ check-pass +//@ ignore-windows doesn't necessarily have the libc crate #![crate_type = "lib"] #![no_std] #![feature(rustc_private)] diff --git a/tests/ui/methods/issues/account-for-shadowed-bindings-issue-123558.rs b/tests/ui/methods/issues/account-for-shadowed-bindings-issue-123558.rs new file mode 100644 index 000000000000..a08bbf1fdbec --- /dev/null +++ b/tests/ui/methods/issues/account-for-shadowed-bindings-issue-123558.rs @@ -0,0 +1,7 @@ +fn main() { + let x = Some(3); + let y = vec![1, 2]; + if let Some(y) = x { + y.push(y); //~ ERROR E0599 + } +} diff --git a/tests/ui/methods/issues/account-for-shadowed-bindings-issue-123558.stderr b/tests/ui/methods/issues/account-for-shadowed-bindings-issue-123558.stderr new file mode 100644 index 000000000000..aecad201c7b2 --- /dev/null +++ b/tests/ui/methods/issues/account-for-shadowed-bindings-issue-123558.stderr @@ -0,0 +1,17 @@ +error[E0599]: no method named `push` found for type `{integer}` in the current scope + --> $DIR/account-for-shadowed-bindings-issue-123558.rs:5:11 + | +LL | y.push(y); + | ^^^^ method not found in `{integer}` + | +note: there's an earlier shadowed binding `y` of type `Vec<{integer}>` that has method `push` available + --> $DIR/account-for-shadowed-bindings-issue-123558.rs:3:9 + | +LL | let y = vec![1, 2]; + | ^ `y` of type `Vec<{integer}>` that has method `push` defined earlier here +LL | if let Some(y) = x { + | - earlier `y` shadowed here with type `{integer}` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/methods/method-call-type-binding.rs b/tests/ui/methods/method-call-type-binding.rs index f547ca8d1c2a..290c66fabe3b 100644 --- a/tests/ui/methods/method-call-type-binding.rs +++ b/tests/ui/methods/method-call-type-binding.rs @@ -1,3 +1,3 @@ fn main() { - 0.clone::(); //~ ERROR associated type bindings are not allowed here + 0.clone::(); //~ ERROR associated item constraints are not allowed here } diff --git a/tests/ui/methods/method-call-type-binding.stderr b/tests/ui/methods/method-call-type-binding.stderr index 54d855d340ea..1acb0b2e12b4 100644 --- a/tests/ui/methods/method-call-type-binding.stderr +++ b/tests/ui/methods/method-call-type-binding.stderr @@ -1,8 +1,8 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/method-call-type-binding.rs:2:15 | LL | 0.clone::(); - | ^^^^^^ associated type not allowed here + | ^^^^^^ associated item constraint not allowed here error: aborting due to 1 previous error diff --git a/tests/ui/methods/opaque_param_in_ufc.rs b/tests/ui/methods/opaque_param_in_ufc.rs index a4b27a0131fd..b170e6805f64 100644 --- a/tests/ui/methods/opaque_param_in_ufc.rs +++ b/tests/ui/methods/opaque_param_in_ufc.rs @@ -1,4 +1,7 @@ #![feature(type_alias_impl_trait)] + +//@ check-pass + struct Foo(T); impl Foo { @@ -15,14 +18,11 @@ fn bar() -> Bar { impl Foo { fn foo() -> Bar { Self::method(); - //~^ ERROR: no function or associated item named `method` found for struct `Foo` Foo::::method(); - //~^ ERROR: no function or associated item named `method` found for struct `Foo` let x = Foo(bar()); Foo::method2(x); let x = Self(bar()); Self::method2(x); - //~^ ERROR: no function or associated item named `method2` found for struct `Foo` todo!() } } diff --git a/tests/ui/methods/opaque_param_in_ufc.stderr b/tests/ui/methods/opaque_param_in_ufc.stderr deleted file mode 100644 index 7e5bbbac8a9a..000000000000 --- a/tests/ui/methods/opaque_param_in_ufc.stderr +++ /dev/null @@ -1,36 +0,0 @@ -error[E0599]: no function or associated item named `method` found for struct `Foo` in the current scope - --> $DIR/opaque_param_in_ufc.rs:17:15 - | -LL | struct Foo(T); - | ------------- function or associated item `method` not found for this struct -... -LL | Self::method(); - | ^^^^^^ function or associated item not found in `Foo` - | - = note: the function or associated item was found for - - `Foo` - -error[E0599]: no function or associated item named `method` found for struct `Foo` in the current scope - --> $DIR/opaque_param_in_ufc.rs:19:21 - | -LL | struct Foo(T); - | ------------- function or associated item `method` not found for this struct -... -LL | Foo::::method(); - | ^^^^^^ function or associated item not found in `Foo` - | - = note: the function or associated item was found for - - `Foo` - -error[E0599]: no function or associated item named `method2` found for struct `Foo` in the current scope - --> $DIR/opaque_param_in_ufc.rs:24:15 - | -LL | struct Foo(T); - | ------------- function or associated item `method2` not found for this struct -... -LL | Self::method2(x); - | ^^^^^^^ function or associated item not found in `Foo` - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/methods/suggest-method-on-call-for-ambig-receiver.rs b/tests/ui/methods/suggest-method-on-call-for-ambig-receiver.rs new file mode 100644 index 000000000000..fc2c15ee8c66 --- /dev/null +++ b/tests/ui/methods/suggest-method-on-call-for-ambig-receiver.rs @@ -0,0 +1,16 @@ +// Fix for . + +fn separate_arms() { + let mut x = None; + match x { + None => { + x = Some(0); + } + Some(right) => { + consume(right); + //~^ ERROR cannot find function `consume` in this scope + } + } +} + +fn main() {} diff --git a/tests/ui/methods/suggest-method-on-call-for-ambig-receiver.stderr b/tests/ui/methods/suggest-method-on-call-for-ambig-receiver.stderr new file mode 100644 index 000000000000..40d8301c24e5 --- /dev/null +++ b/tests/ui/methods/suggest-method-on-call-for-ambig-receiver.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find function `consume` in this scope + --> $DIR/suggest-method-on-call-for-ambig-receiver.rs:10:13 + | +LL | consume(right); + | ^^^^^^^ not found in this scope + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/mir/dyn_metadata_sroa.rs b/tests/ui/mir/dyn_metadata_sroa.rs new file mode 100644 index 000000000000..1a00c0f0a3e3 --- /dev/null +++ b/tests/ui/mir/dyn_metadata_sroa.rs @@ -0,0 +1,19 @@ +//@ run-pass +//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir + +#![feature(ptr_metadata)] + +// Regression for , +// which failed because of SRoA would project into `DynMetadata`. + +trait Foo {} + +struct Bar; + +impl Foo for Bar {} + +fn main() { + let a: *mut dyn Foo = &mut Bar; + + let _d = a.to_raw_parts().0 as usize; +} diff --git a/tests/ui/mir/issue-75053.rs b/tests/ui/mir/issue-75053.rs index e2ec4076b499..38684f3548f2 100644 --- a/tests/ui/mir/issue-75053.rs +++ b/tests/ui/mir/issue-75053.rs @@ -13,10 +13,13 @@ trait MyFrom: Sized { fn my_from(value: T) -> Result; } -trait F {} -impl F for () {} -type DummyT = impl F; -fn _dummy_t() -> DummyT {} +mod f { + pub trait F {} + impl F for () {} + pub type DummyT = impl F; + fn _dummy_t() -> DummyT {} +} +use f::*; struct Phantom1(PhantomData); struct Phantom2(PhantomData); diff --git a/tests/ui/mir/issue-75053.stderr b/tests/ui/mir/issue-75053.stderr index bd37f0c92ad8..a464d3266f4e 100644 --- a/tests/ui/mir/issue-75053.stderr +++ b/tests/ui/mir/issue-75053.stderr @@ -1,5 +1,5 @@ error: fatal error triggered by #[rustc_error] - --> $DIR/issue-75053.rs:46:1 + --> $DIR/issue-75053.rs:49:1 | LL | fn main() { | ^^^^^^^^^ diff --git a/tests/ui/mir/lint/assignment-overlap.rs b/tests/ui/mir/lint/assignment-overlap.rs index 806d09cda851..6396cccd4e88 100644 --- a/tests/ui/mir/lint/assignment-overlap.rs +++ b/tests/ui/mir/lint/assignment-overlap.rs @@ -9,11 +9,11 @@ use core::intrinsics::mir::*; #[custom_mir(dialect = "runtime", phase = "optimized")] pub fn main() { - mir!( + mir! { let a: [u8; 1024]; { a = a; Return() } - ) + } } diff --git a/tests/ui/mir/lint/call-overlap.rs b/tests/ui/mir/lint/call-overlap.rs index eb806378fe54..def78ea1e3b1 100644 --- a/tests/ui/mir/lint/call-overlap.rs +++ b/tests/ui/mir/lint/call-overlap.rs @@ -9,7 +9,7 @@ use core::intrinsics::mir::*; #[custom_mir(dialect = "runtime", phase = "optimized")] pub fn main() { - mir!( + mir! { let a: [u8; 1024]; { Call(a = f(Move(a)), ReturnTo(bb1), UnwindUnreachable()) @@ -17,7 +17,7 @@ pub fn main() { bb1 = { Return() } - ) + } } pub fn f(a: T) -> T { a } diff --git a/tests/ui/mir/lint/no-storage.rs b/tests/ui/mir/lint/no-storage.rs index 42dd1b963dc1..a6af8646f6ec 100644 --- a/tests/ui/mir/lint/no-storage.rs +++ b/tests/ui/mir/lint/no-storage.rs @@ -8,7 +8,7 @@ use core::intrinsics::mir::*; #[custom_mir(dialect = "built")] pub fn f(a: bool) { - mir!( + mir! { let b: (); { match a { true => bb1, _ => bb2 } @@ -26,5 +26,5 @@ pub fn f(a: bool) { StorageDead(b); Return() } - ) + } } diff --git a/tests/ui/mir/lint/storage-live.rs b/tests/ui/mir/lint/storage-live.rs index 5dea1cb3567e..d734b773642e 100644 --- a/tests/ui/mir/lint/storage-live.rs +++ b/tests/ui/mir/lint/storage-live.rs @@ -5,6 +5,7 @@ //@ normalize-stderr-test "note: .*\n\n" -> "" //@ normalize-stderr-test "thread 'rustc' panicked.*\n" -> "" //@ normalize-stderr-test "storage_live\[....\]" -> "storage_live[HASH]" +//@ normalize-stderr-test "(delayed at [^:]+):\d+:\d+ - " -> "$1:LL:CC - " //@ rustc-env:RUST_BACKTRACE=0 #![feature(custom_mir, core_intrinsics)] @@ -15,14 +16,14 @@ use core::ptr::{addr_of, addr_of_mut}; #[custom_mir(dialect = "built")] fn multiple_storage() { - mir!( + mir! { let a: usize; { StorageLive(a); StorageLive(a); Return() } - ) + } } fn main() { diff --git a/tests/ui/mir/lint/storage-live.stderr b/tests/ui/mir/lint/storage-live.stderr index 2eb8d8e70001..7d4c3f0832a9 100644 --- a/tests/ui/mir/lint/storage-live.stderr +++ b/tests/ui/mir/lint/storage-live.stderr @@ -1,12 +1,12 @@ error: internal compiler error: broken MIR in Item(DefId(0:8 ~ storage_live[HASH]::multiple_storage)) (after pass CheckPackedRef) at bb0[1]: StorageLive(_1) which already has storage here - --> $DIR/storage-live.rs:22:13 + --> $DIR/storage-live.rs:23:13 | LL | StorageLive(a); | ^^^^^^^^^^^^^^ | -note: delayed at compiler/rustc_mir_transform/src/lint.rs:97:26 - disabled backtrace - --> $DIR/storage-live.rs:22:13 +note: delayed at compiler/rustc_mir_transform/src/lint.rs:LL:CC - disabled backtrace + --> $DIR/storage-live.rs:23:13 | LL | StorageLive(a); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/mir/lint/storage-return.rs b/tests/ui/mir/lint/storage-return.rs index b6281d4b2a8e..d51aee9518fb 100644 --- a/tests/ui/mir/lint/storage-return.rs +++ b/tests/ui/mir/lint/storage-return.rs @@ -8,12 +8,12 @@ use core::intrinsics::mir::*; #[custom_mir(dialect = "built")] fn main() { - mir!( + mir! { let a: (); { StorageLive(a); RET = a; Return() } - ) + } } diff --git a/tests/ui/mir/mir_codegen_ssa.rs b/tests/ui/mir/mir_codegen_ssa.rs index bf01edcbaa84..1666b8293eba 100644 --- a/tests/ui/mir/mir_codegen_ssa.rs +++ b/tests/ui/mir/mir_codegen_ssa.rs @@ -5,7 +5,7 @@ use std::intrinsics::mir::*; #[custom_mir(dialect = "runtime", phase = "optimized")] pub fn f(a: u32) -> u32 { - mir!( + mir! { let x: u32; { // Previously code generation failed with ICE "use of .. before def ..." because the @@ -15,5 +15,5 @@ pub fn f(a: u32) -> u32 { RET = x; Return() } - ) + } } diff --git a/tests/ui/mir/ssa_call_ret.rs b/tests/ui/mir/ssa_call_ret.rs index 9b2c78c737f1..baaa7326fd80 100644 --- a/tests/ui/mir/ssa_call_ret.rs +++ b/tests/ui/mir/ssa_call_ret.rs @@ -10,7 +10,7 @@ use core::intrinsics::mir::*; #[custom_mir(dialect = "runtime", phase = "optimized")] pub fn f() -> u32 { - mir!( + mir! { let a: u32; { Call(a = g(), ReturnTo(bb1), UnwindCleanup(bb2)) @@ -23,7 +23,7 @@ pub fn f() -> u32 { RET = a; UnwindResume() } - ) + } } #[inline(never)] diff --git a/tests/ui/mir/validate/critical-edge.rs b/tests/ui/mir/validate/critical-edge.rs index 0a548ae8e584..9048d08a22a7 100644 --- a/tests/ui/mir/validate/critical-edge.rs +++ b/tests/ui/mir/validate/critical-edge.rs @@ -12,7 +12,7 @@ use core::intrinsics::mir::*; #[custom_mir(dialect = "runtime", phase = "optimized")] #[inline(always)] pub fn f(a: u32) -> u32 { - mir!( + mir! { { match a { 0 => bb1, @@ -27,5 +27,5 @@ pub fn f(a: u32) -> u32 { RET = 2; Return() } - ) + } } diff --git a/tests/ui/mir/validate/noncleanup-cleanup.rs b/tests/ui/mir/validate/noncleanup-cleanup.rs index 744e71e99b10..b46bb46952b8 100644 --- a/tests/ui/mir/validate/noncleanup-cleanup.rs +++ b/tests/ui/mir/validate/noncleanup-cleanup.rs @@ -9,13 +9,12 @@ use core::intrinsics::mir::*; #[custom_mir(dialect = "built")] pub fn main() { - mir!( + mir! { { Call(RET = main(), ReturnTo(block), UnwindCleanup(block)) } block = { Return() } - ) - + } } diff --git a/tests/ui/mir/validate/noncleanup-resume.rs b/tests/ui/mir/validate/noncleanup-resume.rs index 5bf6b03c9e94..b2a0e92e0686 100644 --- a/tests/ui/mir/validate/noncleanup-resume.rs +++ b/tests/ui/mir/validate/noncleanup-resume.rs @@ -9,9 +9,9 @@ use core::intrinsics::mir::*; #[custom_mir(dialect = "built")] pub fn main() { - mir!( + mir! { { UnwindResume() } - ) + } } diff --git a/tests/ui/mir/validate/noncleanup-terminate.rs b/tests/ui/mir/validate/noncleanup-terminate.rs index b5bf2604cce6..24cf75e7d8e1 100644 --- a/tests/ui/mir/validate/noncleanup-terminate.rs +++ b/tests/ui/mir/validate/noncleanup-terminate.rs @@ -9,9 +9,9 @@ use core::intrinsics::mir::*; #[custom_mir(dialect = "built")] pub fn main() { - mir!( + mir! { { UnwindTerminate(ReasonAbi) } - ) + } } diff --git a/tests/ui/mismatched_types/binops.stderr b/tests/ui/mismatched_types/binops.stderr index 099c580a0565..92f21a67c370 100644 --- a/tests/ui/mismatched_types/binops.stderr +++ b/tests/ui/mismatched_types/binops.stderr @@ -6,14 +6,14 @@ LL | 1 + Some(1); | = help: the trait `Add>` is not implemented for `{integer}` = help: the following other types implement trait `Add`: - <&'a f128 as Add> - <&'a f16 as Add> - <&'a f32 as Add> - <&'a f64 as Add> - <&'a i128 as Add> - <&'a i16 as Add> - <&'a i32 as Add> - <&'a i64 as Add> + `&'a f128` implements `Add` + `&'a f16` implements `Add` + `&'a f32` implements `Add` + `&'a f64` implements `Add` + `&'a i128` implements `Add` + `&'a i16` implements `Add` + `&'a i32` implements `Add` + `&'a i64` implements `Add` and 56 others error[E0277]: cannot subtract `Option<{integer}>` from `usize` @@ -24,10 +24,10 @@ LL | 2 as usize - Some(1); | = help: the trait `Sub>` is not implemented for `usize` = help: the following other types implement trait `Sub`: - <&'a usize as Sub> - <&usize as Sub<&usize>> - > - + `&'a usize` implements `Sub` + `&usize` implements `Sub<&usize>` + `usize` implements `Sub<&usize>` + `usize` implements `Sub` error[E0277]: cannot multiply `{integer}` by `()` --> $DIR/binops.rs:4:7 @@ -37,14 +37,14 @@ LL | 3 * (); | = help: the trait `Mul<()>` is not implemented for `{integer}` = help: the following other types implement trait `Mul`: - <&'a f128 as Mul> - <&'a f16 as Mul> - <&'a f32 as Mul> - <&'a f64 as Mul> - <&'a i128 as Mul> - <&'a i16 as Mul> - <&'a i32 as Mul> - <&'a i64 as Mul> + `&'a f128` implements `Mul` + `&'a f16` implements `Mul` + `&'a f32` implements `Mul` + `&'a f64` implements `Mul` + `&'a i128` implements `Mul` + `&'a i16` implements `Mul` + `&'a i32` implements `Mul` + `&'a i64` implements `Mul` and 57 others error[E0277]: cannot divide `{integer}` by `&str` @@ -55,14 +55,14 @@ LL | 4 / ""; | = help: the trait `Div<&str>` is not implemented for `{integer}` = help: the following other types implement trait `Div`: - <&'a f128 as Div> - <&'a f16 as Div> - <&'a f32 as Div> - <&'a f64 as Div> - <&'a i128 as Div> - <&'a i16 as Div> - <&'a i32 as Div> - <&'a i64 as Div> + `&'a f128` implements `Div` + `&'a f16` implements `Div` + `&'a f32` implements `Div` + `&'a f64` implements `Div` + `&'a i128` implements `Div` + `&'a i16` implements `Div` + `&'a i32` implements `Div` + `&'a i64` implements `Div` and 62 others error[E0277]: can't compare `{integer}` with `String` diff --git a/tests/crashes/118185-2.rs b/tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.rs similarity index 85% rename from tests/crashes/118185-2.rs rename to tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.rs index c3a29c3a3f54..fd41beecb0a2 100644 --- a/tests/crashes/118185-2.rs +++ b/tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.rs @@ -1,9 +1,8 @@ -//@ known-bug: #118185 - fn main() { let target: Target = create_target(); target.get(0); // correct arguments work - target.get(10.0); // CRASH HERE + target.get(10.0); // (used to crash here) + //~^ ERROR mismatched types } // must be generic diff --git a/tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.stderr b/tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.stderr new file mode 100644 index 000000000000..0f86916fcdae --- /dev/null +++ b/tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/diagnostic-method-lookup-returns-sig-with-fewer-args.rs:4:16 + | +LL | target.get(10.0); // (used to crash here) + | --- ^^^^ expected `i32`, found floating-point number + | | + | arguments to this method are incorrect + | +note: method defined here + --> $DIR/diagnostic-method-lookup-returns-sig-with-fewer-args.rs:22:12 + | +LL | pub fn get(&self, data: i32) { + | ^^^ --------- + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr index 99c9028ae1ef..a657a65c426d 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr @@ -26,7 +26,7 @@ LL | let _ = produces_string().and_then(takes_str_but_wrong_abi); | | | required by a bound introduced by this call | - = help: the trait `FnOnce<(String,)>` is not implemented for fn item `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` + = help: the trait `FnOnce(String)` is not implemented for fn item `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL @@ -38,7 +38,7 @@ LL | let _ = produces_string().and_then(takes_str_but_unsafe); | | | required by a bound introduced by this call | - = help: the trait `FnOnce<(String,)>` is not implemented for fn item `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` + = help: the trait `FnOnce(String)` is not implemented for fn item `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` = note: unsafe function cannot be called generically without an unsafe block note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL diff --git a/tests/ui/namespace/namespace-mix.stderr b/tests/ui/namespace/namespace-mix.stderr index 4eff08ead42c..b80363fe8f84 100644 --- a/tests/ui/namespace/namespace-mix.stderr +++ b/tests/ui/namespace/namespace-mix.stderr @@ -12,7 +12,7 @@ help: a tuple struct with a similar name exists | LL | check(m1::TS); | ~~ -help: consider importing one of these items instead +help: consider importing one of these constants instead | LL + use m2::S; | @@ -40,7 +40,7 @@ help: a tuple struct with a similar name exists | LL | check(xm1::TS); | ~~ -help: consider importing one of these items instead +help: consider importing one of these constants instead | LL + use m2::S; | @@ -66,7 +66,7 @@ help: a tuple variant with a similar name exists | LL | check(m7::TV); | ~~ -help: consider importing one of these items instead +help: consider importing one of these constants instead | LL + use m8::V; | @@ -94,7 +94,7 @@ help: a tuple variant with a similar name exists | LL | check(xm7::TV); | ~~ -help: consider importing one of these items instead +help: consider importing one of these constants instead | LL + use m8::V; | diff --git a/tests/ui/never_type/defaulted-never-note.fallback.stderr b/tests/ui/never_type/defaulted-never-note.fallback.stderr index 92fa9068cfd5..fe9a924f64a0 100644 --- a/tests/ui/never_type/defaulted-never-note.fallback.stderr +++ b/tests/ui/never_type/defaulted-never-note.fallback.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `!: ImplementedForUnitButNotNever` is not satisfied - --> $DIR/defaulted-never-note.rs:30:9 + --> $DIR/defaulted-never-note.rs:32:9 | LL | foo(_x); | --- ^^ the trait `ImplementedForUnitButNotNever` is not implemented for `!` diff --git a/tests/ui/never_type/defaulted-never-note.nofallback.stderr b/tests/ui/never_type/defaulted-never-note.nofallback.stderr new file mode 100644 index 000000000000..d88615186dd6 --- /dev/null +++ b/tests/ui/never_type/defaulted-never-note.nofallback.stderr @@ -0,0 +1,18 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/defaulted-never-note.rs:28:1 + | +LL | fn smeg() { + | ^^^^^^^^^ + | + = warning: 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 #123748 + = help: specify the types explicitly +note: in edition 2024, the requirement `!: ImplementedForUnitButNotNever` will fail + --> $DIR/defaulted-never-note.rs:32:9 + | +LL | foo(_x); + | ^^ + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/never_type/defaulted-never-note.rs b/tests/ui/never_type/defaulted-never-note.rs index f4e5273b33a7..40861e73b393 100644 --- a/tests/ui/never_type/defaulted-never-note.rs +++ b/tests/ui/never_type/defaulted-never-note.rs @@ -26,6 +26,8 @@ fn foo(_t: T) {} //[fallback]~^ NOTE required by this bound in `foo` //[fallback]~| NOTE required by a bound in `foo` fn smeg() { + //[nofallback]~^ warn: this function depends on never type fallback being `()` + //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! let _x = return; foo(_x); //[fallback]~^ ERROR the trait bound diff --git a/tests/ui/never_type/dependency-on-fallback-to-unit.rs b/tests/ui/never_type/dependency-on-fallback-to-unit.rs new file mode 100644 index 000000000000..5448d0be2c6d --- /dev/null +++ b/tests/ui/never_type/dependency-on-fallback-to-unit.rs @@ -0,0 +1,28 @@ +//@ check-pass + +fn main() { + def(); + _ = question_mark(); +} + +fn def() { + //~^ warn: this function depends on never type fallback being `()` + //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + match true { + false => <_>::default(), + true => return, + }; +} + +// +// +fn question_mark() -> Result<(), ()> { + //~^ warn: this function depends on never type fallback being `()` + //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + deserialize()?; + Ok(()) +} + +fn deserialize() -> Result { + Ok(T::default()) +} diff --git a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr new file mode 100644 index 000000000000..ec49137ba795 --- /dev/null +++ b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr @@ -0,0 +1,33 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/dependency-on-fallback-to-unit.rs:8:1 + | +LL | fn def() { + | ^^^^^^^^ + | + = warning: 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 #123748 + = help: specify the types explicitly +note: in edition 2024, the requirement `!: Default` will fail + --> $DIR/dependency-on-fallback-to-unit.rs:12:19 + | +LL | false => <_>::default(), + | ^ + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: this function depends on never type fallback being `()` + --> $DIR/dependency-on-fallback-to-unit.rs:19:1 + | +LL | fn question_mark() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: 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 #123748 + = help: specify the types explicitly +note: in edition 2024, the requirement `!: Default` will fail + --> $DIR/dependency-on-fallback-to-unit.rs:22:5 + | +LL | deserialize()?; + | ^^^^^^^^^^^^^ + +warning: 2 warnings emitted + diff --git a/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr b/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr new file mode 100644 index 000000000000..2a3c5edc2184 --- /dev/null +++ b/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr @@ -0,0 +1,33 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/diverging-fallback-control-flow.rs:30:1 + | +LL | fn assignment() { + | ^^^^^^^^^^^^^^^ + | + = warning: 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 #123748 + = help: specify the types explicitly +note: in edition 2024, the requirement `!: UnitDefault` will fail + --> $DIR/diverging-fallback-control-flow.rs:36:13 + | +LL | x = UnitDefault::default(); + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: this function depends on never type fallback being `()` + --> $DIR/diverging-fallback-control-flow.rs:42:1 + | +LL | fn assignment_rev() { + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: 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 #123748 + = help: specify the types explicitly +note: in edition 2024, the requirement `!: UnitDefault` will fail + --> $DIR/diverging-fallback-control-flow.rs:50:13 + | +LL | x = UnitDefault::default(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: 2 warnings emitted + diff --git a/tests/ui/never_type/diverging-fallback-control-flow.rs b/tests/ui/never_type/diverging-fallback-control-flow.rs index e209a9908850..575e2e9273cf 100644 --- a/tests/ui/never_type/diverging-fallback-control-flow.rs +++ b/tests/ui/never_type/diverging-fallback-control-flow.rs @@ -28,6 +28,8 @@ impl UnitDefault for () { } fn assignment() { + //[nofallback]~^ warn: this function depends on never type fallback being `()` + //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! let x; if true { @@ -38,6 +40,8 @@ fn assignment() { } fn assignment_rev() { + //[nofallback]~^ warn: this function depends on never type fallback being `()` + //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! let x; if true { diff --git a/tests/ui/never_type/diverging-fallback-no-leak.fallback.stderr b/tests/ui/never_type/diverging-fallback-no-leak.fallback.stderr index 01abf2e17f1e..c5463814475c 100644 --- a/tests/ui/never_type/diverging-fallback-no-leak.fallback.stderr +++ b/tests/ui/never_type/diverging-fallback-no-leak.fallback.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `!: Test` is not satisfied - --> $DIR/diverging-fallback-no-leak.rs:17:23 + --> $DIR/diverging-fallback-no-leak.rs:20:23 | LL | unconstrained_arg(return); | ----------------- ^^^^^^ the trait `Test` is not implemented for `!` diff --git a/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr b/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr new file mode 100644 index 000000000000..11245cc7aabf --- /dev/null +++ b/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr @@ -0,0 +1,18 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/diverging-fallback-no-leak.rs:14:1 + | +LL | fn main() { + | ^^^^^^^^^ + | + = warning: 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 #123748 + = help: specify the types explicitly +note: in edition 2024, the requirement `!: Test` will fail + --> $DIR/diverging-fallback-no-leak.rs:20:23 + | +LL | unconstrained_arg(return); + | ^^^^^^ + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/never_type/diverging-fallback-no-leak.rs b/tests/ui/never_type/diverging-fallback-no-leak.rs index 425437da2077..c6d59c7f2738 100644 --- a/tests/ui/never_type/diverging-fallback-no-leak.rs +++ b/tests/ui/never_type/diverging-fallback-no-leak.rs @@ -12,6 +12,9 @@ impl Test for () {} fn unconstrained_arg(_: T) {} fn main() { + //[nofallback]~^ warn: this function depends on never type fallback being `()` + //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + // Here the type variable falls back to `!`, // and hence we get a type error. unconstrained_arg(return); diff --git a/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr b/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr new file mode 100644 index 000000000000..b485c94df4d6 --- /dev/null +++ b/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr @@ -0,0 +1,18 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/diverging-fallback-unconstrained-return.rs:28:1 + | +LL | fn main() { + | ^^^^^^^^^ + | + = warning: 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 #123748 + = help: specify the types explicitly +note: in edition 2024, the requirement `!: UnitReturn` will fail + --> $DIR/diverging-fallback-unconstrained-return.rs:39:23 + | +LL | let _ = if true { unconstrained_return() } else { panic!() }; + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/never_type/diverging-fallback-unconstrained-return.rs b/tests/ui/never_type/diverging-fallback-unconstrained-return.rs index aeb6ee6e26ef..927991db5138 100644 --- a/tests/ui/never_type/diverging-fallback-unconstrained-return.rs +++ b/tests/ui/never_type/diverging-fallback-unconstrained-return.rs @@ -26,6 +26,9 @@ fn unconstrained_return() -> T { } fn main() { + //[nofallback]~^ warn: this function depends on never type fallback being `()` + //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + // In Ye Olde Days, the `T` parameter of `unconstrained_return` // winds up "entangled" with the `!` type that results from // `panic!`, and hence falls back to `()`. This is kind of unfortunate diff --git a/tests/ui/never_type/fallback-closure-ret.nofallback.stderr b/tests/ui/never_type/fallback-closure-ret.nofallback.stderr new file mode 100644 index 000000000000..3fb5536dee7e --- /dev/null +++ b/tests/ui/never_type/fallback-closure-ret.nofallback.stderr @@ -0,0 +1,18 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/fallback-closure-ret.rs:21:1 + | +LL | fn main() { + | ^^^^^^^^^ + | + = warning: 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 #123748 + = help: specify the types explicitly +note: in edition 2024, the requirement `!: Bar` will fail + --> $DIR/fallback-closure-ret.rs:24:5 + | +LL | foo(|| panic!()); + | ^^^^^^^^^^^^^^^^ + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/never_type/fallback-closure-ret.rs b/tests/ui/never_type/fallback-closure-ret.rs index dcf38e03a137..30f9ac54d0bb 100644 --- a/tests/ui/never_type/fallback-closure-ret.rs +++ b/tests/ui/never_type/fallback-closure-ret.rs @@ -12,12 +12,14 @@ #![cfg_attr(fallback, feature(never_type_fallback))] -trait Bar { } -impl Bar for () { } -impl Bar for u32 { } +trait Bar {} +impl Bar for () {} +impl Bar for u32 {} fn foo(_: impl Fn() -> R) {} fn main() { + //[nofallback]~^ warn: this function depends on never type fallback being `()` + //[nofallback]~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! foo(|| panic!()); } diff --git a/tests/ui/never_type/from_infer_breaking_with_unit_fallback.rs b/tests/ui/never_type/from_infer_breaking_with_unit_fallback.rs new file mode 100644 index 000000000000..19a1f9d0e131 --- /dev/null +++ b/tests/ui/never_type/from_infer_breaking_with_unit_fallback.rs @@ -0,0 +1,29 @@ +// issue: rust-lang/rust#66757 +// +// This is a *minimization* of the issue. +// Note that the original version with the `?` does not fail anymore even with fallback to unit, +// see `tests/ui/never_type/question_mark_from_never.rs`. +// +//@ revisions: unit never +//@[never] check-pass +#![allow(internal_features)] +#![feature(rustc_attrs, never_type)] +#![cfg_attr(unit, rustc_never_type_options(fallback = "unit"))] +#![cfg_attr(never, rustc_never_type_options(fallback = "never"))] + +struct E; + +impl From for E { + fn from(_: !) -> E { + E + } +} + +#[allow(unreachable_code)] +fn foo(never: !) { + >::from(never); // Ok + >::from(never); // Should the inference fail? + //[unit]~^ error: the trait bound `E: From<()>` is not satisfied +} + +fn main() {} diff --git a/tests/ui/never_type/from_infer_breaking_with_unit_fallback.unit.stderr b/tests/ui/never_type/from_infer_breaking_with_unit_fallback.unit.stderr new file mode 100644 index 000000000000..3b8913ccf457 --- /dev/null +++ b/tests/ui/never_type/from_infer_breaking_with_unit_fallback.unit.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `E: From<()>` is not satisfied + --> $DIR/from_infer_breaking_with_unit_fallback.rs:25:6 + | +LL | >::from(never); // Should the inference fail? + | ^ the trait `From<()>` is not implemented for `E` + | + = help: the trait `From` is implemented for `E` + = help: for that trait implementation, expected `!`, found `()` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/never_type/impl_trait_fallback.rs b/tests/ui/never_type/impl_trait_fallback.rs index ce06f8f7817f..fbe13dbe2ace 100644 --- a/tests/ui/never_type/impl_trait_fallback.rs +++ b/tests/ui/never_type/impl_trait_fallback.rs @@ -6,5 +6,7 @@ trait T {} impl T for () {} fn should_ret_unit() -> impl T { + //~^ warn: this function depends on never type fallback being `()` + //~| warn: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! panic!() } diff --git a/tests/ui/never_type/impl_trait_fallback.stderr b/tests/ui/never_type/impl_trait_fallback.stderr new file mode 100644 index 000000000000..4496746e018e --- /dev/null +++ b/tests/ui/never_type/impl_trait_fallback.stderr @@ -0,0 +1,18 @@ +warning: this function depends on never type fallback being `()` + --> $DIR/impl_trait_fallback.rs:8:1 + | +LL | fn should_ret_unit() -> impl T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: 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 #123748 + = help: specify the types explicitly +note: in edition 2024, the requirement `!: T` will fail + --> $DIR/impl_trait_fallback.rs:8:25 + | +LL | fn should_ret_unit() -> impl T { + | ^^^^^^ + = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/never_type/issue-13352.stderr b/tests/ui/never_type/issue-13352.stderr index 1b979d934462..7134e4d40a6a 100644 --- a/tests/ui/never_type/issue-13352.stderr +++ b/tests/ui/never_type/issue-13352.stderr @@ -6,10 +6,10 @@ LL | 2_usize + (loop {}); | = help: the trait `Add<()>` is not implemented for `usize` = help: the following other types implement trait `Add`: - <&'a usize as Add> - <&usize as Add<&usize>> - > - + `&'a usize` implements `Add` + `&usize` implements `Add<&usize>` + `usize` implements `Add<&usize>` + `usize` implements `Add` error: aborting due to 1 previous error diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs index 0ae498c134f0..d65bfee843e3 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs @@ -7,6 +7,7 @@ fn _zero() { if false { unsafe { mem::zeroed() } //~^ warn: never type fallback affects this call to an `unsafe` function + //~| warn: this will change its meaning in a future release! } else { return; }; @@ -21,6 +22,7 @@ fn _trans() { struct Zst; core::mem::transmute(Zst) //~^ warn: never type fallback affects this call to an `unsafe` function + //~| warn: this will change its meaning in a future release! } } else { return; @@ -36,6 +38,7 @@ fn _union() { unsafe { Union { a: () }.b } //~^ warn: never type fallback affects this union access + //~| warn: this will change its meaning in a future release! } else { return; }; @@ -45,6 +48,7 @@ fn _deref() { if false { unsafe { *ptr::from_ref(&()).cast() } //~^ warn: never type fallback affects this raw pointer dereference + //~| warn: this will change its meaning in a future release! } else { return; }; @@ -62,6 +66,7 @@ fn _only_generics() { unsafe { internally_create(x) } //~^ warn: never type fallback affects this call to an `unsafe` function + //~| warn: this will change its meaning in a future release! x.unwrap() } else { @@ -73,9 +78,11 @@ fn _stored_function() { if false { let zeroed = mem::zeroed; //~^ warn: never type fallback affects this `unsafe` function + //~| warn: this will change its meaning in a future release! unsafe { zeroed() } //~^ warn: never type fallback affects this call to an `unsafe` function + //~| warn: this will change its meaning in a future release! } else { return; }; @@ -90,6 +97,7 @@ fn _only_generics_stored_function() { let x = None; let f = internally_create; //~^ warn: never type fallback affects this `unsafe` function + //~| warn: this will change its meaning in a future release! unsafe { f(x) } @@ -113,6 +121,7 @@ fn _method() { unsafe { S(marker::PhantomData).create_out_of_thin_air() //~^ warn: never type fallback affects this call to an `unsafe` method + //~| warn: this will change its meaning in a future release! } } else { return; @@ -129,6 +138,7 @@ fn _objc() { () => { match send_message::<_ /* ?0 */>() { //~^ warn: never type fallback affects this call to an `unsafe` function + //~| warn: this will change its meaning in a future release! Ok(x) => x, Err(_) => loop {}, } diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.stderr index 84c9385fd139..fbd92f8f662b 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.stderr +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.stderr @@ -4,75 +4,93 @@ warning: never type fallback affects this call to an `unsafe` function LL | unsafe { mem::zeroed() } | ^^^^^^^^^^^^^ | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 = help: specify the type explicitly = note: `#[warn(never_type_fallback_flowing_into_unsafe)]` on by default warning: never type fallback affects this call to an `unsafe` function - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:22:13 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:23:13 | LL | core::mem::transmute(Zst) | ^^^^^^^^^^^^^^^^^^^^^^^^^ | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 = help: specify the type explicitly warning: never type fallback affects this union access - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:37:18 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:39:18 | LL | unsafe { Union { a: () }.b } | ^^^^^^^^^^^^^^^^^ | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 = help: specify the type explicitly warning: never type fallback affects this raw pointer dereference - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:46:18 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:49:18 | LL | unsafe { *ptr::from_ref(&()).cast() } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 = help: specify the type explicitly warning: never type fallback affects this call to an `unsafe` function - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:63:18 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:67:18 | LL | unsafe { internally_create(x) } | ^^^^^^^^^^^^^^^^^^^^ | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 = help: specify the type explicitly warning: never type fallback affects this call to an `unsafe` function - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:77:18 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:83:18 | LL | unsafe { zeroed() } | ^^^^^^^^ | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 = help: specify the type explicitly warning: never type fallback affects this `unsafe` function - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:74:22 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:79:22 | LL | let zeroed = mem::zeroed; | ^^^^^^^^^^^ | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 = help: specify the type explicitly warning: never type fallback affects this `unsafe` function - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:91:17 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:98:17 | LL | let f = internally_create; | ^^^^^^^^^^^^^^^^^ | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 = help: specify the type explicitly warning: never type fallback affects this call to an `unsafe` method - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:114:13 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:122:13 | LL | S(marker::PhantomData).create_out_of_thin_air() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 = help: specify the type explicitly warning: never type fallback affects this call to an `unsafe` function - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:130:19 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:139:19 | LL | match send_message::<_ /* ?0 */>() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -80,6 +98,8 @@ LL | match send_message::<_ /* ?0 */>() { LL | msg_send!(); | ----------- in this macro invocation | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 = help: specify the type explicitly = note: this warning originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/never_type/question_mark_from_never.rs b/tests/ui/never_type/question_mark_from_never.rs new file mode 100644 index 000000000000..06d2a1926ea9 --- /dev/null +++ b/tests/ui/never_type/question_mark_from_never.rs @@ -0,0 +1,46 @@ +// issue: rust-lang/rust#66757 +// +// See also: `tests/ui/never_type/from_infer_breaking_with_unit_fallback.rs`. +// +//@ revisions: unit never +//@ check-pass +#![allow(internal_features)] +#![feature(rustc_attrs, never_type)] +#![cfg_attr(unit, rustc_never_type_options(fallback = "unit"))] +#![cfg_attr(never, rustc_never_type_options(fallback = "never"))] + +type Infallible = !; + +struct E; + +impl From for E { + fn from(_: Infallible) -> E { + E + } +} + +fn u32_try_from(x: u32) -> Result { + Ok(x) +} + +fn _f() -> Result<(), E> { + // In an old attempt to make `Infallible = !` this caused a problem. + // + // Because at the time the code desugared to + // + // match u32::try_from(1u32) { + // Ok(x) => x, Err(e) => return Err(E::from(e)) + // } + // + // With `Infallible = !`, `e: !` but with fallback to `()`, `e` in `E::from(e)` decayed to `()` + // causing an error. + // + // This does not happen with `Infallible = !`. + // And also does not happen with the newer `?` desugaring that does not pass `e` by value. + // (instead we only pass `Result` (where `Error = !` in this case) which does not get + // the implicit coercion and thus does not decay even with fallback to unit) + u32_try_from(1u32)?; + Ok(()) +} + +fn main() {} diff --git a/tests/ui/nll/issue-52534-1.rs b/tests/ui/nll/issue-52534-1.rs index d9ea3ae42c49..526b81bb2d05 100644 --- a/tests/ui/nll/issue-52534-1.rs +++ b/tests/ui/nll/issue-52534-1.rs @@ -17,14 +17,14 @@ fn foo(x: &u32) -> &u32 { fn baz(x: &u32) -> &&u32 { let x = 22; &&x -//~^ ERROR cannot return value referencing local variable +//~^ ERROR cannot return value referencing local variable `x` //~| ERROR cannot return reference to temporary value } fn foobazbar<'a>(x: u32, y: &'a u32) -> &'a u32 { let x = 22; &x -//~^ ERROR cannot return reference to local variable +//~^ ERROR cannot return reference to local variable `x` } fn foobar<'a>(x: &'a u32) -> &'a u32 { diff --git a/tests/ui/nll/issue-55511.rs b/tests/ui/nll/issue-55511.rs index 7dfa9c7bcdff..78ca89b624a9 100644 --- a/tests/ui/nll/issue-55511.rs +++ b/tests/ui/nll/issue-55511.rs @@ -1,4 +1,3 @@ -#![warn(indirect_structural_match)] use std::cell::Cell; trait Foo<'a> { const C: Option>; diff --git a/tests/ui/nll/issue-55511.stderr b/tests/ui/nll/issue-55511.stderr index ac7cd54df71f..726630307bb2 100644 --- a/tests/ui/nll/issue-55511.stderr +++ b/tests/ui/nll/issue-55511.stderr @@ -1,5 +1,5 @@ error[E0597]: `a` does not live long enough - --> $DIR/issue-55511.rs:13:28 + --> $DIR/issue-55511.rs:12:28 | LL | let a = 22; | - binding `a` declared here diff --git a/tests/ui/nll/ty-outlives/impl-trait-captures.stderr b/tests/ui/nll/ty-outlives/impl-trait-captures.stderr index 3893cdf482e6..48569d1446dd 100644 --- a/tests/ui/nll/ty-outlives/impl-trait-captures.stderr +++ b/tests/ui/nll/ty-outlives/impl-trait-captures.stderr @@ -1,4 +1,4 @@ -error[E0700]: hidden type for `Opaque(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::{opaque#0}), [DefId(0:9 ~ impl_trait_captures[aeb9]::foo::'a)_'a/#0, T, DefId(0:9 ~ impl_trait_captures[aeb9]::foo::'a)_'a/#0])` captures lifetime that does not appear in bounds +error[E0700]: hidden type for `Opaque(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::{opaque#0}), ['a/#0, T, 'a/#0])` captures lifetime that does not appear in bounds --> $DIR/impl-trait-captures.rs:11:5 | LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> { @@ -8,7 +8,7 @@ LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> { LL | x | ^ | -help: to declare that `Opaque(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::{opaque#0}), [DefId(0:9 ~ impl_trait_captures[aeb9]::foo::'a)_'a/#0, T, DefId(0:14 ~ impl_trait_captures[aeb9]::foo::{opaque#0}::'a)_'a/#2])` captures `ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_))`, you can add an explicit `ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_))` lifetime bound +help: to declare that `Opaque(DefId(0:13 ~ impl_trait_captures[aeb9]::foo::{opaque#0}), ['a/#0, T, 'a/#2])` captures `ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_))`, you can add an explicit `ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_))` lifetime bound | LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> + ReLateParam(DefId(0:8 ~ impl_trait_captures[aeb9]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[aeb9]::foo::'_), '_)) { | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/tests/ui/numbers-arithmetic/f16-f128-lit.rs b/tests/ui/numbers-arithmetic/f16-f128-lit.rs index 762436edb166..7fd20d91d824 100644 --- a/tests/ui/numbers-arithmetic/f16-f128-lit.rs +++ b/tests/ui/numbers-arithmetic/f16-f128-lit.rs @@ -1,3 +1,5 @@ +// Make sure negation happens correctly. Also included: +// issue: rust-lang/rust#124583 //@ run-pass #![feature(f16)] @@ -8,9 +10,11 @@ fn main() { assert_eq!((-0.0_f16).to_bits(), 0x8000); assert_eq!(10.0_f16.to_bits(), 0x4900); assert_eq!((-10.0_f16).to_bits(), 0xC900); + assert_eq!((-(-0.0f16)).to_bits(), 0x0000); assert_eq!(0.0_f128.to_bits(), 0x0000_0000_0000_0000_0000_0000_0000_0000); assert_eq!((-0.0_f128).to_bits(), 0x8000_0000_0000_0000_0000_0000_0000_0000); assert_eq!(10.0_f128.to_bits(), 0x4002_4000_0000_0000_0000_0000_0000_0000); assert_eq!((-10.0_f128).to_bits(), 0xC002_4000_0000_0000_0000_0000_0000_0000); + assert_eq!((-(-0.0f128)).to_bits(), 0x0000_0000_0000_0000_0000_0000_0000_0000); } diff --git a/tests/ui/numbers-arithmetic/not-suggest-float-literal.stderr b/tests/ui/numbers-arithmetic/not-suggest-float-literal.stderr index 14685a3f9371..a910666bd56c 100644 --- a/tests/ui/numbers-arithmetic/not-suggest-float-literal.stderr +++ b/tests/ui/numbers-arithmetic/not-suggest-float-literal.stderr @@ -6,10 +6,10 @@ LL | x + 100.0 | = help: the trait `Add<{float}>` is not implemented for `u8` = help: the following other types implement trait `Add`: - <&'a u8 as Add> - <&u8 as Add<&u8>> - > - + `&'a u8` implements `Add` + `&u8` implements `Add<&u8>` + `u8` implements `Add<&u8>` + `u8` implements `Add` error[E0277]: cannot add `&str` to `f64` --> $DIR/not-suggest-float-literal.rs:6:7 @@ -19,10 +19,10 @@ LL | x + "foo" | = help: the trait `Add<&str>` is not implemented for `f64` = help: the following other types implement trait `Add`: - <&'a f64 as Add> - <&f64 as Add<&f64>> - > - + `&'a f64` implements `Add` + `&f64` implements `Add<&f64>` + `f64` implements `Add<&f64>` + `f64` implements `Add` error[E0277]: cannot add `{integer}` to `f64` --> $DIR/not-suggest-float-literal.rs:11:7 @@ -32,10 +32,10 @@ LL | x + y | = help: the trait `Add<{integer}>` is not implemented for `f64` = help: the following other types implement trait `Add`: - <&'a f64 as Add> - <&f64 as Add<&f64>> - > - + `&'a f64` implements `Add` + `&f64` implements `Add<&f64>` + `f64` implements `Add<&f64>` + `f64` implements `Add` error[E0277]: cannot subtract `{float}` from `u8` --> $DIR/not-suggest-float-literal.rs:15:7 @@ -45,10 +45,10 @@ LL | x - 100.0 | = help: the trait `Sub<{float}>` is not implemented for `u8` = help: the following other types implement trait `Sub`: - <&'a u8 as Sub> - <&u8 as Sub<&u8>> - > - + `&'a u8` implements `Sub` + `&u8` implements `Sub<&u8>` + `u8` implements `Sub<&u8>` + `u8` implements `Sub` error[E0277]: cannot subtract `&str` from `f64` --> $DIR/not-suggest-float-literal.rs:19:7 @@ -58,10 +58,10 @@ LL | x - "foo" | = help: the trait `Sub<&str>` is not implemented for `f64` = help: the following other types implement trait `Sub`: - <&'a f64 as Sub> - <&f64 as Sub<&f64>> - > - + `&'a f64` implements `Sub` + `&f64` implements `Sub<&f64>` + `f64` implements `Sub<&f64>` + `f64` implements `Sub` error[E0277]: cannot subtract `{integer}` from `f64` --> $DIR/not-suggest-float-literal.rs:24:7 @@ -71,10 +71,10 @@ LL | x - y | = help: the trait `Sub<{integer}>` is not implemented for `f64` = help: the following other types implement trait `Sub`: - <&'a f64 as Sub> - <&f64 as Sub<&f64>> - > - + `&'a f64` implements `Sub` + `&f64` implements `Sub<&f64>` + `f64` implements `Sub<&f64>` + `f64` implements `Sub` error[E0277]: cannot multiply `u8` by `{float}` --> $DIR/not-suggest-float-literal.rs:28:7 @@ -84,10 +84,10 @@ LL | x * 100.0 | = help: the trait `Mul<{float}>` is not implemented for `u8` = help: the following other types implement trait `Mul`: - <&'a u8 as Mul> - <&u8 as Mul<&u8>> - > - + `&'a u8` implements `Mul` + `&u8` implements `Mul<&u8>` + `u8` implements `Mul<&u8>` + `u8` implements `Mul` error[E0277]: cannot multiply `f64` by `&str` --> $DIR/not-suggest-float-literal.rs:32:7 @@ -97,10 +97,10 @@ LL | x * "foo" | = help: the trait `Mul<&str>` is not implemented for `f64` = help: the following other types implement trait `Mul`: - <&'a f64 as Mul> - <&f64 as Mul<&f64>> - > - + `&'a f64` implements `Mul` + `&f64` implements `Mul<&f64>` + `f64` implements `Mul<&f64>` + `f64` implements `Mul` error[E0277]: cannot multiply `f64` by `{integer}` --> $DIR/not-suggest-float-literal.rs:37:7 @@ -110,10 +110,10 @@ LL | x * y | = help: the trait `Mul<{integer}>` is not implemented for `f64` = help: the following other types implement trait `Mul`: - <&'a f64 as Mul> - <&f64 as Mul<&f64>> - > - + `&'a f64` implements `Mul` + `&f64` implements `Mul<&f64>` + `f64` implements `Mul<&f64>` + `f64` implements `Mul` error[E0277]: cannot divide `u8` by `{float}` --> $DIR/not-suggest-float-literal.rs:41:7 @@ -123,11 +123,11 @@ LL | x / 100.0 | = help: the trait `Div<{float}>` is not implemented for `u8` = help: the following other types implement trait `Div`: - <&'a u8 as Div> - <&u8 as Div<&u8>> - > - >> - + `&'a u8` implements `Div` + `&u8` implements `Div<&u8>` + `u8` implements `Div<&u8>` + `u8` implements `Div>` + `u8` implements `Div` error[E0277]: cannot divide `f64` by `&str` --> $DIR/not-suggest-float-literal.rs:45:7 @@ -137,10 +137,10 @@ LL | x / "foo" | = help: the trait `Div<&str>` is not implemented for `f64` = help: the following other types implement trait `Div`: - <&'a f64 as Div> - <&f64 as Div<&f64>> - > - + `&'a f64` implements `Div` + `&f64` implements `Div<&f64>` + `f64` implements `Div<&f64>` + `f64` implements `Div` error[E0277]: cannot divide `f64` by `{integer}` --> $DIR/not-suggest-float-literal.rs:50:7 @@ -150,10 +150,10 @@ LL | x / y | = help: the trait `Div<{integer}>` is not implemented for `f64` = help: the following other types implement trait `Div`: - <&'a f64 as Div> - <&f64 as Div<&f64>> - > - + `&'a f64` implements `Div` + `&f64` implements `Div<&f64>` + `f64` implements `Div<&f64>` + `f64` implements `Div` error: aborting due to 12 previous errors diff --git a/tests/ui/numbers-arithmetic/suggest-float-literal.stderr b/tests/ui/numbers-arithmetic/suggest-float-literal.stderr index 03779d356371..8585ac485dbb 100644 --- a/tests/ui/numbers-arithmetic/suggest-float-literal.stderr +++ b/tests/ui/numbers-arithmetic/suggest-float-literal.stderr @@ -6,10 +6,10 @@ LL | x + 100 | = help: the trait `Add<{integer}>` is not implemented for `f32` = help: the following other types implement trait `Add`: - <&'a f32 as Add> - <&f32 as Add<&f32>> - > - + `&'a f32` implements `Add` + `&f32` implements `Add<&f32>` + `f32` implements `Add<&f32>` + `f32` implements `Add` help: consider using a floating-point literal by writing it with `.0` | LL | x + 100.0 @@ -23,10 +23,10 @@ LL | x + 100 | = help: the trait `Add<{integer}>` is not implemented for `f64` = help: the following other types implement trait `Add`: - <&'a f64 as Add> - <&f64 as Add<&f64>> - > - + `&'a f64` implements `Add` + `&f64` implements `Add<&f64>` + `f64` implements `Add<&f64>` + `f64` implements `Add` help: consider using a floating-point literal by writing it with `.0` | LL | x + 100.0 @@ -40,10 +40,10 @@ LL | x - 100 | = help: the trait `Sub<{integer}>` is not implemented for `f32` = help: the following other types implement trait `Sub`: - <&'a f32 as Sub> - <&f32 as Sub<&f32>> - > - + `&'a f32` implements `Sub` + `&f32` implements `Sub<&f32>` + `f32` implements `Sub<&f32>` + `f32` implements `Sub` help: consider using a floating-point literal by writing it with `.0` | LL | x - 100.0 @@ -57,10 +57,10 @@ LL | x - 100 | = help: the trait `Sub<{integer}>` is not implemented for `f64` = help: the following other types implement trait `Sub`: - <&'a f64 as Sub> - <&f64 as Sub<&f64>> - > - + `&'a f64` implements `Sub` + `&f64` implements `Sub<&f64>` + `f64` implements `Sub<&f64>` + `f64` implements `Sub` help: consider using a floating-point literal by writing it with `.0` | LL | x - 100.0 @@ -74,10 +74,10 @@ LL | x * 100 | = help: the trait `Mul<{integer}>` is not implemented for `f32` = help: the following other types implement trait `Mul`: - <&'a f32 as Mul> - <&f32 as Mul<&f32>> - > - + `&'a f32` implements `Mul` + `&f32` implements `Mul<&f32>` + `f32` implements `Mul<&f32>` + `f32` implements `Mul` help: consider using a floating-point literal by writing it with `.0` | LL | x * 100.0 @@ -91,10 +91,10 @@ LL | x * 100 | = help: the trait `Mul<{integer}>` is not implemented for `f64` = help: the following other types implement trait `Mul`: - <&'a f64 as Mul> - <&f64 as Mul<&f64>> - > - + `&'a f64` implements `Mul` + `&f64` implements `Mul<&f64>` + `f64` implements `Mul<&f64>` + `f64` implements `Mul` help: consider using a floating-point literal by writing it with `.0` | LL | x * 100.0 @@ -108,10 +108,10 @@ LL | x / 100 | = help: the trait `Div<{integer}>` is not implemented for `f32` = help: the following other types implement trait `Div`: - <&'a f32 as Div> - <&f32 as Div<&f32>> - > - + `&'a f32` implements `Div` + `&f32` implements `Div<&f32>` + `f32` implements `Div<&f32>` + `f32` implements `Div` help: consider using a floating-point literal by writing it with `.0` | LL | x / 100.0 @@ -125,10 +125,10 @@ LL | x / 100 | = help: the trait `Div<{integer}>` is not implemented for `f64` = help: the following other types implement trait `Div`: - <&'a f64 as Div> - <&f64 as Div<&f64>> - > - + `&'a f64` implements `Div` + `&f64` implements `Div<&f64>` + `f64` implements `Div<&f64>` + `f64` implements `Div` help: consider using a floating-point literal by writing it with `.0` | LL | x / 100.0 diff --git a/tests/ui/object-safety/issue-106247.rs b/tests/ui/object-safety/issue-106247.rs index 78bae5681619..20a451a59a16 100644 --- a/tests/ui/object-safety/issue-106247.rs +++ b/tests/ui/object-safety/issue-106247.rs @@ -1,7 +1,5 @@ //@ check-pass -#![deny(where_clauses_object_safety)] - pub trait Trait { fn method(&self) where Self: Sync; } diff --git a/tests/ui/offset-of/offset-of-dst-field.rs b/tests/ui/offset-of/offset-of-dst-field.rs index 016ebfadd142..5ae15f323577 100644 --- a/tests/ui/offset-of/offset-of-dst-field.rs +++ b/tests/ui/offset-of/offset-of-dst-field.rs @@ -49,3 +49,7 @@ fn delta() { fn generic_with_maybe_sized() -> usize { offset_of!(Delta, z) //~ ERROR the size for values of type } + +fn illformed_tuple() { + offset_of!(([u8], u8), 1); //~ ERROR the size for values of type +} diff --git a/tests/ui/offset-of/offset-of-dst-field.stderr b/tests/ui/offset-of/offset-of-dst-field.stderr index adfd16c6f2b9..bcfe796897e0 100644 --- a/tests/ui/offset-of/offset-of-dst-field.stderr +++ b/tests/ui/offset-of/offset-of-dst-field.stderr @@ -81,6 +81,15 @@ LL - fn generic_with_maybe_sized() -> usize { LL + fn generic_with_maybe_sized() -> usize { | -error: aborting due to 8 previous errors +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/offset-of-dst-field.rs:54:16 + | +LL | offset_of!(([u8], u8), 1); + | ^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: only the last element of a tuple may have a dynamically sized type + +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/offset-of/offset-of-slice.rs b/tests/ui/offset-of/offset-of-slice.rs new file mode 100644 index 000000000000..a0fe3198f686 --- /dev/null +++ b/tests/ui/offset-of/offset-of-slice.rs @@ -0,0 +1,26 @@ +//@run-pass +#![feature(offset_of_slice, offset_of_nested)] + +use std::mem::offset_of; + +#[repr(C)] +struct S { + a: u8, + b: (u8, u8), + c: [i32], +} + +#[repr(C)] +struct T { + x: i8, + y: S, +} + +type Tup = (i16, [i32]); + +fn main() { + assert_eq!(offset_of!(S, c), 4); + assert_eq!(offset_of!(T, y), 4); + assert_eq!(offset_of!(T, y.c), 8); + assert_eq!(offset_of!(Tup, 1), 4); +} diff --git a/tests/ui/on-unimplemented/multiple-impls.stderr b/tests/ui/on-unimplemented/multiple-impls.stderr index b50dc251baac..ba4e43ff3594 100644 --- a/tests/ui/on-unimplemented/multiple-impls.stderr +++ b/tests/ui/on-unimplemented/multiple-impls.stderr @@ -8,8 +8,8 @@ LL | Index::index(&[] as &[i32], 2u32); | = help: the trait `Index` is not implemented for `[i32]` = help: the following other types implement trait `Index`: - <[i32] as Index>> - <[i32] as Index>> + `[i32]` implements `Index>` + `[i32]` implements `Index>` error[E0277]: the trait bound `[i32]: Index>` is not satisfied --> $DIR/multiple-impls.rs:36:33 @@ -21,8 +21,8 @@ LL | Index::index(&[] as &[i32], Foo(2u32)); | = help: the trait `Index>` is not implemented for `[i32]` = help: the following other types implement trait `Index`: - <[i32] as Index>> - <[i32] as Index>> + `[i32]` implements `Index>` + `[i32]` implements `Index>` error[E0277]: the trait bound `[i32]: Index>` is not satisfied --> $DIR/multiple-impls.rs:39:33 @@ -34,8 +34,8 @@ LL | Index::index(&[] as &[i32], Bar(2u32)); | = help: the trait `Index>` is not implemented for `[i32]` = help: the following other types implement trait `Index`: - <[i32] as Index>> - <[i32] as Index>> + `[i32]` implements `Index>` + `[i32]` implements `Index>` error[E0277]: the trait bound `[i32]: Index` is not satisfied --> $DIR/multiple-impls.rs:33:5 @@ -45,8 +45,8 @@ LL | Index::index(&[] as &[i32], 2u32); | = help: the trait `Index` is not implemented for `[i32]` = help: the following other types implement trait `Index`: - <[i32] as Index>> - <[i32] as Index>> + `[i32]` implements `Index>` + `[i32]` implements `Index>` error[E0277]: the trait bound `[i32]: Index>` is not satisfied --> $DIR/multiple-impls.rs:36:5 @@ -56,8 +56,8 @@ LL | Index::index(&[] as &[i32], Foo(2u32)); | = help: the trait `Index>` is not implemented for `[i32]` = help: the following other types implement trait `Index`: - <[i32] as Index>> - <[i32] as Index>> + `[i32]` implements `Index>` + `[i32]` implements `Index>` error[E0277]: the trait bound `[i32]: Index>` is not satisfied --> $DIR/multiple-impls.rs:39:5 @@ -67,8 +67,8 @@ LL | Index::index(&[] as &[i32], Bar(2u32)); | = help: the trait `Index>` is not implemented for `[i32]` = help: the following other types implement trait `Index`: - <[i32] as Index>> - <[i32] as Index>> + `[i32]` implements `Index>` + `[i32]` implements `Index>` error: aborting due to 6 previous errors diff --git a/tests/ui/on-unimplemented/slice-index.stderr b/tests/ui/on-unimplemented/slice-index.stderr index f17f3cfce8de..0f8d105abef1 100644 --- a/tests/ui/on-unimplemented/slice-index.stderr +++ b/tests/ui/on-unimplemented/slice-index.stderr @@ -17,8 +17,8 @@ LL | x[..1i32]; | = help: the trait `SliceIndex<[i32]>` is not implemented for `RangeTo`, which is required by `[i32]: Index<_>` = help: the following other types implement trait `SliceIndex`: - as SliceIndex<[T]>> - as SliceIndex> + `RangeTo` implements `SliceIndex<[T]>` + `RangeTo` implements `SliceIndex` = note: required for `[i32]` to implement `Index>` error: aborting due to 2 previous errors diff --git a/tests/ui/on-unimplemented/sum.stderr b/tests/ui/on-unimplemented/sum.stderr index 65bab458cf18..f8e266a87278 100644 --- a/tests/ui/on-unimplemented/sum.stderr +++ b/tests/ui/on-unimplemented/sum.stderr @@ -8,8 +8,8 @@ LL | vec![(), ()].iter().sum::(); | = help: the trait `Sum<&()>` is not implemented for `i32` = help: the following other types implement trait `Sum`: - > - + `i32` implements `Sum<&'a i32>` + `i32` implements `Sum` note: the method call chain might not have had the expected associated types --> $DIR/sum.rs:4:18 | @@ -30,8 +30,8 @@ LL | vec![(), ()].iter().product::(); | = help: the trait `Product<&()>` is not implemented for `i32` = help: the following other types implement trait `Product`: - > - + `i32` implements `Product<&'a i32>` + `i32` implements `Product` note: the method call chain might not have had the expected associated types --> $DIR/sum.rs:7:18 | diff --git a/tests/ui/or-patterns/bindings-runpass-2.rs b/tests/ui/or-patterns/bindings-runpass-2.rs index 657d7f1ed189..a9ae99810840 100644 --- a/tests/ui/or-patterns/bindings-runpass-2.rs +++ b/tests/ui/or-patterns/bindings-runpass-2.rs @@ -26,5 +26,6 @@ fn main() { assert_eq!(or_at(Err(7)), 207); assert_eq!(or_at(Err(8)), 8); assert_eq!(or_at(Err(20)), 220); + assert_eq!(or_at(Err(34)), 134); assert_eq!(or_at(Err(50)), 500); } diff --git a/tests/ui/or-patterns/inner-or-pat.or3.stderr b/tests/ui/or-patterns/inner-or-pat.or3.stderr index 10ec7c202e4d..5c522a97ccef 100644 --- a/tests/ui/or-patterns/inner-or-pat.or3.stderr +++ b/tests/ui/or-patterns/inner-or-pat.or3.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/inner-or-pat.rs:38:54 + --> $DIR/inner-or-pat.rs:36:54 | LL | match x { | - this expression has type `&str` diff --git a/tests/ui/or-patterns/inner-or-pat.or4.stderr b/tests/ui/or-patterns/inner-or-pat.or4.stderr index 97800161d82f..508520c82379 100644 --- a/tests/ui/or-patterns/inner-or-pat.or4.stderr +++ b/tests/ui/or-patterns/inner-or-pat.or4.stderr @@ -1,5 +1,5 @@ error[E0408]: variable `x` is not bound in all patterns - --> $DIR/inner-or-pat.rs:53:37 + --> $DIR/inner-or-pat.rs:51:37 | LL | (x @ "red" | (x @ "blue" | "red")) => { | - ^^^^^ pattern doesn't bind `x` diff --git a/tests/ui/or-patterns/inner-or-pat.rs b/tests/ui/or-patterns/inner-or-pat.rs index ceb0a8b3f796..4d136de00535 100644 --- a/tests/ui/or-patterns/inner-or-pat.rs +++ b/tests/ui/or-patterns/inner-or-pat.rs @@ -1,7 +1,5 @@ -//@ revisions: or1 or2 or3 or4 or5 +//@ revisions: or1 or3 or4 //@ [or1] run-pass -//@ [or2] run-pass -//@ [or5] run-pass #![allow(unreachable_patterns)] #![allow(unused_variables)] diff --git a/tests/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs b/tests/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs index 7d62364a6aee..76dc298a5c85 100644 --- a/tests/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs +++ b/tests/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs @@ -1,21 +1,20 @@ -//@ check-pass +//@ run-pass #![deny(unreachable_patterns)] fn main() { - match (3,42) { - (a,_) | (_,a) if a > 10 => {println!("{}", a)} - _ => () + match (3, 42) { + (a, _) | (_, a) if a > 10 => {} + _ => unreachable!(), } - match Some((3,42)) { - Some((a, _)) | Some((_, a)) if a > 10 => {println!("{}", a)} - _ => () - + match Some((3, 42)) { + Some((a, _)) | Some((_, a)) if a > 10 => {} + _ => unreachable!(), } - match Some((3,42)) { - Some((a, _) | (_, a)) if a > 10 => {println!("{}", a)} - _ => () + match Some((3, 42)) { + Some((a, _) | (_, a)) if a > 10 => {} + _ => unreachable!(), } } diff --git a/tests/ui/or-patterns/search-via-bindings.rs b/tests/ui/or-patterns/search-via-bindings.rs index a760112f1d42..42174bd7cef7 100644 --- a/tests/ui/or-patterns/search-via-bindings.rs +++ b/tests/ui/or-patterns/search-via-bindings.rs @@ -42,6 +42,23 @@ fn search_old_style(target: (bool, bool, bool)) -> u32 { } } +// Check that a dummy or-pattern also leads to running the guard multiple times. +fn search_with_dummy(target: (bool, bool)) -> u32 { + let x = ((false, true), (false, true), ()); + let mut guard_count = 0; + match x { + ((a, _) | (_, a), (b, _) | (_, b), _ | _) + if { + guard_count += 1; + (a, b) == target + } => + { + guard_count + } + _ => unreachable!(), + } +} + fn main() { assert_eq!(search((false, false, false)), 1); assert_eq!(search((false, false, true)), 2); @@ -60,4 +77,9 @@ fn main() { assert_eq!(search_old_style((true, false, true)), 6); assert_eq!(search_old_style((true, true, false)), 7); assert_eq!(search_old_style((true, true, true)), 8); + + assert_eq!(search_with_dummy((false, false)), 1); + assert_eq!(search_with_dummy((false, true)), 3); + assert_eq!(search_with_dummy((true, false)), 5); + assert_eq!(search_with_dummy((true, true)), 7); } diff --git a/tests/ui/or-patterns/simplification_subtleties.rs b/tests/ui/or-patterns/simplification_subtleties.rs new file mode 100644 index 000000000000..a932bd531e6d --- /dev/null +++ b/tests/ui/or-patterns/simplification_subtleties.rs @@ -0,0 +1,11 @@ +//@ run-pass + +#[allow(unreachable_patterns)] +fn main() { + // Test that we don't naively sort the two `2`s together and confuse the failure paths. + match (1, true) { + (1 | 2, false | false) => unreachable!(), + (2, _) => unreachable!(), + _ => {} + } +} diff --git a/tests/ui/panic-handler/panic-handler-std.rs b/tests/ui/panic-handler/panic-handler-std.rs index 91b3997819ce..051828ec8809 100644 --- a/tests/ui/panic-handler/panic-handler-std.rs +++ b/tests/ui/panic-handler/panic-handler-std.rs @@ -1,8 +1,9 @@ //@ normalize-stderr-test "loaded from .*libstd-.*.rlib" -> "loaded from SYSROOT/libstd-*.rlib" //@ error-pattern: found duplicate lang item `panic_impl` +extern crate core; -use std::panic::PanicInfo; +use core::panic::PanicInfo; #[panic_handler] fn panic(info: PanicInfo) -> ! { diff --git a/tests/ui/panic-handler/panic-handler-std.stderr b/tests/ui/panic-handler/panic-handler-std.stderr index 48c216ce27ec..caae16118ef7 100644 --- a/tests/ui/panic-handler/panic-handler-std.stderr +++ b/tests/ui/panic-handler/panic-handler-std.stderr @@ -1,5 +1,5 @@ error[E0152]: found duplicate lang item `panic_impl` - --> $DIR/panic-handler-std.rs:8:1 + --> $DIR/panic-handler-std.rs:9:1 | LL | / fn panic(info: PanicInfo) -> ! { LL | | loop {} diff --git a/tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs b/tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs index 11e4929f12e0..ce6644e67586 100644 --- a/tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs +++ b/tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs @@ -5,12 +5,11 @@ //@ no-prefer-dynamic //@ ignore-wasm32 no processes //@ ignore-sgx no processes -//@ ignore-macos extern crate exit_success_if_unwind; -use std::process::Command; use std::env; +use std::process::Command; fn main() { let mut args = env::args_os(); @@ -25,7 +24,6 @@ fn main() { let mut cmd = Command::new(env::args_os().next().unwrap()); cmd.arg("foo"); - // ARMv6 hanges while printing the backtrace, see #41004 if cfg!(target_arch = "arm") && cfg!(target_env = "gnu") { cmd.env("RUST_BACKTRACE", "0"); diff --git a/tests/ui/panic-runtime/abort.rs b/tests/ui/panic-runtime/abort.rs index 22bd2ecfb00e..caf0243ebdb2 100644 --- a/tests/ui/panic-runtime/abort.rs +++ b/tests/ui/panic-runtime/abort.rs @@ -4,10 +4,9 @@ //@ no-prefer-dynamic //@ ignore-wasm32 no processes //@ ignore-sgx no processes -//@ ignore-macos -use std::process::Command; use std::env; +use std::process::Command; struct Bomb; @@ -23,7 +22,6 @@ fn main() { if let Some(s) = args.next() { if &*s == "foo" { - let _bomb = Bomb; panic!("try to catch me"); diff --git a/tests/ui/panic-runtime/link-to-abort.rs b/tests/ui/panic-runtime/link-to-abort.rs index 2a7052616f2c..a4013f2a6cf7 100644 --- a/tests/ui/panic-runtime/link-to-abort.rs +++ b/tests/ui/panic-runtime/link-to-abort.rs @@ -2,7 +2,6 @@ //@ compile-flags:-C panic=abort //@ no-prefer-dynamic -//@ ignore-macos #![feature(panic_abort)] diff --git a/tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed b/tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed deleted file mode 100644 index aae71ede7713..000000000000 --- a/tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed +++ /dev/null @@ -1,15 +0,0 @@ -//@ run-rustfix -#![feature(stmt_expr_attributes)] -#![allow(unused_assignments, unused_attributes)] - -fn main() { - let mut x = (#[deprecated] 1) + 2; //~ ERROR ambiguous - - (#[deprecated] x) = 4; //~ ERROR ambiguous - - x = (#[deprecated] 5) as i32; //~ ERROR ambiguous - - let _r = (#[deprecated] 1)..6; //~ ERROR ambiguous - - println!("{}", x); -} diff --git a/tests/ui/parser/attribute/attr-binary-expr-ambigous.rs b/tests/ui/parser/attribute/attr-binary-expr-ambigous.rs deleted file mode 100644 index 613e01d743bd..000000000000 --- a/tests/ui/parser/attribute/attr-binary-expr-ambigous.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ run-rustfix -#![feature(stmt_expr_attributes)] -#![allow(unused_assignments, unused_attributes)] - -fn main() { - let mut x = #[deprecated] 1 + 2; //~ ERROR ambiguous - - #[deprecated] x = 4; //~ ERROR ambiguous - - x = #[deprecated] 5 as i32; //~ ERROR ambiguous - - let _r = #[deprecated] 1..6; //~ ERROR ambiguous - - println!("{}", x); -} diff --git a/tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr b/tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr deleted file mode 100644 index 0430570fd49d..000000000000 --- a/tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr +++ /dev/null @@ -1,46 +0,0 @@ -error: ambiguous outer attributes - --> $DIR/attr-binary-expr-ambigous.rs:6:17 - | -LL | let mut x = #[deprecated] 1 + 2; - | ^^^^^^^^^^^^^^^^^^^ - | -help: wrap the expression in parentheses - | -LL | let mut x = (#[deprecated] 1) + 2; - | + + - -error: ambiguous outer attributes - --> $DIR/attr-binary-expr-ambigous.rs:8:5 - | -LL | #[deprecated] x = 4; - | ^^^^^^^^^^^^^^^^^^^ - | -help: wrap the expression in parentheses - | -LL | (#[deprecated] x) = 4; - | + + - -error: ambiguous outer attributes - --> $DIR/attr-binary-expr-ambigous.rs:10:9 - | -LL | x = #[deprecated] 5 as i32; - | ^^^^^^^^^^^^^^^^^^^^^^ - | -help: wrap the expression in parentheses - | -LL | x = (#[deprecated] 5) as i32; - | + + - -error: ambiguous outer attributes - --> $DIR/attr-binary-expr-ambigous.rs:12:14 - | -LL | let _r = #[deprecated] 1..6; - | ^^^^^^^^^^^^^^^^^^ - | -help: wrap the expression in parentheses - | -LL | let _r = (#[deprecated] 1)..6; - | + + - -error: aborting due to 4 previous errors - diff --git a/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.rs b/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.rs index d1950087c4c2..26761a1d2544 100644 --- a/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.rs +++ b/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.rs @@ -28,9 +28,9 @@ fn main() {} #[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; } //~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = #[attr] ..#[attr] 0; } -//~^ ERROR expected expression, found `..` +//~^ ERROR attributes are not allowed on range expressions starting with `..` #[cfg(FALSE)] fn e() { let _ = #[attr] ..; } -//~^ ERROR expected expression, found `..` +//~^ ERROR attributes are not allowed on range expressions starting with `..` #[cfg(FALSE)] fn e() { let _ = #[attr] &#![attr] 0; } //~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; } diff --git a/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.stderr b/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.stderr index e46c591080d4..3593c5182ce9 100644 --- a/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.stderr +++ b/tests/ui/parser/attribute/attr-stmt-expr-attr-bad.stderr @@ -119,17 +119,17 @@ LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; } = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files = note: outer attributes, like `#[test]`, annotate the item following them -error: expected expression, found `..` +error: attributes are not allowed on range expressions starting with `..` --> $DIR/attr-stmt-expr-attr-bad.rs:30:40 | LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..#[attr] 0; } - | ^^ expected expression + | ^^ -error: expected expression, found `..` +error: attributes are not allowed on range expressions starting with `..` --> $DIR/attr-stmt-expr-attr-bad.rs:32:40 | LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..; } - | ^^ expected expression + | ^^ error: an inner attribute is not permitted in this context --> $DIR/attr-stmt-expr-attr-bad.rs:34:41 diff --git a/tests/ui/parser/bad-let-else-statement.rs b/tests/ui/parser/bad-let-else-statement.rs index de41a07568fb..ff6619cbc986 100644 --- a/tests/ui/parser/bad-let-else-statement.rs +++ b/tests/ui/parser/bad-let-else-statement.rs @@ -3,8 +3,7 @@ #![feature(explicit_tail_calls)] fn a() { - let foo = { - //~^ WARN irrefutable `let...else` pattern + let 0 = { 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -22,8 +21,7 @@ fn b() { } fn c() { - let foo = if true { - //~^ WARN irrefutable `let...else` pattern + let 0 = if true { 1 } else { 0 @@ -43,8 +41,7 @@ fn d() { } fn e() { - let foo = match true { - //~^ WARN irrefutable `let...else` pattern + let 0 = match true { true => 1, false => 0 } else { @@ -53,10 +50,12 @@ fn e() { }; } -struct X {a: i32} fn f() { - let foo = X { - //~^ WARN irrefutable `let...else` pattern + struct X { + a: i32, + } + + let X { a: 0 } = X { a: 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -74,8 +73,7 @@ fn g() { } fn h() { - let foo = const { - //~^ WARN irrefutable `let...else` pattern + let 0 = const { 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -84,8 +82,7 @@ fn h() { } fn i() { - let foo = &{ - //~^ WARN irrefutable `let...else` pattern + let 0 = &{ 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -94,8 +91,8 @@ fn i() { } fn j() { - let bar = 0; - let foo = bar = { //~ ERROR: cannot assign twice + let mut bar = 0; + let foo = bar = { //~^ WARN irrefutable `let...else` pattern 1 } else { @@ -105,8 +102,7 @@ fn j() { } fn k() { - let foo = 1 + { - //~^ WARN irrefutable `let...else` pattern + let 0 = 1 + { 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -115,8 +111,8 @@ fn k() { } fn l() { - let foo = 1..{ - //~^ WARN irrefutable `let...else` pattern + const RANGE: std::ops::Range = 0..0; + let RANGE = 1..{ 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -125,8 +121,7 @@ fn l() { } fn m() { - let foo = return { - //~^ WARN irrefutable `let...else` pattern + let 0 = return { () } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -135,8 +130,7 @@ fn m() { } fn n() { - let foo = -{ - //~^ WARN irrefutable `let...else` pattern + let 0 = -{ 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -145,8 +139,7 @@ fn n() { } fn o() -> Result<(), ()> { - let foo = do yeet { - //~^ WARN irrefutable `let...else` pattern + let 0 = do yeet { () } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -155,8 +148,7 @@ fn o() -> Result<(), ()> { } fn p() { - let foo = become { - //~^ WARN irrefutable `let...else` pattern + let 0 = become { () } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -185,22 +177,37 @@ fn r() { fn s() { macro_rules! a { - () => { {} } - //~^ WARN irrefutable `let...else` pattern - //~| WARN irrefutable `let...else` pattern + () => { + { 1 } + }; } macro_rules! b { (1) => { - let x = a!() else { return; }; + let 0 = a!() else { return; }; }; (2) => { - let x = a! {} else { return; }; + let 0 = a! {} else { return; }; //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed }; } - b!(1); b!(2); + b!(1); + b!(2); +} + +fn t() { + macro_rules! primitive { + (8) => { u8 }; + } + + let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! { + //~^ WARN irrefutable `let...else` pattern + 8 + } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; } fn main() {} diff --git a/tests/ui/parser/bad-let-else-statement.stderr b/tests/ui/parser/bad-let-else-statement.stderr index 3f7e176b3e31..0bf6a346dfb1 100644 --- a/tests/ui/parser/bad-let-else-statement.stderr +++ b/tests/ui/parser/bad-let-else-statement.stderr @@ -1,19 +1,18 @@ error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:9:5 + --> $DIR/bad-let-else-statement.rs:8:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = ({ -LL | +LL ~ let 0 = ({ LL | 1 LL ~ }) else { | error: `for...else` loops are not supported - --> $DIR/bad-let-else-statement.rs:18:7 + --> $DIR/bad-let-else-statement.rs:17:7 | LL | let foo = for i in 1..2 { | --- `else` is attached to this loop @@ -28,22 +27,22 @@ LL | | }; = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:30:5 + --> $DIR/bad-let-else-statement.rs:28:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = (if true { -LL | - ... +LL ~ let 0 = (if true { +LL | 1 +LL | } else { LL | 0 LL ~ }) else { | error: `loop...else` loops are not supported - --> $DIR/bad-let-else-statement.rs:39:7 + --> $DIR/bad-let-else-statement.rs:37:7 | LL | let foo = loop { | ---- `else` is attached to this loop @@ -58,36 +57,34 @@ LL | | }; = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:50:5 + --> $DIR/bad-let-else-statement.rs:47:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = (match true { -LL | +LL ~ let 0 = (match true { LL | true => 1, LL | false => 0 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:61:5 + --> $DIR/bad-let-else-statement.rs:60:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = (X { -LL | +LL ~ let X { a: 0 } = (X { LL | a: 1 LL ~ }) else { | error: `while...else` loops are not supported - --> $DIR/bad-let-else-statement.rs:70:7 + --> $DIR/bad-let-else-statement.rs:69:7 | LL | let foo = while false { | ----- `else` is attached to this loop @@ -102,35 +99,33 @@ LL | | }; = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:80:5 + --> $DIR/bad-let-else-statement.rs:78:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = (const { -LL | +LL ~ let 0 = (const { LL | 1 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:90:5 + --> $DIR/bad-let-else-statement.rs:87:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = &({ -LL | +LL ~ let 0 = &({ LL | 1 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:101:5 + --> $DIR/bad-let-else-statement.rs:98:5 | LL | } else { | ^ @@ -144,91 +139,85 @@ LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:111:5 + --> $DIR/bad-let-else-statement.rs:107:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = 1 + ({ -LL | +LL ~ let 0 = 1 + ({ LL | 1 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:121:5 + --> $DIR/bad-let-else-statement.rs:117:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = 1..({ -LL | +LL ~ let RANGE = 1..({ LL | 1 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:131:5 + --> $DIR/bad-let-else-statement.rs:126:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = return ({ -LL | +LL ~ let 0 = return ({ LL | () LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:141:5 + --> $DIR/bad-let-else-statement.rs:135:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = -({ -LL | +LL ~ let 0 = -({ LL | 1 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:151:5 + --> $DIR/bad-let-else-statement.rs:144:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = do yeet ({ -LL | +LL ~ let 0 = do yeet ({ LL | () LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:161:5 + --> $DIR/bad-let-else-statement.rs:153:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = become ({ -LL | +LL ~ let 0 = become ({ LL | () LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:171:5 + --> $DIR/bad-let-else-statement.rs:163:5 | LL | } else { | ^ @@ -242,7 +231,7 @@ LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:181:31 + --> $DIR/bad-let-else-statement.rs:173:31 | LL | let bad = format_args! {""} else { return; }; | ^ @@ -253,24 +242,38 @@ LL | let bad = format_args! ("") else { return; }; | ~ ~ error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:198:25 + --> $DIR/bad-let-else-statement.rs:207:5 | -LL | let x = a! {} else { return; }; +LL | } else { + | ^ + | +help: use parentheses instead of braces for this macro + | +LL ~ let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! ( +LL | +LL | 8 +LL ~ ) else { + | + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:190:25 + | +LL | let 0 = a! {} else { return; }; | ^ ... -LL | b!(1); b!(2); - | ----- in this macro invocation +LL | b!(2); + | ----- in this macro invocation | = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) help: use parentheses instead of braces for this macro | -LL | let x = a! () else { return; }; +LL | let 0 = a! () else { return; }; | ~~ warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:6:5 + --> $DIR/bad-let-else-statement.rs:95:5 | -LL | / let foo = { +LL | / let foo = bar = { LL | | LL | | 1 LL | | } else { @@ -281,169 +284,7 @@ LL | | } else { = note: `#[warn(irrefutable_let_patterns)]` on by default warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:25:5 - | -LL | / let foo = if true { -LL | | -LL | | 1 -LL | | } else { -LL | | 0 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:46:5 - | -LL | / let foo = match true { -LL | | -LL | | true => 1, -LL | | false => 0 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:58:5 - | -LL | / let foo = X { -LL | | -LL | | a: 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:77:5 - | -LL | / let foo = const { -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:87:5 - | -LL | / let foo = &{ -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:98:5 - | -LL | / let foo = bar = { -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -error[E0384]: cannot assign twice to immutable variable `bar` - --> $DIR/bad-let-else-statement.rs:98:15 - | -LL | let bar = 0; - | --- - | | - | first assignment to `bar` - | help: consider making this binding mutable: `mut bar` -LL | let foo = bar = { - | _______________^ -LL | | -LL | | 1 -LL | | } else { - | |_____^ cannot assign twice to immutable variable - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:108:5 - | -LL | / let foo = 1 + { -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:118:5 - | -LL | / let foo = 1..{ -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:128:5 - | -LL | / let foo = return { -LL | | -LL | | () -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:138:5 - | -LL | / let foo = -{ -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:148:5 - | -LL | / let foo = do yeet { -LL | | -LL | | () -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:158:5 - | -LL | / let foo = become { -LL | | -LL | | () -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:168:5 + --> $DIR/bad-let-else-statement.rs:160:5 | LL | / let foo = |x: i32| { LL | | @@ -455,7 +296,7 @@ LL | | } else { = help: consider removing the `else` clause warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:178:5 + --> $DIR/bad-let-else-statement.rs:170:5 | LL | let ok = format_args!("") else { return; }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -464,7 +305,7 @@ LL | let ok = format_args!("") else { return; }; = help: consider removing the `else` clause warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:181:5 + --> $DIR/bad-let-else-statement.rs:173:5 | LL | let bad = format_args! {""} else { return; }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -473,45 +314,16 @@ LL | let bad = format_args! {""} else { return; }; = help: consider removing the `else` clause warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:188:19 + --> $DIR/bad-let-else-statement.rs:204:5 | -LL | () => { {} } - | ___________________^ +LL | / let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! { LL | | -LL | | -LL | | } -... | -LL | | (1) => { -LL | | let x = a!() else { return; }; - | |____________^ -... -LL | b!(1); b!(2); - | ----- in this macro invocation +LL | | 8 +LL | | } else { + | |_____^ | = note: this pattern will always match, so the `else` clause is useless = help: consider removing the `else` clause - = note: this warning originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:188:19 - | -LL | () => { {} } - | ___________________^ -LL | | -LL | | -LL | | } -... | -LL | | (2) => { -LL | | let x = a! {} else { return; }; - | |____________^ -... -LL | b!(1); b!(2); - | ----- in this macro invocation - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - = note: this warning originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) +error: aborting due to 20 previous errors; 5 warnings emitted -error: aborting due to 20 previous errors; 18 warnings emitted - -For more information about this error, try `rustc --explain E0384`. diff --git a/tests/ui/parser/break-in-unlabeled-block-in-macro.stderr b/tests/ui/parser/break-in-unlabeled-block-in-macro.stderr index 9407e8ac0029..2f46cb36750b 100644 --- a/tests/ui/parser/break-in-unlabeled-block-in-macro.stderr +++ b/tests/ui/parser/break-in-unlabeled-block-in-macro.stderr @@ -21,16 +21,21 @@ LL | foo!(()); = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0268]: `break` outside of a loop or labeled block - --> $DIR/break-in-unlabeled-block-in-macro.rs:27:19 + --> $DIR/break-in-unlabeled-block-in-macro.rs:33:17 | -LL | foo!(stmt break ()); - | ^^^^^^^^ cannot `break` outside of a loop or labeled block +LL | foo!(=> break ()); + | ^^^^^^^^ cannot `break` outside of a loop or labeled block + +error[E0268]: `break` outside of a loop or labeled block + --> $DIR/break-in-unlabeled-block-in-macro.rs:38:17 | -help: consider labeling this block to be able to break within it - | -LL ~ 'block: { -LL ~ foo!(stmt break 'block ()); +LL | break () + | ^^^^^^^^ cannot `break` outside of a loop or labeled block +... +LL | bar!() + | ------ in this macro invocation | + = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0268]: `break` outside of a loop or labeled block --> $DIR/break-in-unlabeled-block-in-macro.rs:12:11 @@ -48,21 +53,16 @@ LL | 'block: { break 'block $e; } | +++++++ ++++++ error[E0268]: `break` outside of a loop or labeled block - --> $DIR/break-in-unlabeled-block-in-macro.rs:33:17 + --> $DIR/break-in-unlabeled-block-in-macro.rs:27:19 | -LL | foo!(=> break ()); - | ^^^^^^^^ cannot `break` outside of a loop or labeled block - -error[E0268]: `break` outside of a loop or labeled block - --> $DIR/break-in-unlabeled-block-in-macro.rs:38:17 +LL | foo!(stmt break ()); + | ^^^^^^^^ cannot `break` outside of a loop or labeled block | -LL | break () - | ^^^^^^^^ cannot `break` outside of a loop or labeled block -... -LL | bar!() - | ------ in this macro invocation +help: consider labeling this block to be able to break within it + | +LL ~ 'block: { +LL ~ foo!(stmt break 'block ()); | - = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 6 previous errors diff --git a/tests/ui/parser/duplicate-visibility.rs b/tests/ui/parser/duplicate-visibility.rs index 54955944c7d3..f0ee60873da0 100644 --- a/tests/ui/parser/duplicate-visibility.rs +++ b/tests/ui/parser/duplicate-visibility.rs @@ -2,8 +2,8 @@ fn main() {} extern "C" { //~ NOTE while parsing this item list starting here pub pub fn foo(); - //~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `unsafe`, or `use`, found keyword `pub` - //~| NOTE expected one of 8 possible tokens + //~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` + //~| NOTE expected one of 9 possible tokens //~| HELP there is already a visibility modifier, remove one //~| NOTE explicit visibility first seen here } //~ NOTE the item list ends here diff --git a/tests/ui/parser/duplicate-visibility.stderr b/tests/ui/parser/duplicate-visibility.stderr index b578b1fe26e8..0d1421ee7f4e 100644 --- a/tests/ui/parser/duplicate-visibility.stderr +++ b/tests/ui/parser/duplicate-visibility.stderr @@ -1,4 +1,4 @@ -error: expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `unsafe`, or `use`, found keyword `pub` +error: expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` --> $DIR/duplicate-visibility.rs:4:9 | LL | extern "C" { @@ -6,7 +6,7 @@ LL | extern "C" { LL | pub pub fn foo(); | ^^^ | | - | expected one of 8 possible tokens + | expected one of 9 possible tokens | help: there is already a visibility modifier, remove one ... LL | } diff --git a/tests/ui/parser/fn-header-semantic-fail.rs b/tests/ui/parser/fn-header-semantic-fail.rs index 6ed173b6854d..5907ac052604 100644 --- a/tests/ui/parser/fn-header-semantic-fail.rs +++ b/tests/ui/parser/fn-header-semantic-fail.rs @@ -44,13 +44,14 @@ fn main() { extern "C" { async fn fe1(); //~ ERROR functions in `extern` blocks cannot have qualifiers - unsafe fn fe2(); //~ ERROR functions in `extern` blocks cannot have qualifiers + unsafe fn fe2(); //~ ERROR items in unadorned `extern` blocks cannot have safety qualifiers const fn fe3(); //~ ERROR functions in `extern` blocks cannot have qualifiers extern "C" fn fe4(); //~ ERROR functions in `extern` blocks cannot have qualifiers - const async unsafe extern "C" fn fe5(); //~ ERROR functions in `extern` blocks - //~| ERROR functions in `extern` blocks + const async unsafe extern "C" fn fe5(); + //~^ ERROR functions in `extern` blocks //~| ERROR functions in `extern` blocks //~| ERROR functions in `extern` blocks //~| ERROR functions cannot be both `const` and `async` + //~| ERROR items in unadorned `extern` blocks cannot have safety qualifiers } } diff --git a/tests/ui/parser/fn-header-semantic-fail.stderr b/tests/ui/parser/fn-header-semantic-fail.stderr index cfc54839eb75..abaa6527b0aa 100644 --- a/tests/ui/parser/fn-header-semantic-fail.stderr +++ b/tests/ui/parser/fn-header-semantic-fail.stderr @@ -78,14 +78,14 @@ LL | extern "C" { LL | async fn fe1(); | ^^^^^ help: remove this qualifier -error: functions in `extern` blocks cannot have qualifiers +error: items in unadorned `extern` blocks cannot have safety qualifiers --> $DIR/fn-header-semantic-fail.rs:47:9 | LL | extern "C" { - | ---------- in this `extern` block + | ---------- help: add unsafe to this `extern` block LL | async fn fe1(); LL | unsafe fn fe2(); - | ^^^^^^ help: remove this qualifier + | ^^^^^^^^^^^^^^^^ error: functions in `extern` blocks cannot have qualifiers --> $DIR/fn-header-semantic-fail.rs:48:9 @@ -105,14 +105,14 @@ LL | extern "C" { LL | extern "C" fn fe4(); | ^^^^^^^^^^ help: remove this qualifier -error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:50:21 +error: items in unadorned `extern` blocks cannot have safety qualifiers + --> $DIR/fn-header-semantic-fail.rs:50:9 | LL | extern "C" { - | ---------- in this `extern` block + | ---------- help: add unsafe to this `extern` block ... LL | const async unsafe extern "C" fn fe5(); - | ^^^^^^ help: remove this qualifier + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: functions in `extern` blocks cannot have qualifiers --> $DIR/fn-header-semantic-fail.rs:50:15 diff --git a/tests/ui/parser/issues/fn-no-semicolon-issue-124935-semi-after-item.rs b/tests/ui/parser/issues/fn-no-semicolon-issue-124935-semi-after-item.rs new file mode 100644 index 000000000000..3c0059ba3e3e --- /dev/null +++ b/tests/ui/parser/issues/fn-no-semicolon-issue-124935-semi-after-item.rs @@ -0,0 +1,6 @@ +// Regression test for issue #124935 +// Tests that we do not erroneously emit an error about +// missing main function when the mod starts with a `;` + +; //~ ERROR expected item, found `;` +fn main() { } diff --git a/tests/ui/parser/issues/fn-no-semicolon-issue-124935-semi-after-item.stderr b/tests/ui/parser/issues/fn-no-semicolon-issue-124935-semi-after-item.stderr new file mode 100644 index 000000000000..9776677589f5 --- /dev/null +++ b/tests/ui/parser/issues/fn-no-semicolon-issue-124935-semi-after-item.stderr @@ -0,0 +1,8 @@ +error: expected item, found `;` + --> $DIR/fn-no-semicolon-issue-124935-semi-after-item.rs:5:1 + | +LL | ; + | ^ help: remove this semicolon + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs index f6aa39df27d8..1c28c0632fa8 100644 --- a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs +++ b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs @@ -5,3 +5,4 @@ struct Apple((Apple, Option(Banana ? Citron))); //~| ERROR expected one of `)` or `,`, found `Citron` //~| ERROR cannot find type `Citron` in this scope [E0412] //~| ERROR parenthesized type parameters may only be used with a `Fn` trait [E0214] +//~| ERROR `Apple` has infinite size diff --git a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr index 71d2d7b79758..b0d8b03ae08c 100644 --- a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr +++ b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr @@ -34,7 +34,18 @@ help: use angle brackets instead LL | struct Apple((Apple, Option)); | ~ ~ -error: aborting due to 4 previous errors +error[E0072]: recursive type `Apple` has infinite size + --> $DIR/issue-103748-ICE-wrong-braces.rs:3:1 + | +LL | struct Apple((Apple, Option(Banana ? Citron))); + | ^^^^^^^^^^^^ ----- recursive without indirection + | +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle + | +LL | struct Apple((Box, Option(Banana ? Citron))); + | ++++ + -Some errors have detailed explanations: E0214, E0412. -For more information about an error, try `rustc --explain E0214`. +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0072, E0214, E0412. +For more information about an error, try `rustc --explain E0072`. diff --git a/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr b/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr index 7b896ce14266..66b57c772d54 100644 --- a/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr +++ b/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr @@ -352,7 +352,7 @@ LL | static bar: &[i32] = &(&[1,2,3] as &[i32][0..1]); | ^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` help: add `#![feature(const_trait_impl)]` to the crate attributes to enable | LL + #![feature(const_trait_impl)] diff --git a/tests/ui/parser/issues/issue-49040.rs b/tests/ui/parser/issues/issue-49040.rs index b7a541dd6642..68e7cc9f80eb 100644 --- a/tests/ui/parser/issues/issue-49040.rs +++ b/tests/ui/parser/issues/issue-49040.rs @@ -1,3 +1,3 @@ #![allow(unused_variables)]; //~ ERROR expected item, found `;` -//~^ ERROR `main` function fn foo() {} +//~^ ERROR `main` function diff --git a/tests/ui/parser/issues/issue-49040.stderr b/tests/ui/parser/issues/issue-49040.stderr index 8af7838c7913..11ef5e1aadfd 100644 --- a/tests/ui/parser/issues/issue-49040.stderr +++ b/tests/ui/parser/issues/issue-49040.stderr @@ -5,10 +5,10 @@ LL | #![allow(unused_variables)]; | ^ help: remove this semicolon error[E0601]: `main` function not found in crate `issue_49040` - --> $DIR/issue-49040.rs:1:29 + --> $DIR/issue-49040.rs:2:12 | -LL | #![allow(unused_variables)]; - | ^ consider adding a `main` function to `$DIR/issue-49040.rs` +LL | fn foo() {} + | ^ consider adding a `main` function to `$DIR/issue-49040.rs` error: aborting due to 2 previous errors diff --git a/tests/ui/parser/issues/issue-76437-async.rs b/tests/ui/parser/issues/issue-76437-async.rs index 497e269d634e..3fafaad0277c 100644 --- a/tests/ui/parser/issues/issue-76437-async.rs +++ b/tests/ui/parser/issues/issue-76437-async.rs @@ -2,6 +2,6 @@ mod t { async pub fn t() {} - //~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `pub` + //~^ ERROR expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` //~| HELP visibility `pub` must come before `async` } diff --git a/tests/ui/parser/issues/issue-76437-async.stderr b/tests/ui/parser/issues/issue-76437-async.stderr index 7f2df5c87364..483599135f56 100644 --- a/tests/ui/parser/issues/issue-76437-async.stderr +++ b/tests/ui/parser/issues/issue-76437-async.stderr @@ -1,10 +1,10 @@ -error: expected one of `extern`, `fn`, or `unsafe`, found keyword `pub` +error: expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` --> $DIR/issue-76437-async.rs:4:11 | LL | async pub fn t() {} | ------^^^ | | | - | | expected one of `extern`, `fn`, or `unsafe` + | | expected one of `extern`, `fn`, `safe`, or `unsafe` | help: visibility `pub` must come before `async`: `pub async` error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-76437-const-async.rs b/tests/ui/parser/issues/issue-76437-const-async.rs index 45d53c639332..d8eb6cdecf1b 100644 --- a/tests/ui/parser/issues/issue-76437-const-async.rs +++ b/tests/ui/parser/issues/issue-76437-const-async.rs @@ -2,6 +2,6 @@ mod t { const async pub fn t() {} - //~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `pub` + //~^ ERROR expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` //~| HELP visibility `pub` must come before `const async` } diff --git a/tests/ui/parser/issues/issue-76437-const-async.stderr b/tests/ui/parser/issues/issue-76437-const-async.stderr index a9acccdce182..81fa8a5f557e 100644 --- a/tests/ui/parser/issues/issue-76437-const-async.stderr +++ b/tests/ui/parser/issues/issue-76437-const-async.stderr @@ -1,10 +1,10 @@ -error: expected one of `extern`, `fn`, or `unsafe`, found keyword `pub` +error: expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` --> $DIR/issue-76437-const-async.rs:4:17 | LL | const async pub fn t() {} | ------------^^^ | | | - | | expected one of `extern`, `fn`, or `unsafe` + | | expected one of `extern`, `fn`, `safe`, or `unsafe` | help: visibility `pub` must come before `const async`: `pub const async` error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-76437-const.rs b/tests/ui/parser/issues/issue-76437-const.rs index c3431e3567bf..dad63f137c0f 100644 --- a/tests/ui/parser/issues/issue-76437-const.rs +++ b/tests/ui/parser/issues/issue-76437-const.rs @@ -2,6 +2,6 @@ mod t { const pub fn t() {} - //~^ ERROR expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` + //~^ ERROR expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` //~| HELP visibility `pub` must come before `const` } diff --git a/tests/ui/parser/issues/issue-76437-const.stderr b/tests/ui/parser/issues/issue-76437-const.stderr index 4c36d773d60e..005a27b7c249 100644 --- a/tests/ui/parser/issues/issue-76437-const.stderr +++ b/tests/ui/parser/issues/issue-76437-const.stderr @@ -1,10 +1,10 @@ -error: expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` +error: expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` --> $DIR/issue-76437-const.rs:4:11 | LL | const pub fn t() {} | ------^^^ | | | - | | expected one of `async`, `extern`, `fn`, or `unsafe` + | | expected one of `async`, `extern`, `fn`, `safe`, or `unsafe` | help: visibility `pub` must come before `const`: `pub const` error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-86895.rs b/tests/ui/parser/issues/issue-86895.rs index 4cd09843107d..3e5dc41e2f4d 100644 --- a/tests/ui/parser/issues/issue-86895.rs +++ b/tests/ui/parser/issues/issue-86895.rs @@ -1,3 +1,3 @@ const pub () {} -//~^ ERROR expected one of `async`, `extern`, `fn`, or `unsafe` +//~^ ERROR expected one of `async`, `extern`, `fn`, `safe`, or `unsafe` pub fn main() {} diff --git a/tests/ui/parser/issues/issue-86895.stderr b/tests/ui/parser/issues/issue-86895.stderr index dcde7242d398..14183ee0a5cf 100644 --- a/tests/ui/parser/issues/issue-86895.stderr +++ b/tests/ui/parser/issues/issue-86895.stderr @@ -1,8 +1,8 @@ -error: expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` +error: expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` --> $DIR/issue-86895.rs:1:7 | LL | const pub () {} - | ^^^ expected one of `async`, `extern`, `fn`, or `unsafe` + | ^^^ expected one of `async`, `extern`, `fn`, `safe`, or `unsafe` error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.rs b/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.rs index 694729376ba8..e6235b1e8923 100644 --- a/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.rs +++ b/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.rs @@ -3,8 +3,8 @@ // Test that even when `const` is already present, the proposed fix is to remove the second `const` const async const fn test() {} -//~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `const` -//~| NOTE expected one of `extern`, `fn`, or `unsafe` +//~^ ERROR expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `const` +//~| NOTE expected one of `extern`, `fn`, `safe`, or `unsafe` //~| HELP `const` already used earlier, remove this one //~| NOTE `const` first seen here //~| ERROR functions cannot be both `const` and `async` diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.stderr b/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.stderr index 4c55179ce237..ed2e4d815492 100644 --- a/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.stderr +++ b/tests/ui/parser/issues/issue-87217-keyword-order/const-async-const.stderr @@ -1,10 +1,10 @@ -error: expected one of `extern`, `fn`, or `unsafe`, found keyword `const` +error: expected one of `extern`, `fn`, `safe`, or `unsafe`, found keyword `const` --> $DIR/const-async-const.rs:5:13 | LL | const async const fn test() {} | ^^^^^ | | - | expected one of `extern`, `fn`, or `unsafe` + | expected one of `extern`, `fn`, `safe`, or `unsafe` | help: `const` already used earlier, remove this one | note: `const` first seen here diff --git a/tests/ui/parser/issues/issue-87694-duplicated-pub.rs b/tests/ui/parser/issues/issue-87694-duplicated-pub.rs index e3ea61dc4ada..816c8ff2a9f5 100644 --- a/tests/ui/parser/issues/issue-87694-duplicated-pub.rs +++ b/tests/ui/parser/issues/issue-87694-duplicated-pub.rs @@ -1,5 +1,5 @@ pub const pub fn test() {} -//~^ ERROR expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` -//~| NOTE expected one of `async`, `extern`, `fn`, or `unsafe` +//~^ ERROR expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` +//~| NOTE expected one of `async`, `extern`, `fn`, `safe`, or `unsafe` //~| HELP there is already a visibility modifier, remove one //~| NOTE explicit visibility first seen here diff --git a/tests/ui/parser/issues/issue-87694-duplicated-pub.stderr b/tests/ui/parser/issues/issue-87694-duplicated-pub.stderr index a210238652ab..dd75f32f68ff 100644 --- a/tests/ui/parser/issues/issue-87694-duplicated-pub.stderr +++ b/tests/ui/parser/issues/issue-87694-duplicated-pub.stderr @@ -1,10 +1,10 @@ -error: expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` +error: expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` --> $DIR/issue-87694-duplicated-pub.rs:1:11 | LL | pub const pub fn test() {} | ^^^ | | - | expected one of `async`, `extern`, `fn`, or `unsafe` + | expected one of `async`, `extern`, `fn`, `safe`, or `unsafe` | help: there is already a visibility modifier, remove one | note: explicit visibility first seen here diff --git a/tests/ui/parser/issues/issue-87694-misplaced-pub.rs b/tests/ui/parser/issues/issue-87694-misplaced-pub.rs index 3f824617cade..b5b0bc5b2fc9 100644 --- a/tests/ui/parser/issues/issue-87694-misplaced-pub.rs +++ b/tests/ui/parser/issues/issue-87694-misplaced-pub.rs @@ -1,5 +1,5 @@ const pub fn test() {} -//~^ ERROR expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` -//~| NOTE expected one of `async`, `extern`, `fn`, or `unsafe` +//~^ ERROR expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` +//~| NOTE expected one of `async`, `extern`, `fn`, `safe`, or `unsafe` //~| HELP visibility `pub` must come before `const` //~| SUGGESTION pub const diff --git a/tests/ui/parser/issues/issue-87694-misplaced-pub.stderr b/tests/ui/parser/issues/issue-87694-misplaced-pub.stderr index 6f686a7e5043..d35e09dceaf7 100644 --- a/tests/ui/parser/issues/issue-87694-misplaced-pub.stderr +++ b/tests/ui/parser/issues/issue-87694-misplaced-pub.stderr @@ -1,10 +1,10 @@ -error: expected one of `async`, `extern`, `fn`, or `unsafe`, found keyword `pub` +error: expected one of `async`, `extern`, `fn`, `safe`, or `unsafe`, found keyword `pub` --> $DIR/issue-87694-misplaced-pub.rs:1:7 | LL | const pub fn test() {} | ------^^^ | | | - | | expected one of `async`, `extern`, `fn`, or `unsafe` + | | expected one of `async`, `extern`, `fn`, `safe`, or `unsafe` | help: visibility `pub` must come before `const`: `pub const` error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/missing-main-issue-124935-semi-after-item.rs b/tests/ui/parser/issues/missing-main-issue-124935-semi-after-item.rs new file mode 100644 index 000000000000..3fbac5fae232 --- /dev/null +++ b/tests/ui/parser/issues/missing-main-issue-124935-semi-after-item.rs @@ -0,0 +1,5 @@ +// Regression test for issue #124935 +// Tests that we still emit an error after an item. + +fn main() { } +; //~ ERROR expected item, found `;` diff --git a/tests/ui/parser/issues/missing-main-issue-124935-semi-after-item.stderr b/tests/ui/parser/issues/missing-main-issue-124935-semi-after-item.stderr new file mode 100644 index 000000000000..2d7f540443d2 --- /dev/null +++ b/tests/ui/parser/issues/missing-main-issue-124935-semi-after-item.stderr @@ -0,0 +1,10 @@ +error: expected item, found `;` + --> $DIR/missing-main-issue-124935-semi-after-item.rs:5:1 + | +LL | ; + | ^ help: remove this semicolon + | + = help: function declarations are not followed by a semicolon + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/label-is-actually-char.rs b/tests/ui/parser/label-is-actually-char.rs index 74df898d1910..915ce86d2690 100644 --- a/tests/ui/parser/label-is-actually-char.rs +++ b/tests/ui/parser/label-is-actually-char.rs @@ -1,4 +1,4 @@ -// Note: it's ok to interpret 'a as 'a', but but not ok to interpret 'abc as +// Note: it's ok to interpret 'a as 'a', but not ok to interpret 'abc as // 'abc' because 'abc' is not a valid char literal. fn main() { diff --git a/tests/ui/parser/macro/macro-repeat.stderr b/tests/ui/parser/macro/macro-repeat.stderr index ade2bbf9b0b0..ba722ab6cf98 100644 --- a/tests/ui/parser/macro/macro-repeat.stderr +++ b/tests/ui/parser/macro/macro-repeat.stderr @@ -1,10 +1,10 @@ -error: variable 'v' is still repeating at this depth +error: variable `v` is still repeating at this depth --> $DIR/macro-repeat.rs:3:9 | LL | $v | ^^ -error: variable 'v' is still repeating at this depth +error: variable `v` is still repeating at this depth --> $DIR/macro-repeat.rs:3:9 | LL | $v diff --git a/tests/ui/parser/macro/mbe-dotdotdot-may-not-begin-a-type.rs b/tests/ui/parser/macro/mbe-dotdotdot-may-not-begin-a-type.rs new file mode 100644 index 000000000000..8be99f22d2ee --- /dev/null +++ b/tests/ui/parser/macro/mbe-dotdotdot-may-not-begin-a-type.rs @@ -0,0 +1,11 @@ +// A bare `...` represents `CVarArgs` (`VaListImpl<'_>`) in function argument type +// position without being a proper type syntactically. +// This test ensures that we do not regress certain MBE calls would we ever promote +// `...` to a proper type syntactically. + +//@ check-pass + +macro_rules! ck { ($ty:ty) => { compile_error!(""); }; (...) => {}; } +ck!(...); + +fn main() {} diff --git a/tests/ui/parser/no-const-fn-in-extern-block.rs b/tests/ui/parser/no-const-fn-in-extern-block.rs index d6c578681ccc..3ad9ba006d3d 100644 --- a/tests/ui/parser/no-const-fn-in-extern-block.rs +++ b/tests/ui/parser/no-const-fn-in-extern-block.rs @@ -3,7 +3,7 @@ extern "C" { //~^ ERROR functions in `extern` blocks cannot have qualifiers const unsafe fn bar(); //~^ ERROR functions in `extern` blocks cannot have qualifiers - //~| ERROR functions in `extern` blocks cannot have qualifiers + //~| ERROR items in unadorned `extern` blocks cannot have safety qualifiers } fn main() {} diff --git a/tests/ui/parser/no-const-fn-in-extern-block.stderr b/tests/ui/parser/no-const-fn-in-extern-block.stderr index 948ce669112c..892024ce8934 100644 --- a/tests/ui/parser/no-const-fn-in-extern-block.stderr +++ b/tests/ui/parser/no-const-fn-in-extern-block.stderr @@ -6,14 +6,14 @@ LL | extern "C" { LL | const fn foo(); | ^^^^^ help: remove this qualifier -error: functions in `extern` blocks cannot have qualifiers - --> $DIR/no-const-fn-in-extern-block.rs:4:11 +error: items in unadorned `extern` blocks cannot have safety qualifiers + --> $DIR/no-const-fn-in-extern-block.rs:4:5 | LL | extern "C" { - | ---------- in this `extern` block + | ---------- help: add unsafe to this `extern` block ... LL | const unsafe fn bar(); - | ^^^^^^ help: remove this qualifier + | ^^^^^^^^^^^^^^^^^^^^^^ error: functions in `extern` blocks cannot have qualifiers --> $DIR/no-const-fn-in-extern-block.rs:4:5 diff --git a/tests/ui/parser/recover/recover-range-pats.stderr b/tests/ui/parser/recover/recover-range-pats.stderr index e0ea8ec24dcf..e29b6c1c666b 100644 --- a/tests/ui/parser/recover/recover-range-pats.stderr +++ b/tests/ui/parser/recover/recover-range-pats.stderr @@ -316,9 +316,6 @@ LL | if let X.. .0 = 0 {} | | | | | expected `u8`, found floating-point number | this is of type `u8` - | - = note: expected type `u8` - found type `{float}` error[E0029]: only `char` and numeric types are allowed in range patterns --> $DIR/recover-range-pats.rs:31:12 @@ -353,9 +350,6 @@ LL | if let X..=.0 = 0 {} | | | | | expected `u8`, found floating-point number | this is of type `u8` - | - = note: expected type `u8` - found type `{float}` error[E0029]: only `char` and numeric types are allowed in range patterns --> $DIR/recover-range-pats.rs:52:12 @@ -390,9 +384,6 @@ LL | if let X... .0 = 0 {} | | | | | expected `u8`, found floating-point number | this is of type `u8` - | - = note: expected type `u8` - found type `{float}` error[E0029]: only `char` and numeric types are allowed in range patterns --> $DIR/recover-range-pats.rs:71:12 diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed index a851300a9828..e9c89807fa56 100644 --- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed +++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed @@ -1,7 +1,8 @@ // Regression test for issues #100790 and #106439. //@ run-rustfix -pub struct Example(#[allow(dead_code)] usize) +#[allow(dead_code)] +pub struct Example(usize) where (): Sized; //~^^^ ERROR where clauses are not allowed before tuple struct bodies diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs index 10f435859f15..3bd0f51ec2cb 100644 --- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs +++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs @@ -1,10 +1,11 @@ // Regression test for issues #100790 and #106439. //@ run-rustfix +#[allow(dead_code)] pub struct Example where (): Sized, -(#[allow(dead_code)] usize); +(usize); //~^^^ ERROR where clauses are not allowed before tuple struct bodies struct _Demo diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr index ddbf237e8662..77eafa6bea33 100644 --- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr +++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr @@ -1,23 +1,23 @@ error: where clauses are not allowed before tuple struct bodies - --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:5:1 + --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:6:1 | LL | pub struct Example | ------- while parsing this tuple struct LL | / where LL | | (): Sized, | |______________^ unexpected where clause -LL | (#[allow(dead_code)] usize); - | --------------------------- the struct body +LL | (usize); + | ------- the struct body | help: move the body before the where clause | -LL ~ pub struct Example(#[allow(dead_code)] usize) +LL ~ pub struct Example(usize) LL | where LL ~ (): Sized; | error: where clauses are not allowed before tuple struct bodies - --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:11:1 + --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:12:1 | LL | struct _Demo | ----- while parsing this tuple struct diff --git a/tests/ui/super-fast-paren-parsing.rs b/tests/ui/parser/super-fast-paren-parsing.rs similarity index 82% rename from tests/ui/super-fast-paren-parsing.rs rename to tests/ui/parser/super-fast-paren-parsing.rs index ce7283eee034..5b7cd6fe47d5 100644 --- a/tests/ui/super-fast-paren-parsing.rs +++ b/tests/ui/parser/super-fast-paren-parsing.rs @@ -3,10 +3,7 @@ #![allow(unused_parens)] #![allow(non_upper_case_globals)] #![allow(dead_code)] -//@ exec-env:RUST_MIN_STACK=16000000 -//@ rustc-env:RUST_MIN_STACK=16000000 -// -// Big stack is needed for pretty printing, a little sad... +#![cfg_attr(rustfmt, rustfmt::skip)] static a: isize = ((((((((((((((((((((((((((((((((((((((((((((((((((( diff --git a/tests/ui/parser/trait-object-delimiters.rs b/tests/ui/parser/trait-object-delimiters.rs index d6bc629aa114..8f6221c1b943 100644 --- a/tests/ui/parser/trait-object-delimiters.rs +++ b/tests/ui/parser/trait-object-delimiters.rs @@ -8,7 +8,7 @@ fn foo2(_: &dyn (Drop + AsRef)) {} //~ ERROR incorrect parentheses around t fn foo2_no_space(_: &dyn(Drop + AsRef)) {} //~ ERROR incorrect parentheses around trait bounds fn foo3(_: &dyn {Drop + AsRef}) {} //~ ERROR expected parameter name, found `{` -//~^ ERROR expected one of `!`, `(`, `)`, `*`, `,`, `?`, `async`, `const`, `for`, `~`, lifetime, or path, found `{` +//~^ ERROR expected one of `!`, `(`, `)`, `*`, `,`, `?`, `async`, `const`, `for`, `use`, `~`, lifetime, or path, found `{` //~| ERROR at least one trait is required for an object type fn foo4(_: &dyn >) {} //~ ERROR expected identifier, found `<` diff --git a/tests/ui/parser/trait-object-delimiters.stderr b/tests/ui/parser/trait-object-delimiters.stderr index 2b1f8df991f4..5f175e86545a 100644 --- a/tests/ui/parser/trait-object-delimiters.stderr +++ b/tests/ui/parser/trait-object-delimiters.stderr @@ -34,11 +34,11 @@ error: expected parameter name, found `{` LL | fn foo3(_: &dyn {Drop + AsRef}) {} | ^ expected parameter name -error: expected one of `!`, `(`, `)`, `*`, `,`, `?`, `async`, `const`, `for`, `~`, lifetime, or path, found `{` +error: expected one of `!`, `(`, `)`, `*`, `,`, `?`, `async`, `const`, `for`, `use`, `~`, lifetime, or path, found `{` --> $DIR/trait-object-delimiters.rs:10:17 | LL | fn foo3(_: &dyn {Drop + AsRef}) {} - | -^ expected one of 12 possible tokens + | -^ expected one of 13 possible tokens | | | help: missing `,` diff --git a/tests/ui/parser/unsafe-foreign-mod-2.rs b/tests/ui/parser/unsafe-foreign-mod-2.rs index 77856fb67340..0b63a993c5b9 100644 --- a/tests/ui/parser/unsafe-foreign-mod-2.rs +++ b/tests/ui/parser/unsafe-foreign-mod-2.rs @@ -1,8 +1,8 @@ extern "C" unsafe { - //~^ ERROR expected `{`, found keyword `unsafe` - //~| ERROR extern block cannot be declared unsafe + //~^ ERROR expected `{`, found keyword `unsafe` + //~| ERROR extern block cannot be declared unsafe unsafe fn foo(); - //~^ ERROR functions in `extern` blocks cannot have qualifiers + //~^ ERROR items in unadorned `extern` blocks cannot have safety qualifiers } fn main() {} diff --git a/tests/ui/parser/unsafe-foreign-mod-2.stderr b/tests/ui/parser/unsafe-foreign-mod-2.stderr index fc05184f018f..e59352395ed6 100644 --- a/tests/ui/parser/unsafe-foreign-mod-2.stderr +++ b/tests/ui/parser/unsafe-foreign-mod-2.stderr @@ -10,14 +10,14 @@ error: extern block cannot be declared unsafe LL | extern "C" unsafe { | ^^^^^^ -error: functions in `extern` blocks cannot have qualifiers +error: items in unadorned `extern` blocks cannot have safety qualifiers --> $DIR/unsafe-foreign-mod-2.rs:4:5 | LL | extern "C" unsafe { - | ----------------- in this `extern` block + | ----------------- help: add unsafe to this `extern` block ... LL | unsafe fn foo(); - | ^^^^^^ help: remove this qualifier + | ^^^^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/parser/variadic-ffi-nested-syntactic-fail.rs b/tests/ui/parser/variadic-ffi-nested-syntactic-fail.rs index 4da9ad84bab7..d9d3f9227a9e 100644 --- a/tests/ui/parser/variadic-ffi-nested-syntactic-fail.rs +++ b/tests/ui/parser/variadic-ffi-nested-syntactic-fail.rs @@ -4,6 +4,10 @@ fn f1<'a>(x: u8, y: &'a ...) {} fn f2<'a>(x: u8, y: Vec<&'a ...>) {} //~^ ERROR C-variadic type `...` may not be nested inside another type +// Regression test for issue #125847. +fn f3() where for<> ...: {} +//~^ ERROR C-variadic type `...` may not be nested inside another type + fn main() { let _recovery_witness: () = 0; //~^ ERROR: mismatched types diff --git a/tests/ui/parser/variadic-ffi-nested-syntactic-fail.stderr b/tests/ui/parser/variadic-ffi-nested-syntactic-fail.stderr index 8b9d676a45da..38833d6555b1 100644 --- a/tests/ui/parser/variadic-ffi-nested-syntactic-fail.stderr +++ b/tests/ui/parser/variadic-ffi-nested-syntactic-fail.stderr @@ -10,15 +10,21 @@ error[E0743]: C-variadic type `...` may not be nested inside another type LL | fn f2<'a>(x: u8, y: Vec<&'a ...>) {} | ^^^ +error[E0743]: C-variadic type `...` may not be nested inside another type + --> $DIR/variadic-ffi-nested-syntactic-fail.rs:8:21 + | +LL | fn f3() where for<> ...: {} + | ^^^ + error[E0308]: mismatched types - --> $DIR/variadic-ffi-nested-syntactic-fail.rs:8:33 + --> $DIR/variadic-ffi-nested-syntactic-fail.rs:12:33 | LL | let _recovery_witness: () = 0; | -- ^ expected `()`, found integer | | | expected due to this -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0308, E0743. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index a2d2388ff508..11126dbc65d2 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -8,14 +8,12 @@ fn f1_1(x: isize, ...) {} fn f1_2(...) {} //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic -//~| ERROR C-variadic function must be declared with at least one named argument extern "C" fn f2_1(x: isize, ...) {} //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic extern "C" fn f2_2(...) {} //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic -//~| ERROR C-variadic function must be declared with at least one named argument extern "C" fn f2_3(..., x: isize) {} //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic @@ -26,7 +24,6 @@ extern "C" fn f3_1(x: isize, ...) {} extern "C" fn f3_2(...) {} //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic -//~| ERROR C-variadic function must be declared with at least one named argument extern "C" fn f3_3(..., x: isize) {} //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic @@ -47,8 +44,6 @@ const extern "C" fn f4_3(..., x: isize, ...) {} //~| ERROR `...` must be the last argument of a C-variadic function extern "C" { - fn e_f1(...); - //~^ ERROR C-variadic function must be declared with at least one named argument fn e_f2(..., x: isize); //~^ ERROR `...` must be the last argument of a C-variadic function } @@ -60,7 +55,6 @@ impl X { //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic fn i_f2(...) {} //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic - //~| ERROR C-variadic function must be declared with at least one named argument fn i_f3(..., x: isize, ...) {} //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic //~| ERROR `...` must be the last argument of a C-variadic function @@ -80,10 +74,8 @@ trait T { //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic fn t_f3(...) {} //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic - //~| ERROR C-variadic function must be declared with at least one named argument fn t_f4(...); //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic - //~| ERROR C-variadic function must be declared with at least one named argument fn t_f5(..., x: isize) {} //~^ ERROR only foreign or `unsafe extern "C"` functions may be C-variadic //~| ERROR `...` must be the last argument of a C-variadic function diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 6a65ed79d4fb..f71e3863440f 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -4,12 +4,6 @@ error: only foreign or `unsafe extern "C"` functions may be C-variadic LL | fn f1_1(x: isize, ...) {} | ^^^ -error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:9:9 - | -LL | fn f1_2(...) {} - | ^^^ - error: only foreign or `unsafe extern "C"` functions may be C-variadic --> $DIR/variadic-ffi-semantic-restrictions.rs:9:9 | @@ -17,91 +11,79 @@ LL | fn f1_2(...) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:13:30 + --> $DIR/variadic-ffi-semantic-restrictions.rs:12:30 | LL | extern "C" fn f2_1(x: isize, ...) {} | ^^^ -error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:16:20 - | -LL | extern "C" fn f2_2(...) {} - | ^^^ - error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:16:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:15:20 | LL | extern "C" fn f2_2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:20:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:18:20 | LL | extern "C" fn f2_3(..., x: isize) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:20:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:18:20 | LL | extern "C" fn f2_3(..., x: isize) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:24:30 + --> $DIR/variadic-ffi-semantic-restrictions.rs:22:30 | LL | extern "C" fn f3_1(x: isize, ...) {} | ^^^ -error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 - | -LL | extern "C" fn f3_2(...) {} - | ^^^ - error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:25:20 | LL | extern "C" fn f3_2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:31:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:28:20 | LL | extern "C" fn f3_3(..., x: isize) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:31:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:28:20 | LL | extern "C" fn f3_3(..., x: isize) {} | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:35:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:32:1 | LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:39:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:36:1 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:39:36 + --> $DIR/variadic-ffi-semantic-restrictions.rs:36:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:44:26 + --> $DIR/variadic-ffi-semantic-restrictions.rs:41:26 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:44:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:41:1 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^^^ ^^^ ^^^ C-variadic because of this @@ -110,67 +92,55 @@ LL | const extern "C" fn f4_3(..., x: isize, ...) {} | `const` because of this error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:44:26 + --> $DIR/variadic-ffi-semantic-restrictions.rs:41:26 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^ ^^^ -error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:50:13 - | -LL | fn e_f1(...); - | ^^^ - error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:52:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:47:13 | LL | fn e_f2(..., x: isize); | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:59:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:54:23 | LL | fn i_f1(x: isize, ...) {} | ^^^ -error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:61:13 - | -LL | fn i_f2(...) {} - | ^^^ - error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:61:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:56:13 | LL | fn i_f2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:64:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:58:13 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:64:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:58:13 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:67:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:61:13 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:67:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:61:13 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:70:5 + --> $DIR/variadic-ffi-semantic-restrictions.rs:64:5 | LL | const fn i_f5(x: isize, ...) {} | ^^^^^ ^^^ C-variadic because of this @@ -178,73 +148,61 @@ LL | const fn i_f5(x: isize, ...) {} | `const` because of this error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:70:29 + --> $DIR/variadic-ffi-semantic-restrictions.rs:64:29 | LL | const fn i_f5(x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:77:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:71:23 | LL | fn t_f1(x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:79:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:73:23 | LL | fn t_f2(x: isize, ...); | ^^^ -error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:81:13 +error: only foreign or `unsafe extern "C"` functions may be C-variadic + --> $DIR/variadic-ffi-semantic-restrictions.rs:75:13 | LL | fn t_f3(...) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:81:13 - | -LL | fn t_f3(...) {} - | ^^^ - -error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:84:13 - | -LL | fn t_f4(...); - | ^^^ - -error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:84:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:77:13 | LL | fn t_f4(...); | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:87:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:79:13 | LL | fn t_f5(..., x: isize) {} | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:87:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:79:13 | LL | fn t_f5(..., x: isize) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:90:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:82:13 | LL | fn t_f6(..., x: isize); | ^^^ error: only foreign or `unsafe extern "C"` functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:90:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:82:13 | LL | fn t_f6(..., x: isize); | ^^^ error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:35:43 + --> $DIR/variadic-ffi-semantic-restrictions.rs:32:43 | LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | ^^^ - value is dropped here @@ -252,7 +210,7 @@ LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | the destructor for this type cannot be evaluated in constant functions error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:39:36 + --> $DIR/variadic-ffi-semantic-restrictions.rs:36:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^ - value is dropped here @@ -260,13 +218,13 @@ LL | const extern "C" fn f4_2(x: isize, ...) {} | the destructor for this type cannot be evaluated in constant functions error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:70:29 + --> $DIR/variadic-ffi-semantic-restrictions.rs:64:29 | LL | const fn i_f5(x: isize, ...) {} | ^^^ - value is dropped here | | | the destructor for this type cannot be evaluated in constant functions -error: aborting due to 43 previous errors +error: aborting due to 36 previous errors For more information about this error, try `rustc --explain E0493`. diff --git a/tests/crashes/124004.rs b/tests/ui/pattern/box-pattern-type-mismatch.rs similarity index 74% rename from tests/crashes/124004.rs rename to tests/ui/pattern/box-pattern-type-mismatch.rs index 1fcf07859457..6c9805032563 100644 --- a/tests/crashes/124004.rs +++ b/tests/ui/pattern/box-pattern-type-mismatch.rs @@ -1,10 +1,11 @@ -//@ known-bug: #124004 +//! This test used to ICE #124004 #![feature(box_patterns)] use std::ops::{ Deref }; struct X(dyn Iterator); +//~^ ERROR: use of undeclared lifetime name `'a` impl Deref for X { type Target = isize; diff --git a/tests/ui/pattern/box-pattern-type-mismatch.stderr b/tests/ui/pattern/box-pattern-type-mismatch.stderr new file mode 100644 index 000000000000..14f7dbbd839c --- /dev/null +++ b/tests/ui/pattern/box-pattern-type-mismatch.stderr @@ -0,0 +1,19 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/box-pattern-type-mismatch.rs:7:31 + | +LL | struct X(dyn Iterator); + | ^^ undeclared lifetime + | + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'a` lifetime + | +LL | struct X(dyn for<'a> Iterator); + | +++++++ +help: consider introducing lifetime `'a` here + | +LL | struct X<'a>(dyn Iterator); + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0261`. diff --git a/tests/ui/by-move-pattern-binding.rs b/tests/ui/pattern/by-move-pattern-binding.rs similarity index 100% rename from tests/ui/by-move-pattern-binding.rs rename to tests/ui/pattern/by-move-pattern-binding.rs diff --git a/tests/ui/by-move-pattern-binding.stderr b/tests/ui/pattern/by-move-pattern-binding.stderr similarity index 100% rename from tests/ui/by-move-pattern-binding.stderr rename to tests/ui/pattern/by-move-pattern-binding.stderr diff --git a/tests/ui/pattern/feature-gate-mut_preserve_binding_mode_2024.rs b/tests/ui/pattern/feature-gate-mut_preserve_binding_mode_2024.rs deleted file mode 100644 index 15c542e6bf10..000000000000 --- a/tests/ui/pattern/feature-gate-mut_preserve_binding_mode_2024.rs +++ /dev/null @@ -1,14 +0,0 @@ -//@ edition: 2024 -//@ compile-flags: -Zunstable-options - -struct Foo(u8); - -fn main() { - let Foo(mut a) = &Foo(0); - a = &42; - //~^ ERROR: mismatched types - - let Foo(mut a) = &mut Foo(0); - a = &mut 42; - //~^ ERROR: mismatched types -} diff --git a/tests/ui/pattern/feature-gate-mut_preserve_binding_mode_2024.stderr b/tests/ui/pattern/feature-gate-mut_preserve_binding_mode_2024.stderr deleted file mode 100644 index 6d0a034be21c..000000000000 --- a/tests/ui/pattern/feature-gate-mut_preserve_binding_mode_2024.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/feature-gate-mut_preserve_binding_mode_2024.rs:8:9 - | -LL | let Foo(mut a) = &Foo(0); - | ----- expected due to the type of this binding -LL | a = &42; - | ^^^ expected `u8`, found `&{integer}` - | -help: consider removing the borrow - | -LL - a = &42; -LL + a = 42; - | - -error[E0308]: mismatched types - --> $DIR/feature-gate-mut_preserve_binding_mode_2024.rs:12:9 - | -LL | let Foo(mut a) = &mut Foo(0); - | ----- expected due to the type of this binding -LL | a = &mut 42; - | ^^^^^^^ expected `u8`, found `&mut {integer}` - | -help: consider removing the borrow - | -LL - a = &mut 42; -LL + a = 42; - | - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/fn-in-pat.rs b/tests/ui/pattern/fn-in-pat.rs similarity index 100% rename from tests/ui/fn-in-pat.rs rename to tests/ui/pattern/fn-in-pat.rs diff --git a/tests/ui/fn-in-pat.stderr b/tests/ui/pattern/fn-in-pat.stderr similarity index 100% rename from tests/ui/fn-in-pat.stderr rename to tests/ui/pattern/fn-in-pat.stderr diff --git a/tests/ui/inc-range-pat.rs b/tests/ui/pattern/inc-range-pat.rs similarity index 100% rename from tests/ui/inc-range-pat.rs rename to tests/ui/pattern/inc-range-pat.rs diff --git a/tests/ui/pattern/match_ergonomics_2024.fixed b/tests/ui/pattern/match_ergonomics_2024.fixed index d8dbcb217c04..1ec2b5a214bf 100644 --- a/tests/ui/pattern/match_ergonomics_2024.fixed +++ b/tests/ui/pattern/match_ergonomics_2024.fixed @@ -2,7 +2,7 @@ //@ run-rustfix //@ rustfix-only-machine-applicable //@ aux-build:match_ergonomics_2024_macros.rs -#![feature(mut_preserve_binding_mode_2024, ref_pat_eat_one_layer_2024)] +#![feature(mut_ref, ref_pat_eat_one_layer_2024)] #![allow(incomplete_features, unused)] #![deny(rust_2024_incompatible_pat)] diff --git a/tests/ui/pattern/match_ergonomics_2024.rs b/tests/ui/pattern/match_ergonomics_2024.rs index 38dc0c8bebb2..c9f992c12d4a 100644 --- a/tests/ui/pattern/match_ergonomics_2024.rs +++ b/tests/ui/pattern/match_ergonomics_2024.rs @@ -2,7 +2,7 @@ //@ run-rustfix //@ rustfix-only-machine-applicable //@ aux-build:match_ergonomics_2024_macros.rs -#![feature(mut_preserve_binding_mode_2024, ref_pat_eat_one_layer_2024)] +#![feature(mut_ref, ref_pat_eat_one_layer_2024)] #![allow(incomplete_features, unused)] #![deny(rust_2024_incompatible_pat)] diff --git a/tests/ui/pattern/missing_lifetime.rs b/tests/ui/pattern/missing_lifetime.rs new file mode 100644 index 000000000000..081f667d8f6a --- /dev/null +++ b/tests/ui/pattern/missing_lifetime.rs @@ -0,0 +1,25 @@ +//! This test used to ICE: rust-lang/rust#125914 +//! Instead of actually analyzing the erroneous patterns, +//! we instead stop after typeck where errors are already +//! reported. + +enum AstKind<'ast> { + //~^ ERROR: `'ast` is never used + ExprInt, +} + +enum Foo { + Bar(isize), + Baz, +} + +enum Other { + Other1(Foo), + Other2(AstKind), //~ ERROR: missing lifetime specifier +} + +fn main() { + match Other::Other1(Foo::Baz) { + ::Other::Other2(::Foo::Bar(..)) => {} + } +} diff --git a/tests/ui/pattern/missing_lifetime.stderr b/tests/ui/pattern/missing_lifetime.stderr new file mode 100644 index 000000000000..ec4063fd289a --- /dev/null +++ b/tests/ui/pattern/missing_lifetime.stderr @@ -0,0 +1,25 @@ +error[E0106]: missing lifetime specifier + --> $DIR/missing_lifetime.rs:18:12 + | +LL | Other2(AstKind), + | ^^^^^^^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +LL ~ enum Other<'a> { +LL | Other1(Foo), +LL ~ Other2(AstKind<'a>), + | + +error[E0392]: lifetime parameter `'ast` is never used + --> $DIR/missing_lifetime.rs:6:14 + | +LL | enum AstKind<'ast> { + | ^^^^ unused lifetime parameter + | + = help: consider removing `'ast`, referring to it in a field, or using a marker such as `PhantomData` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0106, E0392. +For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/pattern/mut_preserve_binding_mode_2021.rs b/tests/ui/pattern/mut_preserve_binding_mode_2021.rs index befa49fdc247..282e0ef95d22 100644 --- a/tests/ui/pattern/mut_preserve_binding_mode_2021.rs +++ b/tests/ui/pattern/mut_preserve_binding_mode_2021.rs @@ -1,6 +1,6 @@ //@ edition: 2021 //@ compile-flags: -Zunstable-options -#![feature(mut_preserve_binding_mode_2024)] +#![feature(ref_pat_eat_one_layer_2024)] #![allow(incomplete_features)] struct Foo(u8); diff --git a/tests/ui/pattern/mut_preserve_binding_mode_2024.rs b/tests/ui/pattern/mut_preserve_binding_mode_2024.rs index 5454962e16ce..19aa73573b46 100644 --- a/tests/ui/pattern/mut_preserve_binding_mode_2024.rs +++ b/tests/ui/pattern/mut_preserve_binding_mode_2024.rs @@ -1,7 +1,7 @@ //@ run-pass //@ edition: 2024 //@ compile-flags: -Zunstable-options -#![feature(mut_preserve_binding_mode_2024)] +#![feature(mut_ref, ref_pat_eat_one_layer_2024)] #![allow(incomplete_features, unused)] struct Foo(u8); diff --git a/tests/ui/no-patterns-in-args-2.rs b/tests/ui/pattern/no-patterns-in-args-2.rs similarity index 100% rename from tests/ui/no-patterns-in-args-2.rs rename to tests/ui/pattern/no-patterns-in-args-2.rs diff --git a/tests/ui/no-patterns-in-args-2.stderr b/tests/ui/pattern/no-patterns-in-args-2.stderr similarity index 100% rename from tests/ui/no-patterns-in-args-2.stderr rename to tests/ui/pattern/no-patterns-in-args-2.stderr diff --git a/tests/ui/no-patterns-in-args.rs b/tests/ui/pattern/no-patterns-in-args.rs similarity index 100% rename from tests/ui/no-patterns-in-args.rs rename to tests/ui/pattern/no-patterns-in-args.rs diff --git a/tests/ui/no-patterns-in-args.stderr b/tests/ui/pattern/no-patterns-in-args.stderr similarity index 100% rename from tests/ui/no-patterns-in-args.stderr rename to tests/ui/pattern/no-patterns-in-args.stderr diff --git a/tests/ui/pattern/no_ref_mut_behind_and.rs b/tests/ui/pattern/no_ref_mut_behind_and.rs new file mode 100644 index 000000000000..c18d64904d03 --- /dev/null +++ b/tests/ui/pattern/no_ref_mut_behind_and.rs @@ -0,0 +1,9 @@ +//@ edition: 2021 +//@ run-pass +#![allow(incomplete_features)] +#![feature(ref_pat_eat_one_layer_2024)] + +fn main() { + let &[[x]] = &[&mut [42]]; + let _: &i32 = x; +} diff --git a/tests/ui/pattern/type_mismatch.rs b/tests/ui/pattern/type_mismatch.rs new file mode 100644 index 000000000000..408ff7588471 --- /dev/null +++ b/tests/ui/pattern/type_mismatch.rs @@ -0,0 +1,30 @@ +//! This test used to ICE: rust-lang/rust#109812 +//! Instead of actually analyzing the erroneous patterns, +//! we instead stop after typeck where errors are already +//! reported. + +#![warn(rust_2021_incompatible_closure_captures)] + +enum Either { + One(X), + Two(X), +} + +struct X(Y); + +struct Y; + +fn consume_fnmut(_: impl FnMut()) {} + +fn move_into_fnmut() { + let x = X(Y); + + consume_fnmut(|| { + let Either::Two(ref mut _t) = x; + //~^ ERROR: mismatched types + + let X(mut _t) = x; + }); +} + +fn main() {} diff --git a/tests/ui/pattern/type_mismatch.stderr b/tests/ui/pattern/type_mismatch.stderr new file mode 100644 index 000000000000..b0441b1fadcf --- /dev/null +++ b/tests/ui/pattern/type_mismatch.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + --> $DIR/type_mismatch.rs:23:13 + | +LL | let Either::Two(ref mut _t) = x; + | ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `X` + | | + | expected `X`, found `Either` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr b/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr index 2a1cd3a7aa4b..0b4d99727581 100644 --- a/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr +++ b/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr @@ -9,15 +9,3 @@ LL | if let CONSTANT = &&MyType { error: aborting due to 1 previous error -Future incompatibility report: Future breakage diagnostic: -warning: to use a constant of type `MyType` in a pattern, `MyType` must be annotated with `#[derive(PartialEq)]` - --> $DIR/const-partial_eq-fallback-ice.rs:14:12 - | -LL | if let CONSTANT = &&MyType { - | ^^^^^^^^ - | - = warning: 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 #120362 - = note: the traits must be derived, manual `impl`s are not sufficient - = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details - diff --git a/tests/ui/pattern/usefulness/consts-opaque.rs b/tests/ui/pattern/usefulness/consts-opaque.rs index 3e4617851823..a5743c633085 100644 --- a/tests/ui/pattern/usefulness/consts-opaque.rs +++ b/tests/ui/pattern/usefulness/consts-opaque.rs @@ -93,10 +93,8 @@ fn main() { const QUUX: Quux = quux; match QUUX { - QUUX => {} //~WARN behave unpredictably - //~| previously accepted - QUUX => {} //~WARN behave unpredictably - //~| previously accepted + QUUX => {} //~ERROR behave unpredictably + QUUX => {} //~ERROR behave unpredictably _ => {} } @@ -105,17 +103,14 @@ fn main() { const WRAPQUUX: Wrap = Wrap(quux); match WRAPQUUX { - WRAPQUUX => {} //~WARN behave unpredictably - //~| previously accepted - WRAPQUUX => {} //~WARN behave unpredictably - //~| previously accepted + WRAPQUUX => {} //~ERROR behave unpredictably + WRAPQUUX => {} //~ERROR behave unpredictably Wrap(_) => {} } match WRAPQUUX { Wrap(_) => {} - WRAPQUUX => {} //~WARN behave unpredictably - //~| previously accepted + WRAPQUUX => {} //~ERROR behave unpredictably } match WRAPQUUX { @@ -123,9 +118,7 @@ fn main() { } match WRAPQUUX { - //~^ ERROR: non-exhaustive patterns: `Wrap(_)` not covered - WRAPQUUX => {} //~WARN behave unpredictably - //~| previously accepted + WRAPQUUX => {} //~ERROR behave unpredictably } #[derive(PartialEq, Eq)] @@ -136,11 +129,9 @@ fn main() { const WHOKNOWSQUUX: WhoKnows = WhoKnows::Yay(quux); match WHOKNOWSQUUX { - WHOKNOWSQUUX => {} //~WARN behave unpredictably - //~| previously accepted + WHOKNOWSQUUX => {} //~ERROR behave unpredictably WhoKnows::Yay(_) => {} - WHOKNOWSQUUX => {} //~WARN behave unpredictably - //~| previously accepted + WHOKNOWSQUUX => {} //~ERROR behave unpredictably WhoKnows::Nope => {} } } diff --git a/tests/ui/pattern/usefulness/consts-opaque.stderr b/tests/ui/pattern/usefulness/consts-opaque.stderr index 6a5bd185e39b..d057309e4206 100644 --- a/tests/ui/pattern/usefulness/consts-opaque.stderr +++ b/tests/ui/pattern/usefulness/consts-opaque.stderr @@ -1,75 +1,50 @@ -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. --> $DIR/consts-opaque.rs:96:9 | LL | QUUX => {} | ^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/consts-opaque.rs:98:9 +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/consts-opaque.rs:97:9 | LL | QUUX => {} | ^^^^ - | - = warning: 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 #120362 -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/consts-opaque.rs:108:9 +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/consts-opaque.rs:106:9 | LL | WRAPQUUX => {} | ^^^^^^^^ - | - = warning: 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 #120362 -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/consts-opaque.rs:110:9 +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/consts-opaque.rs:107:9 | LL | WRAPQUUX => {} | ^^^^^^^^ - | - = warning: 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 #120362 -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/consts-opaque.rs:117:9 +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/consts-opaque.rs:113:9 | LL | WRAPQUUX => {} | ^^^^^^^^ - | - = warning: 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 #120362 -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/consts-opaque.rs:127:9 +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/consts-opaque.rs:121:9 | LL | WRAPQUUX => {} | ^^^^^^^^ - | - = warning: 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 #120362 -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/consts-opaque.rs:139:9 +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/consts-opaque.rs:132:9 | LL | WHOKNOWSQUUX => {} | ^^^^^^^^^^^^ - | - = warning: 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 #120362 -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/consts-opaque.rs:142:9 +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/consts-opaque.rs:134:9 | LL | WHOKNOWSQUUX => {} | ^^^^^^^^^^^^ - | - = warning: 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 #120362 error: unreachable pattern --> $DIR/consts-opaque.rs:48:9 @@ -146,111 +121,5 @@ error: unreachable pattern LL | _ => {} // should not be emitting unreachable warning | ^ -error[E0004]: non-exhaustive patterns: `Wrap(_)` not covered - --> $DIR/consts-opaque.rs:125:11 - | -LL | match WRAPQUUX { - | ^^^^^^^^ pattern `Wrap(_)` not covered - | -note: `Wrap usize>` defined here - --> $DIR/consts-opaque.rs:104:12 - | -LL | struct Wrap(T); - | ^^^^ - = note: the matched value is of type `Wrap usize>` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL | WRAPQUUX => {}, Wrap(_) => todo!() - | ++++++++++++++++++++ - -error: aborting due to 10 previous errors; 8 warnings emitted - -For more information about this error, try `rustc --explain E0004`. -Future incompatibility report: Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/consts-opaque.rs:96:9 - | -LL | QUUX => {} - | ^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/consts-opaque.rs:98:9 - | -LL | QUUX => {} - | ^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/consts-opaque.rs:108:9 - | -LL | WRAPQUUX => {} - | ^^^^^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/consts-opaque.rs:110:9 - | -LL | WRAPQUUX => {} - | ^^^^^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/consts-opaque.rs:117:9 - | -LL | WRAPQUUX => {} - | ^^^^^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/consts-opaque.rs:127:9 - | -LL | WRAPQUUX => {} - | ^^^^^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/consts-opaque.rs:139:9 - | -LL | WHOKNOWSQUUX => {} - | ^^^^^^^^^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/consts-opaque.rs:142:9 - | -LL | WHOKNOWSQUUX => {} - | ^^^^^^^^^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default +error: aborting due to 17 previous errors diff --git a/tests/ui/polymorphization/abi_mismatch.rs b/tests/ui/polymorphization/abi_mismatch.rs new file mode 100644 index 000000000000..22c2c162d1c1 --- /dev/null +++ b/tests/ui/polymorphization/abi_mismatch.rs @@ -0,0 +1,20 @@ +//! This test used to ICE: #123917 +//! The reason was that while the AST knows about two fields +//! named `ptr`, only one exists at the layout level, so accessing +//! `_extra_field` would use an oob index +//@ compile-flags: -Zmir-opt-level=5 -Zpolymorphize=on + +struct NonNull(*mut T); + +struct Token { + ptr: *mut T, + ptr: NonNull, + //~^ ERROR: `ptr` is already declared + _extra_field: (), +} + +fn tokenize(item: *mut T) -> Token { + Token { ptr: NonNull(item), _extra_field: () } +} + +fn main() {} diff --git a/tests/ui/polymorphization/abi_mismatch.stderr b/tests/ui/polymorphization/abi_mismatch.stderr new file mode 100644 index 000000000000..e96c737f7773 --- /dev/null +++ b/tests/ui/polymorphization/abi_mismatch.stderr @@ -0,0 +1,11 @@ +error[E0124]: field `ptr` is already declared + --> $DIR/abi_mismatch.rs:11:5 + | +LL | ptr: *mut T, + | ----------- `ptr` first declared here +LL | ptr: NonNull, + | ^^^^^^^^^^^^^^^ field already declared + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0124`. diff --git a/tests/ui/privacy/issue-46209-private-enum-variant-reexport.rs b/tests/ui/privacy/issue-46209-private-enum-variant-reexport.rs index 653dcab57714..796ba4d23320 100644 --- a/tests/ui/privacy/issue-46209-private-enum-variant-reexport.rs +++ b/tests/ui/privacy/issue-46209-private-enum-variant-reexport.rs @@ -6,7 +6,7 @@ mod rank { pub use self::Lieutenant::{JuniorGrade, Full}; //~^ ERROR `JuniorGrade` is private, and cannot be re-exported //~| ERROR `Full` is private, and cannot be re-exported - //~| ERROR unused imports: `Full`, `JuniorGrade` + //~| ERROR unused imports: `Full` and `JuniorGrade` pub use self::PettyOfficer::*; //~^ ERROR glob import doesn't reexport anything //~| ERROR unused import: `self::PettyOfficer::*` diff --git a/tests/ui/privacy/issue-46209-private-enum-variant-reexport.stderr b/tests/ui/privacy/issue-46209-private-enum-variant-reexport.stderr index 93a39edbb823..f1701d547a62 100644 --- a/tests/ui/privacy/issue-46209-private-enum-variant-reexport.stderr +++ b/tests/ui/privacy/issue-46209-private-enum-variant-reexport.stderr @@ -46,7 +46,7 @@ error: unused import: `self::Professor::*` LL | pub use self::Professor::*; | ^^^^^^^^^^^^^^^^^^ -error: unused imports: `Full`, `JuniorGrade` +error: unused imports: `Full` and `JuniorGrade` --> $DIR/issue-46209-private-enum-variant-reexport.rs:6:32 | LL | pub use self::Lieutenant::{JuniorGrade, Full}; diff --git a/tests/crashes/122736.rs b/tests/ui/privacy/no-ice-on-inference-failure.rs similarity index 80% rename from tests/crashes/122736.rs rename to tests/ui/privacy/no-ice-on-inference-failure.rs index 83b60444c2f4..e63b7bff9bc4 100644 --- a/tests/crashes/122736.rs +++ b/tests/ui/privacy/no-ice-on-inference-failure.rs @@ -1,9 +1,9 @@ -//@ known-bug: #122736 fn main_ref() { let array = [(); { let mut x = &0; let mut n = 0; while n < 5 { + //~^ ERROR constant evaluation is taking a long time x = &0; } 0 diff --git a/tests/ui/privacy/no-ice-on-inference-failure.stderr b/tests/ui/privacy/no-ice-on-inference-failure.stderr new file mode 100644 index 000000000000..67476e6e2189 --- /dev/null +++ b/tests/ui/privacy/no-ice-on-inference-failure.stderr @@ -0,0 +1,27 @@ +error: constant evaluation is taking a long time + --> $DIR/no-ice-on-inference-failure.rs:5:9 + | +LL | / while n < 5 { +LL | | +LL | | x = &0; +LL | | } + | |_________^ + | + = note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval. + If your compilation actually takes a long time, you can safely allow the lint. +help: the constant being evaluated + --> $DIR/no-ice-on-inference-failure.rs:2:22 + | +LL | let array = [(); { + | ______________________^ +LL | | let mut x = &0; +LL | | let mut n = 0; +LL | | while n < 5 { +... | +LL | | 0 +LL | | }]; + | |_____^ + = note: `#[deny(long_running_const_eval)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_priv_dep.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_priv_dep.rs new file mode 100644 index 000000000000..ed76815a6ac8 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_priv_dep.rs @@ -0,0 +1,9 @@ +//@ aux-crate:shared=shared.rs + +extern crate shared; + +pub use shared::Shared; + +pub struct SharedInType { + pub f: Shared +} diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_pub_dep.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_pub_dep.rs new file mode 100644 index 000000000000..ed76815a6ac8 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/diamond_pub_dep.rs @@ -0,0 +1,9 @@ +//@ aux-crate:shared=shared.rs + +extern crate shared; + +pub use shared::Shared; + +pub struct SharedInType { + pub f: Shared +} diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/indirect1.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/indirect1.rs new file mode 100644 index 000000000000..0e4a73c7fc01 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/indirect1.rs @@ -0,0 +1,4 @@ +//@ aux-crate:priv:indirect2=indirect2.rs +//@ compile-flags: -Zunstable-options + +extern crate indirect2; diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/indirect2.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/indirect2.rs new file mode 100644 index 000000000000..5f6b289eb141 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/indirect2.rs @@ -0,0 +1,4 @@ +//@ aux-crate:shared=shared.rs + +// This is public. +extern crate shared; diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs new file mode 100644 index 000000000000..9e2aa898afe8 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs @@ -0,0 +1,22 @@ +//@ force-host +//@ no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro] +pub fn fn_like(input: TokenStream) -> TokenStream { + "".parse().unwrap() +} + +#[proc_macro_derive(PmDerive)] +pub fn pm_derive(item: TokenStream) -> TokenStream { + "".parse().unwrap() +} + +#[proc_macro_attribute] +pub fn pm_attr(attr: TokenStream, item: TokenStream) -> TokenStream { + "".parse().unwrap() +} diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/priv_dep.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/priv_dep.rs index e7afeb84fb4f..4eeecdc05697 100644 --- a/tests/ui/privacy/pub-priv-dep/auxiliary/priv_dep.rs +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/priv_dep.rs @@ -1,2 +1,11 @@ pub struct OtherType; pub trait OtherTrait {} + +#[macro_export] +macro_rules! m { + () => {}; +} + +pub enum E { + V1 +} diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/reexport.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/reexport.rs new file mode 100644 index 000000000000..0655e3ae2cfd --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/reexport.rs @@ -0,0 +1,5 @@ +//@ aux-crate:shared=shared.rs + +extern crate shared; + +pub use shared::Shared; diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/shared.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/shared.rs new file mode 100644 index 000000000000..efc4daa7befb --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/shared.rs @@ -0,0 +1 @@ +pub struct Shared; diff --git a/tests/ui/privacy/pub-priv-dep/diamond_deps.rs b/tests/ui/privacy/pub-priv-dep/diamond_deps.rs new file mode 100644 index 000000000000..0e1f6f36bc8c --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/diamond_deps.rs @@ -0,0 +1,48 @@ +//@ aux-crate:priv:diamond_priv_dep=diamond_priv_dep.rs +//@ aux-crate:diamond_pub_dep=diamond_pub_dep.rs +//@ compile-flags: -Zunstable-options + +// A diamond dependency: +// +// diamond_reepxort +// /\ +// (public) / \ (PRIVATE) +// / \ +// diamond_pub_dep diamond_priv_dep +// \ / +// (public) \ / (public) +// \/ +// shared +// +// Where the pub and private crates reexport something from the shared crate. +// +// Checks the behavior when the same shared item appears in the public API, +// depending on whether it comes from the public side or the private side. +// +// NOTE: compiletest does not support deduplicating shared dependencies. +// However, it should work well enough for this test, the only downside is +// that diamond_shared gets built twice. + +#![crate_type = "lib"] +#![deny(exported_private_dependencies)] + +extern crate diamond_priv_dep; +extern crate diamond_pub_dep; + +// FIXME: This should trigger. +pub fn leaks_priv() -> diamond_priv_dep::Shared { + diamond_priv_dep::Shared +} + +pub fn leaks_pub() -> diamond_pub_dep::Shared { + diamond_pub_dep::Shared +} + +pub struct PrivInStruct { + pub f: diamond_priv_dep::SharedInType +//~^ ERROR type `diamond_priv_dep::SharedInType` from private dependency 'diamond_priv_dep' in public interface +} + +pub struct PubInStruct { + pub f: diamond_pub_dep::SharedInType +} diff --git a/tests/ui/privacy/pub-priv-dep/diamond_deps.stderr b/tests/ui/privacy/pub-priv-dep/diamond_deps.stderr new file mode 100644 index 000000000000..8a6d35a747b6 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/diamond_deps.stderr @@ -0,0 +1,14 @@ +error: type `diamond_priv_dep::SharedInType` from private dependency 'diamond_priv_dep' in public interface + --> $DIR/diamond_deps.rs:42:5 + | +LL | pub f: diamond_priv_dep::SharedInType + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/diamond_deps.rs:27:9 + | +LL | #![deny(exported_private_dependencies)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs index f26dbb47ba5e..112eaf528be2 100644 --- a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs +++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs @@ -1,12 +1,20 @@ //@ aux-crate:priv:priv_dep=priv_dep.rs //@ aux-build:pub_dep.rs +//@ aux-crate:priv:pm=pm.rs //@ compile-flags: -Zunstable-options + +// Basic behavior check of exported_private_dependencies from either a public +// dependency or a private one. + #![deny(exported_private_dependencies)] // This crate is a private dependency -extern crate priv_dep; +// FIXME: This should trigger. +pub extern crate priv_dep; // This crate is a public dependency extern crate pub_dep; +// This crate is a private dependency +extern crate pm; use priv_dep::{OtherTrait, OtherType}; use pub_dep::PubType; @@ -25,7 +33,10 @@ pub struct PublicType { } impl PublicType { - pub fn pub_fn(param: OtherType) {} + pub fn pub_fn_param(param: OtherType) {} + //~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface + + pub fn pub_fn_return() -> OtherType { OtherType } //~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface fn priv_fn(param: OtherType) {} @@ -36,9 +47,61 @@ pub trait MyPubTrait { } //~^^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface +pub trait WithSuperTrait: OtherTrait {} +//~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface + +pub trait PubLocalTraitWithAssoc { + type X; +} + +pub struct PrivateAssoc; +impl PubLocalTraitWithAssoc for PrivateAssoc { + type X = OtherType; +//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface +} + +pub fn in_bounds(x: T) { unimplemented!() } +//~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface + +pub fn private_in_generic() -> std::num::Saturating { unimplemented!() } +//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface + +pub static STATIC: OtherType = OtherType; +//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface + +pub const CONST: OtherType = OtherType; +//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface + +pub type Alias = OtherType; +//~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface + +pub struct PublicWithPrivateImpl; + +// FIXME: This should trigger. +// See https://github.com/rust-lang/rust/issues/71043 +impl OtherTrait for PublicWithPrivateImpl {} + +pub trait PubTraitOnPrivate {} + +// FIXME: This should trigger. +// See https://github.com/rust-lang/rust/issues/71043 +impl PubTraitOnPrivate for OtherType {} + pub struct AllowedPrivType { #[allow(exported_private_dependencies)] pub allowed: OtherType, } +// FIXME: This should trigger. +pub use priv_dep::m; +// FIXME: This should trigger. +pub use pm::fn_like; +// FIXME: This should trigger. +pub use pm::PmDerive; +// FIXME: This should trigger. +pub use pm::pm_attr; + +// FIXME: This should trigger. +pub use priv_dep::E::V1; + fn main() {} diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr index e62a440d8f56..53d461a5774a 100644 --- a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr +++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr @@ -1,26 +1,74 @@ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:21:5 + --> $DIR/pub-priv1.rs:29:5 | LL | pub field: OtherType, | ^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/pub-priv1.rs:4:9 + --> $DIR/pub-priv1.rs:9:9 | LL | #![deny(exported_private_dependencies)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:28:5 + --> $DIR/pub-priv1.rs:36:5 | -LL | pub fn pub_fn(param: OtherType) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub fn pub_fn_param(param: OtherType) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `OtherType` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:39:5 + | +LL | pub fn pub_fn_return() -> OtherType { OtherType } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: trait `OtherTrait` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:35:5 + --> $DIR/pub-priv1.rs:46:5 | LL | type Foo: OtherTrait; | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: trait `OtherTrait` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:50:1 + | +LL | pub trait WithSuperTrait: OtherTrait {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `OtherType` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:59:5 + | +LL | type X = OtherType; + | ^^^^^^ + +error: trait `OtherTrait` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:63:1 + | +LL | pub fn in_bounds(x: T) { unimplemented!() } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `OtherType` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:66:1 + | +LL | pub fn private_in_generic() -> std::num::Saturating { unimplemented!() } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `OtherType` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:69:1 + | +LL | pub static STATIC: OtherType = OtherType; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `OtherType` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:72:1 + | +LL | pub const CONST: OtherType = OtherType; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `OtherType` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:75:1 + | +LL | pub type Alias = OtherType; + | ^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/privacy/pub-priv-dep/reexport_from_priv.rs b/tests/ui/privacy/pub-priv-dep/reexport_from_priv.rs new file mode 100644 index 000000000000..3c6e9825e728 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/reexport_from_priv.rs @@ -0,0 +1,15 @@ +//@ aux-crate:priv:reexport=reexport.rs +//@ compile-flags: -Zunstable-options +//@ check-pass + +// Checks the behavior of a reexported item from a private dependency. + +#![crate_type = "lib"] +#![deny(exported_private_dependencies)] + +extern crate reexport; + +// FIXME: This should trigger. +pub fn leaks_priv() -> reexport::Shared { + reexport::Shared +} diff --git a/tests/ui/privacy/pub-priv-dep/shared_both_private.rs b/tests/ui/privacy/pub-priv-dep/shared_both_private.rs new file mode 100644 index 000000000000..20a4b85c01e8 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/shared_both_private.rs @@ -0,0 +1,32 @@ +//@ aux-crate:priv:shared=shared.rs +//@ aux-crate:reexport=reexport.rs +//@ compile-flags: -Zunstable-options +//@ check-pass + +// A shared dependency, where a private dependency reexports a public dependency. +// +// shared_both_private +// /\ +// (PRIVATE) / | (PRIVATE) +// / | +// reexport | +// \ | +// (public) \ / +// \/ +// shared + +#![crate_type = "lib"] +#![deny(exported_private_dependencies)] + +extern crate shared; +extern crate reexport; + +// FIXME: This should trigger. +pub fn leaks_priv() -> shared::Shared { + shared::Shared +} + +// FIXME: This should trigger. +pub fn leaks_priv_reexport() -> reexport::Shared { + reexport::Shared +} diff --git a/tests/ui/privacy/pub-priv-dep/shared_direct_private.rs b/tests/ui/privacy/pub-priv-dep/shared_direct_private.rs new file mode 100644 index 000000000000..b329a7acb583 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/shared_direct_private.rs @@ -0,0 +1,39 @@ +//@ aux-crate:priv:shared=shared.rs +//@ aux-crate:reexport=reexport.rs +//@ compile-flags: -Zunstable-options +//@ check-pass + +// A shared dependency, where the public side reexports the same item as a +// direct private dependency. +// +// shared_direct_private +// /\ +// (public) / | (PRIVATE) +// / | +// reexport | +// \ | +// (public) \ / +// \/ +// shared +// + +#![crate_type = "lib"] +#![deny(exported_private_dependencies)] + +extern crate shared; +extern crate reexport; + +// FIXME: Should this trigger? +// +// One could make an argument that I said I want "reexport" to be public, and +// since "reexport" says "shared_direct_private" is public, then it should +// transitively be public for me. However, as written, this is explicitly +// referring to a dependency that is marked "private", which I think is +// confusing. +pub fn leaks_priv() -> shared::Shared { + shared::Shared +} + +pub fn leaks_pub() -> reexport::Shared { + reexport::Shared +} diff --git a/tests/ui/privacy/pub-priv-dep/shared_indirect.rs b/tests/ui/privacy/pub-priv-dep/shared_indirect.rs new file mode 100644 index 000000000000..34b624b4a1a4 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/shared_indirect.rs @@ -0,0 +1,29 @@ +//@ aux-crate:priv:shared=shared.rs +//@ aux-crate:priv:indirect1=indirect1.rs +//@ compile-flags: -Zunstable-options +//@ check-pass + +// A shared dependency, where it is only indirectly public. +// +// shared_indirect +// /\ +// (PRIVATE) / | (PRIVATE) +// / | +// indirect1 | | +// (PRIVATE) | | +// indirect2 | | +// \ | +// (public) \ / +// \/ +// shared + +#![crate_type = "lib"] +#![deny(exported_private_dependencies)] + +extern crate shared; +extern crate indirect1; + +// FIXME: This should trigger. +pub fn leaks_priv() -> shared::Shared { + shared::Shared +} diff --git a/tests/ui/privacy/ufc-method-call.different_name.stderr b/tests/ui/privacy/ufc-method-call.different_name.stderr new file mode 100644 index 000000000000..16496c480dd1 --- /dev/null +++ b/tests/ui/privacy/ufc-method-call.different_name.stderr @@ -0,0 +1,15 @@ +error[E0599]: no function or associated item named `foo` found for struct `Foo` in the current scope + --> $DIR/ufc-method-call.rs:27:27 + | +LL | pub struct Foo(T); + | ----------------- function or associated item `foo` not found for this struct +... +LL | test::Foo::::foo(); + | ^^^ function or associated item not found in `Foo` + | + = note: the function or associated item was found for + - `Foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/privacy/ufc-method-call.rs b/tests/ui/privacy/ufc-method-call.rs new file mode 100644 index 000000000000..525d9a9eee90 --- /dev/null +++ b/tests/ui/privacy/ufc-method-call.rs @@ -0,0 +1,30 @@ +//! This test used to report that the method call cannot +//! call the private method `Foo::foo`, even though the user +//! explicitly selected `Foo::foo`. This is because we only +//! looked for methods of the right name, without properly checking +//! the `Self` type + +//@ revisions: same_name different_name + +pub mod test { + pub struct A; + pub struct B; + pub struct Foo(T); + + impl Foo { + fn foo() {} + } + + impl Foo { + #[cfg(same_name)] + fn foo() {} + #[cfg(different_name)] + fn bar() {} + } +} + +fn main() { + test::Foo::::foo(); + //[same_name]~^ ERROR associated function `foo` is private + //[different_name]~^^ ERROR no function or associated item named `foo` found for struct `Foo` +} diff --git a/tests/ui/issues/issue-53498.stderr b/tests/ui/privacy/ufc-method-call.same_name.stderr similarity index 90% rename from tests/ui/issues/issue-53498.stderr rename to tests/ui/privacy/ufc-method-call.same_name.stderr index 61a1aedf5082..194ba42cbf98 100644 --- a/tests/ui/issues/issue-53498.stderr +++ b/tests/ui/privacy/ufc-method-call.same_name.stderr @@ -1,5 +1,5 @@ error[E0624]: associated function `foo` is private - --> $DIR/issue-53498.rs:16:27 + --> $DIR/ufc-method-call.rs:27:27 | LL | fn foo() {} | -------- private associated function defined here diff --git a/tests/ui/privacy/where-priv-type.rs b/tests/ui/privacy/where-priv-type.rs index cd9cce7ec3e8..20467301ae17 100644 --- a/tests/ui/privacy/where-priv-type.rs +++ b/tests/ui/privacy/where-priv-type.rs @@ -11,55 +11,46 @@ pub struct PubTy; pub struct PubTyGeneric(T); pub trait PubTr {} impl PubTr for PrivTy {} -pub trait PubTrWithAssocTy { type AssocTy; } -impl PubTrWithAssocTy for PrivTy { type AssocTy = PrivTy; } - +pub trait PubTrWithAssocTy { + type AssocTy; +} +impl PubTrWithAssocTy for PrivTy { + type AssocTy = PrivTy; +} pub struct S //~^ WARNING type `PrivTy` is more private than the item `S` where - PrivTy: -{} - + PrivTy:, {} pub enum E //~^ WARNING type `PrivTy` is more private than the item `E` where - PrivTy: -{} - + PrivTy:, {} pub fn f() //~^ WARNING type `PrivTy` is more private than the item `f` where - PrivTy: -{} - + PrivTy:, +{ +} impl S //~^ WARNING type `PrivTy` is more private than the item `S` where - PrivTy: + PrivTy:, { pub fn f() //~^ WARNING type `PrivTy` is more private than the item `S::f` where - PrivTy: - {} + PrivTy:, + { + } } +impl PubTr for PubTy where PrivTy: {} -impl PubTr for PubTy -where - PrivTy: -{} - - -impl PubTr for PubTyGeneric -where - T: PubTrWithAssocTy -{} - +impl PubTr for PubTyGeneric where T: PubTrWithAssocTy {} pub struct Const; @@ -70,10 +61,11 @@ pub trait Trait { impl Trait for Const where - Const<{ my_const_fn(U) }>: , + Const<{ my_const_fn(U) }>:, { type AssocTy = Const<{ my_const_fn(U) }>; //~^ ERROR private type + //~| ERROR private type fn assoc_fn() -> Self::AssocTy { Const } diff --git a/tests/ui/privacy/where-priv-type.stderr b/tests/ui/privacy/where-priv-type.stderr index 126330b14a63..08963e07c35a 100644 --- a/tests/ui/privacy/where-priv-type.stderr +++ b/tests/ui/privacy/where-priv-type.stderr @@ -1,5 +1,5 @@ warning: type `PrivTy` is more private than the item `S` - --> $DIR/where-priv-type.rs:18:1 + --> $DIR/where-priv-type.rs:21:1 | LL | pub struct S | ^^^^^^^^^^^^ struct `S` is reachable at visibility `pub` @@ -12,7 +12,7 @@ LL | struct PrivTy; = note: `#[warn(private_bounds)]` on by default warning: type `PrivTy` is more private than the item `E` - --> $DIR/where-priv-type.rs:25:1 + --> $DIR/where-priv-type.rs:26:1 | LL | pub enum E | ^^^^^^^^^^ enum `E` is reachable at visibility `pub` @@ -24,13 +24,13 @@ LL | struct PrivTy; | ^^^^^^^^^^^^^ warning: type `PrivTy` is more private than the item `f` - --> $DIR/where-priv-type.rs:32:1 + --> $DIR/where-priv-type.rs:31:1 | LL | / pub fn f() LL | | LL | | where -LL | | PrivTy: - | |___________^ function `f` is reachable at visibility `pub` +LL | | PrivTy:, + | |____________^ function `f` is reachable at visibility `pub` | note: but type `PrivTy` is only usable at visibility `pub(crate)` --> $DIR/where-priv-type.rs:8:1 @@ -39,13 +39,13 @@ LL | struct PrivTy; | ^^^^^^^^^^^^^ warning: type `PrivTy` is more private than the item `S` - --> $DIR/where-priv-type.rs:39:1 + --> $DIR/where-priv-type.rs:38:1 | LL | / impl S LL | | LL | | where -LL | | PrivTy: - | |___________^ implementation `S` is reachable at visibility `pub` +LL | | PrivTy:, + | |____________^ implementation `S` is reachable at visibility `pub` | note: but type `PrivTy` is only usable at visibility `pub(crate)` --> $DIR/where-priv-type.rs:8:1 @@ -54,13 +54,13 @@ LL | struct PrivTy; | ^^^^^^^^^^^^^ warning: type `PrivTy` is more private than the item `S::f` - --> $DIR/where-priv-type.rs:44:5 + --> $DIR/where-priv-type.rs:43:5 | LL | / pub fn f() LL | | LL | | where -LL | | PrivTy: - | |_______________^ associated function `S::f` is reachable at visibility `pub` +LL | | PrivTy:, + | |________________^ associated function `S::f` is reachable at visibility `pub` | note: but type `PrivTy` is only usable at visibility `pub(crate)` --> $DIR/where-priv-type.rs:8:1 @@ -69,7 +69,7 @@ LL | struct PrivTy; | ^^^^^^^^^^^^^ error[E0446]: private type `fn(u8) -> u8 {my_const_fn}` in public interface - --> $DIR/where-priv-type.rs:75:5 + --> $DIR/where-priv-type.rs:66:5 | LL | type AssocTy = Const<{ my_const_fn(U) }>; | ^^^^^^^^^^^^ can't leak private type @@ -77,6 +77,17 @@ LL | type AssocTy = Const<{ my_const_fn(U) }>; LL | const fn my_const_fn(val: u8) -> u8 { | ----------------------------------- `fn(u8) -> u8 {my_const_fn}` declared as private -error: aborting due to 1 previous error; 5 warnings emitted +error[E0446]: private type `fn(u8) -> u8 {my_const_fn}` in public interface + --> $DIR/where-priv-type.rs:66:5 + | +LL | type AssocTy = Const<{ my_const_fn(U) }>; + | ^^^^^^^^^^^^ can't leak private type +... +LL | const fn my_const_fn(val: u8) -> u8 { + | ----------------------------------- `fn(u8) -> u8 {my_const_fn}` declared as private + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors; 5 warnings emitted For more information about this error, try `rustc --explain E0446`. diff --git a/tests/ui/proc-macro/auxiliary/api/literal.rs b/tests/ui/proc-macro/auxiliary/api/literal.rs new file mode 100644 index 000000000000..7109340bb645 --- /dev/null +++ b/tests/ui/proc-macro/auxiliary/api/literal.rs @@ -0,0 +1,83 @@ +// ignore-tidy-linelength + +use proc_macro::Literal; + +pub fn test() { + test_display_literal(); + test_parse_literal(); +} + +fn test_display_literal() { + assert_eq!(Literal::isize_unsuffixed(-10).to_string(), "-10"); + assert_eq!(Literal::isize_suffixed(-10).to_string(), "-10isize"); + assert_eq!(Literal::f32_unsuffixed(-10.0).to_string(), "-10.0"); + assert_eq!(Literal::f32_suffixed(-10.0).to_string(), "-10f32"); + assert_eq!(Literal::f64_unsuffixed(-10.0).to_string(), "-10.0"); + assert_eq!(Literal::f64_suffixed(-10.0).to_string(), "-10f64"); + assert_eq!( + Literal::f64_unsuffixed(1e100).to_string(), + "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0", + ); + + assert_eq!(Literal::string("aA").to_string(), r#" "aA" "#.trim()); + assert_eq!(Literal::string("\t").to_string(), r#" "\t" "#.trim()); + assert_eq!(Literal::string("❤").to_string(), r#" "❤" "#.trim()); + assert_eq!(Literal::string("'").to_string(), r#" "'" "#.trim()); + assert_eq!(Literal::string("\"").to_string(), r#" "\"" "#.trim()); + assert_eq!(Literal::string("\0").to_string(), r#" "\0" "#.trim()); + assert_eq!(Literal::string("\u{1}").to_string(), r#" "\u{1}" "#.trim()); + + assert_eq!(Literal::byte_string(b"aA").to_string(), r#" b"aA" "#.trim()); + assert_eq!(Literal::byte_string(b"\t").to_string(), r#" b"\t" "#.trim()); + assert_eq!(Literal::byte_string(b"'").to_string(), r#" b"'" "#.trim()); + assert_eq!(Literal::byte_string(b"\"").to_string(), r#" b"\"" "#.trim()); + assert_eq!(Literal::byte_string(b"\0").to_string(), r#" b"\0" "#.trim()); + assert_eq!(Literal::byte_string(b"\x01").to_string(), r#" b"\x01" "#.trim()); + + assert_eq!(Literal::c_string(c"aA").to_string(), r#" c"aA" "#.trim()); + assert_eq!(Literal::c_string(c"\t").to_string(), r#" c"\t" "#.trim()); + assert_eq!(Literal::c_string(c"❤").to_string(), r#" c"❤" "#.trim()); + assert_eq!(Literal::c_string(c"\'").to_string(), r#" c"'" "#.trim()); + assert_eq!(Literal::c_string(c"\"").to_string(), r#" c"\"" "#.trim()); + assert_eq!(Literal::c_string(c"\x7f\xff\xfe\u{333}").to_string(), r#" c"\u{7f}\xff\xfe\u{333}" "#.trim()); + + assert_eq!(Literal::character('a').to_string(), r#" 'a' "#.trim()); + assert_eq!(Literal::character('\t').to_string(), r#" '\t' "#.trim()); + assert_eq!(Literal::character('❤').to_string(), r#" '❤' "#.trim()); + assert_eq!(Literal::character('\'').to_string(), r#" '\'' "#.trim()); + assert_eq!(Literal::character('"').to_string(), r#" '"' "#.trim()); + assert_eq!(Literal::character('\0').to_string(), r#" '\0' "#.trim()); + assert_eq!(Literal::character('\u{1}').to_string(), r#" '\u{1}' "#.trim()); + + assert_eq!(Literal::byte_character(b'a').to_string(), r#" b'a' "#.trim()); + assert_eq!(Literal::byte_character(b'\t').to_string(), r#" b'\t' "#.trim()); + assert_eq!(Literal::byte_character(b'\'').to_string(), r#" b'\'' "#.trim()); + assert_eq!(Literal::byte_character(b'"').to_string(), r#" b'"' "#.trim()); + assert_eq!(Literal::byte_character(0).to_string(), r#" b'\0' "#.trim()); + assert_eq!(Literal::byte_character(1).to_string(), r#" b'\x01' "#.trim()); +} + +fn test_parse_literal() { + assert_eq!("1".parse::().unwrap().to_string(), "1"); + assert_eq!("1.0".parse::().unwrap().to_string(), "1.0"); + assert_eq!("'a'".parse::().unwrap().to_string(), "'a'"); + assert_eq!("b'a'".parse::().unwrap().to_string(), "b'a'"); + assert_eq!("\"\n\"".parse::().unwrap().to_string(), "\"\n\""); + assert_eq!("b\"\"".parse::().unwrap().to_string(), "b\"\""); + assert_eq!("c\"\"".parse::().unwrap().to_string(), "c\"\""); + assert_eq!("r##\"\"##".parse::().unwrap().to_string(), "r##\"\"##"); + assert_eq!("10ulong".parse::().unwrap().to_string(), "10ulong"); + assert_eq!("-10ulong".parse::().unwrap().to_string(), "-10ulong"); + + assert!("true".parse::().is_err()); + assert!(".8".parse::().is_err()); + assert!("0 1".parse::().is_err()); + assert!("'a".parse::().is_err()); + assert!(" 0".parse::().is_err()); + assert!("0 ".parse::().is_err()); + assert!("/* comment */0".parse::().is_err()); + assert!("0/* comment */".parse::().is_err()); + assert!("0// comment".parse::().is_err()); + assert!("- 10".parse::().is_err()); + assert!("-'x'".parse::().is_err()); +} diff --git a/tests/ui/proc-macro/auxiliary/api/mod.rs b/tests/ui/proc-macro/auxiliary/api/mod.rs index 45ef6922d283..e0a381cb6c1a 100644 --- a/tests/ui/proc-macro/auxiliary/api/mod.rs +++ b/tests/ui/proc-macro/auxiliary/api/mod.rs @@ -10,7 +10,7 @@ extern crate proc_macro; mod cmp; -mod parse; +mod literal; use proc_macro::TokenStream; @@ -19,7 +19,7 @@ pub fn run(input: TokenStream) -> TokenStream { assert!(input.is_empty()); cmp::test(); - parse::test(); + literal::test(); TokenStream::new() } diff --git a/tests/ui/proc-macro/auxiliary/api/parse.rs b/tests/ui/proc-macro/auxiliary/api/parse.rs deleted file mode 100644 index 801c616c8040..000000000000 --- a/tests/ui/proc-macro/auxiliary/api/parse.rs +++ /dev/null @@ -1,58 +0,0 @@ -// ignore-tidy-linelength - -use proc_macro::Literal; - -pub fn test() { - test_display_literal(); - test_parse_literal(); -} - -fn test_display_literal() { - assert_eq!(Literal::isize_unsuffixed(-10).to_string(), "-10"); - assert_eq!(Literal::isize_suffixed(-10).to_string(), "-10isize"); - assert_eq!(Literal::f32_unsuffixed(-10.0).to_string(), "-10.0"); - assert_eq!(Literal::f32_suffixed(-10.0).to_string(), "-10f32"); - assert_eq!(Literal::f64_unsuffixed(-10.0).to_string(), "-10.0"); - assert_eq!(Literal::f64_suffixed(-10.0).to_string(), "-10f64"); - assert_eq!( - Literal::f64_unsuffixed(1e100).to_string(), - "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0", - ); - - assert_eq!(Literal::string("a \t ❤ ' \" \u{1}").to_string(), "\"a \\t ❤ ' \\\" \\u{1}\"",); - assert_eq!(Literal::c_string(c"\'\"\x7f\u{7fff}").to_string(), r#"c"\'\"\x7f\xe7\xbf\xbf""#); - assert_eq!(Literal::character('a').to_string(), "'a'"); - assert_eq!(Literal::character('\t').to_string(), "'\\t'"); - assert_eq!(Literal::character('❤').to_string(), "'❤'"); - assert_eq!(Literal::character('\'').to_string(), "'\\''"); - assert_eq!(Literal::character('"').to_string(), "'\"'"); - assert_eq!(Literal::character('\u{1}').to_string(), "'\\u{1}'"); - - assert_eq!(Literal::byte_character(b'a').to_string(), "b'a'"); - assert_eq!(Literal::byte_character(0).to_string(), "b'\\x00'"); -} - -fn test_parse_literal() { - assert_eq!("1".parse::().unwrap().to_string(), "1"); - assert_eq!("1.0".parse::().unwrap().to_string(), "1.0"); - assert_eq!("'a'".parse::().unwrap().to_string(), "'a'"); - assert_eq!("b'a'".parse::().unwrap().to_string(), "b'a'"); - assert_eq!("\"\n\"".parse::().unwrap().to_string(), "\"\n\""); - assert_eq!("b\"\"".parse::().unwrap().to_string(), "b\"\""); - assert_eq!("c\"\"".parse::().unwrap().to_string(), "c\"\""); - assert_eq!("r##\"\"##".parse::().unwrap().to_string(), "r##\"\"##"); - assert_eq!("10ulong".parse::().unwrap().to_string(), "10ulong"); - assert_eq!("-10ulong".parse::().unwrap().to_string(), "-10ulong"); - - assert!("true".parse::().is_err()); - assert!(".8".parse::().is_err()); - assert!("0 1".parse::().is_err()); - assert!("'a".parse::().is_err()); - assert!(" 0".parse::().is_err()); - assert!("0 ".parse::().is_err()); - assert!("/* comment */0".parse::().is_err()); - assert!("0/* comment */".parse::().is_err()); - assert!("0// comment".parse::().is_err()); - assert!("- 10".parse::().is_err()); - assert!("-'x'".parse::().is_err()); -} diff --git a/tests/ui/proc-macro/capture-macro-rules-invoke.stdout b/tests/ui/proc-macro/capture-macro-rules-invoke.stdout index 71e34119ba7e..172e5ec6e427 100644 --- a/tests/ui/proc-macro/capture-macro-rules-invoke.stdout +++ b/tests/ui/proc-macro/capture-macro-rules-invoke.stdout @@ -11,9 +11,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ span: $DIR/capture-macro-rules-invoke.rs:21:21: 21:26 (#3), }, ] -PRINT-BANG INPUT (DISPLAY): 1 + 1, { "a" }, let a = 1;, String, my_name, 'a, my_val = 30, -std::option::Option, pub(in some::path) , [a b c], -30 -PRINT-BANG RE-COLLECTED (DISPLAY): 1 + 1, { "a" }, let a = 1, String, my_name, 'a, my_val = 30, +PRINT-BANG INPUT (DISPLAY): 1 + 1, { "a" }, let a = 1, String, my_name, 'a, my_val = 30, std::option::Option, pub(in some::path), [a b c], -30 PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): 1 + 1, { "a" }, let a = 1, String, my_name, 'a, my_val = 30, std :: option :: Option, pub(in some :: path), [a b c], - 30 diff --git a/tests/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout b/tests/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout index e11d2c0715cb..0168689b605f 100644 --- a/tests/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout +++ b/tests/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout @@ -1,5 +1,4 @@ PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = #[allow(warnings)] 0; 0 }, } -PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = #[allow(warnings)] #[allow(warnings)] 0; 0 }, } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "enum", @@ -40,31 +39,6 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Group { delimiter: None, stream: TokenStream [ - Punct { - ch: '#', - spacing: Alone, - span: #0 bytes(543..544), - }, - Group { - delimiter: Bracket, - stream: TokenStream [ - Ident { - ident: "allow", - span: #0 bytes(545..550), - }, - Group { - delimiter: Parenthesis, - stream: TokenStream [ - Ident { - ident: "warnings", - span: #0 bytes(551..559), - }, - ], - span: #0 bytes(550..560), - }, - ], - span: #0 bytes(544..561), - }, Punct { ch: '#', spacing: Alone, @@ -122,8 +96,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ span: #3 bytes(308..357), }, ] -PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { 0; }; 0 }, } -PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { 0 }; 0 }, } +PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { 0 }; 0 }, } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "enum", @@ -280,8 +253,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ span: #11 bytes(432..485), }, ] -PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { PATH; }; 0 }, } -PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { PATH }; 0 }, } +PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { PATH }; 0 }, } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "enum", @@ -358,8 +330,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ span: #15 bytes(432..485), }, ] -PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { 0 + 1; }; 0 }, } -PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { 0 + 1 }; 0 }, } +PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { 0 + 1 }; 0 }, } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "enum", @@ -449,8 +420,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ span: #19 bytes(432..485), }, ] -PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { PATH + 1; }; 0 }, } -PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { PATH + 1 }; 0 }, } +PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { PATH + 1 }; 0 }, } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "enum", diff --git a/tests/ui/proc-macro/issue-78675-captured-inner-attrs.stdout b/tests/ui/proc-macro/issue-78675-captured-inner-attrs.stdout index 37ecf3a8df33..346f87643c27 100644 --- a/tests/ui/proc-macro/issue-78675-captured-inner-attrs.stdout +++ b/tests/ui/proc-macro/issue-78675-captured-inner-attrs.stdout @@ -1,6 +1,4 @@ -PRINT-BANG INPUT (DISPLAY): foo! { #[fake_attr] mod bar { - #![doc = r" Foo"] -} } +PRINT-BANG INPUT (DISPLAY): foo! { #[fake_attr] mod bar { #![doc = r" Foo"] } } PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): foo! { #[fake_attr] mod bar { #! [doc = r" Foo"] } } PRINT-BANG INPUT (DEBUG): TokenStream [ Ident { diff --git a/tests/ui/proc-macro/issue-81555.rs b/tests/ui/proc-macro/issue-81555.rs index b337ab7ce37d..7a61a31952f4 100644 --- a/tests/ui/proc-macro/issue-81555.rs +++ b/tests/ui/proc-macro/issue-81555.rs @@ -10,5 +10,6 @@ use test_macros::identity_attr; fn main() { let _x; let y = (); - (#[identity_attr] _x) = y; + #[identity_attr] + _x = y; } diff --git a/tests/ui/proc-macro/nested-macro-rules.stderr b/tests/ui/proc-macro/nested-macro-rules.stderr index 270e9161b033..8fe041d61b81 100644 --- a/tests/ui/proc-macro/nested-macro-rules.stderr +++ b/tests/ui/proc-macro/nested-macro-rules.stderr @@ -1,4 +1,4 @@ -warning: non-local `macro_rules!` definition, they should be avoided as they go against expectation +warning: non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module --> $DIR/auxiliary/nested-macro-rules.rs:7:9 | LL | macro_rules! outer_macro { @@ -19,7 +19,6 @@ LL | nested_macro_rules::outer_macro!(SecondStruct, SecondAttrStruct); | = help: remove the `#[macro_export]` or move this `macro_rules!` outside the of the current function `main` = note: a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue note: the lint level is defined here --> $DIR/nested-macro-rules.rs:8:9 diff --git a/tests/ui/proc-macro/pretty-print-hack-show.local.stderr b/tests/ui/proc-macro/pretty-print-hack-show.local.stderr index 118882f73a3e..889cd0c90ebb 100644 --- a/tests/ui/proc-macro/pretty-print-hack-show.local.stderr +++ b/tests/ui/proc-macro/pretty-print-hack-show.local.stderr @@ -1,185 +1,6 @@ error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default + = note: older versions of the `rental` crate no longer compile; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives -error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 8 previous errors - -Future incompatibility report: Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default +error: aborting due to 1 previous error diff --git a/tests/ui/proc-macro/pretty-print-hack-show.local.stdout b/tests/ui/proc-macro/pretty-print-hack-show.local.stdout deleted file mode 100644 index 3d793d2a0145..000000000000 --- a/tests/ui/proc-macro/pretty-print-hack-show.local.stdout +++ /dev/null @@ -1,44 +0,0 @@ -PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input, } -PRINT-DERIVE RE-COLLECTED (DISPLAY): enum ProceduralMasqueradeDummyType { Input } -PRINT-DERIVE INPUT (DEBUG): TokenStream [ - Ident { - ident: "enum", - span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:1: 4:5 (#0), - }, - Ident { - ident: "ProceduralMasqueradeDummyType", - span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6: 4:35 (#0), - }, - Group { - delimiter: Brace, - stream: TokenStream [ - Ident { - ident: "Input", - span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:13:5: 13:10 (#0), - }, - ], - span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:36: 14:2 (#0), - }, -] -PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input, } -PRINT-DERIVE RE-COLLECTED (DISPLAY): enum ProceduralMasqueradeDummyType { Input } -PRINT-DERIVE INPUT (DEBUG): TokenStream [ - Ident { - ident: "enum", - span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:1: 4:5 (#0), - }, - Ident { - ident: "ProceduralMasqueradeDummyType", - span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6: 4:35 (#0), - }, - Group { - delimiter: Brace, - stream: TokenStream [ - Ident { - ident: "Input", - span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:13:5: 13:10 (#0), - }, - ], - span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:36: 14:2 (#0), - }, -] diff --git a/tests/ui/proc-macro/pretty-print-hack-show.remapped.stderr b/tests/ui/proc-macro/pretty-print-hack-show.remapped.stderr index 118882f73a3e..889cd0c90ebb 100644 --- a/tests/ui/proc-macro/pretty-print-hack-show.remapped.stderr +++ b/tests/ui/proc-macro/pretty-print-hack-show.remapped.stderr @@ -1,185 +1,6 @@ error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default + = note: older versions of the `rental` crate no longer compile; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives -error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 8 previous errors - -Future incompatibility report: Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default - -Future breakage diagnostic: -error: using an old version of `rental` - --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6 - | -LL | enum ProceduralMasqueradeDummyType { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #83125 - = note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives - = note: `#[deny(proc_macro_back_compat)]` on by default +error: aborting due to 1 previous error diff --git a/tests/ui/proc-macro/pretty-print-hack-show.remapped.stdout b/tests/ui/proc-macro/pretty-print-hack-show.remapped.stdout deleted file mode 100644 index 3d793d2a0145..000000000000 --- a/tests/ui/proc-macro/pretty-print-hack-show.remapped.stdout +++ /dev/null @@ -1,44 +0,0 @@ -PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input, } -PRINT-DERIVE RE-COLLECTED (DISPLAY): enum ProceduralMasqueradeDummyType { Input } -PRINT-DERIVE INPUT (DEBUG): TokenStream [ - Ident { - ident: "enum", - span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:1: 4:5 (#0), - }, - Ident { - ident: "ProceduralMasqueradeDummyType", - span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6: 4:35 (#0), - }, - Group { - delimiter: Brace, - stream: TokenStream [ - Ident { - ident: "Input", - span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:13:5: 13:10 (#0), - }, - ], - span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:36: 14:2 (#0), - }, -] -PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input, } -PRINT-DERIVE RE-COLLECTED (DISPLAY): enum ProceduralMasqueradeDummyType { Input } -PRINT-DERIVE INPUT (DEBUG): TokenStream [ - Ident { - ident: "enum", - span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:1: 4:5 (#0), - }, - Ident { - ident: "ProceduralMasqueradeDummyType", - span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6: 4:35 (#0), - }, - Group { - delimiter: Brace, - stream: TokenStream [ - Ident { - ident: "Input", - span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:13:5: 13:10 (#0), - }, - ], - span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:36: 14:2 (#0), - }, -] diff --git a/tests/ui/env-args-reverse-iterator.rs b/tests/ui/process/env-args-reverse-iterator.rs similarity index 100% rename from tests/ui/env-args-reverse-iterator.rs rename to tests/ui/process/env-args-reverse-iterator.rs diff --git a/tests/ui/env-funky-keys.rs b/tests/ui/process/env-funky-keys.rs similarity index 100% rename from tests/ui/env-funky-keys.rs rename to tests/ui/process/env-funky-keys.rs diff --git a/tests/ui/env-null-vars.rs b/tests/ui/process/env-null-vars.rs similarity index 52% rename from tests/ui/env-null-vars.rs rename to tests/ui/process/env-null-vars.rs index bb86fd353c4e..24d783553d12 100644 --- a/tests/ui/env-null-vars.rs +++ b/tests/ui/process/env-null-vars.rs @@ -1,21 +1,15 @@ +// Ensure that env::vars() does not panic if environ is null. +// Regression test for rust-lang/rust#53200 //@ run-pass -#![allow(unused_imports)] - -//@ ignore-windows - -// issue-53200 - #![feature(rustc_private)] -extern crate libc; - -use std::env; // FIXME: more platforms? #[cfg(target_os = "linux")] fn main() { + extern crate libc; unsafe { libc::clearenv(); } - assert_eq!(env::vars().count(), 0); + assert_eq!(std::env::vars().count(), 0); } #[cfg(not(target_os = "linux"))] diff --git a/tests/ui/env-vars.rs b/tests/ui/process/env-vars.rs similarity index 100% rename from tests/ui/env-vars.rs rename to tests/ui/process/env-vars.rs diff --git a/tests/ui/exec-env.rs b/tests/ui/process/exec-env.rs similarity index 100% rename from tests/ui/exec-env.rs rename to tests/ui/process/exec-env.rs diff --git a/tests/ui/inherit-env.rs b/tests/ui/process/inherit-env.rs similarity index 100% rename from tests/ui/inherit-env.rs rename to tests/ui/process/inherit-env.rs diff --git a/tests/ui/process/no-stdio.rs b/tests/ui/process/no-stdio.rs index 05b1e52b799e..8eebf6dbc7d4 100644 --- a/tests/ui/process/no-stdio.rs +++ b/tests/ui/process/no-stdio.rs @@ -5,11 +5,13 @@ #![feature(rustc_private)] +#[cfg(unix)] extern crate libc; -use std::process::{Command, Stdio}; use std::env; +use std::ffi::c_int; use std::io::{self, Read, Write}; +use std::process::{Command, Stdio}; #[cfg(unix)] unsafe fn without_stdio R>(f: F) -> R { @@ -36,14 +38,14 @@ unsafe fn without_stdio R>(f: F) -> R { } #[cfg(unix)] -fn assert_fd_is_valid(fd: libc::c_int) { +fn assert_fd_is_valid(fd: c_int) { if unsafe { libc::fcntl(fd, libc::F_GETFD) == -1 } { panic!("file descriptor {} is not valid: {}", fd, io::Error::last_os_error()); } } #[cfg(windows)] -fn assert_fd_is_valid(_fd: libc::c_int) {} +fn assert_fd_is_valid(_fd: c_int) {} #[cfg(windows)] unsafe fn without_stdio R>(f: F) -> R { diff --git a/tests/ui/pub/pub-ident-struct-4.fixed b/tests/ui/pub/pub-ident-struct-4.fixed index 5fedbb724374..a62ece43eced 100644 --- a/tests/ui/pub/pub-ident-struct-4.fixed +++ b/tests/ui/pub/pub-ident-struct-4.fixed @@ -1,6 +1,7 @@ //@ run-rustfix -pub struct T(#[allow(dead_code)] String); +#[allow(dead_code)] +pub struct T(String); //~^ ERROR missing `struct` for struct definition fn main() {} diff --git a/tests/ui/pub/pub-ident-struct-4.rs b/tests/ui/pub/pub-ident-struct-4.rs index 5c721c25a781..0d56a31beaf1 100644 --- a/tests/ui/pub/pub-ident-struct-4.rs +++ b/tests/ui/pub/pub-ident-struct-4.rs @@ -1,6 +1,7 @@ //@ run-rustfix -pub T(#[allow(dead_code)] String); +#[allow(dead_code)] +pub T(String); //~^ ERROR missing `struct` for struct definition fn main() {} diff --git a/tests/ui/pub/pub-ident-struct-4.stderr b/tests/ui/pub/pub-ident-struct-4.stderr index 5fbb02c8673c..ec1367832113 100644 --- a/tests/ui/pub/pub-ident-struct-4.stderr +++ b/tests/ui/pub/pub-ident-struct-4.stderr @@ -1,12 +1,12 @@ error: missing `struct` for struct definition - --> $DIR/pub-ident-struct-4.rs:3:4 + --> $DIR/pub-ident-struct-4.rs:4:4 | -LL | pub T(#[allow(dead_code)] String); +LL | pub T(String); | ^ | help: add `struct` here to parse `T` as a public struct | -LL | pub struct T(#[allow(dead_code)] String); +LL | pub struct T(String); | ++++++ error: aborting due to 1 previous error diff --git a/tests/ui/pub/pub-reexport-priv-extern-crate.rs b/tests/ui/pub/pub-reexport-priv-extern-crate.rs index dd5cd420fa54..fb495be40015 100644 --- a/tests/ui/pub/pub-reexport-priv-extern-crate.rs +++ b/tests/ui/pub/pub-reexport-priv-extern-crate.rs @@ -1,5 +1,5 @@ extern crate core; -pub use core as reexported_core; //~ ERROR `core` is private, and cannot be re-exported +pub use core as reexported_core; //~ ERROR `core` is private and cannot be re-exported //~^ WARN this was previously accepted mod foo1 { diff --git a/tests/ui/pub/pub-reexport-priv-extern-crate.stderr b/tests/ui/pub/pub-reexport-priv-extern-crate.stderr index c7fadc6f9293..915d07fd08a6 100644 --- a/tests/ui/pub/pub-reexport-priv-extern-crate.stderr +++ b/tests/ui/pub/pub-reexport-priv-extern-crate.stderr @@ -22,7 +22,7 @@ note: the crate import `core` is defined here LL | extern crate core; | ^^^^^^^^^^^^^^^^^^ -error: extern crate `core` is private, and cannot be re-exported (error E0365), consider declaring with `pub` +error[E0365]: extern crate `core` is private and cannot be re-exported --> $DIR/pub-reexport-priv-extern-crate.rs:2:9 | LL | pub use core as reexported_core; @@ -31,7 +31,12 @@ LL | pub use core as reexported_core; = warning: 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 #34537 = note: `#[deny(pub_use_of_private_extern_crate)]` on by default +help: consider making the `extern crate` item publicly accessible + | +LL | pub extern crate core; + | +++ error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0603`. +Some errors have detailed explanations: E0365, E0603. +For more information about an error, try `rustc --explain E0365`. diff --git a/tests/ui/impossible_range.fixed b/tests/ui/range/impossible_range.fixed similarity index 100% rename from tests/ui/impossible_range.fixed rename to tests/ui/range/impossible_range.fixed diff --git a/tests/ui/impossible_range.rs b/tests/ui/range/impossible_range.rs similarity index 100% rename from tests/ui/impossible_range.rs rename to tests/ui/range/impossible_range.rs diff --git a/tests/ui/impossible_range.stderr b/tests/ui/range/impossible_range.stderr similarity index 100% rename from tests/ui/impossible_range.stderr rename to tests/ui/range/impossible_range.stderr diff --git a/tests/ui/range_inclusive.rs b/tests/ui/range/range_inclusive.rs similarity index 100% rename from tests/ui/range_inclusive.rs rename to tests/ui/range/range_inclusive.rs diff --git a/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs deleted file mode 100644 index 87ce4f1e14d9..000000000000 --- a/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs +++ /dev/null @@ -1,15 +0,0 @@ -// issue: rust-lang/rust#105047 -// ICE raw ptr comparison should already be caught in the trait systems - -#![feature(raw_ref_op)] - -const RCZ: *const i32 = &raw const *&0; - -const fn f() { - if let RCZ = &raw const *&0 { } - //~^ WARN function pointers and raw pointers not derived from integers in patterns - //~| ERROR pointers cannot be reliably compared during const eval - //~| WARN this was previously accepted by the compiler but is being phased out -} - -fn main() {} diff --git a/tests/ui/raw-ref-op/const-eval-compare-ice-105047.stderr b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.stderr deleted file mode 100644 index 9c472cda2442..000000000000 --- a/tests/ui/raw-ref-op/const-eval-compare-ice-105047.stderr +++ /dev/null @@ -1,31 +0,0 @@ -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/const-eval-compare-ice-105047.rs:9:12 - | -LL | if let RCZ = &raw const *&0 { } - | ^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -error: pointers cannot be reliably compared during const eval - --> $DIR/const-eval-compare-ice-105047.rs:9:12 - | -LL | if let RCZ = &raw const *&0 { } - | ^^^ - | - = note: see issue #53020 for more information - -error: aborting due to 1 previous error; 1 warning emitted - -Future incompatibility report: Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/const-eval-compare-ice-105047.rs:9:12 - | -LL | if let RCZ = &raw const *&0 { } - | ^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - diff --git a/tests/ui/regions/regions-issue-21422.rs b/tests/ui/regions/regions-issue-21422.rs index 54beed9b3ac2..67852a6f5de0 100644 --- a/tests/ui/regions/regions-issue-21422.rs +++ b/tests/ui/regions/regions-issue-21422.rs @@ -5,6 +5,7 @@ //@ pretty-expanded FIXME #23616 +#[allow(dead_code)] pub struct P<'a> { _ptr: *const &'a u8, } diff --git a/tests/ui/conflicting-repr-hints.rs b/tests/ui/repr/conflicting-repr-hints.rs similarity index 100% rename from tests/ui/conflicting-repr-hints.rs rename to tests/ui/repr/conflicting-repr-hints.rs diff --git a/tests/ui/conflicting-repr-hints.stderr b/tests/ui/repr/conflicting-repr-hints.stderr similarity index 100% rename from tests/ui/conflicting-repr-hints.stderr rename to tests/ui/repr/conflicting-repr-hints.stderr diff --git a/tests/ui/resolve/filter-intrinsics.rs b/tests/ui/resolve/filter-intrinsics.rs index c0956ef85aff..8d6d22817dc5 100644 --- a/tests/ui/resolve/filter-intrinsics.rs +++ b/tests/ui/resolve/filter-intrinsics.rs @@ -1,6 +1,6 @@ fn main() { - // Should suggest only `std::mem::size_of` - let _ = size_of::(); + // Should suggest only `std::mem::transmute` + let _ = transmute::(); //~^ ERROR cannot find // Should suggest `std::intrinsics::fabsf64`, diff --git a/tests/ui/resolve/filter-intrinsics.stderr b/tests/ui/resolve/filter-intrinsics.stderr index cc1092dd0cfa..9c9e92f6d4f8 100644 --- a/tests/ui/resolve/filter-intrinsics.stderr +++ b/tests/ui/resolve/filter-intrinsics.stderr @@ -1,12 +1,12 @@ -error[E0425]: cannot find function `size_of` in this scope +error[E0425]: cannot find function `transmute` in this scope --> $DIR/filter-intrinsics.rs:3:13 | -LL | let _ = size_of::(); - | ^^^^^^^ not found in this scope +LL | let _ = transmute::(); + | ^^^^^^^^^ not found in this scope | help: consider importing this function | -LL + use std::mem::size_of; +LL + use std::mem::transmute; | error[E0425]: cannot find function `fabsf64` in this scope diff --git a/tests/ui/resolve/fn-new-doesnt-exist.rs b/tests/ui/resolve/fn-new-doesnt-exist.rs deleted file mode 100644 index 4f6290808fc0..000000000000 --- a/tests/ui/resolve/fn-new-doesnt-exist.rs +++ /dev/null @@ -1,5 +0,0 @@ -use std::net::TcpStream; - -fn main() { - let stream = TcpStream::new(); //~ ERROR no function or associated item named `new` found -} diff --git a/tests/ui/resolve/fn-new-doesnt-exist.stderr b/tests/ui/resolve/fn-new-doesnt-exist.stderr deleted file mode 100644 index 418dd9ea6b84..000000000000 --- a/tests/ui/resolve/fn-new-doesnt-exist.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0599]: no function or associated item named `new` found for struct `TcpStream` in the current scope - --> $DIR/fn-new-doesnt-exist.rs:4:28 - | -LL | let stream = TcpStream::new(); - | ^^^ function or associated item not found in `TcpStream` - | -note: if you're trying to build a new `TcpStream` consider using one of the following associated functions: - TcpStream::connect - TcpStream::connect_timeout - --> $SRC_DIR/std/src/net/tcp.rs:LL:COL - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/resolve/issue-16058.stderr b/tests/ui/resolve/issue-16058.stderr index 914990c35ff7..12b1ac8a5fb4 100644 --- a/tests/ui/resolve/issue-16058.stderr +++ b/tests/ui/resolve/issue-16058.stderr @@ -4,7 +4,7 @@ error[E0574]: expected struct, variant or union type, found enum `Result` LL | Result { | ^^^^^^ not a struct, variant or union type | -help: consider importing one of these items instead +help: consider importing one of these type aliases instead | LL + use std::fmt::Result; | diff --git a/tests/ui/resolve/issue-21221-1.stderr b/tests/ui/resolve/issue-21221-1.stderr index a38116cd7287..ccf03afaa19f 100644 --- a/tests/ui/resolve/issue-21221-1.stderr +++ b/tests/ui/resolve/issue-21221-1.stderr @@ -4,14 +4,14 @@ error[E0405]: cannot find trait `Mul` in this scope LL | impl Mul for Foo { | ^^^ not found in this scope | -help: consider importing one of these items +help: consider importing one of these traits + | +LL + use std::ops::Mul; | LL + use mul1::Mul; | LL + use mul2::Mul; | -LL + use std::ops::Mul; - | error[E0412]: cannot find type `Mul` in this scope --> $DIR/issue-21221-1.rs:58:16 @@ -19,14 +19,14 @@ error[E0412]: cannot find type `Mul` in this scope LL | fn getMul() -> Mul { | ^^^ not found in this scope | -help: consider importing one of these items +help: consider importing one of these traits + | +LL + use std::ops::Mul; | LL + use mul1::Mul; | LL + use mul2::Mul; | -LL + use std::ops::Mul; - | error[E0405]: cannot find trait `ThisTraitReallyDoesntExistInAnyModuleReally` in this scope --> $DIR/issue-21221-1.rs:63:6 diff --git a/tests/ui/resolve/issue-21221-2.stderr b/tests/ui/resolve/issue-21221-2.stderr index 3bd4c1a5d131..5db327955eb1 100644 --- a/tests/ui/resolve/issue-21221-2.stderr +++ b/tests/ui/resolve/issue-21221-2.stderr @@ -4,7 +4,7 @@ error[E0405]: cannot find trait `T` in this scope LL | impl T for Foo { } | ^ not found in this scope | -help: consider importing one of these items +help: consider importing one of these traits | LL + use baz::T; | diff --git a/tests/ui/resolve/issue-50599.stderr b/tests/ui/resolve/issue-50599.stderr index e5eacd741fbe..24fb3d580b8f 100644 --- a/tests/ui/resolve/issue-50599.stderr +++ b/tests/ui/resolve/issue-50599.stderr @@ -4,7 +4,7 @@ error[E0425]: cannot find value `LOG10_2` in module `std::f64` LL | const M: usize = (f64::from(N) * std::f64::LOG10_2) as usize; | ^^^^^^^ not found in `std::f64` | -help: consider importing one of these items +help: consider importing one of these constants | LL + use std::f128::consts::LOG10_2; | diff --git a/tests/ui/resolve/issue-73427.stderr b/tests/ui/resolve/issue-73427.stderr index c5e245d884b7..0a9a504f79ca 100644 --- a/tests/ui/resolve/issue-73427.stderr +++ b/tests/ui/resolve/issue-73427.stderr @@ -105,7 +105,7 @@ help: the following enum variant is available | LL | (E::TupleWithFields(/* fields */)).foo(); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -help: consider importing one of these items instead +help: consider importing one of these constants instead | LL + use std::f128::consts::E; | diff --git a/tests/ui/resolve/issue-82865.stderr b/tests/ui/resolve/issue-82865.stderr index 9d0439d9d876..730fd6d60264 100644 --- a/tests/ui/resolve/issue-82865.stderr +++ b/tests/ui/resolve/issue-82865.stderr @@ -15,13 +15,6 @@ LL | Box::z LL | mac!(); | ------ in this macro invocation | -note: if you're trying to build a new `Box<_, _>` consider using one of the following associated functions: - Box::::new - Box::::new_uninit - Box::::new_zeroed - Box::::try_new - and 18 others - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/resolve/privacy-enum-ctor.stderr b/tests/ui/resolve/privacy-enum-ctor.stderr index 01c8a38d2a48..ee3aecddcc34 100644 --- a/tests/ui/resolve/privacy-enum-ctor.stderr +++ b/tests/ui/resolve/privacy-enum-ctor.stderr @@ -82,7 +82,7 @@ help: a function with a similar name exists | LL | let _: E = m::f; | ~ -help: consider importing one of these items instead +help: consider importing one of these constants instead | LL + use std::f128::consts::E; | @@ -123,7 +123,7 @@ help: alternatively, the following enum variant is available | LL | let _: E = (E::Fn(/* fields */)); | ~~~~~~~~~~~~~~~~~~~~~ -help: consider importing one of these items instead +help: consider importing one of these constants instead | LL + use std::f128::consts::E; | diff --git a/tests/ui/resolve/suggest-builder-fn.rs b/tests/ui/resolve/suggest-builder-fn.rs new file mode 100644 index 000000000000..0d9b35549a47 --- /dev/null +++ b/tests/ui/resolve/suggest-builder-fn.rs @@ -0,0 +1,64 @@ +// Tests that we suggest the right alternatives when +// a builder method cannot be resolved + +use std::net::TcpStream; + +trait SomeTrait {} + +struct Foo { + v : T +} + +impl Foo { + // Should not be suggested if constraint on the impl not met + fn new() -> Self { + Self { v: T::default() } + } +} + +struct Bar; + +impl Bar { + // Should be suggested + fn build() -> Self { + Self {} + } + + // Method with self can't be a builder. + // Should not be suggested + fn build_with_self(&self) -> Self { + Self {} + } +} + +mod SomeMod { + use Bar; + + impl Bar { + // Public method. Should be suggested + pub fn build_public() -> Self { + Self {} + } + + // Private method. Should not be suggested + fn build_private() -> Self { + Self {} + } + } +} + +fn main() { + // `new` not found on `TcpStream` and `connect` should be suggested + let _stream = TcpStream::new(); + //~^ ERROR no function or associated item named `new` found + + // Although `new` is found on `>` it should not be + // suggested because `u8` does not meet the `T: SomeTrait` constraint + let _foo = Foo::::new(); + //~^ ERROR the function or associated item `new` exists for struct `Foo`, but its trait bounds were not satisfied + + // Should suggest only `::build()` and `SomeMod::::build_public()`. + // Other methods should not suggested because they are private or are not a builder + let _bar = Bar::new(); + //~^ ERROR no function or associated item named `new` found +} diff --git a/tests/ui/resolve/suggest-builder-fn.stderr b/tests/ui/resolve/suggest-builder-fn.stderr new file mode 100644 index 000000000000..9c5eed35ccff --- /dev/null +++ b/tests/ui/resolve/suggest-builder-fn.stderr @@ -0,0 +1,51 @@ +error[E0599]: no function or associated item named `new` found for struct `TcpStream` in the current scope + --> $DIR/suggest-builder-fn.rs:52:29 + | +LL | let _stream = TcpStream::new(); + | ^^^ function or associated item not found in `TcpStream` + | +note: if you're trying to build a new `TcpStream` consider using one of the following associated functions: + TcpStream::connect + TcpStream::connect_timeout + --> $SRC_DIR/std/src/net/tcp.rs:LL:COL + +error[E0599]: the function or associated item `new` exists for struct `Foo`, but its trait bounds were not satisfied + --> $DIR/suggest-builder-fn.rs:57:27 + | +LL | struct Foo { + | ------------- function or associated item `new` not found for this struct +... +LL | let _foo = Foo::::new(); + | ^^^ function or associated item cannot be called on `Foo` due to unsatisfied trait bounds + | +note: trait bound `u8: SomeTrait` was not satisfied + --> $DIR/suggest-builder-fn.rs:12:9 + | +LL | impl Foo { + | ^^^^^^^^^ ------ + | | + | unsatisfied trait bound introduced here + +error[E0599]: no function or associated item named `new` found for struct `Bar` in the current scope + --> $DIR/suggest-builder-fn.rs:62:21 + | +LL | struct Bar; + | ---------- function or associated item `new` not found for this struct +... +LL | let _bar = Bar::new(); + | ^^^ function or associated item not found in `Bar` + | +note: if you're trying to build a new `Bar` consider using one of the following associated functions: + Bar::build + SomeMod::::build_public + --> $DIR/suggest-builder-fn.rs:23:5 + | +LL | fn build() -> Self { + | ^^^^^^^^^^^^^^^^^^ +... +LL | pub fn build_public() -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/return/dont-suggest-through-inner-const.rs b/tests/ui/return/dont-suggest-through-inner-const.rs new file mode 100644 index 000000000000..b2347dedd52d --- /dev/null +++ b/tests/ui/return/dont-suggest-through-inner-const.rs @@ -0,0 +1,9 @@ +const fn f() -> usize { + //~^ ERROR mismatched types + const FIELD: usize = loop { + 0 + //~^ ERROR mismatched types + }; +} + +fn main() {} diff --git a/tests/ui/return/dont-suggest-through-inner-const.stderr b/tests/ui/return/dont-suggest-through-inner-const.stderr new file mode 100644 index 000000000000..6aeee74b0adf --- /dev/null +++ b/tests/ui/return/dont-suggest-through-inner-const.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/dont-suggest-through-inner-const.rs:4:9 + | +LL | 0 + | ^ expected `()`, found integer + +error[E0308]: mismatched types + --> $DIR/dont-suggest-through-inner-const.rs:1:17 + | +LL | const fn f() -> usize { + | - ^^^^^ expected `usize`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/ret-bang.rs b/tests/ui/return/ret-bang.rs similarity index 100% rename from tests/ui/ret-bang.rs rename to tests/ui/return/ret-bang.rs diff --git a/tests/ui/ret-non-nil.rs b/tests/ui/return/ret-non-nil.rs similarity index 100% rename from tests/ui/ret-non-nil.rs rename to tests/ui/return/ret-non-nil.rs diff --git a/tests/ui/ret-non-nil.stderr b/tests/ui/return/ret-non-nil.stderr similarity index 100% rename from tests/ui/ret-non-nil.stderr rename to tests/ui/return/ret-non-nil.stderr diff --git a/tests/ui/return-disjoint-regions.rs b/tests/ui/return/return-disjoint-regions.rs similarity index 100% rename from tests/ui/return-disjoint-regions.rs rename to tests/ui/return/return-disjoint-regions.rs diff --git a/tests/ui/return-disjoint-regions.stderr b/tests/ui/return/return-disjoint-regions.stderr similarity index 100% rename from tests/ui/return-disjoint-regions.stderr rename to tests/ui/return/return-disjoint-regions.stderr diff --git a/tests/ui/return/return-from-residual-sugg-issue-125997.fixed b/tests/ui/return/return-from-residual-sugg-issue-125997.fixed new file mode 100644 index 000000000000..b2eca69aeb90 --- /dev/null +++ b/tests/ui/return/return-from-residual-sugg-issue-125997.fixed @@ -0,0 +1,42 @@ +//@ run-rustfix + +#![allow(unused_imports)] +#![allow(dead_code)] + +use std::fs::File; +use std::io::prelude::*; + +fn test1() -> Result<(), Box> { + let mut _file = File::create("foo.txt")?; + //~^ ERROR the `?` operator can only be used in a function + + Ok(()) +} + +fn test2() -> Result<(), Box> { + let mut _file = File::create("foo.txt")?; + //~^ ERROR the `?` operator can only be used in a function + println!(); + + Ok(()) +} + +macro_rules! mac { + () => { + fn test3() -> Result<(), Box> { + let mut _file = File::create("foo.txt")?; + //~^ ERROR the `?` operator can only be used in a function + println!(); + + Ok(()) +} + }; +} + +fn main() -> Result<(), Box> { + let mut _file = File::create("foo.txt")?; + //~^ ERROR the `?` operator can only be used in a function + mac!(); + + Ok(()) +} diff --git a/tests/ui/return/return-from-residual-sugg-issue-125997.rs b/tests/ui/return/return-from-residual-sugg-issue-125997.rs new file mode 100644 index 000000000000..dd8550a388b8 --- /dev/null +++ b/tests/ui/return/return-from-residual-sugg-issue-125997.rs @@ -0,0 +1,34 @@ +//@ run-rustfix + +#![allow(unused_imports)] +#![allow(dead_code)] + +use std::fs::File; +use std::io::prelude::*; + +fn test1() { + let mut _file = File::create("foo.txt")?; + //~^ ERROR the `?` operator can only be used in a function +} + +fn test2() { + let mut _file = File::create("foo.txt")?; + //~^ ERROR the `?` operator can only be used in a function + println!(); +} + +macro_rules! mac { + () => { + fn test3() { + let mut _file = File::create("foo.txt")?; + //~^ ERROR the `?` operator can only be used in a function + println!(); + } + }; +} + +fn main() { + let mut _file = File::create("foo.txt")?; + //~^ ERROR the `?` operator can only be used in a function + mac!(); +} diff --git a/tests/ui/return/return-from-residual-sugg-issue-125997.stderr b/tests/ui/return/return-from-residual-sugg-issue-125997.stderr new file mode 100644 index 000000000000..ef938f0213df --- /dev/null +++ b/tests/ui/return/return-from-residual-sugg-issue-125997.stderr @@ -0,0 +1,86 @@ +error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) + --> $DIR/return-from-residual-sugg-issue-125997.rs:10:44 + | +LL | fn test1() { + | ---------- this function should return `Result` or `Option` to accept `?` +LL | let mut _file = File::create("foo.txt")?; + | ^ cannot use the `?` operator in a function that returns `()` + | + = help: the trait `FromResidual>` is not implemented for `()` +help: consider adding return type + | +LL ~ fn test1() -> Result<(), Box> { +LL | let mut _file = File::create("foo.txt")?; +LL | +LL + +LL + Ok(()) +LL + } + | + +error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) + --> $DIR/return-from-residual-sugg-issue-125997.rs:15:44 + | +LL | fn test2() { + | ---------- this function should return `Result` or `Option` to accept `?` +LL | let mut _file = File::create("foo.txt")?; + | ^ cannot use the `?` operator in a function that returns `()` + | + = help: the trait `FromResidual>` is not implemented for `()` +help: consider adding return type + | +LL ~ fn test2() -> Result<(), Box> { +LL | let mut _file = File::create("foo.txt")?; +LL | +LL | println!(); +LL + +LL + Ok(()) +LL + } + | + +error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) + --> $DIR/return-from-residual-sugg-issue-125997.rs:31:44 + | +LL | fn main() { + | --------- this function should return `Result` or `Option` to accept `?` +LL | let mut _file = File::create("foo.txt")?; + | ^ cannot use the `?` operator in a function that returns `()` + | + = help: the trait `FromResidual>` is not implemented for `()` +help: consider adding return type + | +LL ~ fn main() -> Result<(), Box> { +LL | let mut _file = File::create("foo.txt")?; +LL | +LL | mac!(); +LL + +LL + Ok(()) +LL + } + | + +error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) + --> $DIR/return-from-residual-sugg-issue-125997.rs:23:52 + | +LL | fn test3() { + | ---------- this function should return `Result` or `Option` to accept `?` +LL | let mut _file = File::create("foo.txt")?; + | ^ cannot use the `?` operator in a function that returns `()` +... +LL | mac!(); + | ------ in this macro invocation + | + = help: the trait `FromResidual>` is not implemented for `()` + = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider adding return type + | +LL ~ fn test3() -> Result<(), Box> { +LL | let mut _file = File::create("foo.txt")?; +LL | +LL | println!(); +LL ~ +LL + Ok(()) +LL + } + | + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/return-nil.rs b/tests/ui/return/return-nil.rs similarity index 100% rename from tests/ui/return-nil.rs rename to tests/ui/return/return-nil.rs diff --git a/tests/ui/return/return-ty-mismatch-note.rs b/tests/ui/return/return-ty-mismatch-note.rs index 352bc2a16376..36d590519fba 100644 --- a/tests/ui/return/return-ty-mismatch-note.rs +++ b/tests/ui/return/return-ty-mismatch-note.rs @@ -1,4 +1,5 @@ -// Checks existence of a note for "a caller chooses ty for ty param" upon return ty mismatch. +// Checks existence or absence of a note for "a caller chooses ty for ty param" upon return ty +// mismatch. fn f() -> (T,) { (0,) //~ ERROR mismatched types @@ -14,6 +15,14 @@ fn h() -> u8 { 0u8 } +// This case was reported in where it doesn't +// make sense to make the "note caller chooses ty for ty param" note if the found type contains +// the ty param... +fn k(_t: &T) -> T { + _t + //~^ ERROR mismatched types +} + fn main() { f::<()>(); g::<(), ()>; diff --git a/tests/ui/return/return-ty-mismatch-note.stderr b/tests/ui/return/return-ty-mismatch-note.stderr index 135903da5c26..47ef6863063c 100644 --- a/tests/ui/return/return-ty-mismatch-note.stderr +++ b/tests/ui/return/return-ty-mismatch-note.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/return-ty-mismatch-note.rs:4:6 + --> $DIR/return-ty-mismatch-note.rs:5:6 | LL | fn f() -> (T,) { | - expected this type parameter @@ -10,7 +10,7 @@ LL | (0,) found type `{integer}` error[E0308]: mismatched types - --> $DIR/return-ty-mismatch-note.rs:8:6 + --> $DIR/return-ty-mismatch-note.rs:9:6 | LL | fn g() -> (U, V) { | - expected this type parameter @@ -21,7 +21,7 @@ LL | (0, "foo") found type `{integer}` error[E0308]: mismatched types - --> $DIR/return-ty-mismatch-note.rs:8:9 + --> $DIR/return-ty-mismatch-note.rs:9:9 | LL | fn g() -> (U, V) { | - expected this type parameter @@ -31,6 +31,19 @@ LL | (0, "foo") = note: expected type parameter `V` found reference `&'static str` -error: aborting due to 3 previous errors +error[E0308]: mismatched types + --> $DIR/return-ty-mismatch-note.rs:22:5 + | +LL | fn k(_t: &T) -> T { + | - - expected `T` because of return type + | | + | expected this type parameter +LL | _t + | ^^ expected type parameter `T`, found `&T` + | + = note: expected type parameter `_` + found reference `&_` + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/return/tail-expr-if-as-return.rs b/tests/ui/return/tail-expr-if-as-return.rs new file mode 100644 index 000000000000..119ffccc6a94 --- /dev/null +++ b/tests/ui/return/tail-expr-if-as-return.rs @@ -0,0 +1,5 @@ +fn main() { + if true { + "" //~ ERROR mismatched types [E0308] + } +} diff --git a/tests/ui/return/tail-expr-if-as-return.stderr b/tests/ui/return/tail-expr-if-as-return.stderr new file mode 100644 index 000000000000..2631f1e426de --- /dev/null +++ b/tests/ui/return/tail-expr-if-as-return.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/tail-expr-if-as-return.rs:3:9 + | +LL | / if true { +LL | | "" + | | ^^ expected `()`, found `&str` +LL | | } + | |_____- expected this to be `()` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-embedded.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-embedded.rs deleted file mode 100644 index 22ea6f7534a7..000000000000 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-embedded.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Test explores how `#[structral_match]` behaves in tandem with -// `*const` and `*mut` pointers. - -//@ run-pass - -#![warn(pointer_structural_match)] - -struct NoDerive(#[allow(dead_code)] i32); - -// This impl makes NoDerive irreflexive -// (which doesn't matter here because `<*const T>::eq` won't recur on `T`). -impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } - -impl Eq for NoDerive { } - -#[derive(PartialEq, Eq)] -struct WrapEmbedded(*const NoDerive); - -const WRAP_UNSAFE_EMBEDDED: WrapEmbedded = WrapEmbedded(std::ptr::null()); - -fn main() { - match WRAP_UNSAFE_EMBEDDED { - WRAP_UNSAFE_EMBEDDED => { println!("WRAP_UNSAFE_EMBEDDED correctly matched itself"); } - _ => { panic!("WRAP_UNSAFE_EMBEDDED did not match itself"); } - } -} diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-param.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-param.rs deleted file mode 100644 index cd513d2aff4d..000000000000 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-param.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Test explores how `#[structral_match]` behaves in tandem with -// `*const` and `*mut` pointers. - -//@ run-pass - -#![warn(pointer_structural_match)] - -struct NoDerive(#[allow(dead_code)] i32); - -// This impl makes NoDerive irreflexive -// (which doesn't matter here because `<*const T>::eq` won't recur on `T`). -impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } - -impl Eq for NoDerive { } - -#[derive(PartialEq, Eq)] -struct WrapParam(*const X); - -const WRAP_UNSAFE_PARAM: WrapParam = WrapParam(std::ptr::null()); - -fn main() { - match WRAP_UNSAFE_PARAM { - WRAP_UNSAFE_PARAM => { println!("WRAP_UNSAFE_PARAM correctly matched itself"); } - _ => { panic!("WRAP_UNSAFE_PARAM did not match itself"); } - } -} diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-embedded.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-embedded.rs deleted file mode 100644 index 9595d00876bc..000000000000 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-embedded.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Test explores how `#[structral_match]` behaves in tandem with -// `*const` and `*mut` pointers. - -//@ run-pass - -#![warn(pointer_structural_match)] - -struct NoDerive(#[allow(dead_code)] i32); - -// This impl makes NoDerive irreflexive -// (which doesn't matter here because `<*const T>::eq` won't recur on `T`). -impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } - -impl Eq for NoDerive { } - -#[derive(PartialEq, Eq)] -struct WrapEmbedded(*const NoDerive); - -const WRAP_UNSAFE_EMBEDDED: & &WrapEmbedded = & &WrapEmbedded(std::ptr::null()); - -fn main() { - match WRAP_UNSAFE_EMBEDDED { - WRAP_UNSAFE_EMBEDDED => { println!("WRAP_UNSAFE_EMBEDDED correctly matched itself"); } - _ => { panic!("WRAP_UNSAFE_EMBEDDED did not match itself"); } - } -} diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-param.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-param.rs deleted file mode 100644 index 9dce827a57cb..000000000000 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-param.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Test explores how `#[structral_match]` behaves in tandem with -// `*const` and `*mut` pointers. - -//@ run-pass - -#![warn(pointer_structural_match)] - -struct NoDerive(#[allow(dead_code)] i32); - -// This impl makes NoDerive irreflexive -// (which doesn't matter here because `<*const T>::eq` won't recur on `T`). -impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } - -impl Eq for NoDerive { } - -#[derive(PartialEq, Eq)] -struct WrapParam(*const X); - -const WRAP_UNSAFE_PARAM: & &WrapParam = & &WrapParam(std::ptr::null()); - -fn main() { - match WRAP_UNSAFE_PARAM { - WRAP_UNSAFE_PARAM => { println!("WRAP_UNSAFE_PARAM correctly matched itself"); } - _ => { panic!("WRAP_UNSAFE_PARAM did not match itself"); } - } -} diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.rs index 112021c783fc..f840b4040b65 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.rs +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.rs @@ -4,7 +4,6 @@ // through that we had intended to reject. // // See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 -#![warn(indirect_structural_match)] struct NoDerive(i32); // This impl makes NoDerive irreflexive. diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr index 8bca7d9889cd..3d00ef2dfbf6 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr @@ -1,5 +1,5 @@ error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/cant-hide-behind-direct-struct-param.rs:22:9 + --> $DIR/cant-hide-behind-direct-struct-param.rs:21:9 | LL | WRAP_DIRECT_PARAM => { panic!("WRAP_DIRECT_PARAM matched itself"); } | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.rs index b64fbd9d49a1..898acefc83cc 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.rs +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.rs @@ -4,8 +4,6 @@ // through that we had intended to reject. // // See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 -#![warn(indirect_structural_match)] -//@ run-pass struct NoDerive(#[allow(dead_code)] i32); @@ -22,8 +20,7 @@ const WRAP_DOUBLY_INDIRECT_INLINE: & &WrapInline = & &WrapInline(& & NoDerive(0) fn main() { match WRAP_DOUBLY_INDIRECT_INLINE { WRAP_DOUBLY_INDIRECT_INLINE => { panic!("WRAP_DOUBLY_INDIRECT_INLINE matched itself"); } - //~^ WARN must be annotated with `#[derive(PartialEq)]` - //~| WARN this was previously accepted + //~^ ERROR must be annotated with `#[derive(PartialEq)]` _ => { println!("WRAP_DOUBLY_INDIRECT_INLINE correctly did not match itself"); } } } diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr index 9945041113d7..3636307e16c6 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr @@ -1,35 +1,11 @@ -warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/cant-hide-behind-doubly-indirect-embedded.rs:24:9 +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` + --> $DIR/cant-hide-behind-doubly-indirect-embedded.rs:22:9 | LL | WRAP_DOUBLY_INDIRECT_INLINE => { panic!("WRAP_DOUBLY_INDIRECT_INLINE matched itself"); } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = warning: 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 #120362 = note: the traits must be derived, manual `impl`s are not sufficient = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -note: the lint level is defined here - --> $DIR/cant-hide-behind-doubly-indirect-embedded.rs:7:9 - | -LL | #![warn(indirect_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: 1 warning emitted - -Future incompatibility report: Future breakage diagnostic: -warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/cant-hide-behind-doubly-indirect-embedded.rs:24:9 - | -LL | WRAP_DOUBLY_INDIRECT_INLINE => { panic!("WRAP_DOUBLY_INDIRECT_INLINE matched itself"); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #120362 - = note: the traits must be derived, manual `impl`s are not sufficient - = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -note: the lint level is defined here - --> $DIR/cant-hide-behind-doubly-indirect-embedded.rs:7:9 - | -LL | #![warn(indirect_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.rs index be37217a7d45..7cbaada88a30 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.rs +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.rs @@ -4,8 +4,6 @@ // through that we had intended to reject. // // See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 -#![warn(indirect_structural_match)] -//@ run-pass struct NoDerive(#[allow(dead_code)] i32); @@ -22,8 +20,7 @@ const WRAP_DOUBLY_INDIRECT_PARAM: & &WrapParam = & &WrapParam(& & NoDe fn main() { match WRAP_DOUBLY_INDIRECT_PARAM { WRAP_DOUBLY_INDIRECT_PARAM => { panic!("WRAP_DOUBLY_INDIRECT_PARAM matched itself"); } - //~^ WARN must be annotated with `#[derive(PartialEq)]` - //~| WARN this was previously accepted + //~^ ERROR must be annotated with `#[derive(PartialEq)]` _ => { println!("WRAP_DOUBLY_INDIRECT_PARAM correctly did not match itself"); } } } diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr index 6ac261ae8143..40fd31762b2f 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr @@ -1,35 +1,11 @@ -warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/cant-hide-behind-doubly-indirect-param.rs:24:9 +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` + --> $DIR/cant-hide-behind-doubly-indirect-param.rs:22:9 | LL | WRAP_DOUBLY_INDIRECT_PARAM => { panic!("WRAP_DOUBLY_INDIRECT_PARAM matched itself"); } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = warning: 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 #120362 = note: the traits must be derived, manual `impl`s are not sufficient = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -note: the lint level is defined here - --> $DIR/cant-hide-behind-doubly-indirect-param.rs:7:9 - | -LL | #![warn(indirect_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: 1 warning emitted - -Future incompatibility report: Future breakage diagnostic: -warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/cant-hide-behind-doubly-indirect-param.rs:24:9 - | -LL | WRAP_DOUBLY_INDIRECT_PARAM => { panic!("WRAP_DOUBLY_INDIRECT_PARAM matched itself"); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #120362 - = note: the traits must be derived, manual `impl`s are not sufficient - = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -note: the lint level is defined here - --> $DIR/cant-hide-behind-doubly-indirect-param.rs:7:9 - | -LL | #![warn(indirect_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.rs index e26234bd4553..ac868efed6fd 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.rs +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.rs @@ -4,8 +4,6 @@ // through that we had intended to reject. // // See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 -#![warn(indirect_structural_match)] -//@ run-pass struct NoDerive(#[allow(dead_code)] i32); @@ -22,8 +20,7 @@ const WRAP_INDIRECT_INLINE: & &WrapInline = & &WrapInline(NoDerive(0)); fn main() { match WRAP_INDIRECT_INLINE { WRAP_INDIRECT_INLINE => { panic!("WRAP_INDIRECT_INLINE matched itself"); } - //~^ WARN must be annotated with `#[derive(PartialEq)]` - //~| WARN this was previously accepted + //~^ ERROR must be annotated with `#[derive(PartialEq)]` _ => { println!("WRAP_INDIRECT_INLINE did not match itself"); } } } diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr index 41616fb90fe9..dbf1848326aa 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr @@ -1,35 +1,11 @@ -warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/cant-hide-behind-indirect-struct-embedded.rs:24:9 +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` + --> $DIR/cant-hide-behind-indirect-struct-embedded.rs:22:9 | LL | WRAP_INDIRECT_INLINE => { panic!("WRAP_INDIRECT_INLINE matched itself"); } | ^^^^^^^^^^^^^^^^^^^^ | - = warning: 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 #120362 = note: the traits must be derived, manual `impl`s are not sufficient = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -note: the lint level is defined here - --> $DIR/cant-hide-behind-indirect-struct-embedded.rs:7:9 - | -LL | #![warn(indirect_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: 1 warning emitted - -Future incompatibility report: Future breakage diagnostic: -warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/cant-hide-behind-indirect-struct-embedded.rs:24:9 - | -LL | WRAP_INDIRECT_INLINE => { panic!("WRAP_INDIRECT_INLINE matched itself"); } - | ^^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #120362 - = note: the traits must be derived, manual `impl`s are not sufficient - = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -note: the lint level is defined here - --> $DIR/cant-hide-behind-indirect-struct-embedded.rs:7:9 - | -LL | #![warn(indirect_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.rs index 729f411a0215..cbfabec6819e 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.rs +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.rs @@ -4,8 +4,6 @@ // through that we had intended to reject. // // See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 -#![warn(indirect_structural_match)] -//@ run-pass struct NoDerive(#[allow(dead_code)] i32); @@ -22,8 +20,7 @@ const WRAP_INDIRECT_PARAM: & &WrapParam = & &WrapParam(NoDerive(0)); fn main() { match WRAP_INDIRECT_PARAM { WRAP_INDIRECT_PARAM => { panic!("WRAP_INDIRECT_PARAM matched itself"); } - //~^ WARN must be annotated with `#[derive(PartialEq)]` - //~| WARN this was previously accepted + //~^ ERROR must be annotated with `#[derive(PartialEq)]` _ => { println!("WRAP_INDIRECT_PARAM correctly did not match itself"); } } } diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr index 99dea5171d1b..58acc11a7449 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr @@ -1,35 +1,11 @@ -warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/cant-hide-behind-indirect-struct-param.rs:24:9 +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` + --> $DIR/cant-hide-behind-indirect-struct-param.rs:22:9 | LL | WRAP_INDIRECT_PARAM => { panic!("WRAP_INDIRECT_PARAM matched itself"); } | ^^^^^^^^^^^^^^^^^^^ | - = warning: 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 #120362 = note: the traits must be derived, manual `impl`s are not sufficient = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -note: the lint level is defined here - --> $DIR/cant-hide-behind-indirect-struct-param.rs:7:9 - | -LL | #![warn(indirect_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: 1 warning emitted - -Future incompatibility report: Future breakage diagnostic: -warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq)]` - --> $DIR/cant-hide-behind-indirect-struct-param.rs:24:9 - | -LL | WRAP_INDIRECT_PARAM => { panic!("WRAP_INDIRECT_PARAM matched itself"); } - | ^^^^^^^^^^^^^^^^^^^ - | - = warning: 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 #120362 - = note: the traits must be derived, manual `impl`s are not sufficient - = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -note: the lint level is defined here - --> $DIR/cant-hide-behind-indirect-struct-param.rs:7:9 - | -LL | #![warn(indirect_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-structurally-matchable.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-not-structurally-matchable.rs similarity index 76% rename from tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-structurally-matchable.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-not-structurally-matchable.rs index 25434e0050f0..d572d12c5b1c 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-structurally-matchable.rs +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-not-structurally-matchable.rs @@ -1,7 +1,5 @@ -//@ run-pass - -// This file checks that fn ptrs are considered structurally matchable. -// See also rust-lang/rust#63479. +// This file checks that fn ptrs are *not* considered structurally matchable. +// See also rust-lang/rust#63479 and RFC 3535. fn main() { let mut count = 0; @@ -40,8 +38,7 @@ fn main() { const CFN1: Wrap = Wrap(trivial); let input: Wrap = Wrap(trivial); match Wrap(input) { - Wrap(CFN1) => count += 1, //~WARN behave unpredictably - //~| previously accepted + Wrap(CFN1) => count += 1, //~ERROR behave unpredictably Wrap(_) => {} }; @@ -49,8 +46,7 @@ fn main() { const CFN2: Wrap = Wrap(sm_to); let input: Wrap = Wrap(sm_to); match Wrap(input) { - Wrap(CFN2) => count += 1, //~WARN behave unpredictably - //~| previously accepted + Wrap(CFN2) => count += 1, //~ERROR behave unpredictably Wrap(_) => {} }; @@ -58,8 +54,7 @@ fn main() { const CFN3: Wrap SM> = Wrap(to_sm); let input: Wrap SM> = Wrap(to_sm); match Wrap(input) { - Wrap(CFN3) => count += 1, //~WARN behave unpredictably - //~| previously accepted + Wrap(CFN3) => count += 1, //~ERROR behave unpredictably Wrap(_) => {} }; @@ -67,8 +62,7 @@ fn main() { const CFN4: Wrap = Wrap(not_sm_to); let input: Wrap = Wrap(not_sm_to); match Wrap(input) { - Wrap(CFN4) => count += 1, //~WARN behave unpredictably - //~| previously accepted + Wrap(CFN4) => count += 1, //~ERROR behave unpredictably Wrap(_) => {} }; @@ -76,8 +70,7 @@ fn main() { const CFN5: Wrap NotSM> = Wrap(to_not_sm); let input: Wrap NotSM> = Wrap(to_not_sm); match Wrap(input) { - Wrap(CFN5) => count += 1, //~WARN behave unpredictably - //~| previously accepted + Wrap(CFN5) => count += 1, //~ERROR behave unpredictably Wrap(_) => {} }; @@ -85,8 +78,7 @@ fn main() { const CFN6: Wrap = Wrap(r_sm_to); let input: Wrap = Wrap(r_sm_to); match Wrap(input) { - Wrap(CFN6) => count += 1, //~WARN behave unpredictably - //~| previously accepted + Wrap(CFN6) => count += 1, //~ERROR behave unpredictably Wrap(_) => {} }; @@ -94,8 +86,7 @@ fn main() { const CFN7: Wrap &SM> = Wrap(r_to_r_sm); let input: Wrap &SM> = Wrap(r_to_r_sm); match Wrap(input) { - Wrap(CFN7) => count += 1, //~WARN behave unpredictably - //~| previously accepted + Wrap(CFN7) => count += 1, //~ERROR behave unpredictably Wrap(_) => {} }; @@ -103,8 +94,7 @@ fn main() { const CFN8: Wrap = Wrap(r_not_sm_to); let input: Wrap = Wrap(r_not_sm_to); match Wrap(input) { - Wrap(CFN8) => count += 1, //~WARN behave unpredictably - //~| previously accepted + Wrap(CFN8) => count += 1, //~ERROR behave unpredictably Wrap(_) => {} }; @@ -112,8 +102,7 @@ fn main() { const CFN9: Wrap &NotSM> = Wrap(r_to_r_not_sm); let input: Wrap &NotSM> = Wrap(r_to_r_not_sm); match Wrap(input) { - Wrap(CFN9) => count += 1, //~WARN behave unpredictably - //~| previously accepted + Wrap(CFN9) => count += 1, //~ERROR behave unpredictably Wrap(_) => {} }; @@ -135,8 +124,7 @@ fn main() { let input = Foo { alpha: not_sm_to, beta: to_not_sm, gamma: sm_to, delta: to_sm }; match input { - CFOO => count += 1, //~WARN behave unpredictably - //~| previously accepted + CFOO => count += 1, //~ERROR behave unpredictably Foo { .. } => {} }; diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-not-structurally-matchable.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-not-structurally-matchable.stderr new file mode 100644 index 000000000000..0bc1e7fc89b5 --- /dev/null +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-not-structurally-matchable.stderr @@ -0,0 +1,62 @@ +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/fn-ptr-is-not-structurally-matchable.rs:41:14 + | +LL | Wrap(CFN1) => count += 1, + | ^^^^ + +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/fn-ptr-is-not-structurally-matchable.rs:49:14 + | +LL | Wrap(CFN2) => count += 1, + | ^^^^ + +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/fn-ptr-is-not-structurally-matchable.rs:57:14 + | +LL | Wrap(CFN3) => count += 1, + | ^^^^ + +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/fn-ptr-is-not-structurally-matchable.rs:65:14 + | +LL | Wrap(CFN4) => count += 1, + | ^^^^ + +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/fn-ptr-is-not-structurally-matchable.rs:73:14 + | +LL | Wrap(CFN5) => count += 1, + | ^^^^ + +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/fn-ptr-is-not-structurally-matchable.rs:81:14 + | +LL | Wrap(CFN6) => count += 1, + | ^^^^ + +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/fn-ptr-is-not-structurally-matchable.rs:89:14 + | +LL | Wrap(CFN7) => count += 1, + | ^^^^ + +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/fn-ptr-is-not-structurally-matchable.rs:97:14 + | +LL | Wrap(CFN8) => count += 1, + | ^^^^ + +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/fn-ptr-is-not-structurally-matchable.rs:105:14 + | +LL | Wrap(CFN9) => count += 1, + | ^^^^ + +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/fn-ptr-is-not-structurally-matchable.rs:127:9 + | +LL | CFOO => count += 1, + | ^^^^ + +error: aborting due to 10 previous errors + diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-structurally-matchable.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-structurally-matchable.stderr deleted file mode 100644 index 11163ba70ec6..000000000000 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-structurally-matchable.stderr +++ /dev/null @@ -1,203 +0,0 @@ -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:43:14 - | -LL | Wrap(CFN1) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:52:14 - | -LL | Wrap(CFN2) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:61:14 - | -LL | Wrap(CFN3) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:70:14 - | -LL | Wrap(CFN4) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:79:14 - | -LL | Wrap(CFN5) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:88:14 - | -LL | Wrap(CFN6) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:97:14 - | -LL | Wrap(CFN7) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:106:14 - | -LL | Wrap(CFN8) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:115:14 - | -LL | Wrap(CFN9) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:138:9 - | -LL | CFOO => count += 1, - | ^^^^ - | - = warning: 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 #120362 - -warning: 10 warnings emitted - -Future incompatibility report: Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:43:14 - | -LL | Wrap(CFN1) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:52:14 - | -LL | Wrap(CFN2) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:61:14 - | -LL | Wrap(CFN3) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:70:14 - | -LL | Wrap(CFN4) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:79:14 - | -LL | Wrap(CFN5) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:88:14 - | -LL | Wrap(CFN6) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:97:14 - | -LL | Wrap(CFN7) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:106:14 - | -LL | Wrap(CFN8) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:115:14 - | -LL | Wrap(CFN9) => count += 1, - | ^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/fn-ptr-is-structurally-matchable.rs:138:9 - | -LL | CFOO => count += 1, - | ^^^^ - | - = warning: 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 #120362 - = note: `#[warn(pointer_structural_match)]` on by default - diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.rs index 81618b3b791e..0fa2370c95bf 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.rs +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.rs @@ -10,8 +10,6 @@ // Issue 62307 pointed out a case where the structural-match checking // was too shallow. -#![warn(indirect_structural_match)] -//@ run-pass #[derive(Debug)] struct B(i32); @@ -29,15 +27,13 @@ fn main() { match RR_B0 { RR_B1 => { println!("CLAIM RR0: {:?} matches {:?}", RR_B1, RR_B0); } - //~^ WARN must be annotated with `#[derive(PartialEq)]` - //~| WARN this was previously accepted + //~^ ERROR must be annotated with `#[derive(PartialEq)]` _ => { } } match RR_B1 { RR_B1 => { println!("CLAIM RR1: {:?} matches {:?}", RR_B1, RR_B1); } - //~^ WARN must be annotated with `#[derive(PartialEq)]` - //~| WARN this was previously accepted + //~^ ERROR must be annotated with `#[derive(PartialEq)]` _ => { } } } diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr index d4ab1ce3ba2a..e79b05fdf9dc 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr @@ -1,63 +1,20 @@ -warning: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq)]` - --> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:31:9 +error: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq)]` + --> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:29:9 | LL | RR_B1 => { println!("CLAIM RR0: {:?} matches {:?}", RR_B1, RR_B0); } | ^^^^^ | - = warning: 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 #120362 = note: the traits must be derived, manual `impl`s are not sufficient = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -note: the lint level is defined here - --> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:13:9 - | -LL | #![warn(indirect_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq)]` - --> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:38:9 +error: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq)]` + --> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:35:9 | LL | RR_B1 => { println!("CLAIM RR1: {:?} matches {:?}", RR_B1, RR_B1); } | ^^^^^ | - = warning: 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 #120362 = note: the traits must be derived, manual `impl`s are not sufficient = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -warning: 2 warnings emitted - -Future incompatibility report: Future breakage diagnostic: -warning: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq)]` - --> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:31:9 - | -LL | RR_B1 => { println!("CLAIM RR0: {:?} matches {:?}", RR_B1, RR_B0); } - | ^^^^^ - | - = warning: 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 #120362 - = note: the traits must be derived, manual `impl`s are not sufficient - = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -note: the lint level is defined here - --> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:13:9 - | -LL | #![warn(indirect_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -warning: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq)]` - --> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:38:9 - | -LL | RR_B1 => { println!("CLAIM RR1: {:?} matches {:?}", RR_B1, RR_B1); } - | ^^^^^ - | - = warning: 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 #120362 - = note: the traits must be derived, manual `impl`s are not sufficient - = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -note: the lint level is defined here - --> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:13:9 - | -LL | #![warn(indirect_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +error: aborting due to 2 previous errors diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs index 634aaf8115f2..9ceb72b68721 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs @@ -1,12 +1,8 @@ -//@ run-pass - // The actual regression test from #63479. (Including this because my // first draft at fn-ptr-is-structurally-matchable.rs failed to actually // cover the case this hit; I've since expanded it accordingly, but the // experience left me wary of leaving this regression test out.) -#![warn(pointer_structural_match)] - #[derive(Eq)] struct A { a: i64 @@ -34,14 +30,12 @@ fn main() { let s = B(my_fn); match s { B(TEST) => println!("matched"), - //~^ WARN behave unpredictably - //~| WARN this was previously accepted by the compiler but is being phased out + //~^ ERROR behave unpredictably _ => panic!("didn't match") }; match (s.0, 0) { TEST2 => println!("matched"), - //~^ WARN behave unpredictably - //~| WARN this was previously accepted by the compiler but is being phased out + //~^ ERROR behave unpredictably _ => panic!("didn't match") } } diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr index 0edcf44c4d79..7b1832ed0fa5 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr @@ -1,55 +1,14 @@ -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-63479-match-fnptr.rs:36:7 +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/issue-63479-match-fnptr.rs:32:7 | LL | B(TEST) => println!("matched"), | ^^^^ - | - = warning: 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 #120362 -note: the lint level is defined here - --> $DIR/issue-63479-match-fnptr.rs:8:9 - | -LL | #![warn(pointer_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-63479-match-fnptr.rs:42:5 +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/issue-63479-match-fnptr.rs:37:5 | LL | TEST2 => println!("matched"), | ^^^^^ - | - = warning: 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 #120362 -warning: 2 warnings emitted - -Future incompatibility report: Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-63479-match-fnptr.rs:36:7 - | -LL | B(TEST) => println!("matched"), - | ^^^^ - | - = warning: 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 #120362 -note: the lint level is defined here - --> $DIR/issue-63479-match-fnptr.rs:8:9 - | -LL | #![warn(pointer_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -Future breakage diagnostic: -warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. - --> $DIR/issue-63479-match-fnptr.rs:42:5 - | -LL | TEST2 => println!("matched"), - | ^^^^^ - | - = warning: 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 #120362 -note: the lint level is defined here - --> $DIR/issue-63479-match-fnptr.rs:8:9 - | -LL | #![warn(pointer_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ +error: aborting due to 2 previous errors diff --git a/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr index 7f989c15f1c0..d0c084f7bd5d 100644 --- a/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr +++ b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr @@ -4,10 +4,8 @@ error[E0432]: unresolved import `alloc` LL | use alloc; | ^^^^^ no external crate `alloc` | -help: consider importing one of these items instead +help: consider importing this module instead | -LL | use core::alloc; - | ~~~~~~~~~~~ LL | use std::alloc; | ~~~~~~~~~~ diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr index 67bfaa4c98c7..4c07f4d6b990 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr @@ -6,7 +6,7 @@ LL | call(foo); | | | required by a bound introduced by this call | - = help: the trait `Fn<()>` is not implemented for fn item `fn() {foo}` + = help: the trait `Fn()` is not implemented for fn item `fn() {foo}` = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits note: required by a bound in `call` @@ -23,7 +23,7 @@ LL | call_mut(foo); | | | required by a bound introduced by this call | - = help: the trait `FnMut<()>` is not implemented for fn item `fn() {foo}` + = help: the trait `FnMut()` is not implemented for fn item `fn() {foo}` = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits note: required by a bound in `call_mut` @@ -40,7 +40,7 @@ LL | call_once(foo); | | | required by a bound introduced by this call | - = help: the trait `FnOnce<()>` is not implemented for fn item `fn() {foo}` + = help: the trait `FnOnce()` is not implemented for fn item `fn() {foo}` = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits note: required by a bound in `call_once` @@ -57,7 +57,7 @@ LL | call(foo_unsafe); | | | required by a bound introduced by this call | - = help: the trait `Fn<()>` is not implemented for fn item `unsafe fn() {foo_unsafe}` + = help: the trait `Fn()` is not implemented for fn item `unsafe fn() {foo_unsafe}` = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits @@ -75,7 +75,7 @@ LL | call_mut(foo_unsafe); | | | required by a bound introduced by this call | - = help: the trait `FnMut<()>` is not implemented for fn item `unsafe fn() {foo_unsafe}` + = help: the trait `FnMut()` is not implemented for fn item `unsafe fn() {foo_unsafe}` = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits @@ -93,7 +93,7 @@ LL | call_once(foo_unsafe); | | | required by a bound introduced by this call | - = help: the trait `FnOnce<()>` is not implemented for fn item `unsafe fn() {foo_unsafe}` + = help: the trait `FnOnce()` is not implemented for fn item `unsafe fn() {foo_unsafe}` = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr index aa660db38679..537819ab8595 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr @@ -91,7 +91,7 @@ LL | const _: () = sse2_and_fxsr(); = help: in order for the call to be safe, the context requires the following additional target features: sse2 and fxsr = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` -error: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe block (error E0133) +error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe block --> $DIR/safe-calls.rs:70:5 | LL | sse2(); diff --git a/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr b/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr deleted file mode 100644 index 284dacf7000b..000000000000 --- a/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error[E0277]: the trait bound `u8: Bar` is not satisfied - --> $DIR/feature-gate-do_not_recommend.rs:19:11 - | -LL | stuff(1u8); - | ----- ^^^ the trait `Foo` is not implemented for `u8`, which is required by `u8: Bar` - | | - | required by a bound introduced by this call - | - = help: the trait `Foo` is implemented for `i32` -note: required for `u8` to implement `Bar` - --> $DIR/feature-gate-do_not_recommend.rs:13:14 - | -LL | impl Bar for T { - | --- ^^^ ^ - | | - | unsatisfied trait bound introduced here -note: required by a bound in `stuff` - --> $DIR/feature-gate-do_not_recommend.rs:16:13 - | -LL | fn stuff(_: T) {} - | ^^^ required by this bound in `stuff` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.rs b/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.rs deleted file mode 100644 index 91863f5e4978..000000000000 --- a/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.rs +++ /dev/null @@ -1,45 +0,0 @@ -#![feature(do_not_recommend)] - -#[do_not_recommend] -//~^ `#[do_not_recommend]` can only be placed -const CONST: () = (); - -#[do_not_recommend] -//~^ `#[do_not_recommend]` can only be placed -static Static: () = (); - -#[do_not_recommend] -//~^ `#[do_not_recommend]` can only be placed -type Type = (); - -#[do_not_recommend] -//~^ `#[do_not_recommend]` can only be placed -enum Enum { -} - -#[do_not_recommend] -//~^ `#[do_not_recommend]` can only be placed -extern { -} - -#[do_not_recommend] -//~^ `#[do_not_recommend]` can only be placed -fn fun() { -} - -#[do_not_recommend] -//~^ `#[do_not_recommend]` can only be placed -struct Struct { -} - -#[do_not_recommend] -//~^ `#[do_not_recommend]` can only be placed -trait Trait { -} - -#[do_not_recommend] -impl Trait for i32 { -} - -fn main() { -} diff --git a/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.stderr b/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.stderr deleted file mode 100644 index 01ebc23c86e1..000000000000 --- a/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.stderr +++ /dev/null @@ -1,50 +0,0 @@ -error: `#[do_not_recommend]` can only be placed on trait implementations - --> $DIR/incorrect-locations.rs:3:1 - | -LL | #[do_not_recommend] - | ^^^^^^^^^^^^^^^^^^^ - -error: `#[do_not_recommend]` can only be placed on trait implementations - --> $DIR/incorrect-locations.rs:7:1 - | -LL | #[do_not_recommend] - | ^^^^^^^^^^^^^^^^^^^ - -error: `#[do_not_recommend]` can only be placed on trait implementations - --> $DIR/incorrect-locations.rs:11:1 - | -LL | #[do_not_recommend] - | ^^^^^^^^^^^^^^^^^^^ - -error: `#[do_not_recommend]` can only be placed on trait implementations - --> $DIR/incorrect-locations.rs:15:1 - | -LL | #[do_not_recommend] - | ^^^^^^^^^^^^^^^^^^^ - -error: `#[do_not_recommend]` can only be placed on trait implementations - --> $DIR/incorrect-locations.rs:20:1 - | -LL | #[do_not_recommend] - | ^^^^^^^^^^^^^^^^^^^ - -error: `#[do_not_recommend]` can only be placed on trait implementations - --> $DIR/incorrect-locations.rs:25:1 - | -LL | #[do_not_recommend] - | ^^^^^^^^^^^^^^^^^^^ - -error: `#[do_not_recommend]` can only be placed on trait implementations - --> $DIR/incorrect-locations.rs:30:1 - | -LL | #[do_not_recommend] - | ^^^^^^^^^^^^^^^^^^^ - -error: `#[do_not_recommend]` can only be placed on trait implementations - --> $DIR/incorrect-locations.rs:35:1 - | -LL | #[do_not_recommend] - | ^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 8 previous errors - diff --git a/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.rs b/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.rs deleted file mode 100644 index f0c5c222e786..000000000000 --- a/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.rs +++ /dev/null @@ -1,10 +0,0 @@ -trait Foo { -} - -#[do_not_recommend] -//~^ ERROR the `#[do_not_recommend]` attribute is an experimental feature -impl Foo for i32 { -} - -fn main() { -} diff --git a/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.stderr b/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.stderr deleted file mode 100644 index 02bc51ccd3f5..000000000000 --- a/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0658]: the `#[do_not_recommend]` attribute is an experimental feature - --> $DIR/unstable-feature.rs:4:1 - | -LL | #[do_not_recommend] - | ^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #51992 for more information - = help: add `#![feature(do_not_recommend)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.rs index 402efaf50271..ea9d48e7859d 100644 --- a/tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.rs +++ b/tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.rs @@ -3,8 +3,8 @@ //@ needs-dlltool //@ compile-flags: --crate-type lib --emit link //@ normalize-stderr-test: "[^ ']*/dlltool.exe" -> "$$DLLTOOL" -//@ normalize-stderr-test: "[^ ]*/foo.def" -> "$$DEF_FILE" -//@ normalize-stderr-test: "[^ ]*/foo.lib" -> "$$LIB_FILE" +//@ normalize-stderr-test: "[^ ]*/foo.dll_imports.def" -> "$$DEF_FILE" +//@ normalize-stderr-test: "[^ ]*/foo.dll_imports.lib" -> "$$LIB_FILE" //@ normalize-stderr-test: "-m [^ ]*" -> "$$TARGET_MACHINE" //@ normalize-stderr-test: "-f [^ ]*" -> "$$ASM_FLAGS" //@ normalize-stderr-test: "--temp-prefix [^ ]*/foo.dll" -> "$$TEMP_PREFIX" diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr index 151bd6facf72..fb2e66db1d48 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr @@ -13,8 +13,8 @@ LL | x(()) = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants help: consider further restricting this bound | -LL | const fn need_const_closure i32 + ~const std::ops::FnOnce<((),)>>(x: T) -> i32 { - | ++++++++++++++++++++++++++++++++ +LL | const fn need_const_closure i32 + ~const FnOnce(())>(x: T) -> i32 { + | +++++++++++++++++++ help: add `#![feature(effects)]` to the crate attributes to enable | LL + #![feature(effects)] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method.stderr index e2b3e3527012..dede411e69c7 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method.stderr @@ -13,8 +13,8 @@ LL | x(()) = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants help: consider further restricting this bound | -LL | const fn need_const_closure i32 + ~const std::ops::FnOnce<((),)>>(x: T) -> i32 { - | ++++++++++++++++++++++++++++++++ +LL | const fn need_const_closure i32 + ~const FnOnce(())>(x: T) -> i32 { + | +++++++++++++++++++ help: add `#![feature(effects)]` to the crate attributes to enable | LL + #![feature(effects)] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closures.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closures.stderr index e5a773123e90..a0f053253892 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closures.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closures.stderr @@ -31,8 +31,8 @@ LL | f() + f() = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants help: consider further restricting this bound | -LL | const fn answer u8 + ~const std::ops::Fn<()>>(f: &F) -> u8 { - | +++++++++++++++++++++++++ +LL | const fn answer u8 + ~const Fn()>(f: &F) -> u8 { + | +++++++++++++ help: add `#![feature(effects)]` to the crate attributes to enable | LL + #![feature(effects)] @@ -47,8 +47,8 @@ LL | f() + f() = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants help: consider further restricting this bound | -LL | const fn answer u8 + ~const std::ops::Fn<()>>(f: &F) -> u8 { - | +++++++++++++++++++++++++ +LL | const fn answer u8 + ~const Fn()>(f: &F) -> u8 { + | +++++++++++++ help: add `#![feature(effects)]` to the crate attributes to enable | LL + #![feature(effects)] @@ -63,8 +63,8 @@ LL | f() * 7 = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants help: consider further restricting this bound | -LL | F: ~const FnOnce() -> u8 + ~const std::ops::Fn<()>, - | +++++++++++++++++++++++++ +LL | F: ~const FnOnce() -> u8 + ~const Fn(), + | +++++++++++++ help: add `#![feature(effects)]` to the crate attributes to enable | LL + #![feature(effects)] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/spec-effectvar-ice.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/spec-effectvar-ice.rs new file mode 100644 index 000000000000..6b5ba5bb6241 --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/spec-effectvar-ice.rs @@ -0,0 +1,22 @@ +//@ check-fail +// Fixes #119830 + +#![feature(effects)] +#![feature(min_specialization)] +#![feature(const_trait_impl)] + +trait Specialize {} + +trait Foo {} + +impl const Foo for T {} +//~^ error: const `impl` for trait `Foo` which is not marked with `#[const_trait]` +//~| error: the const parameter `host` is not constrained by the impl trait, self type, or predicates [E0207] + +impl const Foo for T where T: const Specialize {} +//~^ error: const `impl` for trait `Foo` which is not marked with `#[const_trait]` +//~| error: `const` can only be applied to `#[const_trait]` traits +//~| error: the const parameter `host` is not constrained by the impl trait, self type, or predicates [E0207] +//~| error: conflicting implementations of trait `Foo` + +fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/spec-effectvar-ice.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/spec-effectvar-ice.stderr new file mode 100644 index 000000000000..70dd0350dc42 --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/spec-effectvar-ice.stderr @@ -0,0 +1,61 @@ +error: const `impl` for trait `Foo` which is not marked with `#[const_trait]` + --> $DIR/spec-effectvar-ice.rs:12:15 + | +LL | trait Foo {} + | - help: mark `Foo` as const: `#[const_trait]` +LL | +LL | impl const Foo for T {} + | ^^^ + | + = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` + = note: adding a non-const method body in the future would be a breaking change + +error: const `impl` for trait `Foo` which is not marked with `#[const_trait]` + --> $DIR/spec-effectvar-ice.rs:16:15 + | +LL | trait Foo {} + | - help: mark `Foo` as const: `#[const_trait]` +... +LL | impl const Foo for T where T: const Specialize {} + | ^^^ + | + = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const` + = note: adding a non-const method body in the future would be a breaking change + +error: `const` can only be applied to `#[const_trait]` traits + --> $DIR/spec-effectvar-ice.rs:16:40 + | +LL | impl const Foo for T where T: const Specialize {} + | ^^^^^^^^^^ + +error[E0207]: the const parameter `host` is not constrained by the impl trait, self type, or predicates + --> $DIR/spec-effectvar-ice.rs:12:9 + | +LL | impl const Foo for T {} + | ^^^^^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error[E0119]: conflicting implementations of trait `Foo` + --> $DIR/spec-effectvar-ice.rs:16:1 + | +LL | impl const Foo for T {} + | ----------------------- first implementation here +... +LL | impl const Foo for T where T: const Specialize {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation + +error[E0207]: the const parameter `host` is not constrained by the impl trait, self type, or predicates + --> $DIR/spec-effectvar-ice.rs:16:9 + | +LL | impl const Foo for T where T: const Specialize {} + | ^^^^^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0119, E0207. +For more information about an error, try `rustc --explain E0119`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/nested-closure.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/nested-closure.rs index d43fabcedec5..7bd372c16952 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/nested-closure.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/nested-closure.rs @@ -1,6 +1,6 @@ //@ check-pass -#![feature(const_trait_impl, lazy_cell)] +#![feature(const_trait_impl)] use std::sync::LazyLock; diff --git a/tests/ui/runtime/on-broken-pipe/error.rs b/tests/ui/runtime/on-broken-pipe/error.rs index ab2036c2f415..0a020873df08 100644 --- a/tests/ui/runtime/on-broken-pipe/error.rs +++ b/tests/ui/runtime/on-broken-pipe/error.rs @@ -1,6 +1,7 @@ //@ run-pass //@ aux-build:sigpipe-utils.rs //@ compile-flags: -Zon-broken-pipe=error +//@ only-unix because SIGPIPE is a unix thing fn main() { extern crate sigpipe_utils; diff --git a/tests/ui/runtime/on-broken-pipe/inherit.rs b/tests/ui/runtime/on-broken-pipe/inherit.rs index 64909b735281..f3c8140eaaef 100644 --- a/tests/ui/runtime/on-broken-pipe/inherit.rs +++ b/tests/ui/runtime/on-broken-pipe/inherit.rs @@ -4,6 +4,7 @@ //@ aux-bin: assert-inherit-sig_ign.rs //@ run-pass //@ compile-flags: -Zon-broken-pipe=kill +//@ only-unix because SIGPIPE is a unix thing #![feature(rustc_private)] diff --git a/tests/ui/runtime/on-broken-pipe/kill.rs b/tests/ui/runtime/on-broken-pipe/kill.rs index 5dace6f1c6ff..748e53ffcdbe 100644 --- a/tests/ui/runtime/on-broken-pipe/kill.rs +++ b/tests/ui/runtime/on-broken-pipe/kill.rs @@ -1,6 +1,7 @@ //@ run-pass //@ aux-build:sigpipe-utils.rs //@ compile-flags: -Zon-broken-pipe=kill +//@ only-unix because SIGPIPE is a unix thing fn main() { extern crate sigpipe_utils; diff --git a/tests/ui/runtime/on-broken-pipe/not-used.rs b/tests/ui/runtime/on-broken-pipe/not-used.rs index e31236f2b3df..22a26047874f 100644 --- a/tests/ui/runtime/on-broken-pipe/not-used.rs +++ b/tests/ui/runtime/on-broken-pipe/not-used.rs @@ -1,5 +1,6 @@ //@ run-pass //@ aux-build:sigpipe-utils.rs +//@ only-unix because SIGPIPE is a unix thing fn main() { extern crate sigpipe_utils; diff --git a/tests/ui/runtime/on-broken-pipe/with-rustc_main.rs b/tests/ui/runtime/on-broken-pipe/with-rustc_main.rs index c1731200038b..c40590ad87f4 100644 --- a/tests/ui/runtime/on-broken-pipe/with-rustc_main.rs +++ b/tests/ui/runtime/on-broken-pipe/with-rustc_main.rs @@ -1,6 +1,7 @@ //@ run-pass //@ aux-build:sigpipe-utils.rs //@ compile-flags: -Zon-broken-pipe=kill +//@ only-unix because SIGPIPE is a unix thing #![feature(rustc_attrs)] diff --git a/tests/ui/runtime/out-of-stack.rs b/tests/ui/runtime/out-of-stack.rs index ab2b50b293ce..c5300635ad92 100644 --- a/tests/ui/runtime/out-of-stack.rs +++ b/tests/ui/runtime/out-of-stack.rs @@ -7,22 +7,21 @@ //@ ignore-sgx no processes //@ ignore-fuchsia must translate zircon signal to SIGABRT, FIXME (#58590) //@ ignore-nto no stack overflow handler used (no alternate stack available) +//@ ignore-ios stack overflow handlers aren't enabled +//@ ignore-tvos stack overflow handlers aren't enabled +//@ ignore-watchos stack overflow handlers aren't enabled +//@ ignore-visionos stack overflow handlers aren't enabled -#![feature(core_intrinsics)] #![feature(rustc_private)] #[cfg(unix)] extern crate libc; use std::env; +use std::hint::black_box; use std::process::Command; use std::thread; -// Inlining to avoid llvm turning the recursive functions into tail calls, -// which doesn't consume stack. -#[inline(always)] -pub fn black_box(dummy: T) { std::intrinsics::black_box(dummy); } - fn silent_recurse() { let buf = [0u8; 1000]; black_box(buf); diff --git a/tests/ui/runtime/stdout-during-shutdown.rs b/tests/ui/runtime/stdout-during-shutdown-unix.rs similarity index 97% rename from tests/ui/runtime/stdout-during-shutdown.rs rename to tests/ui/runtime/stdout-during-shutdown-unix.rs index 8549f5d8eb6d..8e0f1d371ae5 100644 --- a/tests/ui/runtime/stdout-during-shutdown.rs +++ b/tests/ui/runtime/stdout-during-shutdown-unix.rs @@ -1,6 +1,7 @@ //@ run-pass //@ check-run-results //@ ignore-emscripten +//@ only-unix // Emscripten doesn't flush its own stdout buffers on exit, which would fail // this test. So this test is disabled on this platform. diff --git a/tests/ui/runtime/stdout-during-shutdown.run.stdout b/tests/ui/runtime/stdout-during-shutdown-unix.run.stdout similarity index 100% rename from tests/ui/runtime/stdout-during-shutdown.run.stdout rename to tests/ui/runtime/stdout-during-shutdown-unix.run.stdout diff --git a/tests/ui/runtime/stdout-during-shutdown-windows.rs b/tests/ui/runtime/stdout-during-shutdown-windows.rs new file mode 100644 index 000000000000..4644965b154c --- /dev/null +++ b/tests/ui/runtime/stdout-during-shutdown-windows.rs @@ -0,0 +1,20 @@ +//@ run-pass +//@ check-run-results +//@ only-windows + +struct Bye; + +impl Drop for Bye { + fn drop(&mut self) { + print!(", world!"); + } +} + +fn main() { + thread_local!{ + static BYE: Bye = Bye; + } + BYE.with(|_| { + print!("hello"); + }); +} diff --git a/tests/ui/runtime/stdout-during-shutdown-windows.run.stdout b/tests/ui/runtime/stdout-during-shutdown-windows.run.stdout new file mode 100644 index 000000000000..30f51a3fba52 --- /dev/null +++ b/tests/ui/runtime/stdout-during-shutdown-windows.run.stdout @@ -0,0 +1 @@ +hello, world! \ No newline at end of file diff --git a/tests/ui/rust-2018/issue-52202-use-suggestions.stderr b/tests/ui/rust-2018/issue-52202-use-suggestions.stderr index 49736205f204..ee1a336ea98f 100644 --- a/tests/ui/rust-2018/issue-52202-use-suggestions.stderr +++ b/tests/ui/rust-2018/issue-52202-use-suggestions.stderr @@ -4,7 +4,7 @@ error[E0422]: cannot find struct, variant or union type `Drain` in this scope LL | let _d = Drain {}; | ^^^^^ not found in this scope | -help: consider importing one of these items +help: consider importing one of these structs | LL + use crate::plumbing::Drain; | diff --git a/tests/ui/rust-2018/macro-use-warned-against.rs b/tests/ui/rust-2018/macro-use-warned-against.rs index 1e78acf201da..d6bb5150178a 100644 --- a/tests/ui/rust-2018/macro-use-warned-against.rs +++ b/tests/ui/rust-2018/macro-use-warned-against.rs @@ -4,7 +4,7 @@ #![warn(macro_use_extern_crate, unused)] -#[macro_use] //~ WARN should be replaced at use sites with a `use` item +#[macro_use] //~ WARN applying the `#[macro_use]` attribute to an `extern crate` item is deprecated extern crate macro_use_warned_against; #[macro_use] //~ WARN unused `#[macro_use]` extern crate macro_use_warned_against2; diff --git a/tests/ui/rust-2018/macro-use-warned-against.stderr b/tests/ui/rust-2018/macro-use-warned-against.stderr index 6b46f002e32e..3ac1f1270517 100644 --- a/tests/ui/rust-2018/macro-use-warned-against.stderr +++ b/tests/ui/rust-2018/macro-use-warned-against.stderr @@ -1,9 +1,10 @@ -warning: deprecated `#[macro_use]` attribute used to import macros should be replaced at use sites with a `use` item to import the macro instead +warning: applying the `#[macro_use]` attribute to an `extern crate` item is deprecated --> $DIR/macro-use-warned-against.rs:7:1 | LL | #[macro_use] | ^^^^^^^^^^^^ | + = help: remove it and import macros at use sites with a `use` item instead note: the lint level is defined here --> $DIR/macro-use-warned-against.rs:5:9 | diff --git a/tests/ui/rust-2021/inherent-dyn-collision.fixed b/tests/ui/rust-2021/inherent-dyn-collision.fixed index f5702613af03..8fe1bf811dbf 100644 --- a/tests/ui/rust-2021/inherent-dyn-collision.fixed +++ b/tests/ui/rust-2021/inherent-dyn-collision.fixed @@ -25,6 +25,7 @@ mod inner { // having a struct of the same name as the trait in-scope, while *also* // implementing the trait for that struct but **without** importing the // trait itself into scope + #[allow(dead_code)] struct TryIntoU32; impl super::TryIntoU32 for TryIntoU32 { diff --git a/tests/ui/rust-2021/inherent-dyn-collision.rs b/tests/ui/rust-2021/inherent-dyn-collision.rs index 0bcb34e5708b..47c844624198 100644 --- a/tests/ui/rust-2021/inherent-dyn-collision.rs +++ b/tests/ui/rust-2021/inherent-dyn-collision.rs @@ -25,6 +25,7 @@ mod inner { // having a struct of the same name as the trait in-scope, while *also* // implementing the trait for that struct but **without** importing the // trait itself into scope + #[allow(dead_code)] struct TryIntoU32; impl super::TryIntoU32 for TryIntoU32 { diff --git a/tests/ui/rust-2021/inherent-dyn-collision.stderr b/tests/ui/rust-2021/inherent-dyn-collision.stderr index f5905574ac39..d9e720dd9af8 100644 --- a/tests/ui/rust-2021/inherent-dyn-collision.stderr +++ b/tests/ui/rust-2021/inherent-dyn-collision.stderr @@ -1,5 +1,5 @@ warning: trait method `try_into` will become ambiguous in Rust 2021 - --> $DIR/inherent-dyn-collision.rs:41:9 + --> $DIR/inherent-dyn-collision.rs:42:9 | LL | get_dyn_trait().try_into().unwrap() | ^^^^^^^^^^^^^^^ help: disambiguate the method call: `(&*get_dyn_trait())` diff --git a/tests/ui/rust-2024/box-slice-into-iter-ambiguous.fixed b/tests/ui/rust-2024/box-slice-into-iter-ambiguous.fixed new file mode 100644 index 000000000000..d49fee55a05b --- /dev/null +++ b/tests/ui/rust-2024/box-slice-into-iter-ambiguous.fixed @@ -0,0 +1,27 @@ +// See https://github.com/rust-lang/rust/issues/88475 +//@ run-rustfix +//@ edition:2021 +//@ check-pass +#![warn(boxed_slice_into_iter)] +#![allow(unused)] + +struct FooIter; + +trait MyIntoIter { + fn into_iter(self) -> FooIter; +} + +impl MyIntoIter for Box<[T]> { + fn into_iter(self) -> FooIter { + FooIter + } +} + +struct Point; + +pub fn main() { + let points: Box<[_]> = vec![Point].into_boxed_slice(); + let y = MyIntoIter::into_iter(points); + //~^ WARNING trait method `into_iter` will become ambiguous in Rust 2024 + //~| WARNING this changes meaning in Rust 2024 +} diff --git a/tests/ui/rust-2024/box-slice-into-iter-ambiguous.rs b/tests/ui/rust-2024/box-slice-into-iter-ambiguous.rs new file mode 100644 index 000000000000..e78f550d226f --- /dev/null +++ b/tests/ui/rust-2024/box-slice-into-iter-ambiguous.rs @@ -0,0 +1,27 @@ +// See https://github.com/rust-lang/rust/issues/88475 +//@ run-rustfix +//@ edition:2021 +//@ check-pass +#![warn(boxed_slice_into_iter)] +#![allow(unused)] + +struct FooIter; + +trait MyIntoIter { + fn into_iter(self) -> FooIter; +} + +impl MyIntoIter for Box<[T]> { + fn into_iter(self) -> FooIter { + FooIter + } +} + +struct Point; + +pub fn main() { + let points: Box<[_]> = vec![Point].into_boxed_slice(); + let y = points.into_iter(); + //~^ WARNING trait method `into_iter` will become ambiguous in Rust 2024 + //~| WARNING this changes meaning in Rust 2024 +} diff --git a/tests/ui/rust-2024/box-slice-into-iter-ambiguous.stderr b/tests/ui/rust-2024/box-slice-into-iter-ambiguous.stderr new file mode 100644 index 000000000000..9cc79a7b1294 --- /dev/null +++ b/tests/ui/rust-2024/box-slice-into-iter-ambiguous.stderr @@ -0,0 +1,15 @@ +warning: trait method `into_iter` will become ambiguous in Rust 2024 + --> $DIR/box-slice-into-iter-ambiguous.rs:24:13 + | +LL | let y = points.into_iter(); + | ^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: `MyIntoIter::into_iter(points)` + | + = warning: this changes meaning in Rust 2024 +note: the lint level is defined here + --> $DIR/box-slice-into-iter-ambiguous.rs:5:9 + | +LL | #![warn(boxed_slice_into_iter)] + | ^^^^^^^^^^^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/tests/ui/rust-2024/unsafe-env-suggestion.fixed b/tests/ui/rust-2024/unsafe-env-suggestion.fixed new file mode 100644 index 000000000000..1f3d2dc0a31e --- /dev/null +++ b/tests/ui/rust-2024/unsafe-env-suggestion.fixed @@ -0,0 +1,22 @@ +//@ run-rustfix + +#![deny(deprecated_safe)] + +use std::env; + +#[deny(unused_unsafe)] +fn main() { + // TODO: Audit that the environment access only happens in single-threaded code. + unsafe { env::set_var("FOO", "BAR") }; + //~^ ERROR call to deprecated safe function + //~| WARN this is accepted in the current edition + // TODO: Audit that the environment access only happens in single-threaded code. + unsafe { env::remove_var("FOO") }; + //~^ ERROR call to deprecated safe function + //~| WARN this is accepted in the current edition + + unsafe { + env::set_var("FOO", "BAR"); + env::remove_var("FOO"); + } +} diff --git a/tests/ui/rust-2024/unsafe-env-suggestion.rs b/tests/ui/rust-2024/unsafe-env-suggestion.rs new file mode 100644 index 000000000000..3bd169973e38 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-env-suggestion.rs @@ -0,0 +1,20 @@ +//@ run-rustfix + +#![deny(deprecated_safe)] + +use std::env; + +#[deny(unused_unsafe)] +fn main() { + env::set_var("FOO", "BAR"); + //~^ ERROR call to deprecated safe function + //~| WARN this is accepted in the current edition + env::remove_var("FOO"); + //~^ ERROR call to deprecated safe function + //~| WARN this is accepted in the current edition + + unsafe { + env::set_var("FOO", "BAR"); + env::remove_var("FOO"); + } +} diff --git a/tests/ui/rust-2024/unsafe-env-suggestion.stderr b/tests/ui/rust-2024/unsafe-env-suggestion.stderr new file mode 100644 index 000000000000..7c12f4aa5ede --- /dev/null +++ b/tests/ui/rust-2024/unsafe-env-suggestion.stderr @@ -0,0 +1,35 @@ +error: call to deprecated safe function `std::env::set_var` is unsafe and requires unsafe block + --> $DIR/unsafe-env-suggestion.rs:9:5 + | +LL | env::set_var("FOO", "BAR"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + = note: for more information, see issue #27970 +note: the lint level is defined here + --> $DIR/unsafe-env-suggestion.rs:3:9 + | +LL | #![deny(deprecated_safe)] + | ^^^^^^^^^^^^^^^ +help: you can wrap the call in an `unsafe` block if you can guarantee the code is only ever called from single-threaded code + | +LL + // TODO: Audit that the environment access only happens in single-threaded code. +LL ~ unsafe { env::set_var("FOO", "BAR") }; + | + +error: call to deprecated safe function `std::env::remove_var` is unsafe and requires unsafe block + --> $DIR/unsafe-env-suggestion.rs:12:5 + | +LL | env::remove_var("FOO"); + | ^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + = note: for more information, see issue #27970 +help: you can wrap the call in an `unsafe` block if you can guarantee the code is only ever called from single-threaded code + | +LL + // TODO: Audit that the environment access only happens in single-threaded code. +LL ~ unsafe { env::remove_var("FOO") }; + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/rust-2024/unsafe-env.e2021.stderr b/tests/ui/rust-2024/unsafe-env.e2021.stderr new file mode 100644 index 000000000000..90c1df192aa0 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-env.e2021.stderr @@ -0,0 +1,42 @@ +error[E0133]: call to unsafe function `unsafe_fn` is unsafe and requires unsafe block + --> $DIR/unsafe-env.rs:15:9 + | +LL | unsafe_fn(); + | ^^^^^^^^^^^ call to unsafe function + | + = note: for more information, see issue #71668 + = note: consult the function's documentation for information on how to avoid undefined behavior +note: an unsafe function restricts its caller, but its body is safe by default + --> $DIR/unsafe-env.rs:9:1 + | +LL | unsafe fn unsafe_fn() { + | ^^^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/unsafe-env.rs:8:8 + | +LL | #[deny(unsafe_op_in_unsafe_fn)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0133]: call to unsafe function `unsafe_fn` is unsafe and requires unsafe function or block + --> $DIR/unsafe-env.rs:33:5 + | +LL | unsafe_fn(); + | ^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: unnecessary `unsafe` block + --> $DIR/unsafe-env.rs:36:5 + | +LL | unsafe { + | ^^^^^^ unnecessary `unsafe` block + | +note: the lint level is defined here + --> $DIR/unsafe-env.rs:21:8 + | +LL | #[deny(unused_unsafe)] + | ^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/rust-2024/unsafe-env.e2024.stderr b/tests/ui/rust-2024/unsafe-env.e2024.stderr new file mode 100644 index 000000000000..5ecdf3cd7a74 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-env.e2024.stderr @@ -0,0 +1,76 @@ +error[E0133]: call to unsafe function `std::env::set_var` is unsafe and requires unsafe block + --> $DIR/unsafe-env.rs:10:5 + | +LL | env::set_var("FOO", "BAR"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function + | + = note: for more information, see issue #71668 + = note: consult the function's documentation for information on how to avoid undefined behavior +note: an unsafe function restricts its caller, but its body is safe by default + --> $DIR/unsafe-env.rs:9:1 + | +LL | unsafe fn unsafe_fn() { + | ^^^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/unsafe-env.rs:8:8 + | +LL | #[deny(unsafe_op_in_unsafe_fn)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0133]: call to unsafe function `std::env::remove_var` is unsafe and requires unsafe block + --> $DIR/unsafe-env.rs:12:5 + | +LL | env::remove_var("FOO"); + | ^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function + | + = note: for more information, see issue #71668 + = note: consult the function's documentation for information on how to avoid undefined behavior + +error[E0133]: call to unsafe function `unsafe_fn` is unsafe and requires unsafe block + --> $DIR/unsafe-env.rs:15:9 + | +LL | unsafe_fn(); + | ^^^^^^^^^^^ call to unsafe function + | + = note: for more information, see issue #71668 + = note: consult the function's documentation for information on how to avoid undefined behavior + +error[E0133]: call to unsafe function `set_var` is unsafe and requires unsafe block + --> $DIR/unsafe-env.rs:23:5 + | +LL | env::set_var("FOO", "BAR"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error[E0133]: call to unsafe function `remove_var` is unsafe and requires unsafe block + --> $DIR/unsafe-env.rs:25:5 + | +LL | env::remove_var("FOO"); + | ^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error[E0133]: call to unsafe function `unsafe_fn` is unsafe and requires unsafe block + --> $DIR/unsafe-env.rs:33:5 + | +LL | unsafe_fn(); + | ^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: unnecessary `unsafe` block + --> $DIR/unsafe-env.rs:36:5 + | +LL | unsafe { + | ^^^^^^ unnecessary `unsafe` block + | +note: the lint level is defined here + --> $DIR/unsafe-env.rs:21:8 + | +LL | #[deny(unused_unsafe)] + | ^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/rust-2024/unsafe-env.rs b/tests/ui/rust-2024/unsafe-env.rs new file mode 100644 index 000000000000..601f44e1d3ec --- /dev/null +++ b/tests/ui/rust-2024/unsafe-env.rs @@ -0,0 +1,40 @@ +//@ revisions: e2021 e2024 +//@[e2021] edition: 2021 +//@[e2024] edition: 2024 +//@[e2024] compile-flags: -Zunstable-options + +use std::env; + +#[deny(unsafe_op_in_unsafe_fn)] +unsafe fn unsafe_fn() { + env::set_var("FOO", "BAR"); + //[e2024]~^ ERROR call to unsafe function `std::env::set_var` is unsafe + env::remove_var("FOO"); + //[e2024]~^ ERROR call to unsafe function `std::env::remove_var` is unsafe + if false { + unsafe_fn(); + //~^ ERROR call to unsafe function `unsafe_fn` is unsafe + } +} +fn safe_fn() {} + +#[deny(unused_unsafe)] +fn main() { + env::set_var("FOO", "BAR"); + //[e2024]~^ ERROR call to unsafe function `set_var` is unsafe + env::remove_var("FOO"); + //[e2024]~^ ERROR call to unsafe function `remove_var` is unsafe + + unsafe { + env::set_var("FOO", "BAR"); + env::remove_var("FOO"); + } + + unsafe_fn(); + //~^ ERROR call to unsafe function `unsafe_fn` is unsafe + + unsafe { + //~^ ERROR unnecessary `unsafe` block + safe_fn(); + } +} diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2021.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2021.stderr new file mode 100644 index 000000000000..3a99caa719b5 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2021.stderr @@ -0,0 +1,19 @@ +error[E0133]: call to unsafe function `test1` is unsafe and requires unsafe function or block + --> $DIR/extern-items-unsafe.rs:14:5 + | +LL | test1(TEST1); + | ^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error[E0133]: use of extern static is unsafe and requires unsafe function or block + --> $DIR/extern-items-unsafe.rs:14:11 + | +LL | test1(TEST1); + | ^^^^^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2024.stderr new file mode 100644 index 000000000000..fcf937b7ac57 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.edition2024.stderr @@ -0,0 +1,19 @@ +error[E0133]: call to unsafe function `test1` is unsafe and requires unsafe block + --> $DIR/extern-items-unsafe.rs:14:5 + | +LL | test1(TEST1); + | ^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error[E0133]: use of extern static is unsafe and requires unsafe block + --> $DIR/extern-items-unsafe.rs:14:11 + | +LL | test1(TEST1); + | ^^^^^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.rs b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.rs new file mode 100644 index 000000000000..ad569a256db9 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items-unsafe.rs @@ -0,0 +1,25 @@ +//@ revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2024] compile-flags: -Zunstable-options + +#![feature(unsafe_extern_blocks)] + +unsafe extern "C" { + static TEST1: i32; + fn test1(i: i32); +} + +fn test2() { + test1(TEST1); + //~^ ERROR: call to unsafe function `test1` is unsafe + //~| ERROR: use of extern static is unsafe +} + +fn test3() { + unsafe { + test1(TEST1); + } +} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.edition2024.stderr new file mode 100644 index 000000000000..d456cfc6829e --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.edition2024.stderr @@ -0,0 +1,12 @@ +error: extern blocks must be unsafe + --> $DIR/extern-items.rs:9:1 + | +LL | / extern "C" { +LL | | +LL | | static TEST1: i32; +LL | | fn test1(i: i32); +LL | | } + | |_^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.rs b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.rs new file mode 100644 index 000000000000..16fa1bbb8a40 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.rs @@ -0,0 +1,20 @@ +//@ revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2021] check-pass +//@[edition2024] edition:2024 +//@[edition2024] compile-flags: -Zunstable-options + +#![feature(unsafe_extern_blocks)] + +extern "C" { + //[edition2024]~^ ERROR extern blocks must be unsafe + static TEST1: i32; + fn test1(i: i32); +} + +unsafe extern "C" { + static TEST2: i32; + fn test2(i: i32); +} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-items.rs b/tests/ui/rust-2024/unsafe-extern-blocks/safe-items.rs new file mode 100644 index 000000000000..74cd5621fce9 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-items.rs @@ -0,0 +1,18 @@ +//@ revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2024] compile-flags: -Zunstable-options +//@ check-pass + +#![feature(unsafe_extern_blocks)] + +unsafe extern "C" { + safe static TEST1: i32; + safe fn test1(i: i32); +} + +fn test2() { + test1(TEST1); +} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr new file mode 100644 index 000000000000..411cf48b4866 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2021.stderr @@ -0,0 +1,20 @@ +error: items in unadorned `extern` blocks cannot have safety qualifiers + --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5 + | +LL | extern "C" { + | ---------- help: add unsafe to this `extern` block +LL | +LL | safe static TEST1: i32; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: items in unadorned `extern` blocks cannot have safety qualifiers + --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:12:5 + | +LL | extern "C" { + | ---------- help: add unsafe to this `extern` block +... +LL | safe fn test1(i: i32); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr new file mode 100644 index 000000000000..b634adc29996 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr @@ -0,0 +1,32 @@ +error: extern blocks must be unsafe + --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:8:1 + | +LL | / extern "C" { +LL | | +LL | | safe static TEST1: i32; +LL | | +LL | | safe fn test1(i: i32); +LL | | +LL | | } + | |_^ + +error: items in unadorned `extern` blocks cannot have safety qualifiers + --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:10:5 + | +LL | extern "C" { + | ---------- help: add unsafe to this `extern` block +LL | +LL | safe static TEST1: i32; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: items in unadorned `extern` blocks cannot have safety qualifiers + --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:12:5 + | +LL | extern "C" { + | ---------- help: add unsafe to this `extern` block +... +LL | safe fn test1(i: i32); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.rs b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.rs new file mode 100644 index 000000000000..11f55cb195f2 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.rs @@ -0,0 +1,20 @@ +//@ revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2024] compile-flags: -Zunstable-options + +#![feature(unsafe_extern_blocks)] + +extern "C" { + //[edition2024]~^ ERROR extern blocks must be unsafe + safe static TEST1: i32; + //~^ ERROR items in unadorned `extern` blocks cannot have safety qualifiers + safe fn test1(i: i32); + //~^ ERROR items in unadorned `extern` blocks cannot have safety qualifiers +} + +fn test2() { + test1(TEST1); +} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.fixed b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.fixed new file mode 100644 index 000000000000..10c19759d8aa --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.fixed @@ -0,0 +1,19 @@ +//@ run-rustfix + +#![feature(unsafe_extern_blocks)] +#![deny(missing_unsafe_on_extern)] +#![allow(unused)] + +unsafe extern "C" { + //~^ ERROR extern blocks should be unsafe [missing_unsafe_on_extern] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + static TEST1: i32; + fn test1(i: i32); +} + +unsafe extern "C" { + static TEST2: i32; + fn test2(i: i32); +} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.rs b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.rs new file mode 100644 index 000000000000..b81e52ddc584 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.rs @@ -0,0 +1,19 @@ +//@ run-rustfix + +#![feature(unsafe_extern_blocks)] +#![deny(missing_unsafe_on_extern)] +#![allow(unused)] + +extern "C" { + //~^ ERROR extern blocks should be unsafe [missing_unsafe_on_extern] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + static TEST1: i32; + fn test1(i: i32); +} + +unsafe extern "C" { + static TEST2: i32; + fn test2(i: i32); +} + +fn main() {} diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.stderr new file mode 100644 index 000000000000..0a3c2cd25e3f --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-extern-suggestion.stderr @@ -0,0 +1,25 @@ +error: extern blocks should be unsafe + --> $DIR/unsafe-extern-suggestion.rs:7:1 + | +LL | extern "C" { + | ^ + | | + | _help: needs `unsafe` before the extern keyword: `unsafe` + | | +LL | | +LL | | +LL | | static TEST1: i32; +LL | | fn test1(i: i32); +LL | | } + | |_^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + = note: for more information, see issue #123743 +note: the lint level is defined here + --> $DIR/unsafe-extern-suggestion.rs:4:9 + | +LL | #![deny(missing_unsafe_on_extern)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2021.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2021.stderr new file mode 100644 index 000000000000..8bb7ffefeea9 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2021.stderr @@ -0,0 +1,19 @@ +error[E0133]: call to unsafe function `test1` is unsafe and requires unsafe function or block + --> $DIR/unsafe-items.rs:20:5 + | +LL | test1(TEST1); + | ^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error[E0133]: use of extern static is unsafe and requires unsafe function or block + --> $DIR/unsafe-items.rs:20:11 + | +LL | test1(TEST1); + | ^^^^^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2024.stderr new file mode 100644 index 000000000000..9a30142a632c --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.edition2024.stderr @@ -0,0 +1,19 @@ +error[E0133]: call to unsafe function `test1` is unsafe and requires unsafe block + --> $DIR/unsafe-items.rs:20:5 + | +LL | test1(TEST1); + | ^^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error[E0133]: use of extern static is unsafe and requires unsafe block + --> $DIR/unsafe-items.rs:20:11 + | +LL | test1(TEST1); + | ^^^^^ use of extern static + | + = note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.rs b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.rs new file mode 100644 index 000000000000..9066953abc61 --- /dev/null +++ b/tests/ui/rust-2024/unsafe-extern-blocks/unsafe-items.rs @@ -0,0 +1,25 @@ +//@ revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 +//@[edition2024] compile-flags: -Zunstable-options + +#![feature(unsafe_extern_blocks)] + +unsafe extern "C" { + unsafe static TEST1: i32; + unsafe fn test1(i: i32); +} + +fn test2() { + unsafe { + test1(TEST1); + } +} + +fn test3() { + test1(TEST1); + //~^ ERROR: call to unsafe function `test1` is unsafe + //~| ERROR: use of extern static is unsafe +} + +fn main() {} diff --git a/tests/ui/rustc-env/README.md b/tests/ui/rustc-env/README.md new file mode 100644 index 000000000000..ff674f3e6cc3 --- /dev/null +++ b/tests/ui/rustc-env/README.md @@ -0,0 +1,6 @@ +Some environment variables affect rustc's behavior not because they are major compiler interfaces +but rather because rustc is, ultimately, a Rust program, with debug logging, stack control, etc. + +Prefer to group tests that use environment variables to control something about rustc's core UX, +like "can we parse this number of parens if we raise RUST_MIN_STACK?" with related code for that +compiler feature. diff --git a/tests/ui/auxiliary/rustc-rust-log-aux.rs b/tests/ui/rustc-env/auxiliary/rust-log-aux.rs similarity index 100% rename from tests/ui/auxiliary/rustc-rust-log-aux.rs rename to tests/ui/rustc-env/auxiliary/rust-log-aux.rs diff --git a/tests/ui/rustc-env/min-stack-banana.rs b/tests/ui/rustc-env/min-stack-banana.rs new file mode 100644 index 000000000000..abbb68437100 --- /dev/null +++ b/tests/ui/rustc-env/min-stack-banana.rs @@ -0,0 +1,2 @@ +//@ rustc-env:RUST_MIN_STACK=banana +fn main() {} diff --git a/tests/ui/rustc-env/min-stack-banana.stderr b/tests/ui/rustc-env/min-stack-banana.stderr new file mode 100644 index 000000000000..d379367ab4f7 --- /dev/null +++ b/tests/ui/rustc-env/min-stack-banana.stderr @@ -0,0 +1,4 @@ +error: `RUST_MIN_STACK` should be a number of bytes, but was "banana" + | + = note: you can also unset `RUST_MIN_STACK` to use the default stack size + diff --git a/tests/ui/rustc-rust-log.rs b/tests/ui/rustc-env/rust-log.rs similarity index 88% rename from tests/ui/rustc-rust-log.rs rename to tests/ui/rustc-env/rust-log.rs index 299b6c40a563..10040754593c 100644 --- a/tests/ui/rustc-rust-log.rs +++ b/tests/ui/rustc-env/rust-log.rs @@ -5,7 +5,7 @@ //@ dont-check-compiler-stdout //@ dont-check-compiler-stderr //@ compile-flags: --error-format human -//@ aux-build: rustc-rust-log-aux.rs +//@ aux-build: rust-log-aux.rs //@ rustc-env:RUSTC_LOG=debug fn main() {} diff --git a/tests/ui/sanitizer/cfi-is-incompatible-with-kcfi.aarch64.stderr b/tests/ui/sanitizer/cfi-is-incompatible-with-kcfi.aarch64.stderr index 1006c3bc17ef..7f596a19104e 100644 --- a/tests/ui/sanitizer/cfi-is-incompatible-with-kcfi.aarch64.stderr +++ b/tests/ui/sanitizer/cfi-is-incompatible-with-kcfi.aarch64.stderr @@ -2,9 +2,5 @@ error: cfi sanitizer is not supported for this target error: `-Zsanitizer=cfi` is incompatible with `-Zsanitizer=kcfi` -error: `-Zsanitizer=cfi` is incompatible with `-Zsanitizer=kcfi` - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/sanitizer/cfi-is-incompatible-with-kcfi.x86_64.stderr b/tests/ui/sanitizer/cfi-is-incompatible-with-kcfi.x86_64.stderr index 1006c3bc17ef..7f596a19104e 100644 --- a/tests/ui/sanitizer/cfi-is-incompatible-with-kcfi.x86_64.stderr +++ b/tests/ui/sanitizer/cfi-is-incompatible-with-kcfi.x86_64.stderr @@ -2,9 +2,5 @@ error: cfi sanitizer is not supported for this target error: `-Zsanitizer=cfi` is incompatible with `-Zsanitizer=kcfi` -error: `-Zsanitizer=cfi` is incompatible with `-Zsanitizer=kcfi` - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/self/arbitrary-self-from-method-substs.default.stderr b/tests/ui/self/arbitrary-self-from-method-substs.default.stderr index bd0519f66c0d..6fff086a89c2 100644 --- a/tests/ui/self/arbitrary-self-from-method-substs.default.stderr +++ b/tests/ui/self/arbitrary-self-from-method-substs.default.stderr @@ -9,7 +9,7 @@ LL | fn get>(self: R) -> u32 { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) -ERROR rustc_hir_typeck::method::confirm Foo was a subtype of &Foo but now is not? + ERROR rustc_hir_typeck::method::confirm Foo was a subtype of &Foo but now is not? error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/self/arbitrary-self-opaque.rs b/tests/ui/self/arbitrary-self-opaque.rs index 3331b037b051..3c2e2d9db727 100644 --- a/tests/ui/self/arbitrary-self-opaque.rs +++ b/tests/ui/self/arbitrary-self-opaque.rs @@ -7,6 +7,7 @@ type Bar = impl Sized; impl Foo { fn foo(self: Bar) {} //~^ ERROR: invalid `self` parameter type: `Bar` + //~| ERROR: item does not constrain } fn main() {} diff --git a/tests/ui/self/arbitrary-self-opaque.stderr b/tests/ui/self/arbitrary-self-opaque.stderr index 0cbe22afac31..5ccc076bfaf6 100644 --- a/tests/ui/self/arbitrary-self-opaque.stderr +++ b/tests/ui/self/arbitrary-self-opaque.stderr @@ -1,3 +1,16 @@ +error: item does not constrain `Bar::{opaque#0}`, but has it in its signature + --> $DIR/arbitrary-self-opaque.rs:8:8 + | +LL | fn foo(self: Bar) {} + | ^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/arbitrary-self-opaque.rs:4:12 + | +LL | type Bar = impl Sized; + | ^^^^^^^^^^ + error: unconstrained opaque type --> $DIR/arbitrary-self-opaque.rs:4:12 | @@ -15,6 +28,6 @@ LL | fn foo(self: Bar) {} = note: type of `self` must be `Self` or a type that dereferences to it = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0307`. diff --git a/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.stderr b/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.stderr index 0b4c0a7fecec..b00260fa0efa 100644 --- a/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.stderr +++ b/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch-async.stderr @@ -34,6 +34,11 @@ LL | async fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } | | | | | let's call the lifetime of this reference `'1` | lifetime `'a` defined here + | +help: consider reusing a named lifetime parameter and update trait if needed + | +LL | async fn bar<'a>(self: Alias<&'a Self>, arg: &'a ()) -> &() { arg } + | ++ error: aborting due to 3 previous errors diff --git a/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs b/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs index f1a3fb0185db..784afa8b40f8 100644 --- a/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs +++ b/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs @@ -3,17 +3,23 @@ use std::pin::Pin; struct Foo; impl Foo { - fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f } - //~^ lifetime may not live long enough + fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { + f //~ ERROR lifetime may not live long enough + } - fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) } - //~^ lifetime may not live long enough + // For this suggestion to be right, we'd need to also suggest `self: Pin<&'a Self>`, which we + // don't, but we provide a follow up suggestion to do so, so I condider that good at least for + // now. + fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { + (self, f) //~ ERROR lifetime may not live long enough + } } type Alias = Pin; impl Foo { - fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } - //~^ lifetime may not live long enough + fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { + arg //~ ERROR lifetime may not live long enough + } } fn main() {} diff --git a/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr b/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr index 209dae9c1b3e..57527dd31df3 100644 --- a/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr +++ b/tests/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr @@ -1,38 +1,47 @@ error: lifetime may not live long enough - --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:6:46 + --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:7:9 | -LL | fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f } - | - - ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` - | | | - | | let's call the lifetime of this reference `'1` +LL | fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { + | - - let's call the lifetime of this reference `'1` + | | | let's call the lifetime of this reference `'2` +LL | f + | ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn a<'a>(self: Pin<&'a Foo>, f: &'a Foo) -> &Foo { f } - | ++++ ++ ++ +LL | fn a<'a>(self: Pin<&Foo>, f: &'a Foo) -> &'a Foo { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:9:69 + --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:14:9 | -LL | fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) } - | - - ^^^^^^^^^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` - | | | - | | let's call the lifetime of this reference `'1` +LL | fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { + | - - let's call the lifetime of this reference `'1` + | | | let's call the lifetime of this reference `'2` +LL | (self, f) + | ^^^^^^^^^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn c<'a>(self: Pin<&'a Self>, f: &'a Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) } - | ++++ ++ ++ +LL | fn c<'a>(self: Pin<&Self>, f: &'a Foo, g: &Foo) -> (Pin<&'a Foo>, &'a Foo) { + | ++++ ++ ++ ++ error: lifetime may not live long enough - --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:15:58 + --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:21:9 | -LL | fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } - | -- ---- has type `Pin<&'1 Foo>` ^^^ method was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a` +LL | fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { + | -- ---- has type `Pin<&'1 Foo>` | | | lifetime `'a` defined here +LL | arg + | ^^^ method was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a` + | +help: consider reusing a named lifetime parameter and update trait if needed + | +LL | fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &'a () { + | ++ error: aborting due to 3 previous errors diff --git a/tests/ui/self/elision/lt-ref-self-async.fixed b/tests/ui/self/elision/lt-ref-self-async.fixed new file mode 100644 index 000000000000..aa1d62012da0 --- /dev/null +++ b/tests/ui/self/elision/lt-ref-self-async.fixed @@ -0,0 +1,47 @@ +//@ edition:2018 +//@ run-rustfix +#![allow(non_snake_case, dead_code)] + +use std::pin::Pin; + +struct Struct<'a> { + data: &'a u32, +} + +impl<'a> Struct<'a> { + // Test using `&self` sugar: + + async fn ref_self<'b>(&'b self, f: &'b u32) -> &u32 { + f + //~^ ERROR lifetime may not live long enough + } + + // Test using `&Self` explicitly: + + async fn ref_Self<'b>(self: &'b Self, f: &'b u32) -> &u32 { + f + //~^ ERROR lifetime may not live long enough + } + + async fn box_ref_Self<'b>(self: Box<&'b Self>, f: &'b u32) -> &u32 { + f + //~^ ERROR lifetime may not live long enough + } + + async fn pin_ref_Self<'b>(self: Pin<&'b Self>, f: &'b u32) -> &u32 { + f + //~^ ERROR lifetime may not live long enough + } + + async fn box_box_ref_Self<'b>(self: Box>, f: &'b u32) -> &u32 { + f + //~^ ERROR lifetime may not live long enough + } + + async fn box_pin_Self<'b>(self: Box>, f: &'b u32) -> &u32 { + f + //~^ ERROR lifetime may not live long enough + } +} + +fn main() {} diff --git a/tests/ui/self/elision/lt-ref-self-async.rs b/tests/ui/self/elision/lt-ref-self-async.rs index 8eb4a48a9c93..38de0fd39f08 100644 --- a/tests/ui/self/elision/lt-ref-self-async.rs +++ b/tests/ui/self/elision/lt-ref-self-async.rs @@ -1,10 +1,12 @@ //@ edition:2018 - -#![allow(non_snake_case)] +//@ run-rustfix +#![allow(non_snake_case, dead_code)] use std::pin::Pin; -struct Struct<'a> { data: &'a u32 } +struct Struct<'a> { + data: &'a u32, +} impl<'a> Struct<'a> { // Test using `&self` sugar: @@ -42,4 +44,4 @@ impl<'a> Struct<'a> { } } -fn main() { } +fn main() {} diff --git a/tests/ui/self/elision/lt-ref-self-async.stderr b/tests/ui/self/elision/lt-ref-self-async.stderr index 29d60ed66352..b84044f75488 100644 --- a/tests/ui/self/elision/lt-ref-self-async.stderr +++ b/tests/ui/self/elision/lt-ref-self-async.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/lt-ref-self-async.rs:13:9 + --> $DIR/lt-ref-self-async.rs:15:9 | LL | async fn ref_self(&self, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -10,11 +10,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn ref_self<'a>(&'a self, f: &'a u32) -> &u32 { +LL | async fn ref_self<'b>(&'b self, f: &'b u32) -> &u32 { | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/lt-ref-self-async.rs:20:9 + --> $DIR/lt-ref-self-async.rs:22:9 | LL | async fn ref_Self(self: &Self, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -25,11 +25,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn ref_Self<'a>(self: &'a Self, f: &'a u32) -> &u32 { +LL | async fn ref_Self<'b>(self: &'b Self, f: &'b u32) -> &u32 { | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/lt-ref-self-async.rs:25:9 + --> $DIR/lt-ref-self-async.rs:27:9 | LL | async fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -40,11 +40,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_ref_Self<'a>(self: Box<&'a Self>, f: &'a u32) -> &u32 { +LL | async fn box_ref_Self<'b>(self: Box<&'b Self>, f: &'b u32) -> &u32 { | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/lt-ref-self-async.rs:30:9 + --> $DIR/lt-ref-self-async.rs:32:9 | LL | async fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -55,11 +55,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn pin_ref_Self<'a>(self: Pin<&'a Self>, f: &'a u32) -> &u32 { +LL | async fn pin_ref_Self<'b>(self: Pin<&'b Self>, f: &'b u32) -> &u32 { | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/lt-ref-self-async.rs:35:9 + --> $DIR/lt-ref-self-async.rs:37:9 | LL | async fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -70,11 +70,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_box_ref_Self<'a>(self: Box>, f: &'a u32) -> &u32 { +LL | async fn box_box_ref_Self<'b>(self: Box>, f: &'b u32) -> &u32 { | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/lt-ref-self-async.rs:40:9 + --> $DIR/lt-ref-self-async.rs:42:9 | LL | async fn box_pin_Self(self: Box>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -85,7 +85,7 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | async fn box_pin_Self<'a>(self: Box>, f: &'a u32) -> &u32 { +LL | async fn box_pin_Self<'b>(self: Box>, f: &'b u32) -> &u32 { | ++++ ++ ++ error: aborting due to 6 previous errors diff --git a/tests/ui/self/elision/lt-ref-self.fixed b/tests/ui/self/elision/lt-ref-self.fixed new file mode 100644 index 000000000000..1d90eacea50d --- /dev/null +++ b/tests/ui/self/elision/lt-ref-self.fixed @@ -0,0 +1,46 @@ +//@ run-rustfix +#![allow(non_snake_case, dead_code)] + +use std::pin::Pin; + +struct Struct<'a> { + data: &'a u32, +} + +impl<'a> Struct<'a> { + // Test using `&self` sugar: + + fn ref_self<'b>(&self, f: &'b u32) -> &'b u32 { + f + //~^ ERROR lifetime may not live long enough + } + + // Test using `&Self` explicitly: + + fn ref_Self<'b>(self: &Self, f: &'b u32) -> &'b u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn box_ref_Self<'b>(self: Box<&Self>, f: &'b u32) -> &'b u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn pin_ref_Self<'b>(self: Pin<&Self>, f: &'b u32) -> &'b u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn box_box_ref_Self<'b>(self: Box>, f: &'b u32) -> &'b u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn box_pin_Self<'b>(self: Box>, f: &'b u32) -> &'b u32 { + f + //~^ ERROR lifetime may not live long enough + } +} + +fn main() {} diff --git a/tests/ui/self/elision/lt-ref-self.rs b/tests/ui/self/elision/lt-ref-self.rs index d37ed5acbcb2..4cd97c5b729b 100644 --- a/tests/ui/self/elision/lt-ref-self.rs +++ b/tests/ui/self/elision/lt-ref-self.rs @@ -1,8 +1,11 @@ -#![allow(non_snake_case)] +//@ run-rustfix +#![allow(non_snake_case, dead_code)] use std::pin::Pin; -struct Struct<'a> { data: &'a u32 } +struct Struct<'a> { + data: &'a u32, +} impl<'a> Struct<'a> { // Test using `&self` sugar: @@ -40,4 +43,4 @@ impl<'a> Struct<'a> { } } -fn main() { } +fn main() {} diff --git a/tests/ui/self/elision/lt-ref-self.stderr b/tests/ui/self/elision/lt-ref-self.stderr index 216737a2c733..5c8e0a7a742a 100644 --- a/tests/ui/self/elision/lt-ref-self.stderr +++ b/tests/ui/self/elision/lt-ref-self.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/lt-ref-self.rs:11:9 + --> $DIR/lt-ref-self.rs:14:9 | LL | fn ref_self(&self, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -10,11 +10,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn ref_self<'a>(&'a self, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn ref_self<'b>(&self, f: &'b u32) -> &'b u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/lt-ref-self.rs:18:9 + --> $DIR/lt-ref-self.rs:21:9 | LL | fn ref_Self(self: &Self, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -25,11 +25,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn ref_Self<'a>(self: &'a Self, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn ref_Self<'b>(self: &Self, f: &'b u32) -> &'b u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/lt-ref-self.rs:23:9 + --> $DIR/lt-ref-self.rs:26:9 | LL | fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -40,11 +40,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn box_ref_Self<'a>(self: Box<&'a Self>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn box_ref_Self<'b>(self: Box<&Self>, f: &'b u32) -> &'b u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/lt-ref-self.rs:28:9 + --> $DIR/lt-ref-self.rs:31:9 | LL | fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -55,11 +55,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn pin_ref_Self<'a>(self: Pin<&'a Self>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn pin_ref_Self<'b>(self: Pin<&Self>, f: &'b u32) -> &'b u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/lt-ref-self.rs:33:9 + --> $DIR/lt-ref-self.rs:36:9 | LL | fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -70,11 +70,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn box_box_ref_Self<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn box_box_ref_Self<'b>(self: Box>, f: &'b u32) -> &'b u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/lt-ref-self.rs:38:9 + --> $DIR/lt-ref-self.rs:41:9 | LL | fn box_pin_Self(self: Box>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -85,8 +85,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn box_pin_Self<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn box_pin_Self<'b>(self: Box>, f: &'b u32) -> &'b u32 { + | ++++ ++ ++ error: aborting due to 6 previous errors diff --git a/tests/ui/self/elision/ref-mut-self.fixed b/tests/ui/self/elision/ref-mut-self.fixed new file mode 100644 index 000000000000..42e8050f65e3 --- /dev/null +++ b/tests/ui/self/elision/ref-mut-self.fixed @@ -0,0 +1,44 @@ +//@ run-rustfix +#![allow(non_snake_case, dead_code)] + +use std::pin::Pin; + +struct Struct {} + +impl Struct { + // Test using `&mut self` sugar: + + fn ref_self<'a>(&mut self, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + // Test using `&mut Self` explicitly: + + fn ref_Self<'a>(self: &mut Self, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn box_ref_Self<'a>(self: Box<&mut Self>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn pin_ref_Self<'a>(self: Pin<&mut Self>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn box_box_ref_Self<'a>(self: Box>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn box_pin_ref_Self<'a>(self: Box>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } +} + +fn main() {} diff --git a/tests/ui/self/elision/ref-mut-self.rs b/tests/ui/self/elision/ref-mut-self.rs index bb82e6be7489..87dab2a9ab42 100644 --- a/tests/ui/self/elision/ref-mut-self.rs +++ b/tests/ui/self/elision/ref-mut-self.rs @@ -1,8 +1,9 @@ -#![allow(non_snake_case)] +//@ run-rustfix +#![allow(non_snake_case, dead_code)] use std::pin::Pin; -struct Struct { } +struct Struct {} impl Struct { // Test using `&mut self` sugar: @@ -40,4 +41,4 @@ impl Struct { } } -fn main() { } +fn main() {} diff --git a/tests/ui/self/elision/ref-mut-self.stderr b/tests/ui/self/elision/ref-mut-self.stderr index 12b64a3f6dcb..620706fdbdbc 100644 --- a/tests/ui/self/elision/ref-mut-self.stderr +++ b/tests/ui/self/elision/ref-mut-self.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/ref-mut-self.rs:11:9 + --> $DIR/ref-mut-self.rs:12:9 | LL | fn ref_self(&mut self, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -10,11 +10,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn ref_self<'a>(&'a mut self, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn ref_self<'a>(&mut self, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-mut-self.rs:18:9 + --> $DIR/ref-mut-self.rs:19:9 | LL | fn ref_Self(self: &mut Self, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -25,11 +25,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn ref_Self<'a>(self: &'a mut Self, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn ref_Self<'a>(self: &mut Self, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-mut-self.rs:23:9 + --> $DIR/ref-mut-self.rs:24:9 | LL | fn box_ref_Self(self: Box<&mut Self>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -40,11 +40,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn box_ref_Self<'a>(self: Box<&'a mut Self>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn box_ref_Self<'a>(self: Box<&mut Self>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-mut-self.rs:28:9 + --> $DIR/ref-mut-self.rs:29:9 | LL | fn pin_ref_Self(self: Pin<&mut Self>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -55,11 +55,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn pin_ref_Self<'a>(self: Pin<&'a mut Self>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn pin_ref_Self<'a>(self: Pin<&mut Self>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-mut-self.rs:33:9 + --> $DIR/ref-mut-self.rs:34:9 | LL | fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -70,11 +70,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn box_box_ref_Self<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn box_box_ref_Self<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-mut-self.rs:38:9 + --> $DIR/ref-mut-self.rs:39:9 | LL | fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -85,8 +85,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn box_pin_ref_Self<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn box_pin_ref_Self<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: aborting due to 6 previous errors diff --git a/tests/ui/self/elision/ref-mut-struct.fixed b/tests/ui/self/elision/ref-mut-struct.fixed new file mode 100644 index 000000000000..f7a84d4a45c7 --- /dev/null +++ b/tests/ui/self/elision/ref-mut-struct.fixed @@ -0,0 +1,37 @@ +//@ run-rustfix +#![allow(non_snake_case, dead_code)] + +use std::pin::Pin; + +struct Struct {} + +impl Struct { + // Test using `&mut Struct` explicitly: + + fn ref_Struct<'a>(self: &mut Struct, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn box_ref_Struct<'a>(self: Box<&mut Struct>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn pin_ref_Struct<'a>(self: Pin<&mut Struct>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn box_box_ref_Struct<'a>(self: Box>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn box_pin_ref_Struct<'a>(self: Box>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } +} + +fn main() {} diff --git a/tests/ui/self/elision/ref-mut-struct.rs b/tests/ui/self/elision/ref-mut-struct.rs index ca8bd8da1335..c227fa8bbfc6 100644 --- a/tests/ui/self/elision/ref-mut-struct.rs +++ b/tests/ui/self/elision/ref-mut-struct.rs @@ -1,8 +1,9 @@ -#![allow(non_snake_case)] +//@ run-rustfix +#![allow(non_snake_case, dead_code)] use std::pin::Pin; -struct Struct { } +struct Struct {} impl Struct { // Test using `&mut Struct` explicitly: @@ -33,4 +34,4 @@ impl Struct { } } -fn main() { } +fn main() {} diff --git a/tests/ui/self/elision/ref-mut-struct.stderr b/tests/ui/self/elision/ref-mut-struct.stderr index cde16ce8ba41..cce07f827184 100644 --- a/tests/ui/self/elision/ref-mut-struct.stderr +++ b/tests/ui/self/elision/ref-mut-struct.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/ref-mut-struct.rs:11:9 + --> $DIR/ref-mut-struct.rs:12:9 | LL | fn ref_Struct(self: &mut Struct, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -10,11 +10,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn ref_Struct<'a>(self: &'a mut Struct, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn ref_Struct<'a>(self: &mut Struct, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-mut-struct.rs:16:9 + --> $DIR/ref-mut-struct.rs:17:9 | LL | fn box_ref_Struct(self: Box<&mut Struct>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -25,11 +25,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn box_ref_Struct<'a>(self: Box<&'a mut Struct>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn box_ref_Struct<'a>(self: Box<&mut Struct>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-mut-struct.rs:21:9 + --> $DIR/ref-mut-struct.rs:22:9 | LL | fn pin_ref_Struct(self: Pin<&mut Struct>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -40,11 +40,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn pin_ref_Struct<'a>(self: Pin<&'a mut Struct>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn pin_ref_Struct<'a>(self: Pin<&mut Struct>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-mut-struct.rs:26:9 + --> $DIR/ref-mut-struct.rs:27:9 | LL | fn box_box_ref_Struct(self: Box>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -55,11 +55,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn box_box_ref_Struct<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn box_box_ref_Struct<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-mut-struct.rs:31:9 + --> $DIR/ref-mut-struct.rs:32:9 | LL | fn box_pin_ref_Struct(self: Box>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -70,8 +70,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn box_pin_ref_Struct<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn box_pin_ref_Struct<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: aborting due to 5 previous errors diff --git a/tests/ui/self/elision/ref-self.fixed b/tests/ui/self/elision/ref-self.fixed new file mode 100644 index 000000000000..8bf5a0bb2232 --- /dev/null +++ b/tests/ui/self/elision/ref-self.fixed @@ -0,0 +1,61 @@ +//@ run-rustfix +#![feature(arbitrary_self_types)] +#![allow(non_snake_case, dead_code)] + +use std::marker::PhantomData; +use std::ops::Deref; +use std::pin::Pin; + +struct Struct {} + +struct Wrap(T, PhantomData

); + +impl Deref for Wrap { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } +} + +impl Struct { + // Test using `&self` sugar: + + fn ref_self<'a>(&self, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + // Test using `&Self` explicitly: + + fn ref_Self<'a>(self: &Self, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn box_ref_Self<'a>(self: Box<&Self>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn pin_ref_Self<'a>(self: Pin<&Self>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn box_box_ref_Self<'a>(self: Box>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn box_pin_ref_Self<'a>(self: Box>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn wrap_ref_Self_Self<'a>(self: Wrap<&Self, Self>, f: &'a u8) -> &'a u8 { + f + //~^ ERROR lifetime may not live long enough + } +} + +fn main() {} diff --git a/tests/ui/self/elision/ref-self.rs b/tests/ui/self/elision/ref-self.rs index dd07fe1b00be..4b4b8aa5b511 100644 --- a/tests/ui/self/elision/ref-self.rs +++ b/tests/ui/self/elision/ref-self.rs @@ -1,17 +1,20 @@ +//@ run-rustfix #![feature(arbitrary_self_types)] -#![allow(non_snake_case)] +#![allow(non_snake_case, dead_code)] use std::marker::PhantomData; use std::ops::Deref; use std::pin::Pin; -struct Struct { } +struct Struct {} struct Wrap(T, PhantomData

); impl Deref for Wrap { type Target = T; - fn deref(&self) -> &T { &self.0 } + fn deref(&self) -> &T { + &self.0 + } } impl Struct { @@ -55,4 +58,4 @@ impl Struct { } } -fn main() { } +fn main() {} diff --git a/tests/ui/self/elision/ref-self.stderr b/tests/ui/self/elision/ref-self.stderr index 35693257c991..c4ec8c55a003 100644 --- a/tests/ui/self/elision/ref-self.stderr +++ b/tests/ui/self/elision/ref-self.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/ref-self.rs:21:9 + --> $DIR/ref-self.rs:24:9 | LL | fn ref_self(&self, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -10,11 +10,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn ref_self<'a>(&'a self, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn ref_self<'a>(&self, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-self.rs:28:9 + --> $DIR/ref-self.rs:31:9 | LL | fn ref_Self(self: &Self, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -25,11 +25,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn ref_Self<'a>(self: &'a Self, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn ref_Self<'a>(self: &Self, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-self.rs:33:9 + --> $DIR/ref-self.rs:36:9 | LL | fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -40,11 +40,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn box_ref_Self<'a>(self: Box<&'a Self>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn box_ref_Self<'a>(self: Box<&Self>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-self.rs:38:9 + --> $DIR/ref-self.rs:41:9 | LL | fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -55,11 +55,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn pin_ref_Self<'a>(self: Pin<&'a Self>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn pin_ref_Self<'a>(self: Pin<&Self>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-self.rs:43:9 + --> $DIR/ref-self.rs:46:9 | LL | fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -70,11 +70,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn box_box_ref_Self<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn box_box_ref_Self<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-self.rs:48:9 + --> $DIR/ref-self.rs:51:9 | LL | fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -85,11 +85,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn box_pin_ref_Self<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn box_pin_ref_Self<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-self.rs:53:9 + --> $DIR/ref-self.rs:56:9 | LL | fn wrap_ref_Self_Self(self: Wrap<&Self, Self>, f: &u8) -> &u8 { | - - let's call the lifetime of this reference `'1` @@ -100,8 +100,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn wrap_ref_Self_Self<'a>(self: Wrap<&'a Self, Self>, f: &'a u8) -> &u8 { - | ++++ ++ ++ +LL | fn wrap_ref_Self_Self<'a>(self: Wrap<&Self, Self>, f: &'a u8) -> &'a u8 { + | ++++ ++ ++ error: aborting due to 7 previous errors diff --git a/tests/ui/self/elision/ref-struct.fixed b/tests/ui/self/elision/ref-struct.fixed new file mode 100644 index 000000000000..411f60131956 --- /dev/null +++ b/tests/ui/self/elision/ref-struct.fixed @@ -0,0 +1,37 @@ +//@ run-rustfix +#![allow(non_snake_case, dead_code)] + +use std::pin::Pin; + +struct Struct {} + +impl Struct { + // Test using `&Struct` explicitly: + + fn ref_Struct<'a>(self: &Struct, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn box_ref_Struct<'a>(self: Box<&Struct>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn pin_ref_Struct<'a>(self: Pin<&Struct>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn box_box_ref_Struct<'a>(self: Box>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } + + fn box_pin_Struct<'a>(self: Box>, f: &'a u32) -> &'a u32 { + f + //~^ ERROR lifetime may not live long enough + } +} + +fn main() {} diff --git a/tests/ui/self/elision/ref-struct.rs b/tests/ui/self/elision/ref-struct.rs index 13a42cd1aed9..733a175e7a1d 100644 --- a/tests/ui/self/elision/ref-struct.rs +++ b/tests/ui/self/elision/ref-struct.rs @@ -1,8 +1,9 @@ -#![allow(non_snake_case)] +//@ run-rustfix +#![allow(non_snake_case, dead_code)] use std::pin::Pin; -struct Struct { } +struct Struct {} impl Struct { // Test using `&Struct` explicitly: @@ -33,4 +34,4 @@ impl Struct { } } -fn main() { } +fn main() {} diff --git a/tests/ui/self/elision/ref-struct.stderr b/tests/ui/self/elision/ref-struct.stderr index a3d3cebeba9c..860186c83532 100644 --- a/tests/ui/self/elision/ref-struct.stderr +++ b/tests/ui/self/elision/ref-struct.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/ref-struct.rs:11:9 + --> $DIR/ref-struct.rs:12:9 | LL | fn ref_Struct(self: &Struct, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -10,11 +10,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn ref_Struct<'a>(self: &'a Struct, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn ref_Struct<'a>(self: &Struct, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-struct.rs:16:9 + --> $DIR/ref-struct.rs:17:9 | LL | fn box_ref_Struct(self: Box<&Struct>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -25,11 +25,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn box_ref_Struct<'a>(self: Box<&'a Struct>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn box_ref_Struct<'a>(self: Box<&Struct>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-struct.rs:21:9 + --> $DIR/ref-struct.rs:22:9 | LL | fn pin_ref_Struct(self: Pin<&Struct>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -40,11 +40,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn pin_ref_Struct<'a>(self: Pin<&'a Struct>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn pin_ref_Struct<'a>(self: Pin<&Struct>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-struct.rs:26:9 + --> $DIR/ref-struct.rs:27:9 | LL | fn box_box_ref_Struct(self: Box>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -55,11 +55,11 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn box_box_ref_Struct<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn box_box_ref_Struct<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: lifetime may not live long enough - --> $DIR/ref-struct.rs:31:9 + --> $DIR/ref-struct.rs:32:9 | LL | fn box_pin_Struct(self: Box>, f: &u32) -> &u32 { | - - let's call the lifetime of this reference `'1` @@ -70,8 +70,8 @@ LL | f | help: consider introducing a named lifetime parameter and update trait if needed | -LL | fn box_pin_Struct<'a>(self: Box>, f: &'a u32) -> &u32 { - | ++++ ++ ++ +LL | fn box_pin_Struct<'a>(self: Box>, f: &'a u32) -> &'a u32 { + | ++++ ++ ++ error: aborting due to 5 previous errors diff --git a/tests/ui/self/self-ctor-nongeneric.rs b/tests/ui/self/self-ctor-nongeneric.rs index 0594e87a0a46..c32cf9df6946 100644 --- a/tests/ui/self/self-ctor-nongeneric.rs +++ b/tests/ui/self/self-ctor-nongeneric.rs @@ -6,8 +6,12 @@ struct S0(usize); impl S0 { fn foo() { const C: S0 = Self(0); + //~^ WARN can't reference `Self` constructor from outer item + //~| WARN this was previously accepted by the compiler but is being phased out fn bar() -> S0 { Self(0) + //~^ WARN can't reference `Self` constructor from outer item + //~| WARN this was previously accepted by the compiler but is being phased out } } } diff --git a/tests/ui/self/self-ctor-nongeneric.stderr b/tests/ui/self/self-ctor-nongeneric.stderr new file mode 100644 index 000000000000..6c03c6f3e38a --- /dev/null +++ b/tests/ui/self/self-ctor-nongeneric.stderr @@ -0,0 +1,27 @@ +warning: can't reference `Self` constructor from outer item + --> $DIR/self-ctor-nongeneric.rs:8:23 + | +LL | impl S0 { + | ------- the inner item doesn't inherit generics from this impl, so `Self` is invalid to reference +LL | fn foo() { +LL | const C: S0 = Self(0); + | ^^^^ help: replace `Self` with the actual type: `S0` + | + = warning: 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 #124186 + = note: `#[warn(self_constructor_from_outer_item)]` on by default + +warning: can't reference `Self` constructor from outer item + --> $DIR/self-ctor-nongeneric.rs:12:13 + | +LL | impl S0 { + | ------- the inner item doesn't inherit generics from this impl, so `Self` is invalid to reference +... +LL | Self(0) + | ^^^^ help: replace `Self` with the actual type: `S0` + | + = warning: 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 #124186 + +warning: 2 warnings emitted + diff --git a/tests/ui/self/self-ctor.rs b/tests/ui/self/self-ctor.rs new file mode 100644 index 000000000000..d166499f8841 --- /dev/null +++ b/tests/ui/self/self-ctor.rs @@ -0,0 +1,14 @@ +struct S0(T); + +impl S0 { + fn foo() { + const C: S0 = Self(0); + //~^ ERROR can't reference `Self` constructor from outer item + fn bar() -> S0 { + Self(0) + //~^ ERROR can't reference `Self` constructor from outer item + } + } +} + +fn main() {} diff --git a/tests/ui/self/self-ctor.stderr b/tests/ui/self/self-ctor.stderr new file mode 100644 index 000000000000..0cb22baaa1a0 --- /dev/null +++ b/tests/ui/self/self-ctor.stderr @@ -0,0 +1,21 @@ +error[E0401]: can't reference `Self` constructor from outer item + --> $DIR/self-ctor.rs:5:28 + | +LL | impl S0 { + | ------------- the inner item doesn't inherit generics from this impl, so `Self` is invalid to reference +LL | fn foo() { +LL | const C: S0 = Self(0); + | ^^^^ help: replace `Self` with the actual type: `S0` + +error[E0401]: can't reference `Self` constructor from outer item + --> $DIR/self-ctor.rs:8:13 + | +LL | impl S0 { + | ------------- the inner item doesn't inherit generics from this impl, so `Self` is invalid to reference +... +LL | Self(0) + | ^^^^ help: replace `Self` with the actual type: `S0` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0401`. diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-2.rs b/tests/ui/simd/intrinsic/generic-arithmetic-2.rs index 4ad98d567113..fc3087cbf754 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-2.rs +++ b/tests/ui/simd/intrinsic/generic-arithmetic-2.rs @@ -30,6 +30,7 @@ extern "rust-intrinsic" { fn simd_bswap(x: T) -> T; fn simd_bitreverse(x: T) -> T; fn simd_ctlz(x: T) -> T; + fn simd_ctpop(x: T) -> T; fn simd_cttz(x: T) -> T; } @@ -77,7 +78,6 @@ fn main() { simd_cttz(x); simd_cttz(y); - simd_add(0, 0); //~^ ERROR expected SIMD input type, found non-SIMD `i32` simd_sub(0, 0); @@ -108,24 +108,25 @@ fn main() { simd_cttz(0); //~^ ERROR expected SIMD input type, found non-SIMD `i32` - simd_shl(z, z); -//~^ ERROR unsupported operation on `f32x4` with element `f32` + //~^ ERROR unsupported operation on `f32x4` with element `f32` simd_shr(z, z); -//~^ ERROR unsupported operation on `f32x4` with element `f32` + //~^ ERROR unsupported operation on `f32x4` with element `f32` simd_and(z, z); -//~^ ERROR unsupported operation on `f32x4` with element `f32` + //~^ ERROR unsupported operation on `f32x4` with element `f32` simd_or(z, z); -//~^ ERROR unsupported operation on `f32x4` with element `f32` + //~^ ERROR unsupported operation on `f32x4` with element `f32` simd_xor(z, z); -//~^ ERROR unsupported operation on `f32x4` with element `f32` + //~^ ERROR unsupported operation on `f32x4` with element `f32` simd_bswap(z); -//~^ ERROR unsupported operation on `f32x4` with element `f32` + //~^ ERROR unsupported operation on `f32x4` with element `f32` simd_bitreverse(z); -//~^ ERROR unsupported operation on `f32x4` with element `f32` + //~^ ERROR unsupported operation on `f32x4` with element `f32` simd_ctlz(z); -//~^ ERROR unsupported operation on `f32x4` with element `f32` + //~^ ERROR unsupported operation on `f32x4` with element `f32` + simd_ctpop(z); + //~^ ERROR unsupported operation on `f32x4` with element `f32` simd_cttz(z); -//~^ ERROR unsupported operation on `f32x4` with element `f32` + //~^ ERROR unsupported operation on `f32x4` with element `f32` } } diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr b/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr index db26f3417c95..6f5f86d7d374 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr +++ b/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr @@ -83,59 +83,65 @@ LL | simd_cttz(0); | ^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shl` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:112:9 + --> $DIR/generic-arithmetic-2.rs:111:9 | LL | simd_shl(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shr` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:114:9 + --> $DIR/generic-arithmetic-2.rs:113:9 | LL | simd_shr(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_and` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:116:9 + --> $DIR/generic-arithmetic-2.rs:115:9 | LL | simd_and(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_or` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:118:9 + --> $DIR/generic-arithmetic-2.rs:117:9 | LL | simd_or(z, z); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_xor` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:120:9 + --> $DIR/generic-arithmetic-2.rs:119:9 | LL | simd_xor(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bswap` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:122:9 + --> $DIR/generic-arithmetic-2.rs:121:9 | LL | simd_bswap(z); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bitreverse` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:124:9 + --> $DIR/generic-arithmetic-2.rs:123:9 | LL | simd_bitreverse(z); | ^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ctlz` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:126:9 + --> $DIR/generic-arithmetic-2.rs:125:9 | LL | simd_ctlz(z); | ^^^^^^^^^^^^ +error[E0511]: invalid monomorphization of `simd_ctpop` intrinsic: unsupported operation on `f32x4` with element `f32` + --> $DIR/generic-arithmetic-2.rs:127:9 + | +LL | simd_ctpop(z); + | ^^^^^^^^^^^^^ + error[E0511]: invalid monomorphization of `simd_cttz` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:128:9 + --> $DIR/generic-arithmetic-2.rs:129:9 | LL | simd_cttz(z); | ^^^^^^^^^^^^ -error: aborting due to 23 previous errors +error: aborting due to 24 previous errors For more information about this error, try `rustc --explain E0511`. diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs b/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs index 33143b1f7b53..60dfa6274148 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs +++ b/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs @@ -47,6 +47,7 @@ extern "rust-intrinsic" { fn simd_bswap(x: T) -> T; fn simd_bitreverse(x: T) -> T; fn simd_ctlz(x: T) -> T; + fn simd_ctpop(x: T) -> T; fn simd_cttz(x: T) -> T; } @@ -57,6 +58,8 @@ fn main() { let x2 = i32x4(2, 3, 4, 5); let y2 = U32::<4>([2, 3, 4, 5]); let z2 = f32x4(2.0, 3.0, 4.0, 5.0); + let x3 = i32x4(0, i32::MAX, i32::MIN, -1_i32); + let y3 = U32::<4>([0, i32::MAX as _, i32::MIN as _, -1_i32 as _]); unsafe { all_eq!(simd_add(x1, x2), i32x4(3, 5, 7, 9)); @@ -147,6 +150,13 @@ fn main() { all_eq!(simd_ctlz(x1), i32x4(31, 30, 30, 29)); all_eq_!(simd_ctlz(y1), U32::<4>([31, 30, 30, 29])); + all_eq!(simd_ctpop(x1), i32x4(1, 1, 2, 1)); + all_eq_!(simd_ctpop(y1), U32::<4>([1, 1, 2, 1])); + all_eq!(simd_ctpop(x2), i32x4(1, 2, 1, 2)); + all_eq_!(simd_ctpop(y2), U32::<4>([1, 2, 1, 2])); + all_eq!(simd_ctpop(x3), i32x4(0, 31, 1, 32)); + all_eq_!(simd_ctpop(y3), U32::<4>([0, 31, 1, 32])); + all_eq!(simd_cttz(x1), i32x4(0, 1, 0, 2)); all_eq_!(simd_cttz(y1), U32::<4>([0, 1, 0, 2])); } diff --git a/tests/ui/simd/repr_packed.rs b/tests/ui/simd/repr_packed.rs index 411bba3454eb..1ba15bda98dd 100644 --- a/tests/ui/simd/repr_packed.rs +++ b/tests/ui/simd/repr_packed.rs @@ -6,9 +6,6 @@ #[repr(simd, packed)] struct Simd([T; N]); -#[repr(simd)] -struct FullSimd([T; N]); - fn check_size_align() { use std::mem; assert_eq!(mem::size_of::>(), mem::size_of::<[T; N]>()); @@ -39,21 +36,15 @@ fn main() { check_ty::(); unsafe { - // powers-of-two have no padding and work as usual + // powers-of-two have no padding and have the same layout as #[repr(simd)] let x: Simd = simd_add(Simd::([0., 1., 2., 3.]), Simd::([2., 2., 2., 2.])); assert_eq!(std::mem::transmute::<_, [f64; 4]>(x), [2., 3., 4., 5.]); - // non-powers-of-two have padding and need to be expanded to full vectors - fn load(v: Simd) -> FullSimd { - unsafe { - let mut tmp = core::mem::MaybeUninit::>::uninit(); - std::ptr::copy_nonoverlapping(&v as *const _, tmp.as_mut_ptr().cast(), 1); - tmp.assume_init() - } - } - let x: FullSimd = - simd_add(load(Simd::([0., 1., 2.])), load(Simd::([2., 2., 2.]))); - assert_eq!(x.0, [2., 3., 4.]); + // non-powers-of-two should have padding (which is removed by #[repr(packed)]), + // but the intrinsic handles it + let x: Simd = simd_add(Simd::([0., 1., 2.]), Simd::([2., 2., 2.])); + let arr: [f64; 3] = x.0; + assert_eq!(arr, [2., 3., 4.]); } } diff --git a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr index 8df0613695be..99f8dbd9a6c8 100644 --- a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr +++ b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr @@ -25,8 +25,8 @@ LL + let str::as_bytes; | error[E0533]: expected unit struct, unit variant or constant, found associated function `str<{ - fn str() { let (/*ERROR*/); } - }, T>::as_bytes` + fn str() { let (/*ERROR*/); } + }, T>::as_bytes` --> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:9 | LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes; diff --git a/tests/ui/span/multiline-span-simple.stderr b/tests/ui/span/multiline-span-simple.stderr index 2de792b03d72..2454769863b9 100644 --- a/tests/ui/span/multiline-span-simple.stderr +++ b/tests/ui/span/multiline-span-simple.stderr @@ -6,10 +6,10 @@ LL | foo(1 as u32 + | = help: the trait `Add<()>` is not implemented for `u32` = help: the following other types implement trait `Add`: - <&'a u32 as Add> - <&u32 as Add<&u32>> - > - + `&'a u32` implements `Add` + `&u32` implements `Add<&u32>` + `u32` implements `Add<&u32>` + `u32` implements `Add` error: aborting due to 1 previous error diff --git a/tests/ui/span/multispan-import-lint.stderr b/tests/ui/span/multispan-import-lint.stderr index 4a955d1b31f5..a4ea1af237bd 100644 --- a/tests/ui/span/multispan-import-lint.stderr +++ b/tests/ui/span/multispan-import-lint.stderr @@ -1,4 +1,4 @@ -warning: unused imports: `Eq`, `Ord`, `PartialEq`, `PartialOrd` +warning: unused imports: `Eq`, `Ord`, `PartialEq`, and `PartialOrd` --> $DIR/multispan-import-lint.rs:5:16 | LL | use std::cmp::{Eq, Ord, min, PartialEq, PartialOrd}; diff --git a/tests/ui/specialization/anyid-repro-125197.rs b/tests/ui/specialization/anyid-repro-125197.rs new file mode 100644 index 000000000000..9428d8dc2d41 --- /dev/null +++ b/tests/ui/specialization/anyid-repro-125197.rs @@ -0,0 +1,17 @@ +//@ aux-build: anyid-repro-125197.rs +//@ check-pass + +// Makes sure that we don't check `specializes(impl1, impl2)` for a pair of impls that don't +// actually participate in specialization. Since , +// we don't treat inductive cycles as errors -- so we may need to winnow more pairs of impls, and +// we try to winnow impls in favor of other impls. However, if we're *inside* the `specializes` +// query, then may have a query cycle if we call `specializes` again! + +extern crate anyid_repro_125197; +use anyid_repro_125197::AnyId; + +fn main() { + let x = "hello, world"; + let y: AnyId = x.into(); + let _ = y == x; +} diff --git a/tests/ui/specialization/auxiliary/anyid-repro-125197.rs b/tests/ui/specialization/auxiliary/anyid-repro-125197.rs new file mode 100644 index 000000000000..c2794959740e --- /dev/null +++ b/tests/ui/specialization/auxiliary/anyid-repro-125197.rs @@ -0,0 +1,26 @@ +use std::fmt::Display; +use std::sync::Arc; + +pub struct AnyId(()); + +impl PartialEq for AnyId { + fn eq(&self, _: &Self) -> bool { + todo!() + } +} + +impl PartialEq for AnyId { + fn eq(&self, _: &T) -> bool { + todo!() + } +} + +impl From for AnyId { + fn from(_: T) -> Self { + todo!() + } +} + +pub trait Identifier: Display + 'static {} + +impl Identifier for T where T: PartialEq + Display + 'static {} diff --git a/tests/ui/specialization/default-proj-ty-as-type-of-const-issue-125757.rs b/tests/ui/specialization/default-proj-ty-as-type-of-const-issue-125757.rs new file mode 100644 index 000000000000..59a015da84eb --- /dev/null +++ b/tests/ui/specialization/default-proj-ty-as-type-of-const-issue-125757.rs @@ -0,0 +1,19 @@ +#![feature(specialization)] +#![allow(incomplete_features)] + +trait Trait { + type Type; +} + +impl Trait for i32 { + default type Type = i32; +} + +struct Wrapper::Type> {} +//~^ ERROR `::Type` is forbidden as the type of a const generic parameter + +impl Wrapper {} +//~^ ERROR the constant `C` is not of type `::Type` +//~^^ ERROR mismatched types + +fn main() {} diff --git a/tests/ui/specialization/default-proj-ty-as-type-of-const-issue-125757.stderr b/tests/ui/specialization/default-proj-ty-as-type-of-const-issue-125757.stderr new file mode 100644 index 000000000000..b4c14c2294e5 --- /dev/null +++ b/tests/ui/specialization/default-proj-ty-as-type-of-const-issue-125757.stderr @@ -0,0 +1,36 @@ +error: `::Type` is forbidden as the type of a const generic parameter + --> $DIR/default-proj-ty-as-type-of-const-issue-125757.rs:12:25 + | +LL | struct Wrapper::Type> {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + +error: the constant `C` is not of type `::Type` + --> $DIR/default-proj-ty-as-type-of-const-issue-125757.rs:15:22 + | +LL | impl Wrapper {} + | ^^^^^^^^^^ expected associated type, found `usize` + | + = help: consider constraining the associated type `::Type` to `usize` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +note: required by a bound in `Wrapper` + --> $DIR/default-proj-ty-as-type-of-const-issue-125757.rs:12:16 + | +LL | struct Wrapper::Type> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Wrapper` + +error[E0308]: mismatched types + --> $DIR/default-proj-ty-as-type-of-const-issue-125757.rs:15:30 + | +LL | impl Wrapper {} + | ^ expected associated type, found `usize` + | + = note: expected associated type `::Type` + found type `usize` + = help: consider constraining the associated type `::Type` to `usize` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/specialization/dont-drop-upcast-candidate.rs b/tests/ui/specialization/dont-drop-upcast-candidate.rs new file mode 100644 index 000000000000..98d8cad7c1fb --- /dev/null +++ b/tests/ui/specialization/dont-drop-upcast-candidate.rs @@ -0,0 +1,13 @@ +#![feature(unsize)] + +use std::marker::Unsize; +use std::ops::Deref; + +trait Foo: Bar {} +trait Bar {} + +impl Bar for T where dyn Foo: Unsize {} +impl Bar for () {} +//~^ ERROR conflicting implementations of trait `Bar` for type `()` + +fn main() {} diff --git a/tests/ui/specialization/dont-drop-upcast-candidate.stderr b/tests/ui/specialization/dont-drop-upcast-candidate.stderr new file mode 100644 index 000000000000..dc0c54f9aa82 --- /dev/null +++ b/tests/ui/specialization/dont-drop-upcast-candidate.stderr @@ -0,0 +1,11 @@ +error[E0119]: conflicting implementations of trait `Bar` for type `()` + --> $DIR/dont-drop-upcast-candidate.rs:10:1 + | +LL | impl Bar for T where dyn Foo: Unsize {} + | ------------------------------------------------ first implementation here +LL | impl Bar for () {} + | ^^^^^^^^^^^^^^^ conflicting implementation for `()` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/specialization/source-impl-requires-constraining-predicates-ambig.next.stderr b/tests/ui/specialization/source-impl-requires-constraining-predicates-ambig.next.stderr new file mode 100644 index 000000000000..17918a77821d --- /dev/null +++ b/tests/ui/specialization/source-impl-requires-constraining-predicates-ambig.next.stderr @@ -0,0 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/source-impl-requires-constraining-predicates-ambig.rs:14:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/specialization/source-impl-requires-constraining-predicates-ambig.rs b/tests/ui/specialization/source-impl-requires-constraining-predicates-ambig.rs new file mode 100644 index 000000000000..977493c885b2 --- /dev/null +++ b/tests/ui/specialization/source-impl-requires-constraining-predicates-ambig.rs @@ -0,0 +1,29 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next] check-pass +//@[current] known-bug: unknown +//@[current] failure-status: 101 +//@[current] dont-check-compiler-stderr + +// Tests that rebasing from the concrete impl to the default impl also processes the +// `[u32; 0]: IntoIterator` predicate to constrain the `?U` impl arg. +// This test also makes sure that we don't do anything weird when rebasing the args +// is ambiguous. + +#![feature(specialization)] +//[next]~^ WARN the feature `specialization` is incomplete + +trait Spec { + type Assoc; +} + +default impl Spec for T where T: IntoIterator { + type Assoc = U; +} + +impl Spec for [T; 0] {} + +fn main() { + let x: <[_; 0] as Spec>::Assoc = 1; +} diff --git a/tests/ui/specialization/source-impl-requires-constraining-predicates.current.stderr b/tests/ui/specialization/source-impl-requires-constraining-predicates.current.stderr new file mode 100644 index 000000000000..3442e5b5ca61 --- /dev/null +++ b/tests/ui/specialization/source-impl-requires-constraining-predicates.current.stderr @@ -0,0 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/source-impl-requires-constraining-predicates.rs:9:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/specialization/source-impl-requires-constraining-predicates.next.stderr b/tests/ui/specialization/source-impl-requires-constraining-predicates.next.stderr new file mode 100644 index 000000000000..3442e5b5ca61 --- /dev/null +++ b/tests/ui/specialization/source-impl-requires-constraining-predicates.next.stderr @@ -0,0 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/source-impl-requires-constraining-predicates.rs:9:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/specialization/source-impl-requires-constraining-predicates.rs b/tests/ui/specialization/source-impl-requires-constraining-predicates.rs new file mode 100644 index 000000000000..532fc3676406 --- /dev/null +++ b/tests/ui/specialization/source-impl-requires-constraining-predicates.rs @@ -0,0 +1,24 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Tests that rebasing from the concrete impl to the default impl also processes the +// `[u32; 0]: IntoIterator` predicate to constrain the `?U` impl arg. + +#![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete + +trait Spec { + type Assoc; +} + +default impl Spec for T where T: IntoIterator { + type Assoc = U; +} + +impl Spec for [T; 0] {} + +fn main() { + let x: <[u32; 0] as Spec>::Assoc = 1; +} diff --git a/tests/ui/specialization/specialization-default-items-drop-coherence.next.stderr b/tests/ui/specialization/specialization-default-items-drop-coherence.next.stderr index 78d77a78e0ee..e9498a003179 100644 --- a/tests/ui/specialization/specialization-default-items-drop-coherence.next.stderr +++ b/tests/ui/specialization/specialization-default-items-drop-coherence.next.stderr @@ -7,13 +7,6 @@ LL | impl Overlap for u32 { LL | impl Overlap for ::Id { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` -error[E0282]: type annotations needed - --> $DIR/specialization-default-items-drop-coherence.rs:18:23 - | -LL | default type Id = T; - | ^ cannot infer type for associated type `::Id` +error: aborting due to 1 previous error -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0119, E0282. -For more information about an error, try `rustc --explain E0119`. +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/specialization/specialization-default-items-drop-coherence.rs b/tests/ui/specialization/specialization-default-items-drop-coherence.rs index fad041f2ee10..6dc012776391 100644 --- a/tests/ui/specialization/specialization-default-items-drop-coherence.rs +++ b/tests/ui/specialization/specialization-default-items-drop-coherence.rs @@ -15,7 +15,7 @@ trait Default { } impl Default for T { - default type Id = T; //[next]~ ERROR type annotations needed + default type Id = T; } trait Overlap { diff --git a/tests/ui/specialization/specialization-overlap-projection.next.stderr b/tests/ui/specialization/specialization-overlap-projection.next.stderr index ab040193fa4c..5b17696162ed 100644 --- a/tests/ui/specialization/specialization-overlap-projection.next.stderr +++ b/tests/ui/specialization/specialization-overlap-projection.next.stderr @@ -9,7 +9,7 @@ LL | #![feature(specialization)] = note: `#[warn(incomplete_features)]` on by default error[E0119]: conflicting implementations of trait `Foo` for type `u32` - --> $DIR/specialization-overlap-projection.rs:28:1 + --> $DIR/specialization-overlap-projection.rs:25:1 | LL | impl Foo for u32 {} | ---------------- first implementation here @@ -17,7 +17,7 @@ LL | impl Foo for ::Output {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` error[E0119]: conflicting implementations of trait `Foo` for type `u32` - --> $DIR/specialization-overlap-projection.rs:30:1 + --> $DIR/specialization-overlap-projection.rs:27:1 | LL | impl Foo for u32 {} | ---------------- first implementation here @@ -25,25 +25,6 @@ LL | impl Foo for u32 {} LL | impl Foo for ::Output {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` -error[E0282]: type annotations needed - --> $DIR/specialization-overlap-projection.rs:17:27 - | -LL | default type Output = bool; - | ^^^^ cannot infer type for associated type `::Output` +error: aborting due to 2 previous errors; 1 warning emitted -error[E0282]: type annotations needed - --> $DIR/specialization-overlap-projection.rs:21:35 - | -LL | impl Assoc for u8 { type Output = u8; } - | ^^ cannot infer type for associated type `::Output` - -error[E0282]: type annotations needed - --> $DIR/specialization-overlap-projection.rs:23:36 - | -LL | impl Assoc for u16 { type Output = u16; } - | ^^^ cannot infer type for associated type `::Output` - -error: aborting due to 5 previous errors; 1 warning emitted - -Some errors have detailed explanations: E0119, E0282. -For more information about an error, try `rustc --explain E0119`. +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/specialization/specialization-overlap-projection.rs b/tests/ui/specialization/specialization-overlap-projection.rs index 78e75f623c4b..16dccf82dcb9 100644 --- a/tests/ui/specialization/specialization-overlap-projection.rs +++ b/tests/ui/specialization/specialization-overlap-projection.rs @@ -15,13 +15,10 @@ trait Assoc { impl Assoc for T { default type Output = bool; - //[next]~^ ERROR type annotations needed } impl Assoc for u8 { type Output = u8; } -//[next]~^ ERROR type annotations needed impl Assoc for u16 { type Output = u16; } -//[next]~^ ERROR type annotations needed trait Foo {} impl Foo for u32 {} diff --git a/tests/crashes/124464.rs b/tests/ui/static/duplicated-fields-issue-124464.rs similarity index 57% rename from tests/crashes/124464.rs rename to tests/ui/static/duplicated-fields-issue-124464.rs index 471479f5cf1b..60609edbfebb 100644 --- a/tests/crashes/124464.rs +++ b/tests/ui/static/duplicated-fields-issue-124464.rs @@ -1,12 +1,16 @@ -//@ known-bug: rust-lang/rust #124464 +// Don't const eval fields with ambiguous layout. +// See issues #125842 and #124464. + enum TestOption { TestSome(T), TestSome(T), +//~^ ERROR the name `TestSome` is defined multiple times } pub struct Request { bar: TestOption, bar: u8, +//~^ ERROR field `bar` is already declared } fn default_instance() -> &'static Request { diff --git a/tests/ui/static/duplicated-fields-issue-124464.stderr b/tests/ui/static/duplicated-fields-issue-124464.stderr new file mode 100644 index 000000000000..a36192ae8d69 --- /dev/null +++ b/tests/ui/static/duplicated-fields-issue-124464.stderr @@ -0,0 +1,22 @@ +error[E0428]: the name `TestSome` is defined multiple times + --> $DIR/duplicated-fields-issue-124464.rs:6:5 + | +LL | TestSome(T), + | ----------- previous definition of the type `TestSome` here +LL | TestSome(T), + | ^^^^^^^^^^^ `TestSome` redefined here + | + = note: `TestSome` must be defined only once in the type namespace of this enum + +error[E0124]: field `bar` is already declared + --> $DIR/duplicated-fields-issue-124464.rs:12:5 + | +LL | bar: TestOption, + | -------------------- `bar` first declared here +LL | bar: u8, + | ^^^^^^^ field already declared + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0124, E0428. +For more information about an error, try `rustc --explain E0124`. diff --git a/tests/ui/static/duplicated-fields-issue-125842.rs b/tests/ui/static/duplicated-fields-issue-125842.rs new file mode 100644 index 000000000000..580b810232e0 --- /dev/null +++ b/tests/ui/static/duplicated-fields-issue-125842.rs @@ -0,0 +1,21 @@ +// Do not try to evaluate static initalizers that reference +// ill-defined types. This used to be an ICE. +// See issues #125842 and #124464. +struct Struct { + field: Option, + field: u8, +//~^ ERROR field `field` is already declared +} + +static STATIC_A: Struct = Struct { + field: 1 +}; + +static STATIC_B: Struct = { + let field = 1; + Struct { + field, + } +}; + +fn main() {} diff --git a/tests/ui/static/duplicated-fields-issue-125842.stderr b/tests/ui/static/duplicated-fields-issue-125842.stderr new file mode 100644 index 000000000000..c80bb99005ee --- /dev/null +++ b/tests/ui/static/duplicated-fields-issue-125842.stderr @@ -0,0 +1,11 @@ +error[E0124]: field `field` is already declared + --> $DIR/duplicated-fields-issue-125842.rs:6:5 + | +LL | field: Option, + | ----------------- `field` first declared here +LL | field: u8, + | ^^^^^^^^^ field already declared + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0124`. diff --git a/tests/ui/static/static-mut-not-constant.stderr b/tests/ui/static/static-mut-not-constant.stderr index d125bec59435..46dc175cb298 100644 --- a/tests/ui/static/static-mut-not-constant.stderr +++ b/tests/ui/static/static-mut-not-constant.stderr @@ -5,7 +5,7 @@ LL | static mut a: Box = Box::new(3); | ^^^^^^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` error: aborting due to 1 previous error diff --git a/tests/ui/static/static-vec-repeat-not-constant.stderr b/tests/ui/static/static-vec-repeat-not-constant.stderr index db0c7eb8d35f..a3b930323d5b 100644 --- a/tests/ui/static/static-vec-repeat-not-constant.stderr +++ b/tests/ui/static/static-vec-repeat-not-constant.stderr @@ -5,7 +5,7 @@ LL | static a: [isize; 2] = [foo(); 2]; | ^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` error: aborting due to 1 previous error diff --git a/tests/ui/auxiliary/check_static_recursion_foreign_helper.rs b/tests/ui/statics/auxiliary/check_static_recursion_foreign_helper.rs similarity index 100% rename from tests/ui/auxiliary/check_static_recursion_foreign_helper.rs rename to tests/ui/statics/auxiliary/check_static_recursion_foreign_helper.rs diff --git a/tests/ui/check-static-immutable-mut-slices.rs b/tests/ui/statics/check-immutable-mut-slices.rs similarity index 100% rename from tests/ui/check-static-immutable-mut-slices.rs rename to tests/ui/statics/check-immutable-mut-slices.rs diff --git a/tests/ui/check-static-immutable-mut-slices.stderr b/tests/ui/statics/check-immutable-mut-slices.stderr similarity index 84% rename from tests/ui/check-static-immutable-mut-slices.stderr rename to tests/ui/statics/check-immutable-mut-slices.stderr index 402f9032b640..5cb35a7c21eb 100644 --- a/tests/ui/check-static-immutable-mut-slices.stderr +++ b/tests/ui/statics/check-immutable-mut-slices.stderr @@ -1,5 +1,5 @@ error[E0764]: mutable references are not allowed in the final value of statics - --> $DIR/check-static-immutable-mut-slices.rs:3:37 + --> $DIR/check-immutable-mut-slices.rs:3:37 | LL | static TEST: &'static mut [isize] = &mut []; | ^^^^^^^ diff --git a/tests/ui/check-static-recursion-foreign.rs b/tests/ui/statics/check-recursion-foreign.rs similarity index 100% rename from tests/ui/check-static-recursion-foreign.rs rename to tests/ui/statics/check-recursion-foreign.rs diff --git a/tests/ui/check-static-values-constraints.rs b/tests/ui/statics/check-values-constraints.rs similarity index 100% rename from tests/ui/check-static-values-constraints.rs rename to tests/ui/statics/check-values-constraints.rs diff --git a/tests/ui/check-static-values-constraints.stderr b/tests/ui/statics/check-values-constraints.stderr similarity index 75% rename from tests/ui/check-static-values-constraints.stderr rename to tests/ui/statics/check-values-constraints.stderr index fe5f2a34272d..24763c175fc8 100644 --- a/tests/ui/check-static-values-constraints.stderr +++ b/tests/ui/statics/check-values-constraints.stderr @@ -1,5 +1,5 @@ error[E0493]: destructor of `SafeStruct` cannot be evaluated at compile-time - --> $DIR/check-static-values-constraints.rs:64:7 + --> $DIR/check-values-constraints.rs:64:7 | LL | ..SafeStruct { | _______^ @@ -12,7 +12,7 @@ LL | }; | - value is dropped here error[E0010]: allocations are not allowed in statics - --> $DIR/check-static-values-constraints.rs:81:33 + --> $DIR/check-values-constraints.rs:81:33 | LL | static STATIC11: Vec = vec![MyOwned]; | ^^^^^^^^^^^^^ allocation not allowed in statics @@ -20,30 +20,30 @@ LL | static STATIC11: Vec = vec![MyOwned]; = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const fn `slice::::into_vec::` in statics - --> $DIR/check-static-values-constraints.rs:81:33 + --> $DIR/check-values-constraints.rs:81:33 | LL | static STATIC11: Vec = vec![MyOwned]; | ^^^^^^^^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const fn `::to_string` in statics - --> $DIR/check-static-values-constraints.rs:92:38 + --> $DIR/check-values-constraints.rs:92:38 | LL | field2: SafeEnum::Variant4("str".to_string()), | ^^^^^^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` help: add `#![feature(const_trait_impl)]` to the crate attributes to enable | LL + #![feature(const_trait_impl)] | error[E0010]: allocations are not allowed in statics - --> $DIR/check-static-values-constraints.rs:96:5 + --> $DIR/check-values-constraints.rs:96:5 | LL | vec![MyOwned], | ^^^^^^^^^^^^^ allocation not allowed in statics @@ -51,17 +51,17 @@ LL | vec![MyOwned], = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const fn `slice::::into_vec::` in statics - --> $DIR/check-static-values-constraints.rs:96:5 + --> $DIR/check-values-constraints.rs:96:5 | LL | vec![MyOwned], | ^^^^^^^^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0010]: allocations are not allowed in statics - --> $DIR/check-static-values-constraints.rs:98:5 + --> $DIR/check-values-constraints.rs:98:5 | LL | vec![MyOwned], | ^^^^^^^^^^^^^ allocation not allowed in statics @@ -69,17 +69,17 @@ LL | vec![MyOwned], = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const fn `slice::::into_vec::` in statics - --> $DIR/check-static-values-constraints.rs:98:5 + --> $DIR/check-values-constraints.rs:98:5 | LL | vec![MyOwned], | ^^^^^^^^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0010]: allocations are not allowed in statics - --> $DIR/check-static-values-constraints.rs:103:6 + --> $DIR/check-values-constraints.rs:103:6 | LL | &vec![MyOwned], | ^^^^^^^^^^^^^ allocation not allowed in statics @@ -87,17 +87,17 @@ LL | &vec![MyOwned], = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const fn `slice::::into_vec::` in statics - --> $DIR/check-static-values-constraints.rs:103:6 + --> $DIR/check-values-constraints.rs:103:6 | LL | &vec![MyOwned], | ^^^^^^^^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0010]: allocations are not allowed in statics - --> $DIR/check-static-values-constraints.rs:105:6 + --> $DIR/check-values-constraints.rs:105:6 | LL | &vec![MyOwned], | ^^^^^^^^^^^^^ allocation not allowed in statics @@ -105,17 +105,17 @@ LL | &vec![MyOwned], = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const fn `slice::::into_vec::` in statics - --> $DIR/check-static-values-constraints.rs:105:6 + --> $DIR/check-values-constraints.rs:105:6 | LL | &vec![MyOwned], | ^^^^^^^^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0010]: allocations are not allowed in statics - --> $DIR/check-static-values-constraints.rs:111:31 + --> $DIR/check-values-constraints.rs:111:31 | LL | static STATIC19: Vec = vec![3]; | ^^^^^^^ allocation not allowed in statics @@ -123,17 +123,17 @@ LL | static STATIC19: Vec = vec![3]; = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const fn `slice::::into_vec::` in statics - --> $DIR/check-static-values-constraints.rs:111:31 + --> $DIR/check-values-constraints.rs:111:31 | LL | static STATIC19: Vec = vec![3]; | ^^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0010]: allocations are not allowed in statics - --> $DIR/check-static-values-constraints.rs:117:32 + --> $DIR/check-values-constraints.rs:117:32 | LL | static x: Vec = vec![3]; | ^^^^^^^ allocation not allowed in statics @@ -141,17 +141,17 @@ LL | static x: Vec = vec![3]; = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const fn `slice::::into_vec::` in statics - --> $DIR/check-static-values-constraints.rs:117:32 + --> $DIR/check-values-constraints.rs:117:32 | LL | static x: Vec = vec![3]; | ^^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0507]: cannot move out of static item `x` - --> $DIR/check-static-values-constraints.rs:119:9 + --> $DIR/check-values-constraints.rs:119:9 | LL | x | ^ move occurs because `x` has type `Vec`, which does not implement the `Copy` trait diff --git a/tests/ui/statics/const_generics.rs b/tests/ui/statics/const_generics.rs new file mode 100644 index 000000000000..70d9b933a765 --- /dev/null +++ b/tests/ui/statics/const_generics.rs @@ -0,0 +1,26 @@ +//! Check that we lose the information that `BAR` points to `FOO` +//! when going through a const generic. +//! This is not an intentional guarantee, it just describes the status quo. + +//@ run-pass +// With optimizations, LLVM will deduplicate the constant `X` whose +// value is `&42` to just be a reference to the static. This is correct, +// but obscures the issue we're trying to show. +//@ revisions: opt noopt +//@[noopt] compile-flags: -Copt-level=0 +//@[opt] compile-flags: -O + +#![feature(const_refs_to_static)] +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +static FOO: usize = 42; +const BAR: &usize = &FOO; +fn foo() { + // Without optimizations, `X` ends up pointing to a copy of `FOO` instead of `FOO` itself. + assert_eq!(cfg!(opt), std::ptr::eq(X, &FOO)); +} + +fn main() { + foo::(); +} diff --git a/tests/ui/structs-enums/enum-rec/issue-17431-6.rs b/tests/ui/structs-enums/enum-rec/issue-17431-6.rs index d8343704f12b..0183bdba1119 100644 --- a/tests/ui/structs-enums/enum-rec/issue-17431-6.rs +++ b/tests/ui/structs-enums/enum-rec/issue-17431-6.rs @@ -1,4 +1,4 @@ -//@ ignore-macos: cycle error does not appear on apple +//@ ignore-apple: cycle error does not appear on apple use std::sync::Mutex; diff --git a/tests/ui/structs-enums/newtype-struct-with-dtor.rs b/tests/ui/structs-enums/newtype-struct-with-dtor.rs index 19672e41c9a3..16439a7fedd3 100644 --- a/tests/ui/structs-enums/newtype-struct-with-dtor.rs +++ b/tests/ui/structs-enums/newtype-struct-with-dtor.rs @@ -3,8 +3,10 @@ #![allow(unused_variables)] //@ pretty-expanded FIXME #23616 +#[allow(dead_code)] pub struct Fd(u32); +#[allow(dead_code)] fn foo(a: u32) {} impl Drop for Fd { diff --git a/tests/ui/structs-enums/rec-align-u64.rs b/tests/ui/structs-enums/rec-align-u64.rs index 72601bb16fff..8ea72fdf45c8 100644 --- a/tests/ui/structs-enums/rec-align-u64.rs +++ b/tests/ui/structs-enums/rec-align-u64.rs @@ -30,21 +30,21 @@ struct Outer { t: Inner } - -#[cfg(any(target_os = "android", - target_os = "dragonfly", - target_os = "emscripten", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "vxworks", - target_os = "nto", +#[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "vxworks", + target_os = "nto", + target_vendor = "apple", ))] mod m { #[cfg(target_arch = "x86")] diff --git a/tests/ui/structs-enums/uninstantiable-struct.rs b/tests/ui/structs-enums/uninstantiable-struct.rs index 97bc7d8414e7..1074dbcd6e6b 100644 --- a/tests/ui/structs-enums/uninstantiable-struct.rs +++ b/tests/ui/structs-enums/uninstantiable-struct.rs @@ -1,4 +1,5 @@ //@ run-pass -pub struct Z(#[allow(dead_code)] &'static Z); +#[allow(dead_code)] +pub struct Z(&'static Z); pub fn main() {} diff --git a/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr b/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr index 2c4be26a82b7..afbb9c32d516 100644 --- a/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr +++ b/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr @@ -10,7 +10,6 @@ LL | t.clone() | = note: expected type parameter `_` found reference `&_` - = note: the caller chooses a type for `T` which can be different from `&T` note: `T` does not implement `Clone`, so `&T` was cloned instead --> $DIR/clone-on-unconstrained-borrowed-type-param.rs:3:5 | diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.no_std.fixed b/tests/ui/suggestions/core-std-import-order-issue-83564.no_std.fixed new file mode 100644 index 000000000000..02d667d98442 --- /dev/null +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.no_std.fixed @@ -0,0 +1,22 @@ +//@ edition:2018 +// +// This is a regression test for #83564. +// For some reason, Rust 2018 or higher is required to reproduce the bug. +//@ run-rustfix +//@ revisions: no_std std +//@ [no_std]compile-flags: --cfg=no_std -C panic=abort +#![cfg_attr(no_std, no_std)] + +use core::num::NonZero; + +fn main() { + //~^ HELP consider importing this struct + let _x = NonZero::new(5u32).unwrap(); + //~^ ERROR failed to resolve: use of undeclared type `NonZero` +} + +#[allow(dead_code)] +#[cfg_attr(no_std, panic_handler)] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.stderr b/tests/ui/suggestions/core-std-import-order-issue-83564.no_std.stderr similarity index 71% rename from tests/ui/suggestions/core-std-import-order-issue-83564.stderr rename to tests/ui/suggestions/core-std-import-order-issue-83564.no_std.stderr index 8665cc6d87cf..d73f613bf9c8 100644 --- a/tests/ui/suggestions/core-std-import-order-issue-83564.stderr +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.no_std.stderr @@ -1,15 +1,13 @@ error[E0433]: failed to resolve: use of undeclared type `NonZero` - --> $DIR/core-std-import-order-issue-83564.rs:8:14 + --> $DIR/core-std-import-order-issue-83564.rs:12:14 | LL | let _x = NonZero::new(5u32).unwrap(); | ^^^^^^^ use of undeclared type `NonZero` | -help: consider importing one of these items +help: consider importing this struct | LL + use core::num::NonZero; | -LL + use std::num::NonZero; - | error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.rs b/tests/ui/suggestions/core-std-import-order-issue-83564.rs index 6f2bdd7a38af..5bb5bfe176ba 100644 --- a/tests/ui/suggestions/core-std-import-order-issue-83564.rs +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.rs @@ -2,9 +2,19 @@ // // This is a regression test for #83564. // For some reason, Rust 2018 or higher is required to reproduce the bug. +//@ run-rustfix +//@ revisions: no_std std +//@ [no_std]compile-flags: --cfg=no_std -C panic=abort +#![cfg_attr(no_std, no_std)] fn main() { - //~^ HELP consider importing one of these items + //~^ HELP consider importing this struct let _x = NonZero::new(5u32).unwrap(); //~^ ERROR failed to resolve: use of undeclared type `NonZero` } + +#[allow(dead_code)] +#[cfg_attr(no_std, panic_handler)] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.std.fixed b/tests/ui/suggestions/core-std-import-order-issue-83564.std.fixed new file mode 100644 index 000000000000..3492b42c685f --- /dev/null +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.std.fixed @@ -0,0 +1,22 @@ +//@ edition:2018 +// +// This is a regression test for #83564. +// For some reason, Rust 2018 or higher is required to reproduce the bug. +//@ run-rustfix +//@ revisions: no_std std +//@ [no_std]compile-flags: --cfg=no_std -C panic=abort +#![cfg_attr(no_std, no_std)] + +use std::num::NonZero; + +fn main() { + //~^ HELP consider importing this struct + let _x = NonZero::new(5u32).unwrap(); + //~^ ERROR failed to resolve: use of undeclared type `NonZero` +} + +#[allow(dead_code)] +#[cfg_attr(no_std, panic_handler)] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.std.stderr b/tests/ui/suggestions/core-std-import-order-issue-83564.std.stderr new file mode 100644 index 000000000000..ebfe197b45d6 --- /dev/null +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.std.stderr @@ -0,0 +1,14 @@ +error[E0433]: failed to resolve: use of undeclared type `NonZero` + --> $DIR/core-std-import-order-issue-83564.rs:12:14 + | +LL | let _x = NonZero::new(5u32).unwrap(); + | ^^^^^^^ use of undeclared type `NonZero` + | +help: consider importing this struct + | +LL + use std::num::NonZero; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/suggestions/crate-or-module-typo.stderr b/tests/ui/suggestions/crate-or-module-typo.stderr index 457d77906468..084d0408a8e7 100644 --- a/tests/ui/suggestions/crate-or-module-typo.stderr +++ b/tests/ui/suggestions/crate-or-module-typo.stderr @@ -30,9 +30,7 @@ help: there is a crate or module with a similar name | LL | bar: std::cell::Cell | ~~~ -help: consider importing one of these items - | -LL + use core::cell; +help: consider importing this module | LL + use std::cell; | diff --git a/tests/ui/suggestions/deref-path-method.stderr b/tests/ui/suggestions/deref-path-method.stderr index bfcc2307fd7f..b27d9aef0661 100644 --- a/tests/ui/suggestions/deref-path-method.stderr +++ b/tests/ui/suggestions/deref-path-method.stderr @@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec<_, _>` consider using one of the foll Vec::::with_capacity Vec::::try_with_capacity Vec::::from_raw_parts - and 6 others + and 4 others --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: the function `contains` is implemented on `[_]` | diff --git a/tests/ui/suggestions/derive-clone-for-eq.fixed b/tests/ui/suggestions/derive-clone-for-eq.fixed index 4dc362f94787..cf800c6e47d8 100644 --- a/tests/ui/suggestions/derive-clone-for-eq.fixed +++ b/tests/ui/suggestions/derive-clone-for-eq.fixed @@ -1,6 +1,7 @@ //@ run-rustfix // https://github.com/rust-lang/rust/issues/79076 +#[allow(dead_code)] #[derive(Clone, Eq)] //~ ERROR [E0277] pub struct Struct(T); diff --git a/tests/ui/suggestions/derive-clone-for-eq.rs b/tests/ui/suggestions/derive-clone-for-eq.rs index b3635000f165..84736426bac0 100644 --- a/tests/ui/suggestions/derive-clone-for-eq.rs +++ b/tests/ui/suggestions/derive-clone-for-eq.rs @@ -1,6 +1,7 @@ //@ run-rustfix // https://github.com/rust-lang/rust/issues/79076 +#[allow(dead_code)] #[derive(Clone, Eq)] //~ ERROR [E0277] pub struct Struct(T); diff --git a/tests/ui/suggestions/derive-clone-for-eq.stderr b/tests/ui/suggestions/derive-clone-for-eq.stderr index 6fae6e1316df..54670fbffcfb 100644 --- a/tests/ui/suggestions/derive-clone-for-eq.stderr +++ b/tests/ui/suggestions/derive-clone-for-eq.stderr @@ -1,11 +1,11 @@ error[E0277]: the trait bound `T: Clone` is not satisfied - --> $DIR/derive-clone-for-eq.rs:4:17 + --> $DIR/derive-clone-for-eq.rs:5:17 | LL | #[derive(Clone, Eq)] | ^^ the trait `Clone` is not implemented for `T`, which is required by `Struct: PartialEq` | note: required for `Struct` to implement `PartialEq` - --> $DIR/derive-clone-for-eq.rs:7:19 + --> $DIR/derive-clone-for-eq.rs:8:19 | LL | impl PartialEq for Struct | ----- ^^^^^^^^^^^^ ^^^^^^^^^ diff --git a/tests/ui/dont-suggest-private-trait-method.rs b/tests/ui/suggestions/dont-suggest-private-trait-method.rs similarity index 100% rename from tests/ui/dont-suggest-private-trait-method.rs rename to tests/ui/suggestions/dont-suggest-private-trait-method.rs diff --git a/tests/ui/dont-suggest-private-trait-method.stderr b/tests/ui/suggestions/dont-suggest-private-trait-method.stderr similarity index 100% rename from tests/ui/dont-suggest-private-trait-method.stderr rename to tests/ui/suggestions/dont-suggest-private-trait-method.stderr diff --git a/tests/ui/suggestions/ice-unwrap-probe-many-result-125876.rs b/tests/ui/suggestions/ice-unwrap-probe-many-result-125876.rs new file mode 100644 index 000000000000..efa296db47cf --- /dev/null +++ b/tests/ui/suggestions/ice-unwrap-probe-many-result-125876.rs @@ -0,0 +1,11 @@ +// Regression test for ICE #125876 + +fn main() { + std::ptr::from_ref(num).cast_mut().as_deref(); + //~^ ERROR cannot find value `num` in this scope + //~| ERROR no method named `as_deref` found for raw pointer `*mut _` in the current scope + //~| WARN type annotations needed + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! + //~| WARN type annotations needed + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! +} diff --git a/tests/ui/suggestions/ice-unwrap-probe-many-result-125876.stderr b/tests/ui/suggestions/ice-unwrap-probe-many-result-125876.stderr new file mode 100644 index 000000000000..d610a3b7cadd --- /dev/null +++ b/tests/ui/suggestions/ice-unwrap-probe-many-result-125876.stderr @@ -0,0 +1,40 @@ +error[E0425]: cannot find value `num` in this scope + --> $DIR/ice-unwrap-probe-many-result-125876.rs:4:24 + | +LL | std::ptr::from_ref(num).cast_mut().as_deref(); + | ^^^ not found in this scope + +warning: type annotations needed + --> $DIR/ice-unwrap-probe-many-result-125876.rs:4:29 + | +LL | std::ptr::from_ref(num).cast_mut().as_deref(); + | ^^^^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! + = note: for more information, see issue #46906 + = note: `#[warn(tyvar_behind_raw_pointer)]` on by default + +warning: type annotations needed + --> $DIR/ice-unwrap-probe-many-result-125876.rs:4:40 + | +LL | std::ptr::from_ref(num).cast_mut().as_deref(); + | ^^^^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! + = note: for more information, see issue #46906 + +error[E0599]: no method named `as_deref` found for raw pointer `*mut _` in the current scope + --> $DIR/ice-unwrap-probe-many-result-125876.rs:4:40 + | +LL | std::ptr::from_ref(num).cast_mut().as_deref(); + | ^^^^^^^^ + | +help: there is a method `as_ref` with a similar name + | +LL | std::ptr::from_ref(num).cast_mut().as_ref(); + | ~~~~~~ + +error: aborting due to 2 previous errors; 2 warnings emitted + +Some errors have detailed explanations: E0425, E0599. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/suggestions/into-str.stderr b/tests/ui/suggestions/into-str.stderr index 2fdfab8bdc54..6c1e1ec428fe 100644 --- a/tests/ui/suggestions/into-str.stderr +++ b/tests/ui/suggestions/into-str.stderr @@ -8,12 +8,12 @@ LL | foo(String::new()); | = note: to coerce a `String` into a `&str`, use `&*` as a prefix = help: the following other types implement trait `From`: - > - > - > - >> - >> - > + `String` implements `From<&String>` + `String` implements `From<&mut str>` + `String` implements `From<&str>` + `String` implements `From>` + `String` implements `From>` + `String` implements `From` = note: required for `String` to implement `Into<&str>` note: required by a bound in `foo` --> $DIR/into-str.rs:1:31 diff --git a/tests/ui/suggestions/issue-109291.stderr b/tests/ui/suggestions/issue-109291.stderr index a173bbbb4900..d4a9351af3a8 100644 --- a/tests/ui/suggestions/issue-109291.stderr +++ b/tests/ui/suggestions/issue-109291.stderr @@ -8,7 +8,6 @@ note: if you're trying to build a new `Backtrace` consider using one of the foll Backtrace::capture Backtrace::force_capture Backtrace::disabled - Backtrace::create --> $SRC_DIR/std/src/backtrace.rs:LL:COL help: there is an associated function `force_capture` with a similar name | diff --git a/tests/ui/suggestions/issue-71394-no-from-impl.stderr b/tests/ui/suggestions/issue-71394-no-from-impl.stderr index e626b5b0eb7c..79f5dcf4b730 100644 --- a/tests/ui/suggestions/issue-71394-no-from-impl.stderr +++ b/tests/ui/suggestions/issue-71394-no-from-impl.stderr @@ -5,14 +5,14 @@ LL | let _: &[i8] = data.into(); | ^^^^ the trait `From<&[u8]>` is not implemented for `&[i8]`, which is required by `&[u8]: Into<_>` | = help: the following other types implement trait `From`: - <[T; 10] as From<(T, T, T, T, T, T, T, T, T, T)>> - <[T; 11] as From<(T, T, T, T, T, T, T, T, T, T, T)>> - <[T; 12] as From<(T, T, T, T, T, T, T, T, T, T, T, T)>> - <[T; 1] as From<(T,)>> - <[T; 2] as From<(T, T)>> - <[T; 3] as From<(T, T, T)>> - <[T; 4] as From<(T, T, T, T)>> - <[T; 5] as From<(T, T, T, T, T)>> + `[T; 10]` implements `From<(T, T, T, T, T, T, T, T, T, T)>` + `[T; 11]` implements `From<(T, T, T, T, T, T, T, T, T, T, T)>` + `[T; 12]` implements `From<(T, T, T, T, T, T, T, T, T, T, T, T)>` + `[T; 1]` implements `From<(T,)>` + `[T; 2]` implements `From<(T, T)>` + `[T; 3]` implements `From<(T, T, T)>` + `[T; 4]` implements `From<(T, T, T, T)>` + `[T; 5]` implements `From<(T, T, T, T, T)>` and 6 others = note: required for `&[u8]` to implement `Into<&[i8]>` diff --git a/tests/ui/suggestions/issue-85347.rs b/tests/ui/suggestions/issue-85347.rs index 95e76e76cfa8..5d1a902af304 100644 --- a/tests/ui/suggestions/issue-85347.rs +++ b/tests/ui/suggestions/issue-85347.rs @@ -3,12 +3,12 @@ trait Foo { type Bar<'a>: Deref::Bar>; //~^ ERROR associated type takes 1 lifetime argument but 0 lifetime arguments were supplied //~| HELP add missing - //~| ERROR associated type bindings are not allowed here - //~| HELP consider removing this type binding + //~| ERROR associated item constraints are not allowed here + //~| HELP consider removing this associated item binding //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments were supplied //~| HELP add missing - //~| ERROR associated type bindings are not allowed here - //~| HELP consider removing this type binding + //~| ERROR associated item constraints are not allowed here + //~| HELP consider removing this associated item binding } fn main() {} diff --git a/tests/ui/suggestions/issue-85347.stderr b/tests/ui/suggestions/issue-85347.stderr index de0aa09ce49c..b3616041c4cd 100644 --- a/tests/ui/suggestions/issue-85347.stderr +++ b/tests/ui/suggestions/issue-85347.stderr @@ -14,13 +14,13 @@ help: add missing lifetime argument LL | type Bar<'a>: Deref::Bar<'a, Target = Self>>; | +++ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-85347.rs:3:46 | LL | type Bar<'a>: Deref::Bar>; - | ^^^^^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | LL | type Bar<'a>: Deref::Bar>; | ~~~~~~~~~~~~~~~ @@ -42,14 +42,14 @@ help: add missing lifetime argument LL | type Bar<'a>: Deref::Bar<'a, Target = Self>>; | +++ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-85347.rs:3:46 | LL | type Bar<'a>: Deref::Bar>; - | ^^^^^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^^^^^ associated item constraint not allowed here | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider removing this type binding +help: consider removing this associated item binding | LL | type Bar<'a>: Deref::Bar>; | ~~~~~~~~~~~~~~~ diff --git a/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.default.stderr b/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.default.stderr index 24e2e0a0f7a2..71ac55bf0448 100644 --- a/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.default.stderr +++ b/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.default.stderr @@ -1,57 +1,25 @@ -error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-in-assoc-const-type.rs:6:14 - | -LL | const A: &str = ""; - | ^ expected named lifetime parameter - | -help: consider introducing a named lifetime parameter - | -LL ~ trait ZstAssert<'a>: Sized { -LL ~ const A: &'a str = ""; - | - -error[E0106]: missing lifetime specifier +error[E0726]: implicit elided lifetime not allowed here --> $DIR/missing-lifetime-in-assoc-const-type.rs:7:14 | LL | const B: S = S { s: &() }; - | ^ expected named lifetime parameter + | ^ expected lifetime parameter | -help: consider introducing a named lifetime parameter - | -LL ~ trait ZstAssert<'a>: Sized { -LL | const A: &str = ""; -LL ~ const B: S<'a> = S { s: &() }; +help: indicate the anonymous lifetime | +LL | const B: S<'_> = S { s: &() }; + | ++++ -error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-in-assoc-const-type.rs:8:15 - | -LL | const C: &'_ str = ""; - | ^^ expected named lifetime parameter - | -help: consider introducing a named lifetime parameter - | -LL ~ trait ZstAssert<'a>: Sized { -LL | const A: &str = ""; -LL | const B: S = S { s: &() }; -LL ~ const C: &'a str = ""; - | - -error[E0106]: missing lifetime specifiers +error[E0726]: implicit elided lifetime not allowed here --> $DIR/missing-lifetime-in-assoc-const-type.rs:9:14 | LL | const D: T = T { a: &(), b: &() }; - | ^ expected 2 lifetime parameters + | ^ expected lifetime parameters | -help: consider introducing a named lifetime parameter - | -LL ~ trait ZstAssert<'a>: Sized { -LL | const A: &str = ""; -LL | const B: S = S { s: &() }; -LL | const C: &'_ str = ""; -LL ~ const D: T<'a, 'a> = T { a: &(), b: &() }; +help: indicate the anonymous lifetimes | +LL | const D: T<'_, '_> = T { a: &(), b: &() }; + | ++++++++ -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0106`. +For more information about this error, try `rustc --explain E0726`. diff --git a/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.generic_const_items.stderr b/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.generic_const_items.stderr index a97ffe7da79e..71ac55bf0448 100644 --- a/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.generic_const_items.stderr +++ b/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.generic_const_items.stderr @@ -1,47 +1,25 @@ -error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-in-assoc-const-type.rs:6:14 - | -LL | const A: &str = ""; - | ^ expected named lifetime parameter - | -help: consider introducing a named lifetime parameter - | -LL | const A<'a>: &'a str = ""; - | ++++ ++ - -error[E0106]: missing lifetime specifier +error[E0726]: implicit elided lifetime not allowed here --> $DIR/missing-lifetime-in-assoc-const-type.rs:7:14 | LL | const B: S = S { s: &() }; - | ^ expected named lifetime parameter + | ^ expected lifetime parameter | -help: consider introducing a named lifetime parameter +help: indicate the anonymous lifetime | -LL | const B<'a>: S<'a> = S { s: &() }; - | ++++ ++++ +LL | const B: S<'_> = S { s: &() }; + | ++++ -error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-in-assoc-const-type.rs:8:15 - | -LL | const C: &'_ str = ""; - | ^^ expected named lifetime parameter - | -help: consider introducing a named lifetime parameter - | -LL | const C<'a>: &'a str = ""; - | ++++ ~~ - -error[E0106]: missing lifetime specifiers +error[E0726]: implicit elided lifetime not allowed here --> $DIR/missing-lifetime-in-assoc-const-type.rs:9:14 | LL | const D: T = T { a: &(), b: &() }; - | ^ expected 2 lifetime parameters + | ^ expected lifetime parameters | -help: consider introducing a named lifetime parameter +help: indicate the anonymous lifetimes | -LL | const D<'a>: T<'a, 'a> = T { a: &(), b: &() }; - | ++++ ++++++++ +LL | const D: T<'_, '_> = T { a: &(), b: &() }; + | ++++++++ -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0106`. +For more information about this error, try `rustc --explain E0726`. diff --git a/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.rs b/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.rs index f36a6567e423..a60f0b94587f 100644 --- a/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.rs +++ b/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.rs @@ -3,10 +3,10 @@ #![cfg_attr(generic_const_items, feature(generic_const_items), allow(incomplete_features))] trait ZstAssert: Sized { - const A: &str = ""; //~ ERROR missing lifetime specifier - const B: S = S { s: &() }; //~ ERROR missing lifetime specifier - const C: &'_ str = ""; //~ ERROR missing lifetime specifier - const D: T = T { a: &(), b: &() }; //~ ERROR missing lifetime specifier + const A: &str = ""; + const B: S = S { s: &() }; //~ ERROR implicit elided lifetime not allowed here + const C: &'_ str = ""; + const D: T = T { a: &(), b: &() }; //~ ERROR implicit elided lifetime not allowed here } struct S<'a> { diff --git a/tests/ui/suggestions/missing-lifetime-specifier.rs b/tests/ui/suggestions/missing-lifetime-specifier.rs index cd7fa0c1d856..dd437fc0c0b8 100644 --- a/tests/ui/suggestions/missing-lifetime-specifier.rs +++ b/tests/ui/suggestions/missing-lifetime-specifier.rs @@ -1,6 +1,11 @@ -// different number of duplicated diagnostics on different targets -//@ only-x86_64 -//@ only-linux +// The specific errors produced depend the thread-local implementation. +// Run only on platforms with "fast" TLS. +//@ ignore-windows FIXME(#84933) +//@ ignore-wasm globals are used instead of thread locals +//@ ignore-emscripten globals are used instead of thread locals +//@ ignore-android does not use #[thread_local] +//@ ignore-nto does not use #[thread_local] +// Different number of duplicated diagnostics on different targets //@ compile-flags: -Zdeduplicate-diagnostics=yes #![allow(bare_trait_objects)] @@ -21,23 +26,19 @@ trait Tar<'t, 'k, I> {} thread_local! { static a: RefCell>>> = RefCell::new(HashMap::new()); - //~^ ERROR missing lifetime specifiers - //~| ERROR missing lifetime specifiers + //~^ ERROR missing lifetime specifiers } thread_local! { static b: RefCell>>> = RefCell::new(HashMap::new()); - //~^ ERROR missing lifetime specifiers - //~| ERROR missing lifetime specifiers + //~^ ERROR missing lifetime specifiers } thread_local! { static c: RefCell>>>> = RefCell::new(HashMap::new()); //~^ ERROR missing lifetime specifiers - //~| ERROR missing lifetime specifiers } thread_local! { static d: RefCell>>>> = RefCell::new(HashMap::new()); //~^ ERROR missing lifetime specifiers - //~| ERROR missing lifetime specifiers } thread_local! { @@ -47,8 +48,7 @@ thread_local! { thread_local! { static f: RefCell>>>> = RefCell::new(HashMap::new()); //~^ ERROR trait takes 2 lifetime arguments but 1 lifetime argument was supplied - //~| ERROR missing lifetime - //~| ERROR missing lifetime + //~| ERROR missing lifetime specifier } fn main() {} diff --git a/tests/ui/suggestions/missing-lifetime-specifier.stderr b/tests/ui/suggestions/missing-lifetime-specifier.stderr index 2b85cfde7b64..e0b6bb872b55 100644 --- a/tests/ui/suggestions/missing-lifetime-specifier.stderr +++ b/tests/ui/suggestions/missing-lifetime-specifier.stderr @@ -1,5 +1,5 @@ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:23:44 + --> $DIR/missing-lifetime-specifier.rs:28:44 | LL | static a: RefCell>>> = RefCell::new(HashMap::new()); | ^^^ expected 2 lifetime parameters @@ -11,20 +11,7 @@ LL | static a: RefCell>>>> = RefC | ++++++++++++++++++ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:23:44 - | -LL | / thread_local! { -LL | | static a: RefCell>>> = RefCell::new(HashMap::new()); - | | ^^^ expected 2 lifetime parameters -LL | | -LL | | -LL | | } - | |_- - | - = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 3 lifetimes it is borrowed from - -error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:28:44 + --> $DIR/missing-lifetime-specifier.rs:32:44 | LL | static b: RefCell>>> = RefCell::new(HashMap::new()); | ^^^^ expected 2 lifetime parameters @@ -38,22 +25,7 @@ LL | static b: RefCell>> | +++++++ ++++++++++++++++++ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:28:44 - | -LL | / thread_local! { -LL | | static b: RefCell>>> = RefCell::new(HashMap::new()); - | | ^^^^ expected 2 lifetime parameters - | | | - | | expected named lifetime parameter -LL | | -LL | | -LL | | } - | |_- - | - = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 4 lifetimes it is borrowed from - -error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:33:47 + --> $DIR/missing-lifetime-specifier.rs:36:47 | LL | static c: RefCell>>>> = RefCell::new(HashMap::new()); | ^ expected 2 lifetime parameters @@ -65,20 +37,7 @@ LL | static c: RefCell>>>> = | +++++++++++++++++ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:33:47 - | -LL | / thread_local! { -LL | | static c: RefCell>>>> = RefCell::new(HashMap::new()); - | | ^ expected 2 lifetime parameters -LL | | -LL | | -LL | | } - | |_- - | - = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 3 lifetimes it is borrowed from - -error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:38:44 + --> $DIR/missing-lifetime-specifier.rs:40:44 | LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); | ^ ^ expected 2 lifetime parameters @@ -91,23 +50,8 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); | +++++++ +++++++++++++++++ -error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:38:44 - | -LL | / thread_local! { -LL | | static d: RefCell>>>> = RefCell::new(HashMap::new()); - | | ^ ^ expected 2 lifetime parameters - | | | - | | expected named lifetime parameter -LL | | -LL | | -LL | | } - | |_- - | - = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 4 lifetimes it is borrowed from - error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-specifier.rs:48:44 + --> $DIR/missing-lifetime-specifier.rs:49:44 | LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | ^ expected named lifetime parameter @@ -118,22 +62,8 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | +++++++ -error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-specifier.rs:48:44 - | -LL | / thread_local! { -LL | | static f: RefCell>>>> = RefCell::new(HashMap::new()); - | | ^ expected named lifetime parameter -LL | | -LL | | -LL | | -LL | | } - | |_- - | - = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 3 lifetimes it is borrowed from - error[E0107]: union takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:44:44 + --> $DIR/missing-lifetime-specifier.rs:45:44 | LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); | ^^^ ------- supplied 1 lifetime argument @@ -141,7 +71,7 @@ LL | static e: RefCell>>>> = RefCell: | expected 2 lifetime arguments | note: union defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:16:11 + --> $DIR/missing-lifetime-specifier.rs:21:11 | LL | pub union Qux<'t, 'k, I> { | ^^^ -- -- @@ -151,7 +81,7 @@ LL | static e: RefCell>>>> = | +++++++++ error[E0107]: trait takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:48:45 + --> $DIR/missing-lifetime-specifier.rs:49:45 | LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | ^^^ ------- supplied 1 lifetime argument @@ -159,7 +89,7 @@ LL | static f: RefCell>>>> = RefCell | expected 2 lifetime arguments | note: trait defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:20:7 + --> $DIR/missing-lifetime-specifier.rs:25:7 | LL | trait Tar<'t, 'k, I> {} | ^^^ -- -- @@ -168,7 +98,7 @@ help: add missing lifetime argument LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | +++++++++ -error: aborting due to 12 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0106, E0107. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/suggestions/mut-borrow-needed-by-trait.rs b/tests/ui/suggestions/mut-borrow-needed-by-trait.rs index 66e1e77c905e..924bfd82eb83 100644 --- a/tests/ui/suggestions/mut-borrow-needed-by-trait.rs +++ b/tests/ui/suggestions/mut-borrow-needed-by-trait.rs @@ -17,6 +17,7 @@ fn main() { let fp = BufWriter::new(fp); //~^ ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied //~| ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied + //~| ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied writeln!(fp, "hello world").unwrap(); //~ ERROR the method } diff --git a/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr b/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr index 09a9b1d3b348..b2f9150142fe 100644 --- a/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr +++ b/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr @@ -10,6 +10,16 @@ LL | let fp = BufWriter::new(fp); note: required by a bound in `BufWriter::::new` --> $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL +error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied + --> $DIR/mut-borrow-needed-by-trait.rs:17:14 + | +LL | let fp = BufWriter::new(fp); + | ^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` + | + = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write` +note: required by a bound in `BufWriter` + --> $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL + error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied --> $DIR/mut-borrow-needed-by-trait.rs:17:14 | @@ -21,13 +31,13 @@ note: required by a bound in `BufWriter` --> $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL error[E0599]: the method `write_fmt` exists for struct `BufWriter<&dyn Write>`, but its trait bounds were not satisfied - --> $DIR/mut-borrow-needed-by-trait.rs:21:14 + --> $DIR/mut-borrow-needed-by-trait.rs:22:14 | LL | writeln!(fp, "hello world").unwrap(); | ---------^^---------------- method cannot be called on `BufWriter<&dyn Write>` due to unsatisfied trait bounds | note: must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method - --> $DIR/mut-borrow-needed-by-trait.rs:21:14 + --> $DIR/mut-borrow-needed-by-trait.rs:22:14 | LL | writeln!(fp, "hello world").unwrap(); | ^^ @@ -35,7 +45,7 @@ LL | writeln!(fp, "hello world").unwrap(); `&dyn std::io::Write: std::io::Write` which is required by `BufWriter<&dyn std::io::Write>: std::io::Write` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0277, E0599. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/suggestions/option-content-move.fixed b/tests/ui/suggestions/option-content-move.fixed index 4a5a9483c20c..ef07d55871e7 100644 --- a/tests/ui/suggestions/option-content-move.fixed +++ b/tests/ui/suggestions/option-content-move.fixed @@ -1,4 +1,5 @@ //@ run-rustfix +#[allow(dead_code)] pub struct LipogramCorpora { selections: Vec<(char, Option)>, } @@ -17,6 +18,7 @@ impl LipogramCorpora { } } +#[allow(dead_code)] pub struct LipogramCorpora2 { selections: Vec<(char, Result)>, } diff --git a/tests/ui/suggestions/option-content-move.rs b/tests/ui/suggestions/option-content-move.rs index 90d05c743997..5be6358fd6a5 100644 --- a/tests/ui/suggestions/option-content-move.rs +++ b/tests/ui/suggestions/option-content-move.rs @@ -1,4 +1,5 @@ //@ run-rustfix +#[allow(dead_code)] pub struct LipogramCorpora { selections: Vec<(char, Option)>, } @@ -17,6 +18,7 @@ impl LipogramCorpora { } } +#[allow(dead_code)] pub struct LipogramCorpora2 { selections: Vec<(char, Result)>, } diff --git a/tests/ui/suggestions/option-content-move.stderr b/tests/ui/suggestions/option-content-move.stderr index a382a04344ae..b4ec5b180d21 100644 --- a/tests/ui/suggestions/option-content-move.stderr +++ b/tests/ui/suggestions/option-content-move.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of `selection.1` which is behind a shared reference - --> $DIR/option-content-move.rs:10:20 + --> $DIR/option-content-move.rs:11:20 | LL | if selection.1.unwrap().contains(selection.0) { | ^^^^^^^^^^^ -------- `selection.1` moved due to this method call @@ -19,7 +19,7 @@ LL | if selection.1.clone().unwrap().contains(selection.0) { | ++++++++ error[E0507]: cannot move out of `selection.1` which is behind a shared reference - --> $DIR/option-content-move.rs:28:20 + --> $DIR/option-content-move.rs:30:20 | LL | if selection.1.unwrap().contains(selection.0) { | ^^^^^^^^^^^ -------- `selection.1` moved due to this method call diff --git a/tests/ui/suggestions/silenced-binding-typo.stderr b/tests/ui/suggestions/silenced-binding-typo.stderr index c362d00c713a..a1e8b9e30d4b 100644 --- a/tests/ui/suggestions/silenced-binding-typo.stderr +++ b/tests/ui/suggestions/silenced-binding-typo.stderr @@ -1,10 +1,12 @@ error[E0425]: cannot find value `x` in this scope --> $DIR/silenced-binding-typo.rs:4:14 | +LL | let _x = 42; + | -- `_x` defined here LL | let _y = x; | ^ | -help: a local variable with a similar name exists, consider renaming `_x` into `x` +help: the leading underscore in `_x` marks it as unused, consider renaming it to `x` | LL | let x = 42; | ~ diff --git a/tests/ui/suggest-null-ptr.fixed b/tests/ui/suggestions/suggest-null-ptr.fixed similarity index 100% rename from tests/ui/suggest-null-ptr.fixed rename to tests/ui/suggestions/suggest-null-ptr.fixed diff --git a/tests/ui/suggest-null-ptr.rs b/tests/ui/suggestions/suggest-null-ptr.rs similarity index 100% rename from tests/ui/suggest-null-ptr.rs rename to tests/ui/suggestions/suggest-null-ptr.rs diff --git a/tests/ui/suggest-null-ptr.stderr b/tests/ui/suggestions/suggest-null-ptr.stderr similarity index 100% rename from tests/ui/suggest-null-ptr.stderr rename to tests/ui/suggestions/suggest-null-ptr.stderr diff --git a/tests/ui/suggestions/suggest-remove-refs-5.stderr b/tests/ui/suggestions/suggest-remove-refs-5.stderr index 3b6994b45d10..b132c56473ee 100644 --- a/tests/ui/suggestions/suggest-remove-refs-5.stderr +++ b/tests/ui/suggestions/suggest-remove-refs-5.stderr @@ -4,7 +4,7 @@ error[E0277]: `&mut &mut &mut &mut Vec` is not an iterator LL | for _ in &mut &mut v {} | ^^^^^^^^^^^ `&mut &mut &mut &mut Vec` is not an iterator | - = help: the trait `Iterator` is not implemented for `&mut &mut &mut &mut Vec`, which is required by `&mut &mut &mut &mut Vec: IntoIterator` + = help: the trait `Iterator` is not implemented for `Vec`, which is required by `&mut &mut &mut &mut Vec: IntoIterator` = note: required for `&mut Vec` to implement `Iterator` = note: 3 redundant requirements hidden = note: required for `&mut &mut &mut &mut Vec` to implement `Iterator` @@ -21,7 +21,7 @@ error[E0277]: `&mut &mut &mut [u8; 1]` is not an iterator LL | for _ in &mut v {} | ^^^^^^ `&mut &mut &mut [u8; 1]` is not an iterator | - = help: the trait `Iterator` is not implemented for `&mut &mut &mut [u8; 1]`, which is required by `&mut &mut &mut [u8; 1]: IntoIterator` + = help: the trait `Iterator` is not implemented for `[u8; 1]`, which is required by `&mut &mut &mut [u8; 1]: IntoIterator` = note: required for `&mut [u8; 1]` to implement `Iterator` = note: 2 redundant requirements hidden = note: required for `&mut &mut &mut [u8; 1]` to implement `Iterator` diff --git a/tests/ui/suggestions/suggest-tryinto-edition-change.rs b/tests/ui/suggestions/suggest-tryinto-edition-change.rs index 373217748745..f45670ae7c1e 100644 --- a/tests/ui/suggestions/suggest-tryinto-edition-change.rs +++ b/tests/ui/suggestions/suggest-tryinto-edition-change.rs @@ -1,5 +1,5 @@ // Make sure that trying to access `TryInto`, `TryFrom`, `FromIterator` in pre-2021 mentions -// Edition 2021 change +// Edition 2021 change. //@ edition:2018 fn test() { @@ -11,19 +11,16 @@ fn test() { //~^ ERROR failed to resolve: use of undeclared type //~| NOTE use of undeclared type //~| NOTE 'std::convert::TryFrom' is included in the prelude starting in Edition 2021 - //~| NOTE 'core::convert::TryFrom' is included in the prelude starting in Edition 2021 let _i: i16 = TryInto::try_into(0_i32).unwrap(); //~^ ERROR failed to resolve: use of undeclared type //~| NOTE use of undeclared type //~| NOTE 'std::convert::TryInto' is included in the prelude starting in Edition 2021 - //~| NOTE 'core::convert::TryInto' is included in the prelude starting in Edition 2021 let _v: Vec<_> = FromIterator::from_iter(&[1]); //~^ ERROR failed to resolve: use of undeclared type //~| NOTE use of undeclared type //~| NOTE 'std::iter::FromIterator' is included in the prelude starting in Edition 2021 - //~| NOTE 'core::iter::FromIterator' is included in the prelude starting in Edition 2021 } fn main() { diff --git a/tests/ui/suggestions/suggest-tryinto-edition-change.stderr b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr index db7c40101cdc..5be55f75cd15 100644 --- a/tests/ui/suggestions/suggest-tryinto-edition-change.stderr +++ b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr @@ -4,45 +4,36 @@ error[E0433]: failed to resolve: use of undeclared type `TryFrom` LL | let _i: i16 = TryFrom::try_from(0_i32).unwrap(); | ^^^^^^^ use of undeclared type `TryFrom` | - = note: 'core::convert::TryFrom' is included in the prelude starting in Edition 2021 = note: 'std::convert::TryFrom' is included in the prelude starting in Edition 2021 -help: consider importing one of these items - | -LL + use core::convert::TryFrom; +help: consider importing this trait | LL + use std::convert::TryFrom; | error[E0433]: failed to resolve: use of undeclared type `TryInto` - --> $DIR/suggest-tryinto-edition-change.rs:16:19 + --> $DIR/suggest-tryinto-edition-change.rs:15:19 | LL | let _i: i16 = TryInto::try_into(0_i32).unwrap(); | ^^^^^^^ use of undeclared type `TryInto` | - = note: 'core::convert::TryInto' is included in the prelude starting in Edition 2021 = note: 'std::convert::TryInto' is included in the prelude starting in Edition 2021 -help: consider importing one of these items - | -LL + use core::convert::TryInto; +help: consider importing this trait | LL + use std::convert::TryInto; | error[E0433]: failed to resolve: use of undeclared type `FromIterator` - --> $DIR/suggest-tryinto-edition-change.rs:22:22 + --> $DIR/suggest-tryinto-edition-change.rs:20:22 | LL | let _v: Vec<_> = FromIterator::from_iter(&[1]); | ^^^^^^^^^^^^ use of undeclared type `FromIterator` | - = note: 'core::iter::FromIterator' is included in the prelude starting in Edition 2021 = note: 'std::iter::FromIterator' is included in the prelude starting in Edition 2021 help: a trait with a similar name exists | LL | let _v: Vec<_> = IntoIterator::from_iter(&[1]); | ~~~~~~~~~~~~ -help: consider importing one of these items - | -LL + use core::iter::FromIterator; +help: consider importing this trait | LL + use std::iter::FromIterator; | diff --git a/tests/ui/trait-impl-bound-suggestions.fixed b/tests/ui/suggestions/trait-impl-bound-suggestions.fixed similarity index 100% rename from tests/ui/trait-impl-bound-suggestions.fixed rename to tests/ui/suggestions/trait-impl-bound-suggestions.fixed diff --git a/tests/ui/trait-impl-bound-suggestions.rs b/tests/ui/suggestions/trait-impl-bound-suggestions.rs similarity index 100% rename from tests/ui/trait-impl-bound-suggestions.rs rename to tests/ui/suggestions/trait-impl-bound-suggestions.rs diff --git a/tests/ui/trait-impl-bound-suggestions.stderr b/tests/ui/suggestions/trait-impl-bound-suggestions.stderr similarity index 100% rename from tests/ui/trait-impl-bound-suggestions.stderr rename to tests/ui/suggestions/trait-impl-bound-suggestions.stderr diff --git a/tests/ui/suggestions/try-operator-dont-suggest-semicolon.rs b/tests/ui/suggestions/try-operator-dont-suggest-semicolon.rs index f882a159f983..35a06d396f2b 100644 --- a/tests/ui/suggestions/try-operator-dont-suggest-semicolon.rs +++ b/tests/ui/suggestions/try-operator-dont-suggest-semicolon.rs @@ -3,6 +3,7 @@ fn main() -> Result<(), ()> { a(|| { + //~^ HELP: try adding a return type b() //~^ ERROR: mismatched types [E0308] //~| NOTE: expected `()`, found `i32` diff --git a/tests/ui/suggestions/try-operator-dont-suggest-semicolon.stderr b/tests/ui/suggestions/try-operator-dont-suggest-semicolon.stderr index 939285498fb6..5506456afe9c 100644 --- a/tests/ui/suggestions/try-operator-dont-suggest-semicolon.stderr +++ b/tests/ui/suggestions/try-operator-dont-suggest-semicolon.stderr @@ -1,13 +1,20 @@ error[E0308]: mismatched types - --> $DIR/try-operator-dont-suggest-semicolon.rs:6:9 + --> $DIR/try-operator-dont-suggest-semicolon.rs:7:9 | LL | b() - | ^^^- help: consider using a semicolon here: `;` - | | - | expected `()`, found `i32` + | ^^^ expected `()`, found `i32` + | +help: consider using a semicolon here + | +LL | b(); + | + +help: try adding a return type + | +LL | a(|| -> i32 { + | ++++++ error[E0308]: mismatched types - --> $DIR/try-operator-dont-suggest-semicolon.rs:16:9 + --> $DIR/try-operator-dont-suggest-semicolon.rs:17:9 | LL | / if true { LL | | diff --git a/tests/ui/suggestions/type-ascription-instead-of-path-in-type.rs b/tests/ui/suggestions/type-ascription-instead-of-path-in-type.rs index c98eec4af01c..99f943d71e9e 100644 --- a/tests/ui/suggestions/type-ascription-instead-of-path-in-type.rs +++ b/tests/ui/suggestions/type-ascription-instead-of-path-in-type.rs @@ -8,5 +8,5 @@ fn main() { //~| HELP you might have meant to write a path instead of an associated type bound //~| ERROR struct takes at least 1 generic argument but 0 generic arguments were supplied //~| HELP add missing generic argument - //~| ERROR associated type bindings are not allowed here + //~| ERROR associated item constraints are not allowed here } diff --git a/tests/ui/suggestions/type-ascription-instead-of-path-in-type.stderr b/tests/ui/suggestions/type-ascription-instead-of-path-in-type.stderr index 834c141ec3e1..56b6a69a283f 100644 --- a/tests/ui/suggestions/type-ascription-instead-of-path-in-type.stderr +++ b/tests/ui/suggestions/type-ascription-instead-of-path-in-type.stderr @@ -20,11 +20,11 @@ help: add missing generic argument LL | let _: Vec = A::B; | ++ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/type-ascription-instead-of-path-in-type.rs:6:16 | LL | let _: Vec = A::B; - | ^^^ associated type not allowed here + | ^^^ associated item constraint not allowed here error: aborting due to 3 previous errors diff --git a/tests/ui/suggestions/unused-imports.stderr b/tests/ui/suggestions/unused-imports.stderr index bf112608da7e..31edb4e5ec20 100644 --- a/tests/ui/suggestions/unused-imports.stderr +++ b/tests/ui/suggestions/unused-imports.stderr @@ -1,4 +1,4 @@ -warning: unused imports: `A`, `C` +warning: unused imports: `A` and `C` --> $DIR/unused-imports.rs:22:14 | LL | use nested::{A, B, C}; @@ -10,7 +10,7 @@ note: the lint level is defined here LL | #![warn(unused_imports)] | ^^^^^^^^^^^^^^ -warning: unused imports: `D`, `E`, `G` +warning: unused imports: `D`, `E`, and `G` --> $DIR/unused-imports.rs:26:5 | LL | D, diff --git a/tests/ui/sync/reentrantlockguard-sync.rs b/tests/ui/sync/reentrantlockguard-sync.rs new file mode 100644 index 000000000000..84d5b1834a88 --- /dev/null +++ b/tests/ui/sync/reentrantlockguard-sync.rs @@ -0,0 +1,15 @@ +#![feature(reentrant_lock)] +use std::sync::ReentrantLock; +use std::cell::Cell; + +// ReentrantLockGuard> must not be Sync, that would be unsound. + +fn test_sync(_t: T) {} + +fn main() +{ + let m = ReentrantLock::new(Cell::new(0i32)); + let guard = m.lock(); + test_sync(guard); + //~^ ERROR `Cell` cannot be shared between threads safely [E0277] +} diff --git a/tests/ui/sync/reentrantlockguard-sync.stderr b/tests/ui/sync/reentrantlockguard-sync.stderr new file mode 100644 index 000000000000..ed2e3e2f1124 --- /dev/null +++ b/tests/ui/sync/reentrantlockguard-sync.stderr @@ -0,0 +1,20 @@ +error[E0277]: `Cell` cannot be shared between threads safely + --> $DIR/reentrantlockguard-sync.rs:13:15 + | +LL | test_sync(guard); + | --------- ^^^^^ `Cell` cannot be shared between threads safely + | | + | required by a bound introduced by this call + | + = help: the trait `Sync` is not implemented for `Cell`, which is required by `ReentrantLockGuard<'_, Cell>: Sync` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead + = note: required for `ReentrantLockGuard<'_, Cell>` to implement `Sync` +note: required by a bound in `test_sync` + --> $DIR/reentrantlockguard-sync.rs:7:17 + | +LL | fn test_sync(_t: T) {} + | ^^^^ required by this bound in `test_sync` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/threads-sendsync/issue-43733-2.rs b/tests/ui/threads-sendsync/issue-43733-2.rs deleted file mode 100644 index 372ebf2cff97..000000000000 --- a/tests/ui/threads-sendsync/issue-43733-2.rs +++ /dev/null @@ -1,30 +0,0 @@ -//@ needs-threads -//@ dont-check-compiler-stderr -#![feature(cfg_target_thread_local, thread_local_internals)] - -// On platforms *without* `#[thread_local]`, use -// a custom non-`Sync` type to fake the same error. -#[cfg(not(target_thread_local))] -struct Key { - _data: std::cell::UnsafeCell>, - _flag: std::cell::Cell<()>, -} - -#[cfg(not(target_thread_local))] -impl Key { - const fn new() -> Self { - Key { - _data: std::cell::UnsafeCell::new(None), - _flag: std::cell::Cell::new(()), - } - } -} - -#[cfg(target_thread_local)] -use std::thread::local_impl::Key; - -static __KEY: Key<()> = Key::new(); -//~^ ERROR `UnsafeCell>` cannot be shared between threads -//~| ERROR cannot be shared between threads safely [E0277] - -fn main() {} diff --git a/tests/ui/threads-sendsync/issue-43733.rs b/tests/ui/threads-sendsync/issue-43733.rs deleted file mode 100644 index c90f60887cfd..000000000000 --- a/tests/ui/threads-sendsync/issue-43733.rs +++ /dev/null @@ -1,32 +0,0 @@ -//@ needs-threads -#![feature(thread_local)] -#![feature(cfg_target_thread_local, thread_local_internals)] - -use std::cell::RefCell; - -type Foo = std::cell::RefCell; - -#[cfg(target_thread_local)] -#[thread_local] -static __KEY: std::thread::local_impl::Key = std::thread::local_impl::Key::new(); - -#[cfg(not(target_thread_local))] -static __KEY: std::thread::local_impl::Key = std::thread::local_impl::Key::new(); - -fn __getit(_: Option<&mut Option>>) -> std::option::Option<&'static Foo> { - __KEY.get(Default::default) - //~^ ERROR call to unsafe function `Key::::get` is unsafe -} - -static FOO: std::thread::LocalKey = std::thread::LocalKey::new(__getit); -//~^ ERROR call to unsafe function `LocalKey::::new` is unsafe - -fn main() { - FOO.with(|foo| println!("{}", foo.borrow())); - std::thread::spawn(|| { - FOO.with(|foo| *foo.borrow_mut() += "foo"); - }) - .join() - .unwrap(); - FOO.with(|foo| println!("{}", foo.borrow())); -} diff --git a/tests/ui/threads-sendsync/issue-43733.stderr b/tests/ui/threads-sendsync/issue-43733.stderr deleted file mode 100644 index 9b13646a2285..000000000000 --- a/tests/ui/threads-sendsync/issue-43733.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0133]: call to unsafe function `Key::::get` is unsafe and requires unsafe function or block - --> $DIR/issue-43733.rs:17:5 - | -LL | __KEY.get(Default::default) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function - | - = note: consult the function's documentation for information on how to avoid undefined behavior - -error[E0133]: call to unsafe function `LocalKey::::new` is unsafe and requires unsafe function or block - --> $DIR/issue-43733.rs:21:42 - | -LL | static FOO: std::thread::LocalKey = std::thread::LocalKey::new(__getit); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function - | - = note: consult the function's documentation for information on how to avoid undefined behavior - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/tool_lints-fail.rs b/tests/ui/tool-attributes/tool_lints-fail.rs similarity index 100% rename from tests/ui/tool_lints-fail.rs rename to tests/ui/tool-attributes/tool_lints-fail.rs diff --git a/tests/ui/tool_lints-fail.stderr b/tests/ui/tool-attributes/tool_lints-fail.stderr similarity index 100% rename from tests/ui/tool_lints-fail.stderr rename to tests/ui/tool-attributes/tool_lints-fail.stderr diff --git a/tests/ui/tool_lints-rpass.rs b/tests/ui/tool-attributes/tool_lints-rpass.rs similarity index 100% rename from tests/ui/tool_lints-rpass.rs rename to tests/ui/tool-attributes/tool_lints-rpass.rs diff --git a/tests/ui/tool_lints.rs b/tests/ui/tool-attributes/tool_lints.rs similarity index 100% rename from tests/ui/tool_lints.rs rename to tests/ui/tool-attributes/tool_lints.rs diff --git a/tests/ui/tool_lints.stderr b/tests/ui/tool-attributes/tool_lints.stderr similarity index 100% rename from tests/ui/tool_lints.stderr rename to tests/ui/tool-attributes/tool_lints.stderr diff --git a/tests/ui/tool_lints_2018_preview.rs b/tests/ui/tool-attributes/tool_lints_2018_preview.rs similarity index 100% rename from tests/ui/tool_lints_2018_preview.rs rename to tests/ui/tool-attributes/tool_lints_2018_preview.rs diff --git a/tests/ui/unknown-lint-tool-name.rs b/tests/ui/tool-attributes/unknown-lint-tool-name.rs similarity index 100% rename from tests/ui/unknown-lint-tool-name.rs rename to tests/ui/tool-attributes/unknown-lint-tool-name.rs diff --git a/tests/ui/unknown-lint-tool-name.stderr b/tests/ui/tool-attributes/unknown-lint-tool-name.stderr similarity index 100% rename from tests/ui/unknown-lint-tool-name.stderr rename to tests/ui/tool-attributes/unknown-lint-tool-name.stderr diff --git a/tests/ui/unknown-tool-name.rs b/tests/ui/tool-attributes/unknown-tool-name.rs similarity index 100% rename from tests/ui/unknown-tool-name.rs rename to tests/ui/tool-attributes/unknown-tool-name.rs diff --git a/tests/ui/unknown-tool-name.stderr b/tests/ui/tool-attributes/unknown-tool-name.stderr similarity index 100% rename from tests/ui/unknown-tool-name.stderr rename to tests/ui/tool-attributes/unknown-tool-name.stderr diff --git a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs index cab484a120c3..445ea2de610f 100644 --- a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs +++ b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs @@ -14,14 +14,12 @@ impl Trait for i32 { // Should not not trigger suggestion here... impl Trait for () {} //~^ ERROR trait takes 1 generic argument but 2 generic arguments were supplied -//~| ERROR `S` is not constrained //... but should do so in all of the below cases except the last one fn func>(t: T) -> impl Trait<(), i32> { //~^ ERROR trait takes 1 generic argument but 2 generic arguments were supplied //~| ERROR trait takes 1 generic argument but 2 generic arguments were supplied //~| ERROR trait takes 1 generic argument but 2 generic arguments were supplied -//~| ERROR type annotations needed 3 } diff --git a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr index 99e81a9039e7..06e2fa5d4d1f 100644 --- a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr +++ b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr @@ -16,14 +16,8 @@ note: trait defined here, with 1 generic parameter: `T` LL | pub trait Trait { | ^^^^^ - -error[E0207]: the type parameter `S` is not constrained by the impl trait, self type, or predicates - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:15:9 - | -LL | impl Trait for () {} - | ^ unconstrained type parameter - error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:20:12 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:19:12 | LL | fn func>(t: T) -> impl Trait<(), i32> { | ^^^^^ expected 1 generic argument @@ -39,7 +33,7 @@ LL | fn func>(t: T) -> impl Trait<(), i32> { | +++++++ error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:20:46 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:19:46 | LL | fn func>(t: T) -> impl Trait<(), i32> { | ^^^^^ expected 1 generic argument @@ -55,7 +49,7 @@ LL | fn func>(t: T) -> impl Trait<(), Assoc = i32> { | +++++++ error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:20:46 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:19:46 | LL | fn func>(t: T) -> impl Trait<(), i32> { | ^^^^^ expected 1 generic argument @@ -71,14 +65,8 @@ help: replace the generic bound with the associated type LL | fn func>(t: T) -> impl Trait<(), Assoc = i32> { | +++++++ -error[E0282]: type annotations needed - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:20:41 - | -LL | fn func>(t: T) -> impl Trait<(), i32> { - | ^^^^^^^^^^^^^^^^^^^ cannot infer type - error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:28:18 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:26:18 | LL | struct Struct> { | ^^^^^ expected 1 generic argument @@ -94,7 +82,7 @@ LL | struct Struct> { | +++++++ error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:33:23 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:31:23 | LL | trait AnotherTrait> {} | ^^^^^ expected 1 generic argument @@ -110,7 +98,7 @@ LL | trait AnotherTrait> {} | +++++++ error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:36:9 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:34:9 | LL | impl> Struct {} | ^^^^^ expected 1 generic argument @@ -126,7 +114,7 @@ LL | impl> Struct {} | +++++++ error[E0107]: struct takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:42:58 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:40:58 | LL | impl, U> YetAnotherTrait for Struct {} | ^^^^^^ - help: remove this generic argument @@ -134,12 +122,12 @@ LL | impl, U> YetAnotherTrait for Struct {} | expected 1 generic argument | note: struct defined here, with 1 generic parameter: `T` - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:28:8 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:26:8 | LL | struct Struct> { | ^^^^^^ - -error: aborting due to 11 previous errors +error: aborting due to 9 previous errors -Some errors have detailed explanations: E0107, E0207, E0282. +Some errors have detailed explanations: E0107, E0207. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr index b7a7784755ec..ebd45b9dd963 100644 --- a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr +++ b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr @@ -4,7 +4,7 @@ error[E0277]: expected a `FnOnce(&i32)` closure, found `i32` LL | f:: X<'x, F = i32>>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnOnce(&i32)` closure, found `i32` | - = help: the trait `for<'a> FnOnce<(&'a i32,)>` is not implemented for `i32` + = help: the trait `for<'a> FnOnce(&'a i32)` is not implemented for `i32` note: required by a bound in `f` --> $DIR/check-trait-object-bounds-2.rs:8:9 | diff --git a/tests/ui/traits/bound/assoc-fn-bound-root-obligation.rs b/tests/ui/traits/bound/assoc-fn-bound-root-obligation.rs index f8e3f8e968e1..98825bd536e0 100644 --- a/tests/ui/traits/bound/assoc-fn-bound-root-obligation.rs +++ b/tests/ui/traits/bound/assoc-fn-bound-root-obligation.rs @@ -2,7 +2,7 @@ fn strip_lf(s: &str) -> &str { s.strip_suffix(b'\n').unwrap_or(s) //~^ ERROR expected a `FnMut(char)` closure, found `u8` //~| NOTE expected an `FnMut(char)` closure, found `u8` - //~| HELP the trait `FnMut<(char,)>` is not implemented for `u8` + //~| HELP the trait `FnMut(char)` is not implemented for `u8` //~| HELP the following other types implement trait `Pattern<'a>`: //~| NOTE required for `u8` to implement `Pattern<'_>` diff --git a/tests/ui/traits/bound/assoc-fn-bound-root-obligation.stderr b/tests/ui/traits/bound/assoc-fn-bound-root-obligation.stderr index 27006f59b902..49272e7d357b 100644 --- a/tests/ui/traits/bound/assoc-fn-bound-root-obligation.stderr +++ b/tests/ui/traits/bound/assoc-fn-bound-root-obligation.stderr @@ -4,7 +4,7 @@ error[E0277]: expected a `FnMut(char)` closure, found `u8` LL | s.strip_suffix(b'\n').unwrap_or(s) | ^^^^^^^^^^^^ expected an `FnMut(char)` closure, found `u8` | - = help: the trait `FnMut<(char,)>` is not implemented for `u8`, which is required by `u8: Pattern<'_>` + = help: the trait `FnMut(char)` is not implemented for `u8`, which is required by `u8: Pattern<'_>` = help: the following other types implement trait `Pattern<'a>`: &'b String &'b [char; N] diff --git a/tests/ui/traits/generic_param_mismatch_in_unsatisfied_projection.rs b/tests/ui/traits/generic_param_mismatch_in_unsatisfied_projection.rs new file mode 100644 index 000000000000..bf1278f992b6 --- /dev/null +++ b/tests/ui/traits/generic_param_mismatch_in_unsatisfied_projection.rs @@ -0,0 +1,27 @@ +//! This test used to ICE: #121134 +//! The issue is that we're trying to prove a projection, but there's +//! no bound for the projection's trait, and the projection has the wrong +//! kind of generic parameter (lifetime vs type). +//! When actually calling the function with those broken bounds, trying to +//! instantiate the bounds with inference vars would ICE. +#![feature(unboxed_closures)] + +trait Output<'a> { + type Type; +} + +struct Wrapper; + +impl Wrapper { + fn do_something_wrapper(&mut self, _: F) + where + F: for<'a> FnOnce(>::Type), + //~^ ERROR: trait takes 0 generic arguments but 1 generic argument was supplied + { + } +} + +fn main() { + let mut wrapper = Wrapper; + wrapper.do_something_wrapper(|value| ()); +} diff --git a/tests/ui/traits/generic_param_mismatch_in_unsatisfied_projection.stderr b/tests/ui/traits/generic_param_mismatch_in_unsatisfied_projection.stderr new file mode 100644 index 000000000000..acda3418894d --- /dev/null +++ b/tests/ui/traits/generic_param_mismatch_in_unsatisfied_projection.stderr @@ -0,0 +1,19 @@ +error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/generic_param_mismatch_in_unsatisfied_projection.rs:18:33 + | +LL | F: for<'a> FnOnce(>::Type), + | ^^^^^^ expected 0 generic arguments + | +note: trait defined here, with 0 generic parameters + --> $DIR/generic_param_mismatch_in_unsatisfied_projection.rs:9:7 + | +LL | trait Output<'a> { + | ^^^^^^ +help: replace the generic bound with the associated type + | +LL | F: for<'a> FnOnce(>::Type), + | ++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/traits/inheritance/repeated-supertrait-ambig.stderr b/tests/ui/traits/inheritance/repeated-supertrait-ambig.stderr index 7027fa69e033..6f7c9fa11d4b 100644 --- a/tests/ui/traits/inheritance/repeated-supertrait-ambig.stderr +++ b/tests/ui/traits/inheritance/repeated-supertrait-ambig.stderr @@ -7,8 +7,8 @@ LL | c.same_as(22) | required by a bound introduced by this call | = help: the following other types implement trait `CompareTo`: - > - > + `i64` implements `CompareTo` + `i64` implements `CompareTo` error[E0277]: the trait bound `C: CompareTo` is not satisfied --> $DIR/repeated-supertrait-ambig.rs:30:15 @@ -30,8 +30,8 @@ LL | ::same_as(c, 22) | ^^^^^^^^^^^^^^^^^ the trait `CompareTo` is not implemented for `dyn CompareToInts` | = help: the following other types implement trait `CompareTo`: - > - > + `i64` implements `CompareTo` + `i64` implements `CompareTo` error[E0277]: the trait bound `C: CompareTo` is not satisfied --> $DIR/repeated-supertrait-ambig.rs:38:27 @@ -55,8 +55,8 @@ LL | assert_eq!(22_i64.same_as(22), true); | required by a bound introduced by this call | = help: the following other types implement trait `CompareTo`: - > - > + `i64` implements `CompareTo` + `i64` implements `CompareTo` error: aborting due to 5 previous errors diff --git a/tests/ui/traits/issue-78372.stderr b/tests/ui/traits/issue-78372.stderr index 58a4c229e5e6..cdcb0cdf2593 100644 --- a/tests/ui/traits/issue-78372.stderr +++ b/tests/ui/traits/issue-78372.stderr @@ -55,12 +55,6 @@ LL | impl DispatchFromDyn> for T {} = help: add `#![feature(dispatch_from_dyn)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0378]: the trait `DispatchFromDyn` may only be implemented for a coercion between structures - --> $DIR/issue-78372.rs:3:1 - | -LL | impl DispatchFromDyn> for T {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error[E0038]: the trait `Foo` cannot be made into an object --> $DIR/issue-78372.rs:12:17 | @@ -88,6 +82,12 @@ LL | fn foo(self: Smaht); = note: type of `self` must be `Self` or a type that dereferences to it = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) + +error[E0307]: invalid `self` parameter type: `&Bar` + --> $DIR/method_resolution3.rs:21:18 + | +LL | fn baz(self: &Bar) { + | ^^^^^^^^^ + | + = note: type of `self` must be `Self` or a type that dereferences to it + = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0307`. diff --git a/tests/ui/type-alias-impl-trait/method_resolution3.next.stderr b/tests/ui/type-alias-impl-trait/method_resolution3.next.stderr new file mode 100644 index 000000000000..9272017cdf5d --- /dev/null +++ b/tests/ui/type-alias-impl-trait/method_resolution3.next.stderr @@ -0,0 +1,15 @@ +error[E0271]: type mismatch resolving `Foo == u32` + --> $DIR/method_resolution3.rs:16:18 + | +LL | fn bar(self: Bar) { + | ^^^^^^^^ types differ + +error[E0271]: type mismatch resolving `Foo == u32` + --> $DIR/method_resolution3.rs:21:18 + | +LL | fn baz(self: &Bar) { + | ^^^^^^^^^ types differ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/type-alias-impl-trait/method_resolution3.rs b/tests/ui/type-alias-impl-trait/method_resolution3.rs new file mode 100644 index 000000000000..447f3144b822 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/method_resolution3.rs @@ -0,0 +1,36 @@ +//! Check that one cannot use arbitrary self types where a generic parameter +//! mismatches with an opaque type. In theory this could unify with the opaque +//! type, registering the generic parameter as the hidden type of the opaque type. + +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +#![feature(type_alias_impl_trait, arbitrary_self_types)] + +type Foo = impl Copy; + +#[derive(Copy, Clone)] +struct Bar(T); + +impl Bar { + fn bar(self: Bar) { + //[current]~^ ERROR: invalid `self` parameter + //[next]~^^ ERROR: type mismatch resolving `Foo == u32` + self.foo() + } + fn baz(self: &Bar) { + //[current]~^ ERROR: invalid `self` parameter + //[next]~^^ ERROR: type mismatch resolving `Foo == u32` + self.foo() + } +} + +impl Bar { + fn foo(self) {} +} + +fn foo() -> Foo { + 42_u32 +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/method_resolution4.current.stderr b/tests/ui/type-alias-impl-trait/method_resolution4.current.stderr new file mode 100644 index 000000000000..3a2ca18f8909 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/method_resolution4.current.stderr @@ -0,0 +1,21 @@ +error[E0307]: invalid `self` parameter type: `Bar` + --> $DIR/method_resolution4.rs:27:18 + | +LL | fn foo(self: Bar) { + | ^^^^^^^^ + | + = note: type of `self` must be `Self` or a type that dereferences to it + = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) + +error[E0307]: invalid `self` parameter type: `&Bar` + --> $DIR/method_resolution4.rs:32:20 + | +LL | fn foomp(self: &Bar) { + | ^^^^^^^^^ + | + = note: type of `self` must be `Self` or a type that dereferences to it + = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0307`. diff --git a/tests/ui/type-alias-impl-trait/method_resolution4.next.stderr b/tests/ui/type-alias-impl-trait/method_resolution4.next.stderr new file mode 100644 index 000000000000..33ed2800ebe0 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/method_resolution4.next.stderr @@ -0,0 +1,15 @@ +error[E0271]: type mismatch resolving `u32 == Foo` + --> $DIR/method_resolution4.rs:27:18 + | +LL | fn foo(self: Bar) { + | ^^^^^^^^ types differ + +error[E0271]: type mismatch resolving `u32 == Foo` + --> $DIR/method_resolution4.rs:32:20 + | +LL | fn foomp(self: &Bar) { + | ^^^^^^^^^ types differ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/type-alias-impl-trait/method_resolution4.rs b/tests/ui/type-alias-impl-trait/method_resolution4.rs new file mode 100644 index 000000000000..42ed04b3c30f --- /dev/null +++ b/tests/ui/type-alias-impl-trait/method_resolution4.rs @@ -0,0 +1,39 @@ +//! Check that one cannot use arbitrary self types where a generic parameter +//! mismatches with an opaque type. In theory this could unify with the opaque +//! type, registering the generic parameter as the hidden type of the opaque type. + +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +#![feature(type_alias_impl_trait, arbitrary_self_types)] + +mod foo { + pub type Foo = impl Copy; + + fn foo() -> Foo { + 42_u32 + } +} +use foo::Foo; + +#[derive(Copy, Clone)] +struct Bar(T); + +impl Bar { + fn bar(self) {} +} + +impl Bar { + fn foo(self: Bar) { + //[current]~^ ERROR: invalid `self` parameter + //[next]~^^ ERROR: type mismatch resolving `u32 == Foo` + self.bar() + } + fn foomp(self: &Bar) { + //[current]~^ ERROR: invalid `self` parameter + //[next]~^^ ERROR: type mismatch resolving `u32 == Foo` + self.bar() + } +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/method_resolution5.rs b/tests/ui/type-alias-impl-trait/method_resolution5.rs new file mode 100644 index 000000000000..64355e4560de --- /dev/null +++ b/tests/ui/type-alias-impl-trait/method_resolution5.rs @@ -0,0 +1,33 @@ +//! Even though `Bar::foo` is defining `Foo`, the old solver does +//! not figure out that `u32` is the hidden type of `Foo` to call `bar`. + +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ check-pass + +#![feature(type_alias_impl_trait)] + +type Foo = impl Sized; + +struct Bar(T); + +impl Bar { + fn bar(mut self) { + self.0 = 42_u32; + } +} + +impl Bar { + fn foo(self) + where + Foo:, + { + self.bar() + } +} + +fn foo() -> Foo { + 42_u32 +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.current.stderr b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.current.stderr new file mode 100644 index 000000000000..f331da1af879 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.current.stderr @@ -0,0 +1,15 @@ +error: item does not constrain `Tait::{opaque#0}`, but has it in its signature + --> $DIR/method_resolution_trait_method_from_opaque.rs:24:8 + | +LL | fn foo(&mut self) { + | ^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/method_resolution_trait_method_from_opaque.rs:17:13 + | +LL | type Tait = impl Iterator; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr new file mode 100644 index 000000000000..2617ce124c10 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.next.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/method_resolution_trait_method_from_opaque.rs:26:9 + | +LL | self.bar.next().unwrap(); + | ^^^^^^^^ cannot infer type + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.rs b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.rs new file mode 100644 index 000000000000..b6adf08853f2 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/method_resolution_trait_method_from_opaque.rs @@ -0,0 +1,31 @@ +//! This test demonstrates how method calls will attempt to unify an opaque type with a reference +//! if the method takes `&self` as its argument. This is almost never what is desired, as the user +//! would like to have method resolution happen on the opaque type instead of inferring the hidden +//! type. Once type-alias-impl-trait requires annotating which functions should constrain the hidden +//! type, this won't be as much of a problem, as most functions that do method calls on opaque types +//! won't also be the ones defining the hidden type. + +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +#![feature(type_alias_impl_trait)] + +pub struct Foo { + bar: Tait, +} + +type Tait = impl Iterator; + +impl Foo { + pub fn new() -> Foo { + Foo { bar: std::iter::empty() } + } + + fn foo(&mut self) { + //[current]~^ ERROR: item does not constrain + self.bar.next().unwrap(); + //[next]~^ ERROR: type annotations needed + } +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference.stderr b/tests/ui/type-alias-impl-trait/nested-tait-inference.current.stderr similarity index 90% rename from tests/ui/type-alias-impl-trait/nested-tait-inference.stderr rename to tests/ui/type-alias-impl-trait/nested-tait-inference.current.stderr index 172ecded7a2f..34532afcbbae 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-inference.stderr +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference.current.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `(): Foo` is not satisfied - --> $DIR/nested-tait-inference.rs:12:13 + --> $DIR/nested-tait-inference.rs:17:13 | LL | fn foo() -> impl Foo { | ^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()` diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference.rs b/tests/ui/type-alias-impl-trait/nested-tait-inference.rs index 82248971692c..50d51c7faf91 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-inference.rs +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference.rs @@ -1,18 +1,23 @@ #![feature(type_alias_impl_trait)] #![allow(dead_code)] +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next] check-pass + use std::fmt::Debug; type FooX = impl Debug; -trait Foo { } +trait Foo {} -impl Foo<()> for () { } +impl Foo<()> for () {} fn foo() -> impl Foo { - //~^ ERROR: the trait bound `(): Foo` is not satisfied + //[current]~^ ERROR: the trait bound `(): Foo` is not satisfied // FIXME(type-alias-impl-trait): We could probably make this work. () } -fn main() { } +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference2.stderr b/tests/ui/type-alias-impl-trait/nested-tait-inference2.current.stderr similarity index 77% rename from tests/ui/type-alias-impl-trait/nested-tait-inference2.stderr rename to tests/ui/type-alias-impl-trait/nested-tait-inference2.current.stderr index 241342b05096..9da3926ac708 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-inference2.stderr +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference2.current.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `(): Foo` is not satisfied - --> $DIR/nested-tait-inference2.rs:13:13 + --> $DIR/nested-tait-inference2.rs:17:13 | LL | fn foo() -> impl Foo { | ^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()` @@ -8,8 +8,8 @@ LL | () | -- return type was inferred to be `()` here | = help: the following other types implement trait `Foo`: - <() as Foo<()>> - <() as Foo> + `()` implements `Foo<()>` + `()` implements `Foo` error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr b/tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr new file mode 100644 index 000000000000..9647d9e376eb --- /dev/null +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference2.next.stderr @@ -0,0 +1,9 @@ +error[E0284]: type annotations needed: cannot satisfy `impl Foo == ()` + --> $DIR/nested-tait-inference2.rs:19:5 + | +LL | () + | ^^ cannot satisfy `impl Foo == ()` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference2.rs b/tests/ui/type-alias-impl-trait/nested-tait-inference2.rs index 0d7f5bad25f1..28d72b0cbeed 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-inference2.rs +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference2.rs @@ -1,6 +1,10 @@ #![feature(type_alias_impl_trait)] #![allow(dead_code)] +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + use std::fmt::Debug; type FooX = impl Debug; @@ -11,8 +15,9 @@ impl Foo<()> for () {} impl Foo for () {} fn foo() -> impl Foo { - //~^ ERROR: the trait bound `(): Foo` is not satisfied + //[current]~^ ERROR: the trait bound `(): Foo` is not satisfied () + //[next]~^ ERROR: cannot satisfy `impl Foo == ()` } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference3.rs b/tests/ui/type-alias-impl-trait/nested-tait-inference3.rs index b0ebdd1bfab7..a7d824c5a6a0 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-inference3.rs +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference3.rs @@ -6,12 +6,13 @@ use std::fmt::Debug; type FooX = impl Debug; //~^ ERROR unconstrained opaque type -trait Foo { } +trait Foo {} -impl Foo for () { } +impl Foo for () {} fn foo() -> impl Foo { + //~^ ERROR: item does not constrain () } -fn main() { } +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/nested-tait-inference3.stderr b/tests/ui/type-alias-impl-trait/nested-tait-inference3.stderr index ce5d30370534..9ccd95448963 100644 --- a/tests/ui/type-alias-impl-trait/nested-tait-inference3.stderr +++ b/tests/ui/type-alias-impl-trait/nested-tait-inference3.stderr @@ -1,3 +1,16 @@ +error: item does not constrain `FooX::{opaque#0}`, but has it in its signature + --> $DIR/nested-tait-inference3.rs:13:4 + | +LL | fn foo() -> impl Foo { + | ^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/nested-tait-inference3.rs:6:13 + | +LL | type FooX = impl Debug; + | ^^^^^^^^^^ + error: unconstrained opaque type --> $DIR/nested-tait-inference3.rs:6:13 | @@ -6,5 +19,5 @@ LL | type FooX = impl Debug; | = note: `FooX` must be used in combination with a concrete type within the same module -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/nested.rs b/tests/ui/type-alias-impl-trait/nested.rs index 6b866be7d173..524703939f1d 100644 --- a/tests/ui/type-alias-impl-trait/nested.rs +++ b/tests/ui/type-alias-impl-trait/nested.rs @@ -8,6 +8,7 @@ trait Trait {} impl Trait for U {} fn bar() -> Bar { + //~^ ERROR: item does not constrain 42 } diff --git a/tests/ui/type-alias-impl-trait/nested.stderr b/tests/ui/type-alias-impl-trait/nested.stderr index a19d4c4eb716..ca1cf6058ea9 100644 --- a/tests/ui/type-alias-impl-trait/nested.stderr +++ b/tests/ui/type-alias-impl-trait/nested.stderr @@ -1,5 +1,18 @@ +error: item does not constrain `Foo::{opaque#0}`, but has it in its signature + --> $DIR/nested.rs:10:4 + | +LL | fn bar() -> Bar { + | ^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/nested.rs:3:12 + | +LL | type Foo = impl std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^ + error[E0277]: `Bar` doesn't implement `Debug` - --> $DIR/nested.rs:15:22 + --> $DIR/nested.rs:16:22 | LL | println!("{:?}", bar()); | ^^^^^ `Bar` cannot be formatted using `{:?}` because it doesn't implement `Debug` @@ -7,6 +20,6 @@ LL | println!("{:?}", bar()); = help: the trait `Debug` is not implemented for `Bar` = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs b/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs index 07607516cc4b..4def8948708f 100644 --- a/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs +++ b/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.rs @@ -11,6 +11,7 @@ mod my_mod { } pub fn get_foot(_: Foo) -> Foot { + //~^ ERROR: item does not constrain `Foo::{opaque#0}`, but has it in its signature get_foo() //~ ERROR opaque type's hidden type cannot be another opaque type } } diff --git a/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.stderr b/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.stderr index 3e67a162f0f0..889cff1ba09e 100644 --- a/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.stderr +++ b/tests/ui/type-alias-impl-trait/nested_type_alias_impl_trait.stderr @@ -1,5 +1,18 @@ +error: item does not constrain `Foo::{opaque#0}`, but has it in its signature + --> $DIR/nested_type_alias_impl_trait.rs:13:12 + | +LL | pub fn get_foot(_: Foo) -> Foot { + | ^^^^^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/nested_type_alias_impl_trait.rs:6:20 + | +LL | pub type Foo = impl Debug; + | ^^^^^^^^^^ + error: opaque type's hidden type cannot be another opaque type from the same scope - --> $DIR/nested_type_alias_impl_trait.rs:14:9 + --> $DIR/nested_type_alias_impl_trait.rs:15:9 | LL | get_foo() | ^^^^^^^^^ one of the two opaque types used here has to be outside its defining scope @@ -15,5 +28,5 @@ note: opaque type being used as hidden type LL | pub type Foo = impl Debug; | ^^^^^^^^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs b/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs index 6431cf37df7a..3954672500b4 100644 --- a/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs +++ b/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs @@ -9,6 +9,7 @@ mod foo { // make compiler happy about using 'Foo' pub fn bar(x: Foo) -> Foo { + //~^ ERROR: item does not constrain `Foo::{opaque#0}` x } } diff --git a/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr b/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr index fabc80c0a4f2..d95a4a8a727a 100644 --- a/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr +++ b/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr @@ -1,3 +1,16 @@ +error: item does not constrain `Foo::{opaque#0}`, but has it in its signature + --> $DIR/no_inferrable_concrete_type.rs:11:12 + | +LL | pub fn bar(x: Foo) -> Foo { + | ^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/no_inferrable_concrete_type.rs:7:20 + | +LL | pub type Foo = impl Copy; + | ^^^^^^^^^ + error: unconstrained opaque type --> $DIR/no_inferrable_concrete_type.rs:7:20 | @@ -6,5 +19,5 @@ LL | pub type Foo = impl Copy; | = note: `Foo` must be used in combination with a concrete type within the same module -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/normalize-alias-type.rs b/tests/ui/type-alias-impl-trait/normalize-alias-type.rs index 605799260385..9fcf42e188c7 100644 --- a/tests/ui/type-alias-impl-trait/normalize-alias-type.rs +++ b/tests/ui/type-alias-impl-trait/normalize-alias-type.rs @@ -8,25 +8,32 @@ pub trait Tr { impl Tr for (u32,) { #[inline] - fn get(&self) -> u32 { self.0 } + fn get(&self) -> u32 { + self.0 + } } pub fn tr1() -> impl Tr { (32,) } -pub fn tr2() -> impl Tr { - struct Inner { - x: X, - } - type X = impl Tr; - impl Tr for Inner { - fn get(&self) -> u32 { - self.x.get() - } - } - - Inner { - x: tr1(), +struct Inner { + x: helper::X, +} +impl Tr for Inner { + fn get(&self) -> u32 { + self.x.get() + } +} + +mod helper { + pub use super::*; + pub type X = impl Tr; + + pub fn tr2() -> impl Tr + where + X:, + { + Inner { x: tr1() } } } diff --git a/tests/ui/type-alias-impl-trait/outlives-bound-var.rs b/tests/ui/type-alias-impl-trait/outlives-bound-var.rs index 0ae2c9600ce3..2c6f44d5416e 100644 --- a/tests/ui/type-alias-impl-trait/outlives-bound-var.rs +++ b/tests/ui/type-alias-impl-trait/outlives-bound-var.rs @@ -5,8 +5,11 @@ //@ check-pass #![feature(type_alias_impl_trait)] -type Ty<'a> = impl Sized + 'a; -fn define<'a>() -> Ty<'a> {} +mod tait { + pub type Ty<'a> = impl Sized + 'a; + fn define<'a>() -> Ty<'a> {} +} +use tait::Ty; // Ty<'^0>: 'static fn test1(_: &'static fn(Ty<'_>)) {} @@ -15,4 +18,4 @@ fn test2() { None::<&fn(Ty<'_>)>; } -fn main() { } +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs index e221f4f3f55c..38fb493b4989 100644 --- a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs +++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs @@ -9,26 +9,29 @@ struct A; impl Test for A {} struct B { - inner: T, + inner: T, } impl Test for B {} -type TestImpl = impl Test; +mod helper { + use super::*; + pub type TestImpl = impl Test; -fn test() -> TestImpl { - A + pub fn test() -> TestImpl { + A + } + + fn make_option2() -> Option { + let inner = make_option().unwrap(); + + Some(B { inner }) + //~^ ERROR concrete type differs from previous defining opaque type use + } } -fn make_option() -> Option { - Some(test()) -} - -fn make_option2() -> Option { - let inner = make_option().unwrap(); - - Some(B { inner }) - //~^ ERROR concrete type differs from previous defining opaque type use +fn make_option() -> Option { + Some(helper::test()) } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr index 05825e686259..252c5d7dfa76 100644 --- a/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr +++ b/tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.stderr @@ -1,14 +1,14 @@ error: concrete type differs from previous defining opaque type use - --> $DIR/recursive-tait-conflicting-defn.rs:30:3 + --> $DIR/recursive-tait-conflicting-defn.rs:28:9 | -LL | Some(B { inner }) - | ^^^^^^^^^^^^^^^^^ expected `A`, got `B` +LL | Some(B { inner }) + | ^^^^^^^^^^^^^^^^^ expected `A`, got `B` | note: previous use here - --> $DIR/recursive-tait-conflicting-defn.rs:20:3 + --> $DIR/recursive-tait-conflicting-defn.rs:22:9 | -LL | A - | ^ +LL | A + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs b/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs index 6f50703aca24..37d84feee4b8 100644 --- a/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs +++ b/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.rs @@ -12,15 +12,19 @@ #![feature(type_alias_impl_trait)] -type Opaque = impl Sized; +mod helper { + pub type Opaque = impl Sized; -fn get_rpit() -> impl Clone {} + pub fn get_rpit() -> impl Clone {} + + fn test() -> Opaque { + super::query(get_rpit); + get_rpit() + } +} + +use helper::*; fn query(_: impl FnOnce() -> Opaque) {} -fn test() -> Opaque { - query(get_rpit); - get_rpit() -} - fn main() {} diff --git a/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query_2.rs b/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query_2.rs index 9cb0316b7a38..80f1b1502d3b 100644 --- a/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query_2.rs +++ b/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query_2.rs @@ -5,14 +5,17 @@ #![feature(type_alias_impl_trait)] -type Opaque = impl Sized; +mod helper { + pub type Opaque = impl Sized; -fn get_rpit() -> impl Sized {} + pub fn get_rpit() -> impl Sized {} + + fn test(_: Opaque) { + super::query(get_rpit); + } +} +use helper::*; fn query(_: impl FnOnce() -> Opaque) {} -fn test(_: Opaque) { - query(get_rpit); -} - fn main() {} diff --git a/tests/ui/type-alias-impl-trait/self_implication.rs b/tests/ui/type-alias-impl-trait/self_implication.rs index eed13933a037..56464af96ed8 100644 --- a/tests/ui/type-alias-impl-trait/self_implication.rs +++ b/tests/ui/type-alias-impl-trait/self_implication.rs @@ -16,17 +16,18 @@ fn foo() { let _b = z; // this should *not* check that `'a` in the type `Foo<'a>::foo::opaque` is live } +struct Foo<'a> { + x: &'a mut u8, +} +// desugared +mod foo { + pub type FooX = impl Sized; + impl<'a> super::Foo<'a> { + pub fn foo(&self) -> FooX {} + } +} + fn bar() { - struct Foo<'a> { - x: &'a mut u8, - } - - // desugared - type FooX = impl Sized; - impl<'a> Foo<'a> { - fn foo(&self) -> FooX {} - } - // use site let mut x = 5; let y = Foo { x: &mut x }; diff --git a/tests/ui/type-alias-impl-trait/structural-match-no-leak.rs b/tests/ui/type-alias-impl-trait/structural-match-no-leak.rs index c2ab6a9d10aa..27f5799c3804 100644 --- a/tests/ui/type-alias-impl-trait/structural-match-no-leak.rs +++ b/tests/ui/type-alias-impl-trait/structural-match-no-leak.rs @@ -1,13 +1,15 @@ #![feature(type_alias_impl_trait)] -type Bar = impl Send; +mod bar { + pub type Bar = impl Send; -// While i32 is structural-match, we do not want to leak this information. -// (See https://github.com/rust-lang/rust/issues/72156) -const fn leak_free() -> Bar { - 7i32 + // While i32 is structural-match, we do not want to leak this information. + // (See https://github.com/rust-lang/rust/issues/72156) + pub const fn leak_free() -> Bar { + 7i32 + } } -const LEAK_FREE: Bar = leak_free(); +const LEAK_FREE: bar::Bar = bar::leak_free(); fn leak_free_test() { match LEAK_FREE { diff --git a/tests/ui/type-alias-impl-trait/structural-match-no-leak.stderr b/tests/ui/type-alias-impl-trait/structural-match-no-leak.stderr index b1ccd5cc402d..98d71aa9a17d 100644 --- a/tests/ui/type-alias-impl-trait/structural-match-no-leak.stderr +++ b/tests/ui/type-alias-impl-trait/structural-match-no-leak.stderr @@ -1,5 +1,5 @@ error: `Bar` cannot be used in patterns - --> $DIR/structural-match-no-leak.rs:14:9 + --> $DIR/structural-match-no-leak.rs:16:9 | LL | LEAK_FREE => (), | ^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/structural-match.rs b/tests/ui/type-alias-impl-trait/structural-match.rs index 7cc9ccaabdca..502595915397 100644 --- a/tests/ui/type-alias-impl-trait/structural-match.rs +++ b/tests/ui/type-alias-impl-trait/structural-match.rs @@ -1,19 +1,22 @@ #![feature(type_alias_impl_trait)] -type Foo = impl Send; +mod foo { + pub type Foo = impl Send; -// This is not structural-match -struct A; + // This is not structural-match + struct A; -const fn value() -> Foo { - A + pub const fn value() -> Foo { + A + } } +use foo::*; const VALUE: Foo = value(); fn test() { match VALUE { VALUE => (), - //~^ `Foo` cannot be used in patterns + //~^ `foo::Foo` cannot be used in patterns _ => (), } } diff --git a/tests/ui/type-alias-impl-trait/structural-match.stderr b/tests/ui/type-alias-impl-trait/structural-match.stderr index b0415db0e55c..c7478b0a135e 100644 --- a/tests/ui/type-alias-impl-trait/structural-match.stderr +++ b/tests/ui/type-alias-impl-trait/structural-match.stderr @@ -1,5 +1,5 @@ -error: `Foo` cannot be used in patterns - --> $DIR/structural-match.rs:15:9 +error: `foo::Foo` cannot be used in patterns + --> $DIR/structural-match.rs:18:9 | LL | VALUE => (), | ^^^^^ diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fns.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fns.rs index bd580a78984a..cbd91066c49d 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fns.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fns.rs @@ -1,5 +1,3 @@ -//@ check-pass - #![feature(type_alias_impl_trait)] // Regression test for issue #61863 @@ -18,6 +16,7 @@ fn bla() -> TE { } fn bla2() -> TE { + //~^ ERROR: item does not constrain `TE::{opaque#0}`, but has it in its signature bla() } diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fns.stderr b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fns.stderr new file mode 100644 index 000000000000..819bde021836 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-fns.stderr @@ -0,0 +1,15 @@ +error: item does not constrain `TE::{opaque#0}`, but has it in its signature + --> $DIR/type-alias-impl-trait-fns.rs:18:4 + | +LL | fn bla2() -> TE { + | ^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/type-alias-impl-trait-fns.rs:23:11 + | +LL | type TE = impl MyTrait; + | ^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.next.stderr b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.next.stderr deleted file mode 100644 index 036ab66f79d1..000000000000 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.next.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0284]: type annotations needed: cannot satisfy `Foo == _` - --> $DIR/type-alias-impl-trait-tuple.rs:22:24 - | -LL | Blah { my_foo: make_foo(), my_u8: 12 } - | ^^^^^^^^^^ cannot satisfy `Foo == _` - -error[E0284]: type annotations needed: cannot satisfy `Foo == _` - --> $DIR/type-alias-impl-trait-tuple.rs:26:10 - | -LL | (self.my_foo, self.my_u8, make_foo()) - | ^^^^^^^^^^^ cannot satisfy `Foo == _` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs index fadb92ab4401..8e90f9699532 100644 --- a/tests/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs +++ b/tests/ui/type-alias-impl-trait/type-alias-impl-trait-tuple.rs @@ -1,16 +1,24 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[current] check-pass +//@ check-pass #![feature(type_alias_impl_trait)] #![allow(dead_code)] -pub trait MyTrait {} +mod foo { + pub trait MyTrait {} -impl MyTrait for bool {} + impl MyTrait for bool {} -type Foo = impl MyTrait; + pub type Foo = impl MyTrait; + + pub fn make_foo() -> Foo { + true + } +} + +use foo::*; struct Blah { my_foo: Foo, @@ -20,16 +28,10 @@ struct Blah { impl Blah { fn new() -> Blah { Blah { my_foo: make_foo(), my_u8: 12 } - //[next]~^ ERROR type annotations needed: cannot satisfy `Foo == _` } fn into_inner(self) -> (Foo, u8, Foo) { (self.my_foo, self.my_u8, make_foo()) - //[next]~^ ERROR type annotations needed: cannot satisfy `Foo == _` } } -fn make_foo() -> Foo { - true -} - fn main() {} diff --git a/tests/ui/type-alias-impl-trait/unbounded_opaque_type.rs b/tests/ui/type-alias-impl-trait/unbounded_opaque_type.rs index 03afec859d27..09ff006acbdd 100644 --- a/tests/ui/type-alias-impl-trait/unbounded_opaque_type.rs +++ b/tests/ui/type-alias-impl-trait/unbounded_opaque_type.rs @@ -1,10 +1,15 @@ //@ check-pass #![feature(type_alias_impl_trait)] -type Opaque = impl Sized; -fn defining() -> Opaque {} -struct Ss<'a, T>(&'a Opaque); +mod opaque { + pub type Opaque = impl Sized; + fn defining() -> Opaque {} +} + +use opaque::Opaque; + +struct Ss<'a, T>(&'a Opaque); fn test<'a, T>(_: Ss<'a, T>) { // test that we have an implied bound `Opaque: 'a` from fn signature diff --git a/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.rs b/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.rs index 37e0d89efc75..56d975355c3e 100644 --- a/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.rs +++ b/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.rs @@ -1,16 +1,21 @@ // test for ICE #121472 index out of bounds un_derefer.rs #![feature(type_alias_impl_trait)] -trait T {} +mod foo { + pub trait T {} -type Alias<'a> = impl T; + pub type Alias<'a> = impl T; + fn bar() { + super::with_positive(|&n| ()); + //~^ ERROR mismatched types + } +} + +use foo::*; struct S; impl<'a> T for &'a S {} fn with_positive(fun: impl Fn(Alias<'_>)) {} -fn main() { - with_positive(|&n| ()); - //~^ ERROR mismatched types -} +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.stderr b/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.stderr index a224bab0705e..279bd3bca5ab 100644 --- a/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.stderr +++ b/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.stderr @@ -1,21 +1,21 @@ error[E0308]: mismatched types - --> $DIR/underef-index-out-of-bounds-121472.rs:14:20 + --> $DIR/underef-index-out-of-bounds-121472.rs:9:31 | -LL | type Alias<'a> = impl T; - | ------ the expected opaque type -... -LL | with_positive(|&n| ()); - | ^^ - | | - | expected opaque type, found `&_` - | expected due to this +LL | pub type Alias<'a> = impl T; + | ------ the expected opaque type +LL | fn bar() { +LL | super::with_positive(|&n| ()); + | ^^ + | | + | expected opaque type, found `&_` + | expected due to this | - = note: expected opaque type `Alias<'_>` + = note: expected opaque type `foo::Alias<'_>` found reference `&_` help: consider removing `&` from the pattern | -LL - with_positive(|&n| ()); -LL + with_positive(|n| ()); +LL - super::with_positive(|&n| ()); +LL + super::with_positive(|n| ()); | error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/unnameable_type.rs b/tests/ui/type-alias-impl-trait/unnameable_type.rs index 1739ab0063fa..5813f529dea1 100644 --- a/tests/ui/type-alias-impl-trait/unnameable_type.rs +++ b/tests/ui/type-alias-impl-trait/unnameable_type.rs @@ -15,10 +15,11 @@ use private::Trait; // downstream type MyPrivate = impl Sized; -//~^ ERROR: unconstrained opaque type impl Trait for u32 { - fn dont_define_this(_private: MyPrivate) {} - //~^ ERROR: incompatible type for trait + fn dont_define_this(private: MyPrivate) { + //~^ ERROR: incompatible type for trait + let _: () = private; + } } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/unnameable_type.stderr b/tests/ui/type-alias-impl-trait/unnameable_type.stderr index e9032433494a..f567b01d29a1 100644 --- a/tests/ui/type-alias-impl-trait/unnameable_type.stderr +++ b/tests/ui/type-alias-impl-trait/unnameable_type.stderr @@ -1,22 +1,14 @@ -error: unconstrained opaque type - --> $DIR/unnameable_type.rs:17:18 - | -LL | type MyPrivate = impl Sized; - | ^^^^^^^^^^ - | - = note: `MyPrivate` must be used in combination with a concrete type within the same module - error[E0053]: method `dont_define_this` has an incompatible type for trait - --> $DIR/unnameable_type.rs:20:35 + --> $DIR/unnameable_type.rs:19:34 | LL | type MyPrivate = impl Sized; | ---------- the found opaque type -... -LL | fn dont_define_this(_private: MyPrivate) {} - | ^^^^^^^^^ - | | - | expected `Private`, found opaque type - | help: change the parameter type to match the trait: `Private` +LL | impl Trait for u32 { +LL | fn dont_define_this(private: MyPrivate) { + | ^^^^^^^^^ + | | + | expected `Private`, found opaque type + | help: change the parameter type to match the trait: `Private` | note: type in trait --> $DIR/unnameable_type.rs:10:39 @@ -26,6 +18,6 @@ LL | fn dont_define_this(_private: Private) {} = note: expected signature `fn(Private)` found signature `fn(MyPrivate)` -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs new file mode 100644 index 000000000000..3defe0cb44d3 --- /dev/null +++ b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs @@ -0,0 +1,10 @@ +#![feature(pattern_types, core_pattern_type)] +#![allow(internal_features)] + +type Pat = + std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>); +//~^ ERROR type and const arguments are not allowed on const parameter `START` +//~| ERROR type arguments are not allowed on const parameter `END` +//~| ERROR associated item constraints are not allowed here + +fn main() {} diff --git a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr new file mode 100644 index 000000000000..7f4e6e314f53 --- /dev/null +++ b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr @@ -0,0 +1,38 @@ +error[E0109]: type and const arguments are not allowed on const parameter `START` + --> $DIR/bad_const_generics_args_on_const_param.rs:5:44 + | +LL | std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>); + | ----- ^^ ^^^ ^ type and const arguments not allowed + | | + | not allowed on const parameter `START` + | +note: const parameter `START` defined here + --> $DIR/bad_const_generics_args_on_const_param.rs:4:16 + | +LL | type Pat = + | ^^^^^ + +error[E0109]: type arguments are not allowed on const parameter `END` + --> $DIR/bad_const_generics_args_on_const_param.rs:5:64 + | +LL | std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>); + | --- ^ type argument not allowed + | | + | not allowed on const parameter `END` + | +note: const parameter `END` defined here + --> $DIR/bad_const_generics_args_on_const_param.rs:4:34 + | +LL | type Pat = + | ^^^ + +error[E0229]: associated item constraints are not allowed here + --> $DIR/bad_const_generics_args_on_const_param.rs:5:67 + | +LL | std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>); + | ^^^^^^^^^^ associated item constraint not allowed here + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0109, E0229. +For more information about an error, try `rustc --explain E0109`. diff --git a/tests/ui/type/pattern_types/bad_pat.stderr b/tests/ui/type/pattern_types/bad_pat.stderr index 4f0f0bc97427..2abf27100c1d 100644 --- a/tests/ui/type/pattern_types/bad_pat.stderr +++ b/tests/ui/type/pattern_types/bad_pat.stderr @@ -14,7 +14,7 @@ LL | type Positive2 = pattern_type!(i32 is 0..=); | = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) -error: "wildcard patterns are not permitted for pattern types" +error: wildcard patterns are not permitted for pattern types --> $DIR/bad_pat.rs:11:33 | LL | type Wild = pattern_type!(() is _); diff --git a/tests/ui/type/type-check-defaults.stderr b/tests/ui/type/type-check-defaults.stderr index 9ba63ffe9c93..499e8142cc8f 100644 --- a/tests/ui/type/type-check-defaults.stderr +++ b/tests/ui/type/type-check-defaults.stderr @@ -66,10 +66,10 @@ LL | trait ProjectionPred> where T::Item : Add {} | = help: the trait `Add` is not implemented for `i32` = help: the following other types implement trait `Add`: - <&'a i32 as Add> - <&i32 as Add<&i32>> - > - + `&'a i32` implements `Add` + `&i32` implements `Add<&i32>` + `i32` implements `Add<&i32>` + `i32` implements `Add` error: aborting due to 7 previous errors diff --git a/tests/ui/typeck/escaping_bound_vars.rs b/tests/ui/typeck/escaping_bound_vars.rs index 985a3fdbccf3..3cde041f1858 100644 --- a/tests/ui/typeck/escaping_bound_vars.rs +++ b/tests/ui/typeck/escaping_bound_vars.rs @@ -10,7 +10,7 @@ pub fn test() where (): Test<{ 1 + (<() as Elide(&())>::call) }>, //~^ ERROR cannot capture late-bound lifetime in constant - //~| ERROR associated type bindings are not allowed here + //~| ERROR associated item constraints are not allowed here { } diff --git a/tests/ui/typeck/escaping_bound_vars.stderr b/tests/ui/typeck/escaping_bound_vars.stderr index bd9c95fab979..f383099ce13b 100644 --- a/tests/ui/typeck/escaping_bound_vars.stderr +++ b/tests/ui/typeck/escaping_bound_vars.stderr @@ -6,11 +6,11 @@ LL | (): Test<{ 1 + (<() as Elide(&())>::call) }>, | | | lifetime defined here -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/escaping_bound_vars.rs:11:28 | LL | (): Test<{ 1 + (<() as Elide(&())>::call) }>, - | ^^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^^ associated item constraint not allowed here error: aborting due to 2 previous errors diff --git a/tests/ui/typeck/issue-81293.stderr b/tests/ui/typeck/issue-81293.stderr index 6976be71135c..3c48db335b5f 100644 --- a/tests/ui/typeck/issue-81293.stderr +++ b/tests/ui/typeck/issue-81293.stderr @@ -21,10 +21,10 @@ LL | a = c + b * 5; | = help: the trait `Add` is not implemented for `usize` = help: the following other types implement trait `Add`: - <&'a usize as Add> - <&usize as Add<&usize>> - > - + `&'a usize` implements `Add` + `&usize` implements `Add<&usize>` + `usize` implements `Add<&usize>` + `usize` implements `Add` error: aborting due to 3 previous errors diff --git a/tests/ui/typeck/issue-81943.stderr b/tests/ui/typeck/issue-81943.stderr index 041ff10752cf..f8da9ef0d180 100644 --- a/tests/ui/typeck/issue-81943.stderr +++ b/tests/ui/typeck/issue-81943.stderr @@ -2,7 +2,9 @@ error[E0308]: mismatched types --> $DIR/issue-81943.rs:7:9 | LL | f(|x| lib::d!(x)); - | ^^^^^^^^^^ expected `()`, found `i32` + | -^^^^^^^^^^ expected `()`, found `i32` + | | + | help: try adding a return type: `-> i32` | = note: this error originates in the macro `lib::d` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -10,28 +12,22 @@ error[E0308]: mismatched types --> $DIR/issue-81943.rs:8:28 | LL | f(|x| match x { tmp => { g(tmp) } }); - | -------------------^^^^^^---- - | | | - | | expected `()`, found `i32` - | expected this to be `()` + | ^^^^^^ expected `()`, found `i32` | help: consider using a semicolon here | LL | f(|x| match x { tmp => { g(tmp); } }); | + -help: consider using a semicolon here +help: try adding a return type | -LL | f(|x| match x { tmp => { g(tmp) } };); - | + +LL | f(|x| -> i32 match x { tmp => { g(tmp) } }); + | ++++++ error[E0308]: mismatched types --> $DIR/issue-81943.rs:10:38 | LL | ($e:expr) => { match $e { x => { g(x) } } } - | ------------------^^^^---- - | | | - | | expected `()`, found `i32` - | expected this to be `()` + | ^^^^ expected `()`, found `i32` LL | } LL | f(|x| d!(x)); | ----- in this macro invocation @@ -41,10 +37,10 @@ help: consider using a semicolon here | LL | ($e:expr) => { match $e { x => { g(x); } } } | + -help: consider using a semicolon here +help: try adding a return type | -LL | ($e:expr) => { match $e { x => { g(x) } }; } - | + +LL | f(|x| -> i32 d!(x)); + | ++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/typeck/issue-83693.rs b/tests/ui/typeck/issue-83693.rs index a4255822056d..02a0bb30d7ce 100644 --- a/tests/ui/typeck/issue-83693.rs +++ b/tests/ui/typeck/issue-83693.rs @@ -8,12 +8,12 @@ impl F { fn call() { ::call //~^ ERROR: cannot find type `TestResult` in this scope [E0412] - //~| associated type bindings are not allowed here [E0229] + //~| associated item constraints are not allowed here [E0229] } } fn call() { ::call //~^ ERROR: cannot find type `x` in this scope [E0412] - //~| ERROR: associated type bindings are not allowed here [E0229] + //~| ERROR: associated item constraints are not allowed here [E0229] } diff --git a/tests/ui/typeck/issue-83693.stderr b/tests/ui/typeck/issue-83693.stderr index ce4f73b820a1..34bca426116e 100644 --- a/tests/ui/typeck/issue-83693.stderr +++ b/tests/ui/typeck/issue-83693.stderr @@ -19,17 +19,17 @@ error[E0412]: cannot find type `x` in this scope LL | ::call | ^ not found in this scope -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-83693.rs:9:18 | LL | ::call - | ^^^^^^^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^^^^^^^ associated item constraint not allowed here -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-83693.rs:16:11 | LL | ::call - | ^^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^^ associated item constraint not allowed here error: aborting due to 5 previous errors diff --git a/tests/ui/typeck/issue-84768.rs b/tests/ui/typeck/issue-84768.rs index ffa92823b42f..3ba5dffcf07a 100644 --- a/tests/ui/typeck/issue-84768.rs +++ b/tests/ui/typeck/issue-84768.rs @@ -5,6 +5,6 @@ fn transform_mut(f: F) where F: for<'b> FnOnce(&'b mut u8) { ::call_once(f, 1) - //~^ ERROR: associated type bindings are not allowed here [E0229] + //~^ ERROR: associated item constraints are not allowed here [E0229] //~| ERROR: mismatched types [E0308] } diff --git a/tests/ui/typeck/issue-84768.stderr b/tests/ui/typeck/issue-84768.stderr index 3d2d53f5c76c..72784ba59c9b 100644 --- a/tests/ui/typeck/issue-84768.stderr +++ b/tests/ui/typeck/issue-84768.stderr @@ -1,8 +1,8 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-84768.rs:7:11 | LL | ::call_once(f, 1) - | ^^^^^^^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^^^^^^^ associated item constraint not allowed here error[E0308]: mismatched types --> $DIR/issue-84768.rs:7:42 diff --git a/tests/ui/typeck/issue-90101.stderr b/tests/ui/typeck/issue-90101.stderr index 800d7e9594a2..d6832d1b34ff 100644 --- a/tests/ui/typeck/issue-90101.stderr +++ b/tests/ui/typeck/issue-90101.stderr @@ -7,11 +7,11 @@ LL | func(Path::new("hello").to_path_buf().to_string_lossy(), "world") | required by a bound introduced by this call | = help: the following other types implement trait `From`: - > - >> - >> - > - > + `PathBuf` implements `From<&T>` + `PathBuf` implements `From>` + `PathBuf` implements `From>` + `PathBuf` implements `From` + `PathBuf` implements `From` = note: required for `Cow<'_, str>` to implement `Into` note: required by a bound in `func` --> $DIR/issue-90101.rs:3:20 diff --git a/tests/ui/typeck/issue-91267.rs b/tests/ui/typeck/issue-91267.rs index 4e39cfab5b43..1bffa09e6436 100644 --- a/tests/ui/typeck/issue-91267.rs +++ b/tests/ui/typeck/issue-91267.rs @@ -3,6 +3,6 @@ fn main() { type_ascribe!(0, u8=e>) //~^ ERROR: cannot find type `e` in this scope [E0412] - //~| ERROR: associated type bindings are not allowed here [E0229] + //~| ERROR: associated item constraints are not allowed here [E0229] //~| ERROR: mismatched types [E0308] } diff --git a/tests/ui/typeck/issue-91267.stderr b/tests/ui/typeck/issue-91267.stderr index 399309d0ec44..d16b1997e9f7 100644 --- a/tests/ui/typeck/issue-91267.stderr +++ b/tests/ui/typeck/issue-91267.stderr @@ -4,11 +4,11 @@ error[E0412]: cannot find type `e` in this scope LL | type_ascribe!(0, u8=e>) | ^ not found in this scope -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-91267.rs:4:25 | LL | type_ascribe!(0, u8=e>) - | ^^^^^^ associated type not allowed here + | ^^^^^^ associated item constraint not allowed here error[E0308]: mismatched types --> $DIR/issue-91267.rs:4:5 diff --git a/tests/ui/typeck/suggest-similar-impls-for-root-obligation.rs b/tests/ui/typeck/suggest-similar-impls-for-root-obligation.rs new file mode 100644 index 000000000000..d00b4f331325 --- /dev/null +++ b/tests/ui/typeck/suggest-similar-impls-for-root-obligation.rs @@ -0,0 +1,16 @@ +trait Foo<'s> {} + +impl<'s> Foo<'s> for () {} + +struct Bar; + +impl<'s, T: Foo<'s>> From for Bar { + fn from(_: T) -> Self { + Bar + } +} + +fn main() { + let _: Bar = ((),).into(); + //~^ ERROR he trait bound `((),): Into` is not satisfied +} diff --git a/tests/ui/typeck/suggest-similar-impls-for-root-obligation.stderr b/tests/ui/typeck/suggest-similar-impls-for-root-obligation.stderr new file mode 100644 index 000000000000..8410574e3112 --- /dev/null +++ b/tests/ui/typeck/suggest-similar-impls-for-root-obligation.stderr @@ -0,0 +1,20 @@ +error[E0277]: the trait bound `((),): Into` is not satisfied + --> $DIR/suggest-similar-impls-for-root-obligation.rs:14:24 + | +LL | let _: Bar = ((),).into(); + | ^^^^ the trait `Foo<'_>` is not implemented for `((),)`, which is required by `((),): Into<_>` + | + = help: the trait `Foo<'_>` is implemented for `()` + = help: for that trait implementation, expected `()`, found `((),)` +note: required for `Bar` to implement `From<((),)>` + --> $DIR/suggest-similar-impls-for-root-obligation.rs:7:22 + | +LL | impl<'s, T: Foo<'s>> From for Bar { + | ------- ^^^^^^^ ^^^ + | | + | unsatisfied trait bound introduced here + = note: required for `((),)` to implement `Into` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/ufcs/bad-builder.stderr b/tests/ui/ufcs/bad-builder.stderr index e466f94d0d84..9cfeb7a5d09d 100644 --- a/tests/ui/ufcs/bad-builder.stderr +++ b/tests/ui/ufcs/bad-builder.stderr @@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec` consider using one of the followi Vec::::with_capacity Vec::::try_with_capacity Vec::::from_raw_parts - and 6 others + and 4 others --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: there is an associated function `new` with a similar name | diff --git a/tests/ui/ufcs/ufcs-qpath-self-mismatch.stderr b/tests/ui/ufcs/ufcs-qpath-self-mismatch.stderr index 964fd4af0092..5b8a49d22c73 100644 --- a/tests/ui/ufcs/ufcs-qpath-self-mismatch.stderr +++ b/tests/ui/ufcs/ufcs-qpath-self-mismatch.stderr @@ -6,10 +6,10 @@ LL | >::add(1, 2); | = help: the trait `Add` is not implemented for `i32` = help: the following other types implement trait `Add`: - <&'a i32 as Add> - <&i32 as Add<&i32>> - > - + `&'a i32` implements `Add` + `&i32` implements `Add<&i32>` + `i32` implements `Add<&i32>` + `i32` implements `Add` error[E0277]: cannot add `u32` to `i32` --> $DIR/ufcs-qpath-self-mismatch.rs:4:5 @@ -19,10 +19,10 @@ LL | >::add(1, 2); | = help: the trait `Add` is not implemented for `i32` = help: the following other types implement trait `Add`: - <&'a i32 as Add> - <&i32 as Add<&i32>> - > - + `&'a i32` implements `Add` + `&i32` implements `Add<&i32>` + `i32` implements `Add<&i32>` + `i32` implements `Add` error[E0308]: mismatched types --> $DIR/ufcs-qpath-self-mismatch.rs:7:28 diff --git a/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr b/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr index 795bd0a0d180..9c5d824185b8 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr @@ -6,7 +6,7 @@ LL | let x = call_it(&S, 22); | | | required by a bound introduced by this call | - = help: the trait `Fn<(isize,)>` is not implemented for `S` + = help: the trait `Fn(isize)` is not implemented for `S` = note: `S` implements `FnMut`, but it must implement `Fn`, which is more general note: required by a bound in `call_it` --> $DIR/unboxed-closures-fnmut-as-fn.rs:22:14 diff --git a/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr b/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr index d836af2b0149..dfdb3ea19c3f 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr @@ -6,7 +6,7 @@ LL | let x = call_it(&square, 22); | | | required by a bound introduced by this call | - = help: the trait `for<'a> Fn<(&'a isize,)>` is not implemented for fn item `for<'a> unsafe fn(&'a isize) -> isize {square}` + = help: the trait `for<'a> Fn(&'a isize)` is not implemented for fn item `for<'a> unsafe fn(&'a isize) -> isize {square}` = note: unsafe function cannot be called generically without an unsafe block note: required by a bound in `call_it` --> $DIR/unboxed-closures-unsafe-extern-fn.rs:9:15 @@ -22,7 +22,7 @@ LL | let y = call_it_mut(&mut square, 22); | | | required by a bound introduced by this call | - = help: the trait `for<'a> FnMut<(&'a isize,)>` is not implemented for fn item `for<'a> unsafe fn(&'a isize) -> isize {square}` + = help: the trait `for<'a> FnMut(&'a isize)` is not implemented for fn item `for<'a> unsafe fn(&'a isize) -> isize {square}` = note: unsafe function cannot be called generically without an unsafe block note: required by a bound in `call_it_mut` --> $DIR/unboxed-closures-unsafe-extern-fn.rs:12:19 @@ -38,7 +38,7 @@ LL | let z = call_it_once(square, 22); | | | required by a bound introduced by this call | - = help: the trait `for<'a> FnOnce<(&'a isize,)>` is not implemented for fn item `for<'a> unsafe fn(&'a isize) -> isize {square}` + = help: the trait `for<'a> FnOnce(&'a isize)` is not implemented for fn item `for<'a> unsafe fn(&'a isize) -> isize {square}` = note: unsafe function cannot be called generically without an unsafe block note: required by a bound in `call_it_once` --> $DIR/unboxed-closures-unsafe-extern-fn.rs:15:20 diff --git a/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr b/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr index c0dcf83a5bb0..b4521cc6890d 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr @@ -6,7 +6,7 @@ LL | let x = call_it(&square, 22); | | | required by a bound introduced by this call | - = help: the trait `for<'a> Fn<(&'a isize,)>` is not implemented for fn item `for<'a> extern "C" fn(&'a isize) -> isize {square}` + = help: the trait `for<'a> Fn(&'a isize)` is not implemented for fn item `for<'a> extern "C" fn(&'a isize) -> isize {square}` note: required by a bound in `call_it` --> $DIR/unboxed-closures-wrong-abi.rs:9:15 | @@ -21,7 +21,7 @@ LL | let y = call_it_mut(&mut square, 22); | | | required by a bound introduced by this call | - = help: the trait `for<'a> FnMut<(&'a isize,)>` is not implemented for fn item `for<'a> extern "C" fn(&'a isize) -> isize {square}` + = help: the trait `for<'a> FnMut(&'a isize)` is not implemented for fn item `for<'a> extern "C" fn(&'a isize) -> isize {square}` note: required by a bound in `call_it_mut` --> $DIR/unboxed-closures-wrong-abi.rs:12:19 | @@ -36,7 +36,7 @@ LL | let z = call_it_once(square, 22); | | | required by a bound introduced by this call | - = help: the trait `for<'a> FnOnce<(&'a isize,)>` is not implemented for fn item `for<'a> extern "C" fn(&'a isize) -> isize {square}` + = help: the trait `for<'a> FnOnce(&'a isize)` is not implemented for fn item `for<'a> extern "C" fn(&'a isize) -> isize {square}` note: required by a bound in `call_it_once` --> $DIR/unboxed-closures-wrong-abi.rs:15:20 | diff --git a/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr b/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr index d261c38f50c2..5519bb33ebc2 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr @@ -6,7 +6,7 @@ LL | let x = call_it(&square, 22); | | | required by a bound introduced by this call | - = help: the trait `for<'a> Fn<(&'a isize,)>` is not implemented for fn item `unsafe fn(isize) -> isize {square}` + = help: the trait `for<'a> Fn(&'a isize)` is not implemented for fn item `unsafe fn(isize) -> isize {square}` = note: unsafe function cannot be called generically without an unsafe block note: required by a bound in `call_it` --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:10:15 @@ -22,7 +22,7 @@ LL | let y = call_it_mut(&mut square, 22); | | | required by a bound introduced by this call | - = help: the trait `for<'a> FnMut<(&'a isize,)>` is not implemented for fn item `unsafe fn(isize) -> isize {square}` + = help: the trait `for<'a> FnMut(&'a isize)` is not implemented for fn item `unsafe fn(isize) -> isize {square}` = note: unsafe function cannot be called generically without an unsafe block note: required by a bound in `call_it_mut` --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:13:19 @@ -38,7 +38,7 @@ LL | let z = call_it_once(square, 22); | | | required by a bound introduced by this call | - = help: the trait `for<'a> FnOnce<(&'a isize,)>` is not implemented for fn item `unsafe fn(isize) -> isize {square}` + = help: the trait `for<'a> FnOnce(&'a isize)` is not implemented for fn item `unsafe fn(isize) -> isize {square}` = note: unsafe function cannot be called generically without an unsafe block note: required by a bound in `call_it_once` --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:16:20 diff --git a/tests/ui/unop-move-semantics.rs b/tests/ui/unop/unop-move-semantics.rs similarity index 100% rename from tests/ui/unop-move-semantics.rs rename to tests/ui/unop/unop-move-semantics.rs diff --git a/tests/ui/unop-move-semantics.stderr b/tests/ui/unop/unop-move-semantics.stderr similarity index 100% rename from tests/ui/unop-move-semantics.stderr rename to tests/ui/unop/unop-move-semantics.stderr diff --git a/tests/ui/unop-neg-bool.rs b/tests/ui/unop/unop-neg-bool.rs similarity index 100% rename from tests/ui/unop-neg-bool.rs rename to tests/ui/unop/unop-neg-bool.rs diff --git a/tests/ui/unop-neg-bool.stderr b/tests/ui/unop/unop-neg-bool.stderr similarity index 100% rename from tests/ui/unop-neg-bool.stderr rename to tests/ui/unop/unop-neg-bool.stderr diff --git a/tests/ui/unpretty/bad-literal.stdout b/tests/ui/unpretty/bad-literal.stdout index 07ecb99dccc5..c5272711d6e9 100644 --- a/tests/ui/unpretty/bad-literal.stdout +++ b/tests/ui/unpretty/bad-literal.stdout @@ -7,5 +7,5 @@ extern crate std; // In #100948 this caused an ICE with -Zunpretty=hir. fn main() { - ; - } + ; +} diff --git a/tests/ui/unpretty/box.stdout b/tests/ui/unpretty/box.stdout index 0fd51edea241..e3b9b9ac2071 100644 --- a/tests/ui/unpretty/box.stdout +++ b/tests/ui/unpretty/box.stdout @@ -8,7 +8,7 @@ use ::std::prelude::rust_2015::*; extern crate std; fn main() { - let _ = - #[rustc_box] - Box::new(1); - } + let _ = + #[rustc_box] + Box::new(1); +} diff --git a/tests/ui/unpretty/expanded-exhaustive.rs b/tests/ui/unpretty/expanded-exhaustive.rs index 6aa032d7ed83..92c2e7b48847 100644 --- a/tests/ui/unpretty/expanded-exhaustive.rs +++ b/tests/ui/unpretty/expanded-exhaustive.rs @@ -25,6 +25,7 @@ #![feature(trait_alias)] #![feature(try_blocks)] #![feature(unnamed_fields)] +#![feature(unsafe_extern_blocks)] #![feature(yeet_expr)] #![allow(incomplete_features)] @@ -473,8 +474,8 @@ mod items { /// ItemKind::ForeignMod mod item_foreign_mod { - extern "C++" {} - extern {} + unsafe extern "C++" {} + unsafe extern {} } /// ItemKind::GlobalAsm diff --git a/tests/ui/unpretty/expanded-exhaustive.stdout b/tests/ui/unpretty/expanded-exhaustive.stdout index 325bace7b562..137a8aa5510d 100644 --- a/tests/ui/unpretty/expanded-exhaustive.stdout +++ b/tests/ui/unpretty/expanded-exhaustive.stdout @@ -26,6 +26,7 @@ #![feature(trait_alias)] #![feature(try_blocks)] #![feature(unnamed_fields)] +#![feature(unsafe_extern_blocks)] #![feature(yeet_expr)] #![allow(incomplete_features)] #[prelude_import] @@ -450,8 +451,8 @@ mod items { mod item_mod { } /// ItemKind::ForeignMod mod item_foreign_mod { - extern "C++" {} - extern {} + unsafe extern "C++" {} + unsafe extern {} } /// ItemKind::GlobalAsm mod item_global_asm { diff --git a/tests/ui/unpretty/flattened-format-args.stdout b/tests/ui/unpretty/flattened-format-args.stdout index 275fa104e667..2de1cdd96b5b 100644 --- a/tests/ui/unpretty/flattened-format-args.stdout +++ b/tests/ui/unpretty/flattened-format-args.stdout @@ -6,10 +6,10 @@ extern crate std; //@ check-pass fn main() { - let x = 1; - // Should flatten to println!("a 123 b {x} xyz\n"): - { - ::std::io::_print(format_arguments::new_v1(&["a 123 b ", - " xyz\n"], &[format_argument::new_display(&x)])); - }; - } + let x = 1; + // Should flatten to println!("a 123 b {x} xyz\n"): + { + ::std::io::_print(format_arguments::new_v1(&["a 123 b ", " xyz\n"], + &[format_argument::new_display(&x)])); + }; +} diff --git a/tests/ui/unpretty/let-else-hir.stdout b/tests/ui/unpretty/let-else-hir.stdout index ed55f293876e..a2ffa5de5673 100644 --- a/tests/ui/unpretty/let-else-hir.stdout +++ b/tests/ui/unpretty/let-else-hir.stdout @@ -9,10 +9,10 @@ extern crate std; fn foo(x: Option) { - let Some(_) = x else - { + let Some(_) = x else + { - { ::std::rt::begin_panic("explicit panic") } - }; - } + { ::std::rt::begin_panic("explicit panic") } + }; +} fn main() { } diff --git a/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr b/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr index 2eb1754392e1..fe12dd72d9eb 100644 --- a/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr +++ b/tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr @@ -1,4 +1,4 @@ -warning: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133) +warning[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block --> $DIR/edition-2024-unsafe_op_in_unsafe_fn.rs:10:5 | LL | unsf(); @@ -15,3 +15,4 @@ LL | unsafe fn foo() { warning: 1 warning emitted +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/unsafe/initializing-ranged-via-ctor.stderr b/tests/ui/unsafe/initializing-ranged-via-ctor.stderr index 56b112867cf0..040c1d5bcbee 100644 --- a/tests/ui/unsafe/initializing-ranged-via-ctor.stderr +++ b/tests/ui/unsafe/initializing-ranged-via-ctor.stderr @@ -6,7 +6,7 @@ LL | println!("{:?}", Some(1).map(NonZeroAndOneU8).unwrap()); | | | required by a bound introduced by this call | - = help: the trait `FnOnce<({integer},)>` is not implemented for fn item `unsafe fn(u8) -> NonZeroAndOneU8 {NonZeroAndOneU8}` + = help: the trait `FnOnce({integer})` is not implemented for fn item `unsafe fn(u8) -> NonZeroAndOneU8 {NonZeroAndOneU8}` = note: unsafe function cannot be called generically without an unsafe block note: required by a bound in `Option::::map` --> $SRC_DIR/core/src/option.rs:LL:COL diff --git a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.stderr b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.stderr index e6d1d4e5cc78..05f36ab47cb5 100644 --- a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.stderr +++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/edition_2024_default.stderr @@ -1,4 +1,4 @@ -warning: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133) +warning[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block --> $DIR/edition_2024_default.rs:13:5 | LL | unsf(); @@ -15,3 +15,4 @@ LL | unsafe fn foo() { warning: 1 warning emitted +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.stderr b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.stderr index 5092c1e689d6..3f97199458da 100644 --- a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.stderr +++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/in_2024_compatibility.stderr @@ -1,4 +1,4 @@ -error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133) +error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block --> $DIR/in_2024_compatibility.rs:7:5 | LL | unsf(); @@ -20,3 +20,4 @@ LL | #![deny(rust_2024_compatibility)] error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/rfc-2585-unsafe_op_in_unsafe_fn.stderr b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/rfc-2585-unsafe_op_in_unsafe_fn.stderr index 4bc604a110ea..1f80342566ca 100644 --- a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/rfc-2585-unsafe_op_in_unsafe_fn.stderr +++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/rfc-2585-unsafe_op_in_unsafe_fn.stderr @@ -1,4 +1,4 @@ -error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133) +error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:9:5 | LL | unsf(); @@ -17,7 +17,7 @@ note: the lint level is defined here LL | #![deny(unsafe_op_in_unsafe_fn)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) +error[E0133]: dereference of raw pointer is unsafe and requires unsafe block --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:11:5 | LL | *PTR; @@ -26,7 +26,7 @@ LL | *PTR; = note: for more information, see issue #71668 = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior -error: use of mutable static is unsafe and requires unsafe block (error E0133) +error[E0133]: use of mutable static is unsafe and requires unsafe block --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:13:5 | LL | VOID = (); @@ -47,7 +47,7 @@ note: the lint level is defined here LL | #![deny(unused_unsafe)] | ^^^^^^^^^^^^^ -error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133) +error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:24:5 | LL | unsf(); @@ -67,7 +67,7 @@ LL | #[deny(warnings)] | ^^^^^^^^ = note: `#[deny(unsafe_op_in_unsafe_fn)]` implied by `#[deny(warnings)]` -error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) +error[E0133]: dereference of raw pointer is unsafe and requires unsafe block --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:26:5 | LL | *PTR; @@ -76,7 +76,7 @@ LL | *PTR; = note: for more information, see issue #71668 = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior -error: use of mutable static is unsafe and requires unsafe block (error E0133) +error[E0133]: use of mutable static is unsafe and requires unsafe block --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:28:5 | LL | VOID = (); diff --git a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.stderr b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.stderr index b9f5969474d0..1ce22ecfdc79 100644 --- a/tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.stderr +++ b/tests/ui/unsafe/unsafe_op_in_unsafe_fn/wrapping-unsafe-block-sugg.stderr @@ -1,4 +1,4 @@ -error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133) +error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block --> $DIR/wrapping-unsafe-block-sugg.rs:13:5 | LL | unsf(); @@ -17,7 +17,7 @@ note: the lint level is defined here LL | #![deny(unsafe_op_in_unsafe_fn)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133) +error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block --> $DIR/wrapping-unsafe-block-sugg.rs:17:5 | LL | unsf(); @@ -26,7 +26,7 @@ LL | unsf(); = note: for more information, see issue #71668 = note: consult the function's documentation for information on how to avoid undefined behavior -error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) +error[E0133]: dereference of raw pointer is unsafe and requires unsafe block --> $DIR/wrapping-unsafe-block-sugg.rs:25:13 | LL | let y = *x; @@ -40,7 +40,7 @@ note: an unsafe function restricts its caller, but its body is safe by default LL | pub unsafe fn bar(x: *const i32) -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) +error[E0133]: dereference of raw pointer is unsafe and requires unsafe block --> $DIR/wrapping-unsafe-block-sugg.rs:29:9 | LL | y + *x @@ -49,7 +49,7 @@ LL | y + *x = note: for more information, see issue #71668 = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior -error: use of mutable static is unsafe and requires unsafe block (error E0133) +error[E0133]: use of mutable static is unsafe and requires unsafe block --> $DIR/wrapping-unsafe-block-sugg.rs:38:13 | LL | let y = BAZ; @@ -63,7 +63,7 @@ note: an unsafe function restricts its caller, but its body is safe by default LL | pub unsafe fn baz() -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of mutable static is unsafe and requires unsafe block (error E0133) +error[E0133]: use of mutable static is unsafe and requires unsafe block --> $DIR/wrapping-unsafe-block-sugg.rs:42:9 | LL | y + BAZ @@ -72,7 +72,7 @@ LL | y + BAZ = note: for more information, see issue #71668 = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior -error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133) +error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block --> $DIR/wrapping-unsafe-block-sugg.rs:48:36 | LL | macro_rules! unsafe_macro { () => (unsf()) } @@ -90,7 +90,7 @@ LL | pub unsafe fn unsafe_in_macro() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `unsafe_macro` (in Nightly builds, run with -Z macro-backtrace for more info) -error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133) +error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block --> $DIR/wrapping-unsafe-block-sugg.rs:48:36 | LL | macro_rules! unsafe_macro { () => (unsf()) } @@ -105,3 +105,4 @@ LL | unsafe_macro!(); error: aborting due to 8 previous errors +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/unsized/unsized3-rpass.rs b/tests/ui/unsized/unsized3-rpass.rs index 1b3e932bfe6d..ff35051774bc 100644 --- a/tests/ui/unsized/unsized3-rpass.rs +++ b/tests/ui/unsized/unsized3-rpass.rs @@ -87,7 +87,7 @@ pub fn main() { let obj: Box = Box::new(St { f: 42 }); let obj: &Tr = &*obj; let data: Box<_> = Box::new(Qux_ { f: St { f: 234 } }); - let x: &Qux = &*ptr::from_raw_parts::((&*data as *const _).cast(), ptr::metadata(obj)); + let x: &Qux = &*ptr::from_raw_parts::(&*data as *const _, ptr::metadata(obj)); assert_eq!(x.f.foo(), 234); } } diff --git a/tests/ui/unused-crate-deps/deny-attr.rs b/tests/ui/unused-crate-deps/deny-attr.rs index 3b2e8b5677e2..71f478b6b7d7 100644 --- a/tests/ui/unused-crate-deps/deny-attr.rs +++ b/tests/ui/unused-crate-deps/deny-attr.rs @@ -4,6 +4,6 @@ //@ aux-crate:bar=bar.rs #![deny(unused_crate_dependencies)] -//~^ ERROR external crate `bar` unused in +//~^ ERROR extern crate `bar` is unused in fn main() {} diff --git a/tests/ui/unused-crate-deps/deny-attr.stderr b/tests/ui/unused-crate-deps/deny-attr.stderr index 89e87c9d5ac2..7540d9cdc162 100644 --- a/tests/ui/unused-crate-deps/deny-attr.stderr +++ b/tests/ui/unused-crate-deps/deny-attr.stderr @@ -1,9 +1,10 @@ -error: external crate `bar` unused in `deny_attr`: remove the dependency or add `use bar as _;` +error: extern crate `bar` is unused in crate `deny_attr` --> $DIR/deny-attr.rs:6:1 | LL | #![deny(unused_crate_dependencies)] | ^ | + = help: remove the dependency or add `use bar as _;` to the crate root note: the lint level is defined here --> $DIR/deny-attr.rs:6:9 | diff --git a/tests/ui/unused-crate-deps/deny-cmdline.rs b/tests/ui/unused-crate-deps/deny-cmdline.rs index d32e8e205af3..890c01e00162 100644 --- a/tests/ui/unused-crate-deps/deny-cmdline.rs +++ b/tests/ui/unused-crate-deps/deny-cmdline.rs @@ -5,4 +5,4 @@ //@ aux-crate:bar=bar.rs fn main() {} -//~^ ERROR external crate `bar` unused in +//~^ ERROR extern crate `bar` is unused in diff --git a/tests/ui/unused-crate-deps/deny-cmdline.stderr b/tests/ui/unused-crate-deps/deny-cmdline.stderr index fc526878ef05..b48d1ec1ba9f 100644 --- a/tests/ui/unused-crate-deps/deny-cmdline.stderr +++ b/tests/ui/unused-crate-deps/deny-cmdline.stderr @@ -1,9 +1,10 @@ -error: external crate `bar` unused in `deny_cmdline`: remove the dependency or add `use bar as _;` +error: extern crate `bar` is unused in crate `deny_cmdline` --> $DIR/deny-cmdline.rs:7:1 | LL | fn main() {} | ^ | + = help: remove the dependency or add `use bar as _;` to the crate root = note: requested on the command line with `-D unused-crate-dependencies` error: aborting due to 1 previous error diff --git a/tests/ui/unused-crate-deps/libfib.rs b/tests/ui/unused-crate-deps/libfib.rs index 8a5ec758e560..d7729c9ebe7e 100644 --- a/tests/ui/unused-crate-deps/libfib.rs +++ b/tests/ui/unused-crate-deps/libfib.rs @@ -5,7 +5,7 @@ //@ compile-flags:--crate-type lib -Wunused-crate-dependencies pub fn fib(n: u32) -> Vec { -//~^ WARNING external crate `bar` unused in +//~^ WARNING extern crate `bar` is unused in let mut prev = 0; let mut cur = 1; let mut v = vec![]; diff --git a/tests/ui/unused-crate-deps/libfib.stderr b/tests/ui/unused-crate-deps/libfib.stderr index 15833126bd62..da6893f50345 100644 --- a/tests/ui/unused-crate-deps/libfib.stderr +++ b/tests/ui/unused-crate-deps/libfib.stderr @@ -1,9 +1,10 @@ -warning: external crate `bar` unused in `libfib`: remove the dependency or add `use bar as _;` +warning: extern crate `bar` is unused in crate `libfib` --> $DIR/libfib.rs:7:1 | LL | pub fn fib(n: u32) -> Vec { | ^ | + = help: remove the dependency or add `use bar as _;` to the crate root = note: requested on the command line with `-W unused-crate-dependencies` warning: 1 warning emitted diff --git a/tests/ui/unused-crate-deps/unused-aliases.rs b/tests/ui/unused-crate-deps/unused-aliases.rs index f86f6bbf3564..2db50cec971d 100644 --- a/tests/ui/unused-crate-deps/unused-aliases.rs +++ b/tests/ui/unused-crate-deps/unused-aliases.rs @@ -6,7 +6,7 @@ //@ aux-crate:barbar=bar.rs #![warn(unused_crate_dependencies)] -//~^ WARNING external crate `barbar` unused in +//~^ WARNING extern crate `barbar` is unused in use bar as _; diff --git a/tests/ui/unused-crate-deps/unused-aliases.stderr b/tests/ui/unused-crate-deps/unused-aliases.stderr index c8c6c4507b0c..d4093b102d50 100644 --- a/tests/ui/unused-crate-deps/unused-aliases.stderr +++ b/tests/ui/unused-crate-deps/unused-aliases.stderr @@ -1,9 +1,10 @@ -warning: external crate `barbar` unused in `unused_aliases`: remove the dependency or add `use barbar as _;` +warning: extern crate `barbar` is unused in crate `unused_aliases` --> $DIR/unused-aliases.rs:8:1 | LL | #![warn(unused_crate_dependencies)] | ^ | + = help: remove the dependency or add `use barbar as _;` to the crate root note: the lint level is defined here --> $DIR/unused-aliases.rs:8:9 | diff --git a/tests/ui/unused-crate-deps/warn-attr.rs b/tests/ui/unused-crate-deps/warn-attr.rs index b558c0678d6b..c40ed0017329 100644 --- a/tests/ui/unused-crate-deps/warn-attr.rs +++ b/tests/ui/unused-crate-deps/warn-attr.rs @@ -5,6 +5,6 @@ //@ aux-crate:bar=bar.rs #![warn(unused_crate_dependencies)] -//~^ WARNING external crate `bar` unused in +//~^ WARNING extern crate `bar` is unused in fn main() {} diff --git a/tests/ui/unused-crate-deps/warn-attr.stderr b/tests/ui/unused-crate-deps/warn-attr.stderr index 0d38315704b1..2c72961093c2 100644 --- a/tests/ui/unused-crate-deps/warn-attr.stderr +++ b/tests/ui/unused-crate-deps/warn-attr.stderr @@ -1,9 +1,10 @@ -warning: external crate `bar` unused in `warn_attr`: remove the dependency or add `use bar as _;` +warning: extern crate `bar` is unused in crate `warn_attr` --> $DIR/warn-attr.rs:7:1 | LL | #![warn(unused_crate_dependencies)] | ^ | + = help: remove the dependency or add `use bar as _;` to the crate root note: the lint level is defined here --> $DIR/warn-attr.rs:7:9 | diff --git a/tests/ui/unused-crate-deps/warn-cmdline-static.rs b/tests/ui/unused-crate-deps/warn-cmdline-static.rs index ccb404e3033b..eb51543ad1ab 100644 --- a/tests/ui/unused-crate-deps/warn-cmdline-static.rs +++ b/tests/ui/unused-crate-deps/warn-cmdline-static.rs @@ -7,4 +7,4 @@ //@ no-prefer-dynamic fn main() {} -//~^ WARNING external crate `bar` unused in +//~^ WARNING extern crate `bar` is unused in diff --git a/tests/ui/unused-crate-deps/warn-cmdline-static.stderr b/tests/ui/unused-crate-deps/warn-cmdline-static.stderr index 65956461d643..79a3ade87190 100644 --- a/tests/ui/unused-crate-deps/warn-cmdline-static.stderr +++ b/tests/ui/unused-crate-deps/warn-cmdline-static.stderr @@ -1,9 +1,10 @@ -warning: external crate `bar` unused in `warn_cmdline_static`: remove the dependency or add `use bar as _;` +warning: extern crate `bar` is unused in crate `warn_cmdline_static` --> $DIR/warn-cmdline-static.rs:9:1 | LL | fn main() {} | ^ | + = help: remove the dependency or add `use bar as _;` to the crate root = note: requested on the command line with `-W unused-crate-dependencies` warning: 1 warning emitted diff --git a/tests/ui/unused-crate-deps/warn-cmdline.rs b/tests/ui/unused-crate-deps/warn-cmdline.rs index 8d390e02ca59..cdeff5e5f602 100644 --- a/tests/ui/unused-crate-deps/warn-cmdline.rs +++ b/tests/ui/unused-crate-deps/warn-cmdline.rs @@ -6,4 +6,4 @@ //@ aux-crate:bar=bar.rs fn main() {} -//~^ WARNING external crate `bar` unused in +//~^ WARNING extern crate `bar` is unused in diff --git a/tests/ui/unused-crate-deps/warn-cmdline.stderr b/tests/ui/unused-crate-deps/warn-cmdline.stderr index ea675ba9a1eb..f5df83ca8841 100644 --- a/tests/ui/unused-crate-deps/warn-cmdline.stderr +++ b/tests/ui/unused-crate-deps/warn-cmdline.stderr @@ -1,9 +1,10 @@ -warning: external crate `bar` unused in `warn_cmdline`: remove the dependency or add `use bar as _;` +warning: extern crate `bar` is unused in crate `warn_cmdline` --> $DIR/warn-cmdline.rs:8:1 | LL | fn main() {} | ^ | + = help: remove the dependency or add `use bar as _;` to the crate root = note: requested on the command line with `-W unused-crate-dependencies` warning: 1 warning emitted diff --git a/tests/ui/use/use-nested-groups-unused-imports.rs b/tests/ui/use/use-nested-groups-unused-imports.rs index ca6b8ba94d1c..0c8ae558a597 100644 --- a/tests/ui/use/use-nested-groups-unused-imports.rs +++ b/tests/ui/use/use-nested-groups-unused-imports.rs @@ -13,7 +13,7 @@ mod foo { } use foo::{Foo, bar::{baz::{}, foobar::*}, *}; - //~^ ERROR unused imports: `*`, `Foo`, `baz::{}`, `foobar::*` + //~^ ERROR unused imports: `*`, `Foo`, `baz::{}`, and `foobar::*` use foo::bar::baz::{*, *}; //~^ ERROR unused import: `*` use foo::{}; diff --git a/tests/ui/use/use-nested-groups-unused-imports.stderr b/tests/ui/use/use-nested-groups-unused-imports.stderr index 6610f8ecd4a2..dd39a852772c 100644 --- a/tests/ui/use/use-nested-groups-unused-imports.stderr +++ b/tests/ui/use/use-nested-groups-unused-imports.stderr @@ -1,4 +1,4 @@ -error: unused imports: `*`, `Foo`, `baz::{}`, `foobar::*` +error: unused imports: `*`, `Foo`, `baz::{}`, and `foobar::*` --> $DIR/use-nested-groups-unused-imports.rs:15:11 | LL | use foo::{Foo, bar::{baz::{}, foobar::*}, *}; diff --git a/tests/ui/wait-forked-but-failed-child.rs b/tests/ui/wait-forked-but-failed-child.rs index 3d052cc193c5..dd6a7fa0e650 100644 --- a/tests/ui/wait-forked-but-failed-child.rs +++ b/tests/ui/wait-forked-but-failed-child.rs @@ -7,8 +7,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::process::Command; // The output from "ps -A -o pid,ppid,args" should look like this: @@ -28,6 +26,7 @@ use std::process::Command; #[cfg(unix)] fn find_zombies() { + extern crate libc; let my_pid = unsafe { libc::getpid() }; // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs index db155f4fa3c3..3f43fbfc0cf4 100644 --- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs @@ -1,7 +1,6 @@ trait Trait { //~^ ERROR cannot find value `bar` in this scope //~| ERROR cycle detected when computing type of `Trait::N` - //~| ERROR cycle detected when computing type of `Trait::N` //~| ERROR the trait `Trait` cannot be made into an object //~| ERROR the trait `Trait` cannot be made into an object //~| ERROR the trait `Trait` cannot be made into an object @@ -11,20 +10,20 @@ trait Trait { //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! fn fnc(&self) -> Trait { - //~^ ERROR the name `N` is already used for a generic parameter in this item's generic parameters - //~| ERROR expected value, found builtin type `u32` - //~| ERROR defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - //~| ERROR associated item referring to unboxed trait object for its own trait - //~| ERROR the trait `Trait` cannot be made into an object - //~| ERROR `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter - //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] - //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! - //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] - //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! - //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] - //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + //~^ ERROR the name `N` is already used for a generic parameter in this item's generic parameters + //~| ERROR expected value, found builtin type `u32` + //~| ERROR defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + //~| ERROR associated item referring to unboxed trait object for its own trait + //~| ERROR the trait `Trait` cannot be made into an object + //~| ERROR `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter + //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! bar - //~^ ERROR cannot find value `bar` in this scope + //~^ ERROR cannot find value `bar` in this scope } } diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr index cf985d9d1fdf..f2456f99e623 100644 --- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr @@ -1,5 +1,5 @@ error[E0403]: the name `N` is already used for a generic parameter in this item's generic parameters - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:18 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:18 | LL | trait Trait { | - first use of `N` @@ -14,13 +14,13 @@ LL | trait Trait { | ^^^ not found in this scope error[E0423]: expected value, found builtin type `u32` - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:29 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:29 | LL | fn fnc(&self) -> Trait { | ^^^ not a value error[E0425]: cannot find value `bar` in this scope - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:26:9 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:25:9 | LL | bar | ^^^ not found in this scope @@ -53,28 +53,14 @@ LL | trait Trait { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0391]: cycle detected when computing type of `Trait::N` - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:13 - | -LL | trait Trait { - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: ...which immediately requires computing type of `Trait::N` again -note: cycle used when computing explicit predicates of trait `Trait` - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:1 - | -LL | trait Trait { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:12 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:12 | LL | fn fnc(&self) -> Trait { | ^^^^^^^^^^^^^^^^^^^^ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:21 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:21 | LL | fn fnc(&self) -> Trait { | ^^^^^ @@ -87,7 +73,7 @@ LL | fn fnc(&self) -> Trait { | +++ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:44 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:44 | LL | fn fnc(&self) -> Trait { | ^^^^^ @@ -120,7 +106,7 @@ LL | trait Trait { | ^^^^^ `Trait` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:8 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:8 | LL | trait Trait { | ----- this trait cannot be made into an object... @@ -136,7 +122,7 @@ LL | trait Trait { | ^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:8 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:8 | LL | trait Trait { | ----- this trait cannot be made into an object... @@ -154,7 +140,7 @@ LL | trait Trait { = note: the only supported types are integers, `bool` and `char` error: associated item referring to unboxed trait object for its own trait - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:44 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:44 | LL | trait Trait { | ----- in this trait @@ -168,7 +154,7 @@ LL | fn fnc(&self) -> Self { | ~~~~ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:21 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:21 | LL | fn fnc(&self) -> Trait { | ^^^^^ @@ -182,13 +168,13 @@ LL | fn fnc(&self) -> Trait { | +++ error[E0038]: the trait `Trait` cannot be made into an object - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:21 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:21 | LL | fn fnc(&self) -> Trait { | ^^^^^ `Trait` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:8 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:8 | LL | trait Trait { | ----- this trait cannot be made into an object... @@ -204,7 +190,7 @@ LL | trait Trait { | ^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:8 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:8 | LL | trait Trait { | ----- this trait cannot be made into an object... @@ -215,14 +201,14 @@ LL | fn fnc(&self) -> Trait { = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:21 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:12:21 | LL | fn fnc(&self) -> Trait { | ^^^^^ | = note: the only supported types are integers, `bool` and `char` -error: aborting due to 14 previous errors; 5 warnings emitted +error: aborting due to 13 previous errors; 5 warnings emitted Some errors have detailed explanations: E0038, E0391, E0403, E0423, E0425. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.rs b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.rs index 2995f26af4a6..a953f1818c58 100644 --- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.rs +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.rs @@ -1,15 +1,11 @@ // Regression test for ICE #122989 trait Foo> { -//~^ WARN trait objects without an explicit `dyn` are deprecated -//~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! -//~| ERROR cycle detected when computing type of `Foo::N` -//~| ERROR cycle detected when computing type of `Foo::N` -//~| ERROR the trait `Foo` cannot be made into an object -//~| ERROR the trait `Foo` cannot be made into an object -//~| ERROR the trait `Foo` cannot be made into an object -//~| ERROR `(dyn Bar<2> + 'static)` is forbidden as the type of a const generic parameter - fn func() { - } + //~^ WARN trait objects without an explicit `dyn` are deprecated + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + //~| ERROR cycle detected when computing type of `Foo::N` + //~| ERROR the trait `Foo` cannot be made into an object + //~| ERROR `(dyn Bar<2> + 'static)` is forbidden as the type of a const generic parameter + fn func() {} } trait Bar> {} diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr index e6fdc4408736..a0fd11de2dc6 100644 --- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr @@ -13,7 +13,7 @@ LL | trait Foo> { | +++ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:15:20 + --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:20 | LL | trait Bar> {} | ^^^^^^ @@ -32,7 +32,7 @@ LL | trait Foo> { | ^^^^^^^^^^^^^^^ | note: ...which requires computing type of `Bar::M`... - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:15:11 + --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:11 | LL | trait Bar> {} | ^^^^^^^^^^^^^^^ @@ -44,26 +44,6 @@ LL | trait Foo> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0391]: cycle detected when computing type of `Foo::N` - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:2:11 - | -LL | trait Foo> { - | ^^^^^^^^^^^^^^^ - | -note: ...which requires computing type of `Bar::M`... - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:15:11 - | -LL | trait Bar> {} - | ^^^^^^^^^^^^^^^ - = note: ...which again requires computing type of `Foo::N`, completing the cycle -note: cycle used when computing explicit predicates of trait `Foo` - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:2:1 - | -LL | trait Foo> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0038]: the trait `Foo` cannot be made into an object --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:2:24 | @@ -71,43 +51,20 @@ LL | trait Foo> { | ^ `Foo` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:8 + --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:8:8 | LL | trait Foo> { | --- this trait cannot be made into an object... ... -LL | fn func() { +LL | fn func() {} | ^^^^ ...because associated function `func` has no `self` parameter help: consider turning `func` into a method by giving it a `&self` argument | -LL | fn func(&self) { +LL | fn func(&self) {} | +++++ help: alternatively, consider constraining `func` so it does not apply to trait objects | -LL | fn func() where Self: Sized { - | +++++++++++++++++ - -error[E0038]: the trait `Foo` cannot be made into an object - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:2:11 - | -LL | trait Foo> { - | ^^^^^^^^^^^^^^^ `Foo` cannot be made into an object - | -note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:8 - | -LL | trait Foo> { - | --- this trait cannot be made into an object... -... -LL | fn func() { - | ^^^^ ...because associated function `func` has no `self` parameter -help: consider turning `func` into a method by giving it a `&self` argument - | -LL | fn func(&self) { - | +++++ -help: alternatively, consider constraining `func` so it does not apply to trait objects - | -LL | fn func() where Self: Sized { +LL | fn func() where Self: Sized {} | +++++++++++++++++ error: `(dyn Bar<2> + 'static)` is forbidden as the type of a const generic parameter @@ -119,61 +76,37 @@ LL | trait Foo> { = note: the only supported types are integers, `bool` and `char` error[E0038]: the trait `Foo` cannot be made into an object - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:15:11 + --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:11 | LL | trait Bar> {} | ^^^^^^^^^^^^^^^ `Foo` cannot be made into an object | note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:8 + --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:8:8 | LL | trait Foo> { | --- this trait cannot be made into an object... ... -LL | fn func() { +LL | fn func() {} | ^^^^ ...because associated function `func` has no `self` parameter help: consider turning `func` into a method by giving it a `&self` argument | -LL | fn func(&self) { +LL | fn func(&self) {} | +++++ help: alternatively, consider constraining `func` so it does not apply to trait objects | -LL | fn func() where Self: Sized { +LL | fn func() where Self: Sized {} | +++++++++++++++++ error: `(dyn Foo<2> + 'static)` is forbidden as the type of a const generic parameter - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:15:20 + --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:20 | LL | trait Bar> {} | ^^^^^^ | = note: the only supported types are integers, `bool` and `char` -error[E0038]: the trait `Foo` cannot be made into an object - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:2:11 - | -LL | trait Foo> { - | ^^^^^^^^^^^^^^^ `Foo` cannot be made into an object - | -note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:8 - | -LL | trait Foo> { - | --- this trait cannot be made into an object... -... -LL | fn func() { - | ^^^^ ...because associated function `func` has no `self` parameter - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider turning `func` into a method by giving it a `&self` argument - | -LL | fn func(&self) { - | +++++ -help: alternatively, consider constraining `func` so it does not apply to trait objects - | -LL | fn func() where Self: Sized { - | +++++++++++++++++ - -error: aborting due to 8 previous errors; 2 warnings emitted +error: aborting due to 5 previous errors; 2 warnings emitted Some errors have detailed explanations: E0038, E0391. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/where-clauses/self-in-where-clause-allowed.rs b/tests/ui/where-clauses/self-in-where-clause-allowed.rs index fd2cfe2bf65e..a4c2d82b4837 100644 --- a/tests/ui/where-clauses/self-in-where-clause-allowed.rs +++ b/tests/ui/where-clauses/self-in-where-clause-allowed.rs @@ -1,7 +1,6 @@ //@ check-fail #![feature(auto_traits)] -#![deny(where_clauses_object_safety)] auto trait AutoTrait {} diff --git a/tests/ui/where-clauses/self-in-where-clause-allowed.stderr b/tests/ui/where-clauses/self-in-where-clause-allowed.stderr index 7f92ac102f01..5d23c4bee00f 100644 --- a/tests/ui/where-clauses/self-in-where-clause-allowed.stderr +++ b/tests/ui/where-clauses/self-in-where-clause-allowed.stderr @@ -1,11 +1,11 @@ error[E0277]: the trait bound `dyn Trait: AutoTrait` is not satisfied - --> $DIR/self-in-where-clause-allowed.rs:22:18 + --> $DIR/self-in-where-clause-allowed.rs:21:18 | LL | trait_object.autotrait_bound(); | ^^^^^^^^^^^^^^^ the trait `AutoTrait` is not implemented for `dyn Trait` | note: required by a bound in `Trait::autotrait_bound` - --> $DIR/self-in-where-clause-allowed.rs:13:43 + --> $DIR/self-in-where-clause-allowed.rs:12:43 | LL | fn autotrait_bound(&self) where Self: AutoTrait {} | ^^^^^^^^^ required by this bound in `Trait::autotrait_bound` diff --git a/tests/run-make/windows-subsystem/console.rs b/tests/ui/windows-subsystem/console.rs similarity index 78% rename from tests/run-make/windows-subsystem/console.rs rename to tests/ui/windows-subsystem/console.rs index 61a92eb6a9db..8f0ca2de370d 100644 --- a/tests/run-make/windows-subsystem/console.rs +++ b/tests/ui/windows-subsystem/console.rs @@ -1,3 +1,4 @@ +//@ run-pass #![windows_subsystem = "console"] fn main() {} diff --git a/tests/ui/windows-subsystem-invalid.rs b/tests/ui/windows-subsystem/windows-subsystem-invalid.rs similarity index 100% rename from tests/ui/windows-subsystem-invalid.rs rename to tests/ui/windows-subsystem/windows-subsystem-invalid.rs diff --git a/tests/ui/windows-subsystem-invalid.stderr b/tests/ui/windows-subsystem/windows-subsystem-invalid.stderr similarity index 100% rename from tests/ui/windows-subsystem-invalid.stderr rename to tests/ui/windows-subsystem/windows-subsystem-invalid.stderr diff --git a/tests/run-make/windows-subsystem/windows.rs b/tests/ui/windows-subsystem/windows.rs similarity index 78% rename from tests/run-make/windows-subsystem/windows.rs rename to tests/ui/windows-subsystem/windows.rs index 1138248f07da..65db0fec7a8b 100644 --- a/tests/run-make/windows-subsystem/windows.rs +++ b/tests/ui/windows-subsystem/windows.rs @@ -1,3 +1,4 @@ +//@ run-pass #![windows_subsystem = "windows"] fn main() {} diff --git a/triagebot.toml b/triagebot.toml index 2e45b257f812..3abff0f1c784 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -122,6 +122,19 @@ issues). """ label = "O-apple" +# This ping group is meant for situations where a rustc/stdlib change breaks RfL. +# In that case, we want to notify the RfL group. +[ping.rust-for-linux] +alias = ["rfl"] +message = """\ +Hey Rust for Linux group! It looks like something broke the Rust for Linux integration. +Could you try to take a look? +In case it's useful, here are some [instructions] for tackling these sorts of issues. + +[instructions]: https://rustc-dev-guide.rust-lang.org/notification-groups/rust-for-linux.html +""" +label = "O-rfl" + [prioritize] label = "I-prioritize" @@ -317,6 +330,7 @@ trigger_files = [ "src/tools/compiletest", "src/tools/tidy", "src/tools/rustdoc-gui-test", + "src/tools/libcxx-version", ] [autolabel."T-infra"] @@ -389,8 +403,11 @@ exclude_labels = [ [autolabel."WG-trait-system-refactor"] trigger_files = [ + "compiler/rustc_middle/src/traits/solve", + "compiler/rustc_next_trait_solver", "compiler/rustc_trait_selection/src/solve", - "compiler/rustc_middle/src/traits/solve" + "compiler/rustc_type_ir/src/solve", + "tests/ui/traits/next-solver", ] [autolabel."PG-exploit-mitigations"] @@ -412,6 +429,12 @@ trigger_files = [ "tests/ui/stack-protector" ] +[autolabel."A-run-make"] +trigger_files = [ + "tests/run-make", + "src/tools/run-make-support" +] + [notify-zulip."I-prioritize"] zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts topic = "#{number} {title}" @@ -452,6 +475,19 @@ message_on_remove = "PR #{number}'s beta-nomination has been removed." message_on_close = "PR #{number} has been closed. Thanks for participating!" message_on_reopen = "PR #{number} has been reopened. Pinging @*T-rustdoc*." +# FIXME: Patch triagebot to support `notify-zulip.

` (where P is one of the previous types except `Self`) +error[E0378]: the trait `DispatchFromDyn` may only be implemented for a coercion between structures + --> $DIR/issue-78372.rs:3:1 + | +LL | impl DispatchFromDyn> for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: aborting due to 8 previous errors Some errors have detailed explanations: E0038, E0307, E0378, E0412, E0658. diff --git a/tests/ui/traits/issue-87558.rs b/tests/ui/traits/issue-87558.rs index 76f0f7453ddd..61a24f6b9671 100644 --- a/tests/ui/traits/issue-87558.rs +++ b/tests/ui/traits/issue-87558.rs @@ -2,7 +2,7 @@ struct ErrorKind; struct Error(ErrorKind); impl Fn(&isize) for Error { //~^ ERROR manual implementations of `Fn` are experimental - //~| ERROR associated type bindings are not allowed here + //~| ERROR associated item constraints are not allowed here //~| ERROR closure, found `Error` //~| ERROR not all trait items implemented, missing: `call` fn from() {} //~ ERROR method `from` is not a member of trait `Fn` diff --git a/tests/ui/traits/issue-87558.stderr b/tests/ui/traits/issue-87558.stderr index 1ce273a9f25e..4cc3ec2ea8b9 100644 --- a/tests/ui/traits/issue-87558.stderr +++ b/tests/ui/traits/issue-87558.stderr @@ -12,11 +12,11 @@ LL | impl Fn(&isize) for Error { | = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-87558.rs:3:6 | LL | impl Fn(&isize) for Error { - | ^^^^^^^^^^ associated type not allowed here + | ^^^^^^^^^^ associated item constraint not allowed here | help: parenthesized trait syntax expands to `Fn<(&isize,), Output=()>` --> $DIR/issue-87558.rs:3:6 @@ -30,7 +30,7 @@ error[E0277]: expected a `FnMut(&isize)` closure, found `Error` LL | impl Fn(&isize) for Error { | ^^^^^ expected an `FnMut(&isize)` closure, found `Error` | - = help: the trait `FnMut<(&isize,)>` is not implemented for `Error` + = help: the trait `FnMut(&isize)` is not implemented for `Error` note: required by a bound in `Fn` --> $SRC_DIR/core/src/ops/function.rs:LL:COL diff --git a/tests/ui/traits/item-privacy.stderr b/tests/ui/traits/item-privacy.stderr index d08bb4745bf5..fd474fac1558 100644 --- a/tests/ui/traits/item-privacy.stderr +++ b/tests/ui/traits/item-privacy.stderr @@ -8,10 +8,7 @@ LL | S.a(); | ^ | = help: items from traits can only be used if the trait is implemented and in scope -help: trait `A` which provides `a` is implemented but not in scope; perhaps you want to import it - | -LL + use method::A; - | + = help: trait `method::A` which provides `a` is implemented but not reachable help: there is a method `b` with a similar name | LL | S.b(); @@ -58,15 +55,12 @@ LL | S::a(&S); | ^ function or associated item not found in `S` | = help: items from traits can only be used if the trait is implemented and in scope + = help: trait `method::A` which provides `a` is implemented but not reachable help: there is an associated constant `B` with a similar name --> $DIR/item-privacy.rs:29:9 | LL | const B: u8 = 0; | ^^^^^^^^^^^ -help: trait `A` which provides `a` is implemented but not in scope; perhaps you want to import it - | -LL + use method::A; - | error[E0599]: no function or associated item named `b` found for struct `S` in the current scope --> $DIR/item-privacy.rs:80:8 @@ -107,10 +101,7 @@ LL | S::A; | ^ associated item not found in `S` | = help: items from traits can only be used if the trait is implemented and in scope -help: trait `A` which provides `A` is implemented but not in scope; perhaps you want to import it - | -LL + use assoc_const::A; - | + = help: trait `assoc_const::A` which provides `A` is implemented but not reachable help: there is an associated constant `B` with a similar name | LL | S::B; @@ -126,14 +117,15 @@ LL | S::B; | ^ associated item not found in `S` | = help: items from traits can only be used if the trait is in scope +help: there is a method `b` with a similar name + --> $DIR/item-privacy.rs:11:9 + | +LL | fn b(&self) { } + | ^^^^^^^^^^^ help: trait `B` which provides `B` is implemented but not in scope; perhaps you want to import it | LL + use assoc_const::B; | -help: there is a method `b` with a similar name - | -LL | S::b; - | ~ error[E0624]: associated constant `A` is private --> $DIR/item-privacy.rs:101:14 @@ -195,14 +187,17 @@ error[E0624]: associated type `A` is private --> $DIR/item-privacy.rs:119:12 | LL | type A = u8; - | ------ associated type defined here + | ------ the associated type is defined here ... LL | let _: T::A; | ^^^^ private associated type -error: associated type `A` is private +error[E0624]: associated type `A` is private --> $DIR/item-privacy.rs:128:9 | +LL | type A = u8; + | ------ the associated type is defined here +... LL | A = u8, | ^^^^^^ private associated type diff --git a/tests/ui/traits/method-on-unbounded-type-param.stderr b/tests/ui/traits/method-on-unbounded-type-param.stderr index 4d968e7bee10..0b1f83a9de92 100644 --- a/tests/ui/traits/method-on-unbounded-type-param.stderr +++ b/tests/ui/traits/method-on-unbounded-type-param.stderr @@ -76,8 +76,8 @@ LL | x.cmp(&x); which is required by `&mut dyn T: Iterator` = help: items from traits can only be used if the trait is implemented and in scope = note: the following traits define an item `cmp`, perhaps you need to implement one of them: - candidate #1: `Iterator` - candidate #2: `Ord` + candidate #1: `Ord` + = note: the trait `Iterator` defines an item `cmp`, but is explicitly unimplemented error: aborting due to 4 previous errors diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs index 2607f047024d..35757f2339db 100644 --- a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs +++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs @@ -18,3 +18,4 @@ fn weird1() -> impl !Sized + Sized {} //~^ ERROR type mismatch resolving `impl !Sized + Sized == ()` fn weird2() -> impl !Sized {} //~^ ERROR type mismatch resolving `impl !Sized == ()` +//~| ERROR the size for values of type `impl !Sized` cannot be known at compilation time diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr index ceaf42431fec..3dd2b27b55b6 100644 --- a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr +++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr @@ -16,6 +16,15 @@ error[E0271]: type mismatch resolving `impl !Sized == ()` LL | fn weird2() -> impl !Sized {} | ^^^^^^^^^^^ types differ +error[E0277]: the size for values of type `impl !Sized` cannot be known at compilation time + --> $DIR/opaque-type-unsatisfied-bound.rs:19:16 + | +LL | fn weird2() -> impl !Sized {} + | ^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `impl !Sized` + = note: the return type of a function must have a statically known size + error[E0277]: the trait bound `impl !Trait: Trait` is not satisfied --> $DIR/opaque-type-unsatisfied-bound.rs:12:13 | @@ -30,7 +39,7 @@ note: required by a bound in `consume` LL | fn consume(_: impl Trait) {} | ^^^^^ required by this bound in `consume` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0271, E0277. For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/auto-with-drop_tracking_mir.fail.stderr b/tests/ui/traits/next-solver/auto-with-drop_tracking_mir.fail.stderr index 562d7ccf9fec..e0b23bd8110a 100644 --- a/tests/ui/traits/next-solver/auto-with-drop_tracking_mir.fail.stderr +++ b/tests/ui/traits/next-solver/auto-with-drop_tracking_mir.fail.stderr @@ -4,7 +4,7 @@ error: future cannot be sent between threads safely LL | is_send(foo()); | ^^^^^ future returned by `foo` is not `Send` | - = help: the trait `Sync` is not implemented for `impl Future`, which is required by `impl Future: Send` + = help: the trait `Sync` is not implemented for `NotSync`, which is required by `impl Future: Send` note: future is not `Send` as this value is used across an await --> $DIR/auto-with-drop_tracking_mir.rs:16:11 | diff --git a/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr index 170f2c7d34c4..e0cbee88aa10 100644 --- a/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr +++ b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr @@ -1,8 +1,8 @@ -error[E0284]: type annotations needed: cannot satisfy `the constant `{ || {} }` can be evaluated` +error[E0284]: type annotations needed: cannot normalize `X::{constant#0}` --> $DIR/const-region-infer-to-static-in-binder.rs:4:10 | LL | struct X; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `{ || {} }` can be evaluated` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `X::{constant#0}` error: using function pointers as const generic parameters is forbidden --> $DIR/const-region-infer-to-static-in-binder.rs:4:20 diff --git a/tests/ui/traits/next-solver/coroutine.fail.stderr b/tests/ui/traits/next-solver/coroutine.fail.stderr index e880d05a4dd3..8c263e8644bd 100644 --- a/tests/ui/traits/next-solver/coroutine.fail.stderr +++ b/tests/ui/traits/next-solver/coroutine.fail.stderr @@ -6,8 +6,6 @@ LL | needs_coroutine( LL | #[coroutine] LL | / || { LL | | -LL | | -LL | | LL | | yield (); LL | | }, | |_________^ the trait `Coroutine` is not implemented for `{coroutine@$DIR/coroutine.rs:20:9: 20:11}` @@ -18,47 +16,6 @@ note: required by a bound in `needs_coroutine` LL | fn needs_coroutine(_: impl Coroutine) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `needs_coroutine` -error[E0271]: type mismatch resolving `<{coroutine@$DIR/coroutine.rs:20:9: 20:11} as Coroutine>::Yield == B` - --> $DIR/coroutine.rs:20:9 - | -LL | needs_coroutine( - | --------------- required by a bound introduced by this call -LL | #[coroutine] -LL | / || { -LL | | -LL | | -LL | | -LL | | yield (); -LL | | }, - | |_________^ types differ - | -note: required by a bound in `needs_coroutine` - --> $DIR/coroutine.rs:14:41 - | -LL | fn needs_coroutine(_: impl Coroutine) {} - | ^^^^^^^^^ required by this bound in `needs_coroutine` +error: aborting due to 1 previous error -error[E0271]: type mismatch resolving `<{coroutine@$DIR/coroutine.rs:20:9: 20:11} as Coroutine>::Return == C` - --> $DIR/coroutine.rs:20:9 - | -LL | needs_coroutine( - | --------------- required by a bound introduced by this call -LL | #[coroutine] -LL | / || { -LL | | -LL | | -LL | | -LL | | yield (); -LL | | }, - | |_________^ types differ - | -note: required by a bound in `needs_coroutine` - --> $DIR/coroutine.rs:14:52 - | -LL | fn needs_coroutine(_: impl Coroutine) {} - | ^^^^^^^^^^ required by this bound in `needs_coroutine` - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0271, E0277. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/coroutine.rs b/tests/ui/traits/next-solver/coroutine.rs index 1882a62cf29c..bdc34842f6d5 100644 --- a/tests/ui/traits/next-solver/coroutine.rs +++ b/tests/ui/traits/next-solver/coroutine.rs @@ -19,8 +19,6 @@ fn main() { #[coroutine] || { //[fail]~^ ERROR Coroutine` is not satisfied - //[fail]~| ERROR as Coroutine>::Yield == B` - //[fail]~| ERROR as Coroutine>::Return == C` yield (); }, ); diff --git a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.rs b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.rs index 7eea81ce03c6..920f8add5079 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.rs +++ b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.rs @@ -1,3 +1,4 @@ +//@ revisions: with without //@ compile-flags: -Znext-solver #![feature(rustc_attrs)] @@ -56,6 +57,7 @@ where X: IncompleteGuidance, X: IncompleteGuidance, { + #[cfg(with)] impls_trait::, _, _, _>(); // entering the cycle from `B` works // entering the cycle from `A` fails, but would work if we were to use the cache diff --git a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.stderr b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.with.stderr similarity index 87% rename from tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.stderr rename to tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.with.stderr index ffa3f29e4bd6..a81229e5e355 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.stderr +++ b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.with.stderr @@ -1,12 +1,12 @@ error[E0277]: the trait bound `A: Trait<_, _, _>` is not satisfied - --> $DIR/incompleteness-unstable-result.rs:63:19 + --> $DIR/incompleteness-unstable-result.rs:65:19 | LL | impls_trait::, _, _, _>(); | ^^^^ the trait `Trait<_, _, _>` is not implemented for `A`, which is required by `A: Trait<_, _, _>` | = help: the trait `Trait` is implemented for `A` note: required for `A` to implement `Trait<_, _, _>` - --> $DIR/incompleteness-unstable-result.rs:32:50 + --> $DIR/incompleteness-unstable-result.rs:33:50 | LL | impl Trait for A | ^^^^^^^^^^^^^^ ^^^^ @@ -16,7 +16,7 @@ LL | A: Trait, = note: 8 redundant requirements hidden = note: required for `A` to implement `Trait<_, _, _>` note: required by a bound in `impls_trait` - --> $DIR/incompleteness-unstable-result.rs:51:28 + --> $DIR/incompleteness-unstable-result.rs:52:28 | LL | fn impls_trait, U: ?Sized, V: ?Sized, D: ?Sized>() {} | ^^^^^^^^^^^^^^ required by this bound in `impls_trait` diff --git a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.without.stderr b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.without.stderr new file mode 100644 index 000000000000..a81229e5e355 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.without.stderr @@ -0,0 +1,26 @@ +error[E0277]: the trait bound `A: Trait<_, _, _>` is not satisfied + --> $DIR/incompleteness-unstable-result.rs:65:19 + | +LL | impls_trait::, _, _, _>(); + | ^^^^ the trait `Trait<_, _, _>` is not implemented for `A`, which is required by `A: Trait<_, _, _>` + | + = help: the trait `Trait` is implemented for `A` +note: required for `A` to implement `Trait<_, _, _>` + --> $DIR/incompleteness-unstable-result.rs:33:50 + | +LL | impl Trait for A + | ^^^^^^^^^^^^^^ ^^^^ +... +LL | A: Trait, + | -------------- unsatisfied trait bound introduced here + = note: 8 redundant requirements hidden + = note: required for `A` to implement `Trait<_, _, _>` +note: required by a bound in `impls_trait` + --> $DIR/incompleteness-unstable-result.rs:52:28 + | +LL | fn impls_trait, U: ?Sized, V: ?Sized, D: ?Sized>() {} + | ^^^^^^^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs new file mode 100644 index 000000000000..5c13a871a7b8 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs @@ -0,0 +1,89 @@ +//@ compile-flags: -Znext-solver + +// A regression test for #125269. We previously ended up +// recursively proving `&<_ as SpeciesPackedElem>::Assoc: Typed` +// for all aliases which ended up causing exponential blowup. +// +// This has been fixed by eagerly normalizing the associated +// type before computing the nested goals, resulting in an +// immediate inductive cycle. + +pub trait Typed {} + +pub struct SpeciesCases(E); + +pub trait SpeciesPackedElim { + type Ogre; + type Cyclops; + type Wendigo; + type Cavetroll; + type Mountaintroll; + type Swamptroll; + type Dullahan; + type Werewolf; + type Occultsaurok; + type Mightysaurok; + type Slysaurok; + type Mindflayer; + type Minotaur; + type Tidalwarrior; + type Yeti; + type Harvester; + type Blueoni; + type Redoni; + type Cultistwarlord; + type Cultistwarlock; + type Huskbrute; + type Tursus; + type Gigasfrost; + type AdletElder; + type SeaBishop; + type HaniwaGeneral; + type TerracottaBesieger; + type TerracottaDemolisher; + type TerracottaPunisher; + type TerracottaPursuer; + type Cursekeeper; +} + +impl<'b, E: SpeciesPackedElim> Typed for &'b SpeciesCases +where + &'b E::Ogre: Typed, + &'b E::Cyclops: Typed, + &'b E::Wendigo: Typed, + &'b E::Cavetroll: Typed, + &'b E::Mountaintroll: Typed, + &'b E::Swamptroll: Typed, + &'b E::Dullahan: Typed, + &'b E::Werewolf: Typed, + &'b E::Occultsaurok: Typed, + &'b E::Mightysaurok: Typed, + &'b E::Slysaurok: Typed, + &'b E::Mindflayer: Typed, + &'b E::Minotaur: Typed, + &'b E::Tidalwarrior: Typed, + &'b E::Yeti: Typed, + &'b E::Harvester: Typed, + &'b E::Blueoni: Typed, + &'b E::Redoni: Typed, + &'b E::Cultistwarlord: Typed, + &'b E::Cultistwarlock: Typed, + &'b E::Huskbrute: Typed, + &'b E::Tursus: Typed, + &'b E::Gigasfrost: Typed, + &'b E::AdletElder: Typed, + &'b E::SeaBishop: Typed, + &'b E::HaniwaGeneral: Typed, + &'b E::TerracottaBesieger: Typed, + &'b E::TerracottaDemolisher: Typed, + &'b E::TerracottaPunisher: Typed, + &'b E::TerracottaPursuer: Typed, + &'b E::Cursekeeper: Typed, +{} + +fn foo() {} + +fn main() { + foo::<&_>(); + //~^ ERROR overflow evaluating the requirement `&_: Typed` +} diff --git a/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.stderr b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.stderr new file mode 100644 index 000000000000..d350eb0f7795 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.stderr @@ -0,0 +1,15 @@ +error[E0275]: overflow evaluating the requirement `&_: Typed` + --> $DIR/cycle-modulo-ambig-aliases.rs:87:11 + | +LL | foo::<&_>(); + | ^^ + | +note: required by a bound in `foo` + --> $DIR/cycle-modulo-ambig-aliases.rs:84:11 + | +LL | fn foo() {} + | ^^^^^ required by this bound in `foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/diagnostics/point-at-failing-nested.stderr b/tests/ui/traits/next-solver/diagnostics/point-at-failing-nested.stderr index 6bf4e3cb5349..9a18a58debd2 100644 --- a/tests/ui/traits/next-solver/diagnostics/point-at-failing-nested.stderr +++ b/tests/ui/traits/next-solver/diagnostics/point-at-failing-nested.stderr @@ -4,6 +4,11 @@ error[E0277]: the trait bound `(): Foo` is not satisfied LL | needs_foo::<()>(); | ^^ the trait `Bar` is not implemented for `()`, which is required by `(): Foo` | +help: this trait has no implementations, consider adding one + --> $DIR/point-at-failing-nested.rs:4:1 + | +LL | trait Bar {} + | ^^^^^^^^^ note: required for `()` to implement `Foo` --> $DIR/point-at-failing-nested.rs:9:12 | diff --git a/tests/ui/traits/next-solver/diagnostics/projection-trait-ref.rs b/tests/ui/traits/next-solver/diagnostics/projection-trait-ref.rs new file mode 100644 index 000000000000..a3ab7bf03e55 --- /dev/null +++ b/tests/ui/traits/next-solver/diagnostics/projection-trait-ref.rs @@ -0,0 +1,17 @@ +//@ compile-flags: -Znext-solver + +trait Trait { + type Assoc; +} + +fn test_poly() { + let x: ::Assoc = (); + //~^ ERROR the trait bound `T: Trait` is not satisfied +} + +fn test() { + let x: ::Assoc = (); + //~^ ERROR the trait bound `i32: Trait` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/diagnostics/projection-trait-ref.stderr b/tests/ui/traits/next-solver/diagnostics/projection-trait-ref.stderr new file mode 100644 index 000000000000..cd8d8b3ffcd3 --- /dev/null +++ b/tests/ui/traits/next-solver/diagnostics/projection-trait-ref.stderr @@ -0,0 +1,26 @@ +error[E0277]: the trait bound `T: Trait` is not satisfied + --> $DIR/projection-trait-ref.rs:8:12 + | +LL | let x: ::Assoc = (); + | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | fn test_poly() { + | +++++++ + +error[E0277]: the trait bound `i32: Trait` is not satisfied + --> $DIR/projection-trait-ref.rs:13:12 + | +LL | let x: ::Assoc = (); + | ^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `i32` + | +help: this trait has no implementations, consider adding one + --> $DIR/projection-trait-ref.rs:3:1 + | +LL | trait Trait { + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.stderr b/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.stderr index 77a0cc497541..ab1d4a56c023 100644 --- a/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.stderr +++ b/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.stderr @@ -6,6 +6,11 @@ LL | needs_foo(()); | | | required by a bound introduced by this call | +help: this trait has no implementations, consider adding one + --> $DIR/where-clause-doesnt-apply.rs:2:1 + | +LL | trait Bar {} + | ^^^^^^^^^ note: required for `()` to implement `Foo` --> $DIR/where-clause-doesnt-apply.rs:4:9 | diff --git a/tests/ui/traits/next-solver/dyn-any-dont-prefer-impl.rs b/tests/ui/traits/next-solver/dyn-any-dont-prefer-impl.rs index a63fe729fd68..1554d74f2145 100644 --- a/tests/ui/traits/next-solver/dyn-any-dont-prefer-impl.rs +++ b/tests/ui/traits/next-solver/dyn-any-dont-prefer-impl.rs @@ -1,5 +1,5 @@ //@ compile-flags: -Znext-solver -//@ check-pass +//@ run-pass // Test that selection prefers the builtin trait object impl for `Any` // instead of the user defined impl. Both impls apply to the trait diff --git a/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr b/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr index 3ef0afa38bff..0c72fc25dca7 100644 --- a/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr +++ b/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr @@ -2,13 +2,8 @@ error[E0271]: type mismatch resolving `::Assoc == i32` --> $DIR/param-candidate-shadows-project.rs:27:19 | LL | require_bar::(); - | ^ type mismatch resolving `::Assoc == i32` + | ^ types differ | -note: types differ - --> $DIR/param-candidate-shadows-project.rs:10:18 - | -LL | type Assoc = i32; - | ^^^ note: required for `T` to implement `Bar` --> $DIR/param-candidate-shadows-project.rs:13:9 | diff --git a/tests/ui/traits/next-solver/fn-trait.rs b/tests/ui/traits/next-solver/fn-trait.rs index 6d6ae9260b0f..7994dd3052c4 100644 --- a/tests/ui/traits/next-solver/fn-trait.rs +++ b/tests/ui/traits/next-solver/fn-trait.rs @@ -19,14 +19,10 @@ fn main() { require_fn(f as fn() -> i32); require_fn(f as unsafe fn() -> i32); //~^ ERROR: expected a `Fn()` closure, found `unsafe fn() -> i32` - //~| ERROR: type mismatch resolving ` i32 as FnOnce<()>>::Output == i32` require_fn(g); //~^ ERROR: expected a `Fn()` closure, found `extern "C" fn() -> i32 {g}` - //~| ERROR: type mismatch resolving ` i32 {g} as FnOnce<()>>::Output == i32` require_fn(g as extern "C" fn() -> i32); //~^ ERROR: expected a `Fn()` closure, found `extern "C" fn() -> i32` - //~| ERROR: type mismatch resolving ` i32 as FnOnce<()>>::Output == i32` require_fn(h); //~^ ERROR: expected a `Fn()` closure, found `unsafe fn() -> i32 {h}` - //~| ERROR: type mismatch resolving ` i32 {h} as FnOnce<()>>::Output == i32` } diff --git a/tests/ui/traits/next-solver/fn-trait.stderr b/tests/ui/traits/next-solver/fn-trait.stderr index e33487235e66..00243fd90595 100644 --- a/tests/ui/traits/next-solver/fn-trait.stderr +++ b/tests/ui/traits/next-solver/fn-trait.stderr @@ -6,7 +6,7 @@ LL | require_fn(f as unsafe fn() -> i32); | | | required by a bound introduced by this call | - = help: the trait `Fn<()>` is not implemented for `unsafe fn() -> i32` + = help: the trait `Fn()` is not implemented for `unsafe fn() -> i32` = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() -> i32` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `require_fn` @@ -15,29 +15,15 @@ note: required by a bound in `require_fn` LL | fn require_fn(_: impl Fn() -> i32) {} | ^^^^^^^^^^^ required by this bound in `require_fn` -error[E0271]: type mismatch resolving ` i32 as FnOnce<()>>::Output == i32` - --> $DIR/fn-trait.rs:20:16 - | -LL | require_fn(f as unsafe fn() -> i32); - | ---------- ^^^^^^^^^^^^^^^^^^^^^^^ types differ - | | - | required by a bound introduced by this call - | -note: required by a bound in `require_fn` - --> $DIR/fn-trait.rs:3:31 - | -LL | fn require_fn(_: impl Fn() -> i32) {} - | ^^^ required by this bound in `require_fn` - error[E0277]: expected a `Fn()` closure, found `extern "C" fn() -> i32 {g}` - --> $DIR/fn-trait.rs:23:16 + --> $DIR/fn-trait.rs:22:16 | LL | require_fn(g); | ---------- ^ expected an `Fn()` closure, found `extern "C" fn() -> i32 {g}` | | | required by a bound introduced by this call | - = help: the trait `Fn<()>` is not implemented for fn item `extern "C" fn() -> i32 {g}` + = help: the trait `Fn()` is not implemented for fn item `extern "C" fn() -> i32 {g}` = note: wrap the `extern "C" fn() -> i32 {g}` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `require_fn` --> $DIR/fn-trait.rs:3:23 @@ -45,29 +31,15 @@ note: required by a bound in `require_fn` LL | fn require_fn(_: impl Fn() -> i32) {} | ^^^^^^^^^^^ required by this bound in `require_fn` -error[E0271]: type mismatch resolving ` i32 {g} as FnOnce<()>>::Output == i32` - --> $DIR/fn-trait.rs:23:16 - | -LL | require_fn(g); - | ---------- ^ types differ - | | - | required by a bound introduced by this call - | -note: required by a bound in `require_fn` - --> $DIR/fn-trait.rs:3:31 - | -LL | fn require_fn(_: impl Fn() -> i32) {} - | ^^^ required by this bound in `require_fn` - error[E0277]: expected a `Fn()` closure, found `extern "C" fn() -> i32` - --> $DIR/fn-trait.rs:26:16 + --> $DIR/fn-trait.rs:24:16 | LL | require_fn(g as extern "C" fn() -> i32); | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an `Fn()` closure, found `extern "C" fn() -> i32` | | | required by a bound introduced by this call | - = help: the trait `Fn<()>` is not implemented for `extern "C" fn() -> i32` + = help: the trait `Fn()` is not implemented for `extern "C" fn() -> i32` = note: wrap the `extern "C" fn() -> i32` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `require_fn` --> $DIR/fn-trait.rs:3:23 @@ -75,29 +47,15 @@ note: required by a bound in `require_fn` LL | fn require_fn(_: impl Fn() -> i32) {} | ^^^^^^^^^^^ required by this bound in `require_fn` -error[E0271]: type mismatch resolving ` i32 as FnOnce<()>>::Output == i32` - --> $DIR/fn-trait.rs:26:16 - | -LL | require_fn(g as extern "C" fn() -> i32); - | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ - | | - | required by a bound introduced by this call - | -note: required by a bound in `require_fn` - --> $DIR/fn-trait.rs:3:31 - | -LL | fn require_fn(_: impl Fn() -> i32) {} - | ^^^ required by this bound in `require_fn` - error[E0277]: expected a `Fn()` closure, found `unsafe fn() -> i32 {h}` - --> $DIR/fn-trait.rs:29:16 + --> $DIR/fn-trait.rs:26:16 | LL | require_fn(h); | ---------- ^ call the function in a closure: `|| unsafe { /* code */ }` | | | required by a bound introduced by this call | - = help: the trait `Fn<()>` is not implemented for fn item `unsafe fn() -> i32 {h}` + = help: the trait `Fn()` is not implemented for fn item `unsafe fn() -> i32 {h}` = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() -> i32 {h}` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `require_fn` @@ -106,21 +64,6 @@ note: required by a bound in `require_fn` LL | fn require_fn(_: impl Fn() -> i32) {} | ^^^^^^^^^^^ required by this bound in `require_fn` -error[E0271]: type mismatch resolving ` i32 {h} as FnOnce<()>>::Output == i32` - --> $DIR/fn-trait.rs:29:16 - | -LL | require_fn(h); - | ---------- ^ types differ - | | - | required by a bound introduced by this call - | -note: required by a bound in `require_fn` - --> $DIR/fn-trait.rs:3:31 - | -LL | fn require_fn(_: impl Fn() -> i32) {} - | ^^^ required by this bound in `require_fn` +error: aborting due to 4 previous errors -error: aborting due to 8 previous errors - -Some errors have detailed explanations: E0271, E0277. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/generalize/bivariant-alias.rs b/tests/ui/traits/next-solver/generalize/bivariant-alias.rs new file mode 100644 index 000000000000..b03d547838a0 --- /dev/null +++ b/tests/ui/traits/next-solver/generalize/bivariant-alias.rs @@ -0,0 +1,20 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// When generalizing an alias in a bivariant context, we have to set +// `has_unconstrained_ty_var` as we may otherwise never check for +// well-formedness of the generalized type, causing us to error due +// to ambiguity. +trait Trait { + type Assoc; +} + +struct BivariantArg>(T); + +fn generalize(input: BivariantArg) { + let _generalized = input; +} + +pub fn main() {} diff --git a/tests/ui/traits/next-solver/issue-118950-root-region.stderr b/tests/ui/traits/next-solver/issue-118950-root-region.stderr index 2e0566cad08e..17da1f524796 100644 --- a/tests/ui/traits/next-solver/issue-118950-root-region.stderr +++ b/tests/ui/traits/next-solver/issue-118950-root-region.stderr @@ -25,10 +25,10 @@ help: this trait has no implementations, consider adding one LL | trait ToUnit<'a> { | ^^^^^^^^^^^^^^^^ -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } -WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } + WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) } error[E0119]: conflicting implementations of trait `Overlap` for type `fn(_)` --> $DIR/issue-118950-root-region.rs:19:1 | diff --git a/tests/ui/traits/next-solver/normalize/ambig-goal-infer-in-type-oulives.rs b/tests/ui/traits/next-solver/normalize/ambig-goal-infer-in-type-oulives.rs new file mode 100644 index 000000000000..18dc34f7cc43 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/ambig-goal-infer-in-type-oulives.rs @@ -0,0 +1,29 @@ +//@ check-pass +//@ compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicitly enabled) + +// Regression test for an ICE when trying to bootstrap rustc +// with #125343. An ambiguous goal returned a `TypeOutlives` +// constraint referencing an inference variable. This inference +// variable was created inside of the goal, causing it to be +// unconstrained in the caller. This then caused an ICE in MIR +// borrowck. + +struct Foo(T); +trait Extend { + fn extend>(iter: I); +} + +impl Extend for Foo { + fn extend>(_: I) { + todo!() + } +} + +impl<'a, T: 'a + Copy> Extend<&'a T> for Foo { + fn extend>(iter: I) { + >::extend(iter.into_iter().copied()) + } +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.rs b/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.rs index 12ea1bf142ab..5239474ff44b 100644 --- a/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.rs +++ b/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.rs @@ -24,7 +24,7 @@ fn needs_bar() {} fn foo + Foo>() { needs_bar::(); - //~^ ERROR type annotations needed: cannot satisfy `::Assoc == i32` + //~^ ERROR type annotations needed: cannot normalize } fn main() {} diff --git a/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.stderr b/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.stderr index 21f3fbfeb872..270ad8517177 100644 --- a/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.stderr +++ b/tests/ui/traits/next-solver/normalize/two-projection-param-candidates-are-ambiguous.stderr @@ -1,8 +1,8 @@ -error[E0284]: type annotations needed: cannot satisfy `::Assoc == i32` +error[E0284]: type annotations needed: cannot normalize `::Assoc` --> $DIR/two-projection-param-candidates-are-ambiguous.rs:26:17 | LL | needs_bar::(); - | ^ cannot satisfy `::Assoc == i32` + | ^ cannot normalize `::Assoc` | note: required for `T` to implement `Bar` --> $DIR/two-projection-param-candidates-are-ambiguous.rs:21:9 diff --git a/tests/ui/traits/next-solver/specialization-transmute.rs b/tests/ui/traits/next-solver/specialization-transmute.rs index caa3bfc552eb..9e31fed9b180 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.rs +++ b/tests/ui/traits/next-solver/specialization-transmute.rs @@ -10,7 +10,7 @@ trait Default { } impl Default for T { - default type Id = T; //~ ERROR type annotations needed + default type Id = T; // This will be fixed by #111994 fn intu(&self) -> &Self::Id { //~^ ERROR type annotations needed diff --git a/tests/ui/traits/next-solver/specialization-transmute.stderr b/tests/ui/traits/next-solver/specialization-transmute.stderr index 76ae08fdb7a7..b96bfab927d2 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.stderr +++ b/tests/ui/traits/next-solver/specialization-transmute.stderr @@ -10,23 +10,23 @@ LL | #![feature(specialization)] error: cannot normalize `::Id: '_` -error[E0284]: type annotations needed: cannot satisfy `::Id == _` +error[E0284]: type annotations needed: cannot normalize `::Id` --> $DIR/specialization-transmute.rs:15:23 | LL | fn intu(&self) -> &Self::Id { - | ^^^^^^^^^ cannot satisfy `::Id == _` + | ^^^^^^^^^ cannot normalize `::Id` -error[E0284]: type annotations needed: cannot satisfy `T <: ::Id` +error[E0284]: type annotations needed: cannot satisfy `::Id normalizes-to T` --> $DIR/specialization-transmute.rs:17:9 | LL | self - | ^^^^ cannot satisfy `T <: ::Id` + | ^^^^ cannot satisfy `::Id normalizes-to T` -error[E0284]: type annotations needed: cannot satisfy `::Id == Option>` +error[E0284]: type annotations needed: cannot satisfy `::Id normalizes-to Option>` --> $DIR/specialization-transmute.rs:28:13 | LL | let s = transmute::>>(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `::Id == Option>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `::Id normalizes-to Option>` | note: required by a bound in `transmute` --> $DIR/specialization-transmute.rs:21:25 @@ -34,13 +34,6 @@ note: required by a bound in `transmute` LL | fn transmute, U: Copy>(t: T) -> U { | ^^^^^^ required by this bound in `transmute` -error[E0282]: type annotations needed - --> $DIR/specialization-transmute.rs:13:23 - | -LL | default type Id = T; - | ^ cannot infer type for associated type `::Id` +error: aborting due to 4 previous errors; 1 warning emitted -error: aborting due to 5 previous errors; 1 warning emitted - -Some errors have detailed explanations: E0282, E0284. -For more information about an error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/traits/next-solver/specialization-unconstrained.rs b/tests/ui/traits/next-solver/specialization-unconstrained.rs index f4046fba20b2..2bbe78401107 100644 --- a/tests/ui/traits/next-solver/specialization-unconstrained.rs +++ b/tests/ui/traits/next-solver/specialization-unconstrained.rs @@ -11,12 +11,12 @@ trait Default { } impl Default for T { - default type Id = T; //~ ERROR type annotations needed + default type Id = T; } fn test, U>() {} fn main() { test::(); - //~^ ERROR cannot satisfy `::Id == ()` + //~^ ERROR cannot satisfy `::Id normalizes-to ()` } diff --git a/tests/ui/traits/next-solver/specialization-unconstrained.stderr b/tests/ui/traits/next-solver/specialization-unconstrained.stderr index 68232aacc0c5..e1d785b554bd 100644 --- a/tests/ui/traits/next-solver/specialization-unconstrained.stderr +++ b/tests/ui/traits/next-solver/specialization-unconstrained.stderr @@ -8,11 +8,11 @@ LL | #![feature(specialization)] = help: consider using `min_specialization` instead, which is more stable and complete = note: `#[warn(incomplete_features)]` on by default -error[E0284]: type annotations needed: cannot satisfy `::Id == ()` +error[E0284]: type annotations needed: cannot satisfy `::Id normalizes-to ()` --> $DIR/specialization-unconstrained.rs:20:5 | LL | test::(); - | ^^^^^^^^^^^^^^^^^ cannot satisfy `::Id == ()` + | ^^^^^^^^^^^^^^^^^ cannot satisfy `::Id normalizes-to ()` | note: required by a bound in `test` --> $DIR/specialization-unconstrained.rs:17:20 @@ -20,13 +20,6 @@ note: required by a bound in `test` LL | fn test, U>() {} | ^^^^^^ required by this bound in `test` -error[E0282]: type annotations needed - --> $DIR/specialization-unconstrained.rs:14:22 - | -LL | default type Id = T; - | ^ cannot infer type for associated type `::Id` +error: aborting due to 1 previous error; 1 warning emitted -error: aborting due to 2 previous errors; 1 warning emitted - -Some errors have detailed explanations: E0282, E0284. -For more information about an error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/traits/next-solver/typeck/index-of-projection.rs b/tests/ui/traits/next-solver/typeck/index-of-projection.rs new file mode 100644 index 000000000000..5023be0bb148 --- /dev/null +++ b/tests/ui/traits/next-solver/typeck/index-of-projection.rs @@ -0,0 +1,13 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// Fixes a regression in `rustc_attr` where we weren't normalizing the +// output type of a index operator performing a `Ty::builtin_index` call, +// leading to an ICE. + +fn main() { + let mut vec = [1, 2, 3]; + let x = || { + let [..] = &vec[..]; + }; +} diff --git a/tests/ui/traits/next-solver/unevaluated-const-impl-trait-ref.fails.stderr b/tests/ui/traits/next-solver/unevaluated-const-impl-trait-ref.fails.stderr index 4be90c702a03..efdc9f8fc9f7 100644 --- a/tests/ui/traits/next-solver/unevaluated-const-impl-trait-ref.fails.stderr +++ b/tests/ui/traits/next-solver/unevaluated-const-impl-trait-ref.fails.stderr @@ -5,8 +5,8 @@ LL | needs::<1>(); | ^ the trait `Trait<1>` is not implemented for `()` | = help: the following other types implement trait `Trait`: - <() as Trait<0>> - <() as Trait<2>> + `()` implements `Trait<0>` + `()` implements `Trait<2>` note: required by a bound in `needs` --> $DIR/unevaluated-const-impl-trait-ref.rs:10:38 | diff --git a/tests/ui/traits/next-solver/unsound-region-obligation.rs b/tests/ui/traits/next-solver/unsound-region-obligation.rs index 32a510d16543..733be2043548 100644 --- a/tests/ui/traits/next-solver/unsound-region-obligation.rs +++ b/tests/ui/traits/next-solver/unsound-region-obligation.rs @@ -1,4 +1,4 @@ -//~ ERROR the type `<() as StaticTy>::Item<'a>` does not fulfill the required lifetime +//~ ERROR the type `&'a ()` does not fulfill the required lifetime //@ compile-flags: -Znext-solver // Regression test for rust-lang/trait-system-refactor-initiative#59 diff --git a/tests/ui/traits/next-solver/unsound-region-obligation.stderr b/tests/ui/traits/next-solver/unsound-region-obligation.stderr index 518de7ea3e02..fe96a184f430 100644 --- a/tests/ui/traits/next-solver/unsound-region-obligation.stderr +++ b/tests/ui/traits/next-solver/unsound-region-obligation.stderr @@ -1,4 +1,4 @@ -error[E0477]: the type `<() as StaticTy>::Item<'a>` does not fulfill the required lifetime +error[E0477]: the type `&'a ()` does not fulfill the required lifetime | = note: type must satisfy the static lifetime diff --git a/tests/ui/traits/object/generics.rs b/tests/ui/traits/object/generics.rs index 462b0bc5bb77..0ae562c0d30a 100644 --- a/tests/ui/traits/object/generics.rs +++ b/tests/ui/traits/object/generics.rs @@ -7,6 +7,7 @@ pub trait Trait2 { fn doit(&self) -> A; } +#[allow(dead_code)] pub struct Impl { m1: marker::PhantomData<(A1,A2,A3)>, /* diff --git a/tests/ui/traits/question-mark-result-err-mismatch.stderr b/tests/ui/traits/question-mark-result-err-mismatch.stderr index 8a6c5fc10c5f..66276bcbe3bb 100644 --- a/tests/ui/traits/question-mark-result-err-mismatch.stderr +++ b/tests/ui/traits/question-mark-result-err-mismatch.stderr @@ -31,12 +31,12 @@ LL | .map_err(|_| ())?; | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the following other types implement trait `From`: - > - > - > - >> - >> - > + `String` implements `From<&String>` + `String` implements `From<&mut str>` + `String` implements `From<&str>` + `String` implements `From>` + `String` implements `From>` + `String` implements `From` = note: required for `Result<(), String>` to implement `FromResidual>` error[E0277]: `?` couldn't convert the error to `String` diff --git a/tests/ui/traits/suggest-dereferences/root-obligation.stderr b/tests/ui/traits/suggest-dereferences/root-obligation.stderr index 56f95e207158..bbfbb98fba77 100644 --- a/tests/ui/traits/suggest-dereferences/root-obligation.stderr +++ b/tests/ui/traits/suggest-dereferences/root-obligation.stderr @@ -2,11 +2,11 @@ error[E0277]: the trait bound `&char: Pattern<'_>` is not satisfied --> $DIR/root-obligation.rs:6:38 | LL | .filter(|c| "aeiou".contains(c)) - | -------- ^ the trait `Fn<(char,)>` is not implemented for `&char`, which is required by `&char: Pattern<'_>` + | -------- ^ the trait `Fn(char)` is not implemented for `char`, which is required by `&char: Pattern<'_>` | | | required by a bound introduced by this call | - = note: required for `&char` to implement `FnOnce<(char,)>` + = note: required for `&char` to implement `FnOnce(char)` = note: required for `&char` to implement `Pattern<'_>` note: required by a bound in `core::str::::contains` --> $SRC_DIR/core/src/str/mod.rs:LL:COL diff --git a/tests/ui/traits/trait-upcasting/illegal-upcast-to-impl-opaque.rs b/tests/ui/traits/trait-upcasting/illegal-upcast-to-impl-opaque.rs new file mode 100644 index 000000000000..5820e49a4e52 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/illegal-upcast-to-impl-opaque.rs @@ -0,0 +1,28 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@[next] failure-status: 101 +//@[next] known-bug: unknown +//@[next] normalize-stderr-test "note: .*\n\n" -> "" +//@[next] normalize-stderr-test "thread 'rustc' panicked.*\n.*\n" -> "" +//@[next] normalize-stderr-test "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " +//@[next] normalize-stderr-test "delayed at .*" -> "" +//@[next] rustc-env:RUST_BACKTRACE=0 +//@ check-pass + +#![feature(trait_upcasting)] + +trait Super { + type Assoc; +} + +trait Sub: Super {} + +impl Super for T { + type Assoc = i32; +} + +fn illegal(x: &dyn Sub) -> &dyn Super { + x +} + +fn main() {} diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-4.rs b/tests/ui/traits/trait-upcasting/type-checking-test-4.rs index f40c48f0d125..01759ec7a93c 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-4.rs +++ b/tests/ui/traits/trait-upcasting/type-checking-test-4.rs @@ -11,6 +11,10 @@ fn test_correct(x: &dyn Foo<'static>) { let _ = x as &dyn Bar<'static, 'static>; } +fn test_correct2<'a>(x: &dyn Foo<'a>) { + let _ = x as &dyn Bar<'_, '_>; +} + fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) { let _ = x as &dyn Bar<'static, 'a>; // Error //~^ ERROR lifetime may not live long enough diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr index 8d506e5807ec..ccced5875778 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr +++ b/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:15:13 + --> $DIR/type-checking-test-4.rs:19:13 | LL | fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) { | -- lifetime `'a` defined here @@ -7,7 +7,7 @@ LL | let _ = x as &dyn Bar<'static, 'a>; // Error | ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:20:13 + --> $DIR/type-checking-test-4.rs:24:13 | LL | fn test_wrong2<'a>(x: &dyn Foo<'static>, y: &'a u32) { | -- lifetime `'a` defined here @@ -15,7 +15,7 @@ LL | let _ = x as &dyn Bar<'a, 'static>; // Error | ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:26:5 + --> $DIR/type-checking-test-4.rs:30:5 | LL | fn test_wrong3<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { | -- lifetime `'a` defined here @@ -24,7 +24,7 @@ LL | y.get_b() // ERROR | ^^^^^^^^^ returning this value requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:31:5 + --> $DIR/type-checking-test-4.rs:35:5 | LL | fn test_wrong4<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { | -- lifetime `'a` defined here @@ -32,7 +32,7 @@ LL | <_ as Bar>::get_b(x) // ERROR | ^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:36:5 + --> $DIR/type-checking-test-4.rs:40:5 | LL | fn test_wrong5<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { | -- lifetime `'a` defined here @@ -40,7 +40,7 @@ LL | <_ as Bar<'_, '_>>::get_b(x) // ERROR | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:44:5 + --> $DIR/type-checking-test-4.rs:48:5 | LL | fn test_wrong6<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { | -- lifetime `'a` defined here diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-opaques.rs b/tests/ui/traits/trait-upcasting/type-checking-test-opaques.rs new file mode 100644 index 000000000000..a3a1ce29465b --- /dev/null +++ b/tests/ui/traits/trait-upcasting/type-checking-test-opaques.rs @@ -0,0 +1,22 @@ +#![feature(trait_upcasting, type_alias_impl_trait)] + +//@ check-pass + +type Tait = impl Sized; + +trait Foo<'a>: Bar<'a, 'a, Tait> {} +trait Bar<'a, 'b, T> {} + +fn test_correct(x: &dyn Foo<'static>) { + let _ = x as &dyn Bar<'static, 'static, Tait>; +} + +fn test_correct2<'a>(x: &dyn Foo<'a>) { + let _ = x as &dyn Bar<'_, '_, Tait>; +} + +fn test_correct3<'a>(x: &dyn Foo<'a>, _: Tait) { + let _ = x as &dyn Bar<'_, '_, ()>; +} + +fn main() {} diff --git a/tests/ui/traits/trait-upcasting/upcast-defining-opaque.current.stderr b/tests/ui/traits/trait-upcasting/upcast-defining-opaque.current.stderr deleted file mode 100644 index a259abb28ae3..000000000000 --- a/tests/ui/traits/trait-upcasting/upcast-defining-opaque.current.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/upcast-defining-opaque.rs:21:5 - | -LL | type Foo = impl Sized; - | ---------- the found opaque type -LL | -LL | fn upcast(x: &dyn Sub) -> &dyn Super { - | ----------------------- expected `&dyn Super` because of return type -LL | x - | ^ expected trait `Super`, found trait `Sub` - | - = note: expected reference `&dyn Super` - found reference `&dyn Sub` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/trait-upcasting/upcast-defining-opaque.rs b/tests/ui/traits/trait-upcasting/upcast-defining-opaque.rs index cb1501a94a2a..07f1549e177f 100644 --- a/tests/ui/traits/trait-upcasting/upcast-defining-opaque.rs +++ b/tests/ui/traits/trait-upcasting/upcast-defining-opaque.rs @@ -1,7 +1,7 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver //@ ignore-compare-mode-next-solver (explicit revisions) -//@[next] check-pass +//@check-pass #![feature(trait_upcasting, type_alias_impl_trait)] @@ -18,7 +18,7 @@ impl Super for T { type Foo = impl Sized; fn upcast(x: &dyn Sub) -> &dyn Super { - x //[current]~ mismatched types + x } fn main() {} diff --git a/tests/ui/traits/vtable/vtable-vacant.rs b/tests/ui/traits/vtable/vtable-vacant.rs index 26de3f606210..b3c768157036 100644 --- a/tests/ui/traits/vtable/vtable-vacant.rs +++ b/tests/ui/traits/vtable/vtable-vacant.rs @@ -1,7 +1,6 @@ //@ build-fail #![feature(rustc_attrs)] #![feature(negative_impls)] -#![allow(where_clauses_object_safety)] // B --> A diff --git a/tests/ui/traits/vtable/vtable-vacant.stderr b/tests/ui/traits/vtable/vtable-vacant.stderr index 9ce1d0b76328..f6961ca010ed 100644 --- a/tests/ui/traits/vtable/vtable-vacant.stderr +++ b/tests/ui/traits/vtable/vtable-vacant.stderr @@ -7,7 +7,7 @@ error: vtable entries for ``: [ Method(::foo_b1), Vacant, ] - --> $DIR/vtable-vacant.rs:15:1 + --> $DIR/vtable-vacant.rs:14:1 | LL | trait B: A { | ^^^^^^^^^^ diff --git a/tests/ui/transmutability/enums/uninhabited_optimization.rs b/tests/ui/transmutability/enums/uninhabited_optimization.rs new file mode 100644 index 000000000000..c2d5b67ab2ce --- /dev/null +++ b/tests/ui/transmutability/enums/uninhabited_optimization.rs @@ -0,0 +1,32 @@ +//@ check-pass +//! Tests that we do not regress rust-lang/rust#125811 +#![feature(transmutability)] + +fn assert_transmutable() +where + (): std::mem::BikeshedIntrinsicFrom +{} + +enum Uninhabited {} + +enum SingleInhabited { + X, + Y(Uninhabited) +} + +enum SingleUninhabited { + X(Uninhabited), + Y(Uninhabited), +} + +enum MultipleUninhabited { + X(u8, Uninhabited), + Y(Uninhabited, u16), +} + +fn main() { + assert_transmutable::(); + assert_transmutable::(); + assert_transmutable::(); + assert_transmutable::(); +} diff --git a/tests/ui/transmutability/issue-101739-2.rs b/tests/ui/transmutability/issue-101739-2.rs index e2cec24aac14..1c0bd29d7079 100644 --- a/tests/ui/transmutability/issue-101739-2.rs +++ b/tests/ui/transmutability/issue-101739-2.rs @@ -15,7 +15,6 @@ mod assert { >() where Dst: BikeshedIntrinsicFrom< //~ ERROR trait takes at most 2 generic arguments but 5 generic arguments were supplied - //~^ ERROR: the constant `ASSUME_ALIGNMENT` is not of type `Assume` Src, ASSUME_ALIGNMENT, //~ ERROR: mismatched types ASSUME_LIFETIMES, diff --git a/tests/ui/transmutability/issue-101739-2.stderr b/tests/ui/transmutability/issue-101739-2.stderr index 639b44608924..38912696c18e 100644 --- a/tests/ui/transmutability/issue-101739-2.stderr +++ b/tests/ui/transmutability/issue-101739-2.stderr @@ -9,29 +9,13 @@ LL | | ASSUME_VALIDITY, LL | | ASSUME_VISIBILITY, | |_____________________________- help: remove these generic arguments -error: the constant `ASSUME_ALIGNMENT` is not of type `Assume` - --> $DIR/issue-101739-2.rs:17:14 - | -LL | Dst: BikeshedIntrinsicFrom< - | ______________^ -LL | | -LL | | Src, -LL | | ASSUME_ALIGNMENT, -... | -LL | | ASSUME_VISIBILITY, -LL | | >, - | |_________^ expected `Assume`, found `bool` - | -note: required by a bound in `BikeshedIntrinsicFrom` - --> $SRC_DIR/core/src/mem/transmutability.rs:LL:COL - error[E0308]: mismatched types - --> $DIR/issue-101739-2.rs:20:13 + --> $DIR/issue-101739-2.rs:19:13 | LL | ASSUME_ALIGNMENT, | ^^^^^^^^^^^^^^^^ expected `Assume`, found `bool` -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0107, E0308. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/transmutability/references/unsafecell.stderr b/tests/ui/transmutability/references/unsafecell.stderr index 651eb8ceb267..8bb323593554 100644 --- a/tests/ui/transmutability/references/unsafecell.stderr +++ b/tests/ui/transmutability/references/unsafecell.stderr @@ -2,7 +2,7 @@ error[E0277]: `&u8` cannot be safely transmuted into `&UnsafeCell` --> $DIR/unsafecell.rs:27:50 | LL | assert::is_maybe_transmutable::<&'static u8, &'static UnsafeCell>(); - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `&'static UnsafeCell` + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `UnsafeCell` | note: required by a bound in `is_maybe_transmutable` --> $DIR/unsafecell.rs:12:14 @@ -17,7 +17,7 @@ error[E0277]: `&UnsafeCell` cannot be safely transmuted into `&UnsafeCell $DIR/unsafecell.rs:29:62 | LL | assert::is_maybe_transmutable::<&'static UnsafeCell, &'static UnsafeCell>(); - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `&'static UnsafeCell` + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `UnsafeCell` | note: required by a bound in `is_maybe_transmutable` --> $DIR/unsafecell.rs:12:14 diff --git a/tests/ui/transmutability/uninhabited.rs b/tests/ui/transmutability/uninhabited.rs index b61b110f6a11..7524922c16a7 100644 --- a/tests/ui/transmutability/uninhabited.rs +++ b/tests/ui/transmutability/uninhabited.rs @@ -30,7 +30,7 @@ fn void() { } // Non-ZST uninhabited types are, nonetheless, uninhabited. -fn yawning_void() { +fn yawning_void_struct() { enum Void {} struct YawningVoid(Void, u128); @@ -49,6 +49,28 @@ fn yawning_void() { assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted } +// Non-ZST uninhabited types are, nonetheless, uninhabited. +fn yawning_void_enum() { + enum Void {} + + enum YawningVoid { + A(Void, u128), + } + + const _: () = { + assert!(std::mem::size_of::() == std::mem::size_of::()); + // Just to be sure the above constant actually evaluated: + assert!(false); //~ ERROR: evaluation of constant value failed + }; + + // This transmutation is vacuously acceptable; since one cannot construct a + // `Void`, unsoundness cannot directly arise from transmuting a void into + // anything else. + assert::is_maybe_transmutable::(); + + assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted +} + // References to uninhabited types are, logically, uninhabited, but for layout // purposes are not ZSTs, and aren't treated as uninhabited when they appear in // enum variants. diff --git a/tests/ui/transmutability/uninhabited.stderr b/tests/ui/transmutability/uninhabited.stderr index 60219b0f263c..88a98c798fc3 100644 --- a/tests/ui/transmutability/uninhabited.stderr +++ b/tests/ui/transmutability/uninhabited.stderr @@ -7,10 +7,18 @@ LL | assert!(false); = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/uninhabited.rs:65:9 + --> $DIR/uninhabited.rs:63:9 | LL | assert!(false); - | ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:65:9 + | ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:63:9 + | + = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/uninhabited.rs:87:9 + | +LL | assert!(false); + | ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:87:9 | = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -36,11 +44,33 @@ LL | | } LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` -error[E0277]: `()` cannot be safely transmuted into `yawning_void::Void` +error[E0277]: `()` cannot be safely transmuted into `yawning_void_struct::Void` --> $DIR/uninhabited.rs:49:41 | LL | assert::is_maybe_transmutable::<(), Void>(); - | ^^^^ `yawning_void::Void` is uninhabited + | ^^^^ `yawning_void_struct::Void` is uninhabited + | +note: required by a bound in `is_maybe_transmutable` + --> $DIR/uninhabited.rs:10:14 + | +LL | pub fn is_maybe_transmutable() + | --------------------- required by a bound in this function +LL | where +LL | Dst: BikeshedIntrinsicFrom + | |__________^ required by this bound in `is_maybe_transmutable` + +error[E0277]: `()` cannot be safely transmuted into `yawning_void_enum::Void` + --> $DIR/uninhabited.rs:71:41 + | +LL | assert::is_maybe_transmutable::<(), Void>(); + | ^^^^ `yawning_void_enum::Void` is uninhabited | note: required by a bound in `is_maybe_transmutable` --> $DIR/uninhabited.rs:10:14 @@ -59,7 +89,7 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0277]: `u128` cannot be safely transmuted into `DistantVoid` - --> $DIR/uninhabited.rs:70:43 + --> $DIR/uninhabited.rs:92:43 | LL | assert::is_maybe_transmutable::(); | ^^^^^^^^^^^ at least one value of `u128` isn't a bit-valid value of `DistantVoid` @@ -80,7 +110,7 @@ LL | | } LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0080, E0277. For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/try-trait/bad-interconversion.stderr b/tests/ui/try-trait/bad-interconversion.stderr index e3edec6a4c76..c30b6334fed6 100644 --- a/tests/ui/try-trait/bad-interconversion.stderr +++ b/tests/ui/try-trait/bad-interconversion.stderr @@ -10,8 +10,8 @@ LL | Ok(Err(123_i32)?) | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the following other types implement trait `From`: - > - > + `u8` implements `From` + `u8` implements `From` = note: required for `Result` to implement `FromResidual>` error[E0277]: the `?` operator can only be used on `Result`s, not `Option`s, in a function that returns `Result` @@ -24,8 +24,8 @@ LL | Some(3)?; | = help: the trait `FromResidual>` is not implemented for `Result` = help: the following other types implement trait `FromResidual`: - as FromResidual>> - as FromResidual>> + `Result` implements `FromResidual>` + `Result` implements `FromResidual>` error[E0277]: the `?` operator can only be used on `Result`s in a function that returns `Result` --> $DIR/bad-interconversion.rs:17:31 @@ -37,8 +37,8 @@ LL | Ok(ControlFlow::Break(123)?) | = help: the trait `FromResidual>` is not implemented for `Result` = help: the following other types implement trait `FromResidual`: - as FromResidual>> - as FromResidual>> + `Result` implements `FromResidual>` + `Result` implements `FromResidual>` error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option` --> $DIR/bad-interconversion.rs:22:22 @@ -50,8 +50,8 @@ LL | Some(Err("hello")?) | = help: the trait `FromResidual>` is not implemented for `Option` = help: the following other types implement trait `FromResidual`: - as FromResidual>> - as FromResidual> + `Option` implements `FromResidual>` + `Option` implements `FromResidual` error[E0277]: the `?` operator can only be used on `Option`s in a function that returns `Option` --> $DIR/bad-interconversion.rs:27:33 @@ -63,8 +63,8 @@ LL | Some(ControlFlow::Break(123)?) | = help: the trait `FromResidual>` is not implemented for `Option` = help: the following other types implement trait `FromResidual`: - as FromResidual>> - as FromResidual> + `Option` implements `FromResidual>` + `Option` implements `FromResidual` error[E0277]: the `?` operator can only be used on `ControlFlow`s in a function that returns `ControlFlow` --> $DIR/bad-interconversion.rs:32:39 diff --git a/tests/ui/try-trait/issue-32709.stderr b/tests/ui/try-trait/issue-32709.stderr index a0dd18fa039c..9b77f578437c 100644 --- a/tests/ui/try-trait/issue-32709.stderr +++ b/tests/ui/try-trait/issue-32709.stderr @@ -10,14 +10,14 @@ LL | Err(5)?; | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the following other types implement trait `From`: - <(T, T) as From<[T; 2]>> - <(T, T, T) as From<[T; 3]>> - <(T, T, T, T) as From<[T; 4]>> - <(T, T, T, T, T) as From<[T; 5]>> - <(T, T, T, T, T, T) as From<[T; 6]>> - <(T, T, T, T, T, T, T) as From<[T; 7]>> - <(T, T, T, T, T, T, T, T) as From<[T; 8]>> - <(T, T, T, T, T, T, T, T, T) as From<[T; 9]>> + `(T, T)` implements `From<[T; 2]>` + `(T, T, T)` implements `From<[T; 3]>` + `(T, T, T, T)` implements `From<[T; 4]>` + `(T, T, T, T, T)` implements `From<[T; 5]>` + `(T, T, T, T, T, T)` implements `From<[T; 6]>` + `(T, T, T, T, T, T, T)` implements `From<[T; 7]>` + `(T, T, T, T, T, T, T, T)` implements `From<[T; 8]>` + `(T, T, T, T, T, T, T, T, T)` implements `From<[T; 9]>` and 4 others = note: required for `Result` to implement `FromResidual>` diff --git a/tests/ui/try-trait/option-to-result.stderr b/tests/ui/try-trait/option-to-result.stderr index fabc1ff2c762..2d97226275d2 100644 --- a/tests/ui/try-trait/option-to-result.stderr +++ b/tests/ui/try-trait/option-to-result.stderr @@ -9,8 +9,8 @@ LL | a?; | = help: the trait `FromResidual>` is not implemented for `Result<(), ()>` = help: the following other types implement trait `FromResidual`: - as FromResidual>> - as FromResidual>> + `Result` implements `FromResidual>` + `Result` implements `FromResidual>` error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option` --> $DIR/option-to-result.rs:11:6 @@ -23,8 +23,8 @@ LL | a?; | = help: the trait `FromResidual>` is not implemented for `Option` = help: the following other types implement trait `FromResidual`: - as FromResidual>> - as FromResidual> + `Option` implements `FromResidual>` + `Option` implements `FromResidual` error: aborting due to 2 previous errors diff --git a/tests/ui/try-trait/try-on-option.stderr b/tests/ui/try-trait/try-on-option.stderr index fad6a1fe8237..84a51a078af1 100644 --- a/tests/ui/try-trait/try-on-option.stderr +++ b/tests/ui/try-trait/try-on-option.stderr @@ -9,8 +9,8 @@ LL | x?; | = help: the trait `FromResidual>` is not implemented for `Result` = help: the following other types implement trait `FromResidual`: - as FromResidual>> - as FromResidual>> + `Result` implements `FromResidual>` + `Result` implements `FromResidual>` error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/try-on-option.rs:11:6 diff --git a/tests/ui/try-trait/try-operator-on-main.stderr b/tests/ui/try-trait/try-operator-on-main.stderr index 7cd38e0cf95e..ba6eacde68fd 100644 --- a/tests/ui/try-trait/try-operator-on-main.stderr +++ b/tests/ui/try-trait/try-operator-on-main.stderr @@ -8,6 +8,16 @@ LL | std::fs::File::open("foo")?; | ^ cannot use the `?` operator in a function that returns `()` | = help: the trait `FromResidual>` is not implemented for `()` +help: consider adding return type + | +LL ~ fn main() -> Result<(), Box> { +LL | // error for a `Try` type on a non-`Try` fn + ... +LL | try_trait_generic::<()>(); +LL + +LL + Ok(()) +LL + } + | error[E0277]: the `?` operator can only be applied to values that implement `Try` --> $DIR/try-operator-on-main.rs:10:5 diff --git a/tests/ui/type-alias-impl-trait/argument-types.rs b/tests/ui/type-alias-impl-trait/argument-types.rs index 9e4a8bb8dda4..7382d4c78c7d 100644 --- a/tests/ui/type-alias-impl-trait/argument-types.rs +++ b/tests/ui/type-alias-impl-trait/argument-types.rs @@ -1,13 +1,21 @@ #![feature(type_alias_impl_trait)] #![allow(dead_code)] //@ check-pass -use std::fmt::Debug; -type Foo = impl Debug; +mod foo { + use std::fmt::Debug; -fn foo1(mut x: Foo) { - x = 22_u32; + pub type Foo = impl Debug; + + fn foo1(mut x: Foo) { + x = 22_u32; + } + + pub fn foo_value() -> Foo { + 11_u32 + } } +use foo::*; fn foo2(mut x: Foo) { // no constraint on x @@ -17,10 +25,6 @@ fn foo3(x: Foo) { println!("{:?}", x); } -fn foo_value() -> Foo { - 11_u32 -} - fn main() { foo3(foo_value()); } diff --git a/tests/ui/type-alias-impl-trait/assoc-projection-ice.rs b/tests/ui/type-alias-impl-trait/assoc-projection-ice.rs index 761402582e24..9dcbc75db3f1 100644 --- a/tests/ui/type-alias-impl-trait/assoc-projection-ice.rs +++ b/tests/ui/type-alias-impl-trait/assoc-projection-ice.rs @@ -2,18 +2,23 @@ //@ build-pass -trait T { type Item; } +mod helper { + pub trait T { + type Item; + } -type Alias<'a> = impl T; + pub type Alias<'a> = impl T; -struct S; -impl<'a> T for &'a S { - type Item = &'a (); -} - -fn filter_positive<'a>() -> Alias<'a> { - &S + struct S; + impl<'a> T for &'a S { + type Item = &'a (); + } + + pub fn filter_positive<'a>() -> Alias<'a> { + &S + } } +use helper::*; fn with_positive(fun: impl Fn(Alias<'_>)) { fun(filter_positive()); diff --git a/tests/ui/type-alias-impl-trait/bounds-are-checked-2.rs b/tests/ui/type-alias-impl-trait/bounds-are-checked-2.rs index 55b4dc8dc232..45f54266014d 100644 --- a/tests/ui/type-alias-impl-trait/bounds-are-checked-2.rs +++ b/tests/ui/type-alias-impl-trait/bounds-are-checked-2.rs @@ -3,12 +3,15 @@ #![feature(type_alias_impl_trait)] -type X = impl Clone; +mod foo { + pub type X = impl Clone; -fn f(t: T) -> X { - t - //~^ ERROR the trait bound `T: Clone` is not satisfied + fn f(t: T) -> X { + t + //~^ ERROR the trait bound `T: Clone` is not satisfied + } } +use foo::X; fn g(o: Option>) -> Option> { o.clone() diff --git a/tests/ui/type-alias-impl-trait/bounds-are-checked-2.stderr b/tests/ui/type-alias-impl-trait/bounds-are-checked-2.stderr index 20e478160c61..0c937f922538 100644 --- a/tests/ui/type-alias-impl-trait/bounds-are-checked-2.stderr +++ b/tests/ui/type-alias-impl-trait/bounds-are-checked-2.stderr @@ -1,13 +1,13 @@ error[E0277]: the trait bound `T: Clone` is not satisfied - --> $DIR/bounds-are-checked-2.rs:9:5 + --> $DIR/bounds-are-checked-2.rs:10:9 | -LL | t - | ^ the trait `Clone` is not implemented for `T` +LL | t + | ^ the trait `Clone` is not implemented for `T` | help: consider restricting type parameter `T` | -LL | type X = impl Clone; - | +++++++++++++++++++ +LL | pub type X = impl Clone; + | +++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/closure-normalization-ice-109020.rs b/tests/ui/type-alias-impl-trait/closure-normalization-ice-109020.rs index c5ee46024f98..0dfa1f40ae6c 100644 --- a/tests/ui/type-alias-impl-trait/closure-normalization-ice-109020.rs +++ b/tests/ui/type-alias-impl-trait/closure-normalization-ice-109020.rs @@ -6,7 +6,14 @@ use std::marker::PhantomData; -type WithEmplacableForFn<'a> = impl EmplacableFn + 'a; +mod foo { + pub type WithEmplacableForFn<'a> = impl super::EmplacableFn + 'a; + + fn _constrain(_: &mut ()) -> WithEmplacableForFn<'_> { + () + } +} +use foo::*; fn with_emplacable_for<'a, F, R>(mut f: F) -> R where @@ -16,9 +23,6 @@ where _: &'a (), _: &mut dyn FnMut(Emplacable>) -> R, ) -> R { - fn _constrain(_: &mut ()) -> WithEmplacableForFn<'_> { - () - } loop {} } diff --git a/tests/ui/type-alias-impl-trait/closure_args.rs b/tests/ui/type-alias-impl-trait/closure_args.rs index eb5ab9d67f72..0141a01aad02 100644 --- a/tests/ui/type-alias-impl-trait/closure_args.rs +++ b/tests/ui/type-alias-impl-trait/closure_args.rs @@ -4,20 +4,24 @@ #![feature(type_alias_impl_trait)] -trait Anything {} -impl Anything for T {} -type Input = impl Anything; +mod foo { + pub trait Anything {} + impl Anything for T {} + pub type Input = impl Anything; + + fn bop(_: Input) { + super::run( + |x: u32| { + println!("{x}"); + }, + 0, + ); + } +} +use foo::Input; + fn run ()>(f: F, i: Input) { f(i); } -fn bop(_: Input) { - run( - |x: u32| { - println!("{x}"); - }, - 0, - ); -} - fn main() {} diff --git a/tests/ui/type-alias-impl-trait/closure_args2.rs b/tests/ui/type-alias-impl-trait/closure_args2.rs index 329596b19e9b..13ac3d31d838 100644 --- a/tests/ui/type-alias-impl-trait/closure_args2.rs +++ b/tests/ui/type-alias-impl-trait/closure_args2.rs @@ -2,20 +2,28 @@ #![feature(type_alias_impl_trait)] -trait Foo { - // This was reachable in https://github.com/rust-lang/rust/issues/100800 - fn foo(&self) { - unreachable!() +mod foo { + pub trait Foo { + // This was reachable in https://github.com/rust-lang/rust/issues/100800 + fn foo(&self) { + unreachable!() + } + } + impl Foo for T {} + + pub struct B; + impl B { + fn foo(&self) {} + } + pub type Input = impl Foo; + fn bop() -> Input { + super::run1(|x: B| x.foo(), B); + super::run2(|x: B| x.foo(), B); + panic!() } } -impl Foo for T {} +use foo::*; -struct B; -impl B { - fn foo(&self) {} -} - -type Input = impl Foo; fn run1(f: F, i: Input) { f(i) } @@ -23,10 +31,4 @@ fn run2(f: F, i: B) { f(i) } -fn bop() -> Input { - run1(|x: B| x.foo(), B); - run2(|x: B| x.foo(), B); - panic!() -} - fn main() {} diff --git a/tests/ui/type-alias-impl-trait/coherence.stderr b/tests/ui/type-alias-impl-trait/coherence.classic.stderr similarity index 95% rename from tests/ui/type-alias-impl-trait/coherence.stderr rename to tests/ui/type-alias-impl-trait/coherence.classic.stderr index 266a532a1db1..ff059bc5806d 100644 --- a/tests/ui/type-alias-impl-trait/coherence.stderr +++ b/tests/ui/type-alias-impl-trait/coherence.classic.stderr @@ -1,5 +1,5 @@ error[E0117]: only traits defined in the current crate can be implemented for arbitrary types - --> $DIR/coherence.rs:14:1 + --> $DIR/coherence.rs:16:1 | LL | impl foreign_crate::ForeignTrait for AliasOfForeignType<()> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------------- diff --git a/tests/ui/type-alias-impl-trait/coherence.next.stderr b/tests/ui/type-alias-impl-trait/coherence.next.stderr new file mode 100644 index 000000000000..dab2786c1f0f --- /dev/null +++ b/tests/ui/type-alias-impl-trait/coherence.next.stderr @@ -0,0 +1,14 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/coherence.rs:16:1 + | +LL | impl foreign_crate::ForeignTrait for AliasOfForeignType<()> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------------- + | | | + | | `AliasOfForeignType<()>` is not defined in the current crate + | impl doesn't use only types from inside the current crate + | + = note: define and implement a trait or new type instead + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0117`. diff --git a/tests/ui/type-alias-impl-trait/coherence.rs b/tests/ui/type-alias-impl-trait/coherence.rs index 641c0fac17a6..760e5210c5b7 100644 --- a/tests/ui/type-alias-impl-trait/coherence.rs +++ b/tests/ui/type-alias-impl-trait/coherence.rs @@ -1,4 +1,6 @@ //@ aux-build:foreign-crate.rs +//@ revisions: classic next +//@[next] compile-flags: -Znext-solver #![feature(type_alias_impl_trait)] extern crate foreign_crate; diff --git a/tests/ui/type-alias-impl-trait/const_generic_type.infer.stderr b/tests/ui/type-alias-impl-trait/const_generic_type.infer.stderr new file mode 100644 index 000000000000..3b6999cabc41 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/const_generic_type.infer.stderr @@ -0,0 +1,10 @@ +error: `Bar` is forbidden as the type of a const generic parameter + --> $DIR/const_generic_type.rs:8:24 + | +LL | async fn test() { + | ^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + +error: aborting due to 1 previous error + diff --git a/tests/ui/type-alias-impl-trait/const_generic_type.no_infer.stderr b/tests/ui/type-alias-impl-trait/const_generic_type.no_infer.stderr new file mode 100644 index 000000000000..55a5a3d20006 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/const_generic_type.no_infer.stderr @@ -0,0 +1,51 @@ +error: item does not constrain `Bar::{opaque#0}`, but has it in its signature + --> $DIR/const_generic_type.rs:8:10 + | +LL | async fn test() { + | ^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/const_generic_type.rs:5:12 + | +LL | type Bar = impl std::fmt::Display; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: item does not constrain `Bar::{opaque#0}`, but has it in its signature + --> $DIR/const_generic_type.rs:8:38 + | +LL | async fn test() { + | ______________________________________^ +LL | | +LL | | +LL | | +LL | | #[cfg(infer)] +LL | | let x: u32 = N; +LL | | } + | |_^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/const_generic_type.rs:5:12 + | +LL | type Bar = impl std::fmt::Display; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: unconstrained opaque type + --> $DIR/const_generic_type.rs:5:12 + | +LL | type Bar = impl std::fmt::Display; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `Bar` must be used in combination with a concrete type within the same module + +error: `Bar` is forbidden as the type of a const generic parameter + --> $DIR/const_generic_type.rs:8:24 + | +LL | async fn test() { + | ^^^^^^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/type-alias-impl-trait/const_generic_type.rs b/tests/ui/type-alias-impl-trait/const_generic_type.rs new file mode 100644 index 000000000000..de493d04f811 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/const_generic_type.rs @@ -0,0 +1,16 @@ +//@edition: 2021 +//@revisions: infer no_infer + +#![feature(type_alias_impl_trait)] +type Bar = impl std::fmt::Display; +//[no_infer]~^ ERROR: unconstrained opaque type + +async fn test() { + //~^ ERROR: `Bar` is forbidden as the type of a const generic parameter + //[no_infer]~^^ ERROR item does not constrain + //[no_infer]~| ERROR item does not constrain + #[cfg(infer)] + let x: u32 = N; +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection.current.stderr b/tests/ui/type-alias-impl-trait/constrain_in_projection.current.stderr new file mode 100644 index 000000000000..c215d197db43 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection.current.stderr @@ -0,0 +1,11 @@ +error[E0277]: the trait bound `Foo: Trait` is not satisfied + --> $DIR/constrain_in_projection.rs:24:14 + | +LL | let x = >::Assoc::default(); + | ^^^ the trait `Trait` is not implemented for `Foo` + | + = help: the trait `Trait<()>` is implemented for `Foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection.rs b/tests/ui/type-alias-impl-trait/constrain_in_projection.rs new file mode 100644 index 000000000000..7d7d16361ae6 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection.rs @@ -0,0 +1,28 @@ +//! Check that projections will constrain opaque types while looking for +//! matching impls. + +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next]check-pass + +#![feature(type_alias_impl_trait)] + +struct Foo; + +type Bar = impl Sized; + +trait Trait { + type Assoc: Default; +} + +impl Trait<()> for Foo { + type Assoc = u32; +} + +fn bop(_: Bar) { + let x = >::Assoc::default(); + //[current]~^ `Foo: Trait` is not satisfied +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection2.current.stderr b/tests/ui/type-alias-impl-trait/constrain_in_projection2.current.stderr new file mode 100644 index 000000000000..909f1f6d61cb --- /dev/null +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection2.current.stderr @@ -0,0 +1,13 @@ +error[E0277]: the trait bound `Foo: Trait` is not satisfied + --> $DIR/constrain_in_projection2.rs:27:14 + | +LL | let x = >::Assoc::default(); + | ^^^ the trait `Trait` is not implemented for `Foo` + | + = help: the following other types implement trait `Trait`: + `Foo` implements `Trait<()>` + `Foo` implements `Trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr b/tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr new file mode 100644 index 000000000000..0d6eac4216ba --- /dev/null +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr @@ -0,0 +1,19 @@ +error[E0283]: type annotations needed: cannot satisfy `Foo: Trait` + --> $DIR/constrain_in_projection2.rs:27:14 + | +LL | let x = >::Assoc::default(); + | ^^^ help: use the fully qualified path to an implementation: `::Assoc` + | +note: multiple `impl`s satisfying `Foo: Trait` found + --> $DIR/constrain_in_projection2.rs:18:1 + | +LL | impl Trait<()> for Foo { + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | impl Trait for Foo { + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: associated types cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection2.rs b/tests/ui/type-alias-impl-trait/constrain_in_projection2.rs new file mode 100644 index 000000000000..af222f6c1534 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection2.rs @@ -0,0 +1,32 @@ +//! Check that projections will constrain opaque types while looking for +//! matching impls and error if ambiguous. + +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +#![feature(type_alias_impl_trait)] + +struct Foo; + +type Bar = impl Sized; + +trait Trait { + type Assoc: Default; +} + +impl Trait<()> for Foo { + type Assoc = u32; +} + +impl Trait for Foo { + type Assoc = u32; +} + +fn bop(_: Bar) { + let x = >::Assoc::default(); + //[next]~^ ERROR: cannot satisfy `Foo: Trait` + //[current]~^^ ERROR: `Foo: Trait` is not satisfied +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/constrain_inputs.rs b/tests/ui/type-alias-impl-trait/constrain_inputs.rs index 03fb64b7b94d..1391a2036b2e 100644 --- a/tests/ui/type-alias-impl-trait/constrain_inputs.rs +++ b/tests/ui/type-alias-impl-trait/constrain_inputs.rs @@ -5,6 +5,7 @@ mod lifetime_params { fn defining(s: &str) -> Ty<'_> { s } fn execute(ty: Ty<'_>) -> &str { todo!() } //~^ ERROR return type references an anonymous lifetime, which is not constrained by the fn input types + //~| ERROR item does not constrain type BadFnSig = fn(Ty<'_>) -> &str; //~^ ERROR return type references an anonymous lifetime, which is not constrained by the fn input types @@ -17,6 +18,7 @@ mod lifetime_params_2 { fn defining(s: &str) -> Ty<'_> { move || s } fn execute(ty: Ty<'_>) -> &str { ty() } //~^ ERROR return type references an anonymous lifetime, which is not constrained by the fn input types + //~| ERROR item does not constrain } // regression test for https://github.com/rust-lang/rust/issues/97104 diff --git a/tests/ui/type-alias-impl-trait/constrain_inputs.stderr b/tests/ui/type-alias-impl-trait/constrain_inputs.stderr index 93953fd06d1f..2468fb7480b0 100644 --- a/tests/ui/type-alias-impl-trait/constrain_inputs.stderr +++ b/tests/ui/type-alias-impl-trait/constrain_inputs.stderr @@ -7,8 +7,21 @@ LL | fn execute(ty: Ty<'_>) -> &str { todo!() } = note: lifetimes appearing in an associated or opaque type are not considered constrained = note: consider introducing a named lifetime parameter +error: item does not constrain `lifetime_params::Ty::{opaque#0}`, but has it in its signature + --> $DIR/constrain_inputs.rs:6:8 + | +LL | fn execute(ty: Ty<'_>) -> &str { todo!() } + | ^^^^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/constrain_inputs.rs:4:19 + | +LL | type Ty<'a> = impl Sized; + | ^^^^^^^^^^ + error[E0581]: return type references an anonymous lifetime, which is not constrained by the fn input types - --> $DIR/constrain_inputs.rs:9:35 + --> $DIR/constrain_inputs.rs:10:35 | LL | type BadFnSig = fn(Ty<'_>) -> &str; | ^^^^ @@ -17,7 +30,7 @@ LL | type BadFnSig = fn(Ty<'_>) -> &str; = note: consider introducing a named lifetime parameter error[E0582]: binding for associated type `Output` references an anonymous lifetime, which does not appear in the trait input types - --> $DIR/constrain_inputs.rs:11:42 + --> $DIR/constrain_inputs.rs:12:42 | LL | type BadTraitRef = dyn Fn(Ty<'_>) -> &str; | ^^^^ @@ -26,7 +39,7 @@ LL | type BadTraitRef = dyn Fn(Ty<'_>) -> &str; = note: consider introducing a named lifetime parameter error[E0581]: return type references an anonymous lifetime, which is not constrained by the fn input types - --> $DIR/constrain_inputs.rs:18:31 + --> $DIR/constrain_inputs.rs:19:31 | LL | fn execute(ty: Ty<'_>) -> &str { ty() } | ^^^^ @@ -34,8 +47,21 @@ LL | fn execute(ty: Ty<'_>) -> &str { ty() } = note: lifetimes appearing in an associated or opaque type are not considered constrained = note: consider introducing a named lifetime parameter +error: item does not constrain `lifetime_params_2::Ty::{opaque#0}`, but has it in its signature + --> $DIR/constrain_inputs.rs:19:8 + | +LL | fn execute(ty: Ty<'_>) -> &str { ty() } + | ^^^^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/constrain_inputs.rs:17:19 + | +LL | type Ty<'a> = impl FnOnce() -> &'a str; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0581]: return type references an anonymous lifetime, which is not constrained by the fn input types - --> $DIR/constrain_inputs.rs:27:37 + --> $DIR/constrain_inputs.rs:29:37 | LL | type BadFnSig = fn(Ty<&str>) -> &str; | ^^^^ @@ -44,7 +70,7 @@ LL | type BadFnSig = fn(Ty<&str>) -> &str; = note: consider introducing a named lifetime parameter error[E0582]: binding for associated type `Output` references an anonymous lifetime, which does not appear in the trait input types - --> $DIR/constrain_inputs.rs:29:44 + --> $DIR/constrain_inputs.rs:31:44 | LL | type BadTraitRef = dyn Fn(Ty<&str>) -> &str; | ^^^^ @@ -52,7 +78,7 @@ LL | type BadTraitRef = dyn Fn(Ty<&str>) -> &str; = note: lifetimes appearing in an associated or opaque type are not considered constrained = note: consider introducing a named lifetime parameter -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors Some errors have detailed explanations: E0581, E0582. For more information about an error, try `rustc --explain E0581`. diff --git a/tests/ui/type-alias-impl-trait/debug-ty-with-weak.rs b/tests/ui/type-alias-impl-trait/debug-ty-with-weak.rs index 7e743fc09dd5..db9c2cc096ab 100644 --- a/tests/ui/type-alias-impl-trait/debug-ty-with-weak.rs +++ b/tests/ui/type-alias-impl-trait/debug-ty-with-weak.rs @@ -3,10 +3,12 @@ #![feature(type_alias_impl_trait)] -type Debuggable = impl core::fmt::Debug; +mod bar { + pub type Debuggable = impl core::fmt::Debug; + fn foo() -> Debuggable { + 0u32 + } +} +use bar::Debuggable; static mut TEST: Option = None; - -fn foo() -> Debuggable { - 0u32 -} diff --git a/tests/ui/type-alias-impl-trait/defined-by-user-annotation.rs b/tests/ui/type-alias-impl-trait/defined-by-user-annotation.rs index 0e1d44e7bb3a..75a4fbdb5d63 100644 --- a/tests/ui/type-alias-impl-trait/defined-by-user-annotation.rs +++ b/tests/ui/type-alias-impl-trait/defined-by-user-annotation.rs @@ -1,4 +1,4 @@ -// User type annotation in fn bodies is a a valid defining site for opaque types. +// User type annotation in fn bodies is a valid defining site for opaque types. //@ check-pass #![feature(type_alias_impl_trait)] diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal.rs b/tests/ui/type-alias-impl-trait/different_args_considered_equal.rs new file mode 100644 index 000000000000..8ce471e39568 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal.rs @@ -0,0 +1,14 @@ +#![feature(type_alias_impl_trait)] + +pub type Opaque<'a> = impl Sized; + +fn get_one<'a>(a: *mut &'a str) -> Opaque<'a> { + a +} + +fn get_iter<'a>() -> impl IntoIterator> { + //~^ ERROR: item does not constrain + None::> +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal.stderr b/tests/ui/type-alias-impl-trait/different_args_considered_equal.stderr new file mode 100644 index 000000000000..f27f22345252 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal.stderr @@ -0,0 +1,15 @@ +error: item does not constrain `Opaque::{opaque#0}`, but has it in its signature + --> $DIR/different_args_considered_equal.rs:9:4 + | +LL | fn get_iter<'a>() -> impl IntoIterator> { + | ^^^^^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/different_args_considered_equal.rs:3:23 + | +LL | pub type Opaque<'a> = impl Sized; + | ^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs new file mode 100644 index 000000000000..43dfea97e6db --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs @@ -0,0 +1,14 @@ +#![feature(type_alias_impl_trait)] + +pub type Opaque<'a> = impl Sized; + +fn get_one<'a>(a: *mut &'a str) -> impl IntoIterator> { + if a.is_null() { + Some(a) + } else { + None::> + //~^ ERROR hidden type for `Opaque<'static>` captures lifetime that does not appear in bounds + } +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr new file mode 100644 index 000000000000..1104c2c498a5 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr @@ -0,0 +1,20 @@ +error[E0700]: hidden type for `Opaque<'static>` captures lifetime that does not appear in bounds + --> $DIR/different_args_considered_equal2.rs:9:9 + | +LL | pub type Opaque<'a> = impl Sized; + | ---------- opaque type defined here +LL | +LL | fn get_one<'a>(a: *mut &'a str) -> impl IntoIterator> { + | -- hidden type `*mut &'a str` captures the lifetime `'a` as defined here +... +LL | None::> + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to declare that `impl IntoIterator>` captures `'a`, you can add an explicit `'a` lifetime bound + | +LL | fn get_one<'a>(a: *mut &'a str) -> impl IntoIterator> + 'a { + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal3.rs b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.rs new file mode 100644 index 000000000000..ea69175ba310 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.rs @@ -0,0 +1,22 @@ +//! Test that we don't allow coercing an opaque type with a non-static +//! lifetime to one with a static lifetime. While `get_iter` looks like +//! it would be doing the opposite, the way we're handling projections +//! makes `Opaque<'a>` the hidden type of `Opaque<'static>`. + +#![feature(type_alias_impl_trait)] + +mod defining_scope { + pub type Opaque<'a> = impl Sized; + + fn get_one<'a>(a: *mut &'a str) -> Opaque<'a> { + a + } +} +use defining_scope::Opaque; + +fn get_iter<'a>() -> impl IntoIterator> { + None::> + //~^ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal3.stderr b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.stderr new file mode 100644 index 000000000000..d8f70e3d7782 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/different_args_considered_equal3.rs:18:5 + | +LL | fn get_iter<'a>() -> impl IntoIterator> { + | -- lifetime `'a` defined here +LL | None::> + | ^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to 1 previous error + diff --git a/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.rs b/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.rs index 59ba2694a764..b209b4bc89df 100644 --- a/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.rs +++ b/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.rs @@ -25,7 +25,7 @@ mod mod3 { } // This is similar to the previous cases in that 'a is equal to 'static, -// which is is some sense an implicit parameter to `Opaque`. +// which is some sense an implicit parameter to `Opaque`. // For example, given a defining use `Opaque<'a> := &'a ()`, // it is ambiguous whether `Opaque<'a> := &'a ()` or `Opaque<'a> := &'static ()` mod mod4 { diff --git a/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs index 023991c29d09..9ed010f2293e 100644 --- a/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs +++ b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs @@ -23,9 +23,11 @@ impl Fn(&'a ()) -> StateWidget<'a>> Widget<()> for StatefulWidget type State = (); fn make_state(&self) -> Self::State {} + //~^ ERROR item does not constrain } fn new_stateful_widget Fn(&'a ()) -> StateWidget<'a>>(build: F) -> impl Widget<()> { + //~^ ERROR item does not constrain StatefulWidget(build) //~^ ERROR expected generic lifetime parameter, found `'a` } diff --git a/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr index 0c76feae198b..9a3f4ae4c1c3 100644 --- a/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr +++ b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr @@ -1,5 +1,31 @@ +error: item does not constrain `StateWidget::{opaque#0}`, but has it in its signature + --> $DIR/failed-to-normalize-ice-99945.rs:25:8 + | +LL | fn make_state(&self) -> Self::State {} + | ^^^^^^^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/failed-to-normalize-ice-99945.rs:20:24 + | +LL | type StateWidget<'a> = impl Widget<&'a ()>; + | ^^^^^^^^^^^^^^^^^^^ + +error: item does not constrain `StateWidget::{opaque#0}`, but has it in its signature + --> $DIR/failed-to-normalize-ice-99945.rs:29:4 + | +LL | fn new_stateful_widget Fn(&'a ()) -> StateWidget<'a>>(build: F) -> impl Widget<()> { + | ^^^^^^^^^^^^^^^^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/failed-to-normalize-ice-99945.rs:20:24 + | +LL | type StateWidget<'a> = impl Widget<&'a ()>; + | ^^^^^^^^^^^^^^^^^^^ + error[E0308]: mismatched types - --> $DIR/failed-to-normalize-ice-99945.rs:34:29 + --> $DIR/failed-to-normalize-ice-99945.rs:36:29 | LL | type StateWidget<'a> = impl Widget<&'a ()>; | ------------------- the expected opaque type @@ -11,7 +37,7 @@ LL | new_stateful_widget(|_| ()).make_state(); found unit type `()` error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/failed-to-normalize-ice-99945.rs:29:5 + --> $DIR/failed-to-normalize-ice-99945.rs:31:5 | LL | type StateWidget<'a> = impl Widget<&'a ()>; | -- this generic parameter must be used with a generic lifetime parameter @@ -19,7 +45,7 @@ LL | type StateWidget<'a> = impl Widget<&'a ()>; LL | StatefulWidget(build) | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0308, E0792. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden4.rs b/tests/ui/type-alias-impl-trait/hkl_forbidden4.rs index 3b54fb7ea999..96c905ef3a90 100644 --- a/tests/ui/type-alias-impl-trait/hkl_forbidden4.rs +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden4.rs @@ -17,10 +17,12 @@ async fn operation(_: &mut ()) -> () { } async fn call(_f: F) +//~^ ERROR item does not constrain where for<'any> F: FnMut(&'any mut ()) -> FutNothing<'any>, { //~^ ERROR: expected generic lifetime parameter, found `'any` + //~| ERROR item does not constrain } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr b/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr index c41ed0642a46..7df1a08bc883 100644 --- a/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr @@ -1,3 +1,32 @@ +error: item does not constrain `FutNothing::{opaque#0}`, but has it in its signature + --> $DIR/hkl_forbidden4.rs:19:10 + | +LL | async fn call(_f: F) + | ^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/hkl_forbidden4.rs:10:23 + | +LL | type FutNothing<'a> = impl 'a + Future; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: item does not constrain `FutNothing::{opaque#0}`, but has it in its signature + --> $DIR/hkl_forbidden4.rs:23:1 + | +LL | / { +LL | | +LL | | +LL | | } + | |_^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/hkl_forbidden4.rs:10:23 + | +LL | type FutNothing<'a> = impl 'a + Future; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: unconstrained opaque type --> $DIR/hkl_forbidden4.rs:10:23 | @@ -16,13 +45,14 @@ LL | call(operation).await | ^^^^^^^^^^^^^^^ error[E0792]: expected generic lifetime parameter, found `'any` - --> $DIR/hkl_forbidden4.rs:22:1 + --> $DIR/hkl_forbidden4.rs:23:1 | LL | type FutNothing<'a> = impl 'a + Future; | -- this generic parameter must be used with a generic lifetime parameter ... LL | / { LL | | +LL | | LL | | } | |_^ @@ -38,6 +68,6 @@ note: previous use here LL | call(operation).await | ^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.rs b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.rs index fc71d0d61ff1..0ee188d825f0 100644 --- a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.rs +++ b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.rs @@ -6,7 +6,6 @@ use std::marker::PhantomData; - trait MyIndex { type O; fn my_index(self) -> Self::O; @@ -16,7 +15,6 @@ trait MyFrom: Sized { fn my_from(value: T) -> Result; } - trait F {} impl F for () {} type DummyT = impl F; @@ -28,6 +26,7 @@ struct Scope(Phantom2>); impl Scope { fn new() -> Self { + //~^ ERROR item does not constrain unimplemented!() } } @@ -43,6 +42,7 @@ impl>>, U> MyIndex> for Scope { //~^ ERROR the type parameter `T` is not constrained by the impl type O = T; fn my_index(self) -> Self::O { + //~^ ERROR item does not constrain MyFrom::my_from(self.0).ok().unwrap() } } diff --git a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr index a39f77ce17f6..22c776e171c2 100644 --- a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr +++ b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr @@ -1,9 +1,35 @@ +error: item does not constrain `DummyT::{opaque#0}`, but has it in its signature + --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:28:8 + | +LL | fn new() -> Self { + | ^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:20:18 + | +LL | type DummyT = impl F; + | ^^^^^^ + +error: item does not constrain `DummyT::{opaque#0}`, but has it in its signature + --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:44:8 + | +LL | fn my_index(self) -> Self::O { + | ^^^^^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:20:18 + | +LL | type DummyT = impl F; + | ^^^^^^ + error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates - --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:42:6 + --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:41:6 | LL | impl>>, U> MyIndex> for Scope { | ^ unconstrained type parameter -error: aborting due to 1 previous error +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/type-alias-impl-trait/implied_bounds.rs b/tests/ui/type-alias-impl-trait/implied_bounds.rs index 53cbf8d22900..269c0eff025d 100644 --- a/tests/ui/type-alias-impl-trait/implied_bounds.rs +++ b/tests/ui/type-alias-impl-trait/implied_bounds.rs @@ -1,7 +1,11 @@ #![feature(type_alias_impl_trait)] -type WithLifetime<'a> = impl Equals; -fn _defining_use<'a>() -> WithLifetime<'a> {} +mod foo { + use super::Equals; + pub type WithLifetime<'a> = impl Equals; + fn _defining_use<'a>() -> WithLifetime<'a> {} +} +use foo::WithLifetime; trait Convert<'a> { type Witness; diff --git a/tests/ui/type-alias-impl-trait/implied_bounds.stderr b/tests/ui/type-alias-impl-trait/implied_bounds.stderr index 64a203fe4658..23f1141e5444 100644 --- a/tests/ui/type-alias-impl-trait/implied_bounds.stderr +++ b/tests/ui/type-alias-impl-trait/implied_bounds.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/implied_bounds.rs:17:9 + --> $DIR/implied_bounds.rs:21:9 | LL | impl<'a> Convert<'a> for () { | -- lifetime `'a` defined here diff --git a/tests/ui/type-alias-impl-trait/implied_bounds2.rs b/tests/ui/type-alias-impl-trait/implied_bounds2.rs index effedb954c35..845476ef9741 100644 --- a/tests/ui/type-alias-impl-trait/implied_bounds2.rs +++ b/tests/ui/type-alias-impl-trait/implied_bounds2.rs @@ -2,9 +2,17 @@ #![feature(type_alias_impl_trait)] -type Ty<'a, A> = impl Sized + 'a; -fn defining<'a, A>() -> Ty<'a, A> {} -fn assert_static() {} -fn test<'a, A>() where Ty<'a, A>: 'static, { assert_static::>() } +mod helper { + pub type Ty<'a, A> = impl Sized + 'a; + fn defining<'a, A>() -> Ty<'a, A> {} + pub fn assert_static() {} +} +use helper::*; +fn test<'a, A>() +where + Ty<'a, A>: 'static, +{ + assert_static::>() +} fn main() {} diff --git a/tests/ui/type-alias-impl-trait/implied_bounds_from_types.rs b/tests/ui/type-alias-impl-trait/implied_bounds_from_types.rs index 239fcc6a5e6f..76a63741e18e 100644 --- a/tests/ui/type-alias-impl-trait/implied_bounds_from_types.rs +++ b/tests/ui/type-alias-impl-trait/implied_bounds_from_types.rs @@ -1,7 +1,11 @@ #![feature(type_alias_impl_trait)] -type WithLifetime = impl Equals; -fn _defining_use() -> WithLifetime {} +mod foo { + use super::Equals; + pub type WithLifetime = impl Equals; + fn _defining_use() -> WithLifetime {} +} +use foo::WithLifetime; trait Convert<'a> { type Witness; @@ -12,7 +16,6 @@ impl<'a> Convert<'a> for () { type Witness = WithLifetime<&'a ()>; fn convert<'b, T: ?Sized>(_proof: &'b WithLifetime<&'a ()>, x: &'a T) -> &'b T { - //~^ ERROR non-defining opaque type use // compiler used to think it gets to assume 'a: 'b here because // of the `&'b WithLifetime<&'a ()>` argument x diff --git a/tests/ui/type-alias-impl-trait/implied_bounds_from_types.stderr b/tests/ui/type-alias-impl-trait/implied_bounds_from_types.stderr index 23dbf0e8f60c..391a8a75786a 100644 --- a/tests/ui/type-alias-impl-trait/implied_bounds_from_types.stderr +++ b/tests/ui/type-alias-impl-trait/implied_bounds_from_types.stderr @@ -1,17 +1,5 @@ -error[E0792]: non-defining opaque type use in defining scope - --> $DIR/implied_bounds_from_types.rs:14:39 - | -LL | fn convert<'b, T: ?Sized>(_proof: &'b WithLifetime<&'a ()>, x: &'a T) -> &'b T { - | ^^^^^^^^^^^^^^^^^^^^^^^^ argument `&'a ()` is not a generic parameter - | -note: for this opaque type - --> $DIR/implied_bounds_from_types.rs:3:24 - | -LL | type WithLifetime = impl Equals; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: lifetime may not live long enough - --> $DIR/implied_bounds_from_types.rs:18:9 + --> $DIR/implied_bounds_from_types.rs:21:9 | LL | impl<'a> Convert<'a> for () { | -- lifetime `'a` defined here @@ -24,6 +12,5 @@ LL | x | = help: consider adding the following bound: `'a: 'b` -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs index 469a493b0b35..fb251e9bde15 100644 --- a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs +++ b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.rs @@ -1,40 +1,68 @@ #![feature(type_alias_impl_trait)] mod test_lifetime_param { - type Ty<'a> = impl Sized; - fn defining(a: &str) -> Ty<'_> { a } - fn assert_static<'a: 'static>() {} - fn test<'a>() where Ty<'a>: 'static { assert_static::<'a>() } + pub type Ty<'a> = impl Sized; + fn defining(a: &str) -> Ty<'_> { + a + } + pub fn assert_static<'a: 'static>() {} +} +fn test_lifetime_param_test<'a>() +where + test_lifetime_param::Ty<'a>: 'static, +{ + test_lifetime_param::assert_static::<'a>() //~^ ERROR: lifetime may not live long enough } mod test_higher_kinded_lifetime_param { - type Ty<'a> = impl Sized; - fn defining(a: &str) -> Ty<'_> { a } - fn assert_static<'a: 'static>() {} - fn test<'a>() where for<'b> Ty<'b>: 'a { assert_static::<'a>() } + pub type Ty<'a> = impl Sized + 'a; + fn defining(a: &str) -> Ty<'_> { + a + } + pub fn assert_static<'a: 'static>() {} +} +fn test_higher_kinded_lifetime_param_test<'a>() +where + for<'b> test_higher_kinded_lifetime_param::Ty<'b>: 'a, +{ + test_higher_kinded_lifetime_param::assert_static::<'a>() //~^ ERROR: lifetime may not live long enough } mod test_higher_kinded_lifetime_param2 { fn assert_static<'a: 'static>() {} - fn test<'a>() { assert_static::<'a>() } - //~^ ERROR: lifetime may not live long enough + fn test<'a>() { + assert_static::<'a>() + //~^ ERROR: lifetime may not live long enough + } } mod test_type_param { - type Ty = impl Sized; - fn defining(s: A) -> Ty { s } - fn assert_static() {} - fn test() where Ty: 'static { assert_static::() } + pub type Ty = impl Sized; + fn defining(s: A) -> Ty { + s + } + pub fn assert_static() {} +} +fn test_type_param_test() +where + test_type_param::Ty: 'static, +{ + test_type_param::assert_static::() //~^ ERROR: parameter type `A` may not live long enough } mod test_implied_from_fn_sig { - type Opaque = impl Sized; - fn defining() -> Opaque {} + mod foo { + pub type Opaque = impl Sized; + fn defining() -> Opaque {} + } fn assert_static() {} - fn test(_: Opaque) { assert_static::(); } + + fn test(_: foo::Opaque) { + assert_static::(); + } } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr index d6dd20739b7a..b7c9c131c7d1 100644 --- a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr +++ b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check3.stderr @@ -1,36 +1,42 @@ error: lifetime may not live long enough - --> $DIR/implied_lifetime_wf_check3.rs:7:43 + --> $DIR/implied_lifetime_wf_check3.rs:14:5 | -LL | fn test<'a>() where Ty<'a>: 'static { assert_static::<'a>() } - | -- lifetime `'a` defined here ^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` +LL | fn test_lifetime_param_test<'a>() + | -- lifetime `'a` defined here +... +LL | test_lifetime_param::assert_static::<'a>() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/implied_lifetime_wf_check3.rs:15:46 + --> $DIR/implied_lifetime_wf_check3.rs:29:5 | -LL | fn test<'a>() where for<'b> Ty<'b>: 'a { assert_static::<'a>() } - | -- lifetime `'a` defined here ^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` +LL | fn test_higher_kinded_lifetime_param_test<'a>() + | -- lifetime `'a` defined here +... +LL | test_higher_kinded_lifetime_param::assert_static::<'a>() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/implied_lifetime_wf_check3.rs:21:21 + --> $DIR/implied_lifetime_wf_check3.rs:36:9 | -LL | fn test<'a>() { assert_static::<'a>() } - | -- ^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` - | | - | lifetime `'a` defined here +LL | fn test<'a>() { + | -- lifetime `'a` defined here +LL | assert_static::<'a>() + | ^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` error[E0310]: the parameter type `A` may not live long enough - --> $DIR/implied_lifetime_wf_check3.rs:29:41 + --> $DIR/implied_lifetime_wf_check3.rs:52:5 | -LL | fn test() where Ty: 'static { assert_static::() } - | ^^^^^^^^^^^^^^^^^^ - | | - | the parameter type `A` must be valid for the static lifetime... - | ...so that the type `A` will meet its required lifetime bounds +LL | test_type_param::assert_static::() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `A` must be valid for the static lifetime... + | ...so that the type `A` will meet its required lifetime bounds | help: consider adding an explicit lifetime bound | -LL | fn test() where Ty: 'static { assert_static::() } - | +++++++++ +LL | fn test_type_param_test() + | +++++++++ error: aborting due to 4 previous errors diff --git a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.rs b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.rs index 1a5daa13458c..f8b09814caac 100644 --- a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.rs +++ b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.rs @@ -1,11 +1,20 @@ #![feature(type_alias_impl_trait)] mod test_type_param_static { - type Ty = impl Sized + 'static; + pub type Ty = impl Sized + 'static; //~^ ERROR: the parameter type `A` may not live long enough - fn defining(s: A) -> Ty { s } - fn assert_static() {} - fn test() where Ty: 'static { assert_static::() } + fn defining(s: A) -> Ty { + s + } + pub fn assert_static() {} +} +use test_type_param_static::*; + +fn test() +where + Ty: 'static, +{ + assert_static::() //~^ ERROR: the parameter type `A` may not live long enough } diff --git a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.stderr b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.stderr index d1c06330c15e..f2e5e95b96f0 100644 --- a/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.stderr +++ b/tests/ui/type-alias-impl-trait/implied_lifetime_wf_check4_static.stderr @@ -1,30 +1,30 @@ error[E0310]: the parameter type `A` may not live long enough - --> $DIR/implied_lifetime_wf_check4_static.rs:4:18 + --> $DIR/implied_lifetime_wf_check4_static.rs:4:22 | -LL | type Ty = impl Sized + 'static; - | ^^^^^^^^^^^^^^^^^^^^ - | | - | the parameter type `A` must be valid for the static lifetime... - | ...so that the type `A` will meet its required lifetime bounds +LL | pub type Ty = impl Sized + 'static; + | ^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `A` must be valid for the static lifetime... + | ...so that the type `A` will meet its required lifetime bounds | help: consider adding an explicit lifetime bound | -LL | type Ty = impl Sized + 'static; - | +++++++++ +LL | pub type Ty = impl Sized + 'static; + | +++++++++ error[E0310]: the parameter type `A` may not live long enough - --> $DIR/implied_lifetime_wf_check4_static.rs:8:41 + --> $DIR/implied_lifetime_wf_check4_static.rs:17:5 | -LL | fn test() where Ty: 'static { assert_static::() } - | ^^^^^^^^^^^^^^^^^^ - | | - | the parameter type `A` must be valid for the static lifetime... - | ...so that the type `A` will meet its required lifetime bounds +LL | assert_static::() + | ^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `A` must be valid for the static lifetime... + | ...so that the type `A` will meet its required lifetime bounds | help: consider adding an explicit lifetime bound | -LL | fn test() where Ty: 'static { assert_static::() } - | +++++++++ +LL | fn test() + | +++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/issue-101750.rs b/tests/ui/type-alias-impl-trait/issue-101750.rs index 1c5261b7a791..9367be8ca07f 100644 --- a/tests/ui/type-alias-impl-trait/issue-101750.rs +++ b/tests/ui/type-alias-impl-trait/issue-101750.rs @@ -2,17 +2,21 @@ //@ check-pass -trait Trait {} +mod foo { + pub trait Trait {} -type TAIT = impl Trait; + pub type TAIT = impl Trait; -struct Concrete; -impl Trait for Concrete {} + pub struct Concrete; + impl Trait for Concrete {} -fn tait() -> TAIT { - Concrete + pub fn tait() -> TAIT { + Concrete + } } +use foo::*; + trait OuterTrait { type Item; } @@ -24,9 +28,7 @@ impl OuterTrait for Dummy { } fn tait_and_impl_trait() -> impl OuterTrait { - Dummy { - t: (tait(), Concrete), - } + Dummy { t: (tait(), Concrete) } } fn tait_and_dyn_trait() -> impl OuterTrait)> { diff --git a/tests/ui/type-alias-impl-trait/issue-109054.rs b/tests/ui/type-alias-impl-trait/issue-109054.rs index 51f30779cd94..d3eb65215934 100644 --- a/tests/ui/type-alias-impl-trait/issue-109054.rs +++ b/tests/ui/type-alias-impl-trait/issue-109054.rs @@ -11,6 +11,7 @@ impl std::ops::Deref for CallMe { type Target = FnType; fn deref(&self) -> &Self::Target { + //~^ ERROR: item does not constrain `ReturnType fn inner(val: &u32) -> ReturnType { async move { *val * 2 } } diff --git a/tests/ui/type-alias-impl-trait/issue-109054.stderr b/tests/ui/type-alias-impl-trait/issue-109054.stderr index a099b7d8b8a6..2a4aa63bb8ca 100644 --- a/tests/ui/type-alias-impl-trait/issue-109054.stderr +++ b/tests/ui/type-alias-impl-trait/issue-109054.stderr @@ -1,5 +1,18 @@ +error: item does not constrain `ReturnType::{opaque#0}`, but has it in its signature + --> $DIR/issue-109054.rs:13:8 + | +LL | fn deref(&self) -> &Self::Target { + | ^^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/issue-109054.rs:7:23 + | +LL | type ReturnType<'a> = impl std::future::Future + 'a; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/issue-109054.rs:18:9 + --> $DIR/issue-109054.rs:19:9 | LL | type ReturnType<'a> = impl std::future::Future + 'a; | -- this generic parameter must be used with a generic lifetime parameter @@ -7,6 +20,6 @@ LL | type ReturnType<'a> = impl std::future::Future + 'a; LL | &inner | ^^^^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/type-alias-impl-trait/issue-53092.rs b/tests/ui/type-alias-impl-trait/issue-53092.rs index 34bab0260885..83b51227aaae 100644 --- a/tests/ui/type-alias-impl-trait/issue-53092.rs +++ b/tests/ui/type-alias-impl-trait/issue-53092.rs @@ -1,7 +1,14 @@ #![feature(type_alias_impl_trait)] #![allow(dead_code)] -type Bug = impl Fn(T) -> U + Copy; +mod bug { + pub type Bug = impl Fn(T) -> U + Copy; + + fn make_bug>() -> Bug { + |x| x.into() //~ ERROR the trait bound `U: From` is not satisfied + } +} +use bug::Bug; union Moo { x: Bug, @@ -9,11 +16,6 @@ union Moo { } const CONST_BUG: Bug = unsafe { Moo { y: () }.x }; -//~^ ERROR non-defining opaque type use - -fn make_bug>() -> Bug { - |x| x.into() //~ ERROR the trait bound `U: From` is not satisfied -} fn main() { CONST_BUG(0); diff --git a/tests/ui/type-alias-impl-trait/issue-53092.stderr b/tests/ui/type-alias-impl-trait/issue-53092.stderr index 0c4cacd66550..f04750866c76 100644 --- a/tests/ui/type-alias-impl-trait/issue-53092.stderr +++ b/tests/ui/type-alias-impl-trait/issue-53092.stderr @@ -1,32 +1,19 @@ -error[E0792]: non-defining opaque type use in defining scope - --> $DIR/issue-53092.rs:11:18 - | -LL | const CONST_BUG: Bug = unsafe { Moo { y: () }.x }; - | ^^^^^^^^^^^ argument `u8` is not a generic parameter - | -note: for this opaque type - --> $DIR/issue-53092.rs:4:18 - | -LL | type Bug = impl Fn(T) -> U + Copy; - | ^^^^^^^^^^^^^^^^^^^^^^ - error[E0277]: the trait bound `U: From` is not satisfied - --> $DIR/issue-53092.rs:15:5 + --> $DIR/issue-53092.rs:8:9 | -LL | |x| x.into() - | ^^^^^^^^^^^^ the trait `From` is not implemented for `U` +LL | |x| x.into() + | ^^^^^^^^^^^^ the trait `From` is not implemented for `U` | note: required by a bound in `make_bug` - --> $DIR/issue-53092.rs:14:19 + --> $DIR/issue-53092.rs:7:23 | -LL | fn make_bug>() -> Bug { - | ^^^^^^^ required by this bound in `make_bug` +LL | fn make_bug>() -> Bug { + | ^^^^^^^ required by this bound in `make_bug` help: consider restricting type parameter `U` | -LL | type Bug> = impl Fn(T) -> U + Copy; - | +++++++++++++++++++++++ +LL | pub type Bug> = impl Fn(T) -> U + Copy; + | +++++++++++++++++++++++ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0277, E0792. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/type-alias-impl-trait/issue-53096.rs b/tests/ui/type-alias-impl-trait/issue-53096.rs index 007dcf3bcb68..590fce84fc9f 100644 --- a/tests/ui/type-alias-impl-trait/issue-53096.rs +++ b/tests/ui/type-alias-impl-trait/issue-53096.rs @@ -1,10 +1,13 @@ #![feature(rustc_attrs)] #![feature(type_alias_impl_trait)] -type Foo = impl Fn() -> usize; -const fn bar() -> Foo { - || 0usize +mod foo { + pub type Foo = impl Fn() -> usize; + pub const fn bar() -> Foo { + || 0usize + } } +use foo::*; const BAZR: Foo = bar(); #[rustc_error] diff --git a/tests/ui/type-alias-impl-trait/issue-53096.stderr b/tests/ui/type-alias-impl-trait/issue-53096.stderr index fba1802efd20..0a744e7be9cb 100644 --- a/tests/ui/type-alias-impl-trait/issue-53096.stderr +++ b/tests/ui/type-alias-impl-trait/issue-53096.stderr @@ -1,5 +1,5 @@ error: fatal error triggered by #[rustc_error] - --> $DIR/issue-53096.rs:11:1 + --> $DIR/issue-53096.rs:14:1 | LL | fn main() {} | ^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/issue-58951.rs b/tests/ui/type-alias-impl-trait/issue-58951.rs index 8a9e586eb25b..b9f27b031c72 100644 --- a/tests/ui/type-alias-impl-trait/issue-58951.rs +++ b/tests/ui/type-alias-impl-trait/issue-58951.rs @@ -2,14 +2,16 @@ #![feature(type_alias_impl_trait)] -type A = impl Iterator; +mod helper { + pub type A = impl Iterator; -fn def_a() -> A { - 0..1 + pub fn def_a() -> A { + 0..1 + } } pub fn use_a() { - def_a().map(|x| x); + helper::def_a().map(|x| x); } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-60407.rs b/tests/ui/type-alias-impl-trait/issue-60407.rs index b833429c7692..6c7c76b5ac72 100644 --- a/tests/ui/type-alias-impl-trait/issue-60407.rs +++ b/tests/ui/type-alias-impl-trait/issue-60407.rs @@ -1,6 +1,13 @@ #![feature(type_alias_impl_trait, rustc_attrs)] -type Debuggable = impl core::fmt::Debug; +mod bar { + pub type Debuggable = impl core::fmt::Debug; + + pub fn foo() -> Debuggable { + 0u32 + } +} +use bar::*; static mut TEST: Option = None; @@ -9,7 +16,3 @@ fn main() { //~^ ERROR unsafe { TEST = Some(foo()) } } - -fn foo() -> Debuggable { - 0u32 -} diff --git a/tests/ui/type-alias-impl-trait/issue-60407.stderr b/tests/ui/type-alias-impl-trait/issue-60407.stderr index 583156b9f7cb..bba9092e9775 100644 --- a/tests/ui/type-alias-impl-trait/issue-60407.stderr +++ b/tests/ui/type-alias-impl-trait/issue-60407.stderr @@ -1,5 +1,5 @@ error: fatal error triggered by #[rustc_error] - --> $DIR/issue-60407.rs:8:1 + --> $DIR/issue-60407.rs:15:1 | LL | fn main() { | ^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/issue-60662.stdout b/tests/ui/type-alias-impl-trait/issue-60662.stdout index e643dba12453..b541cbeb2279 100644 --- a/tests/ui/type-alias-impl-trait/issue-60662.stdout +++ b/tests/ui/type-alias-impl-trait/issue-60662.stdout @@ -10,5 +10,5 @@ extern crate std; trait Animal { } fn main() { - type ServeFut = /*impl Trait*/; - } + type ServeFut = /*impl Trait*/; +} diff --git a/tests/ui/type-alias-impl-trait/issue-63279.stderr b/tests/ui/type-alias-impl-trait/issue-63279.stderr index 58cafd21ca87..97158ee297da 100644 --- a/tests/ui/type-alias-impl-trait/issue-63279.stderr +++ b/tests/ui/type-alias-impl-trait/issue-63279.stderr @@ -4,7 +4,7 @@ error[E0277]: expected a `FnOnce()` closure, found `()` LL | fn c() -> Closure { | ^^^^^^^ expected an `FnOnce()` closure, found `()` | - = help: the trait `FnOnce<()>` is not implemented for `()` + = help: the trait `FnOnce()` is not implemented for `()` = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` error[E0277]: expected a `FnOnce()` closure, found `()` @@ -13,7 +13,7 @@ error[E0277]: expected a `FnOnce()` closure, found `()` LL | || -> Closure { || () } | ^^^^^^^ expected an `FnOnce()` closure, found `()` | - = help: the trait `FnOnce<()>` is not implemented for `()` + = help: the trait `FnOnce()` is not implemented for `()` = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` error[E0308]: mismatched types diff --git a/tests/ui/type-alias-impl-trait/issue-63355.rs b/tests/ui/type-alias-impl-trait/issue-63355.rs index 0c977b309949..a0d0355b5af5 100644 --- a/tests/ui/type-alias-impl-trait/issue-63355.rs +++ b/tests/ui/type-alias-impl-trait/issue-63355.rs @@ -1,5 +1,4 @@ #![feature(type_alias_impl_trait)] -//@ check-pass pub trait Foo {} @@ -39,6 +38,7 @@ impl Baz for () { } fn bar() -> Self::Bar { + //~^ ERROR: item does not constrain `FooImpl::{opaque#0}` () } } diff --git a/tests/ui/type-alias-impl-trait/issue-63355.stderr b/tests/ui/type-alias-impl-trait/issue-63355.stderr new file mode 100644 index 000000000000..6755c0380560 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/issue-63355.stderr @@ -0,0 +1,15 @@ +error: item does not constrain `FooImpl::{opaque#0}`, but has it in its signature + --> $DIR/issue-63355.rs:40:8 + | +LL | fn bar() -> Self::Bar { + | ^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/issue-63355.rs:29:20 + | +LL | pub type FooImpl = impl Foo; + | ^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs b/tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs index 61251d7f6da9..f98ce4a426c4 100644 --- a/tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs +++ b/tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs @@ -1,19 +1,21 @@ //@ check-pass #![feature(type_alias_impl_trait, rustc_attrs)] +mod foo { + pub type T = impl Sized; + // The concrete type referred by impl-trait-type-alias(`T`) is guaranteed + // to be the same as where it occurs, whereas `impl Trait`'s instance is location sensitive; + // so difference assertion should not be declared on impl-trait-type-alias's instances. + // for details, check RFC-2515: + // https://github.com/rust-lang/rfcs/blob/master/text/2515-type_alias_impl_trait.md -type T = impl Sized; -// The concrete type referred by impl-trait-type-alias(`T`) is guaranteed -// to be the same as where it occurs, whereas `impl Trait`'s instance is location sensitive; -// so difference assertion should not be declared on impl-trait-type-alias's instances. -// for details, check RFC-2515: -// https://github.com/rust-lang/rfcs/blob/master/text/2515-type_alias_impl_trait.md + fn bop(_: T) { + super::take(|| {}); + super::take(|| {}); + } +} +use foo::*; fn take(_: fn() -> T) {} -fn bop(_: T) { - take(|| {}); - take(|| {}); -} - fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-65918.rs b/tests/ui/type-alias-impl-trait/issue-65918.rs index c81650876c8a..275b9717cef3 100644 --- a/tests/ui/type-alias-impl-trait/issue-65918.rs +++ b/tests/ui/type-alias-impl-trait/issue-65918.rs @@ -15,10 +15,13 @@ trait MyFrom: Sized { } /* MCVE starts here */ -trait F {} -impl F for () {} -type DummyT = impl F; -fn _dummy_t() -> DummyT {} +mod f { + pub trait F {} + impl F for () {} + pub type DummyT = impl F; + fn _dummy_t() -> DummyT {} +} +use f::DummyT; struct Phantom1(PhantomData); struct Phantom2(PhantomData); diff --git a/tests/ui/type-alias-impl-trait/issue-72793.rs b/tests/ui/type-alias-impl-trait/issue-72793.rs index 9389517e37bd..0353b7f3787b 100644 --- a/tests/ui/type-alias-impl-trait/issue-72793.rs +++ b/tests/ui/type-alias-impl-trait/issue-72793.rs @@ -3,18 +3,24 @@ #![feature(type_alias_impl_trait)] -trait T { type Item; } +mod foo { + pub trait T { + type Item; + } -type Alias<'a> = impl T; + pub type Alias<'a> = impl T; -struct S; -impl<'a> T for &'a S { - type Item = &'a (); + struct S; + impl<'a> T for &'a S { + type Item = &'a (); + } + + pub fn filter_positive<'a>() -> Alias<'a> { + &S + } } -fn filter_positive<'a>() -> Alias<'a> { - &S -} +use foo::*; fn with_positive(fun: impl Fn(Alias<'_>)) { fun(filter_positive()); diff --git a/tests/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs b/tests/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs index 9ceef0139f05..90ab4fd8d977 100644 --- a/tests/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs +++ b/tests/ui/type-alias-impl-trait/issue-76202-trait-impl-for-tait.rs @@ -7,11 +7,18 @@ //@ check-pass #![feature(type_alias_impl_trait)] +mod g { + pub trait Dummy {} + impl Dummy for () {} + pub type F = impl Dummy; + pub fn f() -> F {} +} +use g::*; + trait Test { fn test(self); } - impl Test for define::F { fn test(self) {} } diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.current.stderr b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.current.stderr new file mode 100644 index 000000000000..2b064dcfc31a --- /dev/null +++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.current.stderr @@ -0,0 +1,25 @@ +error: item does not constrain `Bar::{opaque#0}`, but has it in its signature + --> $DIR/issue-84660-unsoundness.rs:22:8 + | +LL | fn convert(_i: In) -> Self::Out { + | ^^^^^^^ + | + = note: consider moving the opaque type's declaration and defining uses into a separate module +note: this opaque type is in the signature + --> $DIR/issue-84660-unsoundness.rs:12:12 + | +LL | type Bar = impl Foo; + | ^^^^^^^^ + +error[E0119]: conflicting implementations of trait `Trait` + --> $DIR/issue-84660-unsoundness.rs:29:1 + | +LL | impl Trait for Out { + | ------------------------------------ first implementation here +... +LL | impl Trait<(), In> for Out { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr new file mode 100644 index 000000000000..5a728a00138c --- /dev/null +++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.next.stderr @@ -0,0 +1,24 @@ +error[E0284]: type annotations needed: cannot satisfy `Bar == _` + --> $DIR/issue-84660-unsoundness.rs:22:37 + | +LL | fn convert(_i: In) -> Self::Out { + | _____________________________________^ +LL | | +LL | | +LL | | unreachable!(); +LL | | } + | |_____^ cannot satisfy `Bar == _` + +error[E0119]: conflicting implementations of trait `Trait` + --> $DIR/issue-84660-unsoundness.rs:29:1 + | +LL | impl Trait for Out { + | ------------------------------------ first implementation here +... +LL | impl Trait<(), In> for Out { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0119, E0284. +For more information about an error, try `rustc --explain E0119`. diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs index 48d4b0c96ff0..f3234bafd115 100644 --- a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs +++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs @@ -1,6 +1,10 @@ // Another example from issue #84660, this time weaponized as a safe transmute: an opaque type in an // impl header being accepted was used to create unsoundness. +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + #![feature(type_alias_impl_trait)] trait Foo {} @@ -16,11 +20,14 @@ trait Trait { impl Trait for Out { type Out = Out; fn convert(_i: In) -> Self::Out { + //[next]~^ ERROR: cannot satisfy `Bar == _` + //[current]~^^ ERROR: item does not constrain `Bar::{opaque#0}`, but has it in its signature unreachable!(); } } -impl Trait<(), In> for Out { //~ ERROR conflicting implementations of trait `Trait` +impl Trait<(), In> for Out { + //~^ ERROR conflicting implementations of trait `Trait` type Out = In; fn convert(i: In) -> Self::Out { i diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.stderr b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.stderr deleted file mode 100644 index 461da20f37b6..000000000000 --- a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0119]: conflicting implementations of trait `Trait` - --> $DIR/issue-84660-unsoundness.rs:23:1 - | -LL | impl Trait for Out { - | ------------------------------------ first implementation here -... -LL | impl Trait<(), In> for Out { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs new file mode 100644 index 000000000000..1bc352041a57 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs @@ -0,0 +1,31 @@ +#![feature(type_alias_impl_trait)] + +//! This test used to ICE rust-lang/rust#124891 +//! because we added an assertion for catching cases where opaque types get +//! registered during the processing of subtyping predicates. + +type Tait = impl FnOnce() -> (); + +fn reify_as_tait() -> Thunk { + //~^ ERROR: expected a `FnOnce()` closure, found `()` + Thunk::new(|cont| cont) + //~^ ERROR: mismatched types + //~| ERROR: expected a `FnOnce()` closure, found `()` +} + +struct Thunk(F); + +impl Thunk { + fn new(f: F) + where + F: ContFn, + { + todo!(); + } +} + +trait ContFn {} + +impl ()> ContFn for F {} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr new file mode 100644 index 000000000000..7bc2fa1b09ea --- /dev/null +++ b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr @@ -0,0 +1,31 @@ +error[E0277]: expected a `FnOnce()` closure, found `()` + --> $DIR/lazy_subtyping_of_opaques.rs:11:23 + | +LL | Thunk::new(|cont| cont) + | ^^^^ expected an `FnOnce()` closure, found `()` + | + = help: the trait `FnOnce()` is not implemented for `()` + = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` + +error[E0277]: expected a `FnOnce()` closure, found `()` + --> $DIR/lazy_subtyping_of_opaques.rs:9:23 + | +LL | fn reify_as_tait() -> Thunk { + | ^^^^^^^^^^^ expected an `FnOnce()` closure, found `()` + | + = help: the trait `FnOnce()` is not implemented for `()` + = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` + +error[E0308]: mismatched types + --> $DIR/lazy_subtyping_of_opaques.rs:11:5 + | +LL | Thunk::new(|cont| cont) + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Thunk<_>`, found `()` + | + = note: expected struct `Thunk<_>` + found unit type `()` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/type-alias-impl-trait/method_resolution.current.stderr b/tests/ui/type-alias-impl-trait/method_resolution.current.stderr new file mode 100644 index 000000000000..a9c05ad33424 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/method_resolution.current.stderr @@ -0,0 +1,15 @@ +error[E0599]: no method named `bar` found for struct `Bar` in the current scope + --> $DIR/method_resolution.rs:21:14 + | +LL | struct Bar(T); + | ------------- method `bar` not found for this struct +... +LL | self.bar() + | ^^^ method not found in `Bar` + | + = note: the method was found for + - `Bar` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/type-alias-impl-trait/method_resolution.next.stderr b/tests/ui/type-alias-impl-trait/method_resolution.next.stderr new file mode 100644 index 000000000000..6b34358a56ea --- /dev/null +++ b/tests/ui/type-alias-impl-trait/method_resolution.next.stderr @@ -0,0 +1,12 @@ +error[E0599]: no method named `bar` found for struct `Bar` in the current scope + --> $DIR/method_resolution.rs:21:14 + | +LL | struct Bar(T); + | ------------- method `bar` not found for this struct +... +LL | self.bar() + | ^^^ method cannot be called on `Bar` due to unsatisfied trait bounds + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/type-alias-impl-trait/method_resolution.rs b/tests/ui/type-alias-impl-trait/method_resolution.rs new file mode 100644 index 000000000000..f636aba15c0c --- /dev/null +++ b/tests/ui/type-alias-impl-trait/method_resolution.rs @@ -0,0 +1,30 @@ +//! `Bar::foo` is not defining `Foo`, so it cannot rely on the fact that +//! `u32` is the hidden type of `Foo` to call `bar` + +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +#![feature(type_alias_impl_trait)] + +type Foo = impl Sized; + +struct Bar(T); + +impl Bar { + fn bar(mut self) { + self.0 = 42_u32; + } +} + +impl Bar { + fn foo(self) { + self.bar() + //~^ ERROR: no method named `bar` + } +} + +fn foo() -> Foo { + 42_u32 +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/method_resolution2.rs b/tests/ui/type-alias-impl-trait/method_resolution2.rs new file mode 100644 index 000000000000..f69661db7992 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/method_resolution2.rs @@ -0,0 +1,28 @@ +//! Check that we do unify `Bar` with `Bar`, as the +//! `foo` method call can be resolved unambiguously by doing so. + +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ check-pass + +#![feature(type_alias_impl_trait)] + +type Foo = impl Sized; + +struct Bar(T); + +impl Bar { + fn bar(self) { + self.foo() + } +} + +impl Bar { + fn foo(self) {} +} + +fn foo() -> Foo { + 42_u32 +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/method_resolution3.current.stderr b/tests/ui/type-alias-impl-trait/method_resolution3.current.stderr new file mode 100644 index 000000000000..e992d059daf7 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/method_resolution3.current.stderr @@ -0,0 +1,21 @@ +error[E0307]: invalid `self` parameter type: `Bar` + --> $DIR/method_resolution3.rs:16:18 + | +LL | fn bar(self: Bar) { + | ^^^^^^^^ + | + = note: type of `self` must be `Self` or a type that dereferences to it + = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

] { + loop {} +} +pub fn bet() -> R<[Q; 32]> { + loop {} +} // in_args test -pub fn alpha(_x: R<&'static [P]>) { loop {} } -pub fn beta(_x: [R; 32]) { loop {} } +pub fn alpha(_x: R<&'static [P]>) { + loop {} +} +pub fn beta(_x: [R; 32]) { + loop {} +} pub trait TraitCat {} pub trait TraitDog {} diff --git a/tests/rustdoc-js/struct-like-variant.rs b/tests/rustdoc-js/struct-like-variant.rs index 2f52a319ab9a..aafde9957ff8 100644 --- a/tests/rustdoc-js/struct-like-variant.rs +++ b/tests/rustdoc-js/struct-like-variant.rs @@ -3,6 +3,6 @@ pub enum Enum { Bar { /// This is a name. - name: String - } + name: String, + }, } diff --git a/tests/rustdoc-js/summaries.rs b/tests/rustdoc-js/summaries.rs index 1ee1c34aa156..9e8fc820245e 100644 --- a/tests/rustdoc-js/summaries.rs +++ b/tests/rustdoc-js/summaries.rs @@ -1,6 +1,5 @@ #![crate_type = "lib"] #![crate_name = "summaries"] - #![allow(rustdoc::broken_intra_doc_links)] //! This *summary* has a [link], [`code`], and [`Sidebar2`] intra-doc. diff --git a/tests/rustdoc-js/tuple-unit.rs b/tests/rustdoc-js/tuple-unit.rs index 93f9a671cbc3..4531545cf6c5 100644 --- a/tests/rustdoc-js/tuple-unit.rs +++ b/tests/rustdoc-js/tuple-unit.rs @@ -3,16 +3,24 @@ pub struct Q; pub struct R(T); // Checks that tuple and unit both work -pub fn side_effect() { } +pub fn side_effect() {} // Check a non-tuple -pub fn not_tuple() -> P { loop {} } +pub fn not_tuple() -> P { + loop {} +} // Check a 1-tuple -pub fn one() -> (P,) { loop {} } +pub fn one() -> (P,) { + loop {} +} // Check a 2-tuple -pub fn two() -> (P,P) { loop {} } +pub fn two() -> (P, P) { + loop {} +} // Check a nested tuple -pub fn nest() -> (Q, R<(u32,)>) { loop {} } +pub fn nest() -> (Q, R<(u32,)>) { + loop {} +} diff --git a/tests/rustdoc-js/type-parameters.rs b/tests/rustdoc-js/type-parameters.rs index cda5e26171fc..0edbfbea178b 100644 --- a/tests/rustdoc-js/type-parameters.rs +++ b/tests/rustdoc-js/type-parameters.rs @@ -1,15 +1,23 @@ -#![crate_name="foo"] +#![crate_name = "foo"] pub trait Some {} impl Some for () {} pub trait Other {} impl Other for () {} -pub fn alef() -> T { loop {} } -pub fn alpha() -> impl Some { } +pub fn alef() -> T { + loop {} +} +pub fn alpha() -> impl Some {} -pub fn bet(t: T) -> U { loop {} } +pub fn bet(t: T) -> U { + loop {} +} pub fn beta(t: T) -> T {} -pub fn other(t: T, u: U) { loop {} } -pub fn alternate(t: T, u: T) { loop {} } +pub fn other(t: T, u: U) { + loop {} +} +pub fn alternate(t: T, u: T) { + loop {} +} diff --git a/tests/rustdoc-js/underscoredtype.js b/tests/rustdoc-js/underscoredtype.js new file mode 100644 index 000000000000..06e3b07b0960 --- /dev/null +++ b/tests/rustdoc-js/underscoredtype.js @@ -0,0 +1,62 @@ +const EXPECTED = [ + { + 'query': 'pid_t', + 'correction': null, + 'proposeCorrectionFrom': null, + 'proposeCorrectionTo': null, + 'others': [ + { 'path': 'underscoredtype::unix', 'name': 'pid_t' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'set_pid' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'get_pid' }, + ], + }, + { + 'query': 'pidt', + 'correction': 'pid_t', + 'proposeCorrectionFrom': null, + 'proposeCorrectionTo': null, + 'others': [ + { 'path': 'underscoredtype::unix', 'name': 'pid_t' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'set_pid' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'get_pid' }, + ], + }, + { + 'query': 'unix::pid_t', + 'correction': null, + 'proposeCorrectionFrom': null, + 'proposeCorrectionTo': null, + 'others': [ + { 'path': 'underscoredtype::unix', 'name': 'pid_t' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'set_pid' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'get_pid' }, + ], + }, + { + 'query': 'unix::pidt', + 'correction': 'pid_t', + 'proposeCorrectionFrom': null, + 'proposeCorrectionTo': null, + 'others': [ + { 'path': 'underscoredtype::unix', 'name': 'pid_t' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'set_pid' }, + ], + 'returned': [ + { 'path': 'underscoredtype::unix', 'name': 'get_pid' }, + ], + }, +]; diff --git a/tests/rustdoc-js/underscoredtype.rs b/tests/rustdoc-js/underscoredtype.rs new file mode 100644 index 000000000000..83c532cf855d --- /dev/null +++ b/tests/rustdoc-js/underscoredtype.rs @@ -0,0 +1,8 @@ +pub mod unix { + #[allow(non_camel_case_types)] + pub type pid_t = i32; + pub fn get_pid() -> pid_t { + 0 + } + pub fn set_pid(_: pid_t) {} +} diff --git a/tests/rustdoc-js/where-clause.rs b/tests/rustdoc-js/where-clause.rs index 56c01019fb69..c0fede1cfd59 100644 --- a/tests/rustdoc-js/where-clause.rs +++ b/tests/rustdoc-js/where-clause.rs @@ -4,27 +4,46 @@ pub trait Trait { fn thank_you(x: T); } -pub fn abracadabra(_: X) where X: Trait {} +pub fn abracadabra(_: X) +where + X: Trait, +{ +} -pub fn alacazam() -> X where X: Trait {} +pub fn alacazam() -> X +where + X: Trait, +{ +} pub trait T1 {} pub trait T2<'a, T> { fn please(_: &'a T); } -pub fn presto(_: A, _: B) where A: T1, B: for <'b> T2<'b, Nested> {} +pub fn presto(_: A, _: B) +where + A: T1, + B: for<'b> T2<'b, Nested>, +{ +} pub trait Shazam {} -pub fn bippety() -> &'static X where X: Shazam { +pub fn bippety() -> &'static X +where + X: Shazam, +{ panic!() } pub struct Drizzel(T); impl Drizzel { - pub fn boppety(&self) -> &T where T: Shazam { + pub fn boppety(&self) -> &T + where + T: Shazam, + { panic!(); } } diff --git a/tests/rustdoc-json/keyword_private.rs b/tests/rustdoc-json/keyword_private.rs new file mode 100644 index 000000000000..1c2b7d021550 --- /dev/null +++ b/tests/rustdoc-json/keyword_private.rs @@ -0,0 +1,20 @@ +// Ensure keyword docs are present with --document-private-items + +//@ compile-flags: --document-private-items +#![feature(rustdoc_internals)] + +// @!has "$.index[*][?(@.name=='match')]" +// @has "$.index[*][?(@.name=='foo')]" +// @is "$.index[*][?(@.name=='foo')].attrs" '["#[doc(keyword = \"match\")]"]' +// @is "$.index[*][?(@.name=='foo')].docs" '"this is a test!"' +#[doc(keyword = "match")] +/// this is a test! +pub mod foo {} + +// @!has "$.index[*][?(@.name=='hello')]" +// @has "$.index[*][?(@.name=='bar')]" +// @is "$.index[*][?(@.name=='bar')].attrs" '["#[doc(keyword = \"hello\")]"]' +// @is "$.index[*][?(@.name=='bar')].docs" '"hello"' +#[doc(keyword = "hello")] +/// hello +mod bar {} diff --git a/tests/rustdoc-json/lifetime/longest.rs b/tests/rustdoc-json/lifetime/longest.rs index 419b0b4fcab1..dccad41a861b 100644 --- a/tests/rustdoc-json/lifetime/longest.rs +++ b/tests/rustdoc-json/lifetime/longest.rs @@ -23,9 +23,5 @@ // @is "$.index[*][?(@.name=='longest')].inner.function.decl.output.borrowed_ref.type.primitive" \"str\" pub fn longest<'a>(l: &'a str, r: &'a str) -> &'a str { - if l.len() > r.len() { - l - } else { - r - } + if l.len() > r.len() { l } else { r } } diff --git a/tests/rustdoc-json/non_lifetime_binders.rs b/tests/rustdoc-json/non_lifetime_binders.rs index d925fcd52215..6f0732646ca1 100644 --- a/tests/rustdoc-json/non_lifetime_binders.rs +++ b/tests/rustdoc-json/non_lifetime_binders.rs @@ -12,4 +12,8 @@ pub struct Wrapper(std::marker::PhantomData); // @is "$.index[*][?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }' // @is "$.index[*][?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[1].name" \"T\" // @is "$.index[*][?(@.name=='foo')].inner.function.generics.where_predicates[0].bound_predicate.generic_params[1].kind" '{ "type": { "bounds": [], "default": null, "synthetic": false } }' -pub fn foo() where for<'a, T> &'a Wrapper: Trait {} +pub fn foo() +where + for<'a, T> &'a Wrapper: Trait, +{ +} diff --git a/tests/rustdoc-json/output_generics.rs b/tests/rustdoc-json/output_generics.rs index d421eafbb47b..e9df64b79e30 100644 --- a/tests/rustdoc-json/output_generics.rs +++ b/tests/rustdoc-json/output_generics.rs @@ -16,12 +16,19 @@ pub trait Trait { fn handle(value: T) -> Self; } -impl Trait for T where T: From { - fn handle(_: U) -> Self { unimplemented!() } +impl Trait for T +where + T: From, +{ + fn handle(_: U) -> Self { + unimplemented!() + } } impl<'a, R> Trait<&'a mut Events> for Other { - fn handle(_: &'a mut Events) -> Self { unimplemented!() } + fn handle(_: &'a mut Events) -> Self { + unimplemented!() + } } fn this_compiles<'a, R>(value: &'a mut Events) { diff --git a/tests/rustdoc-json/type/extern.rs b/tests/rustdoc-json/type/extern.rs index c30146ce9e03..59e099ec9fce 100644 --- a/tests/rustdoc-json/type/extern.rs +++ b/tests/rustdoc-json/type/extern.rs @@ -1,6 +1,6 @@ #![feature(extern_types)] -extern { +extern "C" { /// No inner information pub type Foo; } diff --git a/tests/rustdoc-json/unions/impl.rs b/tests/rustdoc-json/unions/impl.rs index 7456892df1bb..1515f7d93971 100644 --- a/tests/rustdoc-json/unions/impl.rs +++ b/tests/rustdoc-json/unions/impl.rs @@ -4,7 +4,7 @@ // @has "$.index[*][?(@.name=='Ux')].inner.union" pub union Ux { a: u32, - b: u64 + b: u64, } // @is "$.index[*][?(@.name=='Num')].visibility" \"public\" diff --git a/tests/rustdoc-json/unions/union.rs b/tests/rustdoc-json/unions/union.rs index 22b70e1ce8c5..1089d9c45585 100644 --- a/tests/rustdoc-json/unions/union.rs +++ b/tests/rustdoc-json/unions/union.rs @@ -7,7 +7,6 @@ pub union Union { float: f32, } - // @has "$.index[*][?(@.name=='make_int_union')].inner.function.decl.output.resolved_path" // @is "$.index[*][?(@.name=='make_int_union')].inner.function.decl.output.resolved_path.id" $Union pub fn make_int_union(int: i32) -> Union { diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs deleted file mode 100644 index 5398d5833c75..000000000000 --- a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs +++ /dev/null @@ -1,83 +0,0 @@ -// This test ensures that warnings are working as expected for "custom_code_classes_in_docs" -// feature. - -#![feature(custom_code_classes_in_docs)] -#![deny(warnings)] -#![feature(no_core)] -#![no_core] - -/// ```{. } -/// main; -/// ``` -//~^^^ ERROR unexpected ` ` character after `.` -pub fn foo() {} - -/// ```{class= a} -/// main; -/// ``` -//~^^^ ERROR unexpected ` ` character after `=` -pub fn foo2() {} - -/// ```{#id} -/// main; -/// ``` -//~^^^ ERROR unexpected character `#` -pub fn foo3() {} - -/// ```{{ -/// main; -/// ``` -//~^^^ ERROR unexpected character `{` -pub fn foo4() {} - -/// ```} -/// main; -/// ``` -//~^^^ ERROR unexpected character `}` -pub fn foo5() {} - -/// ```) -/// main; -/// ``` -//~^^^ ERROR unexpected character `)` -pub fn foo6() {} - -/// ```{class=} -/// main; -/// ``` -//~^^^ ERROR unexpected `}` character after `=` -pub fn foo7() {} - -/// ```( -/// main; -/// ``` -//~^^^ ERROR unclosed comment: missing `)` at the end -pub fn foo8() {} - -/// ```{class=one=two} -/// main; -/// ``` -//~^^^ ERROR unexpected `=` character -pub fn foo9() {} - -/// ```{.one.two} -/// main; -/// ``` -pub fn foo10() {} - -/// ```{class=(one} -/// main; -/// ``` -//~^^^ ERROR unexpected `(` character after `=` -pub fn foo11() {} - -/// ```{class=one.two} -/// main; -/// ``` -pub fn foo12() {} - -/// ```{(comment)} -/// main; -/// ``` -//~^^^ ERROR unexpected character `(` -pub fn foo13() {} diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr deleted file mode 100644 index 14b4b3bab3fa..000000000000 --- a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr +++ /dev/null @@ -1,97 +0,0 @@ -error: unexpected ` ` character after `.` - --> $DIR/custom_code_classes_in_docs-warning.rs:9:1 - | -LL | / /// ```{. } -LL | | /// main; -LL | | /// ``` - | |_______^ - | -note: the lint level is defined here - --> $DIR/custom_code_classes_in_docs-warning.rs:5:9 - | -LL | #![deny(warnings)] - | ^^^^^^^^ - = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]` - -error: unexpected ` ` character after `=` - --> $DIR/custom_code_classes_in_docs-warning.rs:15:1 - | -LL | / /// ```{class= a} -LL | | /// main; -LL | | /// ``` - | |_______^ - -error: unexpected character `#` - --> $DIR/custom_code_classes_in_docs-warning.rs:21:1 - | -LL | / /// ```{#id} -LL | | /// main; -LL | | /// ``` - | |_______^ - -error: unexpected character `{` - --> $DIR/custom_code_classes_in_docs-warning.rs:27:1 - | -LL | / /// ```{{ -LL | | /// main; -LL | | /// ``` - | |_______^ - -error: unexpected character `}` - --> $DIR/custom_code_classes_in_docs-warning.rs:33:1 - | -LL | / /// ```} -LL | | /// main; -LL | | /// ``` - | |_______^ - -error: unexpected character `)` - --> $DIR/custom_code_classes_in_docs-warning.rs:39:1 - | -LL | / /// ```) -LL | | /// main; -LL | | /// ``` - | |_______^ - -error: unexpected `}` character after `=` - --> $DIR/custom_code_classes_in_docs-warning.rs:45:1 - | -LL | / /// ```{class=} -LL | | /// main; -LL | | /// ``` - | |_______^ - -error: unclosed comment: missing `)` at the end - --> $DIR/custom_code_classes_in_docs-warning.rs:51:1 - | -LL | / /// ```( -LL | | /// main; -LL | | /// ``` - | |_______^ - -error: unexpected `=` character - --> $DIR/custom_code_classes_in_docs-warning.rs:57:1 - | -LL | / /// ```{class=one=two} -LL | | /// main; -LL | | /// ``` - | |_______^ - -error: unexpected `(` character after `=` - --> $DIR/custom_code_classes_in_docs-warning.rs:68:1 - | -LL | / /// ```{class=(one} -LL | | /// main; -LL | | /// ``` - | |_______^ - -error: unexpected character `(` - --> $DIR/custom_code_classes_in_docs-warning.rs:79:1 - | -LL | / /// ```{(comment)} -LL | | /// main; -LL | | /// ``` - | |_______^ - -error: aborting due to 11 previous errors - diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs b/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs index 57d9038cb0ce..6b1aa455d981 100644 --- a/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs +++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.rs @@ -1,7 +1,6 @@ // This test ensures that warnings are working as expected for "custom_code_classes_in_docs" // feature. -#![feature(custom_code_classes_in_docs)] #![deny(warnings)] #![feature(no_core)] #![no_core] diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr b/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr index ad049804213d..fc47404734ee 100644 --- a/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr +++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning3.stderr @@ -1,5 +1,5 @@ error: unclosed quote string `"` - --> $DIR/custom_code_classes_in_docs-warning3.rs:9:1 + --> $DIR/custom_code_classes_in_docs-warning3.rs:8:1 | LL | / /// ```{class="} LL | | /// main; @@ -11,14 +11,14 @@ LL | | /// ``` | |_______^ | note: the lint level is defined here - --> $DIR/custom_code_classes_in_docs-warning3.rs:5:9 + --> $DIR/custom_code_classes_in_docs-warning3.rs:4:9 | LL | #![deny(warnings)] | ^^^^^^^^ = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]` error: unclosed quote string `"` - --> $DIR/custom_code_classes_in_docs-warning3.rs:9:1 + --> $DIR/custom_code_classes_in_docs-warning3.rs:8:1 | LL | / /// ```{class="} LL | | /// main; diff --git a/tests/rustdoc-ui/doctest/auxiliary/pub_trait.rs b/tests/rustdoc-ui/doctest/auxiliary/pub_trait.rs new file mode 100644 index 000000000000..0a47fdc74d72 --- /dev/null +++ b/tests/rustdoc-ui/doctest/auxiliary/pub_trait.rs @@ -0,0 +1 @@ +pub trait Trait {} diff --git a/tests/rustdoc-ui/doctest/non-local-defs-impl.rs b/tests/rustdoc-ui/doctest/non-local-defs-impl.rs new file mode 100644 index 000000000000..c984e097c046 --- /dev/null +++ b/tests/rustdoc-ui/doctest/non-local-defs-impl.rs @@ -0,0 +1,31 @@ +//@ check-fail +//@ edition:2018 +//@ failure-status: 101 +//@ aux-build:pub_trait.rs +//@ compile-flags: --test --test-args --test-threads=1 +//@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" + +#![doc(test(attr(deny(non_local_definitions))))] +#![doc(test(attr(allow(dead_code))))] + +/// This will produce a warning: +/// ```rust,no_run +/// # extern crate pub_trait; +/// # use pub_trait::Trait; +/// +/// struct Local; +/// impl Trait for &Local {} +/// ``` +/// +/// But this shoudln't produce a warning: +/// ```rust,no_run +/// # extern crate pub_trait; +/// # use pub_trait::Trait; +/// +/// struct Local; +/// impl Trait for &Local {} +/// +/// # fn main() {} +/// ``` +pub fn doctest() {} diff --git a/tests/rustdoc-ui/doctest/non-local-defs-impl.stdout b/tests/rustdoc-ui/doctest/non-local-defs-impl.stdout new file mode 100644 index 000000000000..27797e22f8ec --- /dev/null +++ b/tests/rustdoc-ui/doctest/non-local-defs-impl.stdout @@ -0,0 +1,37 @@ + +running 2 tests +test $DIR/non-local-defs-impl.rs - doctest (line 13) - compile ... FAILED +test $DIR/non-local-defs-impl.rs - doctest (line 22) - compile ... ok + +failures: + +---- $DIR/non-local-defs-impl.rs - doctest (line 13) stdout ---- +error: non-local `impl` definition, `impl` blocks should be written at the same level as their item + --> $DIR/non-local-defs-impl.rs:18:1 + | +LL | impl Trait for &Local {} + | ^^^^^-----^^^^^------ + | | | + | | `&'_ Local` is not local + | | help: remove `&` to make the `impl` local + | `Trait` is not local + | + = note: `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type + = note: an `impl` is never scoped, even when it is nested inside an item, as it may impact type checking outside of that item, which can be the case if neither the trait or the self type are at the same nesting level as the `impl` + = help: make this doc-test a standalone test with its own `fn main() { ... }` + = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue +note: the lint level is defined here + --> $DIR/non-local-defs-impl.rs:11:9 + | +LL | #![deny(non_local_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +Couldn't compile the test. + +failures: + $DIR/non-local-defs-impl.rs - doctest (line 13) + +test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/non_local_defs.stderr b/tests/rustdoc-ui/doctest/non_local_defs.stderr index 39a25de1aae7..2b47e6b5bc4d 100644 --- a/tests/rustdoc-ui/doctest/non_local_defs.stderr +++ b/tests/rustdoc-ui/doctest/non_local_defs.stderr @@ -1,4 +1,4 @@ -warning: non-local `macro_rules!` definition, they should be avoided as they go against expectation +warning: non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module --> $DIR/non_local_defs.rs:9:1 | LL | macro_rules! a_macro { () => {} } @@ -6,7 +6,6 @@ LL | macro_rules! a_macro { () => {} } | = help: remove the `#[macro_export]` or make this doc-test a standalone test with its own `fn main() { ... }` = note: a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute - = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue = note: `#[warn(non_local_definitions)]` on by default diff --git a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs deleted file mode 100644 index e96444039f4c..000000000000 --- a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ check-pass - -/// ```{class=language-c} -/// int main(void) { return 0; } -/// ``` -//~^^^ WARNING custom classes in code blocks will change behaviour -//~| NOTE found these custom classes: class=language-c -//~| NOTE see issue #79483 -//~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -//~| HELP add `#![feature(custom_code_classes_in_docs)]` to the crate attributes to enable -pub struct Bar; - -/// ```ASN.1 -/// int main(void) { return 0; } -/// ``` -pub struct Bar2; diff --git a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr deleted file mode 100644 index 822806997c26..000000000000 --- a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr +++ /dev/null @@ -1,15 +0,0 @@ -warning: custom classes in code blocks will change behaviour - --> $DIR/feature-gate-custom_code_classes_in_docs.rs:3:1 - | -LL | / /// ```{class=language-c} -LL | | /// int main(void) { return 0; } -LL | | /// ``` - | |_______^ - | - = note: see issue #79483 for more information - = help: add `#![feature(custom_code_classes_in_docs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: found these custom classes: class=language-c - -warning: 1 warning emitted - diff --git a/tests/rustdoc-ui/ice-blanket-impl-119792.rs b/tests/rustdoc-ui/ice-blanket-impl-119792.rs new file mode 100644 index 000000000000..90f0ea8469b9 --- /dev/null +++ b/tests/rustdoc-ui/ice-blanket-impl-119792.rs @@ -0,0 +1,19 @@ +//@ check-pass +// https://github.com/rust-lang/rust/issues/119792 + +struct Wrapper(T); + +trait Div {} +trait Mul { + type Output; +} + +impl Mul for Wrapper { + type Output = (); +} + +impl Div for Wrapper {} + +pub trait NumOps {} + +impl NumOps for T where T: Mul + Div {} diff --git a/tests/rustdoc-ui/invalid_associated_const.rs b/tests/rustdoc-ui/invalid_associated_const.rs index 6f211a383a6f..f93834268f63 100644 --- a/tests/rustdoc-ui/invalid_associated_const.rs +++ b/tests/rustdoc-ui/invalid_associated_const.rs @@ -2,8 +2,8 @@ trait T { type A: S = 34>; - //~^ ERROR associated type bindings are not allowed here - //~| ERROR associated type bindings are not allowed here + //~^ ERROR associated item constraints are not allowed here + //~| ERROR associated item constraints are not allowed here } trait S { diff --git a/tests/rustdoc-ui/invalid_associated_const.stderr b/tests/rustdoc-ui/invalid_associated_const.stderr index 5eaddc2b8c98..6e5ddc449828 100644 --- a/tests/rustdoc-ui/invalid_associated_const.stderr +++ b/tests/rustdoc-ui/invalid_associated_const.stderr @@ -1,22 +1,22 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/invalid_associated_const.rs:4:17 | LL | type A: S = 34>; - | ^^^^^^^^ associated type not allowed here + | ^^^^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | LL | type A: S = 34>; | ~~~~~~~~~~ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/invalid_associated_const.rs:4:17 | LL | type A: S = 34>; - | ^^^^^^^^ associated type not allowed here + | ^^^^^^^^ associated item constraint not allowed here | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider removing this type binding +help: consider removing this associated item binding | LL | type A: S = 34>; | ~~~~~~~~~~ diff --git a/tests/rustdoc-ui/issue-102467.rs b/tests/rustdoc-ui/issue-102467.rs index a27e61569794..d9bd48103d24 100644 --- a/tests/rustdoc-ui/issue-102467.rs +++ b/tests/rustdoc-ui/issue-102467.rs @@ -5,8 +5,8 @@ trait T { type A: S = 34>; - //~^ ERROR associated type bindings are not allowed here - //~| ERROR associated type bindings are not allowed here + //~^ ERROR associated item constraints are not allowed here + //~| ERROR associated item constraints are not allowed here } trait S { diff --git a/tests/rustdoc-ui/issue-102467.stderr b/tests/rustdoc-ui/issue-102467.stderr index 119ca949e999..99f911023192 100644 --- a/tests/rustdoc-ui/issue-102467.stderr +++ b/tests/rustdoc-ui/issue-102467.stderr @@ -1,22 +1,22 @@ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-102467.rs:7:17 | LL | type A: S = 34>; - | ^^^^^^^^ associated type not allowed here + | ^^^^^^^^ associated item constraint not allowed here | -help: consider removing this type binding +help: consider removing this associated item binding | LL | type A: S = 34>; | ~~~~~~~~~~ -error[E0229]: associated type bindings are not allowed here +error[E0229]: associated item constraints are not allowed here --> $DIR/issue-102467.rs:7:17 | LL | type A: S = 34>; - | ^^^^^^^^ associated type not allowed here + | ^^^^^^^^ associated item constraint not allowed here | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider removing this type binding +help: consider removing this associated item binding | LL | type A: S = 34>; | ~~~~~~~~~~ diff --git a/tests/rustdoc-ui/issues/issue-91713.stdout b/tests/rustdoc-ui/issues/issue-91713.stdout index cc3c8385d9a8..1ea3dbfb59f3 100644 --- a/tests/rustdoc-ui/issues/issue-91713.stdout +++ b/tests/rustdoc-ui/issues/issue-91713.stdout @@ -1,5 +1,4 @@ Available passes for running rustdoc: -check-custom-code-classes - check for custom code classes without the feature-gate enabled check_doc_test_visibility - run various visibility-related lints on doctests strip-aliased-non-local - strips all non-local private aliased items from the output strip-hidden - strips all `#[doc(hidden)]` items from the output @@ -12,7 +11,6 @@ calculate-doc-coverage - counts the number of items with and without documentati run-lints - runs some of rustdoc's lints Default passes for rustdoc: -check-custom-code-classes collect-trait-impls check_doc_test_visibility strip-aliased-non-local diff --git a/tests/rustdoc/issue-95633.rs b/tests/rustdoc-ui/pub-use-primitive-document-private-items-95633.rs similarity index 71% rename from tests/rustdoc/issue-95633.rs rename to tests/rustdoc-ui/pub-use-primitive-document-private-items-95633.rs index 5695ef579f2b..d53a67cde8b4 100644 --- a/tests/rustdoc/issue-95633.rs +++ b/tests/rustdoc-ui/pub-use-primitive-document-private-items-95633.rs @@ -1,6 +1,8 @@ +//@ check-pass //@ compile-flags: --document-private-items // This ensures that no ICE is triggered when rustdoc is run on this code. +// https://github.com/rust-lang/rust/issues/95633 mod stdlib { pub (crate) use std::i8; diff --git a/tests/rustdoc-ui/remap-path-prefix-failed-doctest-output.rs b/tests/rustdoc-ui/remap-path-prefix-failed-doctest-output.rs new file mode 100644 index 000000000000..2b220370d77a --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-failed-doctest-output.rs @@ -0,0 +1,14 @@ +// FIXME: if/when the output of the test harness can be tested on its own, this test should be +// adapted to use that, and that normalize line can go away + +//@ failure-status: 101 +//@ compile-flags:--test -Z unstable-options --remap-path-prefix={{src-base}}=remapped_path --test-args --test-threads=1 +//@ rustc-env:RUST_BACKTRACE=0 +//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout-test "exit (status|code): 101" -> "exit status: 101" + +// doctest fails at runtime +/// ``` +/// panic!("oh no"); +/// ``` +pub struct SomeStruct; diff --git a/tests/rustdoc-ui/remap-path-prefix-failed-doctest-output.stdout b/tests/rustdoc-ui/remap-path-prefix-failed-doctest-output.stdout new file mode 100644 index 000000000000..2102e2c38918 --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-failed-doctest-output.stdout @@ -0,0 +1,21 @@ + +running 1 test +test remapped_path/remap-path-prefix-failed-doctest-output.rs - SomeStruct (line 11) ... FAILED + +failures: + +---- remapped_path/remap-path-prefix-failed-doctest-output.rs - SomeStruct (line 11) stdout ---- +Test executable failed (exit status: 101). + +stderr: +thread 'main' panicked at remapped_path/remap-path-prefix-failed-doctest-output.rs:3:1: +oh no +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + + + +failures: + remapped_path/remap-path-prefix-failed-doctest-output.rs - SomeStruct (line 11) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-invalid-doctest.rs b/tests/rustdoc-ui/remap-path-prefix-invalid-doctest.rs new file mode 100644 index 000000000000..2e023e32d7a7 --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-invalid-doctest.rs @@ -0,0 +1,13 @@ +// FIXME: if/when the output of the test harness can be tested on its own, this test should be +// adapted to use that, and that normalize line can go away + +//@ failure-status: 101 +//@ compile-flags:--test -Z unstable-options --remap-path-prefix={{src-base}}=remapped_path --test-args --test-threads=1 +//@ rustc-env:RUST_BACKTRACE=0 +//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" + +// doctest fails to compile +/// ``` +/// this is not real code +/// ``` +pub struct SomeStruct; diff --git a/tests/rustdoc-ui/remap-path-prefix-invalid-doctest.stdout b/tests/rustdoc-ui/remap-path-prefix-invalid-doctest.stdout new file mode 100644 index 000000000000..a05b51699894 --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-invalid-doctest.stdout @@ -0,0 +1,22 @@ + +running 1 test +test remapped_path/remap-path-prefix-invalid-doctest.rs - SomeStruct (line 10) ... FAILED + +failures: + +---- remapped_path/remap-path-prefix-invalid-doctest.rs - SomeStruct (line 10) stdout ---- +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `is` + --> remapped_path/remap-path-prefix-invalid-doctest.rs:11:6 + | +LL | this is not real code + | ^^ expected one of 8 possible tokens + +error: aborting due to 1 previous error + +Couldn't compile the test. + +failures: + remapped_path/remap-path-prefix-invalid-doctest.rs - SomeStruct (line 10) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/remap-path-prefix-passed-doctest-output.rs b/tests/rustdoc-ui/remap-path-prefix-passed-doctest-output.rs new file mode 100644 index 000000000000..c58f3faeb532 --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-passed-doctest-output.rs @@ -0,0 +1,14 @@ +//@ check-pass +//@ check-run-results + +// FIXME: if/when the output of the test harness can be tested on its own, this test should be +// adapted to use that, and that normalize line can go away + +//@ compile-flags:--test -Z unstable-options --remap-path-prefix={{src-base}}=remapped_path --test-args --test-threads=1 +//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" + +// doctest passes at runtime +/// ``` +/// assert!(true); +/// ``` +pub struct SomeStruct; diff --git a/tests/rustdoc-ui/remap-path-prefix-passed-doctest-output.stdout b/tests/rustdoc-ui/remap-path-prefix-passed-doctest-output.stdout new file mode 100644 index 000000000000..8ffb56948856 --- /dev/null +++ b/tests/rustdoc-ui/remap-path-prefix-passed-doctest-output.stdout @@ -0,0 +1,6 @@ + +running 1 test +test remapped_path/remap-path-prefix-passed-doctest-output.rs - SomeStruct (line 11) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/unable-fulfill-trait.rs b/tests/rustdoc-ui/unable-fulfill-trait.rs index a69f74b09ac0..f3b6256346f0 100644 --- a/tests/rustdoc-ui/unable-fulfill-trait.rs +++ b/tests/rustdoc-ui/unable-fulfill-trait.rs @@ -1,13 +1,16 @@ // This test ensures that it's not crashing rustdoc. pub struct Foo<'a, 'b, T> { - field1: dyn Bar<'a, 'b,>, + field1: dyn Bar<'a, 'b>, //~^ ERROR //~| ERROR + //~| ERROR } pub trait Bar<'x, 's, U> - where U: 'x, - Self:'x, - Self:'s -{} +where + U: 'x, + Self: 'x, + Self: 's, +{ +} diff --git a/tests/rustdoc-ui/unable-fulfill-trait.stderr b/tests/rustdoc-ui/unable-fulfill-trait.stderr index 72f35cb92244..40d103f2a62e 100644 --- a/tests/rustdoc-ui/unable-fulfill-trait.stderr +++ b/tests/rustdoc-ui/unable-fulfill-trait.stderr @@ -1,26 +1,43 @@ error[E0107]: trait takes 1 generic argument but 0 generic arguments were supplied --> $DIR/unable-fulfill-trait.rs:4:17 | -LL | field1: dyn Bar<'a, 'b,>, +LL | field1: dyn Bar<'a, 'b>, | ^^^ expected 1 generic argument | note: trait defined here, with 1 generic parameter: `U` - --> $DIR/unable-fulfill-trait.rs:9:11 + --> $DIR/unable-fulfill-trait.rs:10:11 | LL | pub trait Bar<'x, 's, U> | ^^^ - help: add missing generic argument | -LL | field1: dyn Bar<'a, 'b, U,>, +LL | field1: dyn Bar<'a, 'b, U>, | +++ error[E0227]: ambiguous lifetime bound, explicit lifetime bound required --> $DIR/unable-fulfill-trait.rs:4:13 | -LL | field1: dyn Bar<'a, 'b,>, - | ^^^^^^^^^^^^^^^^ +LL | field1: dyn Bar<'a, 'b>, + | ^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error[E0478]: lifetime bound not satisfied + --> $DIR/unable-fulfill-trait.rs:4:13 + | +LL | field1: dyn Bar<'a, 'b>, + | ^^^^^^^^^^^^^^^ + | +note: lifetime parameter instantiated with the lifetime `'b` as defined here + --> $DIR/unable-fulfill-trait.rs:3:20 + | +LL | pub struct Foo<'a, 'b, T> { + | ^^ +note: but lifetime parameter must outlive the lifetime `'a` as defined here + --> $DIR/unable-fulfill-trait.rs:3:16 + | +LL | pub struct Foo<'a, 'b, T> { + | ^^ -Some errors have detailed explanations: E0107, E0227. +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0107, E0227, E0478. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/rustdoc/issue-82465-asref-for-and-of-local.rs b/tests/rustdoc/asref-for-and-of-local-82465.rs similarity index 63% rename from tests/rustdoc/issue-82465-asref-for-and-of-local.rs rename to tests/rustdoc/asref-for-and-of-local-82465.rs index adf4d111a6cb..e62046889043 100644 --- a/tests/rustdoc/issue-82465-asref-for-and-of-local.rs +++ b/tests/rustdoc/asref-for-and-of-local-82465.rs @@ -1,7 +1,10 @@ +// https://github.com/rust-lang/rust/issues/82465 +#![crate_name = "foo"] + use std::convert::AsRef; pub struct Local; -// @has issue_82465_asref_for_and_of_local/struct.Local.html '//h3[@class="code-header"]' 'impl AsRef for Local' +// @has foo/struct.Local.html '//h3[@class="code-header"]' 'impl AsRef for Local' impl AsRef for Local { fn as_ref(&self) -> &str { todo!() diff --git a/tests/rustdoc/async-fn.rs b/tests/rustdoc/async-fn.rs index 010263f6ad31..4de5d8575b03 100644 --- a/tests/rustdoc/async-fn.rs +++ b/tests/rustdoc/async-fn.rs @@ -79,7 +79,7 @@ struct AsyncFdReadyGuard<'a, T> { x: &'a T } impl Foo { // @has async_fn/struct.Foo.html - // @has - '//*[@class="method"]' 'pub async fn complicated_lifetimes( &self, context: &impl Bar ) -> impl Iterator' + // @has - '//*[@class="method"]' 'pub async fn complicated_lifetimes( &self, context: &impl Bar, ) -> impl Iterator' pub async fn complicated_lifetimes(&self, context: &impl Bar) -> impl Iterator { [0].iter() } diff --git a/tests/rustdoc/auxiliary/cross_crate_generic_typedef.rs b/tests/rustdoc/auxiliary/cross_crate_generic_typedef.rs index f4e020b3b959..d85129a69fc6 100644 --- a/tests/rustdoc/auxiliary/cross_crate_generic_typedef.rs +++ b/tests/rustdoc/auxiliary/cross_crate_generic_typedef.rs @@ -3,3 +3,8 @@ pub struct InlineOne { } pub type InlineU64 = InlineOne; + +pub enum GenericEnum { + Variant(T), + Variant2(T, T), +} diff --git a/tests/rustdoc/const-display.rs b/tests/rustdoc/const-display.rs index c8967f426f0a..959a00102b7e 100644 --- a/tests/rustdoc/const-display.rs +++ b/tests/rustdoc/const-display.rs @@ -24,6 +24,12 @@ pub const unsafe fn foo_unsafe() -> u32 { 42 } #[unstable(feature = "humans", issue = "none")] pub const fn foo2() -> u32 { 42 } +// @has 'foo/fn.foo3.html' '//pre' 'pub const fn foo3() -> u32' +// @!hasraw - '//span[@class="since"]' +#[unstable(feature = "humans", issue = "none")] +#[rustc_const_unstable(feature = "humans", issue = "none")] +pub const fn foo3() -> u32 { 42 } + // @has 'foo/fn.bar2.html' '//pre' 'pub const fn bar2() -> u32' // @has - //span '1.0.0 (const: 1.0.0)' #[stable(feature = "rust1", since = "1.0.0")] diff --git a/tests/rustdoc/custom_code_classes.rs b/tests/rustdoc/custom_code_classes.rs index cd20d8b7d6c9..569857a09cbe 100644 --- a/tests/rustdoc/custom_code_classes.rs +++ b/tests/rustdoc/custom_code_classes.rs @@ -1,6 +1,5 @@ // Test for `custom_code_classes_in_docs` feature. -#![feature(custom_code_classes_in_docs)] #![crate_name = "foo"] #![feature(no_core)] #![no_core] diff --git a/tests/rustdoc/decl-line-wrapping-empty-arg-list.decl.html b/tests/rustdoc/decl-line-wrapping-empty-arg-list.decl.html index 29c08c5bd5de..12c6bc214a72 100644 --- a/tests/rustdoc/decl-line-wrapping-empty-arg-list.decl.html +++ b/tests/rustdoc/decl-line-wrapping-empty-arg-list.decl.html @@ -1,2 +1 @@ -

{ fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - let message = self.assert_kind.diagnostic_message(); + diag.primary_message(match self.lint_kind { + AssertLintKind::ArithmeticOverflow => fluent::mir_transform_arithmetic_overflow, + AssertLintKind::UnconditionalPanic => fluent::mir_transform_operation_will_panic, + }); + let label = self.assert_kind.diagnostic_message(); self.assert_kind.add_args(&mut |name, value| { diag.arg(name, value); }); - diag.span_label(self.span, message); - } - - fn msg(&self) -> DiagMessage { - match self.lint_kind { - AssertLintKind::ArithmeticOverflow => fluent::mir_transform_arithmetic_overflow, - AssertLintKind::UnconditionalPanic => fluent::mir_transform_operation_will_panic, - } + diag.span_label(self.span, label); } } @@ -104,19 +101,16 @@ pub(crate) struct MustNotSupend<'tcx, 'a> { // Needed for def_path_str impl<'a> LintDiagnostic<'a, ()> for MustNotSupend<'_, '_> { fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { + diag.primary_message(fluent::mir_transform_must_not_suspend); diag.span_label(self.yield_sp, fluent::_subdiag::label); if let Some(reason) = self.reason { - diag.subdiagnostic(diag.dcx, reason); + diag.subdiagnostic(reason); } diag.span_help(self.src_sp, fluent::_subdiag::help); diag.arg("pre", self.pre); diag.arg("def_path", self.tcx.def_path_str(self.def_id)); diag.arg("post", self.post); } - - fn msg(&self) -> rustc_errors::DiagMessage { - fluent::mir_transform_must_not_suspend - } } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 123166a764d8..b2670040b14b 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -83,8 +83,8 @@ //! that contain `AllocId`s. use rustc_const_eval::const_eval::DummyMachine; -use rustc_const_eval::interpret::{intern_const_alloc_for_constprop, MemoryKind}; -use rustc_const_eval::interpret::{ImmTy, InterpCx, OpTy, Projectable, Scalar}; +use rustc_const_eval::interpret::{intern_const_alloc_for_constprop, MemPlaceMeta, MemoryKind}; +use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable, Scalar}; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; @@ -95,11 +95,11 @@ use rustc_middle::bug; use rustc_middle::mir::interpret::GlobalAlloc; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; -use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::layout::{HasParamEnv, LayoutOf}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::DefId; use rustc_span::DUMMY_SP; -use rustc_target::abi::{self, Abi, Size, VariantIdx, FIRST_VARIANT}; +use rustc_target::abi::{self, Abi, FieldIdx, Size, VariantIdx, FIRST_VARIANT}; use smallvec::SmallVec; use std::borrow::Cow; @@ -177,6 +177,12 @@ enum AggregateTy<'tcx> { Array, Tuple, Def(DefId, ty::GenericArgsRef<'tcx>), + RawPtr { + /// Needed for cast propagation. + data_pointer_ty: Ty<'tcx>, + /// The data pointer can be anything thin, so doesn't determine the output. + output_pointer_ty: Ty<'tcx>, + }, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] @@ -223,7 +229,6 @@ enum Value<'tcx> { NullaryOp(NullOp<'tcx>, Ty<'tcx>), UnaryOp(UnOp, VnIndex), BinaryOp(BinOp, VnIndex, VnIndex), - CheckedBinaryOp(BinOp, VnIndex, VnIndex), Cast { kind: CastKind, value: VnIndex, @@ -234,7 +239,7 @@ enum Value<'tcx> { struct VnState<'body, 'tcx> { tcx: TyCtxt<'tcx>, - ecx: InterpCx<'tcx, 'tcx, DummyMachine>, + ecx: InterpCx<'tcx, DummyMachine>, param_env: ty::ParamEnv<'tcx>, local_decls: &'body LocalDecls<'tcx>, /// Value stored in each local. @@ -325,8 +330,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let is_sized = !self.feature_unsized_locals || self.local_decls[local].ty.is_sized(self.tcx, self.param_env); if is_sized { - self.rev_locals.ensure_contains_elem(value, SmallVec::new); - self.rev_locals[value].push(local); + self.rev_locals.ensure_contains_elem(value, SmallVec::new).push(local); } } @@ -386,11 +390,22 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { AggregateTy::Def(def_id, args) => { self.tcx.type_of(def_id).instantiate(self.tcx, args) } + AggregateTy::RawPtr { output_pointer_ty, .. } => output_pointer_ty, }; let variant = if ty.is_enum() { Some(variant) } else { None }; let ty = self.ecx.layout_of(ty).ok()?; if ty.is_zst() { ImmTy::uninit(ty).into() + } else if matches!(kind, AggregateTy::RawPtr { .. }) { + // Pointers don't have fields, so don't `project_field` them. + let data = self.ecx.read_pointer(fields[0]).ok()?; + let meta = if fields[1].layout.is_zst() { + MemPlaceMeta::None + } else { + MemPlaceMeta::Meta(self.ecx.read_scalar(fields[1]).ok()?) + }; + let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx); + ImmTy::from_immediate(ptr_imm, ty).into() } else if matches!(ty.abi, Abi::Scalar(..) | Abi::ScalarPair(..)) { let dest = self.ecx.allocate(ty, MemoryKind::Stack).ok()?; let variant_dest = if let Some(variant) = variant { @@ -472,7 +487,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let slice = self.evaluated[slice].as_ref()?; let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); let len = slice.len(&self.ecx).ok()?; - let imm = ImmTy::try_from_uint(len, usize_layout)?; + let imm = ImmTy::from_uint(len, usize_layout); imm.into() } NullaryOp(null_op, ty) => { @@ -485,19 +500,21 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let val = match null_op { NullOp::SizeOf => layout.size.bytes(), NullOp::AlignOf => layout.align.abi.bytes(), - NullOp::OffsetOf(fields) => { - layout.offset_of_subfield(&self.ecx, fields.iter()).bytes() - } + NullOp::OffsetOf(fields) => self + .ecx + .tcx + .offset_of_subfield(self.ecx.param_env(), layout, fields.iter()) + .bytes(), NullOp::UbChecks => return None, }; let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); - let imm = ImmTy::try_from_uint(val, usize_layout)?; + let imm = ImmTy::from_uint(val, usize_layout); imm.into() } UnaryOp(un_op, operand) => { let operand = self.evaluated[operand].as_ref()?; let operand = self.ecx.read_immediate(operand).ok()?; - let (val, _) = self.ecx.overflowing_unary_op(un_op, &operand).ok()?; + let val = self.ecx.unary_op(un_op, &operand).ok()?; val.into() } BinaryOp(bin_op, lhs, rhs) => { @@ -505,23 +522,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let lhs = self.ecx.read_immediate(lhs).ok()?; let rhs = self.evaluated[rhs].as_ref()?; let rhs = self.ecx.read_immediate(rhs).ok()?; - let (val, _) = self.ecx.overflowing_binary_op(bin_op, &lhs, &rhs).ok()?; + let val = self.ecx.binary_op(bin_op, &lhs, &rhs).ok()?; val.into() } - CheckedBinaryOp(bin_op, lhs, rhs) => { - let lhs = self.evaluated[lhs].as_ref()?; - let lhs = self.ecx.read_immediate(lhs).ok()?; - let rhs = self.evaluated[rhs].as_ref()?; - let rhs = self.ecx.read_immediate(rhs).ok()?; - let (val, overflowed) = self.ecx.overflowing_binary_op(bin_op, &lhs, &rhs).ok()?; - let tuple = Ty::new_tup_from_iter( - self.tcx, - [val.layout.ty, self.tcx.types.bool].into_iter(), - ); - let tuple = self.ecx.layout_of(tuple).ok()?; - ImmTy::from_scalar_pair(val.to_scalar(), Scalar::from_bool(overflowed), tuple) - .into() - } Cast { kind, value, from: _, to } => match kind { CastKind::IntToInt | CastKind::IntToFloat => { let value = self.evaluated[value].as_ref()?; @@ -568,11 +571,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { let ret = self.ecx.ptr_to_ptr(&src, to).ok()?; ret.into() } - CastKind::PointerCoercion( - ty::adjustment::PointerCoercion::MutToConstPointer - | ty::adjustment::PointerCoercion::ArrayToPointer - | ty::adjustment::PointerCoercion::UnsafeFnPointer, - ) => { + CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer) => { let src = self.evaluated[value].as_ref()?; let src = self.ecx.read_immediate(src).ok()?; let to = self.ecx.layout_of(to).ok()?; @@ -831,24 +830,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // on both operands for side effect. let lhs = lhs?; let rhs = rhs?; - if let Some(value) = self.simplify_binary(op, false, ty, lhs, rhs) { + + if let Some(value) = self.simplify_binary(op, ty, lhs, rhs) { return Some(value); } Value::BinaryOp(op, lhs, rhs) } - Rvalue::CheckedBinaryOp(op, box (ref mut lhs, ref mut rhs)) => { - let ty = lhs.ty(self.local_decls, self.tcx); - let lhs = self.simplify_operand(lhs, location); - let rhs = self.simplify_operand(rhs, location); - // Only short-circuit options after we called `simplify_operand` - // on both operands for side effect. - let lhs = lhs?; - let rhs = rhs?; - if let Some(value) = self.simplify_binary(op, true, ty, lhs, rhs) { - return Some(value); - } - Value::CheckedBinaryOp(op, lhs, rhs) - } Rvalue::UnaryOp(op, ref mut arg) => { let arg = self.simplify_operand(arg, location)?; if let Some(value) = self.simplify_unary(op, arg) { @@ -889,10 +876,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { rvalue: &mut Rvalue<'tcx>, location: Location, ) -> Option { - let Rvalue::Aggregate(box ref kind, ref mut fields) = *rvalue else { bug!() }; + let Rvalue::Aggregate(box ref kind, ref mut field_ops) = *rvalue else { bug!() }; let tcx = self.tcx; - if fields.is_empty() { + if field_ops.is_empty() { let is_zst = match *kind { AggregateKind::Array(..) | AggregateKind::Tuple @@ -911,13 +898,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } } - let (ty, variant_index) = match *kind { + let (mut ty, variant_index) = match *kind { AggregateKind::Array(..) => { - assert!(!fields.is_empty()); + assert!(!field_ops.is_empty()); (AggregateTy::Array, FIRST_VARIANT) } AggregateKind::Tuple => { - assert!(!fields.is_empty()); + assert!(!field_ops.is_empty()); (AggregateTy::Tuple, FIRST_VARIANT) } AggregateKind::Closure(did, args) @@ -928,15 +915,49 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // Do not track unions. AggregateKind::Adt(_, _, _, _, Some(_)) => return None, - // FIXME: Do the extra work to GVN `from_raw_parts` - AggregateKind::RawPtr(..) => return None, + AggregateKind::RawPtr(pointee_ty, mtbl) => { + assert_eq!(field_ops.len(), 2); + let data_pointer_ty = field_ops[FieldIdx::ZERO].ty(self.local_decls, self.tcx); + let output_pointer_ty = Ty::new_ptr(self.tcx, pointee_ty, mtbl); + (AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty }, FIRST_VARIANT) + } }; - let fields: Option> = fields + let fields: Option> = field_ops .iter_mut() .map(|op| self.simplify_operand(op, location).or_else(|| self.new_opaque())) .collect(); - let fields = fields?; + let mut fields = fields?; + + if let AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty } = &mut ty { + let mut was_updated = false; + + // Any thin pointer of matching mutability is fine as the data pointer. + while let Value::Cast { + kind: CastKind::PtrToPtr, + value: cast_value, + from: cast_from, + to: _, + } = self.get(fields[0]) + && let ty::RawPtr(from_pointee_ty, from_mtbl) = cast_from.kind() + && let ty::RawPtr(_, output_mtbl) = output_pointer_ty.kind() + && from_mtbl == output_mtbl + && from_pointee_ty.is_sized(self.tcx, self.param_env) + { + fields[0] = *cast_value; + *data_pointer_ty = *cast_from; + was_updated = true; + } + + if was_updated { + if let Some(const_) = self.try_as_constant(fields[0]) { + field_ops[FieldIdx::ZERO] = Operand::Constant(Box::new(const_)); + } else if let Some(local) = self.try_as_local(fields[0], location) { + field_ops[FieldIdx::ZERO] = Operand::Copy(Place::from(local)); + self.reused_locals.insert(local); + } + } + } if let AggregateTy::Array = ty && fields.len() > 4 @@ -968,6 +989,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { (UnOp::Not, Value::BinaryOp(BinOp::Ne, lhs, rhs)) => { Value::BinaryOp(BinOp::Eq, *lhs, *rhs) } + (UnOp::PtrMetadata, Value::Aggregate(AggregateTy::RawPtr { .. }, _, fields)) => { + return Some(fields[1]); + } _ => return None, }; @@ -978,7 +1002,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn simplify_binary( &mut self, op: BinOp, - checked: bool, lhs_ty: Ty<'tcx>, lhs: VnIndex, rhs: VnIndex, @@ -1007,22 +1030,39 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { use Either::{Left, Right}; let a = as_bits(lhs).map_or(Right(lhs), Left); let b = as_bits(rhs).map_or(Right(rhs), Left); + let result = match (op, a, b) { // Neutral elements. - (BinOp::Add | BinOp::BitOr | BinOp::BitXor, Left(0), Right(p)) + ( + BinOp::Add + | BinOp::AddWithOverflow + | BinOp::AddUnchecked + | BinOp::BitOr + | BinOp::BitXor, + Left(0), + Right(p), + ) | ( BinOp::Add + | BinOp::AddWithOverflow + | BinOp::AddUnchecked | BinOp::BitOr | BinOp::BitXor | BinOp::Sub + | BinOp::SubWithOverflow + | BinOp::SubUnchecked | BinOp::Offset | BinOp::Shl | BinOp::Shr, Right(p), Left(0), ) - | (BinOp::Mul, Left(1), Right(p)) - | (BinOp::Mul | BinOp::Div, Right(p), Left(1)) => p, + | (BinOp::Mul | BinOp::MulWithOverflow | BinOp::MulUnchecked, Left(1), Right(p)) + | ( + BinOp::Mul | BinOp::MulWithOverflow | BinOp::MulUnchecked | BinOp::Div, + Right(p), + Left(1), + ) => p, // Attempt to simplify `x & ALL_ONES` to `x`, with `ALL_ONES` depending on type size. (BinOp::BitAnd, Right(p), Left(ones)) | (BinOp::BitAnd, Left(ones), Right(p)) if ones == layout.size.truncate(u128::MAX) @@ -1031,10 +1071,21 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { p } // Absorbing elements. - (BinOp::Mul | BinOp::BitAnd, _, Left(0)) + ( + BinOp::Mul | BinOp::MulWithOverflow | BinOp::MulUnchecked | BinOp::BitAnd, + _, + Left(0), + ) | (BinOp::Rem, _, Left(1)) | ( - BinOp::Mul | BinOp::Div | BinOp::Rem | BinOp::BitAnd | BinOp::Shl | BinOp::Shr, + BinOp::Mul + | BinOp::MulWithOverflow + | BinOp::MulUnchecked + | BinOp::Div + | BinOp::Rem + | BinOp::BitAnd + | BinOp::Shl + | BinOp::Shr, Left(0), _, ) => self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty), @@ -1046,7 +1097,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.insert_scalar(Scalar::from_uint(ones, layout.size), lhs_ty) } // Sub/Xor with itself. - (BinOp::Sub | BinOp::BitXor, a, b) if a == b => { + (BinOp::Sub | BinOp::SubWithOverflow | BinOp::SubUnchecked | BinOp::BitXor, a, b) + if a == b => + { self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty) } // Comparison: @@ -1060,7 +1113,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { _ => return None, }; - if checked { + if op.is_overflowing() { let false_val = self.insert_bool(false); Some(self.insert_tuple(vec![result, false_val])) } else { @@ -1090,17 +1143,38 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return self.new_opaque(); } - if let PtrToPtr | PointerCoercion(MutToConstPointer) = kind + let mut was_updated = false; + + // If that cast just casts away the metadata again, + if let PtrToPtr = kind + && let Value::Aggregate(AggregateTy::RawPtr { data_pointer_ty, .. }, _, fields) = + self.get(value) + && let ty::RawPtr(to_pointee, _) = to.kind() + && to_pointee.is_sized(self.tcx, self.param_env) + { + from = *data_pointer_ty; + value = fields[0]; + was_updated = true; + if *data_pointer_ty == to { + return Some(fields[0]); + } + } + + if let PtrToPtr = kind && let Value::Cast { kind: inner_kind, value: inner_value, from: inner_from, to: _ } = *self.get(value) - && let PtrToPtr | PointerCoercion(MutToConstPointer) = inner_kind + && let PtrToPtr = inner_kind { from = inner_from; value = inner_value; *kind = PtrToPtr; + was_updated = true; if inner_from == to { return Some(inner_value); } + } + + if was_updated { if let Some(const_) = self.try_as_constant(value) { *operand = Operand::Constant(Box::new(const_)); } else if let Some(local) = self.try_as_local(value, location) { @@ -1116,7 +1190,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Trivial case: we are fetching a statically known length. let place_ty = place.ty(self.local_decls, self.tcx).ty; if let ty::Array(_, len) = place_ty.kind() { - return self.insert_constant(Const::from_ty_const(*len, self.tcx)); + return self.insert_constant(Const::from_ty_const( + *len, + self.tcx.types.usize, + self.tcx, + )); } let mut inner = self.simplify_place_value(place, location)?; @@ -1138,7 +1216,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { && let Some(to) = to.builtin_deref(true) && let ty::Slice(..) = to.kind() { - return self.insert_constant(Const::from_ty_const(*len, self.tcx)); + return self.insert_constant(Const::from_ty_const( + *len, + self.tcx.types.usize, + self.tcx, + )); } // Fallback: a symbolic `Len`. @@ -1147,7 +1229,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } fn op_to_prop_const<'tcx>( - ecx: &mut InterpCx<'_, 'tcx, DummyMachine>, + ecx: &mut InterpCx<'tcx, DummyMachine>, op: &OpTy<'tcx>, ) -> Option> { // Do not attempt to propagate unsized locals. @@ -1168,7 +1250,7 @@ fn op_to_prop_const<'tcx>( // If this constant has scalar ABI, return it as a `ConstValue::Scalar`. if let Abi::Scalar(abi::Scalar::Initialized { .. }) = op.layout.abi && let Ok(scalar) = ecx.read_scalar(op) - && scalar.try_to_int().is_ok() + && scalar.try_to_scalar_int().is_ok() { return Some(ConstValue::Scalar(scalar)); } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 401056cd4960..d04bb8d302e2 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -1,7 +1,6 @@ //! Inlining pass for MIR functions use crate::deref_separator::deref_finder; use rustc_attr::InlineAttr; -use rustc_const_eval::transform::validate::validate_types; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; @@ -11,7 +10,7 @@ use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs} use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::TypeVisitableExt; -use rustc_middle::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt}; +use rustc_middle::ty::{self, Instance, InstanceKind, ParamEnv, Ty, TyCtxt}; use rustc_session::config::{DebugInfo, OptLevel}; use rustc_span::source_map::Spanned; use rustc_span::sym; @@ -21,6 +20,7 @@ use rustc_target::spec::abi::Abi; use crate::cost_checker::CostChecker; use crate::simplify::simplify_cfg; use crate::util; +use crate::validate::validate_types; use std::iter; use std::ops::{Range, RangeFrom}; @@ -225,13 +225,8 @@ impl<'tcx> Inliner<'tcx> { // Normally, this shouldn't be required, but trait normalization failure can create a // validation ICE. let output_type = callee_body.return_ty(); - if !util::relate_types( - self.tcx, - self.param_env, - ty::Variance::Covariant, - output_type, - destination_ty, - ) { + if !util::relate_types(self.tcx, self.param_env, ty::Covariant, output_type, destination_ty) + { trace!(?output_type, ?destination_ty); return Err("failed to normalize return type"); } @@ -261,13 +256,8 @@ impl<'tcx> Inliner<'tcx> { self_arg_ty.into_iter().chain(arg_tuple_tys).zip(callee_body.args_iter()) { let input_type = callee_body.local_decls[input].ty; - if !util::relate_types( - self.tcx, - self.param_env, - ty::Variance::Covariant, - input_type, - arg_ty, - ) { + if !util::relate_types(self.tcx, self.param_env, ty::Covariant, input_type, arg_ty) + { trace!(?arg_ty, ?input_type); return Err("failed to normalize tuple argument type"); } @@ -276,13 +266,8 @@ impl<'tcx> Inliner<'tcx> { for (arg, input) in args.iter().zip(callee_body.args_iter()) { let input_type = callee_body.local_decls[input].ty; let arg_ty = arg.node.ty(&caller_body.local_decls, self.tcx); - if !util::relate_types( - self.tcx, - self.param_env, - ty::Variance::Covariant, - input_type, - arg_ty, - ) { + if !util::relate_types(self.tcx, self.param_env, ty::Covariant, input_type, arg_ty) + { trace!(?arg_ty, ?input_type); return Err("failed to normalize argument type"); } @@ -308,7 +293,7 @@ impl<'tcx> Inliner<'tcx> { } match callee.def { - InstanceDef::Item(_) => { + InstanceKind::Item(_) => { // If there is no MIR available (either because it was not in metadata or // because it has no MIR because it's an extern function), then the inliner // won't cause cycles on this. @@ -317,24 +302,24 @@ impl<'tcx> Inliner<'tcx> { } } // These have no own callable MIR. - InstanceDef::Intrinsic(_) | InstanceDef::Virtual(..) => { + InstanceKind::Intrinsic(_) | InstanceKind::Virtual(..) => { return Err("instance without MIR (intrinsic / virtual)"); } // This cannot result in an immediate cycle since the callee MIR is a shim, which does // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we // do not need to catch this here, we can wait until the inliner decides to continue // inlining a second time. - InstanceDef::VTableShim(_) - | InstanceDef::ReifyShim(..) - | InstanceDef::FnPtrShim(..) - | InstanceDef::ClosureOnceShim { .. } - | InstanceDef::ConstructCoroutineInClosureShim { .. } - | InstanceDef::CoroutineKindShim { .. } - | InstanceDef::DropGlue(..) - | InstanceDef::CloneShim(..) - | InstanceDef::ThreadLocalShim(..) - | InstanceDef::FnPtrAddrShim(..) - | InstanceDef::AsyncDropGlueCtorShim(..) => return Ok(()), + InstanceKind::VTableShim(_) + | InstanceKind::ReifyShim(..) + | InstanceKind::FnPtrShim(..) + | InstanceKind::ClosureOnceShim { .. } + | InstanceKind::ConstructCoroutineInClosureShim { .. } + | InstanceKind::CoroutineKindShim { .. } + | InstanceKind::DropGlue(..) + | InstanceKind::CloneShim(..) + | InstanceKind::ThreadLocalShim(..) + | InstanceKind::FnPtrAddrShim(..) + | InstanceKind::AsyncDropGlueCtorShim(..) => return Ok(()), } if self.tcx.is_constructor(callee_def_id) { @@ -387,7 +372,7 @@ impl<'tcx> Inliner<'tcx> { let callee = Instance::resolve(self.tcx, self.param_env, def_id, args).ok().flatten()?; - if let InstanceDef::Virtual(..) | InstanceDef::Intrinsic(_) = callee.def { + if let InstanceKind::Virtual(..) | InstanceKind::Intrinsic(_) = callee.def { return None; } @@ -399,7 +384,7 @@ impl<'tcx> Inliner<'tcx> { // Additionally, check that the body that we're inlining actually agrees // with the ABI of the trait that the item comes from. - if let InstanceDef::Item(instance_def_id) = callee.def + if let InstanceKind::Item(instance_def_id) = callee.def && self.tcx.def_kind(instance_def_id) == DefKind::AssocFn && let instance_fn_sig = self.tcx.fn_sig(instance_def_id).skip_binder() && instance_fn_sig.abi() != fn_sig.abi() @@ -1078,10 +1063,10 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> { #[instrument(skip(tcx), level = "debug")] fn try_instance_mir<'tcx>( tcx: TyCtxt<'tcx>, - instance: InstanceDef<'tcx>, + instance: InstanceKind<'tcx>, ) -> Result<&'tcx Body<'tcx>, &'static str> { - if let ty::InstanceDef::DropGlue(_, Some(ty)) - | ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) = instance + if let ty::InstanceKind::DropGlue(_, Some(ty)) + | ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)) = instance && let ty::Adt(def, args) = ty.kind() { let fields = def.all_fields(); diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index 8c5f965108bd..35bcd24ce95b 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -3,7 +3,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::mir::TerminatorKind; use rustc_middle::ty::TypeVisitableExt; -use rustc_middle::ty::{self, GenericArgsRef, InstanceDef, TyCtxt}; +use rustc_middle::ty::{self, GenericArgsRef, InstanceKind, TyCtxt}; use rustc_session::Limit; use rustc_span::sym; @@ -22,7 +22,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( "you should not call `mir_callgraph_reachable` on immediate self recursion" ); assert!( - matches!(root.def, InstanceDef::Item(_)), + matches!(root.def, InstanceKind::Item(_)), "you should not call `mir_callgraph_reachable` on shims" ); assert!( @@ -70,7 +70,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( } match callee.def { - InstanceDef::Item(_) => { + InstanceKind::Item(_) => { // If there is no MIR available (either because it was not in metadata or // because it has no MIR because it's an extern function), then the inliner // won't cause cycles on this. @@ -80,24 +80,24 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( } } // These have no own callable MIR. - InstanceDef::Intrinsic(_) | InstanceDef::Virtual(..) => continue, + InstanceKind::Intrinsic(_) | InstanceKind::Virtual(..) => continue, // These have MIR and if that MIR is inlined, instantiated and then inlining is run // again, a function item can end up getting inlined. Thus we'll be able to cause // a cycle that way - InstanceDef::VTableShim(_) - | InstanceDef::ReifyShim(..) - | InstanceDef::FnPtrShim(..) - | InstanceDef::ClosureOnceShim { .. } - | InstanceDef::ConstructCoroutineInClosureShim { .. } - | InstanceDef::CoroutineKindShim { .. } - | InstanceDef::ThreadLocalShim { .. } - | InstanceDef::CloneShim(..) => {} + InstanceKind::VTableShim(_) + | InstanceKind::ReifyShim(..) + | InstanceKind::FnPtrShim(..) + | InstanceKind::ClosureOnceShim { .. } + | InstanceKind::ConstructCoroutineInClosureShim { .. } + | InstanceKind::CoroutineKindShim { .. } + | InstanceKind::ThreadLocalShim { .. } + | InstanceKind::CloneShim(..) => {} // This shim does not call any other functions, thus there can be no recursion. - InstanceDef::FnPtrAddrShim(..) => { + InstanceKind::FnPtrAddrShim(..) => { continue; } - InstanceDef::DropGlue(..) | InstanceDef::AsyncDropGlueCtorShim(..) => { + InstanceKind::DropGlue(..) | InstanceKind::AsyncDropGlueCtorShim(..) => { // FIXME: A not fully instantiated drop shim can cause ICEs if one attempts to // have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this // needs some more analysis. @@ -151,12 +151,12 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( pub(crate) fn mir_inliner_callees<'tcx>( tcx: TyCtxt<'tcx>, - instance: ty::InstanceDef<'tcx>, + instance: ty::InstanceKind<'tcx>, ) -> &'tcx [(DefId, GenericArgsRef<'tcx>)] { let steal; let guard; let body = match (instance, instance.def_id().as_local()) { - (InstanceDef::Item(_), Some(def_id)) => { + (InstanceKind::Item(_), Some(def_id)) => { steal = tcx.mir_promoted(def_id).0; guard = steal.borrow(); &*guard diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index f1adeab3f884..6806c517c17d 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -9,7 +9,6 @@ use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{self, GenericArgsRef, ParamEnv, Ty, TyCtxt}; use rustc_span::sym; use rustc_span::symbol::Symbol; -use rustc_target::abi::FieldIdx; use rustc_target::spec::abi::Abi; pub struct InstSimplify; @@ -124,7 +123,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { /// Transform `&(*a)` ==> `a`. fn simplify_ref_deref(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { - if let Rvalue::Ref(_, _, place) = rvalue { + if let Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) = rvalue { if let Some((base, ProjectionElem::Deref)) = place.as_ref().last_projection() { if rvalue.ty(self.local_decls, self.tcx) != base.ty(self.local_decls, self.tcx).ty { return; @@ -151,7 +150,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { return; } - let const_ = Const::from_ty_const(len, self.tcx); + let const_ = Const::from_ty_const(len, self.tcx.types.usize, self.tcx); let constant = ConstOperand { span: source_info.span, const_, user_ty: None }; *rvalue = Rvalue::Use(Operand::Constant(Box::new(constant))); } @@ -217,13 +216,11 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { && let Some(place) = operand.place() { let variant = adt_def.non_enum_variant(); - for (i, field) in variant.fields.iter().enumerate() { + for (i, field) in variant.fields.iter_enumerated() { let field_ty = field.ty(self.tcx, args); if field_ty == *cast_ty { - let place = place.project_deeper( - &[ProjectionElem::Field(FieldIdx::from_usize(i), *cast_ty)], - self.tcx, - ); + let place = place + .project_deeper(&[ProjectionElem::Field(i, *cast_ty)], self.tcx); let operand = if operand.is_move() { Operand::Move(place) } else { diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index ae807655b68d..23cc0c46e739 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -155,7 +155,7 @@ struct ThreadingOpportunity { struct TOFinder<'tcx, 'a> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - ecx: InterpCx<'tcx, 'tcx, DummyMachine>, + ecx: InterpCx<'tcx, DummyMachine>, body: &'a Body<'tcx>, map: &'a Map, loop_headers: &'a BitSet, diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index d0a5a6cada8f..8d6c00bbedba 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -64,7 +64,7 @@ impl<'tcx> MirLint<'tcx> for KnownPanicsLint { /// Visits MIR nodes, performs const propagation /// and runs lint checks as it goes struct ConstPropagator<'mir, 'tcx> { - ecx: InterpCx<'mir, 'tcx, DummyMachine>, + ecx: InterpCx<'tcx, DummyMachine>, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, worklist: Vec, @@ -102,8 +102,12 @@ impl<'tcx> Value<'tcx> { } (PlaceElem::Index(idx), Value::Aggregate { fields, .. }) => { let idx = prop.get_const(idx.into())?.immediate()?; - let idx = prop.ecx.read_target_usize(idx).ok()?; - fields.get(FieldIdx::from_u32(idx.try_into().ok()?)).unwrap_or(&Value::Uninit) + let idx = prop.ecx.read_target_usize(idx).ok()?.try_into().ok()?; + if idx <= FieldIdx::MAX_AS_U32 { + fields.get(FieldIdx::from_u32(idx)).unwrap_or(&Value::Uninit) + } else { + return None; + } } ( PlaceElem::ConstantIndex { offset, min_length: _, from_end: false }, @@ -304,20 +308,25 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { fn check_unary_op(&mut self, op: UnOp, arg: &Operand<'tcx>, location: Location) -> Option<()> { let arg = self.eval_operand(arg)?; - if let (val, true) = self.use_ecx(|this| { - let val = this.ecx.read_immediate(&arg)?; - let (_res, overflow) = this.ecx.overflowing_unary_op(op, &val)?; - Ok((val, overflow)) - })? { - // `AssertKind` only has an `OverflowNeg` variant, so make sure that is - // appropriate to use. - assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow"); - self.report_assert_as_lint( - location, - AssertLintKind::ArithmeticOverflow, - AssertKind::OverflowNeg(val.to_const_int()), - ); - return None; + // The only operator that can overflow is `Neg`. + if op == UnOp::Neg && arg.layout.ty.is_integral() { + // Compute this as `0 - arg` so we can use `SubWithOverflow` to check for overflow. + let (arg, overflow) = self.use_ecx(|this| { + let arg = this.ecx.read_immediate(&arg)?; + let (_res, overflow) = this + .ecx + .binary_op(BinOp::SubWithOverflow, &ImmTy::from_int(0, arg.layout), &arg)? + .to_scalar_pair(); + Ok((arg, overflow.to_bool()?)) + })?; + if overflow { + self.report_assert_as_lint( + location, + AssertLintKind::ArithmeticOverflow, + AssertKind::OverflowNeg(arg.to_const_int()), + ); + return None; + } } Some(()) @@ -347,15 +356,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { debug!("check_binary_op: reporting assert for {:?}", location); let panic = AssertKind::Overflow( op, - match l { - Some(l) => l.to_const_int(), - // Invent a dummy value, the diagnostic ignores it anyway - None => ConstInt::new( - ScalarInt::try_from_uint(1_u8, left_size).unwrap(), - left_ty.is_signed(), - left_ty.is_ptr_sized_integral(), - ), - }, + // Invent a dummy value, the diagnostic ignores it anyway + ConstInt::new( + ScalarInt::try_from_uint(1_u8, left_size).unwrap(), + left_ty.is_signed(), + left_ty.is_ptr_sized_integral(), + ), r.to_const_int(), ); self.report_assert_as_lint(location, AssertLintKind::ArithmeticOverflow, panic); @@ -363,11 +369,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } - if let (Some(l), Some(r)) = (l, r) { - // The remaining operators are handled through `overflowing_binary_op`. + // Div/Rem are handled via the assertions they trigger. + // But for Add/Sub/Mul, those assertions only exist in debug builds, and we want to + // lint in release builds as well, so we check on the operation instead. + // So normalize to the "overflowing" operator, and then ensure that it + // actually is an overflowing operator. + let op = op.wrapping_to_overflowing().unwrap_or(op); + // The remaining operators are handled through `wrapping_to_overflowing`. + if let (Some(l), Some(r)) = (l, r) + && l.layout.ty.is_integral() + && op.is_overflowing() + { if self.use_ecx(|this| { - let (_res, overflow) = this.ecx.overflowing_binary_op(op, &l, &r)?; - Ok(overflow) + let (_res, overflow) = this.ecx.binary_op(op, &l, &r)?.to_scalar_pair(); + overflow.to_bool() })? { self.report_assert_as_lint( location, @@ -401,15 +416,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right); self.check_binary_op(*op, left, right, location)?; } - Rvalue::CheckedBinaryOp(op, box (left, right)) => { - trace!( - "checking CheckedBinaryOp(op = {:?}, left = {:?}, right = {:?})", - op, - left, - right - ); - self.check_binary_op(*op, left, right, location)?; - } // Do not try creating references (#67862) Rvalue::AddressOf(_, place) | Rvalue::Ref(_, _, place) => { @@ -555,24 +561,16 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let right = self.eval_operand(right)?; let right = self.use_ecx(|this| this.ecx.read_immediate(&right))?; - let val = - self.use_ecx(|this| this.ecx.wrapping_binary_op(bin_op, &left, &right))?; - val.into() - } - - CheckedBinaryOp(bin_op, box (ref left, ref right)) => { - let left = self.eval_operand(left)?; - let left = self.use_ecx(|this| this.ecx.read_immediate(&left))?; - - let right = self.eval_operand(right)?; - let right = self.use_ecx(|this| this.ecx.read_immediate(&right))?; - - let (val, overflowed) = - self.use_ecx(|this| this.ecx.overflowing_binary_op(bin_op, &left, &right))?; - let overflowed = ImmTy::from_bool(overflowed, self.tcx); - Value::Aggregate { - variant: VariantIdx::ZERO, - fields: [Value::from(val), overflowed.into()].into_iter().collect(), + let val = self.use_ecx(|this| this.ecx.binary_op(bin_op, &left, &right))?; + if matches!(val.layout.abi, Abi::ScalarPair(..)) { + // FIXME `Value` should properly support pairs in `Immediate`... but currently it does not. + let (val, overflow) = val.to_pair(&self.ecx); + Value::Aggregate { + variant: VariantIdx::ZERO, + fields: [val.into(), overflow.into()].into_iter().collect(), + } + } else { + val.into() } } @@ -580,7 +578,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let operand = self.eval_operand(operand)?; let val = self.use_ecx(|this| this.ecx.read_immediate(&operand))?; - let val = self.use_ecx(|this| this.ecx.wrapping_unary_op(un_op, &val))?; + let val = self.use_ecx(|this| this.ecx.unary_op(un_op, &val))?; val.into() } @@ -624,9 +622,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let val = match null_op { NullOp::SizeOf => op_layout.size.bytes(), NullOp::AlignOf => op_layout.align.abi.bytes(), - NullOp::OffsetOf(fields) => { - op_layout.offset_of_subfield(self, fields.iter()).bytes() - } + NullOp::OffsetOf(fields) => self + .tcx + .offset_of_subfield(self.param_env, op_layout, fields.iter()) + .bytes(), NullOp::UbChecks => return None, }; ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into() @@ -707,9 +706,9 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { self.super_operand(operand, location); } - fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, location: Location) { - trace!("visit_constant: {:?}", constant); - self.super_constant(constant, location); + fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, location: Location) { + trace!("visit_const_operand: {:?}", constant); + self.super_const_operand(constant, location); self.eval_constant(constant); } @@ -785,8 +784,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { TerminatorKind::SwitchInt { ref discr, ref targets } => { if let Some(ref value) = self.eval_operand(discr) && let Some(value_const) = self.use_ecx(|this| this.ecx.read_scalar(value)) - && let Ok(constant) = value_const.try_to_int() - && let Ok(constant) = constant.try_to_bits(constant.size()) + && let Ok(constant) = value_const.to_bits(value_const.size()) { // We managed to evaluate the discriminant, so we know we only need to visit // one target. diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 9af48f0bad25..87dd9cc11ebb 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -1,8 +1,10 @@ +// tidy-alphabetical-start #![feature(assert_matches)] #![feature(box_patterns)] #![feature(const_type_name)] #![feature(cow_is_borrowed)] #![feature(decl_macro)] +#![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(is_sorted)] #![feature(let_chains)] @@ -12,7 +14,7 @@ #![feature(round_char_boundary)] #![feature(try_blocks)] #![feature(yeet_expr)] -#![feature(if_let_guard)] +// tidy-alphabetical-end #[macro_use] extern crate tracing; @@ -49,13 +51,12 @@ mod abort_unwinding_calls; mod add_call_guards; mod add_moves_for_packed_drops; mod add_retag; +mod add_subtyping_projections; +mod check_alignment; mod check_const_item_mutation; mod check_packed_ref; -mod remove_place_mention; // This pass is public to allow external drivers to perform MIR cleanup -mod add_subtyping_projections; pub mod cleanup_post_borrowck; -mod const_debuginfo; mod copy_prop; mod coroutine; mod cost_checker; @@ -93,6 +94,7 @@ mod prettify; mod promote_consts; mod ref_prop; mod remove_noop_landing_pads; +mod remove_place_mention; mod remove_storage_markers; mod remove_uninit_drops; mod remove_unneeded_drops; @@ -102,16 +104,16 @@ mod reveal_all; mod shim; mod ssa; // This pass is public to allow external drivers to perform MIR cleanup -mod check_alignment; pub mod simplify; mod simplify_branches; mod simplify_comparison_integral; +mod single_use_consts; mod sroa; mod unreachable_enum_branching; mod unreachable_prop; +mod validate; -use rustc_const_eval::transform::check_consts::{self, ConstCx}; -use rustc_const_eval::transform::validate; +use rustc_const_eval::check_consts::{self, ConstCx}; use rustc_mir_dataflow::rustc_peek; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -396,7 +398,7 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> & if is_fn_like { // Do not compute the mir call graph without said call graph actually being used. if pm::should_run_pass(tcx, &inline::Inline) { - tcx.ensure_with_value().mir_inliner_callees(ty::InstanceDef::Item(def.to_def_id())); + tcx.ensure_with_value().mir_inliner_callees(ty::InstanceKind::Item(def.to_def_id())); } } @@ -593,7 +595,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &gvn::GVN, &simplify::SimplifyLocals::AfterGVN, &dataflow_const_prop::DataflowConstProp, - &const_debuginfo::ConstDebugInfo, + &single_use_consts::SingleUseConsts, &o1(simplify_branches::SimplifyConstCondition::AfterConstProp), &jump_threading::JumpThreading, &early_otherwise_branch::EarlyOtherwiseBranch, diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 43d8c45bb2dd..3ffc447217d7 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -140,16 +140,16 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { rhs = args.next().unwrap(); } let bin_op = match intrinsic.name { - sym::add_with_overflow => BinOp::Add, - sym::sub_with_overflow => BinOp::Sub, - sym::mul_with_overflow => BinOp::Mul, + sym::add_with_overflow => BinOp::AddWithOverflow, + sym::sub_with_overflow => BinOp::SubWithOverflow, + sym::mul_with_overflow => BinOp::MulWithOverflow, _ => bug!("unexpected intrinsic"), }; block.statements.push(Statement { source_info: terminator.source_info, kind: StatementKind::Assign(Box::new(( *destination, - Rvalue::CheckedBinaryOp(bin_op, Box::new((lhs.node, rhs.node))), + Rvalue::BinaryOp(bin_op, Box::new((lhs.node, rhs.node))), ))), }); terminator.kind = TerminatorKind::Goto { target }; @@ -316,6 +316,23 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { terminator.kind = TerminatorKind::Goto { target }; } + sym::ptr_metadata => { + let Ok([ptr]) = <[_; 1]>::try_from(std::mem::take(args)) else { + span_bug!( + terminator.source_info.span, + "Wrong number of arguments for ptr_metadata intrinsic", + ); + }; + let target = target.unwrap(); + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::UnaryOp(UnOp::PtrMetadata, ptr.node), + ))), + }); + terminator.kind = TerminatorKind::Goto { target }; + } _ => {} } } diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 1411d9be2236..6ab4ec6fe7e6 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -372,7 +372,7 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp { } fn int_equal(l: ScalarInt, r: impl Into, size: Size) -> bool { - l.assert_int(l.size()) == ScalarInt::try_from_uint(r, size).unwrap().assert_int(size) + l.to_bits_unchecked() == ScalarInt::try_from_uint(r, size).unwrap().to_bits_unchecked() } // We first compare the two branches, and then the other branches need to fulfill the same conditions. diff --git a/compiler/rustc_mir_transform/src/normalize_array_len.rs b/compiler/rustc_mir_transform/src/normalize_array_len.rs index 2070895c9002..d5e727066614 100644 --- a/compiler/rustc_mir_transform/src/normalize_array_len.rs +++ b/compiler/rustc_mir_transform/src/normalize_array_len.rs @@ -95,7 +95,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { *rvalue = Rvalue::Use(Operand::Constant(Box::new(ConstOperand { span: rustc_span::DUMMY_SP, user_ty: None, - const_: Const::from_ty_const(len, self.tcx), + const_: Const::from_ty_const(len, self.tcx.types.usize, self.tcx), }))); } self.super_rvalue(rvalue, loc); diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index b3116c002d3c..ecdca8292b43 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -30,7 +30,7 @@ use std::assert_matches::assert_matches; use std::cell::Cell; use std::{cmp, iter, mem}; -use rustc_const_eval::transform::check_consts::{qualifs, ConstCx}; +use rustc_const_eval::check_consts::{qualifs, ConstCx}; /// A `MirPass` for promotion. /// @@ -464,13 +464,13 @@ impl<'tcx> Validator<'_, 'tcx> { Rvalue::UnaryOp(op, operand) => { match op { // These operations can never fail. - UnOp::Neg | UnOp::Not => {} + UnOp::Neg | UnOp::Not | UnOp::PtrMetadata => {} } self.validate_operand(operand)?; } - Rvalue::BinaryOp(op, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(op, box (lhs, rhs)) => { + Rvalue::BinaryOp(op, box (lhs, rhs)) => { let op = *op; let lhs_ty = lhs.ty(self.body, self.tcx); @@ -500,14 +500,14 @@ impl<'tcx> Validator<'_, 'tcx> { } _ => None, }; - match rhs_val.map(|x| x.assert_uint(sz)) { + match rhs_val.map(|x| x.to_uint(sz)) { // for the zero test, int vs uint does not matter Some(x) if x != 0 => {} // okay _ => return Err(Unpromotable), // value not known or 0 -- not okay } // Furthermore, for signed divison, we also have to exclude `int::MIN / -1`. if lhs_ty.is_signed() { - match rhs_val.map(|x| x.assert_int(sz)) { + match rhs_val.map(|x| x.to_int(sz)) { Some(-1) | None => { // The RHS is -1 or unknown, so we have to be careful. // But is the LHS int::MIN? @@ -518,7 +518,7 @@ impl<'tcx> Validator<'_, 'tcx> { _ => None, }; let lhs_min = sz.signed_int_min(); - match lhs_val.map(|x| x.assert_int(sz)) { + match lhs_val.map(|x| x.to_int(sz)) { Some(x) if x != lhs_min => {} // okay _ => return Err(Unpromotable), // value not known or int::MIN -- not okay } @@ -539,10 +539,13 @@ impl<'tcx> Validator<'_, 'tcx> { | BinOp::Offset | BinOp::Add | BinOp::AddUnchecked + | BinOp::AddWithOverflow | BinOp::Sub | BinOp::SubUnchecked + | BinOp::SubWithOverflow | BinOp::Mul | BinOp::MulUnchecked + | BinOp::MulWithOverflow | BinOp::BitXor | BinOp::BitAnd | BinOp::BitOr @@ -953,7 +956,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> { } } - fn visit_constant(&mut self, constant: &mut ConstOperand<'tcx>, _location: Location) { + fn visit_const_operand(&mut self, constant: &mut ConstOperand<'tcx>, _location: Location) { if constant.const_.is_required_const() { self.promoted.required_consts.push(*constant); } diff --git a/compiler/rustc_mir_transform/src/required_consts.rs b/compiler/rustc_mir_transform/src/required_consts.rs index 71ac929d35eb..00bfb5e66008 100644 --- a/compiler/rustc_mir_transform/src/required_consts.rs +++ b/compiler/rustc_mir_transform/src/required_consts.rs @@ -12,7 +12,7 @@ impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> { } impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> { - fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _: Location) { + fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, _: Location) { if constant.const_.is_required_const() { self.required_consts.push(*constant); } diff --git a/compiler/rustc_mir_transform/src/reveal_all.rs b/compiler/rustc_mir_transform/src/reveal_all.rs index 4d2eca578404..5eaa024f8468 100644 --- a/compiler/rustc_mir_transform/src/reveal_all.rs +++ b/compiler/rustc_mir_transform/src/reveal_all.rs @@ -49,14 +49,14 @@ impl<'tcx> MutVisitor<'tcx> for RevealAllVisitor<'tcx> { } #[inline] - fn visit_constant(&mut self, constant: &mut ConstOperand<'tcx>, location: Location) { + fn visit_const_operand(&mut self, constant: &mut ConstOperand<'tcx>, location: Location) { // We have to use `try_normalize_erasing_regions` here, since it's // possible that we visit impossible-to-satisfy where clauses here, // see #91745 if let Ok(c) = self.tcx.try_normalize_erasing_regions(self.param_env, constant.const_) { constant.const_ = c; } - self.super_constant(constant, location); + self.super_const_operand(constant, location); } #[inline] diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index dcf54ad2cfc8..825f8957187e 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -4,7 +4,7 @@ use rustc_hir::lang_items::LangItem; use rustc_middle::mir::*; use rustc_middle::query::Providers; use rustc_middle::ty::GenericArgs; -use rustc_middle::ty::{self, CoroutineArgs, EarlyBinder, Ty, TyCtxt}; +use rustc_middle::ty::{self, CoroutineArgs, CoroutineArgsExt, EarlyBinder, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; @@ -29,16 +29,16 @@ pub fn provide(providers: &mut Providers) { providers.mir_shims = make_shim; } -fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'tcx> { +fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body<'tcx> { debug!("make_shim({:?})", instance); let mut result = match instance { - ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance), - ty::InstanceDef::VTableShim(def_id) => { + ty::InstanceKind::Item(..) => bug!("item {:?} passed to make_shim", instance), + ty::InstanceKind::VTableShim(def_id) => { let adjustment = Adjustment::Deref { source: DerefSource::MutPtr }; build_call_shim(tcx, instance, Some(adjustment), CallKind::Direct(def_id)) } - ty::InstanceDef::FnPtrShim(def_id, ty) => { + ty::InstanceKind::FnPtrShim(def_id, ty) => { let trait_ = tcx.trait_of_item(def_id).unwrap(); // Supports `Fn` or `async Fn` traits. let adjustment = match tcx @@ -58,10 +58,10 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' // a virtual call, or a direct call to a function for which // indirect calls must be codegen'd differently than direct ones // (such as `#[track_caller]`). - ty::InstanceDef::ReifyShim(def_id, _) => { + ty::InstanceKind::ReifyShim(def_id, _) => { build_call_shim(tcx, instance, None, CallKind::Direct(def_id)) } - ty::InstanceDef::ClosureOnceShim { call_once: _, track_caller: _ } => { + ty::InstanceKind::ClosureOnceShim { call_once: _, track_caller: _ } => { let fn_mut = tcx.require_lang_item(LangItem::FnMut, None); let call_mut = tcx .associated_items(fn_mut) @@ -73,16 +73,16 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' build_call_shim(tcx, instance, Some(Adjustment::RefMut), CallKind::Direct(call_mut)) } - ty::InstanceDef::ConstructCoroutineInClosureShim { + ty::InstanceKind::ConstructCoroutineInClosureShim { coroutine_closure_def_id, receiver_by_ref, } => build_construct_coroutine_by_move_shim(tcx, coroutine_closure_def_id, receiver_by_ref), - ty::InstanceDef::CoroutineKindShim { coroutine_def_id } => { + ty::InstanceKind::CoroutineKindShim { coroutine_def_id } => { return tcx.optimized_mir(coroutine_def_id).coroutine_by_move_body().unwrap().clone(); } - ty::InstanceDef::DropGlue(def_id, ty) => { + ty::InstanceKind::DropGlue(def_id, ty) => { // FIXME(#91576): Drop shims for coroutines aren't subject to the MIR passes at the end // of this function. Is this intentional? if let Some(ty::Coroutine(coroutine_def_id, args)) = ty.map(Ty::kind) { @@ -127,16 +127,16 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' build_drop_shim(tcx, def_id, ty) } - ty::InstanceDef::ThreadLocalShim(..) => build_thread_local_shim(tcx, instance), - ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty), - ty::InstanceDef::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty), - ty::InstanceDef::AsyncDropGlueCtorShim(def_id, ty) => { + ty::InstanceKind::ThreadLocalShim(..) => build_thread_local_shim(tcx, instance), + ty::InstanceKind::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty), + ty::InstanceKind::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty), + ty::InstanceKind::AsyncDropGlueCtorShim(def_id, ty) => { async_destructor_ctor::build_async_destructor_ctor_shim(tcx, def_id, ty) } - ty::InstanceDef::Virtual(..) => { - bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance) + ty::InstanceKind::Virtual(..) => { + bug!("InstanceKind::Virtual ({:?}) is for direct calls only", instance) } - ty::InstanceDef::Intrinsic(_) => { + ty::InstanceKind::Intrinsic(_) => { bug!("creating shims from intrinsics ({:?}) is unsupported", instance) } }; @@ -240,7 +240,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) block(&mut blocks, TerminatorKind::Goto { target: return_block }); block(&mut blocks, TerminatorKind::Return); - let source = MirSource::from_instance(ty::InstanceDef::DropGlue(def_id, ty)); + let source = MirSource::from_instance(ty::InstanceKind::DropGlue(def_id, ty)); let mut body = new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span); @@ -392,7 +392,10 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { } } -fn build_thread_local_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'tcx> { +fn build_thread_local_shim<'tcx>( + tcx: TyCtxt<'tcx>, + instance: ty::InstanceKind<'tcx>, +) -> Body<'tcx> { let def_id = instance.def_id(); let span = tcx.def_span(def_id); @@ -472,7 +475,7 @@ impl<'tcx> CloneShimBuilder<'tcx> { } fn into_mir(self) -> Body<'tcx> { - let source = MirSource::from_instance(ty::InstanceDef::CloneShim( + let source = MirSource::from_instance(ty::InstanceKind::CloneShim( self.def_id, self.sig.inputs_and_output[0], )); @@ -634,7 +637,7 @@ impl<'tcx> CloneShimBuilder<'tcx> { dest: Place<'tcx>, src: Place<'tcx>, coroutine_def_id: DefId, - args: CoroutineArgs<'tcx>, + args: CoroutineArgs>, ) { self.block(vec![], TerminatorKind::Goto { target: self.block_index_offset(3) }, false); let unwind = self.block(vec![], TerminatorKind::UnwindResume, true); @@ -682,14 +685,14 @@ impl<'tcx> CloneShimBuilder<'tcx> { #[instrument(level = "debug", skip(tcx), ret)] fn build_call_shim<'tcx>( tcx: TyCtxt<'tcx>, - instance: ty::InstanceDef<'tcx>, + instance: ty::InstanceKind<'tcx>, rcvr_adjustment: Option, call_kind: CallKind<'tcx>, ) -> Body<'tcx> { // `FnPtrShim` contains the fn pointer type that a call shim is being built for - this is used // to instantiate into the signature of the shim. It is not necessary for users of this - // MIR body to perform further instantiations (see `InstanceDef::has_polymorphic_mir_body`). - let (sig_args, untuple_args) = if let ty::InstanceDef::FnPtrShim(_, ty) = instance { + // MIR body to perform further instantiations (see `InstanceKind::has_polymorphic_mir_body`). + let (sig_args, untuple_args) = if let ty::InstanceKind::FnPtrShim(_, ty) = instance { let sig = tcx.instantiate_bound_regions_with_erased(ty.fn_sig(tcx)); let untuple_args = sig.inputs(); @@ -741,8 +744,8 @@ fn build_call_shim<'tcx>( } // FIXME(eddyb) avoid having this snippet both here and in - // `Instance::fn_sig` (introduce `InstanceDef::fn_sig`?). - if let ty::InstanceDef::VTableShim(..) = instance { + // `Instance::fn_sig` (introduce `InstanceKind::fn_sig`?). + if let ty::InstanceKind::VTableShim(..) = instance { // Modify fn(self, ...) to fn(self: *mut Self, ...) let mut inputs_and_output = sig.inputs_and_output.to_vec(); let self_arg = &mut inputs_and_output[0]; @@ -1007,7 +1010,7 @@ fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'t terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), is_cleanup: false, }; - let source = MirSource::from_instance(ty::InstanceDef::FnPtrAddrShim(def_id, self_ty)); + let source = MirSource::from_instance(ty::InstanceKind::FnPtrAddrShim(def_id, self_ty)); new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span) } @@ -1087,7 +1090,7 @@ fn build_construct_coroutine_by_move_shim<'tcx>( is_cleanup: false, }; - let source = MirSource::from_instance(ty::InstanceDef::ConstructCoroutineInClosureShim { + let source = MirSource::from_instance(ty::InstanceKind::ConstructCoroutineInClosureShim { coroutine_closure_def_id, receiver_by_ref, }); diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index f4481c22fc1f..ea4f5fca59e6 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -12,7 +12,7 @@ use rustc_middle::mir::{ Terminator, TerminatorKind, UnwindAction, UnwindTerminateReason, RETURN_PLACE, }; use rustc_middle::ty::adjustment::PointerCoercion; -use rustc_middle::ty::util::Discr; +use rustc_middle::ty::util::{AsyncDropGlueMorphology, Discr}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::source_map::respan; @@ -116,15 +116,25 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { } fn build(self) -> Body<'tcx> { - let (tcx, def_id, Some(self_ty)) = (self.tcx, self.def_id, self.self_ty) else { + let (tcx, Some(self_ty)) = (self.tcx, self.self_ty) else { return self.build_zst_output(); }; + match self_ty.async_drop_glue_morphology(tcx) { + AsyncDropGlueMorphology::Noop => span_bug!( + self.span, + "async drop glue shim generator encountered type with noop async drop glue morphology" + ), + AsyncDropGlueMorphology::DeferredDropInPlace => { + return self.build_deferred_drop_in_place(); + } + AsyncDropGlueMorphology::Custom => (), + } let surface_drop_kind = || { - let param_env = tcx.param_env_reveal_all_normalized(def_id); - if self_ty.has_surface_async_drop(tcx, param_env) { + let adt_def = self_ty.ty_adt_def()?; + if adt_def.async_destructor(tcx).is_some() { Some(SurfaceDropKind::Async) - } else if self_ty.has_surface_drop(tcx, param_env) { + } else if adt_def.destructor(tcx).is_some() { Some(SurfaceDropKind::Sync) } else { None @@ -267,6 +277,13 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { self.return_() } + fn build_deferred_drop_in_place(mut self) -> Body<'tcx> { + self.put_self(); + let deferred = self.combine_deferred_drop_in_place(); + self.combine_fuse(deferred); + self.return_() + } + fn build_fused_async_surface(mut self) -> Body<'tcx> { self.put_self(); let surface = self.combine_async_surface(); @@ -441,6 +458,14 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { ) } + fn combine_deferred_drop_in_place(&mut self) -> Ty<'tcx> { + self.apply_combinator( + 1, + LangItem::AsyncDropDeferredDropInPlace, + &[self.self_ty.unwrap().into()], + ) + } + fn combine_fuse(&mut self, inner_future_ty: Ty<'tcx>) -> Ty<'tcx> { self.apply_combinator(1, LangItem::AsyncDropFuse, &[inner_future_ty.into()]) } @@ -481,7 +506,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { if let Some(ty) = self.self_ty { debug_assert_eq!( output.ty(&self.locals, self.tcx), - ty.async_destructor_ty(self.tcx, self.param_env), + ty.async_destructor_ty(self.tcx), "output async destructor types did not match for type: {ty:?}", ); } @@ -504,7 +529,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { last_bb.terminator = Some(Terminator { source_info, kind: TerminatorKind::Return }); - let source = MirSource::from_instance(ty::InstanceDef::AsyncDropGlueCtorShim( + let source = MirSource::from_instance(ty::InstanceKind::AsyncDropGlueCtorShim( self.def_id, self.self_ty, )); @@ -536,7 +561,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { // If projection of Discriminant then compare with `Ty::discriminant_ty` if let ty::Alias(ty::Projection, ty::AliasTy { args, def_id, .. }) = expected_ty.kind() - && Some(*def_id) == self.tcx.lang_items().discriminant_type() + && self.tcx.is_lang_item(*def_id, LangItem::Discriminant) && args.first().unwrap().as_type().unwrap().discriminant_ty(self.tcx) == operand_ty { return; diff --git a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs index 03907babf2b0..e174cccdad6b 100644 --- a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs +++ b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs @@ -49,7 +49,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral { let layout = tcx .layout_of(param_env.and(opt.branch_value_ty)) .expect("if we have an evaluated constant we must know the layout"); - int.assert_bits(layout.size) + int.to_bits(layout.size) } Scalar::Ptr(..) => continue, }; diff --git a/compiler/rustc_mir_transform/src/single_use_consts.rs b/compiler/rustc_mir_transform/src/single_use_consts.rs new file mode 100644 index 000000000000..93736e55996e --- /dev/null +++ b/compiler/rustc_mir_transform/src/single_use_consts.rs @@ -0,0 +1,199 @@ +use rustc_index::{bit_set::BitSet, IndexVec}; +use rustc_middle::bug; +use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; + +/// Various parts of MIR building introduce temporaries that are commonly not needed. +/// +/// Notably, `if CONST` and `match CONST` end up being used-once temporaries, which +/// obfuscates the structure for other passes and codegen, which would like to always +/// be able to just see the constant directly. +/// +/// At higher optimization levels fancier passes like GVN will take care of this +/// in a more general fashion, but this handles the easy cases so can run in debug. +/// +/// This only removes constants with a single-use because re-evaluating constants +/// isn't always an improvement, especially for large ones. +/// +/// It also removes *never*-used constants, since it had all the information +/// needed to do that too, including updating the debug info. +pub struct SingleUseConsts; + +impl<'tcx> MirPass<'tcx> for SingleUseConsts { + fn is_enabled(&self, sess: &rustc_session::Session) -> bool { + sess.mir_opt_level() > 0 + } + + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let mut finder = SingleUseConstsFinder { + ineligible_locals: BitSet::new_empty(body.local_decls.len()), + locations: IndexVec::from_elem(LocationPair::new(), &body.local_decls), + locals_in_debug_info: BitSet::new_empty(body.local_decls.len()), + }; + + finder.ineligible_locals.insert_range(..=Local::from_usize(body.arg_count)); + + finder.visit_body(body); + + for (local, locations) in finder.locations.iter_enumerated() { + if finder.ineligible_locals.contains(local) { + continue; + } + + let Some(init_loc) = locations.init_loc else { + continue; + }; + + // We're only changing an operand, not the terminator kinds or successors + let basic_blocks = body.basic_blocks.as_mut_preserves_cfg(); + let init_statement = + basic_blocks[init_loc.block].statements[init_loc.statement_index].replace_nop(); + let StatementKind::Assign(place_and_rvalue) = init_statement.kind else { + bug!("No longer an assign?"); + }; + let (place, rvalue) = *place_and_rvalue; + assert_eq!(place.as_local(), Some(local)); + let Rvalue::Use(operand) = rvalue else { bug!("No longer a use?") }; + + let mut replacer = LocalReplacer { tcx, local, operand: Some(operand) }; + + if finder.locals_in_debug_info.contains(local) { + for var_debug_info in &mut body.var_debug_info { + replacer.visit_var_debug_info(var_debug_info); + } + } + + let Some(use_loc) = locations.use_loc else { continue }; + + let use_block = &mut basic_blocks[use_loc.block]; + if let Some(use_statement) = use_block.statements.get_mut(use_loc.statement_index) { + replacer.visit_statement(use_statement, use_loc); + } else { + replacer.visit_terminator(use_block.terminator_mut(), use_loc); + } + + if replacer.operand.is_some() { + bug!( + "operand wasn't used replacing local {local:?} with locations {locations:?} in body {body:#?}" + ); + } + } + } +} + +#[derive(Copy, Clone, Debug)] +struct LocationPair { + init_loc: Option, + use_loc: Option, +} + +impl LocationPair { + fn new() -> Self { + Self { init_loc: None, use_loc: None } + } +} + +struct SingleUseConstsFinder { + ineligible_locals: BitSet, + locations: IndexVec, + locals_in_debug_info: BitSet, +} + +impl<'tcx> Visitor<'tcx> for SingleUseConstsFinder { + fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { + if let Some(local) = place.as_local() + && let Rvalue::Use(operand) = rvalue + && let Operand::Constant(_) = operand + { + let locations = &mut self.locations[local]; + if locations.init_loc.is_some() { + self.ineligible_locals.insert(local); + } else { + locations.init_loc = Some(location); + } + } else { + self.super_assign(place, rvalue, location); + } + } + + fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { + if let Some(place) = operand.place() + && let Some(local) = place.as_local() + { + let locations = &mut self.locations[local]; + if locations.use_loc.is_some() { + self.ineligible_locals.insert(local); + } else { + locations.use_loc = Some(location); + } + } else { + self.super_operand(operand, location); + } + } + + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + match &statement.kind { + // Storage markers are irrelevant to this. + StatementKind::StorageLive(_) | StatementKind::StorageDead(_) => {} + _ => self.super_statement(statement, location), + } + } + + fn visit_var_debug_info(&mut self, var_debug_info: &VarDebugInfo<'tcx>) { + if let VarDebugInfoContents::Place(place) = &var_debug_info.value + && let Some(local) = place.as_local() + { + self.locals_in_debug_info.insert(local); + } else { + self.super_var_debug_info(var_debug_info); + } + } + + fn visit_local(&mut self, local: Local, _context: PlaceContext, _location: Location) { + // If there's any path that gets here, rather than being understood elsewhere, + // then we'd better not do anything with this local. + self.ineligible_locals.insert(local); + } +} + +struct LocalReplacer<'tcx> { + tcx: TyCtxt<'tcx>, + local: Local, + operand: Option>, +} + +impl<'tcx> MutVisitor<'tcx> for LocalReplacer<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_operand(&mut self, operand: &mut Operand<'tcx>, _location: Location) { + if let Operand::Copy(place) | Operand::Move(place) = operand + && let Some(local) = place.as_local() + && local == self.local + { + *operand = self.operand.take().unwrap_or_else(|| { + bug!("there was a second use of the operand"); + }); + } + } + + fn visit_var_debug_info(&mut self, var_debug_info: &mut VarDebugInfo<'tcx>) { + if let VarDebugInfoContents::Place(place) = &var_debug_info.value + && let Some(local) = place.as_local() + && local == self.local + { + let const_op = self + .operand + .as_ref() + .unwrap_or_else(|| { + bug!("the operand was already stolen"); + }) + .constant() + .unwrap() + .clone(); + var_debug_info.value = VarDebugInfoContents::Const(const_op); + } + } +} diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index cdf3305b5609..c2108795372f 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -1,4 +1,5 @@ use rustc_data_structures::flat_map_in_place::FlatMapInPlace; +use rustc_hir::LangItem; use rustc_index::bit_set::{BitSet, GrowableBitSet}; use rustc_index::IndexVec; use rustc_middle::bug; @@ -70,6 +71,11 @@ fn escaping_locals<'tcx>( // Exclude #[repr(simd)] types so that they are not de-optimized into an array return true; } + if tcx.is_lang_item(def.did(), LangItem::DynMetadata) { + // codegen wants to see the `DynMetadata`, + // not the inner reference-to-opaque-type. + return true; + } // We already excluded unions and enums, so this ADT must have one variant let variant = def.variant(FIRST_VARIANT); if variant.fields.len() > 1 { diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_mir_transform/src/validate.rs similarity index 88% rename from compiler/rustc_const_eval/src/transform/validate.rs rename to compiler/rustc_mir_transform/src/validate.rs index fdc7f6a69cba..f5d10521fdd0 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1,14 +1,18 @@ //! Validates the MIR to ensure that invariants are upheld. use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::LangItem; use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; use rustc_infer::traits::Reveal; use rustc_middle::mir::coverage::CoverageKind; -use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; -use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt, Variance}; +use rustc_middle::ty::adjustment::PointerCoercion; +use rustc_middle::ty::{ + self, CoroutineArgsExt, InstanceKind, ParamEnv, ScalarInt, Ty, TyCtxt, TypeVisitableExt, + Variance, +}; use rustc_middle::{bug, span_bug}; use rustc_target::abi::{Size, FIRST_VARIANT}; use rustc_target::spec::abi::Abi; @@ -40,7 +44,7 @@ impl<'tcx> MirPass<'tcx> for Validator { // terribly important that they pass the validator. However, I think other passes might // still see them, in which case they might be surprised. It would probably be better if we // didn't put this through the MIR pipeline at all. - if matches!(body.source.instance, InstanceDef::Intrinsic(..) | InstanceDef::Virtual(..)) { + if matches!(body.source.instance, InstanceKind::Intrinsic(..) | InstanceKind::Virtual(..)) { return; } let def_id = body.source.def_id(); @@ -91,7 +95,7 @@ impl<'tcx> MirPass<'tcx> for Validator { } if let MirPhase::Runtime(_) = body.phase { - if let ty::InstanceDef::Item(_) = body.source.instance { + if let ty::InstanceKind::Item(_) = body.source.instance { if body.has_free_regions() { cfg_checker.fail( Location::START, @@ -685,6 +689,17 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { check_equal(self, location, *f_ty); } ty::Adt(adt_def, args) => { + // see + if self.tcx.is_lang_item(adt_def.did(), LangItem::DynMetadata) { + self.fail( + location, + format!( + "You can't project to field {f:?} of `DynMetadata` because \ + layout is weird and thinks it doesn't have fields." + ), + ); + } + let var = parent_ty.variant_index.unwrap_or(FIRST_VARIANT); let Some(field) = adt_def.variant(var).fields.get(f) else { fail_out_of_bounds(self, location); @@ -830,7 +845,25 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { && cntxt != PlaceContext::NonUse(NonUseContext::VarDebugInfo) && place.projection[1..].contains(&ProjectionElem::Deref) { - self.fail(location, format!("{place:?}, has deref at the wrong place")); + self.fail( + location, + format!("place {place:?} has deref as a later projection (it is only permitted as the first projection)"), + ); + } + + // Ensure all downcast projections are followed by field projections. + let mut projections_iter = place.projection.iter(); + while let Some(proj) = projections_iter.next() { + if matches!(proj, ProjectionElem::Downcast(..)) { + if !matches!(projections_iter.next(), Some(ProjectionElem::Field(..))) { + self.fail( + location, + format!( + "place {place:?} has `Downcast` projection not followed by `Field`" + ), + ); + } + } } self.super_place(place, cntxt, location); @@ -1037,8 +1070,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ) } } - AddUnchecked | SubUnchecked | MulUnchecked | Shl | ShlUnchecked | Shr - | ShrUnchecked => { + AddUnchecked | AddWithOverflow | SubUnchecked | SubWithOverflow + | MulUnchecked | MulWithOverflow | Shl | ShlUnchecked | Shr | ShrUnchecked => { for x in [a, b] { check_kinds!( x, @@ -1067,31 +1100,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } } - Rvalue::CheckedBinaryOp(op, vals) => { - use BinOp::*; - let a = vals.0.ty(&self.body.local_decls, self.tcx); - let b = vals.1.ty(&self.body.local_decls, self.tcx); - match op { - Add | Sub | Mul => { - for x in [a, b] { - check_kinds!( - x, - "Cannot perform checked arithmetic on type {:?}", - ty::Uint(..) | ty::Int(..) - ) - } - if a != b { - self.fail( - location, - format!( - "Cannot perform checked arithmetic on unequal types {a:?} and {b:?}" - ), - ); - } - } - _ => self.fail(location, format!("There is no checked version of {op:?}")), - } - } Rvalue::UnaryOp(op, operand) => { let a = operand.ty(&self.body.local_decls, self.tcx); match op { @@ -1105,6 +1113,16 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ty::Int(..) | ty::Uint(..) | ty::Bool ); } + UnOp::PtrMetadata => { + if !matches!(self.mir_phase, MirPhase::Runtime(_)) { + // It would probably be fine to support this in earlier phases, + // but at the time of writing it's only ever introduced from intrinsic lowering, + // so earlier things can just `bug!` on it. + self.fail(location, "PtrMetadata should be in runtime MIR only"); + } + + check_kinds!(a, "Cannot PtrMetadata non-pointer type {:?}", ty::RawPtr(..)); + } } } Rvalue::ShallowInitBox(operand, _) => { @@ -1118,23 +1136,115 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // FIXME(dyn-star): make sure nothing needs to be done here. } // FIXME: Add Checks for these - CastKind::PointerWithExposedProvenance - | CastKind::PointerExposeProvenance - | CastKind::PointerCoercion(_) => {} + CastKind::PointerWithExposedProvenance | CastKind::PointerExposeProvenance => {} + CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => { + // FIXME: check signature compatibility. + check_kinds!( + op_ty, + "CastKind::{kind:?} input must be a fn item, not {:?}", + ty::FnDef(..) + ); + check_kinds!( + target_type, + "CastKind::{kind:?} output must be a fn pointer, not {:?}", + ty::FnPtr(..) + ); + } + CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer) => { + // FIXME: check safety and signature compatibility. + check_kinds!( + op_ty, + "CastKind::{kind:?} input must be a fn pointer, not {:?}", + ty::FnPtr(..) + ); + check_kinds!( + target_type, + "CastKind::{kind:?} output must be a fn pointer, not {:?}", + ty::FnPtr(..) + ); + } + CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(..)) => { + // FIXME: check safety, captures, and signature compatibility. + check_kinds!( + op_ty, + "CastKind::{kind:?} input must be a closure, not {:?}", + ty::Closure(..) + ); + check_kinds!( + target_type, + "CastKind::{kind:?} output must be a fn pointer, not {:?}", + ty::FnPtr(..) + ); + } + CastKind::PointerCoercion(PointerCoercion::MutToConstPointer) => { + // FIXME: check same pointee? + check_kinds!( + op_ty, + "CastKind::{kind:?} input must be a raw mut pointer, not {:?}", + ty::RawPtr(_, Mutability::Mut) + ); + check_kinds!( + target_type, + "CastKind::{kind:?} output must be a raw const pointer, not {:?}", + ty::RawPtr(_, Mutability::Not) + ); + if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) { + self.fail(location, format!("After borrowck, MIR disallows {kind:?}")); + } + } + CastKind::PointerCoercion(PointerCoercion::ArrayToPointer) => { + // FIXME: Check pointee types + check_kinds!( + op_ty, + "CastKind::{kind:?} input must be a raw pointer, not {:?}", + ty::RawPtr(..) + ); + check_kinds!( + target_type, + "CastKind::{kind:?} output must be a raw pointer, not {:?}", + ty::RawPtr(..) + ); + if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) { + self.fail(location, format!("After borrowck, MIR disallows {kind:?}")); + } + } + CastKind::PointerCoercion(PointerCoercion::Unsize) => { + // This is used for all `CoerceUnsized` types, + // not just pointers/references, so is hard to check. + } CastKind::IntToInt | CastKind::IntToFloat => { let input_valid = op_ty.is_integral() || op_ty.is_char() || op_ty.is_bool(); let target_valid = target_type.is_numeric() || target_type.is_char(); if !input_valid || !target_valid { self.fail( location, - format!("Wrong cast kind {kind:?} for the type {op_ty}",), + format!("Wrong cast kind {kind:?} for the type {op_ty}"), ); } } - CastKind::FnPtrToPtr | CastKind::PtrToPtr => { - if !(op_ty.is_any_ptr() && target_type.is_unsafe_ptr()) { - self.fail(location, "Can't cast {op_ty} into 'Ptr'"); - } + CastKind::FnPtrToPtr => { + check_kinds!( + op_ty, + "CastKind::{kind:?} input must be a fn pointer, not {:?}", + ty::FnPtr(..) + ); + check_kinds!( + target_type, + "CastKind::{kind:?} output must be a raw pointer, not {:?}", + ty::RawPtr(..) + ); + } + CastKind::PtrToPtr => { + check_kinds!( + op_ty, + "CastKind::{kind:?} input must be a raw pointer, not {:?}", + ty::RawPtr(..) + ); + check_kinds!( + target_type, + "CastKind::{kind:?} output must be a raw pointer, not {:?}", + ty::RawPtr(..) + ); } CastKind::FloatToFloat | CastKind::FloatToInt => { if !op_ty.is_floating_point() || !target_type.is_numeric() { @@ -1375,7 +1485,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { }); for (value, _) in targets.iter() { - if Scalar::<()>::try_from_uint(value, size).is_none() { + if ScalarInt::try_from_uint(value, size).is_none() { self.fail( location, format!("the value {value:#x} is not a proper {switch_ty:?}"), diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index dd1065590b30..235743fccc89 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -207,8 +207,8 @@ mod move_check; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{par_for_each_in, LRef, MTLock}; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; @@ -224,7 +224,7 @@ use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ - self, AssocKind, GenericParamDefKind, Instance, InstanceDef, Ty, TyCtxt, TypeFoldable, + self, AssocKind, GenericParamDefKind, Instance, InstanceKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, VtblEntry, }; use rustc_middle::ty::{GenericArgKind, GenericArgs}; @@ -236,6 +236,7 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::Size; use std::path::PathBuf; +use tracing::{debug, instrument, trace}; use crate::errors::{ self, EncounteredErrorWhileInstantiating, NoOptimizedMir, RecursionLimit, TypeLengthLimit, @@ -250,10 +251,10 @@ pub enum MonoItemCollectionStrategy { pub struct UsageMap<'tcx> { // Maps every mono item to the mono items used by it. - used_map: FxHashMap, Vec>>, + used_map: UnordMap, Vec>>, // Maps every mono item to the mono items that use it. - user_map: FxHashMap, Vec>>, + user_map: UnordMap, Vec>>, } type MonoItems<'tcx> = Vec>>; @@ -261,10 +262,10 @@ type MonoItems<'tcx> = Vec>>; /// The state that is shared across the concurrent threads that are doing collection. struct SharedState<'tcx> { /// Items that have been or are currently being recursively collected. - visited: MTLock>>, + visited: MTLock>>, /// Items that have been or are currently being recursively treated as "mentioned", i.e., their /// consts are evaluated but nothing is added to the collection. - mentioned: MTLock>>, + mentioned: MTLock>>, /// Which items are being used where, for better errors. usage_map: MTLock>, } @@ -289,7 +290,7 @@ enum CollectionMode { impl<'tcx> UsageMap<'tcx> { fn new() -> UsageMap<'tcx> { - UsageMap { used_map: FxHashMap::default(), user_map: FxHashMap::default() } + UsageMap { used_map: Default::default(), user_map: Default::default() } } fn record_used<'a>( @@ -419,7 +420,7 @@ fn collect_items_rec<'tcx>( used_items.push(respan( starting_item.span, MonoItem::Fn(Instance { - def: InstanceDef::ThreadLocalShim(def_id), + def: InstanceKind::ThreadLocalShim(def_id), args: GenericArgs::empty(), }), )); @@ -592,7 +593,7 @@ fn check_recursion_limit<'tcx>( let recursion_depth = recursion_depths.get(&def_id).cloned().unwrap_or(0); debug!(" => recursion depth={}", recursion_depth); - let adjusted_recursion_depth = if Some(def_id) == tcx.lang_items().drop_in_place_fn() { + let adjusted_recursion_depth = if tcx.is_lang_item(def_id, LangItem::DropInPlace) { // HACK: drop_in_place creates tight monomorphization loops. Give // it more margin. recursion_depth / 4 @@ -667,7 +668,7 @@ struct MirUsedCollector<'a, 'tcx> { used_items: &'a mut MonoItems<'tcx>, /// See the comment in `collect_items_of_instance` for the purpose of this set. /// Note that this contains *not-monomorphized* items! - used_mentioned_items: &'a mut FxHashSet>, + used_mentioned_items: &'a mut UnordSet>, instance: Instance<'tcx>, visiting_call_terminator: bool, move_check: move_check::MoveCheckState, @@ -798,7 +799,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { /// This does not walk the MIR of the constant as that is not needed for codegen, all we need is /// to ensure that the constant evaluates successfully and walk the result. #[instrument(skip(self), level = "debug")] - fn visit_constant(&mut self, constant: &mir::ConstOperand<'tcx>, location: Location) { + fn visit_const_operand(&mut self, constant: &mir::ConstOperand<'tcx>, location: Location) { // No `super_constant` as we don't care about `visit_ty`/`visit_ty_const`. let Some(val) = self.eval_constant(constant) else { return }; collect_const_value(self.tcx, val, self.used_items); @@ -937,14 +938,14 @@ fn visit_instance_use<'tcx>( if !should_codegen_locally(tcx, instance) { return; } - if let ty::InstanceDef::Intrinsic(def_id) = instance.def { + if let ty::InstanceKind::Intrinsic(def_id) = instance.def { let name = tcx.item_name(def_id); if let Some(_requirement) = ValidityRequirement::from_intrinsic(name) { // The intrinsics assert_inhabited, assert_zero_valid, and assert_mem_uninitialized_valid will // be lowered in codegen to nothing or a call to panic_nounwind. So if we encounter any // of those intrinsics, we need to include a mono item for panic_nounwind, else we may try to // codegen a call to that function without generating code for the function itself. - let def_id = tcx.lang_items().get(LangItem::PanicNounwind).unwrap(); + let def_id = tcx.require_lang_item(LangItem::PanicNounwind, None); let panic_instance = Instance::mono(tcx, def_id); if should_codegen_locally(tcx, panic_instance) { output.push(create_fn_mono_item(tcx, panic_instance, source)); @@ -959,31 +960,31 @@ fn visit_instance_use<'tcx>( } match instance.def { - ty::InstanceDef::Virtual(..) | ty::InstanceDef::Intrinsic(_) => { + ty::InstanceKind::Virtual(..) | ty::InstanceKind::Intrinsic(_) => { if !is_direct_call { bug!("{:?} being reified", instance); } } - ty::InstanceDef::ThreadLocalShim(..) => { + ty::InstanceKind::ThreadLocalShim(..) => { bug!("{:?} being reified", instance); } - ty::InstanceDef::DropGlue(_, None) | ty::InstanceDef::AsyncDropGlueCtorShim(_, None) => { + ty::InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None) => { // Don't need to emit noop drop glue if we are calling directly. if !is_direct_call { output.push(create_fn_mono_item(tcx, instance, source)); } } - ty::InstanceDef::DropGlue(_, Some(_)) - | ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(_)) - | ty::InstanceDef::VTableShim(..) - | ty::InstanceDef::ReifyShim(..) - | ty::InstanceDef::ClosureOnceShim { .. } - | ty::InstanceDef::ConstructCoroutineInClosureShim { .. } - | ty::InstanceDef::CoroutineKindShim { .. } - | ty::InstanceDef::Item(..) - | ty::InstanceDef::FnPtrShim(..) - | ty::InstanceDef::CloneShim(..) - | ty::InstanceDef::FnPtrAddrShim(..) => { + ty::InstanceKind::DropGlue(_, Some(_)) + | ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) + | ty::InstanceKind::VTableShim(..) + | ty::InstanceKind::ReifyShim(..) + | ty::InstanceKind::ClosureOnceShim { .. } + | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } + | ty::InstanceKind::CoroutineKindShim { .. } + | ty::InstanceKind::Item(..) + | ty::InstanceKind::FnPtrShim(..) + | ty::InstanceKind::CloneShim(..) + | ty::InstanceKind::FnPtrAddrShim(..) => { output.push(create_fn_mono_item(tcx, instance, source)); } } @@ -1271,7 +1272,7 @@ fn collect_items_of_instance<'tcx>( // mentioned item. So instead we collect all pre-monomorphized `MentionedItem` that were already // added to `used_items` in a hash set, which can efficiently query in the // `body.mentioned_items` loop below without even having to monomorphize the item. - let mut used_mentioned_items = FxHashSet::>::default(); + let mut used_mentioned_items = Default::default(); let mut collector = MirUsedCollector { tcx, body, @@ -1429,10 +1430,19 @@ impl<'v> RootCollector<'_, 'v> { match self.tcx.def_kind(id.owner_id) { DefKind::Enum | DefKind::Struct | DefKind::Union => { if self.strategy == MonoItemCollectionStrategy::Eager - && self.tcx.generics_of(id.owner_id).count() == 0 + && self.tcx.generics_of(id.owner_id).is_empty() { debug!("RootCollector: ADT drop-glue for `{id:?}`",); + // This type is impossible to instantiate, so we should not try to + // generate a `drop_in_place` instance for it. + if self.tcx.instantiate_and_check_impossible_predicates(( + id.owner_id.to_def_id(), + ty::List::empty(), + )) { + return; + } + let ty = self.tcx.type_of(id.owner_id.to_def_id()).no_bound_vars().unwrap(); visit_drop_use(self.tcx, ty, true, DUMMY_SP, self.output); } @@ -1618,10 +1628,10 @@ fn create_mono_items_for_default_impls<'tcx>( //=----------------------------------------------------------------------------- #[instrument(skip(tcx, strategy), level = "debug")] -pub fn collect_crate_mono_items( - tcx: TyCtxt<'_>, +pub(crate) fn collect_crate_mono_items<'tcx>( + tcx: TyCtxt<'tcx>, strategy: MonoItemCollectionStrategy, -) -> (FxHashSet>, UsageMap<'_>) { +) -> (Vec>, UsageMap<'tcx>) { let _prof_timer = tcx.prof.generic_activity("monomorphization_collector"); let roots = tcx @@ -1631,8 +1641,8 @@ pub fn collect_crate_mono_items( debug!("building mono item graph, beginning at roots"); let mut state = SharedState { - visited: MTLock::new(FxHashSet::default()), - mentioned: MTLock::new(FxHashSet::default()), + visited: MTLock::new(UnordSet::default()), + mentioned: MTLock::new(UnordSet::default()), usage_map: MTLock::new(UsageMap::new()), }; let recursion_limit = tcx.recursion_limit(); @@ -1655,5 +1665,11 @@ pub fn collect_crate_mono_items( }); } - (state.visited.into_inner(), state.usage_map.into_inner()) + // The set of MonoItems was created in an inherently indeterministic order because + // of parallelism. We sort it here to ensure that the output is deterministic. + let mono_items = tcx.with_stable_hashing_context(move |ref hcx| { + state.visited.into_inner().into_sorted(hcx, true) + }); + + (mono_items, state.usage_map.into_inner()) } diff --git a/compiler/rustc_monomorphize/src/collector/move_check.rs b/compiler/rustc_monomorphize/src/collector/move_check.rs index 4cc7275ab806..851f86e86b8e 100644 --- a/compiler/rustc_monomorphize/src/collector/move_check.rs +++ b/compiler/rustc_monomorphize/src/collector/move_check.rs @@ -1,4 +1,5 @@ use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; +use tracing::debug; use super::*; use crate::errors::LargeAssignmentsLint; diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index 35fc78f20453..c0d1efd96c5b 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use crate::fluent_generated as fluent; -use rustc_errors::{Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level}; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level}; use rustc_macros::{Diagnostic, LintDiagnostic}; use rustc_span::{Span, Symbol}; @@ -48,7 +48,7 @@ pub struct UnusedGenericParamsHint { impl Diagnostic<'_, G> for UnusedGenericParamsHint { #[track_caller] - fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let mut diag = Diag::new(dcx, level, fluent::monomorphize_unused_generic_params); diag.span(self.span); for (span, name) in self.param_spans.into_iter().zip(self.param_names) { diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index ddfb42a6f4a6..aa3b4cd5b678 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -1,9 +1,7 @@ +// tidy-alphabetical-start #![feature(array_windows)] #![feature(is_sorted)] -#![allow(rustc::potential_query_instability)] - -#[macro_use] -extern crate tracing; +// tidy-alphabetical-end use rustc_hir::lang_items::LangItem; use rustc_middle::bug; diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 8f7b4c6472c9..9a7c488833a1 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -98,11 +98,13 @@ use std::fs::{self, File}; use std::io::{BufWriter, Write}; use std::path::{Path, PathBuf}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sync; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE}; use rustc_hir::definitions::DefPathDataName; +use rustc_hir::LangItem; use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; @@ -112,10 +114,11 @@ use rustc_middle::mir::mono::{ }; use rustc_middle::query::Providers; use rustc_middle::ty::print::{characteristic_def_id_of_type, with_no_trimmed_paths}; -use rustc_middle::ty::{self, visit::TypeVisitableExt, InstanceDef, TyCtxt}; +use rustc_middle::ty::{self, visit::TypeVisitableExt, InstanceKind, TyCtxt}; use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath}; use rustc_session::CodegenUnits; use rustc_span::symbol::Symbol; +use tracing::debug; use crate::collector::UsageMap; use crate::collector::{self, MonoItemCollectionStrategy}; @@ -130,7 +133,7 @@ struct PlacedMonoItems<'tcx> { /// The codegen units, sorted by name to make things deterministic. codegen_units: Vec>, - internalization_candidates: FxHashSet>, + internalization_candidates: UnordSet>, } // The output CGUs are sorted by name. @@ -196,9 +199,9 @@ fn place_mono_items<'tcx, I>(cx: &PartitioningCx<'_, 'tcx>, mono_items: I) -> Pl where I: Iterator>, { - let mut codegen_units = FxHashMap::default(); + let mut codegen_units = UnordMap::default(); let is_incremental_build = cx.tcx.sess.opts.incremental.is_some(); - let mut internalization_candidates = FxHashSet::default(); + let mut internalization_candidates = UnordSet::default(); // Determine if monomorphizations instantiated in this crate will be made // available to downstream crates. This depends on whether we are in @@ -208,7 +211,7 @@ where cx.tcx.sess.opts.share_generics() && cx.tcx.local_crate_exports_generics(); let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx); - let cgu_name_cache = &mut FxHashMap::default(); + let cgu_name_cache = &mut UnordMap::default(); for mono_item in mono_items { // Handle only root (GloballyShared) items directly here. Inlined (LocalCopy) items @@ -259,7 +262,7 @@ where // going via another root item. This includes drop-glue, functions from // external crates, and local functions the definition of which is // marked with `#[inline]`. - let mut reachable_inlined_items = FxHashSet::default(); + let mut reachable_inlined_items = FxIndexSet::default(); get_reachable_inlined_items(cx.tcx, mono_item, cx.usage_map, &mut reachable_inlined_items); // Add those inlined items. It's possible an inlined item is reachable @@ -283,8 +286,9 @@ where codegen_units.insert(cgu_name, CodegenUnit::new(cgu_name)); } - let mut codegen_units: Vec<_> = codegen_units.into_values().collect(); - codegen_units.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str())); + let mut codegen_units: Vec<_> = cx.tcx.with_stable_hashing_context(|ref hcx| { + codegen_units.into_items().map(|(_, cgu)| cgu).collect_sorted(hcx, true) + }); for cgu in codegen_units.iter_mut() { cgu.compute_size_estimate(); @@ -296,7 +300,7 @@ where tcx: TyCtxt<'tcx>, item: MonoItem<'tcx>, usage_map: &UsageMap<'tcx>, - visited: &mut FxHashSet>, + visited: &mut FxIndexSet>, ) { usage_map.for_each_inlined_used_item(tcx, item, |inlined_item| { let is_new = visited.insert(inlined_item); @@ -319,7 +323,7 @@ fn merge_codegen_units<'tcx>( assert!(codegen_units.is_sorted_by(|a, b| a.name().as_str() <= b.name().as_str())); // This map keeps track of what got merged into what. - let mut cgu_contents: FxHashMap> = + let mut cgu_contents: UnordMap> = codegen_units.iter().map(|cgu| (cgu.name(), vec![cgu.name()])).collect(); // If N is the maximum number of CGUs, and the CGUs are sorted from largest @@ -421,22 +425,24 @@ fn merge_codegen_units<'tcx>( // For CGUs that contain the code of multiple modules because of the // merging done above, we use a concatenation of the names of all // contained CGUs. - let new_cgu_names: FxHashMap = cgu_contents - .into_iter() - // This `filter` makes sure we only update the name of CGUs that - // were actually modified by merging. - .filter(|(_, cgu_contents)| cgu_contents.len() > 1) - .map(|(current_cgu_name, cgu_contents)| { - let mut cgu_contents: Vec<&str> = cgu_contents.iter().map(|s| s.as_str()).collect(); + let new_cgu_names = UnordMap::from( + cgu_contents + .items() + // This `filter` makes sure we only update the name of CGUs that + // were actually modified by merging. + .filter(|(_, cgu_contents)| cgu_contents.len() > 1) + .map(|(current_cgu_name, cgu_contents)| { + let mut cgu_contents: Vec<&str> = + cgu_contents.iter().map(|s| s.as_str()).collect(); - // Sort the names, so things are deterministic and easy to - // predict. We are sorting primitive `&str`s here so we can - // use unstable sort. - cgu_contents.sort_unstable(); + // Sort the names, so things are deterministic and easy to + // predict. We are sorting primitive `&str`s here so we can + // use unstable sort. + cgu_contents.sort_unstable(); - (current_cgu_name, cgu_contents.join("--")) - }) - .collect(); + (*current_cgu_name, cgu_contents.join("--")) + }), + ); for cgu in codegen_units.iter_mut() { if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) { @@ -510,7 +516,7 @@ fn compute_inlined_overlap<'tcx>(cgu1: &CodegenUnit<'tcx>, cgu2: &CodegenUnit<'t fn internalize_symbols<'tcx>( cx: &PartitioningCx<'_, 'tcx>, codegen_units: &mut [CodegenUnit<'tcx>], - internalization_candidates: FxHashSet>, + internalization_candidates: UnordSet>, ) { /// For symbol internalization, we need to know whether a symbol/mono-item /// is used from outside the codegen unit it is defined in. This type is @@ -521,7 +527,7 @@ fn internalize_symbols<'tcx>( MultipleCgus, } - let mut mono_item_placements = FxHashMap::default(); + let mut mono_item_placements = UnordMap::default(); let single_codegen_unit = codegen_units.len() == 1; if !single_codegen_unit { @@ -614,20 +620,20 @@ fn characteristic_def_id_of_mono_item<'tcx>( match mono_item { MonoItem::Fn(instance) => { let def_id = match instance.def { - ty::InstanceDef::Item(def) => def, - ty::InstanceDef::VTableShim(..) - | ty::InstanceDef::ReifyShim(..) - | ty::InstanceDef::FnPtrShim(..) - | ty::InstanceDef::ClosureOnceShim { .. } - | ty::InstanceDef::ConstructCoroutineInClosureShim { .. } - | ty::InstanceDef::CoroutineKindShim { .. } - | ty::InstanceDef::Intrinsic(..) - | ty::InstanceDef::DropGlue(..) - | ty::InstanceDef::Virtual(..) - | ty::InstanceDef::CloneShim(..) - | ty::InstanceDef::ThreadLocalShim(..) - | ty::InstanceDef::FnPtrAddrShim(..) - | ty::InstanceDef::AsyncDropGlueCtorShim(..) => return None, + ty::InstanceKind::Item(def) => def, + ty::InstanceKind::VTableShim(..) + | ty::InstanceKind::ReifyShim(..) + | ty::InstanceKind::FnPtrShim(..) + | ty::InstanceKind::ClosureOnceShim { .. } + | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } + | ty::InstanceKind::CoroutineKindShim { .. } + | ty::InstanceKind::Intrinsic(..) + | ty::InstanceKind::DropGlue(..) + | ty::InstanceKind::Virtual(..) + | ty::InstanceKind::CloneShim(..) + | ty::InstanceKind::ThreadLocalShim(..) + | ty::InstanceKind::FnPtrAddrShim(..) + | ty::InstanceKind::AsyncDropGlueCtorShim(..) => return None, }; // If this is a method, we want to put it into the same module as @@ -738,7 +744,7 @@ fn mono_item_linkage_and_visibility<'tcx>( (Linkage::External, vis) } -type CguNameCache = FxHashMap<(DefId, bool), Symbol>; +type CguNameCache = UnordMap<(DefId, bool), Symbol>; fn static_visibility<'tcx>( tcx: TyCtxt<'tcx>, @@ -771,28 +777,28 @@ fn mono_item_visibility<'tcx>( }; let def_id = match instance.def { - InstanceDef::Item(def_id) - | InstanceDef::DropGlue(def_id, Some(_)) - | InstanceDef::AsyncDropGlueCtorShim(def_id, Some(_)) => def_id, + InstanceKind::Item(def_id) + | InstanceKind::DropGlue(def_id, Some(_)) + | InstanceKind::AsyncDropGlueCtorShim(def_id, Some(_)) => def_id, // We match the visibility of statics here - InstanceDef::ThreadLocalShim(def_id) => { + InstanceKind::ThreadLocalShim(def_id) => { return static_visibility(tcx, can_be_internalized, def_id); } // These are all compiler glue and such, never exported, always hidden. - InstanceDef::VTableShim(..) - | InstanceDef::ReifyShim(..) - | InstanceDef::FnPtrShim(..) - | InstanceDef::Virtual(..) - | InstanceDef::Intrinsic(..) - | InstanceDef::ClosureOnceShim { .. } - | InstanceDef::ConstructCoroutineInClosureShim { .. } - | InstanceDef::CoroutineKindShim { .. } - | InstanceDef::DropGlue(..) - | InstanceDef::AsyncDropGlueCtorShim(..) - | InstanceDef::CloneShim(..) - | InstanceDef::FnPtrAddrShim(..) => return Visibility::Hidden, + InstanceKind::VTableShim(..) + | InstanceKind::ReifyShim(..) + | InstanceKind::FnPtrShim(..) + | InstanceKind::Virtual(..) + | InstanceKind::Intrinsic(..) + | InstanceKind::ClosureOnceShim { .. } + | InstanceKind::ConstructCoroutineInClosureShim { .. } + | InstanceKind::CoroutineKindShim { .. } + | InstanceKind::DropGlue(..) + | InstanceKind::AsyncDropGlueCtorShim(..) + | InstanceKind::CloneShim(..) + | InstanceKind::FnPtrAddrShim(..) => return Visibility::Hidden, }; // The `start_fn` lang item is actually a monomorphized instance of a @@ -807,8 +813,8 @@ fn mono_item_visibility<'tcx>( // internalization, but we have to understand that it's referenced // from the `main` symbol we'll generate later. // - // This may be fixable with a new `InstanceDef` perhaps? Unsure! - if tcx.lang_items().start_fn() == Some(def_id) { + // This may be fixable with a new `InstanceKind` perhaps? Unsure! + if tcx.is_lang_item(def_id, LangItem::Start) { *can_be_internalized = false; return Visibility::Hidden; } @@ -931,7 +937,7 @@ fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit< // // Also, unreached inlined items won't be counted here. This is fine. - let mut inlined_items = FxHashSet::default(); + let mut inlined_items = UnordSet::default(); let mut root_items = 0; let mut unique_inlined_items = 0; @@ -1163,7 +1169,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co } if tcx.sess.opts.unstable_opts.print_mono_items.is_some() { - let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default(); + let mut item_to_cgus: UnordMap<_, Vec<_>> = Default::default(); for cgu in codegen_units { for (&mono_item, &data) in cgu.items() { @@ -1239,7 +1245,7 @@ fn dump_mono_items_stats<'tcx>( let mut file = BufWriter::new(file); // Gather instantiated mono items grouped by def_id - let mut items_per_def_id: FxHashMap<_, Vec<_>> = Default::default(); + let mut items_per_def_id: FxIndexMap<_, Vec<_>> = Default::default(); for cgu in codegen_units { cgu.items() .keys() diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs index 6487169d1739..2d69bfa4da8e 100644 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ b/compiler/rustc_monomorphize/src/polymorphize.rs @@ -18,6 +18,7 @@ use rustc_middle::ty::{ GenericArgsRef, Ty, TyCtxt, UnusedGenericParams, }; use rustc_span::symbol::sym; +use tracing::{debug, instrument}; use crate::errors::UnusedGenericParamsHint; @@ -32,7 +33,7 @@ pub fn provide(providers: &mut Providers) { /// parameters are used). fn unused_generic_params<'tcx>( tcx: TyCtxt<'tcx>, - instance: ty::InstanceDef<'tcx>, + instance: ty::InstanceKind<'tcx>, ) -> UnusedGenericParams { assert!(instance.def_id().is_local()); @@ -51,7 +52,7 @@ fn unused_generic_params<'tcx>( debug!(?generics); // Exit early when there are no parameters to be unused. - if generics.count() == 0 { + if generics.is_empty() { return UnusedGenericParams::new_all_used(); } @@ -87,7 +88,7 @@ fn unused_generic_params<'tcx>( fn should_polymorphize<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, - instance: ty::InstanceDef<'tcx>, + instance: ty::InstanceKind<'tcx>, ) -> bool { // If an instance's MIR body is not polymorphic then the modified generic parameters that are // derived from polymorphization's result won't make any difference. @@ -96,7 +97,7 @@ fn should_polymorphize<'tcx>( } // Don't polymorphize intrinsics or virtual calls - calling `instance_mir` will panic. - if matches!(instance, ty::InstanceDef::Intrinsic(..) | ty::InstanceDef::Virtual(..)) { + if matches!(instance, ty::InstanceKind::Intrinsic(..) | ty::InstanceKind::Virtual(..)) { return false; } @@ -229,7 +230,7 @@ impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> { /// a closure, coroutine or constant). #[instrument(level = "debug", skip(self, def_id, args))] fn visit_child_body(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) { - let instance = ty::InstanceDef::Item(def_id); + let instance = ty::InstanceKind::Item(def_id); let unused = self.tcx.unused_generic_params(instance); debug!(?self.unused_parameters, ?unused); for (i, arg) in args.iter().enumerate() { @@ -260,9 +261,9 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { self.super_local_decl(local, local_decl); } - fn visit_constant(&mut self, ct: &mir::ConstOperand<'tcx>, location: Location) { + fn visit_const_operand(&mut self, ct: &mir::ConstOperand<'tcx>, location: Location) { match ct.const_ { - mir::Const::Ty(c) => { + mir::Const::Ty(_, c) => { c.visit_with(self); } mir::Const::Unevaluated(mir::UnevaluatedConst { def, args: _, promoted }, ty) => { diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml index 8bcc21d82f85..3a5f438b4325 100644 --- a/compiler/rustc_next_trait_solver/Cargo.toml +++ b/compiler/rustc_next_trait_solver/Cargo.toml @@ -4,20 +4,26 @@ version = "0.0.0" edition = "2021" [dependencies] -rustc_type_ir = { path = "../rustc_type_ir", default-features = false } +# tidy-alphabetical-start +bitflags = "2.4.1" derivative = "2.2.0" -rustc_macros = { path = "../rustc_macros", optional = true } -rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } -rustc_serialize = { path = "../rustc_serialize", optional = true } +rustc_ast_ir = { path = "../rustc_ast_ir" } rustc_data_structures = { path = "../rustc_data_structures", optional = true } -rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false } +rustc_index = { path = "../rustc_index", default-features = false } +rustc_macros = { path = "../rustc_macros", optional = true } +rustc_serialize = { path = "../rustc_serialize", optional = true } +rustc_type_ir = { path = "../rustc_type_ir", default-features = false } +rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } +tracing = "0.1" +# tidy-alphabetical-end [features] default = ["nightly"] nightly = [ - "rustc_type_ir/nightly", + "rustc_ast_ir/nightly", + "rustc_data_structures", + "rustc_index/nightly", "rustc_macros", "rustc_serialize", - "rustc_data_structures", - "rustc_ast_ir/nightly", + "rustc_type_ir/nightly", ] diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 755f5cfa5a37..a81fd03d034f 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -4,10 +4,11 @@ use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::visit::TypeVisitableExt; use rustc_type_ir::{ - self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, InferCtxtLike, - Interner, + self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, Interner, }; +use crate::infcx::SolverDelegate; + /// Whether we're canonicalizing a query input or the query response. /// /// When canonicalizing an input we're in the context of the caller @@ -37,7 +38,7 @@ pub enum CanonicalizeMode { }, } -pub struct Canonicalizer<'a, Infcx: InferCtxtLike, I: Interner> { +pub struct Canonicalizer<'a, Infcx: SolverDelegate, I: Interner> { infcx: &'a Infcx, canonicalize_mode: CanonicalizeMode, @@ -46,7 +47,7 @@ pub struct Canonicalizer<'a, Infcx: InferCtxtLike, I: Interner> { binder_index: ty::DebruijnIndex, } -impl<'a, Infcx: InferCtxtLike, I: Interner> Canonicalizer<'a, Infcx, I> { +impl<'a, Infcx: SolverDelegate, I: Interner> Canonicalizer<'a, Infcx, I> { pub fn canonicalize>( infcx: &'a Infcx, canonicalize_mode: CanonicalizeMode, @@ -210,17 +211,16 @@ impl<'a, Infcx: InferCtxtLike, I: Interner> Canonicalizer<'a, Infc } } -impl, I: Interner> TypeFolder +impl, I: Interner> TypeFolder for Canonicalizer<'_, Infcx, I> { fn interner(&self) -> I { self.infcx.interner() } - fn fold_binder(&mut self, t: I::Binder) -> I::Binder + fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder where T: TypeFoldable, - I::Binder: TypeSuperFoldable, { self.binder_index.shift_in(1); let t = t.super_fold_with(self); @@ -268,7 +268,7 @@ impl, I: Interner> TypeFolder ty::ReVar(vid) => { assert_eq!( self.infcx.opportunistic_resolve_lt_var(vid), - None, + r, "region vid should have been resolved fully before canonicalization" ); match self.canonicalize_mode { @@ -302,13 +302,8 @@ impl, I: Interner> TypeFolder ty::Infer(i) => match i { ty::TyVar(vid) => { assert_eq!( - self.infcx.root_ty_var(vid), - vid, - "ty vid should have been resolved fully before canonicalization" - ); - assert_eq!( - self.infcx.probe_ty_var(vid), - None, + self.infcx.opportunistic_resolve_ty_var(vid), + t, "ty vid should have been resolved fully before canonicalization" ); @@ -318,10 +313,24 @@ impl, I: Interner> TypeFolder .unwrap_or_else(|| panic!("ty var should have been resolved: {t:?}")), )) } - ty::IntVar(_) => CanonicalVarKind::Ty(CanonicalTyVarKind::Int), - ty::FloatVar(_) => CanonicalVarKind::Ty(CanonicalTyVarKind::Float), + ty::IntVar(vid) => { + assert_eq!( + self.infcx.opportunistic_resolve_int_var(vid), + t, + "ty vid should have been resolved fully before canonicalization" + ); + CanonicalVarKind::Ty(CanonicalTyVarKind::Int) + } + ty::FloatVar(vid) => { + assert_eq!( + self.infcx.opportunistic_resolve_float_var(vid), + t, + "ty vid should have been resolved fully before canonicalization" + ); + CanonicalVarKind::Ty(CanonicalTyVarKind::Float) + } ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => { - todo!() + panic!("fresh vars not expected in canonicalization") } }, ty::Placeholder(placeholder) => match self.canonicalize_mode { @@ -378,26 +387,15 @@ impl, I: Interner> TypeFolder } fn fold_const(&mut self, c: I::Const) -> I::Const { - // We could canonicalize all consts with static types, but the only ones we - // *really* need to worry about are the ones that we end up putting into `CanonicalVarKind` - // since canonical vars can't reference other canonical vars. - let ty = c - .ty() - .fold_with(&mut RegionsToStatic { interner: self.interner(), binder: ty::INNERMOST }); let kind = match c.kind() { ty::ConstKind::Infer(i) => match i { ty::InferConst::Var(vid) => { assert_eq!( - self.infcx.root_ct_var(vid), - vid, - "region vid should have been resolved fully before canonicalization" + self.infcx.opportunistic_resolve_ct_var(vid), + c, + "const vid should have been resolved fully before canonicalization" ); - assert_eq!( - self.infcx.probe_ct_var(vid), - None, - "region vid should have been resolved fully before canonicalization" - ); - CanonicalVarKind::Const(self.infcx.universe_of_ct(vid).unwrap(), ty) + CanonicalVarKind::Const(self.infcx.universe_of_ct(vid).unwrap()) } ty::InferConst::EffectVar(_) => CanonicalVarKind::Effect, ty::InferConst::Fresh(_) => todo!(), @@ -405,23 +403,21 @@ impl, I: Interner> TypeFolder ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode { CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst( PlaceholderLike::new(placeholder.universe(), self.variables.len().into()), - ty, ), CanonicalizeMode::Response { .. } => { - CanonicalVarKind::PlaceholderConst(placeholder, ty) + CanonicalVarKind::PlaceholderConst(placeholder) } }, ty::ConstKind::Param(_) => match self.canonicalize_mode { CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst( PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()), - ty, ), CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"), }, // FIXME: See comment above -- we could fold the region separately or something. ty::ConstKind::Bound(_, _) | ty::ConstKind::Unevaluated(_) - | ty::ConstKind::Value(_) + | ty::ConstKind::Value(_, _) | ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => return c.super_fold_with(self), }; @@ -435,35 +431,6 @@ impl, I: Interner> TypeFolder }), ); - Const::new_anon_bound(self.interner(), self.binder_index, var, ty) - } -} - -struct RegionsToStatic { - interner: I, - binder: ty::DebruijnIndex, -} - -impl TypeFolder for RegionsToStatic { - fn interner(&self) -> I { - self.interner - } - - fn fold_binder(&mut self, t: I::Binder) -> I::Binder - where - T: TypeFoldable, - I::Binder: TypeSuperFoldable, - { - self.binder.shift_in(1); - let t = t.super_fold_with(self); - self.binder.shift_out(1); - t - } - - fn fold_region(&mut self, r: I::Region) -> I::Region { - match r.kind() { - ty::ReBound(db, _) if self.binder > db => r, - _ => Region::new_static(self.interner()), - } + Const::new_anon_bound(self.interner(), self.binder_index, var) } } diff --git a/compiler/rustc_next_trait_solver/src/infcx.rs b/compiler/rustc_next_trait_solver/src/infcx.rs new file mode 100644 index 000000000000..c249eb94cf65 --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/infcx.rs @@ -0,0 +1,205 @@ +use std::fmt::Debug; + +use rustc_type_ir::fold::TypeFoldable; +use rustc_type_ir::relate::Relate; +use rustc_type_ir::solve::{Certainty, Goal, NoSolution, SolverMode}; +use rustc_type_ir::{self as ty, Interner}; + +pub trait SolverDelegate: Sized { + type Interner: Interner; + fn interner(&self) -> Self::Interner; + + type Span: Copy; + + fn solver_mode(&self) -> SolverMode; + + fn build_with_canonical( + interner: Self::Interner, + solver_mode: SolverMode, + canonical: &ty::Canonical, + ) -> (Self, V, ty::CanonicalVarValues) + where + V: TypeFoldable; + + fn universe(&self) -> ty::UniverseIndex; + fn create_next_universe(&self) -> ty::UniverseIndex; + + fn universe_of_ty(&self, ty: ty::TyVid) -> Option; + fn universe_of_lt(&self, lt: ty::RegionVid) -> Option; + fn universe_of_ct(&self, ct: ty::ConstVid) -> Option; + + fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid; + fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid; + + fn opportunistic_resolve_ty_var(&self, vid: ty::TyVid) -> ::Ty; + fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> ::Ty; + fn opportunistic_resolve_float_var( + &self, + vid: ty::FloatVid, + ) -> ::Ty; + fn opportunistic_resolve_ct_var( + &self, + vid: ty::ConstVid, + ) -> ::Const; + fn opportunistic_resolve_effect_var( + &self, + vid: ty::EffectVid, + ) -> ::Const; + fn opportunistic_resolve_lt_var( + &self, + vid: ty::RegionVid, + ) -> ::Region; + + fn defining_opaque_types(&self) -> ::DefiningOpaqueTypes; + + fn next_ty_infer(&self) -> ::Ty; + fn next_const_infer(&self) -> ::Const; + fn fresh_args_for_item( + &self, + def_id: ::DefId, + ) -> ::GenericArgs; + + fn fresh_var_for_kind_with_span( + &self, + arg: ::GenericArg, + span: Self::Span, + ) -> ::GenericArg; + + fn instantiate_binder_with_infer + Copy>( + &self, + value: ty::Binder, + ) -> T; + + fn enter_forall + Copy, U>( + &self, + value: ty::Binder, + f: impl FnOnce(T) -> U, + ) -> U; + + fn relate>( + &self, + param_env: ::ParamEnv, + lhs: T, + variance: ty::Variance, + rhs: T, + ) -> Result::Predicate>>, NoSolution>; + + fn eq_structurally_relating_aliases>( + &self, + param_env: ::ParamEnv, + lhs: T, + rhs: T, + ) -> Result::Predicate>>, NoSolution>; + + fn resolve_vars_if_possible(&self, value: T) -> T + where + T: TypeFoldable; + + fn probe(&self, probe: impl FnOnce() -> T) -> T; + + // FIXME: Uplift the leak check into this crate. + fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution>; + + // FIXME: This is only here because elaboration lives in `rustc_infer`! + fn elaborate_supertraits( + interner: Self::Interner, + trait_ref: ty::Binder>, + ) -> impl Iterator>>; + + fn try_const_eval_resolve( + &self, + param_env: ::ParamEnv, + unevaluated: ty::UnevaluatedConst, + ) -> Option<::Const>; + + fn sub_regions( + &self, + sub: ::Region, + sup: ::Region, + ); + + fn register_ty_outlives( + &self, + ty: ::Ty, + r: ::Region, + ); + + // FIXME: This only is here because `wf::obligations` is in `rustc_trait_selection`! + fn well_formed_goals( + &self, + param_env: ::ParamEnv, + arg: ::GenericArg, + ) -> Option::Predicate>>>; + + fn clone_opaque_types_for_query_response( + &self, + ) -> Vec<(ty::OpaqueTypeKey, ::Ty)>; + + fn make_deduplicated_outlives_constraints( + &self, + ) -> Vec::GenericArg>>; + + fn instantiate_canonical( + &self, + canonical: ty::Canonical, + values: ty::CanonicalVarValues, + ) -> V + where + V: TypeFoldable; + + fn instantiate_canonical_var_with_infer( + &self, + cv_info: ty::CanonicalVarInfo, + universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, + ) -> ::GenericArg; + + // FIXME: Can we implement this in terms of `add` and `inject`? + fn insert_hidden_type( + &self, + opaque_type_key: ty::OpaqueTypeKey, + param_env: ::ParamEnv, + hidden_ty: ::Ty, + goals: &mut Vec::Predicate>>, + ) -> Result<(), NoSolution>; + + fn add_item_bounds_for_hidden_type( + &self, + def_id: ::DefId, + args: ::GenericArgs, + param_env: ::ParamEnv, + hidden_ty: ::Ty, + goals: &mut Vec::Predicate>>, + ); + + fn inject_new_hidden_type_unchecked( + &self, + key: ty::OpaqueTypeKey, + hidden_ty: ::Ty, + ); + + fn reset_opaque_types(&self); + + fn trait_ref_is_knowable( + &self, + trait_ref: ty::TraitRef, + lazily_normalize_ty: impl FnMut( + ::Ty, + ) -> Result<::Ty, E>, + ) -> Result; + + fn fetch_eligible_assoc_item( + &self, + param_env: ::ParamEnv, + goal_trait_ref: ty::TraitRef, + trait_assoc_def_id: ::DefId, + impl_def_id: ::DefId, + ) -> Result::DefId>, NoSolution>; + + fn is_transmutable( + &self, + param_env: ::ParamEnv, + dst: ::Ty, + src: ::Ty, + assume: ::Const, + ) -> Result; +} diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs index 4202dc39fb2f..79c6925221e1 100644 --- a/compiler/rustc_next_trait_solver/src/lib.rs +++ b/compiler/rustc_next_trait_solver/src/lib.rs @@ -1,2 +1,12 @@ +//! Crate containing the implementation of the next-generation trait solver. +//! +//! This crate may also contain things that are used by the old trait solver, +//! but were uplifted in the process of making the new trait solver generic. +//! So if you got to this crate from the old solver, it's totally normal. + +#![feature(let_chains)] + pub mod canonicalizer; +pub mod infcx; +pub mod resolve; pub mod solve; diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs new file mode 100644 index 000000000000..3d8d957eaae4 --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/resolve.rs @@ -0,0 +1,81 @@ +use crate::infcx::SolverDelegate; +use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_type_ir::inherent::*; +use rustc_type_ir::visit::TypeVisitableExt; +use rustc_type_ir::{self as ty, Interner}; + +/////////////////////////////////////////////////////////////////////////// +// EAGER RESOLUTION + +/// Resolves ty, region, and const vars to their inferred values or their root vars. +pub struct EagerResolver<'a, Infcx, I = ::Interner> +where + Infcx: SolverDelegate, + I: Interner, +{ + infcx: &'a Infcx, +} + +impl<'a, Infcx: SolverDelegate> EagerResolver<'a, Infcx> { + pub fn new(infcx: &'a Infcx) -> Self { + EagerResolver { infcx } + } +} + +impl, I: Interner> TypeFolder for EagerResolver<'_, Infcx> { + fn interner(&self) -> I { + self.infcx.interner() + } + + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { + match t.kind() { + ty::Infer(ty::TyVar(vid)) => { + let resolved = self.infcx.opportunistic_resolve_ty_var(vid); + if t != resolved && resolved.has_infer() { + resolved.fold_with(self) + } else { + resolved + } + } + ty::Infer(ty::IntVar(vid)) => self.infcx.opportunistic_resolve_int_var(vid), + ty::Infer(ty::FloatVar(vid)) => self.infcx.opportunistic_resolve_float_var(vid), + _ => { + if t.has_infer() { + t.super_fold_with(self) + } else { + t + } + } + } + } + + fn fold_region(&mut self, r: I::Region) -> I::Region { + match r.kind() { + ty::ReVar(vid) => self.infcx.opportunistic_resolve_lt_var(vid), + _ => r, + } + } + + fn fold_const(&mut self, c: I::Const) -> I::Const { + match c.kind() { + ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { + let resolved = self.infcx.opportunistic_resolve_ct_var(vid); + if c != resolved && resolved.has_infer() { + resolved.fold_with(self) + } else { + resolved + } + } + ty::ConstKind::Infer(ty::InferConst::EffectVar(vid)) => { + self.infcx.opportunistic_resolve_effect_var(vid) + } + _ => { + if c.has_infer() { + c.super_fold_with(self) + } else { + c + } + } + } + } +} diff --git a/compiler/rustc_next_trait_solver/src/solve.rs b/compiler/rustc_next_trait_solver/src/solve.rs deleted file mode 100644 index eba96facabc6..000000000000 --- a/compiler/rustc_next_trait_solver/src/solve.rs +++ /dev/null @@ -1 +0,0 @@ -pub use rustc_type_ir::solve::*; diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs similarity index 72% rename from compiler/rustc_trait_selection/src/solve/alias_relate.rs rename to compiler/rustc_next_trait_solver/src/solve/alias_relate.rs index 65cc0a458573..fbc8ac1d5d5c 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs @@ -15,18 +15,26 @@ //! (3.) Otherwise, if we end with two rigid (non-projection) or infer types, //! relate them structurally. -use super::EvalCtxt; -use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; -use rustc_middle::ty; +use rustc_type_ir::inherent::*; +use rustc_type_ir::{self as ty, Interner}; +use tracing::{instrument, trace}; -impl<'tcx> EvalCtxt<'_, 'tcx> { +use crate::infcx::SolverDelegate; +use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult}; + +impl EvalCtxt<'_, Infcx> +where + Infcx: SolverDelegate, + I: Interner, +{ #[instrument(level = "trace", skip(self), ret)] pub(super) fn compute_alias_relate_goal( &mut self, - goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>, - ) -> QueryResult<'tcx> { - let tcx = self.tcx(); + goal: Goal, + ) -> QueryResult { + let tcx = self.interner(); let Goal { param_env, predicate: (lhs, rhs, direction) } = goal; + debug_assert!(lhs.to_alias_term().is_some() || rhs.to_alias_term().is_some()); // Structurally normalize the lhs. let lhs = if let Some(alias) = lhs.to_alias_term() { @@ -46,6 +54,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { rhs }; + // Add a `make_canonical_response` probe step so that we treat this as + // a candidate, even if `try_evaluate_added_goals` bails due to an error. + // It's `Certainty::AMBIGUOUS` because this candidate is not "finished", + // since equating the normalized terms will lead to additional constraints. + self.inspect.make_canonical_response(Certainty::AMBIGUOUS); + // Apply the constraints. self.try_evaluate_added_goals()?; let lhs = self.resolve_vars_if_possible(lhs); @@ -53,8 +67,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { trace!(?lhs, ?rhs); let variance = match direction { - ty::AliasRelationDirection::Equate => ty::Variance::Invariant, - ty::AliasRelationDirection::Subtype => ty::Variance::Covariant, + ty::AliasRelationDirection::Equate => ty::Invariant, + ty::AliasRelationDirection::Subtype => ty::Covariant, }; match (lhs.to_alias_term(), rhs.to_alias_term()) { (None, None) => { @@ -70,7 +84,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.relate_rigid_alias_non_alias( param_env, alias, - variance.xform(ty::Variance::Contravariant), + variance.xform(ty::Contravariant), lhs, )?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs similarity index 59% rename from compiler/rustc_trait_selection/src/solve/assembly/mod.rs rename to compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index b1dd6ae6611e..9a1537d26060 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -1,68 +1,69 @@ //! Code shared by trait and projection goals for candidate assembly. -use crate::solve::GoalSource; -use crate::solve::{EvalCtxt, SolverMode}; -use rustc_hir::def_id::DefId; -use rustc_infer::traits::query::NoSolution; -use rustc_middle::bug; -use rustc_middle::traits::solve::inspect::ProbeKind; -use rustc_middle::traits::solve::{ - CandidateSource, CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult, -}; -use rustc_middle::traits::BuiltinImplSource; -use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams}; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::ty::{fast_reject, TypeFoldable}; -use rustc_middle::ty::{TypeVisitableExt, Upcast}; -use rustc_span::{ErrorGuaranteed, DUMMY_SP}; -use std::fmt::Debug; - pub(super) mod structural_traits; +use rustc_type_ir::fold::TypeFoldable; +use rustc_type_ir::inherent::*; +use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::visit::TypeVisitableExt as _; +use rustc_type_ir::{self as ty, Interner, Upcast as _}; +use tracing::{debug, instrument}; + +use crate::infcx::SolverDelegate; +use crate::solve::inspect::ProbeKind; +use crate::solve::{ + BuiltinImplSource, CandidateSource, CanonicalResponse, Certainty, EvalCtxt, Goal, GoalSource, + MaybeCause, NoSolution, QueryResult, SolverMode, +}; + /// A candidate is a possible way to prove a goal. /// /// It consists of both the `source`, which describes how that goal would be proven, /// and the `result` when using the given `source`. -#[derive(Debug, Clone)] -pub(super) struct Candidate<'tcx> { - pub(super) source: CandidateSource<'tcx>, - pub(super) result: CanonicalResponse<'tcx>, +#[derive(derivative::Derivative)] +#[derivative(Debug(bound = ""), Clone(bound = ""))] +pub(super) struct Candidate { + pub(super) source: CandidateSource, + pub(super) result: CanonicalResponse, } /// Methods used to assemble candidates for either trait or projection goals. -pub(super) trait GoalKind<'tcx>: - TypeFoldable> + Copy + Eq + std::fmt::Display +pub(super) trait GoalKind::Interner>: + TypeFoldable + Copy + Eq + std::fmt::Display +where + Infcx: SolverDelegate, + I: Interner, { - fn self_ty(self) -> Ty<'tcx>; + fn self_ty(self) -> I::Ty; - fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx>; + fn trait_ref(self, tcx: I) -> ty::TraitRef; - fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self; + fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self; - fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId; + fn trait_def_id(self, tcx: I) -> I::DefId; /// Try equating an assumption predicate against a goal's predicate. If it /// holds, then execute the `then` callback, which should do any additional /// work, then produce a response (typically by executing /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]). fn probe_and_match_goal_against_assumption( - ecx: &mut EvalCtxt<'_, 'tcx>, - source: CandidateSource<'tcx>, - goal: Goal<'tcx, Self>, - assumption: ty::Clause<'tcx>, - then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + source: CandidateSource, + goal: Goal, + assumption: I::Clause, + then: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> QueryResult, + ) -> Result, NoSolution>; /// Consider a clause, which consists of a "assumption" and some "requirements", /// to satisfy a goal. If the requirements hold, then attempt to satisfy our /// goal by equating it with the assumption. fn probe_and_consider_implied_clause( - ecx: &mut EvalCtxt<'_, 'tcx>, - parent_source: CandidateSource<'tcx>, - goal: Goal<'tcx, Self>, - assumption: ty::Clause<'tcx>, - requirements: impl IntoIterator>)>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + parent_source: CandidateSource, + goal: Goal, + assumption: I::Clause, + requirements: impl IntoIterator)>, + ) -> Result, NoSolution> { Self::probe_and_match_goal_against_assumption(ecx, parent_source, goal, assumption, |ecx| { for (nested_source, goal) in requirements { ecx.add_goal(nested_source, goal); @@ -75,15 +76,15 @@ pub(super) trait GoalKind<'tcx>: /// additionally checking all of the supertraits and object bounds to hold, /// since they're not implied by the well-formedness of the object type. fn probe_and_consider_object_bound_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - source: CandidateSource<'tcx>, - goal: Goal<'tcx, Self>, - assumption: ty::Clause<'tcx>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + source: CandidateSource, + goal: Goal, + assumption: I::Clause, + ) -> Result, NoSolution> { Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| { - let tcx = ecx.tcx(); - let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else { - bug!("expected object type in `probe_and_consider_object_bound_candidate`"); + let tcx = ecx.interner(); + let ty::Dynamic(bounds, _, _) = goal.predicate.self_ty().kind() else { + panic!("expected object type in `probe_and_consider_object_bound_candidate`"); }; ecx.add_goals( GoalSource::ImplWhereBound, @@ -99,10 +100,10 @@ pub(super) trait GoalKind<'tcx>: } fn consider_impl_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - impl_def_id: DefId, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + impl_def_id: I::DefId, + ) -> Result, NoSolution>; /// If the predicate contained an error, we want to avoid emitting unnecessary trait /// errors but still want to emit errors for other trait goals. We have some special @@ -111,85 +112,85 @@ pub(super) trait GoalKind<'tcx>: /// Trait goals always hold while projection goals never do. This is a bit arbitrary /// but prevents incorrect normalization while hiding any trait errors. fn consider_error_guaranteed_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - guar: ErrorGuaranteed, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + guar: I::ErrorGuaranteed, + ) -> Result, NoSolution>; /// A type implements an `auto trait` if its components do as well. /// /// These components are given by built-in rules from /// [`structural_traits::instantiate_constituent_tys_for_auto_trait`]. fn consider_auto_trait_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; /// A trait alias holds if the RHS traits and `where` clauses hold. fn consider_trait_alias_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; /// A type is `Sized` if its tail component is `Sized`. /// /// These components are given by built-in rules from /// [`structural_traits::instantiate_constituent_tys_for_sized_trait`]. fn consider_builtin_sized_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; /// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`. /// /// These components are given by built-in rules from /// [`structural_traits::instantiate_constituent_tys_for_copy_clone_trait`]. fn consider_builtin_copy_clone_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; /// A type is `PointerLike` if we can compute its layout, and that layout /// matches the layout of `usize`. fn consider_builtin_pointer_like_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; /// A type is a `FnPtr` if it is of `FnPtr` type. fn consider_builtin_fn_ptr_trait_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; /// A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn` /// family of traits where `A` is given by the signature of the type. fn consider_builtin_fn_trait_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, kind: ty::ClosureKind, - ) -> Result, NoSolution>; + ) -> Result, NoSolution>; /// An async closure is known to implement the `AsyncFn` family of traits /// where `A` is given by the signature of the type. fn consider_builtin_async_fn_trait_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, kind: ty::ClosureKind, - ) -> Result, NoSolution>; + ) -> Result, NoSolution>; /// Compute the built-in logic of the `AsyncFnKindHelper` helper trait, which /// is used internally to delay computation for async closures until after /// upvar analysis is performed in HIR typeck. fn consider_builtin_async_fn_kind_helper_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; /// `Tuple` is implemented if the `Self` type is a tuple. fn consider_builtin_tuple_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; /// `Pointee` is always implemented. /// @@ -197,65 +198,65 @@ pub(super) trait GoalKind<'tcx>: /// the built-in types. For structs, the metadata type is given by the struct /// tail. fn consider_builtin_pointee_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; /// A coroutine (that comes from an `async` desugaring) is known to implement /// `Future`, where `O` is given by the coroutine's return type /// that was computed during type-checking. fn consider_builtin_future_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; /// A coroutine (that comes from a `gen` desugaring) is known to implement /// `Iterator`, where `O` is given by the generator's yield type /// that was computed during type-checking. fn consider_builtin_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; /// A coroutine (that comes from a `gen` desugaring) is known to implement /// `FusedIterator` fn consider_builtin_fused_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; fn consider_builtin_async_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; /// A coroutine (that doesn't come from an `async` or `gen` desugaring) is known to /// implement `Coroutine`, given the resume, yield, /// and return types of the coroutine computed during type-checking. fn consider_builtin_coroutine_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; fn consider_builtin_discriminant_kind_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; fn consider_builtin_async_destruct_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; fn consider_builtin_destruct_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; fn consider_builtin_transmute_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution>; /// Consider (possibly several) candidates to upcast or unsize a type to another /// type, excluding the coercion of a sized type into a `dyn Trait`. @@ -265,16 +266,20 @@ pub(super) trait GoalKind<'tcx>: /// otherwise recompute this for codegen. This is a bit of a mess but the /// easiest way to maintain the existing behavior for now. fn consider_structural_builtin_unsize_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Vec>; + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Vec>; } -impl<'tcx> EvalCtxt<'_, 'tcx> { - pub(super) fn assemble_and_evaluate_candidates>( +impl EvalCtxt<'_, Infcx> +where + Infcx: SolverDelegate, + I: Interner, +{ + pub(super) fn assemble_and_evaluate_candidates>( &mut self, - goal: Goal<'tcx, G>, - ) -> Vec> { + goal: Goal, + ) -> Vec> { let Ok(normalized_self_ty) = self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty()) else { @@ -286,15 +291,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return self.forced_ambiguity(MaybeCause::Ambiguity).into_iter().collect(); } - let goal: Goal<'tcx, G> = - goal.with(self.tcx(), goal.predicate.with_self_ty(self.tcx(), normalized_self_ty)); + let goal: Goal = goal.with( + self.interner(), + goal.predicate.with_self_ty(self.interner(), normalized_self_ty), + ); // Vars that show up in the rest of the goal substs may have been constrained by // normalizing the self type as well, since type variables are not uniquified. let goal = self.resolve_vars_if_possible(goal); let mut candidates = vec![]; - self.assemble_non_blanket_impl_candidates(goal, &mut candidates); + self.assemble_impl_candidates(goal, &mut candidates); self.assemble_builtin_impl_candidates(goal, &mut candidates); @@ -302,8 +309,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.assemble_object_bound_candidates(goal, &mut candidates); - self.assemble_blanket_impl_candidates(goal, &mut candidates); - self.assemble_param_env_candidates(goal, &mut candidates); match self.solver_mode() { @@ -319,7 +324,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { pub(super) fn forced_ambiguity( &mut self, cause: MaybeCause, - ) -> Result, NoSolution> { + ) -> Result, NoSolution> { // This may fail if `try_evaluate_added_goals` overflows because it // fails to reach a fixpoint but ends up getting an error after // running for some additional step. @@ -332,152 +337,38 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } #[instrument(level = "trace", skip_all)] - fn assemble_non_blanket_impl_candidates>( + fn assemble_impl_candidates>( &mut self, - goal: Goal<'tcx, G>, - candidates: &mut Vec>, + goal: Goal, + candidates: &mut Vec>, ) { - let tcx = self.tcx(); - let self_ty = goal.predicate.self_ty(); - let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx)); - let mut consider_impls_for_simplified_type = |simp| { - if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) { - for &impl_def_id in impls_for_type { - // For every `default impl`, there's always a non-default `impl` - // that will *also* apply. There's no reason to register a candidate - // for this impl, since it is *not* proof that the trait goal holds. - if tcx.defaultness(impl_def_id).is_default() { - return; - } - - match G::consider_impl_candidate(self, goal, impl_def_id) { - Ok(candidate) => candidates.push(candidate), - Err(NoSolution) => (), - } + let tcx = self.interner(); + tcx.for_each_relevant_impl( + goal.predicate.trait_def_id(tcx), + goal.predicate.self_ty(), + |impl_def_id| { + // For every `default impl`, there's always a non-default `impl` + // that will *also* apply. There's no reason to register a candidate + // for this impl, since it is *not* proof that the trait goal holds. + if tcx.impl_is_default(impl_def_id) { + return; } - } - }; - match self_ty.kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Adt(_, _) - | ty::Foreign(_) - | ty::Str - | ty::Array(_, _) - | ty::Pat(_, _) - | ty::Slice(_) - | ty::RawPtr(_, _) - | ty::Ref(_, _, _) - | ty::FnDef(_, _) - | ty::FnPtr(_) - | ty::Dynamic(_, _, _) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(_, _) - | ty::Never - | ty::Tuple(_) => { - let simp = - fast_reject::simplify_type(tcx, self_ty, TreatParams::ForLookup).unwrap(); - consider_impls_for_simplified_type(simp); - } - - // HACK: For integer and float variables we have to manually look at all impls - // which have some integer or float as a self type. - ty::Infer(ty::IntVar(_)) => { - use ty::IntTy::*; - use ty::UintTy::*; - // This causes a compiler error if any new integer kinds are added. - let (I8 | I16 | I32 | I64 | I128 | Isize): ty::IntTy; - let (U8 | U16 | U32 | U64 | U128 | Usize): ty::UintTy; - let possible_integers = [ - // signed integers - SimplifiedType::Int(I8), - SimplifiedType::Int(I16), - SimplifiedType::Int(I32), - SimplifiedType::Int(I64), - SimplifiedType::Int(I128), - SimplifiedType::Int(Isize), - // unsigned integers - SimplifiedType::Uint(U8), - SimplifiedType::Uint(U16), - SimplifiedType::Uint(U32), - SimplifiedType::Uint(U64), - SimplifiedType::Uint(U128), - SimplifiedType::Uint(Usize), - ]; - for simp in possible_integers { - consider_impls_for_simplified_type(simp); + match G::consider_impl_candidate(self, goal, impl_def_id) { + Ok(candidate) => candidates.push(candidate), + Err(NoSolution) => (), } - } - - ty::Infer(ty::FloatVar(_)) => { - // This causes a compiler error if any new float kinds are added. - let (ty::FloatTy::F16 | ty::FloatTy::F32 | ty::FloatTy::F64 | ty::FloatTy::F128); - let possible_floats = [ - SimplifiedType::Float(ty::FloatTy::F16), - SimplifiedType::Float(ty::FloatTy::F32), - SimplifiedType::Float(ty::FloatTy::F64), - SimplifiedType::Float(ty::FloatTy::F128), - ]; - - for simp in possible_floats { - consider_impls_for_simplified_type(simp); - } - } - - // The only traits applying to aliases and placeholders are blanket impls. - // - // Impls which apply to an alias after normalization are handled by - // `assemble_candidates_after_normalizing_self_ty`. - ty::Alias(_, _) | ty::Placeholder(..) | ty::Error(_) => (), - - // FIXME: These should ideally not exist as a self type. It would be nice for - // the builtin auto trait impls of coroutines to instead directly recurse - // into the witness. - ty::CoroutineWitness(..) => (), - - // These variants should not exist as a self type. - ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) - | ty::Param(_) - | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"), - } + }, + ); } #[instrument(level = "trace", skip_all)] - fn assemble_blanket_impl_candidates>( + fn assemble_builtin_impl_candidates>( &mut self, - goal: Goal<'tcx, G>, - candidates: &mut Vec>, + goal: Goal, + candidates: &mut Vec>, ) { - let tcx = self.tcx(); - let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx)); - for &impl_def_id in trait_impls.blanket_impls() { - // For every `default impl`, there's always a non-default `impl` - // that will *also* apply. There's no reason to register a candidate - // for this impl, since it is *not* proof that the trait goal holds. - if tcx.defaultness(impl_def_id).is_default() { - return; - } - - match G::consider_impl_candidate(self, goal, impl_def_id) { - Ok(candidate) => candidates.push(candidate), - Err(NoSolution) => (), - } - } - } - - #[instrument(level = "trace", skip_all)] - fn assemble_builtin_impl_candidates>( - &mut self, - goal: Goal<'tcx, G>, - candidates: &mut Vec>, - ) { - let tcx = self.tcx(); - let lang_items = tcx.lang_items(); + let tcx = self.interner(); let trait_def_id = goal.predicate.trait_def_id(tcx); // N.B. When assembling built-in candidates for lang items that are also @@ -493,43 +384,43 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_auto_trait_candidate(self, goal) } else if tcx.trait_is_alias(trait_def_id) { G::consider_trait_alias_candidate(self, goal) - } else if lang_items.sized_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Sized) { G::consider_builtin_sized_candidate(self, goal) - } else if lang_items.copy_trait() == Some(trait_def_id) - || lang_items.clone_trait() == Some(trait_def_id) + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Copy) + || tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Clone) { G::consider_builtin_copy_clone_candidate(self, goal) - } else if lang_items.pointer_like() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::PointerLike) { G::consider_builtin_pointer_like_candidate(self, goal) - } else if lang_items.fn_ptr_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::FnPtrTrait) { G::consider_builtin_fn_ptr_trait_candidate(self, goal) - } else if let Some(kind) = self.tcx().fn_trait_kind_from_def_id(trait_def_id) { + } else if let Some(kind) = self.interner().fn_trait_kind_from_def_id(trait_def_id) { G::consider_builtin_fn_trait_candidates(self, goal, kind) - } else if let Some(kind) = self.tcx().async_fn_trait_kind_from_def_id(trait_def_id) { + } else if let Some(kind) = self.interner().async_fn_trait_kind_from_def_id(trait_def_id) { G::consider_builtin_async_fn_trait_candidates(self, goal, kind) - } else if lang_items.async_fn_kind_helper() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncFnKindHelper) { G::consider_builtin_async_fn_kind_helper_candidate(self, goal) - } else if lang_items.tuple_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Tuple) { G::consider_builtin_tuple_candidate(self, goal) - } else if lang_items.pointee_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::PointeeTrait) { G::consider_builtin_pointee_candidate(self, goal) - } else if lang_items.future_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Future) { G::consider_builtin_future_candidate(self, goal) - } else if lang_items.iterator_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Iterator) { G::consider_builtin_iterator_candidate(self, goal) - } else if lang_items.fused_iterator_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::FusedIterator) { G::consider_builtin_fused_iterator_candidate(self, goal) - } else if lang_items.async_iterator_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncIterator) { G::consider_builtin_async_iterator_candidate(self, goal) - } else if lang_items.coroutine_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Coroutine) { G::consider_builtin_coroutine_candidate(self, goal) - } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::DiscriminantKind) { G::consider_builtin_discriminant_kind_candidate(self, goal) - } else if lang_items.async_destruct_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncDestruct) { G::consider_builtin_async_destruct_candidate(self, goal) - } else if lang_items.destruct_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Destruct) { G::consider_builtin_destruct_candidate(self, goal) - } else if lang_items.transmute_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::TransmuteTrait) { G::consider_builtin_transmute_candidate(self, goal) } else { Err(NoSolution) @@ -539,18 +430,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // There may be multiple unsize candidates for a trait with several supertraits: // `trait Foo: Bar + Bar` and `dyn Foo: Unsize>` - if lang_items.unsize_trait() == Some(trait_def_id) { + if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Unsize) { candidates.extend(G::consider_structural_builtin_unsize_candidates(self, goal)); } } #[instrument(level = "trace", skip_all)] - fn assemble_param_env_candidates>( + fn assemble_param_env_candidates>( &mut self, - goal: Goal<'tcx, G>, - candidates: &mut Vec>, + goal: Goal, + candidates: &mut Vec>, ) { - for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() { + for (i, assumption) in goal.param_env.caller_bounds().into_iter().enumerate() { candidates.extend(G::probe_and_consider_implied_clause( self, CandidateSource::ParamEnv(i), @@ -562,10 +453,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } #[instrument(level = "trace", skip_all)] - fn assemble_alias_bound_candidates>( + fn assemble_alias_bound_candidates>( &mut self, - goal: Goal<'tcx, G>, - candidates: &mut Vec>, + goal: Goal, + candidates: &mut Vec>, ) { let () = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| { ecx.assemble_alias_bound_candidates_recur(goal.predicate.self_ty(), goal, candidates); @@ -581,13 +472,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// If so, continue searching by recursively calling after normalization. // FIXME: This may recurse infinitely, but I can't seem to trigger it without // hitting another overflow error something. Add a depth parameter needed later. - fn assemble_alias_bound_candidates_recur>( + fn assemble_alias_bound_candidates_recur>( &mut self, - self_ty: Ty<'tcx>, - goal: Goal<'tcx, G>, - candidates: &mut Vec>, + self_ty: I::Ty, + goal: Goal, + candidates: &mut Vec>, ) { - let (kind, alias_ty) = match *self_ty.kind() { + let (kind, alias_ty) = match self_ty.kind() { ty::Bool | ty::Char | ty::Int(_) @@ -615,7 +506,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Error(_) => return, ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) | ty::Bound(..) => { - bug!("unexpected self type for `{goal:?}`") + panic!("unexpected self type for `{goal:?}`") } ty::Infer(ty::TyVar(_)) => { @@ -632,16 +523,15 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) => (kind, alias_ty), ty::Alias(ty::Inherent | ty::Weak, _) => { - self.tcx().sess.dcx().span_delayed_bug( - DUMMY_SP, - format!("could not normalize {self_ty}, it is not WF"), - ); + self.interner().delay_bug(format!("could not normalize {self_ty:?}, it is not WF")); return; } }; - for assumption in - self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args) + for assumption in self + .interner() + .item_bounds(alias_ty.def_id) + .iter_instantiated(self.interner(), &alias_ty.args) { candidates.extend(G::probe_and_consider_implied_clause( self, @@ -666,18 +556,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } #[instrument(level = "trace", skip_all)] - fn assemble_object_bound_candidates>( + fn assemble_object_bound_candidates>( &mut self, - goal: Goal<'tcx, G>, - candidates: &mut Vec>, + goal: Goal, + candidates: &mut Vec>, ) { - let tcx = self.tcx(); - if !tcx.trait_def(goal.predicate.trait_def_id(tcx)).implement_via_object { + let tcx = self.interner(); + if !tcx.trait_may_be_implemented_via_object(goal.predicate.trait_def_id(tcx)) { return; } let self_ty = goal.predicate.self_ty(); - let bounds = match *self_ty.kind() { + let bounds = match self_ty.kind() { ty::Bool | ty::Char | ty::Int(_) @@ -705,12 +595,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Error(_) => return, ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) - | ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"), + | ty::Bound(..) => panic!("unexpected self type for `{goal:?}`"), ty::Dynamic(bounds, ..) => bounds, }; // Do not consider built-in object impls for non-object-safe types. - if bounds.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) { + if bounds.principal_def_id().is_some_and(|def_id| !tcx.trait_is_object_safe(def_id)) { return; } @@ -739,14 +629,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // a projection goal. if let Some(principal) = bounds.principal() { let principal_trait_ref = principal.with_self_ty(tcx, self_ty); - self.walk_vtable(principal_trait_ref, |ecx, assumption, vtable_base, _| { + for (idx, assumption) in + Infcx::elaborate_supertraits(tcx, principal_trait_ref).enumerate() + { candidates.extend(G::probe_and_consider_object_bound_candidate( - ecx, - CandidateSource::BuiltinImpl(BuiltinImplSource::Object { vtable_base }), + self, + CandidateSource::BuiltinImpl(BuiltinImplSource::Object(idx)), goal, assumption.upcast(tcx), )); - }); + } } } @@ -757,12 +649,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// To do so we add an ambiguous candidate in case such an unknown impl could /// apply to the current goal. #[instrument(level = "trace", skip_all)] - fn assemble_coherence_unknowable_candidates>( + fn assemble_coherence_unknowable_candidates>( &mut self, - goal: Goal<'tcx, G>, - candidates: &mut Vec>, + goal: Goal, + candidates: &mut Vec>, ) { - let tcx = self.tcx(); + let tcx = self.interner(); candidates.extend(self.probe_trait_candidate(CandidateSource::CoherenceUnknowable).enter( |ecx| { @@ -786,13 +678,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // to improve this however. However, this should make it fairly straightforward to refine // the filtering going forward, so it seems alright-ish for now. #[instrument(level = "debug", skip(self, goal))] - fn discard_impls_shadowed_by_env>( + fn discard_impls_shadowed_by_env>( &mut self, - goal: Goal<'tcx, G>, - candidates: &mut Vec>, + goal: Goal, + candidates: &mut Vec>, ) { - let tcx = self.tcx(); - let trait_goal: Goal<'tcx, ty::TraitPredicate<'tcx>> = + let tcx = self.interner(); + let trait_goal: Goal> = goal.with(tcx, goal.predicate.trait_ref(tcx)); let mut trait_candidates_from_env = vec![]; @@ -817,7 +709,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { false } CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => true, - CandidateSource::CoherenceUnknowable => bug!("uh oh"), + CandidateSource::CoherenceUnknowable => panic!("uh oh"), }); } // If it is still ambiguous we instead just force the whole goal @@ -835,10 +727,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// to somehow try to merge the candidates into one. If that fails, we return /// ambiguity. #[instrument(level = "debug", skip(self), ret)] - pub(super) fn merge_candidates( - &mut self, - candidates: Vec>, - ) -> QueryResult<'tcx> { + pub(super) fn merge_candidates(&mut self, candidates: Vec>) -> QueryResult { // First try merging all candidates. This is complete and fully sound. let responses = candidates.iter().map(|c| c.result).collect::>(); if let Some(result) = self.try_merge_responses(&responses) { diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs similarity index 76% rename from compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs rename to compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index cf826596392c..202af76565a8 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -1,28 +1,30 @@ //! Code which is used by built-in goals that match "structurally", such a auto //! traits, `Copy`/`Clone`. -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::LangItem; -use rustc_hir::{def_id::DefId, Movability, Mutability}; -use rustc_infer::traits::query::NoSolution; -use rustc_macros::{TypeFoldable, TypeVisitable}; -use rustc_middle::bug; -use rustc_middle::traits::solve::Goal; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, Upcast}; -use rustc_span::sym; -use crate::solve::EvalCtxt; +use rustc_ast_ir::{Movability, Mutability}; +use rustc_data_structures::fx::FxHashMap; +use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_type_ir::inherent::*; +use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::{self as ty, Interner, Upcast as _}; +use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; +use tracing::instrument; + +use crate::infcx::SolverDelegate; +use crate::solve::{EvalCtxt, Goal, NoSolution}; // Calculates the constituent types of a type for `auto trait` purposes. -// -// For types with an "existential" binder, i.e. coroutine witnesses, we also -// instantiate the binder with placeholders eagerly. #[instrument(level = "trace", skip(ecx), ret)] -pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( - ecx: &EvalCtxt<'_, 'tcx>, - ty: Ty<'tcx>, -) -> Result>>, NoSolution> { - let tcx = ecx.tcx(); - match *ty.kind() { +pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait( + ecx: &EvalCtxt<'_, Infcx>, + ty: I::Ty, +) -> Result>, NoSolution> +where + Infcx: SolverDelegate, + I: Interner, +{ + let tcx = ecx.interner(); + match ty.kind() { ty::Uint(_) | ty::Int(_) | ty::Bool @@ -34,7 +36,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( | ty::Char => Ok(vec![]), // Treat `str` like it's defined as `struct str([u8]);` - ty::Str => Ok(vec![ty::Binder::dummy(Ty::new_slice(tcx, tcx.types.u8))]), + ty::Str => Ok(vec![ty::Binder::dummy(Ty::new_slice(tcx, Ty::new_u8(tcx)))]), ty::Dynamic(..) | ty::Param(..) @@ -43,7 +45,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( | ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => { - bug!("unexpected type `{ty}`") + panic!("unexpected type `{ty:?}`") } ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => { @@ -56,7 +58,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( ty::Tuple(tys) => { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet - Ok(tys.iter().map(ty::Binder::dummy).collect()) + Ok(tys.into_iter().map(ty::Binder::dummy).collect()) } ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]), @@ -74,33 +76,40 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( } ty::CoroutineWitness(def_id, args) => Ok(ecx - .tcx() + .interner() .bound_coroutine_hidden_types(def_id) - .map(|bty| bty.instantiate(tcx, args)) + .into_iter() + .map(|bty| bty.instantiate(tcx, &args)) .collect()), // For `PhantomData`, we pass `T`. ty::Adt(def, args) if def.is_phantom_data() => Ok(vec![ty::Binder::dummy(args.type_at(0))]), - ty::Adt(def, args) => { - Ok(def.all_fields().map(|f| ty::Binder::dummy(f.ty(tcx, args))).collect()) - } + ty::Adt(def, args) => Ok(def + .all_field_tys(tcx) + .iter_instantiated(tcx, &args) + .map(ty::Binder::dummy) + .collect()), ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { // We can resolve the `impl Trait` to its concrete type, // which enforces a DAG between the functions requiring // the auto trait bounds in question. - Ok(vec![ty::Binder::dummy(tcx.type_of(def_id).instantiate(tcx, args))]) + Ok(vec![ty::Binder::dummy(tcx.type_of(def_id).instantiate(tcx, &args))]) } } } #[instrument(level = "trace", skip(ecx), ret)] -pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( - ecx: &EvalCtxt<'_, 'tcx>, - ty: Ty<'tcx>, -) -> Result>>, NoSolution> { - match *ty.kind() { +pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait( + ecx: &EvalCtxt<'_, Infcx>, + ty: I::Ty, +) -> Result>, NoSolution> +where + Infcx: SolverDelegate, + I: Interner, +{ + match ty.kind() { // impl Sized for u*, i*, bool, f*, FnDef, FnPtr, *(const/mut) T, char, &mut? T, [T; N], dyn* Trait, ! // impl Sized for Coroutine, CoroutineWitness, Closure, CoroutineClosure ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) @@ -133,7 +142,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("unexpected type `{ty}`") + panic!("unexpected type `{ty:?}`") } // impl Sized for () @@ -150,8 +159,8 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( // "best effort" optimization and `sized_constraint` may return `Some`, even // if the ADT is sized for all possible args. ty::Adt(def, args) => { - if let Some(sized_crit) = def.sized_constraint(ecx.tcx()) { - Ok(vec![ty::Binder::dummy(sized_crit.instantiate(ecx.tcx(), args))]) + if let Some(sized_crit) = def.sized_constraint(ecx.interner()) { + Ok(vec![ty::Binder::dummy(sized_crit.instantiate(ecx.interner(), &args))]) } else { Ok(vec![]) } @@ -160,11 +169,15 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( } #[instrument(level = "trace", skip(ecx), ret)] -pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( - ecx: &EvalCtxt<'_, 'tcx>, - ty: Ty<'tcx>, -) -> Result>>, NoSolution> { - match *ty.kind() { +pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait( + ecx: &EvalCtxt<'_, Infcx>, + ty: I::Ty, +) -> Result>, NoSolution> +where + Infcx: SolverDelegate, + I: Interner, +{ + match ty.kind() { // impl Copy/Clone for FnDef, FnPtr ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Ok(vec![]), @@ -196,11 +209,11 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("unexpected type `{ty}`") + panic!("unexpected type `{ty:?}`") } // impl Copy/Clone for (T1, T2, .., Tn) where T1: Copy/Clone, T2: Copy/Clone, .. Tn: Copy/Clone - ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()), + ty::Tuple(tys) => Ok(tys.into_iter().map(ty::Binder::dummy).collect()), // impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]), @@ -209,10 +222,10 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( // only when `coroutine_clone` is enabled and the coroutine is movable // impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses) - ty::Coroutine(def_id, args) => match ecx.tcx().coroutine_movability(def_id) { + ty::Coroutine(def_id, args) => match ecx.interner().coroutine_movability(def_id) { Movability::Static => Err(NoSolution), Movability::Movable => { - if ecx.tcx().features().coroutine_clone { + if ecx.interner().features().coroutine_clone() { let coroutine = args.as_coroutine(); Ok(vec![ ty::Binder::dummy(coroutine.tupled_upvars_ty()), @@ -226,29 +239,28 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( // impl Copy/Clone for CoroutineWitness where T: Copy/Clone forall T in coroutine_hidden_types ty::CoroutineWitness(def_id, args) => Ok(ecx - .tcx() + .interner() .bound_coroutine_hidden_types(def_id) - .map(|bty| bty.instantiate(ecx.tcx(), args)) + .into_iter() + .map(|bty| bty.instantiate(ecx.interner(), &args)) .collect()), } } // Returns a binder of the tupled inputs types and output type from a builtin callable type. -pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( - tcx: TyCtxt<'tcx>, - self_ty: Ty<'tcx>, +pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable( + tcx: I, + self_ty: I::Ty, goal_kind: ty::ClosureKind, -) -> Result, Ty<'tcx>)>>, NoSolution> { - match *self_ty.kind() { +) -> Result>, NoSolution> { + match self_ty.kind() { // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed. ty::FnDef(def_id, args) => { let sig = tcx.fn_sig(def_id); - if sig.skip_binder().is_fn_trait_compatible() - && tcx.codegen_fn_attrs(def_id).target_features.is_empty() - { + if sig.skip_binder().is_fn_trait_compatible() && !tcx.has_target_features(def_id) { Ok(Some( - sig.instantiate(tcx, args) - .map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output())), + sig.instantiate(tcx, &args) + .map_bound(|sig| (Ty::new_tup(tcx, &sig.inputs()), sig.output())), )) } else { Err(NoSolution) @@ -257,7 +269,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed. ty::FnPtr(sig) => { if sig.is_fn_trait_compatible() { - Ok(Some(sig.map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output())))) + Ok(Some(sig.map_bound(|sig| (Ty::new_tup(tcx, &sig.inputs()), sig.output())))) } else { Err(NoSolution) } @@ -299,14 +311,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( return Err(NoSolution); } - // If `Fn`/`FnMut`, we only implement this goal if we - // have no captures. - let no_borrows = match args.tupled_upvars_ty().kind() { - ty::Tuple(tys) => tys.is_empty(), - ty::Error(_) => false, - _ => bug!("tuple_fields called on non-tuple"), - }; - if closure_kind != ty::ClosureKind::FnOnce && !no_borrows { + // A coroutine-closure implements `FnOnce` *always*, since it may + // always be called once. It additionally implements `Fn`/`FnMut` + // only if it has no upvars referencing the closure-env lifetime, + // and if the closure kind permits it. + if closure_kind != ty::ClosureKind::FnOnce && args.has_self_borrows() { return Err(NoSolution); } @@ -314,7 +323,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( tcx, goal_kind, // No captures by ref, so this doesn't matter. - tcx.lifetimes.re_static, + Region::new_static(tcx), def_id, args, sig, @@ -329,7 +338,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( coroutine_closure_to_ambiguous_coroutine( tcx, goal_kind, // No captures by ref, so this doesn't matter. - tcx.lifetimes.re_static, + Region::new_static(tcx), def_id, args, sig, @@ -365,22 +374,24 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("unexpected type `{self_ty}`") + panic!("unexpected type `{self_ty:?}`") } } } /// Relevant types for an async callable, including its inputs, output, /// and the return type you get from awaiting the output. -#[derive(Copy, Clone, Debug, TypeVisitable, TypeFoldable)] -pub(in crate::solve) struct AsyncCallableRelevantTypes<'tcx> { - pub tupled_inputs_ty: Ty<'tcx>, +#[derive(derivative::Derivative)] +#[derivative(Clone(bound = ""), Copy(bound = ""), Debug(bound = ""))] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +pub(in crate::solve) struct AsyncCallableRelevantTypes { + pub tupled_inputs_ty: I::Ty, /// Type returned by calling the closure /// i.e. `f()`. - pub output_coroutine_ty: Ty<'tcx>, + pub output_coroutine_ty: I::Ty, /// Type returned by `await`ing the output /// i.e. `f().await`. - pub coroutine_return_ty: Ty<'tcx>, + pub coroutine_return_ty: I::Ty, } // Returns a binder of the tupled inputs types, output type, and coroutine type @@ -388,16 +399,13 @@ pub(in crate::solve) struct AsyncCallableRelevantTypes<'tcx> { // the coroutine-closure, emit an additional trait predicate for `AsyncFnKindHelper` // which enforces the closure is actually callable with the given trait. When we // know the kind already, we can short-circuit this check. -pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tcx>( - tcx: TyCtxt<'tcx>, - self_ty: Ty<'tcx>, +pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable( + tcx: I, + self_ty: I::Ty, goal_kind: ty::ClosureKind, - env_region: ty::Region<'tcx>, -) -> Result< - (ty::Binder<'tcx, AsyncCallableRelevantTypes<'tcx>>, Vec>), - NoSolution, -> { - match *self_ty.kind() { + env_region: I::Region, +) -> Result<(ty::Binder>, Vec), NoSolution> { + match self_ty.kind() { ty::CoroutineClosure(def_id, args) => { let args = args.as_coroutine_closure(); let kind_ty = args.kind_ty(); @@ -424,7 +432,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc nested.push( ty::TraitRef::new( tcx, - tcx.require_lang_item(LangItem::AsyncFnKindHelper, None), + tcx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper), [kind_ty, Ty::from_closure_kind(tcx, goal_kind)], ) .upcast(tcx), @@ -448,7 +456,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc ty::FnDef(..) | ty::FnPtr(..) => { let bound_sig = self_ty.fn_sig(tcx); let sig = bound_sig.skip_binder(); - let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None); + let future_trait_def_id = tcx.require_lang_item(TraitSolverLangItem::Future); // `FnDef` and `FnPtr` only implement `AsyncFn*` when their // return type implements `Future`. let nested = vec![ @@ -456,16 +464,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc .rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()])) .upcast(tcx), ]; - let future_output_def_id = tcx - .associated_items(future_trait_def_id) - .filter_by_name_unhygienic(sym::Output) - .next() - .unwrap() - .def_id; + let future_output_def_id = tcx.require_lang_item(TraitSolverLangItem::FutureOutput); let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]); Ok(( bound_sig.rebind(AsyncCallableRelevantTypes { - tupled_inputs_ty: Ty::new_tup(tcx, sig.inputs()), + tupled_inputs_ty: Ty::new_tup(tcx, &sig.inputs()), output_coroutine_ty: sig.output(), coroutine_return_ty: future_output_ty, }), @@ -476,7 +479,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc let args = args.as_closure(); let bound_sig = args.sig(); let sig = bound_sig.skip_binder(); - let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None); + let future_trait_def_id = tcx.require_lang_item(TraitSolverLangItem::Future); // `Closure`s only implement `AsyncFn*` when their return type // implements `Future`. let mut nested = vec![ @@ -494,7 +497,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc } } else { let async_fn_kind_trait_def_id = - tcx.require_lang_item(LangItem::AsyncFnKindHelper, None); + tcx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper); // When we don't know the closure kind (and therefore also the closure's upvars, // which are computed at the same time), we must delay the computation of the // generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait @@ -512,12 +515,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc ); } - let future_output_def_id = tcx - .associated_items(future_trait_def_id) - .filter_by_name_unhygienic(sym::Output) - .next() - .unwrap() - .def_id; + let future_output_def_id = tcx.require_lang_item(TraitSolverLangItem::FutureOutput); let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]); Ok(( bound_sig.rebind(AsyncCallableRelevantTypes { @@ -555,21 +553,21 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("unexpected type `{self_ty}`") + panic!("unexpected type `{self_ty:?}`") } } } /// Given a coroutine-closure, project to its returned coroutine when we are *certain* /// that the closure's kind is compatible with the goal. -fn coroutine_closure_to_certain_coroutine<'tcx>( - tcx: TyCtxt<'tcx>, +fn coroutine_closure_to_certain_coroutine( + tcx: I, goal_kind: ty::ClosureKind, - goal_region: ty::Region<'tcx>, - def_id: DefId, - args: ty::CoroutineClosureArgs<'tcx>, - sig: ty::CoroutineClosureSignature<'tcx>, -) -> Ty<'tcx> { + goal_region: I::Region, + def_id: I::DefId, + args: ty::CoroutineClosureArgs, + sig: ty::CoroutineClosureSignature, +) -> I::Ty { sig.to_coroutine_given_kind_and_upvars( tcx, args.parent_args(), @@ -586,26 +584,20 @@ fn coroutine_closure_to_certain_coroutine<'tcx>( /// yet what the closure's upvars are. /// /// Note that we do not also push a `AsyncFnKindHelper` goal here. -fn coroutine_closure_to_ambiguous_coroutine<'tcx>( - tcx: TyCtxt<'tcx>, +fn coroutine_closure_to_ambiguous_coroutine( + tcx: I, goal_kind: ty::ClosureKind, - goal_region: ty::Region<'tcx>, - def_id: DefId, - args: ty::CoroutineClosureArgs<'tcx>, - sig: ty::CoroutineClosureSignature<'tcx>, -) -> Ty<'tcx> { - let async_fn_kind_trait_def_id = tcx.require_lang_item(LangItem::AsyncFnKindHelper, None); - let upvars_projection_def_id = tcx - .associated_items(async_fn_kind_trait_def_id) - .filter_by_name_unhygienic(sym::Upvars) - .next() - .unwrap() - .def_id; + goal_region: I::Region, + def_id: I::DefId, + args: ty::CoroutineClosureArgs, + sig: ty::CoroutineClosureSignature, +) -> I::Ty { + let upvars_projection_def_id = tcx.require_lang_item(TraitSolverLangItem::AsyncFnKindUpvars); let tupled_upvars_ty = Ty::new_projection( tcx, upvars_projection_def_id, [ - ty::GenericArg::from(args.kind_ty()), + I::GenericArg::from(args.kind_ty()), Ty::from_closure_kind(tcx, goal_kind).into(), goal_region.into(), sig.tupled_inputs_ty.into(), @@ -662,41 +654,44 @@ fn coroutine_closure_to_ambiguous_coroutine<'tcx>( // This is unsound in general and once that is fixed, we don't need to // normalize eagerly here. See https://github.com/lcnr/solver-woes/issues/9 // for more details. -pub(in crate::solve) fn predicates_for_object_candidate<'tcx>( - ecx: &EvalCtxt<'_, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - object_bound: &'tcx ty::List>, -) -> Vec>> { - let tcx = ecx.tcx(); +pub(in crate::solve) fn predicates_for_object_candidate( + ecx: &EvalCtxt<'_, Infcx>, + param_env: I::ParamEnv, + trait_ref: ty::TraitRef, + object_bounds: I::BoundExistentialPredicates, +) -> Vec> +where + Infcx: SolverDelegate, + I: Interner, +{ + let tcx = ecx.interner(); let mut requirements = vec![]; - requirements.extend( - tcx.super_predicates_of(trait_ref.def_id).instantiate(tcx, trait_ref.args).predicates, - ); - for item in tcx.associated_items(trait_ref.def_id).in_definition_order() { - // FIXME(associated_const_equality): Also add associated consts to - // the requirements here. - if item.kind == ty::AssocKind::Type { - // associated types that require `Self: Sized` do not show up in the built-in - // implementation of `Trait for dyn Trait`, and can be dropped here. - if tcx.generics_require_sized_self(item.def_id) { - continue; - } + requirements + .extend(tcx.super_predicates_of(trait_ref.def_id).iter_instantiated(tcx, &trait_ref.args)); - requirements - .extend(tcx.item_bounds(item.def_id).iter_instantiated(tcx, trait_ref.args)); + // FIXME(associated_const_equality): Also add associated consts to + // the requirements here. + for associated_type_def_id in tcx.associated_type_def_ids(trait_ref.def_id) { + // associated types that require `Self: Sized` do not show up in the built-in + // implementation of `Trait for dyn Trait`, and can be dropped here. + if tcx.generics_require_sized_self(associated_type_def_id) { + continue; } + + requirements.extend( + tcx.item_bounds(associated_type_def_id).iter_instantiated(tcx, &trait_ref.args), + ); } let mut replace_projection_with = FxHashMap::default(); - for bound in object_bound { + for bound in object_bounds { if let ty::ExistentialPredicate::Projection(proj) = bound.skip_binder() { let proj = proj.with_self_ty(tcx, trait_ref.self_ty()); let old_ty = replace_projection_with.insert(proj.def_id(), bound.rebind(proj)); assert_eq!( old_ty, None, - "{} has two generic parameters: {} and {}", + "{:?} has two generic parameters: {:?} and {:?}", proj.projection_term, proj.term, old_ty.unwrap() @@ -715,20 +710,22 @@ pub(in crate::solve) fn predicates_for_object_candidate<'tcx>( .collect() } -struct ReplaceProjectionWith<'a, 'tcx> { - ecx: &'a EvalCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - mapping: FxHashMap>, - nested: Vec>>, +struct ReplaceProjectionWith<'a, Infcx: SolverDelegate, I: Interner> { + ecx: &'a EvalCtxt<'a, Infcx>, + param_env: I::ParamEnv, + mapping: FxHashMap>>, + nested: Vec>, } -impl<'tcx> TypeFolder> for ReplaceProjectionWith<'_, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.ecx.tcx() +impl, I: Interner> TypeFolder + for ReplaceProjectionWith<'_, Infcx, I> +{ + fn interner(&self) -> I { + self.ecx.interner() } - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Alias(ty::Projection, alias_ty) = *ty.kind() + fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + if let ty::Alias(ty::Projection, alias_ty) = ty.kind() && let Some(replacement) = self.mapping.get(&alias_ty.def_id) { // We may have a case where our object type's projection bound is higher-ranked, @@ -741,11 +738,11 @@ impl<'tcx> TypeFolder> for ReplaceProjectionWith<'_, 'tcx> { .eq_and_get_goals( self.param_env, alias_ty, - proj.projection_term.expect_ty(self.ecx.tcx()), + proj.projection_term.expect_ty(self.ecx.interner()), ) .expect("expected to be able to unify goal projection with dyn's projection"), ); - proj.term.ty().unwrap() + proj.term.expect_ty() } else { ty.super_fold_with(self) } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs similarity index 59% rename from compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs rename to compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 52deb22098f4..c6611285a3be 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -8,57 +8,53 @@ //! section of the [rustc-dev-guide][c]. //! //! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html -use super::{CanonicalInput, Certainty, EvalCtxt, Goal}; -use crate::solve::eval_ctxt::NestedGoals; -use crate::solve::{ - inspect, response_no_constraints_raw, CanonicalResponse, QueryResult, Response, -}; -use rustc_data_structures::fx::FxHashSet; -use rustc_index::IndexVec; -use rustc_infer::infer::canonical::query_response::make_query_region_constraints; -use rustc_infer::infer::canonical::CanonicalVarValues; -use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; -use rustc_infer::infer::resolve::EagerResolver; -use rustc_infer::infer::RegionVariableOrigin; -use rustc_infer::infer::{InferCtxt, InferOk}; -use rustc_infer::traits::solve::NestedNormalizationGoals; -use rustc_middle::bug; -use rustc_middle::infer::canonical::Canonical; -use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::solve::{ - ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput, -}; -use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable}; -use rustc_next_trait_solver::canonicalizer::{CanonicalizeMode, Canonicalizer}; -use rustc_span::{Span, DUMMY_SP}; -use std::assert_matches::assert_matches; + use std::iter; -use std::ops::Deref; -trait ResponseT<'tcx> { - fn var_values(&self) -> CanonicalVarValues<'tcx>; +use rustc_index::IndexVec; +use rustc_type_ir::fold::TypeFoldable; +use rustc_type_ir::inherent::*; +use rustc_type_ir::{self as ty, Canonical, CanonicalVarValues, Interner}; +use tracing::{instrument, trace}; + +use crate::canonicalizer::{CanonicalizeMode, Canonicalizer}; +use crate::infcx::SolverDelegate; +use crate::resolve::EagerResolver; +use crate::solve::eval_ctxt::NestedGoals; +use crate::solve::inspect; +use crate::solve::{ + response_no_constraints_raw, CanonicalInput, CanonicalResponse, Certainty, EvalCtxt, + ExternalConstraintsData, Goal, MaybeCause, NestedNormalizationGoals, NoSolution, + PredefinedOpaquesData, QueryInput, QueryResult, Response, +}; + +trait ResponseT { + fn var_values(&self) -> CanonicalVarValues; } -impl<'tcx> ResponseT<'tcx> for Response> { - fn var_values(&self) -> CanonicalVarValues<'tcx> { +impl ResponseT for Response { + fn var_values(&self) -> CanonicalVarValues { self.var_values } } -impl<'tcx, T> ResponseT<'tcx> for inspect::State, T> { - fn var_values(&self) -> CanonicalVarValues<'tcx> { +impl ResponseT for inspect::State { + fn var_values(&self) -> CanonicalVarValues { self.var_values } } -impl<'tcx> EvalCtxt<'_, 'tcx> { +impl EvalCtxt<'_, Infcx> +where + Infcx: SolverDelegate, + I: Interner, +{ /// Canonicalizes the goal remembering the original values /// for each bound variable. - pub(super) fn canonicalize_goal>>( + pub(super) fn canonicalize_goal>( &self, - goal: Goal<'tcx, T>, - ) -> (Vec>, CanonicalInput<'tcx, T>) { + goal: Goal, + ) -> (Vec, CanonicalInput) { let opaque_types = self.infcx.clone_opaque_types_for_query_response(); let (goal, opaque_types) = (goal, opaque_types).fold_with(&mut EagerResolver::new(self.infcx)); @@ -71,7 +67,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { QueryInput { goal, predefined_opaques_in_body: self - .tcx() + .interner() .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), }, ); @@ -88,7 +84,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response( &mut self, certainty: Certainty, - ) -> QueryResult<'tcx> { + ) -> QueryResult { self.inspect.make_canonical_response(certainty); let goals_certainty = self.try_evaluate_added_goals()?; @@ -99,6 +95,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { previous call to `try_evaluate_added_goals!`" ); + // We only check for leaks from universes which were entered inside + // of the query. + self.infcx.leak_check(self.max_input_universe).map_err(|NoSolution| { + trace!("failed the leak check"); + NoSolution + })?; + // When normalizing, we've replaced the expected term with an unconstrained // inference variable. This means that we dropped information which could // have been important. We handle this by instead returning the nested goals @@ -111,7 +114,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if cfg!(debug_assertions) { assert!(normalizes_to_goals.is_empty()); if goals.is_empty() { - assert_matches!(goals_certainty, Certainty::Yes); + assert!(matches!(goals_certainty, Certainty::Yes)); } } (certainty, NestedNormalizationGoals(goals)) @@ -121,14 +124,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }; let external_constraints = - self.compute_external_query_constraints(normalization_nested_goals)?; + self.compute_external_query_constraints(certainty, normalization_nested_goals); let (var_values, mut external_constraints) = (self.var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx)); // Remove any trivial region constraints once we've resolved regions external_constraints .region_constraints - .outlives - .retain(|(outlives, _)| outlives.0.as_region().map_or(true, |re| re != outlives.1)); + .retain(|outlives| outlives.0.as_region().map_or(true, |re| re != outlives.1)); let canonical = Canonicalizer::canonicalize( self.infcx, @@ -137,7 +139,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Response { var_values, certainty, - external_constraints: self.tcx().mk_external_constraints(external_constraints), + external_constraints: self.interner().mk_external_constraints(external_constraints), }, ); @@ -151,9 +153,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { pub(in crate::solve) fn make_ambiguous_response_no_constraints( &self, maybe_cause: MaybeCause, - ) -> CanonicalResponse<'tcx> { + ) -> CanonicalResponse { response_no_constraints_raw( - self.tcx(), + self.interner(), self.max_input_universe, self.variables, Certainty::Maybe(maybe_cause), @@ -170,38 +172,36 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "trace", skip(self), ret)] fn compute_external_query_constraints( &self, - normalization_nested_goals: NestedNormalizationGoals<'tcx>, - ) -> Result, NoSolution> { - // We only check for leaks from universes which were entered inside - // of the query. - self.infcx.leak_check(self.max_input_universe, None).map_err(|e| { - trace!(?e, "failed the leak check"); - NoSolution - })?; + certainty: Certainty, + normalization_nested_goals: NestedNormalizationGoals, + ) -> ExternalConstraintsData { + // We only return region constraints once the certainty is `Yes`. This + // is necessary as we may drop nested goals on ambiguity, which may result + // in unconstrained inference variables in the region constraints. It also + // prevents us from emitting duplicate region constraints, avoiding some + // unnecessary work. This slightly weakens the leak check in case it uses + // region constraints from an ambiguous nested goal. This is tested in both + // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and + // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`. + let region_constraints = if certainty == Certainty::Yes { + self.infcx.make_deduplicated_outlives_constraints() + } else { + Default::default() + }; - // Cannot use `take_registered_region_obligations` as we may compute the response - // inside of a `probe` whenever we have multiple choices inside of the solver. - let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned(); - let mut region_constraints = self.infcx.with_region_constraints(|region_constraints| { - make_query_region_constraints( - self.tcx(), - region_obligations - .iter() - .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())), - region_constraints, - ) - }); - - let mut seen = FxHashSet::default(); - region_constraints.outlives.retain(|outlives| seen.insert(*outlives)); - - let mut opaque_types = self.infcx.clone_opaque_types_for_query_response(); - // Only return opaque type keys for newly-defined opaques - opaque_types.retain(|(a, _)| { - self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a) - }); - - Ok(ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals }) + ExternalConstraintsData { + region_constraints, + opaque_types: self + .infcx + .clone_opaque_types_for_query_response() + .into_iter() + // Only return *newly defined* opaque types. + .filter(|(a, _)| { + self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a) + }) + .collect(), + normalization_nested_goals, + } } /// After calling a canonical query, we apply the constraints returned @@ -214,10 +214,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// the `normalization_nested_goals` pub(super) fn instantiate_and_apply_query_response( &mut self, - param_env: ty::ParamEnv<'tcx>, - original_values: Vec>, - response: CanonicalResponse<'tcx>, - ) -> (NestedNormalizationGoals<'tcx>, Certainty) { + param_env: I::ParamEnv, + original_values: Vec, + response: CanonicalResponse, + ) -> (NestedNormalizationGoals, Certainty) { let instantiation = Self::compute_query_response_instantiation_values( self.infcx, &original_values, @@ -225,7 +225,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ); let Response { var_values, external_constraints, certainty } = - response.instantiate(self.tcx(), &instantiation); + self.infcx.instantiate_canonical(response, instantiation); Self::unify_query_var_values(self.infcx, param_env, &original_values, var_values); @@ -233,7 +233,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { region_constraints, opaque_types, normalization_nested_goals, - } = external_constraints.deref(); + } = &*external_constraints; self.register_region_constraints(region_constraints); self.register_new_opaque_types(opaque_types); (normalization_nested_goals.clone(), certainty) @@ -242,11 +242,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// This returns the canoncial variable values to instantiate the bound variables of /// the canonical response. This depends on the `original_values` for the /// bound variables. - fn compute_query_response_instantiation_values>( - infcx: &InferCtxt<'tcx>, - original_values: &[ty::GenericArg<'tcx>], - response: &Canonical<'tcx, T>, - ) -> CanonicalVarValues<'tcx> { + fn compute_query_response_instantiation_values>( + infcx: &Infcx, + original_values: &[I::GenericArg], + response: &Canonical, + ) -> CanonicalVarValues { // FIXME: Longterm canonical queries should deal with all placeholders // created inside of the query directly instead of returning them to the // caller. @@ -268,35 +268,35 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // inference variable of the input right away, which is more performant. let mut opt_values = IndexVec::from_elem_n(None, response.variables.len()); for (original_value, result_value) in iter::zip(original_values, var_values.var_values) { - match result_value.unpack() { - GenericArgKind::Type(t) => { - if let &ty::Bound(debruijn, b) = t.kind() { + match result_value.kind() { + ty::GenericArgKind::Type(t) => { + if let ty::Bound(debruijn, b) = t.kind() { assert_eq!(debruijn, ty::INNERMOST); - opt_values[b.var] = Some(*original_value); + opt_values[b.var()] = Some(*original_value); } } - GenericArgKind::Lifetime(r) => { - if let ty::ReBound(debruijn, br) = *r { + ty::GenericArgKind::Lifetime(r) => { + if let ty::ReBound(debruijn, br) = r.kind() { assert_eq!(debruijn, ty::INNERMOST); - opt_values[br.var] = Some(*original_value); + opt_values[br.var()] = Some(*original_value); } } - GenericArgKind::Const(c) => { - if let ty::ConstKind::Bound(debruijn, b) = c.kind() { + ty::GenericArgKind::Const(c) => { + if let ty::ConstKind::Bound(debruijn, bv) = c.kind() { assert_eq!(debruijn, ty::INNERMOST); - opt_values[b] = Some(*original_value); + opt_values[bv.var()] = Some(*original_value); } } } } - let var_values = infcx.tcx.mk_args_from_iter(response.variables.iter().enumerate().map( - |(index, info)| { + let var_values = infcx.interner().mk_args_from_iter( + response.variables.into_iter().enumerate().map(|(index, info)| { if info.universe() != ty::UniverseIndex::ROOT { // A variable from inside a binder of the query. While ideally these shouldn't // exist at all (see the FIXME at the start of this method), we have to deal with // them for now. - infcx.instantiate_canonical_var(DUMMY_SP, info, |idx| { + infcx.instantiate_canonical_var_with_infer(info, |idx| { ty::UniverseIndex::from(prev_universe.index() + idx.index()) }) } else if info.is_existential() { @@ -307,18 +307,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // more placeholders then they should be able to. However the inference variables have // to "come from somewhere", so by equating them with the original values of the caller // later on, we pull them down into their correct universe again. - if let Some(v) = opt_values[BoundVar::from_usize(index)] { + if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] { v } else { - infcx.instantiate_canonical_var(DUMMY_SP, info, |_| prev_universe) + infcx.instantiate_canonical_var_with_infer(info, |_| prev_universe) } } else { // For placeholders which were already part of the input, we simply map this // universal bound variable back the placeholder of the input. original_values[info.expect_placeholder_index()] } - }, - )); + }), + ); CanonicalVarValues { var_values } } @@ -337,40 +337,35 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// always relate them structurally here. #[instrument(level = "trace", skip(infcx))] fn unify_query_var_values( - infcx: &InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - original_values: &[ty::GenericArg<'tcx>], - var_values: CanonicalVarValues<'tcx>, + infcx: &Infcx, + param_env: I::ParamEnv, + original_values: &[I::GenericArg], + var_values: CanonicalVarValues, ) { assert_eq!(original_values.len(), var_values.len()); - let cause = ObligationCause::dummy(); for (&orig, response) in iter::zip(original_values, var_values.var_values) { - let InferOk { value: (), obligations } = infcx - .at(&cause, param_env) - .trace(orig, response) - .eq_structurally_relating_aliases(orig, response) - .unwrap(); - assert!(obligations.is_empty()); + let goals = infcx.eq_structurally_relating_aliases(param_env, orig, response).unwrap(); + assert!(goals.is_empty()); } } - fn register_region_constraints(&mut self, region_constraints: &QueryRegionConstraints<'tcx>) { - for &(ty::OutlivesPredicate(lhs, rhs), _) in ®ion_constraints.outlives { - match lhs.unpack() { - GenericArgKind::Lifetime(lhs) => self.register_region_outlives(lhs, rhs), - GenericArgKind::Type(lhs) => self.register_ty_outlives(lhs, rhs), - GenericArgKind::Const(_) => bug!("const outlives: {lhs:?}: {rhs:?}"), + fn register_region_constraints( + &mut self, + outlives: &[ty::OutlivesPredicate], + ) { + for &ty::OutlivesPredicate(lhs, rhs) in outlives { + match lhs.kind() { + ty::GenericArgKind::Lifetime(lhs) => self.register_region_outlives(lhs, rhs), + ty::GenericArgKind::Type(lhs) => self.register_ty_outlives(lhs, rhs), + ty::GenericArgKind::Const(_) => panic!("const outlives: {lhs:?}: {rhs:?}"), } } - - assert!(region_constraints.member_constraints.is_empty()); } - fn register_new_opaque_types(&mut self, opaque_types: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)]) { + fn register_new_opaque_types(&mut self, opaque_types: &[(ty::OpaqueTypeKey, I::Ty)]) { for &(key, ty) in opaque_types { - let hidden_ty = ty::OpaqueHiddenType { ty, span: DUMMY_SP }; - self.infcx.inject_new_hidden_type_unchecked(key, hidden_ty); + self.infcx.inject_new_hidden_type_unchecked(key, ty); } } } @@ -379,13 +374,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// evaluating a goal. The `var_values` not only include the bound variables /// of the query input, but also contain all unconstrained inference vars /// created while evaluating this goal. -pub(in crate::solve) fn make_canonical_state<'tcx, T: TypeFoldable>>( - infcx: &InferCtxt<'tcx>, - var_values: &[ty::GenericArg<'tcx>], +pub(in crate::solve) fn make_canonical_state( + infcx: &Infcx, + var_values: &[I::GenericArg], max_input_universe: ty::UniverseIndex, data: T, -) -> inspect::CanonicalState, T> { - let var_values = CanonicalVarValues { var_values: infcx.tcx.mk_args(var_values) }; +) -> inspect::CanonicalState +where + Infcx: SolverDelegate, + I: Interner, + T: TypeFoldable, +{ + let var_values = CanonicalVarValues { var_values: infcx.interner().mk_args(var_values) }; let state = inspect::State { var_values, data }; let state = state.fold_with(&mut EagerResolver::new(infcx)); Canonicalizer::canonicalize( @@ -396,45 +396,33 @@ pub(in crate::solve) fn make_canonical_state<'tcx, T: TypeFoldable> ) } -/// Instantiate a `CanonicalState`. -/// -/// Unlike for query responses, `CanonicalState` also track fresh inference -/// variables created while evaluating a goal. When creating two separate -/// `CanonicalState` during a single evaluation both may reference this -/// fresh inference variable. When instantiating them we now create separate -/// inference variables for it and have to unify them somehow. We do this -/// by extending the `var_values` while building the proof tree. -/// -/// This currently assumes that unifying the var values trivially succeeds. -/// Adding any inference constraints which weren't present when originally -/// computing the canonical query can result in bugs. -#[instrument(level = "trace", skip(infcx, span, param_env))] -pub(in crate::solve) fn instantiate_canonical_state<'tcx, T: TypeFoldable>>( - infcx: &InferCtxt<'tcx>, - span: Span, - param_env: ty::ParamEnv<'tcx>, - orig_values: &mut Vec>, - state: inspect::CanonicalState, T>, -) -> T { +// FIXME: needs to be pub to be accessed by downstream +// `rustc_trait_selection::solve::inspect::analyse`. +pub fn instantiate_canonical_state>( + infcx: &Infcx, + span: Infcx::Span, + param_env: I::ParamEnv, + orig_values: &mut Vec, + state: inspect::CanonicalState, +) -> T +where + Infcx: SolverDelegate, + I: Interner, +{ // In case any fresh inference variables have been created between `state` // and the previous instantiation, extend `orig_values` for it. assert!(orig_values.len() <= state.value.var_values.len()); - for i in orig_values.len()..state.value.var_values.len() { - let unconstrained = match state.value.var_values.var_values[i].unpack() { - ty::GenericArgKind::Lifetime(_) => { - infcx.next_region_var(RegionVariableOrigin::MiscVariable(span)).into() - } - ty::GenericArgKind::Type(_) => infcx.next_ty_var(span).into(), - ty::GenericArgKind::Const(ct) => infcx.next_const_var(ct.ty(), span).into(), - }; - + for &arg in &state.value.var_values.var_values[orig_values.len()..state.value.var_values.len()] + { + // FIXME: This is so ugly. + let unconstrained = infcx.fresh_var_for_kind_with_span(arg, span); orig_values.push(unconstrained); } let instantiation = EvalCtxt::compute_query_response_instantiation_values(infcx, orig_values, &state); - let inspect::State { var_values, data } = state.instantiate(infcx.tcx, &instantiation); + let inspect::State { var_values, data } = infcx.instantiate_canonical(state, instantiation); EvalCtxt::unify_query_var_values(infcx, param_env, orig_values, var_values); data diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs similarity index 66% rename from compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs rename to compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 70308d4359d2..485758b91a28 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1,47 +1,32 @@ -use std::io::Write; use std::ops::ControlFlow; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_hir::def_id::DefId; -use rustc_infer::infer::at::ToTrace; -use rustc_infer::infer::canonical::CanonicalVarValues; -use rustc_infer::infer::{ - BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt, -}; -use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::solve::{MaybeCause, NestedNormalizationGoals}; -use rustc_infer::traits::ObligationCause; -use rustc_macros::{extension, HashStable, HashStable_NoContext, TyDecodable, TyEncodable}; -use rustc_middle::bug; -use rustc_middle::infer::canonical::CanonicalVarInfos; -use rustc_middle::traits::solve::{ - inspect, CanonicalInput, CanonicalResponse, Certainty, PredefinedOpaques, - PredefinedOpaquesData, QueryResult, -}; -use rustc_middle::traits::specialization_graph; -use rustc_middle::ty::{ - self, InferCtxtLike, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, -}; -use rustc_session::config::DumpSolverProofTree; -use rustc_span::DUMMY_SP; -use rustc_type_ir::{self as ir, Interner}; +use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_type_ir::inherent::*; +use rustc_type_ir::relate::Relate; +use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; +use rustc_type_ir::{self as ty, CanonicalVarValues, Interner}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; +use tracing::{instrument, trace}; -use crate::traits::coherence; -use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; - -use super::inspect::ProofTreeBuilder; -use super::{search_graph, GoalEvaluationKind, FIXPOINT_STEP_LIMIT}; -use super::{search_graph::SearchGraph, Goal}; -use super::{GoalSource, SolverMode}; -pub use select::InferCtxtSelectExt; +use crate::infcx::SolverDelegate; +use crate::solve::inspect::{self, ProofTreeBuilder}; +use crate::solve::search_graph::SearchGraph; +use crate::solve::{ + search_graph, CanonicalInput, CanonicalResponse, Certainty, Goal, GoalEvaluationKind, + GoalSource, MaybeCause, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, + QueryResult, SolverMode, FIXPOINT_STEP_LIMIT, +}; pub(super) mod canonical; mod probe; -mod select; -pub struct EvalCtxt<'a, 'tcx> { +pub struct EvalCtxt<'a, Infcx, I = ::Interner> +where + Infcx: SolverDelegate, + I: Interner, +{ /// The inference context that backs (mostly) inference and placeholder terms /// instantiated while solving goals. /// @@ -57,11 +42,11 @@ pub struct EvalCtxt<'a, 'tcx> { /// If some `InferCtxt` method is missing, please first think defensively about /// the method's compatibility with this solver, or if an existing one does /// the job already. - infcx: &'a InferCtxt<'tcx>, + infcx: &'a Infcx, /// The variable info for the `var_values`, only used to make an ambiguous response /// with no constraints. - variables: CanonicalVarInfos<'tcx>, + variables: I::CanonicalVars, /// Whether we're currently computing a `NormalizesTo` goal. Unlike other goals, /// `NormalizesTo` goals act like functions with the expected term always being /// fully unconstrained. This would weaken inference however, as the nested goals @@ -70,9 +55,9 @@ pub struct EvalCtxt<'a, 'tcx> { /// when then adds these to its own context. The caller is always an `AliasRelate` /// goal so this never leaks out of the solver. is_normalizes_to_goal: bool, - pub(super) var_values: CanonicalVarValues<'tcx>, + pub(super) var_values: CanonicalVarValues, - predefined_opaques_in_body: PredefinedOpaques<'tcx>, + predefined_opaques_in_body: I::PredefinedOpaques, /// The highest universe index nameable by the caller. /// @@ -85,9 +70,9 @@ pub struct EvalCtxt<'a, 'tcx> { /// new placeholders to the caller. pub(super) max_input_universe: ty::UniverseIndex, - pub(super) search_graph: &'a mut SearchGraph<'tcx>, + pub(super) search_graph: &'a mut SearchGraph, - nested_goals: NestedGoals>, + nested_goals: NestedGoals, // Has this `EvalCtxt` errored out with `NoSolution` in `try_evaluate_added_goals`? // @@ -97,7 +82,7 @@ pub struct EvalCtxt<'a, 'tcx> { // evaluation code. tainted: Result<(), NoSolution>, - pub(super) inspect: ProofTreeBuilder>, + pub(super) inspect: ProofTreeBuilder, } #[derive(derivative::Derivative)] @@ -116,9 +101,9 @@ pub struct NestedGoals { /// /// Forgetting to replace the RHS with a fresh inference variable when we evaluate /// this goal results in an ICE.. - pub normalizes_to_goals: Vec>>, + pub normalizes_to_goals: Vec>>, /// The rest of the goals which have not yet processed or remain ambiguous. - pub goals: Vec<(GoalSource, ir::solve::Goal)>, + pub goals: Vec<(GoalSource, Goal)>, } impl NestedGoals { @@ -131,15 +116,36 @@ impl NestedGoals { } } -#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)] +#[derive(PartialEq, Eq, Debug, Hash, HashStable_NoContext, Clone, Copy)] pub enum GenerateProofTree { Yes, - IfEnabled, - Never, + No, } -#[extension(pub trait InferCtxtEvalExt<'tcx>)] -impl<'tcx> InferCtxt<'tcx> { +pub trait SolverDelegateEvalExt: SolverDelegate { + fn evaluate_root_goal( + &self, + goal: Goal::Predicate>, + generate_proof_tree: GenerateProofTree, + ) -> (Result<(bool, Certainty), NoSolution>, Option>); + + // FIXME: This is only exposed because we need to use it in `analyse.rs` + // which is not yet uplifted. Once that's done, we should remove this. + fn evaluate_root_goal_raw( + &self, + goal: Goal::Predicate>, + generate_proof_tree: GenerateProofTree, + ) -> ( + Result<(NestedNormalizationGoals, bool, Certainty), NoSolution>, + Option>, + ); +} + +impl SolverDelegateEvalExt for Infcx +where + Infcx: SolverDelegate, + I: Interner, +{ /// Evaluates a goal from **outside** of the trait solver. /// /// Using this while inside of the solver is wrong as it uses a new @@ -147,17 +153,34 @@ impl<'tcx> InferCtxt<'tcx> { #[instrument(level = "debug", skip(self))] fn evaluate_root_goal( &self, - goal: Goal<'tcx, ty::Predicate<'tcx>>, + goal: Goal, generate_proof_tree: GenerateProofTree, - ) -> (Result<(bool, Certainty), NoSolution>, Option>>) - { + ) -> (Result<(bool, Certainty), NoSolution>, Option>) { EvalCtxt::enter_root(self, generate_proof_tree, |ecx| { ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal) }) } + + #[instrument(level = "debug", skip(self))] + fn evaluate_root_goal_raw( + &self, + goal: Goal, + generate_proof_tree: GenerateProofTree, + ) -> ( + Result<(NestedNormalizationGoals, bool, Certainty), NoSolution>, + Option>, + ) { + EvalCtxt::enter_root(self, generate_proof_tree, |ecx| { + ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal) + }) + } } -impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { +impl<'a, Infcx, I> EvalCtxt<'a, Infcx> +where + Infcx: SolverDelegate, + I: Interner, +{ pub(super) fn solver_mode(&self) -> SolverMode { self.search_graph.solver_mode() } @@ -168,51 +191,41 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { /// Creates a root evaluation context and search graph. This should only be /// used from outside of any evaluation, and other methods should be preferred - /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]). + /// over using this manually (such as [`SolverDelegateEvalExt::evaluate_root_goal`]). pub(super) fn enter_root( - infcx: &InferCtxt<'tcx>, + infcx: &Infcx, generate_proof_tree: GenerateProofTree, - f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> R, - ) -> (R, Option>>) { - let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal }; - let mut search_graph = search_graph::SearchGraph::new(mode); + f: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> R, + ) -> (R, Option>) { + let mut search_graph = search_graph::SearchGraph::new(infcx.solver_mode()); let mut ecx = EvalCtxt { infcx, search_graph: &mut search_graph, nested_goals: NestedGoals::new(), - inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree), + inspect: ProofTreeBuilder::new_maybe_root(generate_proof_tree), // Only relevant when canonicalizing the response, // which we don't do within this evaluation context. predefined_opaques_in_body: infcx - .tcx + .interner() .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()), max_input_universe: ty::UniverseIndex::ROOT, - variables: ty::List::empty(), + variables: Default::default(), var_values: CanonicalVarValues::dummy(), is_normalizes_to_goal: false, tainted: Ok(()), }; let result = f(&mut ecx); - let tree = ecx.inspect.finalize(); - if let (Some(tree), DumpSolverProofTree::Always) = ( - &tree, - infcx.tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default(), - ) { - let mut lock = std::io::stdout().lock(); - let _ = lock.write_fmt(format_args!("{tree:?}\n")); - let _ = lock.flush(); - } - + let proof_tree = ecx.inspect.finalize(); assert!( ecx.nested_goals.is_empty(), "root `EvalCtxt` should not have any goals added to it" ); assert!(search_graph.is_empty()); - (result, tree) + (result, proof_tree) } /// Creates a nested evaluation context that shares the same search graph as the @@ -224,21 +237,14 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { /// This function takes care of setting up the inference context, setting the anchor, /// and registering opaques from the canonicalized input. fn enter_canonical( - tcx: TyCtxt<'tcx>, - search_graph: &'a mut search_graph::SearchGraph<'tcx>, - canonical_input: CanonicalInput<'tcx>, - canonical_goal_evaluation: &mut ProofTreeBuilder>, - f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>, Goal<'tcx, ty::Predicate<'tcx>>) -> R, + tcx: I, + search_graph: &'a mut search_graph::SearchGraph, + canonical_input: CanonicalInput, + canonical_goal_evaluation: &mut ProofTreeBuilder, + f: impl FnOnce(&mut EvalCtxt<'_, Infcx>, Goal) -> R, ) -> R { - let intercrate = match search_graph.solver_mode() { - SolverMode::Normal => false, - SolverMode::Coherence => true, - }; - let (ref infcx, input, var_values) = tcx - .infer_ctxt() - .intercrate(intercrate) - .with_next_trait_solver(true) - .build_with_canonical(DUMMY_SP, &canonical_input); + let (ref infcx, input, var_values) = + SolverDelegate::build_with_canonical(tcx, search_graph.solver_mode(), &canonical_input); let mut ecx = EvalCtxt { infcx, @@ -254,8 +260,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { }; for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { - let hidden_ty = ty::OpaqueHiddenType { ty, span: DUMMY_SP }; - ecx.infcx.inject_new_hidden_type_unchecked(key, hidden_ty); + ecx.infcx.inject_new_hidden_type_unchecked(key, ty); } if !ecx.nested_goals.is_empty() { @@ -270,7 +275,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { // instead of taking them. This would cause an ICE here, since we have // assertions against dropping an `InferCtxt` without taking opaques. // FIXME: Once we remove support for the old impl we can remove this. - let _ = infcx.take_opaque_types(); + // FIXME: Could we make `build_with_canonical` into `enter_with_canonical` and call this at the end? + infcx.reset_opaque_types(); result } @@ -282,15 +288,15 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { /// logic of the solver. /// /// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal] - /// if you're inside of the solver or [InferCtxtEvalExt::evaluate_root_goal] if you're + /// if you're inside of the solver or [SolverDelegateEvalExt::evaluate_root_goal] if you're /// outside of it. #[instrument(level = "debug", skip(tcx, search_graph, goal_evaluation), ret)] fn evaluate_canonical_goal( - tcx: TyCtxt<'tcx>, - search_graph: &'a mut search_graph::SearchGraph<'tcx>, - canonical_input: CanonicalInput<'tcx>, - goal_evaluation: &mut ProofTreeBuilder>, - ) -> QueryResult<'tcx> { + tcx: I, + search_graph: &'a mut search_graph::SearchGraph, + canonical_input: CanonicalInput, + goal_evaluation: &mut ProofTreeBuilder, + ) -> QueryResult { let mut canonical_goal_evaluation = goal_evaluation.new_canonical_goal_evaluation(canonical_input); @@ -329,7 +335,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { &mut self, goal_evaluation_kind: GoalEvaluationKind, source: GoalSource, - goal: Goal<'tcx, ty::Predicate<'tcx>>, + goal: Goal, ) -> Result<(bool, Certainty), NoSolution> { let (normalization_nested_goals, has_changed, certainty) = self.evaluate_goal_raw(goal_evaluation_kind, source, goal)?; @@ -350,13 +356,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { &mut self, goal_evaluation_kind: GoalEvaluationKind, _source: GoalSource, - goal: Goal<'tcx, ty::Predicate<'tcx>>, - ) -> Result<(NestedNormalizationGoals<'tcx>, bool, Certainty), NoSolution> { + goal: Goal, + ) -> Result<(NestedNormalizationGoals, bool, Certainty), NoSolution> { let (orig_values, canonical_goal) = self.canonicalize_goal(goal); let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); let canonical_response = EvalCtxt::evaluate_canonical_goal( - self.tcx(), + self.interner(), self.search_graph, canonical_goal, &mut goal_evaluation, @@ -391,10 +397,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { fn instantiate_response_discarding_overflow( &mut self, - param_env: ty::ParamEnv<'tcx>, - original_values: Vec>, - response: CanonicalResponse<'tcx>, - ) -> (NestedNormalizationGoals<'tcx>, Certainty, bool) { + param_env: I::ParamEnv, + original_values: Vec, + response: CanonicalResponse, + ) -> (NestedNormalizationGoals, Certainty, bool) { if let Certainty::Maybe(MaybeCause::Overflow { .. }) = response.value.certainty { return (NestedNormalizationGoals::empty(), response.value.certainty, false); } @@ -407,7 +413,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { (normalization_nested_goals, certainty, has_changed) } - fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> { + fn compute_goal(&mut self, goal: Goal) -> QueryResult { let Goal { param_env, predicate } = goal; let kind = predicate.kind(); if let Some(kind) = kind.no_bound_vars() { @@ -443,7 +449,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct }) } ty::PredicateKind::ConstEquate(_, _) => { - bug!("ConstEquate should not be emitted when `-Znext-solver` is active") + panic!("ConstEquate should not be emitted when `-Znext-solver` is active") } ty::PredicateKind::NormalizesTo(predicate) => { self.compute_normalizes_to_goal(Goal { param_env, predicate }) @@ -459,30 +465,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } } else { self.infcx.enter_forall(kind, |kind| { - let goal = goal.with(self.tcx(), ty::Binder::dummy(kind)); + let goal = goal.with(self.interner(), ty::Binder::dummy(kind)); self.add_goal(GoalSource::InstantiateHigherRanked, goal); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } } - #[instrument(level = "trace", skip(self))] - pub(super) fn add_normalizes_to_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) { - self.inspect.add_normalizes_to_goal(self.infcx, self.max_input_universe, goal); - self.nested_goals.normalizes_to_goals.push(goal); - } - - #[instrument(level = "debug", skip(self))] - pub(super) fn add_goal(&mut self, source: GoalSource, goal: Goal<'tcx, ty::Predicate<'tcx>>) { - self.inspect.add_goal(self.infcx, self.max_input_universe, source, goal); - self.nested_goals.goals.push((source, goal)); - } - // Recursively evaluates all the goals added to this `EvalCtxt` to completion, returning // the certainty of all the goals. #[instrument(level = "trace", skip(self))] pub(super) fn try_evaluate_added_goals(&mut self) -> Result { - self.inspect.start_evaluate_added_goals(); let mut response = Ok(Certainty::overflow(false)); for _ in 0..FIXPOINT_STEP_LIMIT { // FIXME: This match is a bit ugly, it might be nice to change the inspect @@ -500,8 +493,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } } - self.inspect.evaluate_added_goals_result(response); - if response.is_err() { self.tainted = Err(NoSolution); } @@ -513,8 +504,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { /// /// Goals for the next step get directly added to the nested goals of the `EvalCtxt`. fn evaluate_added_goals_step(&mut self) -> Result, NoSolution> { - let tcx = self.tcx(); - self.inspect.start_evaluate_added_goals_step(); + let tcx = self.interner(); let mut goals = core::mem::take(&mut self.nested_goals); // If this loop did not result in any progress, what's our final certainty. @@ -595,34 +585,61 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } /// Record impl args in the proof tree for later access by `InspectCandidate`. - pub(crate) fn record_impl_args(&mut self, impl_args: ty::GenericArgsRef<'tcx>) { + pub(crate) fn record_impl_args(&mut self, impl_args: I::GenericArgs) { self.inspect.record_impl_args(self.infcx, self.max_input_universe, impl_args) } -} -impl<'tcx> EvalCtxt<'_, 'tcx> { - pub(super) fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx + pub(super) fn interner(&self) -> I { + self.infcx.interner() } - pub(super) fn next_ty_infer(&mut self) -> Ty<'tcx> { - let ty = self.infcx.next_ty_var(DUMMY_SP); + #[instrument(level = "trace", skip(self))] + pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal>) { + goal.predicate = goal + .predicate + .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env }); + self.inspect.add_normalizes_to_goal(self.infcx, self.max_input_universe, goal); + self.nested_goals.normalizes_to_goals.push(goal); + } + + #[instrument(level = "debug", skip(self))] + pub(super) fn add_goal(&mut self, source: GoalSource, mut goal: Goal) { + goal.predicate = goal + .predicate + .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env }); + self.inspect.add_goal(self.infcx, self.max_input_universe, source, goal); + self.nested_goals.goals.push((source, goal)); + } + + #[instrument(level = "trace", skip(self, goals))] + pub(super) fn add_goals( + &mut self, + source: GoalSource, + goals: impl IntoIterator>, + ) { + for goal in goals { + self.add_goal(source, goal); + } + } + + pub(super) fn next_ty_infer(&mut self) -> I::Ty { + let ty = self.infcx.next_ty_infer(); self.inspect.add_var_value(ty); ty } - pub(super) fn next_const_infer(&mut self, ty: Ty<'tcx>) -> ty::Const<'tcx> { - let ct = self.infcx.next_const_var(ty, DUMMY_SP); + pub(super) fn next_const_infer(&mut self) -> I::Const { + let ct = self.infcx.next_const_infer(); self.inspect.add_var_value(ct); ct } /// Returns a ty infer or a const infer depending on whether `kind` is a `Ty` or `Const`. /// If `kind` is an integer inference variable this will still return a ty infer var. - pub(super) fn next_term_infer_of_kind(&mut self, kind: ty::Term<'tcx>) -> ty::Term<'tcx> { - match kind.unpack() { + pub(super) fn next_term_infer_of_kind(&mut self, kind: I::Term) -> I::Term { + match kind.kind() { ty::TermKind::Ty(_) => self.next_ty_infer().into(), - ty::TermKind::Const(ct) => self.next_const_infer(ct.ty()).into(), + ty::TermKind::Const(_) => self.next_const_infer().into(), } } @@ -631,13 +648,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// This is the case if the `term` does not occur in any other part of the predicate /// and is able to name all other placeholder and inference variables. #[instrument(level = "trace", skip(self), ret)] - pub(super) fn term_is_fully_unconstrained( - &self, - goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, - ) -> bool { - let universe_of_term = match goal.predicate.term.unpack() { + pub(super) fn term_is_fully_unconstrained(&self, goal: Goal>) -> bool { + let universe_of_term = match goal.predicate.term.kind() { ty::TermKind::Ty(ty) => { - if let &ty::Infer(ty::TyVar(vid)) = ty.kind() { + if let ty::Infer(ty::TyVar(vid)) = ty.kind() { self.infcx.universe_of_ty(vid).unwrap() } else { return false; @@ -652,13 +666,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } }; - struct ContainsTermOrNotNameable<'a, 'tcx> { - term: ty::Term<'tcx>, + struct ContainsTermOrNotNameable<'a, Infcx: SolverDelegate, I: Interner> { + term: I::Term, universe_of_term: ty::UniverseIndex, - infcx: &'a InferCtxt<'tcx>, + infcx: &'a Infcx, } - impl<'a, 'tcx> ContainsTermOrNotNameable<'a, 'tcx> { + impl, I: Interner> ContainsTermOrNotNameable<'_, Infcx, I> { fn check_nameable(&self, universe: ty::UniverseIndex) -> ControlFlow<()> { if self.universe_of_term.can_name(universe) { ControlFlow::Continue(()) @@ -668,21 +682,23 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } - impl<'tcx> TypeVisitor> for ContainsTermOrNotNameable<'_, 'tcx> { + impl, I: Interner> TypeVisitor + for ContainsTermOrNotNameable<'_, Infcx, I> + { type Result = ControlFlow<()>; - fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { - match *t.kind() { + fn visit_ty(&mut self, t: I::Ty) -> Self::Result { + match t.kind() { ty::Infer(ty::TyVar(vid)) => { - if let ty::TermKind::Ty(term) = self.term.unpack() - && let Some(term_vid) = term.ty_vid() - && self.infcx.root_var(vid) == self.infcx.root_var(term_vid) + if let ty::TermKind::Ty(term) = self.term.kind() + && let ty::Infer(ty::TyVar(term_vid)) = term.kind() + && self.infcx.root_ty_var(vid) == self.infcx.root_ty_var(term_vid) { ControlFlow::Break(()) } else { self.check_nameable(self.infcx.universe_of_ty(vid).unwrap()) } } - ty::Placeholder(p) => self.check_nameable(p.universe), + ty::Placeholder(p) => self.check_nameable(p.universe()), _ => { if t.has_non_region_infer() || t.has_placeholders() { t.super_visit_with(self) @@ -693,10 +709,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } - fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result { + fn visit_const(&mut self, c: I::Const) -> Self::Result { match c.kind() { ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { - if let ty::TermKind::Const(term) = self.term.unpack() + if let ty::TermKind::Const(term) = self.term.kind() && let ty::ConstKind::Infer(ty::InferConst::Var(term_vid)) = term.kind() && self.infcx.root_const_var(vid) == self.infcx.root_const_var(term_vid) { @@ -705,7 +721,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.check_nameable(self.infcx.universe_of_ct(vid).unwrap()) } } - ty::ConstKind::Placeholder(p) => self.check_nameable(p.universe), + ty::ConstKind::Placeholder(p) => self.check_nameable(p.universe()), _ => { if c.has_non_region_infer() || c.has_placeholders() { c.super_visit_with(self) @@ -727,23 +743,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } #[instrument(level = "trace", skip(self, param_env), ret)] - pub(super) fn eq>( + pub(super) fn eq>( &mut self, - param_env: ty::ParamEnv<'tcx>, + param_env: I::ParamEnv, lhs: T, rhs: T, ) -> Result<(), NoSolution> { - self.infcx - .at(&ObligationCause::dummy(), param_env) - // New solver ignores DefineOpaqueTypes, so choose Yes for consistency - .eq(DefineOpaqueTypes::Yes, lhs, rhs) - .map(|InferOk { value: (), obligations }| { - self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); - }) - .map_err(|e| { - trace!(?e, "failed to equate"); - NoSolution - }) + self.relate(param_env, lhs, ty::Variance::Invariant, rhs) } /// This should be used when relating a rigid alias with another type. @@ -754,15 +760,15 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "trace", skip(self, param_env), ret)] pub(super) fn relate_rigid_alias_non_alias( &mut self, - param_env: ty::ParamEnv<'tcx>, - alias: ty::AliasTerm<'tcx>, + param_env: I::ParamEnv, + alias: ty::AliasTerm, variance: ty::Variance, - term: ty::Term<'tcx>, + term: I::Term, ) -> Result<(), NoSolution> { // NOTE: this check is purely an optimization, the structural eq would // always fail if the term is not an inference variable. if term.is_infer() { - let tcx = self.tcx(); + let tcx = self.interner(); // We need to relate `alias` to `term` treating only the outermost // constructor as rigid, relating any contained generic arguments as // normal. We do this by first structurally equating the `term` @@ -774,11 +780,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let identity_args = self.fresh_args_for_item(alias.def_id); let rigid_ctor = ty::AliasTerm::new(tcx, alias.def_id, identity_args); let ctor_term = rigid_ctor.to_term(tcx); - let InferOk { value: (), obligations } = self - .infcx - .at(&ObligationCause::dummy(), param_env) - .trace(term, ctor_term) - .eq_structurally_relating_aliases(term, ctor_term)?; + let obligations = + self.infcx.eq_structurally_relating_aliases(param_env, term, ctor_term)?; debug_assert!(obligations.is_empty()); self.relate(param_env, alias, variance, rigid_ctor) } else { @@ -790,61 +793,38 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// unconstrained "return value" or when we're sure that all aliases in /// the types are rigid. #[instrument(level = "trace", skip(self, param_env), ret)] - pub(super) fn eq_structurally_relating_aliases>( + pub(super) fn eq_structurally_relating_aliases>( &mut self, - param_env: ty::ParamEnv<'tcx>, + param_env: I::ParamEnv, lhs: T, rhs: T, ) -> Result<(), NoSolution> { - let cause = ObligationCause::dummy(); - let InferOk { value: (), obligations } = self - .infcx - .at(&cause, param_env) - .trace(lhs, rhs) - .eq_structurally_relating_aliases(lhs, rhs)?; - assert!(obligations.is_empty()); + let result = self.infcx.eq_structurally_relating_aliases(param_env, lhs, rhs)?; + assert_eq!(result, vec![]); Ok(()) } #[instrument(level = "trace", skip(self, param_env), ret)] - pub(super) fn sub>( + pub(super) fn sub>( &mut self, - param_env: ty::ParamEnv<'tcx>, + param_env: I::ParamEnv, sub: T, sup: T, ) -> Result<(), NoSolution> { - self.infcx - .at(&ObligationCause::dummy(), param_env) - // New solver ignores DefineOpaqueTypes, so choose Yes for consistency - .sub(DefineOpaqueTypes::Yes, sub, sup) - .map(|InferOk { value: (), obligations }| { - self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); - }) - .map_err(|e| { - trace!(?e, "failed to subtype"); - NoSolution - }) + self.relate(param_env, sub, ty::Variance::Covariant, sup) } #[instrument(level = "trace", skip(self, param_env), ret)] - pub(super) fn relate>( + pub(super) fn relate>( &mut self, - param_env: ty::ParamEnv<'tcx>, + param_env: I::ParamEnv, lhs: T, variance: ty::Variance, rhs: T, ) -> Result<(), NoSolution> { - self.infcx - .at(&ObligationCause::dummy(), param_env) - // New solver ignores DefineOpaqueTypes, so choose Yes for consistency - .relate(DefineOpaqueTypes::Yes, lhs, variance, rhs) - .map(|InferOk { value: (), obligations }| { - self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); - }) - .map_err(|e| { - trace!(?e, "failed to relate"); - NoSolution - }) + let goals = self.infcx.relate(param_env, lhs, variance, rhs)?; + self.add_goals(GoalSource::Misc, goals); + Ok(()) } /// Equates two values returning the nested goals without adding them @@ -853,169 +833,130 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// If possible, try using `eq` instead which automatically handles nested /// goals correctly. #[instrument(level = "trace", skip(self, param_env), ret)] - pub(super) fn eq_and_get_goals>( + pub(super) fn eq_and_get_goals>( &self, - param_env: ty::ParamEnv<'tcx>, + param_env: I::ParamEnv, lhs: T, rhs: T, - ) -> Result>>, NoSolution> { - self.infcx - .at(&ObligationCause::dummy(), param_env) - // New solver ignores DefineOpaqueTypes, so choose Yes for consistency - .eq(DefineOpaqueTypes::Yes, lhs, rhs) - .map(|InferOk { value: (), obligations }| { - obligations.into_iter().map(|o| o.into()).collect() - }) - .map_err(|e| { - trace!(?e, "failed to equate"); - NoSolution - }) + ) -> Result>, NoSolution> { + self.infcx.relate(param_env, lhs, ty::Variance::Invariant, rhs) } - pub(super) fn instantiate_binder_with_infer> + Copy>( + pub(super) fn instantiate_binder_with_infer + Copy>( &self, - value: ty::Binder<'tcx, T>, + value: ty::Binder, ) -> T { - self.infcx.instantiate_binder_with_fresh_vars( - DUMMY_SP, - BoundRegionConversionTime::HigherRankedType, - value, - ) + self.infcx.instantiate_binder_with_infer(value) } - pub(super) fn enter_forall> + Copy, U>( + pub(super) fn enter_forall + Copy, U>( &self, - value: ty::Binder<'tcx, T>, + value: ty::Binder, f: impl FnOnce(T) -> U, ) -> U { self.infcx.enter_forall(value, f) } + pub(super) fn resolve_vars_if_possible(&self, value: T) -> T where - T: TypeFoldable>, + T: TypeFoldable, { self.infcx.resolve_vars_if_possible(value) } - pub(super) fn fresh_args_for_item(&mut self, def_id: DefId) -> ty::GenericArgsRef<'tcx> { - let args = self.infcx.fresh_args_for_item(DUMMY_SP, def_id); + pub(super) fn fresh_args_for_item(&mut self, def_id: I::DefId) -> I::GenericArgs { + let args = self.infcx.fresh_args_for_item(def_id); for arg in args { self.inspect.add_var_value(arg); } args } - pub(super) fn translate_args( - &self, - param_env: ty::ParamEnv<'tcx>, - source_impl: DefId, - source_args: ty::GenericArgsRef<'tcx>, - target_node: specialization_graph::Node, - ) -> ty::GenericArgsRef<'tcx> { - crate::traits::translate_args(self.infcx, param_env, source_impl, source_args, target_node) + pub(super) fn register_ty_outlives(&self, ty: I::Ty, lt: I::Region) { + self.infcx.register_ty_outlives(ty, lt); } - pub(super) fn register_ty_outlives(&self, ty: Ty<'tcx>, lt: ty::Region<'tcx>) { - self.infcx.register_region_obligation_with_cause(ty, lt, &ObligationCause::dummy()); - } - - pub(super) fn register_region_outlives(&self, a: ty::Region<'tcx>, b: ty::Region<'tcx>) { + pub(super) fn register_region_outlives(&self, a: I::Region, b: I::Region) { // `b : a` ==> `a <= b` - // (inlined from `InferCtxt::region_outlives_predicate`) - self.infcx.sub_regions( - rustc_infer::infer::SubregionOrigin::RelateRegionParamBound(DUMMY_SP), - b, - a, - ); + self.infcx.sub_regions(b, a); } /// Computes the list of goals required for `arg` to be well-formed pub(super) fn well_formed_goals( &self, - param_env: ty::ParamEnv<'tcx>, - arg: ty::GenericArg<'tcx>, - ) -> Option>>> { - crate::traits::wf::unnormalized_obligations(self.infcx, param_env, arg) - .map(|obligations| obligations.into_iter().map(|obligation| obligation.into())) - } - - pub(super) fn is_transmutable( - &self, - src_and_dst: rustc_transmute::Types<'tcx>, - assume: rustc_transmute::Assume, - ) -> Result { - use rustc_transmute::Answer; - // FIXME(transmutability): This really should be returning nested goals for `Answer::If*` - match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( - ObligationCause::dummy(), - src_and_dst, - assume, - ) { - Answer::Yes => Ok(Certainty::Yes), - Answer::No(_) | Answer::If(_) => Err(NoSolution), - } + param_env: I::ParamEnv, + arg: I::GenericArg, + ) -> Option>> { + self.infcx.well_formed_goals(param_env, arg) } pub(super) fn trait_ref_is_knowable( &mut self, - param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::TraitRef<'tcx>, + param_env: I::ParamEnv, + trait_ref: ty::TraitRef, ) -> Result { let infcx = self.infcx; let lazily_normalize_ty = |ty| self.structurally_normalize_ty(param_env, ty); - coherence::trait_ref_is_knowable(infcx, trait_ref, lazily_normalize_ty) - .map(|is_knowable| is_knowable.is_ok()) + infcx.trait_ref_is_knowable(trait_ref, lazily_normalize_ty) } - pub(super) fn can_define_opaque_ty(&self, def_id: impl Into) -> bool { - self.infcx.can_define_opaque_ty(def_id) + pub(super) fn fetch_eligible_assoc_item( + &self, + param_env: I::ParamEnv, + goal_trait_ref: ty::TraitRef, + trait_assoc_def_id: I::DefId, + impl_def_id: I::DefId, + ) -> Result, NoSolution> { + self.infcx.fetch_eligible_assoc_item( + param_env, + goal_trait_ref, + trait_assoc_def_id, + impl_def_id, + ) + } + + pub(super) fn can_define_opaque_ty(&self, def_id: I::LocalDefId) -> bool { + self.infcx.defining_opaque_types().contains(&def_id) } pub(super) fn insert_hidden_type( &mut self, - opaque_type_key: OpaqueTypeKey<'tcx>, - param_env: ty::ParamEnv<'tcx>, - hidden_ty: Ty<'tcx>, + opaque_type_key: ty::OpaqueTypeKey, + param_env: I::ParamEnv, + hidden_ty: I::Ty, ) -> Result<(), NoSolution> { - let mut obligations = Vec::new(); - self.infcx.insert_hidden_type( - opaque_type_key, - &ObligationCause::dummy(), - param_env, - hidden_ty, - &mut obligations, - )?; - self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); + let mut goals = Vec::new(); + self.infcx.insert_hidden_type(opaque_type_key, param_env, hidden_ty, &mut goals)?; + self.add_goals(GoalSource::Misc, goals); Ok(()) } pub(super) fn add_item_bounds_for_hidden_type( &mut self, - opaque_def_id: DefId, - opaque_args: ty::GenericArgsRef<'tcx>, - param_env: ty::ParamEnv<'tcx>, - hidden_ty: Ty<'tcx>, + opaque_def_id: I::DefId, + opaque_args: I::GenericArgs, + param_env: I::ParamEnv, + hidden_ty: I::Ty, ) { - let mut obligations = Vec::new(); + let mut goals = Vec::new(); self.infcx.add_item_bounds_for_hidden_type( opaque_def_id, opaque_args, - ObligationCause::dummy(), param_env, hidden_ty, - &mut obligations, + &mut goals, ); - self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); + self.add_goals(GoalSource::Misc, goals); } // Do something for each opaque/hidden pair defined with `def_id` in the // current inference context. pub(super) fn unify_existing_opaque_tys( &mut self, - param_env: ty::ParamEnv<'tcx>, - key: ty::OpaqueTypeKey<'tcx>, - ty: Ty<'tcx>, - ) -> Vec> { + param_env: I::ParamEnv, + key: ty::OpaqueTypeKey, + ty: I::Ty, + ) -> Vec> { // FIXME: Super inefficient to be cloning this... let opaques = self.infcx.clone_opaque_types_for_query_response(); @@ -1034,7 +975,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } ecx.eq(param_env, candidate_ty, ty)?; ecx.add_item_bounds_for_hidden_type( - candidate_key.def_id.to_def_id(), + candidate_key.def_id.into(), candidate_key.args, param_env, candidate_ty, @@ -1051,52 +992,87 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // as an ambiguity rather than no-solution. pub(super) fn try_const_eval_resolve( &self, - param_env: ty::ParamEnv<'tcx>, - unevaluated: ty::UnevaluatedConst<'tcx>, - ty: Ty<'tcx>, - ) -> Option> { - use rustc_middle::mir::interpret::ErrorHandled; - match self.infcx.const_eval_resolve(param_env, unevaluated, DUMMY_SP) { - Ok(Some(val)) => Some(ty::Const::new_value(self.tcx(), val, ty)), - Ok(None) | Err(ErrorHandled::TooGeneric(_)) => None, - Err(ErrorHandled::Reported(e, _)) => { - Some(ty::Const::new_error(self.tcx(), e.into(), ty)) + param_env: I::ParamEnv, + unevaluated: ty::UnevaluatedConst, + ) -> Option { + self.infcx.try_const_eval_resolve(param_env, unevaluated) + } + + pub(super) fn is_transmutable( + &mut self, + param_env: I::ParamEnv, + dst: I::Ty, + src: I::Ty, + assume: I::Const, + ) -> Result { + self.infcx.is_transmutable(param_env, dst, src, assume) + } +} + +/// Eagerly replace aliases with inference variables, emitting `AliasRelate` +/// goals, used when adding goals to the `EvalCtxt`. We compute the +/// `AliasRelate` goals before evaluating the actual goal to get all the +/// constraints we can. +/// +/// This is a performance optimization to more eagerly detect cycles during trait +/// solving. See tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs. +struct ReplaceAliasWithInfer<'me, 'a, Infcx, I> +where + Infcx: SolverDelegate, + I: Interner, +{ + ecx: &'me mut EvalCtxt<'a, Infcx>, + param_env: I::ParamEnv, +} + +impl TypeFolder for ReplaceAliasWithInfer<'_, '_, Infcx, I> +where + Infcx: SolverDelegate, + I: Interner, +{ + fn interner(&self) -> I { + self.ecx.interner() + } + + fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + match ty.kind() { + ty::Alias(..) if !ty.has_escaping_bound_vars() => { + let infer_ty = self.ecx.next_ty_infer(); + let normalizes_to = ty::PredicateKind::AliasRelate( + ty.into(), + infer_ty.into(), + ty::AliasRelationDirection::Equate, + ); + self.ecx.add_goal( + GoalSource::Misc, + Goal::new(self.interner(), self.param_env, normalizes_to), + ); + infer_ty } + _ => ty.super_fold_with(self), } } - /// Walk through the vtable of a principal trait ref, executing a `supertrait_visitor` - /// for every trait ref encountered (including the principal). Passes both the vtable - /// base and the (optional) vptr slot. - pub(super) fn walk_vtable( - &mut self, - principal: ty::PolyTraitRef<'tcx>, - mut supertrait_visitor: impl FnMut(&mut Self, ty::PolyTraitRef<'tcx>, usize, Option), - ) { - let tcx = self.tcx(); - let mut offset = 0; - prepare_vtable_segments::<()>(tcx, principal, |segment| { - match segment { - VtblSegment::MetadataDSA => { - offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); - } - VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - let own_vtable_entries = count_own_vtable_entries(tcx, trait_ref); - - supertrait_visitor( - self, - trait_ref, - offset, - emit_vptr.then(|| offset + own_vtable_entries), - ); - - offset += own_vtable_entries; - if emit_vptr { - offset += 1; - } - } + fn fold_const(&mut self, ct: I::Const) -> I::Const { + match ct.kind() { + ty::ConstKind::Unevaluated(..) if !ct.has_escaping_bound_vars() => { + let infer_ct = self.ecx.next_const_infer(); + let normalizes_to = ty::PredicateKind::AliasRelate( + ct.into(), + infer_ct.into(), + ty::AliasRelationDirection::Equate, + ); + self.ecx.add_goal( + GoalSource::Misc, + Goal::new(self.interner(), self.param_env, normalizes_to), + ); + infer_ct } - ControlFlow::Continue(()) - }); + _ => ct.super_fold_with(self), + } + } + + fn fold_predicate(&mut self, predicate: I::Predicate) -> I::Predicate { + if predicate.allow_normalization() { predicate.super_fold_with(self) } else { predicate } } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs similarity index 57% rename from compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs rename to compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs index 9edc489754ca..1c5358b3edb4 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs @@ -1,23 +1,30 @@ -use crate::solve::assembly::Candidate; - -use super::EvalCtxt; -use rustc_infer::traits::BuiltinImplSource; -use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::solve::{inspect, CandidateSource, QueryResult}; -use rustc_middle::ty::TyCtxt; use std::marker::PhantomData; -pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> { - ecx: &'me mut EvalCtxt<'a, 'tcx>, +use rustc_type_ir::Interner; +use tracing::instrument; + +use crate::infcx::SolverDelegate; +use crate::solve::assembly::Candidate; +use crate::solve::inspect; +use crate::solve::{BuiltinImplSource, CandidateSource, EvalCtxt, NoSolution, QueryResult}; + +pub(in crate::solve) struct ProbeCtxt<'me, 'a, Infcx, I, F, T> +where + Infcx: SolverDelegate, + I: Interner, +{ + ecx: &'me mut EvalCtxt<'a, Infcx, I>, probe_kind: F, _result: PhantomData, } -impl<'tcx, F, T> ProbeCtxt<'_, '_, 'tcx, F, T> +impl ProbeCtxt<'_, '_, Infcx, I, F, T> where - F: FnOnce(&T) -> inspect::ProbeKind>, + F: FnOnce(&T) -> inspect::ProbeKind, + Infcx: SolverDelegate, + I: Interner, { - pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T { + pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> T) -> T { let ProbeCtxt { ecx: outer_ecx, probe_kind, _result } = self; let infcx = outer_ecx.infcx; @@ -34,7 +41,7 @@ where tainted: outer_ecx.tainted, inspect: outer_ecx.inspect.take_and_enter_probe(), }; - let r = nested_ecx.infcx.probe(|_| { + let r = nested_ecx.infcx.probe(|| { let r = f(&mut nested_ecx); nested_ecx.inspect.probe_final_state(infcx, max_input_universe); r @@ -48,30 +55,43 @@ where } } -pub(in crate::solve) struct TraitProbeCtxt<'me, 'a, 'tcx, F> { - cx: ProbeCtxt<'me, 'a, 'tcx, F, QueryResult<'tcx>>, - source: CandidateSource<'tcx>, +pub(in crate::solve) struct TraitProbeCtxt<'me, 'a, Infcx, I, F> +where + Infcx: SolverDelegate, + I: Interner, +{ + cx: ProbeCtxt<'me, 'a, Infcx, I, F, QueryResult>, + source: CandidateSource, } -impl<'tcx, F> TraitProbeCtxt<'_, '_, 'tcx, F> +impl TraitProbeCtxt<'_, '_, Infcx, I, F> where - F: FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind>, + Infcx: SolverDelegate, + I: Interner, + F: FnOnce(&QueryResult) -> inspect::ProbeKind, { #[instrument(level = "debug", skip_all, fields(source = ?self.source))] pub(in crate::solve) fn enter( self, - f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, - ) -> Result, NoSolution> { + f: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> QueryResult, + ) -> Result, NoSolution> { self.cx.enter(|ecx| f(ecx)).map(|result| Candidate { source: self.source, result }) } } -impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { +impl<'a, Infcx, I> EvalCtxt<'a, Infcx, I> +where + Infcx: SolverDelegate, + I: Interner, +{ /// `probe_kind` is only called when proof tree building is enabled so it can be /// as expensive as necessary to output the desired information. - pub(in crate::solve) fn probe(&mut self, probe_kind: F) -> ProbeCtxt<'_, 'a, 'tcx, F, T> + pub(in crate::solve) fn probe( + &mut self, + probe_kind: F, + ) -> ProbeCtxt<'_, 'a, Infcx, I, F, T> where - F: FnOnce(&T) -> inspect::ProbeKind>, + F: FnOnce(&T) -> inspect::ProbeKind, { ProbeCtxt { ecx: self, probe_kind, _result: PhantomData } } @@ -79,28 +99,20 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { pub(in crate::solve) fn probe_builtin_trait_candidate( &mut self, source: BuiltinImplSource, - ) -> TraitProbeCtxt< - '_, - 'a, - 'tcx, - impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind>, - > { + ) -> TraitProbeCtxt<'_, 'a, Infcx, I, impl FnOnce(&QueryResult) -> inspect::ProbeKind> + { self.probe_trait_candidate(CandidateSource::BuiltinImpl(source)) } pub(in crate::solve) fn probe_trait_candidate( &mut self, - source: CandidateSource<'tcx>, - ) -> TraitProbeCtxt< - '_, - 'a, - 'tcx, - impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind>, - > { + source: CandidateSource, + ) -> TraitProbeCtxt<'_, 'a, Infcx, I, impl FnOnce(&QueryResult) -> inspect::ProbeKind> + { TraitProbeCtxt { cx: ProbeCtxt { ecx: self, - probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::TraitCandidate { + probe_kind: move |result: &QueryResult| inspect::ProbeKind::TraitCandidate { source, result: *result, }, diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs similarity index 59% rename from compiler/rustc_trait_selection/src/solve/inspect/build.rs rename to compiler/rustc_next_trait_solver/src/solve/inspect/build.rs index 803300c5196c..5fbec4b28d43 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs @@ -3,21 +3,19 @@ //! This code is *a bit* of a mess and can hopefully be //! mostly ignored. For a general overview of how it works, //! see the comment on [ProofTreeBuilder]. + +use std::marker::PhantomData; use std::mem; -use rustc_infer::infer::InferCtxt; -use rustc_middle::bug; -use rustc_middle::infer::canonical::CanonicalVarValues; -use rustc_middle::traits::query::NoSolution; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_next_trait_solver::solve::{ - CanonicalInput, Certainty, Goal, GoalSource, QueryInput, QueryResult, -}; -use rustc_session::config::DumpSolverProofTree; -use rustc_type_ir::Interner; +use rustc_type_ir::{self as ty, Interner}; +use crate::infcx::SolverDelegate; use crate::solve::eval_ctxt::canonical; -use crate::solve::{self, inspect, GenerateProofTree}; +use crate::solve::inspect; +use crate::solve::{ + CanonicalInput, Certainty, GenerateProofTree, Goal, GoalEvaluationKind, GoalSource, QueryInput, + QueryResult, +}; /// The core data structure when building proof trees. /// @@ -39,7 +37,12 @@ use crate::solve::{self, inspect, GenerateProofTree}; /// trees. At the end of trait solving `ProofTreeBuilder::finalize` /// is called to recursively convert the whole structure to a /// finished proof tree. -pub(in crate::solve) struct ProofTreeBuilder { +pub(in crate::solve) struct ProofTreeBuilder::Interner> +where + Infcx: SolverDelegate, + I: Interner, +{ + _infcx: PhantomData, state: Option>>, } @@ -53,7 +56,7 @@ enum DebugSolver { Root, GoalEvaluation(WipGoalEvaluation), CanonicalGoalEvaluation(WipCanonicalGoalEvaluation), - GoalEvaluationStep(WipGoalEvaluationStep), + CanonicalGoalEvaluationStep(WipCanonicalGoalEvaluationStep), } impl From> for DebugSolver { @@ -68,9 +71,9 @@ impl From> for DebugSolver { } } -impl From> for DebugSolver { - fn from(g: WipGoalEvaluationStep) -> DebugSolver { - DebugSolver::GoalEvaluationStep(g) +impl From> for DebugSolver { + fn from(g: WipCanonicalGoalEvaluationStep) -> DebugSolver { + DebugSolver::CanonicalGoalEvaluationStep(g) } } @@ -78,7 +81,7 @@ impl From> for DebugSolver { #[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))] struct WipGoalEvaluation { pub uncanonicalized_goal: Goal, - pub kind: WipGoalEvaluationKind, + pub orig_values: Vec, pub evaluation: Option>, } @@ -86,31 +89,19 @@ impl WipGoalEvaluation { fn finalize(self) -> inspect::GoalEvaluation { inspect::GoalEvaluation { uncanonicalized_goal: self.uncanonicalized_goal, - kind: match self.kind { - WipGoalEvaluationKind::Root { orig_values } => { - inspect::GoalEvaluationKind::Root { orig_values } - } - WipGoalEvaluationKind::Nested => inspect::GoalEvaluationKind::Nested, - }, + orig_values: self.orig_values, evaluation: self.evaluation.unwrap().finalize(), } } } -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))] -pub(in crate::solve) enum WipGoalEvaluationKind { - Root { orig_values: Vec }, - Nested, -} - #[derive(derivative::Derivative)] #[derivative(PartialEq(bound = ""), Eq(bound = ""))] pub(in crate::solve) enum WipCanonicalGoalEvaluationKind { Overflow, CycleInStack, ProvisionalCacheHit, - Interned { revisions: I::GoalEvaluationSteps }, + Interned { final_revision: I::CanonicalGoalEvaluationStepRef }, } impl std::fmt::Debug for WipCanonicalGoalEvaluationKind { @@ -119,7 +110,9 @@ impl std::fmt::Debug for WipCanonicalGoalEvaluationKind { Self::Overflow => write!(f, "Overflow"), Self::CycleInStack => write!(f, "CycleInStack"), Self::ProvisionalCacheHit => write!(f, "ProvisionalCacheHit"), - Self::Interned { revisions: _ } => f.debug_struct("Interned").finish_non_exhaustive(), + Self::Interned { final_revision: _ } => { + f.debug_struct("Interned").finish_non_exhaustive() + } } } } @@ -131,13 +124,15 @@ struct WipCanonicalGoalEvaluation { kind: Option>, /// Only used for uncached goals. After we finished evaluating /// the goal, this is interned and moved into `kind`. - revisions: Vec>, + final_revision: Option>, result: Option>, } impl WipCanonicalGoalEvaluation { fn finalize(self) -> inspect::CanonicalGoalEvaluation { - assert!(self.revisions.is_empty()); + // We've already interned the final revision in + // `fn finalize_canonical_goal_evaluation`. + assert!(self.final_revision.is_none()); let kind = match self.kind.unwrap() { WipCanonicalGoalEvaluationKind::Overflow => { inspect::CanonicalGoalEvaluationKind::Overflow @@ -148,8 +143,8 @@ impl WipCanonicalGoalEvaluation { WipCanonicalGoalEvaluationKind::ProvisionalCacheHit => { inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit } - WipCanonicalGoalEvaluationKind::Interned { revisions } => { - inspect::CanonicalGoalEvaluationKind::Evaluation { revisions } + WipCanonicalGoalEvaluationKind::Interned { final_revision } => { + inspect::CanonicalGoalEvaluationKind::Evaluation { final_revision } } }; @@ -159,29 +154,7 @@ impl WipCanonicalGoalEvaluation { #[derive(derivative::Derivative)] #[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))] -struct WipAddedGoalsEvaluation { - evaluations: Vec>>, - result: Option>, -} - -impl WipAddedGoalsEvaluation { - fn finalize(self) -> inspect::AddedGoalsEvaluation { - inspect::AddedGoalsEvaluation { - evaluations: self - .evaluations - .into_iter() - .map(|evaluations| { - evaluations.into_iter().map(WipGoalEvaluation::finalize).collect() - }) - .collect(), - result: self.result.unwrap(), - } - } -} - -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))] -struct WipGoalEvaluationStep { +struct WipCanonicalGoalEvaluationStep { /// Unlike `EvalCtxt::var_values`, we append a new /// generic arg here whenever we create a new inference /// variable. @@ -194,36 +167,28 @@ struct WipGoalEvaluationStep { evaluation: WipProbe, } -impl WipGoalEvaluationStep { +impl WipCanonicalGoalEvaluationStep { fn current_evaluation_scope(&mut self) -> &mut WipProbe { let mut current = &mut self.evaluation; for _ in 0..self.probe_depth { match current.steps.last_mut() { Some(WipProbeStep::NestedProbe(p)) => current = p, - _ => bug!(), + _ => panic!(), } } current } - fn added_goals_evaluation(&mut self) -> &mut WipAddedGoalsEvaluation { - let mut current = &mut self.evaluation; - loop { - match current.steps.last_mut() { - Some(WipProbeStep::NestedProbe(p)) => current = p, - Some(WipProbeStep::EvaluateGoals(evaluation)) => return evaluation, - _ => bug!(), - } - } - } - - fn finalize(self) -> inspect::GoalEvaluationStep { + fn finalize(self) -> inspect::CanonicalGoalEvaluationStep { let evaluation = self.evaluation.finalize(); match evaluation.kind { inspect::ProbeKind::Root { .. } => (), _ => unreachable!("unexpected root evaluation: {evaluation:?}"), } - inspect::GoalEvaluationStep { instantiated_goal: self.instantiated_goal, evaluation } + inspect::CanonicalGoalEvaluationStep { + instantiated_goal: self.instantiated_goal, + evaluation, + } } } @@ -250,7 +215,6 @@ impl WipProbe { #[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))] enum WipProbeStep { AddGoal(GoalSource, inspect::CanonicalState>), - EvaluateGoals(WipAddedGoalsEvaluation), NestedProbe(WipProbe), MakeCanonicalResponse { shallow_certainty: Certainty }, RecordImplArgs { impl_args: inspect::CanonicalState }, @@ -260,7 +224,6 @@ impl WipProbeStep { fn finalize(self) -> inspect::ProbeStep { match self { WipProbeStep::AddGoal(source, goal) => inspect::ProbeStep::AddGoal(source, goal), - WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()), WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()), WipProbeStep::RecordImplArgs { impl_args } => { inspect::ProbeStep::RecordImplArgs { impl_args } @@ -272,27 +235,36 @@ impl WipProbeStep { } } -// FIXME: Genericize this impl. -impl<'tcx> ProofTreeBuilder> { - fn new(state: impl Into>>) -> ProofTreeBuilder> { - ProofTreeBuilder { state: Some(Box::new(state.into())) } +impl, I: Interner> ProofTreeBuilder { + fn new(state: impl Into>) -> ProofTreeBuilder { + ProofTreeBuilder { state: Some(Box::new(state.into())), _infcx: PhantomData } } - fn nested>>>(&self, state: impl FnOnce() -> T) -> Self { - ProofTreeBuilder { state: self.state.as_ref().map(|_| Box::new(state().into())) } + fn opt_nested>>(&self, state: impl FnOnce() -> Option) -> Self { + ProofTreeBuilder { + state: self.state.as_ref().and_then(|_| Some(state()?.into())).map(Box::new), + _infcx: PhantomData, + } } - fn as_mut(&mut self) -> Option<&mut DebugSolver>> { + fn nested>>(&self, state: impl FnOnce() -> T) -> Self { + ProofTreeBuilder { + state: self.state.as_ref().map(|_| Box::new(state().into())), + _infcx: PhantomData, + } + } + + fn as_mut(&mut self) -> Option<&mut DebugSolver> { self.state.as_deref_mut() } - pub fn take_and_enter_probe(&mut self) -> ProofTreeBuilder> { - let mut nested = ProofTreeBuilder { state: self.state.take() }; + pub fn take_and_enter_probe(&mut self) -> ProofTreeBuilder { + let mut nested = ProofTreeBuilder { state: self.state.take(), _infcx: PhantomData }; nested.enter_probe(); nested } - pub fn finalize(self) -> Option>> { + pub fn finalize(self) -> Option> { match *self.state? { DebugSolver::GoalEvaluation(wip_goal_evaluation) => { Some(wip_goal_evaluation.finalize()) @@ -301,33 +273,19 @@ impl<'tcx> ProofTreeBuilder> { } } - pub fn new_maybe_root( - tcx: TyCtxt<'tcx>, - generate_proof_tree: GenerateProofTree, - ) -> ProofTreeBuilder> { + pub fn new_maybe_root(generate_proof_tree: GenerateProofTree) -> ProofTreeBuilder { match generate_proof_tree { - GenerateProofTree::Never => ProofTreeBuilder::new_noop(), - GenerateProofTree::IfEnabled => { - let opts = &tcx.sess.opts.unstable_opts; - match opts.next_solver.map(|c| c.dump_tree).unwrap_or_default() { - DumpSolverProofTree::Always => ProofTreeBuilder::new_root(), - // `OnError` is handled by reevaluating goals in error - // reporting with `GenerateProofTree::Yes`. - DumpSolverProofTree::OnError | DumpSolverProofTree::Never => { - ProofTreeBuilder::new_noop() - } - } - } + GenerateProofTree::No => ProofTreeBuilder::new_noop(), GenerateProofTree::Yes => ProofTreeBuilder::new_root(), } } - pub fn new_root() -> ProofTreeBuilder> { + pub fn new_root() -> ProofTreeBuilder { ProofTreeBuilder::new(DebugSolver::Root) } - pub fn new_noop() -> ProofTreeBuilder> { - ProofTreeBuilder { state: None } + pub fn new_noop() -> ProofTreeBuilder { + ProofTreeBuilder { state: None, _infcx: PhantomData } } pub fn is_noop(&self) -> bool { @@ -336,47 +294,44 @@ impl<'tcx> ProofTreeBuilder> { pub(in crate::solve) fn new_goal_evaluation( &mut self, - goal: Goal, ty::Predicate<'tcx>>, - orig_values: &[ty::GenericArg<'tcx>], - kind: solve::GoalEvaluationKind, - ) -> ProofTreeBuilder> { - self.nested(|| WipGoalEvaluation { - uncanonicalized_goal: goal, - kind: match kind { - solve::GoalEvaluationKind::Root => { - WipGoalEvaluationKind::Root { orig_values: orig_values.to_vec() } - } - solve::GoalEvaluationKind::Nested => WipGoalEvaluationKind::Nested, - }, - evaluation: None, + goal: Goal, + orig_values: &[I::GenericArg], + kind: GoalEvaluationKind, + ) -> ProofTreeBuilder { + self.opt_nested(|| match kind { + GoalEvaluationKind::Root => Some(WipGoalEvaluation { + uncanonicalized_goal: goal, + orig_values: orig_values.to_vec(), + evaluation: None, + }), + GoalEvaluationKind::Nested => None, }) } pub fn new_canonical_goal_evaluation( &mut self, - goal: CanonicalInput>, - ) -> ProofTreeBuilder> { + goal: CanonicalInput, + ) -> ProofTreeBuilder { self.nested(|| WipCanonicalGoalEvaluation { goal, kind: None, - revisions: vec![], + final_revision: None, result: None, }) } - pub fn finalize_evaluation( + pub fn finalize_canonical_goal_evaluation( &mut self, - tcx: TyCtxt<'tcx>, - ) -> Option<&'tcx [inspect::GoalEvaluationStep>]> { + tcx: I, + ) -> Option { self.as_mut().map(|this| match this { DebugSolver::CanonicalGoalEvaluation(evaluation) => { - let revisions = mem::take(&mut evaluation.revisions) - .into_iter() - .map(WipGoalEvaluationStep::finalize); - let revisions = &*tcx.arena.alloc_from_iter(revisions); - let kind = WipCanonicalGoalEvaluationKind::Interned { revisions }; + let final_revision = mem::take(&mut evaluation.final_revision).unwrap(); + let final_revision = + tcx.intern_canonical_goal_evaluation_step(final_revision.finalize()); + let kind = WipCanonicalGoalEvaluationKind::Interned { final_revision }; assert_eq!(evaluation.kind.replace(kind), None); - revisions + final_revision } _ => unreachable!(), }) @@ -384,7 +339,7 @@ impl<'tcx> ProofTreeBuilder> { pub fn canonical_goal_evaluation( &mut self, - canonical_goal_evaluation: ProofTreeBuilder>, + canonical_goal_evaluation: ProofTreeBuilder, ) { if let Some(this) = self.as_mut() { match (this, *canonical_goal_evaluation.state.unwrap()) { @@ -400,7 +355,7 @@ impl<'tcx> ProofTreeBuilder> { } } - pub fn goal_evaluation_kind(&mut self, kind: WipCanonicalGoalEvaluationKind>) { + pub fn canonical_goal_evaluation_kind(&mut self, kind: WipCanonicalGoalEvaluationKind) { if let Some(this) = self.as_mut() { match this { DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => { @@ -411,19 +366,13 @@ impl<'tcx> ProofTreeBuilder> { } } - pub fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder>) { + pub fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder) { if let Some(this) = self.as_mut() { - match (this, *goal_evaluation.state.unwrap()) { - ( - DebugSolver::GoalEvaluationStep(state), - DebugSolver::GoalEvaluation(goal_evaluation), - ) => state - .added_goals_evaluation() - .evaluations - .last_mut() - .unwrap() - .push(goal_evaluation), - (this @ DebugSolver::Root, goal_evaluation) => *this = goal_evaluation, + match this { + DebugSolver::Root => *this = *goal_evaluation.state.unwrap(), + DebugSolver::CanonicalGoalEvaluationStep(_) => { + assert!(goal_evaluation.state.is_none()) + } _ => unreachable!(), } } @@ -431,10 +380,10 @@ impl<'tcx> ProofTreeBuilder> { pub fn new_goal_evaluation_step( &mut self, - var_values: CanonicalVarValues<'tcx>, - instantiated_goal: QueryInput, ty::Predicate<'tcx>>, - ) -> ProofTreeBuilder> { - self.nested(|| WipGoalEvaluationStep { + var_values: ty::CanonicalVarValues, + instantiated_goal: QueryInput, + ) -> ProofTreeBuilder { + self.nested(|| WipCanonicalGoalEvaluationStep { var_values: var_values.var_values.to_vec(), instantiated_goal, evaluation: WipProbe { @@ -447,34 +396,34 @@ impl<'tcx> ProofTreeBuilder> { }) } - pub fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder>) { + pub fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder) { if let Some(this) = self.as_mut() { match (this, *goal_evaluation_step.state.unwrap()) { ( DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluations), - DebugSolver::GoalEvaluationStep(goal_evaluation_step), + DebugSolver::CanonicalGoalEvaluationStep(goal_evaluation_step), ) => { - canonical_goal_evaluations.revisions.push(goal_evaluation_step); + canonical_goal_evaluations.final_revision = Some(goal_evaluation_step); } _ => unreachable!(), } } } - pub fn add_var_value>>(&mut self, arg: T) { + pub fn add_var_value>(&mut self, arg: T) { match self.as_mut() { None => {} - Some(DebugSolver::GoalEvaluationStep(state)) => { + Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { state.var_values.push(arg.into()); } - Some(s) => bug!("tried to add var values to {s:?}"), + Some(s) => panic!("tried to add var values to {s:?}"), } } pub fn enter_probe(&mut self) { match self.as_mut() { None => {} - Some(DebugSolver::GoalEvaluationStep(state)) => { + Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { let initial_num_var_values = state.var_values.len(); state.current_evaluation_scope().steps.push(WipProbeStep::NestedProbe(WipProbe { initial_num_var_values, @@ -484,29 +433,25 @@ impl<'tcx> ProofTreeBuilder> { })); state.probe_depth += 1; } - Some(s) => bug!("tried to start probe to {s:?}"), + Some(s) => panic!("tried to start probe to {s:?}"), } } - pub fn probe_kind(&mut self, probe_kind: inspect::ProbeKind>) { + pub fn probe_kind(&mut self, probe_kind: inspect::ProbeKind) { match self.as_mut() { None => {} - Some(DebugSolver::GoalEvaluationStep(state)) => { + Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { let prev = state.current_evaluation_scope().kind.replace(probe_kind); assert_eq!(prev, None); } - _ => bug!(), + _ => panic!(), } } - pub fn probe_final_state( - &mut self, - infcx: &InferCtxt<'tcx>, - max_input_universe: ty::UniverseIndex, - ) { + pub fn probe_final_state(&mut self, infcx: &Infcx, max_input_universe: ty::UniverseIndex) { match self.as_mut() { None => {} - Some(DebugSolver::GoalEvaluationStep(state)) => { + Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { let final_state = canonical::make_canonical_state( infcx, &state.var_values, @@ -516,34 +461,34 @@ impl<'tcx> ProofTreeBuilder> { let prev = state.current_evaluation_scope().final_state.replace(final_state); assert_eq!(prev, None); } - _ => bug!(), + _ => panic!(), } } pub fn add_normalizes_to_goal( &mut self, - infcx: &InferCtxt<'tcx>, + infcx: &Infcx, max_input_universe: ty::UniverseIndex, - goal: Goal, ty::NormalizesTo<'tcx>>, + goal: Goal>, ) { self.add_goal( infcx, max_input_universe, GoalSource::Misc, - goal.with(infcx.tcx, goal.predicate), + goal.with(infcx.interner(), goal.predicate), ); } pub fn add_goal( &mut self, - infcx: &InferCtxt<'tcx>, + infcx: &Infcx, max_input_universe: ty::UniverseIndex, source: GoalSource, - goal: Goal, ty::Predicate<'tcx>>, + goal: Goal, ) { match self.as_mut() { None => {} - Some(DebugSolver::GoalEvaluationStep(state)) => { + Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { let goal = canonical::make_canonical_state( infcx, &state.var_values, @@ -552,18 +497,18 @@ impl<'tcx> ProofTreeBuilder> { ); state.current_evaluation_scope().steps.push(WipProbeStep::AddGoal(source, goal)) } - _ => bug!(), + _ => panic!(), } } pub(crate) fn record_impl_args( &mut self, - infcx: &InferCtxt<'tcx>, + infcx: &Infcx, max_input_universe: ty::UniverseIndex, - impl_args: ty::GenericArgsRef<'tcx>, + impl_args: I::GenericArgs, ) { match self.as_mut() { - Some(DebugSolver::GoalEvaluationStep(state)) => { + Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { let impl_args = canonical::make_canonical_state( infcx, &state.var_values, @@ -576,78 +521,45 @@ impl<'tcx> ProofTreeBuilder> { .push(WipProbeStep::RecordImplArgs { impl_args }); } None => {} - _ => bug!(), + _ => panic!(), } } pub fn make_canonical_response(&mut self, shallow_certainty: Certainty) { match self.as_mut() { - Some(DebugSolver::GoalEvaluationStep(state)) => { + Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { state .current_evaluation_scope() .steps .push(WipProbeStep::MakeCanonicalResponse { shallow_certainty }); } None => {} - _ => bug!(), + _ => panic!(), } } - pub fn finish_probe(mut self) -> ProofTreeBuilder> { + pub fn finish_probe(mut self) -> ProofTreeBuilder { match self.as_mut() { None => {} - Some(DebugSolver::GoalEvaluationStep(state)) => { + Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => { assert_ne!(state.probe_depth, 0); let num_var_values = state.current_evaluation_scope().initial_num_var_values; state.var_values.truncate(num_var_values); state.probe_depth -= 1; } - _ => bug!(), + _ => panic!(), } self } - pub fn start_evaluate_added_goals(&mut self) { - match self.as_mut() { - None => {} - Some(DebugSolver::GoalEvaluationStep(state)) => { - state.current_evaluation_scope().steps.push(WipProbeStep::EvaluateGoals( - WipAddedGoalsEvaluation { evaluations: vec![], result: None }, - )); - } - _ => bug!(), - } - } - - pub fn start_evaluate_added_goals_step(&mut self) { - match self.as_mut() { - None => {} - Some(DebugSolver::GoalEvaluationStep(state)) => { - state.added_goals_evaluation().evaluations.push(vec![]); - } - _ => bug!(), - } - } - - pub fn evaluate_added_goals_result(&mut self, result: Result) { - match self.as_mut() { - None => {} - Some(DebugSolver::GoalEvaluationStep(state)) => { - let prev = state.added_goals_evaluation().result.replace(result); - assert_eq!(prev, None); - } - _ => bug!(), - } - } - - pub fn query_result(&mut self, result: QueryResult>) { + pub fn query_result(&mut self, result: QueryResult) { if let Some(this) = self.as_mut() { match this { DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => { assert_eq!(canonical_goal_evaluation.result.replace(result), None); } - DebugSolver::GoalEvaluationStep(evaluation_step) => { + DebugSolver::CanonicalGoalEvaluationStep(evaluation_step) => { assert_eq!( evaluation_step .evaluation diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs new file mode 100644 index 000000000000..0d8c00601269 --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs @@ -0,0 +1,6 @@ +pub use rustc_type_ir::solve::inspect::*; + +mod build; +pub(in crate::solve) use build::*; + +pub use crate::solve::eval_ctxt::canonical::instantiate_canonical_state; diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs similarity index 66% rename from compiler/rustc_trait_selection/src/solve/mod.rs rename to compiler/rustc_next_trait_solver/src/solve/mod.rs index b085d009d75a..02069016c2bd 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -13,35 +13,23 @@ //! //! FIXME(@lcnr): Write that section. If you read this before then ask me //! about it on zulip. -use rustc_hir::def_id::DefId; -use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; -use rustc_infer::traits::query::NoSolution; -use rustc_macros::extension; -use rustc_middle::bug; -use rustc_middle::infer::canonical::CanonicalVarInfos; -use rustc_middle::traits::solve::{ - CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, QueryResult, Response, -}; -use rustc_middle::ty::{ - self, AliasRelationDirection, CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, Ty, - TyCtxt, TypeOutlivesPredicate, UniverseIndex, -}; mod alias_relate; mod assembly; mod eval_ctxt; -mod fulfill; pub mod inspect; -mod normalize; mod normalizes_to; mod project_goals; mod search_graph; mod trait_goals; -pub use eval_ctxt::{EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt}; -pub use fulfill::FulfillmentCtxt; -pub(crate) use normalize::deeply_normalize_for_diagnostics; -pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes}; +use rustc_type_ir::inherent::*; +pub use rustc_type_ir::solve::*; +use rustc_type_ir::{self as ty, Interner}; +use tracing::instrument; + +pub use self::eval_ctxt::{EvalCtxt, GenerateProofTree, SolverDelegateEvalExt}; +use crate::infcx::SolverDelegate; /// How many fixpoint iterations we should attempt inside of the solver before bailing /// with overflow. @@ -54,40 +42,30 @@ pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes}; /// recursion limit again. However, this feels very unlikely. const FIXPOINT_STEP_LIMIT: usize = 8; -#[derive(Debug, Clone, Copy)] -enum SolverMode { - /// Ordinary trait solving, using everywhere except for coherence. - Normal, - /// Trait solving during coherence. There are a few notable differences - /// between coherence and ordinary trait solving. - /// - /// Most importantly, trait solving during coherence must not be incomplete, - /// i.e. return `Err(NoSolution)` for goals for which a solution exists. - /// This means that we must not make any guesses or arbitrary choices. - Coherence, -} - #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum GoalEvaluationKind { Root, Nested, } -#[extension(trait CanonicalResponseExt)] -impl<'tcx> Canonical<'tcx, Response>> { - fn has_no_inference_or_external_constraints(&self) -> bool { - self.value.external_constraints.region_constraints.is_empty() - && self.value.var_values.is_identity() - && self.value.external_constraints.opaque_types.is_empty() - } +fn has_no_inference_or_external_constraints( + response: ty::Canonical>, +) -> bool { + response.value.external_constraints.region_constraints.is_empty() + && response.value.var_values.is_identity() + && response.value.external_constraints.opaque_types.is_empty() } -impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { +impl<'a, Infcx, I> EvalCtxt<'a, Infcx> +where + Infcx: SolverDelegate, + I: Interner, +{ #[instrument(level = "trace", skip(self))] fn compute_type_outlives_goal( &mut self, - goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>, - ) -> QueryResult<'tcx> { + goal: Goal>, + ) -> QueryResult { let ty::OutlivesPredicate(ty, lt) = goal.predicate; self.register_ty_outlives(ty, lt); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) @@ -96,21 +74,18 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { #[instrument(level = "trace", skip(self))] fn compute_region_outlives_goal( &mut self, - goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>, - ) -> QueryResult<'tcx> { + goal: Goal>, + ) -> QueryResult { let ty::OutlivesPredicate(a, b) = goal.predicate; self.register_region_outlives(a, b); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } #[instrument(level = "trace", skip(self))] - fn compute_coerce_goal( - &mut self, - goal: Goal<'tcx, CoercePredicate<'tcx>>, - ) -> QueryResult<'tcx> { + fn compute_coerce_goal(&mut self, goal: Goal>) -> QueryResult { self.compute_subtype_goal(Goal { param_env: goal.param_env, - predicate: SubtypePredicate { + predicate: ty::SubtypePredicate { a_is_expected: false, a: goal.predicate.a, b: goal.predicate.b, @@ -119,10 +94,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } #[instrument(level = "trace", skip(self))] - fn compute_subtype_goal( - &mut self, - goal: Goal<'tcx, SubtypePredicate<'tcx>>, - ) -> QueryResult<'tcx> { + fn compute_subtype_goal(&mut self, goal: Goal>) -> QueryResult { if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } else { @@ -131,8 +103,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } } - fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> { - if self.tcx().check_is_object_safe(trait_def_id) { + fn compute_object_safe_goal(&mut self, trait_def_id: I::DefId) -> QueryResult { + if self.interner().trait_is_object_safe(trait_def_id) { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { Err(NoSolution) @@ -140,10 +112,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } #[instrument(level = "trace", skip(self))] - fn compute_well_formed_goal( - &mut self, - goal: Goal<'tcx, ty::GenericArg<'tcx>>, - ) -> QueryResult<'tcx> { + fn compute_well_formed_goal(&mut self, goal: Goal) -> QueryResult { match self.well_formed_goals(goal.param_env, goal.predicate) { Some(goals) => { self.add_goals(GoalSource::Misc, goals); @@ -156,8 +125,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { #[instrument(level = "trace", skip(self))] fn compute_const_evaluatable_goal( &mut self, - Goal { param_env, predicate: ct }: Goal<'tcx, ty::Const<'tcx>>, - ) -> QueryResult<'tcx> { + Goal { param_env, predicate: ct }: Goal, + ) -> QueryResult { match ct.kind() { ty::ConstKind::Unevaluated(uv) => { // We never return `NoSolution` here as `try_const_eval_resolve` emits an @@ -168,7 +137,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { // FIXME(generic_const_exprs): Implement handling for generic // const expressions here. - if let Some(_normalized) = self.try_const_eval_resolve(param_env, uv, ct.ty()) { + if let Some(_normalized) = self.try_const_eval_resolve(param_env, uv) { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) @@ -177,7 +146,9 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ty::ConstKind::Infer(_) => { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } - ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => { + ty::ConstKind::Placeholder(_) + | ty::ConstKind::Value(_, _) + | ty::ConstKind::Error(_) => { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } // We can freely ICE here as: @@ -185,7 +156,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { // - `Bound` cannot exist as we don't have a binder around the self Type // - `Expr` is part of `feature(generic_const_exprs)` and is not implemented yet ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Expr(_) => { - bug!("unexpect const kind: {:?}", ct) + panic!("unexpect const kind: {:?}", ct) } } } @@ -193,34 +164,57 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { #[instrument(level = "trace", skip(self), ret)] fn compute_const_arg_has_type_goal( &mut self, - goal: Goal<'tcx, (ty::Const<'tcx>, Ty<'tcx>)>, - ) -> QueryResult<'tcx> { + goal: Goal, + ) -> QueryResult { let (ct, ty) = goal.predicate; - self.eq(goal.param_env, ct.ty(), ty)?; + + let ct_ty = match ct.kind() { + // FIXME: Ignore effect vars because canonicalization doesn't handle them correctly + // and if we stall on the var then we wind up creating ambiguity errors in a probe + // for this goal which contains an effect var. Which then ends up ICEing. + ty::ConstKind::Infer(ty::InferConst::EffectVar(_)) => { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + ty::ConstKind::Infer(_) => { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + } + ty::ConstKind::Error(_) => { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + ty::ConstKind::Unevaluated(uv) => { + self.interner().type_of(uv.def).instantiate(self.interner(), &uv.args) + } + ty::ConstKind::Expr(_) => unimplemented!( + "`feature(generic_const_exprs)` is not supported in the new trait solver" + ), + ty::ConstKind::Param(_) => { + unreachable!("`ConstKind::Param` should have been canonicalized to `Placeholder`") + } + ty::ConstKind::Bound(_, _) => panic!("escaping bound vars in {:?}", ct), + ty::ConstKind::Value(ty, _) => ty, + ty::ConstKind::Placeholder(placeholder) => { + self.interner().find_const_ty_from_env(goal.param_env, placeholder) + } + }; + + self.eq(goal.param_env, ct_ty, ty)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } -impl<'tcx> EvalCtxt<'_, 'tcx> { - #[instrument(level = "trace", skip(self, goals))] - fn add_goals( - &mut self, - source: GoalSource, - goals: impl IntoIterator>>, - ) { - for goal in goals { - self.add_goal(source, goal); - } - } - +impl EvalCtxt<'_, Infcx> +where + Infcx: SolverDelegate, + I: Interner, +{ /// Try to merge multiple possible ways to prove a goal, if that is not possible returns `None`. /// /// In this case we tend to flounder and return ambiguity by calling `[EvalCtxt::flounder]`. #[instrument(level = "trace", skip(self), ret)] fn try_merge_responses( &mut self, - responses: &[CanonicalResponse<'tcx>], - ) -> Option> { + responses: &[CanonicalResponse], + ) -> Option> { if responses.is_empty() { return None; } @@ -236,14 +230,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .iter() .find(|response| { response.value.certainty == Certainty::Yes - && response.has_no_inference_or_external_constraints() + && has_no_inference_or_external_constraints(**response) }) .copied() } /// If we fail to merge responses we flounder and return overflow or ambiguity. #[instrument(level = "trace", skip(self), ret)] - fn flounder(&mut self, responses: &[CanonicalResponse<'tcx>]) -> QueryResult<'tcx> { + fn flounder(&mut self, responses: &[CanonicalResponse]) -> QueryResult { if responses.is_empty() { return Err(NoSolution); } @@ -253,7 +247,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { certainty.unify_with(response.value.certainty) }) else { - bug!("expected flounder response to be ambiguous") + panic!("expected flounder response to be ambiguous") }; Ok(self.make_ambiguous_response_no_constraints(maybe_cause)) @@ -267,18 +261,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "trace", skip(self, param_env), ret)] fn structurally_normalize_ty( &mut self, - param_env: ty::ParamEnv<'tcx>, - ty: Ty<'tcx>, - ) -> Result, NoSolution> { + param_env: I::ParamEnv, + ty: I::Ty, + ) -> Result { if let ty::Alias(..) = ty.kind() { let normalized_ty = self.next_ty_infer(); let alias_relate_goal = Goal::new( - self.tcx(), + self.interner(), param_env, ty::PredicateKind::AliasRelate( ty.into(), normalized_ty.into(), - AliasRelationDirection::Equate, + ty::AliasRelationDirection::Equate, ), ); self.add_goal(GoalSource::Misc, alias_relate_goal); @@ -290,17 +284,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } -fn response_no_constraints_raw<'tcx>( - tcx: TyCtxt<'tcx>, - max_universe: UniverseIndex, - variables: CanonicalVarInfos<'tcx>, +fn response_no_constraints_raw( + tcx: I, + max_universe: ty::UniverseIndex, + variables: I::CanonicalVars, certainty: Certainty, -) -> CanonicalResponse<'tcx> { - Canonical { +) -> CanonicalResponse { + ty::Canonical { max_universe, variables, value: Response { - var_values: CanonicalVarValues::make_identity(tcx, variables), + var_values: ty::CanonicalVarValues::make_identity(tcx, variables), // FIXME: maybe we should store the "no response" version in tcx, like // we do for tcx.types and stuff. external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()), diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs similarity index 59% rename from compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs rename to compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs index 94e078f56159..0f1c1f13c165 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs @@ -1,20 +1,22 @@ -use crate::solve::EvalCtxt; -use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; -use rustc_middle::ty; +use rustc_type_ir::{self as ty, Interner}; +use tracing::instrument; -impl<'tcx> EvalCtxt<'_, 'tcx> { +use crate::infcx::SolverDelegate; +use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult}; + +impl EvalCtxt<'_, Infcx> +where + Infcx: SolverDelegate, + I: Interner, +{ #[instrument(level = "trace", skip(self), ret)] pub(super) fn normalize_anon_const( &mut self, - goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, - ) -> QueryResult<'tcx> { + goal: Goal>, + ) -> QueryResult { if let Some(normalized_const) = self.try_const_eval_resolve( goal.param_env, ty::UnevaluatedConst::new(goal.predicate.alias.def_id, goal.predicate.alias.args), - self.tcx() - .type_of(goal.predicate.alias.def_id) - .no_bound_vars() - .expect("const ty should not rely on other generics"), ) { self.instantiate_normalizes_to_term(goal, normalized_const.into()); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs similarity index 74% rename from compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs rename to compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs index 353bdb9caff8..8436f3ad4841 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs @@ -4,17 +4,22 @@ //! 1. instantiate generic parameters, //! 2. equate the self type, and //! 3. instantiate and register where clauses. -use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, QueryResult}; -use rustc_middle::ty; -use crate::solve::EvalCtxt; +use rustc_type_ir::{self as ty, Interner}; -impl<'tcx> EvalCtxt<'_, 'tcx> { +use crate::infcx::SolverDelegate; +use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult}; + +impl EvalCtxt<'_, Infcx> +where + Infcx: SolverDelegate, + I: Interner, +{ pub(super) fn normalize_inherent_associated_type( &mut self, - goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, - ) -> QueryResult<'tcx> { - let tcx = self.tcx(); + goal: Goal>, + ) -> QueryResult { + let tcx = self.interner(); let inherent = goal.predicate.alias.expect_ty(tcx); let impl_def_id = tcx.parent(inherent.def_id); @@ -24,7 +29,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.eq( goal.param_env, inherent.self_ty(), - tcx.type_of(impl_def_id).instantiate(tcx, impl_args), + tcx.type_of(impl_def_id).instantiate(tcx, &impl_args), )?; // Equate IAT with the RHS of the project goal @@ -39,12 +44,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.add_goals( GoalSource::Misc, tcx.predicates_of(inherent.def_id) - .instantiate(tcx, inherent_args) - .into_iter() - .map(|(pred, _)| goal.with(tcx, pred)), + .iter_instantiated(tcx, &inherent_args) + .map(|pred| goal.with(tcx, pred)), ); - let normalized = tcx.type_of(inherent.def_id).instantiate(tcx, inherent_args); + let normalized = tcx.type_of(inherent.def_id).instantiate(tcx, &inherent_args); self.instantiate_normalizes_to_term(goal, normalized.into()); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs similarity index 63% rename from compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs rename to compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 8c492b62c1ad..ebc83bef5137 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -1,35 +1,33 @@ -use crate::traits::specialization_graph; - -use super::assembly::structural_traits::AsyncCallableRelevantTypes; -use super::assembly::{self, structural_traits, Candidate}; -use super::{EvalCtxt, GoalSource}; -use rustc_hir::def_id::DefId; -use rustc_hir::LangItem; -use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::solve::inspect::ProbeKind; -use rustc_infer::traits::solve::MaybeCause; -use rustc_infer::traits::specialization_graph::LeafDef; -use rustc_infer::traits::Reveal; -use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal, QueryResult}; -use rustc_middle::traits::BuiltinImplSource; -use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; -use rustc_middle::ty::NormalizesTo; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::ty::{TypeVisitableExt, Upcast}; -use rustc_middle::{bug, span_bug}; -use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP}; - mod anon_const; mod inherent; mod opaque_types; mod weak_types; -impl<'tcx> EvalCtxt<'_, 'tcx> { +use rustc_type_ir::inherent::*; +use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::Upcast as _; +use rustc_type_ir::{self as ty, Interner, NormalizesTo}; +use tracing::instrument; + +use crate::infcx::SolverDelegate; +use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes}; +use crate::solve::assembly::{self, Candidate}; +use crate::solve::inspect::ProbeKind; +use crate::solve::{ + BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, + NoSolution, QueryResult, +}; + +impl EvalCtxt<'_, Infcx> +where + Infcx: SolverDelegate, + I: Interner, +{ #[instrument(level = "trace", skip(self), ret)] pub(super) fn compute_normalizes_to_goal( &mut self, - goal: Goal<'tcx, NormalizesTo<'tcx>>, - ) -> QueryResult<'tcx> { + goal: Goal>, + ) -> QueryResult { self.set_is_normalizes_to_goal(); debug_assert!(self.term_is_fully_unconstrained(goal)); let normalize_result = self @@ -40,7 +38,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Ok(res) => Ok(res), Err(NoSolution) => { let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal; - self.relate_rigid_alias_non_alias(param_env, alias, ty::Variance::Invariant, term)?; + self.relate_rigid_alias_non_alias(param_env, alias, ty::Invariant, term)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } @@ -49,11 +47,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// Normalize the given alias by at least one step. If the alias is rigid, this /// returns `NoSolution`. #[instrument(level = "trace", skip(self), ret)] - fn normalize_at_least_one_step( - &mut self, - goal: Goal<'tcx, NormalizesTo<'tcx>>, - ) -> QueryResult<'tcx> { - match goal.predicate.alias.kind(self.tcx()) { + fn normalize_at_least_one_step(&mut self, goal: Goal>) -> QueryResult { + match goal.predicate.alias.kind(self.interner()) { ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => { let candidates = self.assemble_and_evaluate_candidates(goal); self.merge_candidates(candidates) @@ -72,41 +67,45 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// emit nested `AliasRelate` goals to structurally normalize the alias. pub fn instantiate_normalizes_to_term( &mut self, - goal: Goal<'tcx, NormalizesTo<'tcx>>, - term: ty::Term<'tcx>, + goal: Goal>, + term: I::Term, ) { self.eq(goal.param_env, goal.predicate.term, term) .expect("expected goal term to be fully unconstrained"); } } -impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { - fn self_ty(self) -> Ty<'tcx> { +impl assembly::GoalKind for NormalizesTo +where + Infcx: SolverDelegate, + I: Interner, +{ + fn self_ty(self) -> I::Ty { self.self_ty() } - fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> { + fn trait_ref(self, tcx: I) -> ty::TraitRef { self.alias.trait_ref(tcx) } - fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { + fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self { self.with_self_ty(tcx, self_ty) } - fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId { + fn trait_def_id(self, tcx: I) -> I::DefId { self.trait_def_id(tcx) } fn probe_and_match_goal_against_assumption( - ecx: &mut EvalCtxt<'_, 'tcx>, - source: CandidateSource<'tcx>, - goal: Goal<'tcx, Self>, - assumption: ty::Clause<'tcx>, - then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + source: CandidateSource, + goal: Goal, + assumption: I::Clause, + then: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> QueryResult, + ) -> Result, NoSolution> { if let Some(projection_pred) = assumption.as_projection_clause() { if projection_pred.projection_def_id() == goal.predicate.def_id() { - let tcx = ecx.tcx(); + let tcx = ecx.interner(); ecx.probe_trait_candidate(source).enter(|ecx| { let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred); @@ -121,9 +120,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { // Add GAT where clauses from the trait's definition ecx.add_goals( GoalSource::Misc, - tcx.predicates_of(goal.predicate.def_id()) - .instantiate_own(tcx, goal.predicate.alias.args) - .map(|(pred, _)| goal.with(tcx, pred)), + tcx.own_predicates_of(goal.predicate.def_id()) + .iter_instantiated(tcx, &goal.predicate.alias.args) + .map(|pred| goal.with(tcx, pred)), ); then(ecx) @@ -137,24 +136,23 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_impl_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, NormalizesTo<'tcx>>, - impl_def_id: DefId, - ) -> Result, NoSolution> { - let tcx = ecx.tcx(); + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal>, + impl_def_id: I::DefId, + ) -> Result, NoSolution> { + let tcx = ecx.interner(); let goal_trait_ref = goal.predicate.alias.trait_ref(tcx); - let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap(); - let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; - if !drcx.args_may_unify( - goal.predicate.trait_ref(tcx).args, - impl_trait_header.trait_ref.skip_binder().args, + let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); + if !ecx.interner().args_may_unify_deep( + goal.predicate.alias.trait_ref(tcx).args, + impl_trait_ref.skip_binder().args, ) { return Err(NoSolution); } // We have to ignore negative impls when projecting. - let impl_polarity = impl_trait_header.polarity; + let impl_polarity = tcx.impl_polarity(impl_def_id); match impl_polarity { ty::ImplPolarity::Negative => return Err(NoSolution), ty::ImplPolarity::Reservation => { @@ -165,31 +163,28 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { let impl_args = ecx.fresh_args_for_item(impl_def_id); - let impl_trait_ref = impl_trait_header.trait_ref.instantiate(tcx, impl_args); + let impl_trait_ref = impl_trait_ref.instantiate(tcx, &impl_args); ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?; let where_clause_bounds = tcx .predicates_of(impl_def_id) - .instantiate(tcx, impl_args) - .predicates - .into_iter() + .iter_instantiated(tcx, &impl_args) .map(|pred| goal.with(tcx, pred)); ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); // Add GAT where clauses from the trait's definition ecx.add_goals( GoalSource::Misc, - tcx.predicates_of(goal.predicate.def_id()) - .instantiate_own(tcx, goal.predicate.alias.args) - .map(|(pred, _)| goal.with(tcx, pred)), + tcx.own_predicates_of(goal.predicate.def_id()) + .iter_instantiated(tcx, &goal.predicate.alias.args) + .map(|pred| goal.with(tcx, pred)), ); // In case the associated item is hidden due to specialization, we have to // return ambiguity this would otherwise be incomplete, resulting in // unsoundness during coherence (#105782). - let Some(assoc_def) = fetch_eligible_assoc_item_def( - ecx, + let Some(target_item_def_id) = ecx.fetch_eligible_assoc_item( goal.param_env, goal_trait_ref, goal.predicate.def_id(), @@ -199,31 +194,23 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); }; - let error_response = |ecx: &mut EvalCtxt<'_, 'tcx>, reason| { - let guar = tcx.dcx().span_delayed_bug(tcx.def_span(assoc_def.item.def_id), reason); - let error_term = match assoc_def.item.kind { - ty::AssocKind::Const => ty::Const::new_error( - tcx, - guar, - tcx.type_of(goal.predicate.def_id()) - .instantiate(tcx, goal.predicate.alias.args), - ) - .into(), - ty::AssocKind::Type => Ty::new_error(tcx, guar).into(), - // This makes no sense... - ty::AssocKind::Fn => span_bug!( - tcx.def_span(assoc_def.item.def_id), - "cannot project to an associated function" - ), + let error_response = |ecx: &mut EvalCtxt<'_, Infcx>, msg: &str| { + let guar = tcx.delay_bug(msg); + let error_term = match goal.predicate.alias.kind(tcx) { + ty::AliasTermKind::ProjectionTy => Ty::new_error(tcx, guar).into(), + ty::AliasTermKind::ProjectionConst => Const::new_error(tcx, guar).into(), + kind => panic!("expected projection, found {kind:?}"), }; ecx.instantiate_normalizes_to_term(goal, error_term); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }; - if !assoc_def.item.defaultness(tcx).has_value() { - return error_response(ecx, "missing value for assoc item in impl"); + if !tcx.has_item_definition(target_item_def_id) { + return error_response(ecx, "missing item"); } + let target_container_def_id = tcx.parent(target_item_def_id); + // Getting the right args here is complex, e.g. given: // - a goal ` as Trait>::Assoc` // - the applicable impl `impl Trait for Vec` @@ -234,44 +221,40 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { // // And then map these args to the args of the defining impl of `Assoc`, going // from `[u32, u64]` to `[u32, i32, u64]`. - let impl_args_with_gat = - goal.predicate.alias.args.rebase_onto(tcx, goal_trait_ref.def_id, impl_args); - let args = ecx.translate_args( - goal.param_env, + let target_args = ecx.translate_args( + goal, impl_def_id, - impl_args_with_gat, - assoc_def.defining_node, - ); + impl_args, + impl_trait_ref, + target_container_def_id, + )?; - if !tcx.check_args_compatible(assoc_def.item.def_id, args) { - return error_response( - ecx, - "associated item has mismatched generic item arguments", - ); + if !tcx.check_args_compatible(target_item_def_id, target_args) { + return error_response(ecx, "associated item has mismatched arguments"); } // Finally we construct the actual value of the associated type. - let term = match assoc_def.item.kind { - ty::AssocKind::Type => tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into()), - ty::AssocKind::Const => { - if tcx.features().associated_const_equality { - bug!("associated const projection is not supported yet") + let term = match goal.predicate.alias.kind(tcx) { + ty::AliasTermKind::ProjectionTy => { + tcx.type_of(target_item_def_id).map_bound(|ty| ty.into()) + } + ty::AliasTermKind::ProjectionConst => { + if tcx.features().associated_const_equality() { + panic!("associated const projection is not supported yet") } else { ty::EarlyBinder::bind( - ty::Const::new_error_with_message( + Const::new_error_with_message( tcx, - tcx.type_of(assoc_def.item.def_id).instantiate_identity(), - DUMMY_SP, "associated const projection is not supported yet", ) .into(), ) } } - ty::AssocKind::Fn => unreachable!("we should never project to a fn"), + kind => panic!("expected projection, found {kind:?}"), }; - ecx.instantiate_normalizes_to_term(goal, term.instantiate(tcx, args)); + ecx.instantiate_normalizes_to_term(goal, term.instantiate(tcx, &target_args)); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -279,64 +262,61 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { /// Fail to normalize if the predicate contains an error, alternatively, we could normalize to `ty::Error` /// and succeed. Can experiment with this to figure out what results in better error messages. fn consider_error_guaranteed_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - _guar: ErrorGuaranteed, - ) -> Result, NoSolution> { + _ecx: &mut EvalCtxt<'_, Infcx>, + _guar: I::ErrorGuaranteed, + ) -> Result, NoSolution> { Err(NoSolution) } fn consider_auto_trait_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { - ecx.tcx().dcx().span_delayed_bug( - ecx.tcx().def_span(goal.predicate.def_id()), - "associated types not allowed on auto traits", - ); + ecx: &mut EvalCtxt<'_, Infcx>, + _goal: Goal, + ) -> Result, NoSolution> { + ecx.interner().delay_bug("associated types not allowed on auto traits"); Err(NoSolution) } fn consider_trait_alias_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { - bug!("trait aliases do not have associated types: {:?}", goal); + _ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { + panic!("trait aliases do not have associated types: {:?}", goal); } fn consider_builtin_sized_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { - bug!("`Sized` does not have an associated type: {:?}", goal); + _ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { + panic!("`Sized` does not have an associated type: {:?}", goal); } fn consider_builtin_copy_clone_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { - bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal); + _ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { + panic!("`Copy`/`Clone` does not have an associated type: {:?}", goal); } fn consider_builtin_pointer_like_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { - bug!("`PointerLike` does not have an associated type: {:?}", goal); + _ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { + panic!("`PointerLike` does not have an associated type: {:?}", goal); } fn consider_builtin_fn_ptr_trait_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { - bug!("`FnPtr` does not have an associated type: {:?}", goal); + _ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { + panic!("`FnPtr` does not have an associated type: {:?}", goal); } fn consider_builtin_fn_trait_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, goal_kind: ty::ClosureKind, - ) -> Result, NoSolution> { - let tcx = ecx.tcx(); + ) -> Result, NoSolution> { + let tcx = ecx.interner(); let tupled_inputs_and_output = match structural_traits::extract_tupled_inputs_and_output_from_callable( tcx, @@ -349,7 +329,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } }; let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| { - ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, None), [output]) + ty::TraitRef::new(tcx, tcx.require_lang_item(TraitSolverLangItem::Sized), [output]) }); let pred = tupled_inputs_and_output @@ -375,16 +355,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_async_fn_trait_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, goal_kind: ty::ClosureKind, - ) -> Result, NoSolution> { - let tcx = ecx.tcx(); + ) -> Result, NoSolution> { + let tcx = ecx.interner(); let env_region = match goal_kind { ty::ClosureKind::Fn | ty::ClosureKind::FnMut => goal.predicate.alias.args.region_at(2), // Doesn't matter what this region is - ty::ClosureKind::FnOnce => tcx.lifetimes.re_static, + ty::ClosureKind::FnOnce => Region::new_static(tcx), }; let (tupled_inputs_and_output_and_coroutine, nested_preds) = structural_traits::extract_tupled_inputs_and_output_from_async_callable( @@ -395,7 +375,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { )?; let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound( |AsyncCallableRelevantTypes { output_coroutine_ty: output_ty, .. }| { - ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, None), [output_ty]) + ty::TraitRef::new( + tcx, + tcx.require_lang_item(TraitSolverLangItem::Sized), + [output_ty], + ) }, ); @@ -406,39 +390,52 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { output_coroutine_ty, coroutine_return_ty, }| { - let (projection_term, term) = match tcx.item_name(goal.predicate.def_id()) { - sym::CallOnceFuture => ( + let (projection_term, term) = if tcx + .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CallOnceFuture) + { + ( ty::AliasTerm::new( tcx, goal.predicate.def_id(), [goal.predicate.self_ty(), tupled_inputs_ty], ), output_coroutine_ty.into(), - ), - sym::CallRefFuture => ( + ) + } else if tcx + .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CallRefFuture) + { + ( ty::AliasTerm::new( tcx, goal.predicate.def_id(), [ - ty::GenericArg::from(goal.predicate.self_ty()), + I::GenericArg::from(goal.predicate.self_ty()), tupled_inputs_ty.into(), env_region.into(), ], ), output_coroutine_ty.into(), - ), - sym::Output => ( + ) + } else if tcx.is_lang_item( + goal.predicate.def_id(), + TraitSolverLangItem::AsyncFnOnceOutput, + ) { + ( ty::AliasTerm::new( tcx, goal.predicate.def_id(), [ - ty::GenericArg::from(goal.predicate.self_ty()), + I::GenericArg::from(goal.predicate.self_ty()), tupled_inputs_ty.into(), ], ), coroutine_return_ty.into(), - ), - name => bug!("no such associated type: {name}"), + ) + } else { + panic!( + "no such associated type in `AsyncFn*`: {:?}", + goal.predicate.def_id() + ) }; ty::ProjectionPredicate { projection_term, term } }, @@ -460,9 +457,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_async_fn_kind_helper_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { let [ closure_fn_kind_ty, goal_kind_ty, @@ -472,7 +469,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { coroutine_captures_by_ref_ty, ] = **goal.predicate.alias.args else { - bug!(); + panic!(); }; // Bail if the upvars haven't been constrained. @@ -492,7 +489,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } let upvars_ty = ty::CoroutineClosureSignature::tupled_upvars_by_closure_kind( - ecx.tcx(), + ecx.interner(), goal_kind, tupled_inputs_ty.expect_ty(), tupled_upvars_ty.expect_ty(), @@ -507,18 +504,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_tuple_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { - bug!("`Tuple` does not have an associated type: {:?}", goal); + _ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { + panic!("`Tuple` does not have an associated type: {:?}", goal); } fn consider_builtin_pointee_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { - let tcx = ecx.tcx(); - let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { + let tcx = ecx.interner(); + let metadata_def_id = tcx.require_lang_item(TraitSolverLangItem::Metadata); assert_eq!(metadata_def_id, goal.predicate.def_id()); ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { let metadata_ty = match goal.predicate.self_ty().kind() { @@ -540,16 +537,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { | ty::CoroutineWitness(..) | ty::Never | ty::Foreign(..) - | ty::Dynamic(_, _, ty::DynStar) => tcx.types.unit, + | ty::Dynamic(_, _, ty::DynStar) => Ty::new_unit(tcx), - ty::Error(e) => Ty::new_error(tcx, *e), + ty::Error(e) => Ty::new_error(tcx, e), - ty::Str | ty::Slice(_) => tcx.types.usize, + ty::Str | ty::Slice(_) => Ty::new_usize(tcx), ty::Dynamic(_, _, ty::Dyn) => { - let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None); + let dyn_metadata = tcx.require_lang_item(TraitSolverLangItem::DynMetadata); tcx.type_of(dyn_metadata) - .instantiate(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())]) + .instantiate(tcx, &[I::GenericArg::from(goal.predicate.self_ty())]) } ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { @@ -559,32 +556,31 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { // exist. Instead, `Pointee` should be a supertrait of `Sized`. let sized_predicate = ty::TraitRef::new( tcx, - tcx.require_lang_item(LangItem::Sized, None), - [ty::GenericArg::from(goal.predicate.self_ty())], + tcx.require_lang_item(TraitSolverLangItem::Sized), + [I::GenericArg::from(goal.predicate.self_ty())], ); // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? ecx.add_goal(GoalSource::Misc, goal.with(tcx, sized_predicate)); - tcx.types.unit + Ty::new_unit(tcx) } - ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() { - None => tcx.types.unit, - Some(tail_def) => { - let tail_ty = tail_def.ty(tcx, args); - Ty::new_projection(tcx, metadata_def_id, [tail_ty]) + ty::Adt(def, args) if def.is_struct() => match def.struct_tail_ty(tcx) { + None => Ty::new_unit(tcx), + Some(tail_ty) => { + Ty::new_projection(tcx, metadata_def_id, [tail_ty.instantiate(tcx, &args)]) } }, - ty::Adt(_, _) => tcx.types.unit, + ty::Adt(_, _) => Ty::new_unit(tcx), ty::Tuple(elements) => match elements.last() { - None => tcx.types.unit, + None => Ty::new_unit(tcx), Some(&tail_ty) => Ty::new_projection(tcx, metadata_def_id, [tail_ty]), }, ty::Infer( ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_), ) - | ty::Bound(..) => bug!( + | ty::Bound(..) => panic!( "unexpected self ty `{:?}` when normalizing `::Metadata`", goal.predicate.self_ty() ), @@ -596,16 +592,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_future_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); - let ty::Coroutine(def_id, args) = *self_ty.kind() else { + let ty::Coroutine(def_id, args) = self_ty.kind() else { return Err(NoSolution); }; // Coroutines are not futures unless they come from `async` desugaring - let tcx = ecx.tcx(); + let tcx = ecx.interner(); if !tcx.coroutine_is_async(def_id) { return Err(NoSolution); } @@ -617,7 +613,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, ty::ProjectionPredicate { - projection_term: ty::AliasTerm::new(ecx.tcx(), goal.predicate.def_id(), [self_ty]), + projection_term: ty::AliasTerm::new( + ecx.interner(), + goal.predicate.def_id(), + [self_ty], + ), term, } .upcast(tcx), @@ -628,16 +628,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); - let ty::Coroutine(def_id, args) = *self_ty.kind() else { + let ty::Coroutine(def_id, args) = self_ty.kind() else { return Err(NoSolution); }; // Coroutines are not Iterators unless they come from `gen` desugaring - let tcx = ecx.tcx(); + let tcx = ecx.interner(); if !tcx.coroutine_is_gen(def_id) { return Err(NoSolution); } @@ -649,7 +649,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, ty::ProjectionPredicate { - projection_term: ty::AliasTerm::new(ecx.tcx(), goal.predicate.def_id(), [self_ty]), + projection_term: ty::AliasTerm::new( + ecx.interner(), + goal.predicate.def_id(), + [self_ty], + ), term, } .upcast(tcx), @@ -660,23 +664,23 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_fused_iterator_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { - bug!("`FusedIterator` does not have an associated type: {:?}", goal); + _ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { + panic!("`FusedIterator` does not have an associated type: {:?}", goal); } fn consider_builtin_async_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); - let ty::Coroutine(def_id, args) = *self_ty.kind() else { + let ty::Coroutine(def_id, args) = self_ty.kind() else { return Err(NoSolution); }; // Coroutines are not AsyncIterators unless they come from `gen` desugaring - let tcx = ecx.tcx(); + let tcx = ecx.interner(); if !tcx.coroutine_is_async_gen(def_id) { return Err(NoSolution); } @@ -687,10 +691,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { // coroutine yield ty `Poll>`. let wrapped_expected_ty = Ty::new_adt( tcx, - tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None)), + tcx.adt_def(tcx.require_lang_item(TraitSolverLangItem::Poll)), tcx.mk_args(&[Ty::new_adt( tcx, - tcx.adt_def(tcx.require_lang_item(LangItem::Option, None)), + tcx.adt_def(tcx.require_lang_item(TraitSolverLangItem::Option)), tcx.mk_args(&[expected_ty.into()]), ) .into()]), @@ -703,29 +707,30 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_coroutine_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); - let ty::Coroutine(def_id, args) = *self_ty.kind() else { + let ty::Coroutine(def_id, args) = self_ty.kind() else { return Err(NoSolution); }; // `async`-desugared coroutines do not implement the coroutine trait - let tcx = ecx.tcx(); + let tcx = ecx.interner(); if !tcx.is_general_coroutine(def_id) { return Err(NoSolution); } let coroutine = args.as_coroutine(); - let name = tcx.associated_item(goal.predicate.def_id()).name; - let term = if name == sym::Return { + let term = if tcx + .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineReturn) + { coroutine.return_ty().into() - } else if name == sym::Yield { + } else if tcx.is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineYield) { coroutine.yield_ty().into() } else { - bug!("unexpected associated item `<{self_ty} as Coroutine>::{name}`") + panic!("unexpected associated item `{:?}` for `{self_ty:?}`", goal.predicate.def_id()) }; Self::probe_and_consider_implied_clause( @@ -734,7 +739,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { goal, ty::ProjectionPredicate { projection_term: ty::AliasTerm::new( - ecx.tcx(), + ecx.interner(), goal.predicate.def_id(), [self_ty, coroutine.resume_ty()], ), @@ -748,18 +753,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_structural_builtin_unsize_candidates( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Vec> { - bug!("`Unsize` does not have an associated type: {:?}", goal); + _ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Vec> { + panic!("`Unsize` does not have an associated type: {:?}", goal); } fn consider_builtin_discriminant_kind_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); - let discriminant_ty = match *self_ty.kind() { + let discriminant_ty = match self_ty.kind() { ty::Bool | ty::Char | ty::Int(..) @@ -783,7 +788,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { | ty::Slice(_) | ty::Dynamic(_, _, _) | ty::Tuple(_) - | ty::Error(_) => self_ty.discriminant_ty(ecx.tcx()), + | ty::Error(_) => self_ty.discriminant_ty(ecx.interner()), // We do not call `Ty::discriminant_ty` on alias, param, or placeholder // types, which return `::Discriminant` @@ -794,7 +799,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) - | ty::Bound(..) => bug!( + | ty::Bound(..) => panic!( "unexpected self ty `{:?}` when normalizing `::Discriminant`", goal.predicate.self_ty() ), @@ -807,11 +812,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_async_destruct_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); - let async_destructor_ty = match *self_ty.kind() { + let async_destructor_ty = match self_ty.kind() { ty::Bool | ty::Char | ty::Int(..) @@ -830,7 +835,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { | ty::Str | ty::Slice(_) | ty::Tuple(_) - | ty::Error(_) => self_ty.async_destructor_ty(ecx.tcx(), goal.param_env), + | ty::Error(_) => self_ty.async_destructor_ty(ecx.interner()), // We do not call `Ty::async_destructor_ty` on alias, param, or placeholder // types, which return `::AsyncDestructor` @@ -842,12 +847,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) | ty::Foreign(..) - | ty::Bound(..) => bug!( + | ty::Bound(..) => panic!( "unexpected self ty `{:?}` when normalizing `::AsyncDestructor`", goal.predicate.self_ty() ), - ty::Pat(..) | ty::Dynamic(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) => bug!( + ty::Pat(..) | ty::Dynamic(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) => panic!( "`consider_builtin_async_destruct_candidate` is not yet implemented for type: {self_ty:?}" ), }; @@ -860,51 +865,56 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } fn consider_builtin_destruct_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { - bug!("`Destruct` does not have an associated type: {:?}", goal); + _ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { + panic!("`Destruct` does not have an associated type: {:?}", goal); } fn consider_builtin_transmute_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { - bug!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal) + _ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { + panic!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal) } } -/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. -/// -/// FIXME: We should merge these 3 implementations as it's likely that they otherwise -/// diverge. -#[instrument(level = "trace", skip(ecx, param_env), ret)] -fn fetch_eligible_assoc_item_def<'tcx>( - ecx: &EvalCtxt<'_, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - goal_trait_ref: ty::TraitRef<'tcx>, - trait_assoc_def_id: DefId, - impl_def_id: DefId, -) -> Result, NoSolution> { - let node_item = specialization_graph::assoc_def(ecx.tcx(), impl_def_id, trait_assoc_def_id) - .map_err(|ErrorGuaranteed { .. }| NoSolution)?; - - let eligible = if node_item.is_final() { - // Non-specializable items are always projectable. - true - } else { - // Only reveal a specializable default if we're past type-checking - // and the obligation is monomorphic, otherwise passes such as - // transmute checking and polymorphic MIR optimizations could - // get a result which isn't correct for all monomorphizations. - if param_env.reveal() == Reveal::All { - let poly_trait_ref = ecx.resolve_vars_if_possible(goal_trait_ref); - !poly_trait_ref.still_further_specializable() +impl EvalCtxt<'_, Infcx> +where + Infcx: SolverDelegate, + I: Interner, +{ + fn translate_args( + &mut self, + goal: Goal>, + impl_def_id: I::DefId, + impl_args: I::GenericArgs, + impl_trait_ref: rustc_type_ir::TraitRef, + target_container_def_id: I::DefId, + ) -> Result { + let tcx = self.interner(); + Ok(if target_container_def_id == impl_trait_ref.def_id { + // Default value from the trait definition. No need to rebase. + goal.predicate.alias.args + } else if target_container_def_id == impl_def_id { + // Same impl, no need to fully translate, just a rebase from + // the trait is sufficient. + goal.predicate.alias.args.rebase_onto(tcx, impl_trait_ref.def_id, impl_args) } else { - trace!(?node_item.item.def_id, "not eligible due to default"); - false - } - }; - - if eligible { Ok(Some(node_item)) } else { Ok(None) } + let target_args = self.fresh_args_for_item(target_container_def_id); + let target_trait_ref = + tcx.impl_trait_ref(target_container_def_id).instantiate(tcx, &target_args); + // Relate source impl to target impl by equating trait refs. + self.eq(goal.param_env, impl_trait_ref, target_trait_ref)?; + // Also add predicates since they may be needed to constrain the + // target impl's params. + self.add_goals( + GoalSource::Misc, + tcx.predicates_of(target_container_def_id) + .iter_instantiated(tcx, &target_args) + .map(|pred| goal.with(tcx, pred)), + ); + goal.predicate.alias.args.rebase_onto(tcx, impl_trait_ref.def_id, target_args) + }) + } } diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs similarity index 61% rename from compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs rename to compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index 9fdb280cdc6d..710671b45d0e 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -1,22 +1,26 @@ //! Computes a normalizes-to (projection) goal for opaque types. This goal //! behaves differently depending on the param-env's reveal mode and whether //! the opaque is in a defining scope. -use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; -use rustc_middle::traits::Reveal; -use rustc_middle::ty; -use rustc_middle::ty::util::NotUniqueParam; -use crate::solve::{EvalCtxt, SolverMode}; +use rustc_index::bit_set::GrowableBitSet; +use rustc_type_ir::inherent::*; +use rustc_type_ir::{self as ty, Interner}; -impl<'tcx> EvalCtxt<'_, 'tcx> { +use crate::infcx::SolverDelegate; +use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, QueryResult, Reveal, SolverMode}; + +impl EvalCtxt<'_, Infcx> +where + Infcx: SolverDelegate, + I: Interner, +{ pub(super) fn normalize_opaque_type( &mut self, - goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, - ) -> QueryResult<'tcx> { - let tcx = self.tcx(); + goal: Goal>, + ) -> QueryResult { + let tcx = self.interner(); let opaque_ty = goal.predicate.alias; - let expected = goal.predicate.term.ty().expect("no such thing as an opaque const"); + let expected = goal.predicate.term.as_type().expect("no such thing as an opaque const"); match (goal.param_env.reveal(), self.solver_mode()) { (Reveal::UserFacing, SolverMode::Normal) => { @@ -30,7 +34,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return Err(NoSolution); } // FIXME: This may have issues when the args contain aliases... - match self.tcx().uses_unique_placeholders_ignoring_regions(opaque_ty.args) { + match uses_unique_placeholders_ignoring_regions(self.interner(), opaque_ty.args) { Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => { return self.evaluate_added_goals_and_make_canonical_response( Certainty::AMBIGUOUS, @@ -59,6 +63,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } // Otherwise, define a new opaque type + // FIXME: should we use `inject_hidden_type_unchecked` here? self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?; self.add_item_bounds_for_hidden_type( opaque_ty.def_id, @@ -81,10 +86,51 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } (Reveal::All, _) => { // FIXME: Add an assertion that opaque type storage is empty. - let actual = tcx.type_of(opaque_ty.def_id).instantiate(tcx, opaque_ty.args); + let actual = tcx.type_of(opaque_ty.def_id).instantiate(tcx, &opaque_ty.args); self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } } } + +/// Checks whether each generic argument is simply a unique generic placeholder. +/// +/// FIXME: Interner argument is needed to constrain the `I` parameter. +pub fn uses_unique_placeholders_ignoring_regions( + _interner: I, + args: I::GenericArgs, +) -> Result<(), NotUniqueParam> { + let mut seen = GrowableBitSet::default(); + for arg in args { + match arg.kind() { + // Ignore regions, since we can't resolve those in a canonicalized + // query in the trait solver. + ty::GenericArgKind::Lifetime(_) => {} + ty::GenericArgKind::Type(t) => match t.kind() { + ty::Placeholder(p) => { + if !seen.insert(p.var()) { + return Err(NotUniqueParam::DuplicateParam(t.into())); + } + } + _ => return Err(NotUniqueParam::NotParam(t.into())), + }, + ty::GenericArgKind::Const(c) => match c.kind() { + ty::ConstKind::Placeholder(p) => { + if !seen.insert(p.var()) { + return Err(NotUniqueParam::DuplicateParam(c.into())); + } + } + _ => return Err(NotUniqueParam::NotParam(c.into())), + }, + } + } + + Ok(()) +} + +// FIXME: This should check for dupes and non-params first, then infer vars. +pub enum NotUniqueParam { + DuplicateParam(I::GenericArg), + NotParam(I::GenericArg), +} diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs similarity index 65% rename from compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs rename to compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs index 13af5068b6c9..45341917bb24 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs @@ -3,30 +3,33 @@ //! //! Since a weak alias is never ambiguous, this just computes the `type_of` of //! the alias and registers the where-clauses of the type alias. -use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, QueryResult}; -use rustc_middle::ty; -use crate::solve::EvalCtxt; +use rustc_type_ir::{self as ty, Interner}; -impl<'tcx> EvalCtxt<'_, 'tcx> { +use crate::infcx::SolverDelegate; +use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult}; + +impl EvalCtxt<'_, Infcx> +where + Infcx: SolverDelegate, + I: Interner, +{ pub(super) fn normalize_weak_type( &mut self, - goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, - ) -> QueryResult<'tcx> { - let tcx = self.tcx(); + goal: Goal>, + ) -> QueryResult { + let tcx = self.interner(); let weak_ty = goal.predicate.alias; // Check where clauses self.add_goals( GoalSource::Misc, tcx.predicates_of(weak_ty.def_id) - .instantiate(tcx, weak_ty.args) - .predicates - .into_iter() + .iter_instantiated(tcx, &weak_ty.args) .map(|pred| goal.with(tcx, pred)), ); - let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args); + let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, &weak_ty.args); self.instantiate_normalizes_to_term(goal, actual.into()); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals.rs similarity index 58% rename from compiler/rustc_trait_selection/src/solve/project_goals.rs rename to compiler/rustc_next_trait_solver/src/solve/project_goals.rs index 0f1be1072a8f..4bb1fe5be6f0 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals.rs @@ -1,16 +1,20 @@ -use crate::solve::GoalSource; +use rustc_type_ir::{self as ty, Interner, ProjectionPredicate}; +use tracing::instrument; -use super::EvalCtxt; -use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; -use rustc_middle::ty::{self, ProjectionPredicate}; +use crate::infcx::SolverDelegate; +use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult}; -impl<'tcx> EvalCtxt<'_, 'tcx> { +impl EvalCtxt<'_, Infcx> +where + Infcx: SolverDelegate, + I: Interner, +{ #[instrument(level = "trace", skip(self), ret)] pub(super) fn compute_projection_goal( &mut self, - goal: Goal<'tcx, ProjectionPredicate<'tcx>>, - ) -> QueryResult<'tcx> { - let tcx = self.tcx(); + goal: Goal>, + ) -> QueryResult { + let tcx = self.interner(); let projection_term = goal.predicate.projection_term.to_term(tcx); let goal = goal.with( tcx, diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs new file mode 100644 index 000000000000..b923a121d814 --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -0,0 +1,604 @@ +use std::mem; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_index::{Idx, IndexVec}; +use rustc_type_ir::inherent::*; +use rustc_type_ir::Interner; +use tracing::debug; + +use crate::infcx::SolverDelegate; +use crate::solve::inspect::{self, ProofTreeBuilder}; +use crate::solve::{ + CacheData, CanonicalInput, Certainty, QueryResult, SolverMode, FIXPOINT_STEP_LIMIT, +}; + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct SolverLimit(usize); + +rustc_index::newtype_index! { + #[orderable] + pub struct StackDepth {} +} + +bitflags::bitflags! { + /// Whether and how this goal has been used as the root of a + /// cycle. We track the kind of cycle as we're otherwise forced + /// to always rerun at least once. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + struct HasBeenUsed: u8 { + const INDUCTIVE_CYCLE = 1 << 0; + const COINDUCTIVE_CYCLE = 1 << 1; + } +} + +#[derive(derivative::Derivative)] +#[derivative(Debug(bound = ""))] +struct StackEntry { + input: CanonicalInput, + + available_depth: SolverLimit, + + /// The maximum depth reached by this stack entry, only up-to date + /// for the top of the stack and lazily updated for the rest. + reached_depth: StackDepth, + + /// Whether this entry is a non-root cycle participant. + /// + /// We must not move the result of non-root cycle participants to the + /// global cache. We store the highest stack depth of a head of a cycle + /// this goal is involved in. This necessary to soundly cache its + /// provisional result. + non_root_cycle_participant: Option, + + encountered_overflow: bool, + + has_been_used: HasBeenUsed, + + /// We put only the root goal of a coinductive cycle into the global cache. + /// + /// If we were to use that result when later trying to prove another cycle + /// participant, we can end up with unstable query results. + /// + /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for + /// an example of where this is needed. + /// + /// There can be multiple roots on the same stack, so we need to track + /// cycle participants per root: + /// ```plain + /// A :- B + /// B :- A, C + /// C :- D + /// D :- C + /// ``` + cycle_participants: FxHashSet>, + /// Starts out as `None` and gets set when rerunning this + /// goal in case we encounter a cycle. + provisional_result: Option>, +} + +/// The provisional result for a goal which is not on the stack. +#[derive(Debug)] +struct DetachedEntry { + /// The head of the smallest non-trivial cycle involving this entry. + /// + /// Given the following rules, when proving `A` the head for + /// the provisional entry of `C` would be `B`. + /// ```plain + /// A :- B + /// B :- C + /// C :- A + B + C + /// ``` + head: StackDepth, + result: QueryResult, +} + +/// Stores the stack depth of a currently evaluated goal *and* already +/// computed results for goals which depend on other goals still on the stack. +/// +/// The provisional result may depend on whether the stack above it is inductive +/// or coinductive. Because of this, we store separate provisional results for +/// each case. If an provisional entry is not applicable, it may be the case +/// that we already have provisional result while computing a goal. In this case +/// we prefer the provisional result to potentially avoid fixpoint iterations. +/// See tests/ui/traits/next-solver/cycles/mixed-cycles-2.rs for an example. +/// +/// The provisional cache can theoretically result in changes to the observable behavior, +/// see tests/ui/traits/next-solver/cycles/provisional-cache-impacts-behavior.rs. +#[derive(derivative::Derivative)] +#[derivative(Default(bound = ""))] +struct ProvisionalCacheEntry { + stack_depth: Option, + with_inductive_stack: Option>, + with_coinductive_stack: Option>, +} + +impl ProvisionalCacheEntry { + fn is_empty(&self) -> bool { + self.stack_depth.is_none() + && self.with_inductive_stack.is_none() + && self.with_coinductive_stack.is_none() + } +} + +pub(super) struct SearchGraph { + mode: SolverMode, + /// The stack of goals currently being computed. + /// + /// An element is *deeper* in the stack if its index is *lower*. + stack: IndexVec>, + provisional_cache: FxHashMap, ProvisionalCacheEntry>, +} + +impl SearchGraph { + pub(super) fn new(mode: SolverMode) -> SearchGraph { + Self { mode, stack: Default::default(), provisional_cache: Default::default() } + } + + pub(super) fn solver_mode(&self) -> SolverMode { + self.mode + } + + /// Pops the highest goal from the stack, lazily updating the + /// the next goal in the stack. + /// + /// Directly popping from the stack instead of using this method + /// would cause us to not track overflow and recursion depth correctly. + fn pop_stack(&mut self) -> StackEntry { + let elem = self.stack.pop().unwrap(); + if let Some(last) = self.stack.raw.last_mut() { + last.reached_depth = last.reached_depth.max(elem.reached_depth); + last.encountered_overflow |= elem.encountered_overflow; + } + elem + } + + pub(super) fn is_empty(&self) -> bool { + self.stack.is_empty() + } + + /// Returns the remaining depth allowed for nested goals. + /// + /// This is generally simply one less than the current depth. + /// However, if we encountered overflow, we significantly reduce + /// the remaining depth of all nested goals to prevent hangs + /// in case there is exponential blowup. + fn allowed_depth_for_nested( + tcx: I, + stack: &IndexVec>, + ) -> Option { + if let Some(last) = stack.raw.last() { + if last.available_depth.0 == 0 { + return None; + } + + Some(if last.encountered_overflow { + SolverLimit(last.available_depth.0 / 4) + } else { + SolverLimit(last.available_depth.0 - 1) + }) + } else { + Some(SolverLimit(tcx.recursion_limit())) + } + } + + fn stack_coinductive_from( + tcx: I, + stack: &IndexVec>, + head: StackDepth, + ) -> bool { + stack.raw[head.index()..] + .iter() + .all(|entry| entry.input.value.goal.predicate.is_coinductive(tcx)) + } + + // When encountering a solver cycle, the result of the current goal + // depends on goals lower on the stack. + // + // We have to therefore be careful when caching goals. Only the final result + // of the cycle root, i.e. the lowest goal on the stack involved in this cycle, + // is moved to the global cache while all others are stored in a provisional cache. + // + // We update both the head of this cycle to rerun its evaluation until + // we reach a fixpoint and all other cycle participants to make sure that + // their result does not get moved to the global cache. + fn tag_cycle_participants( + stack: &mut IndexVec>, + usage_kind: HasBeenUsed, + head: StackDepth, + ) { + stack[head].has_been_used |= usage_kind; + debug_assert!(!stack[head].has_been_used.is_empty()); + + // The current root of these cycles. Note that this may not be the final + // root in case a later goal depends on a goal higher up the stack. + let mut current_root = head; + while let Some(parent) = stack[current_root].non_root_cycle_participant { + current_root = parent; + debug_assert!(!stack[current_root].has_been_used.is_empty()); + } + + let (stack, cycle_participants) = stack.raw.split_at_mut(head.index() + 1); + let current_cycle_root = &mut stack[current_root.as_usize()]; + for entry in cycle_participants { + entry.non_root_cycle_participant = entry.non_root_cycle_participant.max(Some(head)); + current_cycle_root.cycle_participants.insert(entry.input); + current_cycle_root.cycle_participants.extend(mem::take(&mut entry.cycle_participants)); + } + } + + fn clear_dependent_provisional_results( + provisional_cache: &mut FxHashMap, ProvisionalCacheEntry>, + head: StackDepth, + ) { + #[allow(rustc::potential_query_instability)] + provisional_cache.retain(|_, entry| { + entry.with_coinductive_stack.take_if(|p| p.head == head); + entry.with_inductive_stack.take_if(|p| p.head == head); + !entry.is_empty() + }); + } + + /// The trait solver behavior is different for coherence + /// so we use a separate cache. Alternatively we could use + /// a single cache and share it between coherence and ordinary + /// trait solving. + pub(super) fn global_cache(&self, tcx: I) -> I::EvaluationCache { + tcx.evaluation_cache(self.mode) + } + + /// Probably the most involved method of the whole solver. + /// + /// Given some goal which is proven via the `prove_goal` closure, this + /// handles caching, overflow, and coinductive cycles. + pub(super) fn with_new_goal>( + &mut self, + tcx: I, + input: CanonicalInput, + inspect: &mut ProofTreeBuilder, + mut prove_goal: impl FnMut(&mut Self, &mut ProofTreeBuilder) -> QueryResult, + ) -> QueryResult { + self.check_invariants(); + // Check for overflow. + let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else { + if let Some(last) = self.stack.raw.last_mut() { + last.encountered_overflow = true; + } + + inspect + .canonical_goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow); + return Self::response_no_constraints(tcx, input, Certainty::overflow(true)); + }; + + if let Some(result) = self.lookup_global_cache(tcx, input, available_depth, inspect) { + debug!("global cache hit"); + return result; + } + + // Check whether the goal is in the provisional cache. + // The provisional result may rely on the path to its cycle roots, + // so we have to check the path of the current goal matches that of + // the cache entry. + let cache_entry = self.provisional_cache.entry(input).or_default(); + if let Some(entry) = cache_entry + .with_coinductive_stack + .as_ref() + .filter(|p| Self::stack_coinductive_from(tcx, &self.stack, p.head)) + .or_else(|| { + cache_entry + .with_inductive_stack + .as_ref() + .filter(|p| !Self::stack_coinductive_from(tcx, &self.stack, p.head)) + }) + { + debug!("provisional cache hit"); + // We have a nested goal which is already in the provisional cache, use + // its result. We do not provide any usage kind as that should have been + // already set correctly while computing the cache entry. + inspect.canonical_goal_evaluation_kind( + inspect::WipCanonicalGoalEvaluationKind::ProvisionalCacheHit, + ); + Self::tag_cycle_participants(&mut self.stack, HasBeenUsed::empty(), entry.head); + return entry.result; + } else if let Some(stack_depth) = cache_entry.stack_depth { + debug!("encountered cycle with depth {stack_depth:?}"); + // We have a nested goal which directly relies on a goal deeper in the stack. + // + // We start by tagging all cycle participants, as that's necessary for caching. + // + // Finally we can return either the provisional response or the initial response + // in case we're in the first fixpoint iteration for this goal. + inspect.canonical_goal_evaluation_kind( + inspect::WipCanonicalGoalEvaluationKind::CycleInStack, + ); + let is_coinductive_cycle = Self::stack_coinductive_from(tcx, &self.stack, stack_depth); + let usage_kind = if is_coinductive_cycle { + HasBeenUsed::COINDUCTIVE_CYCLE + } else { + HasBeenUsed::INDUCTIVE_CYCLE + }; + Self::tag_cycle_participants(&mut self.stack, usage_kind, stack_depth); + + // Return the provisional result or, if we're in the first iteration, + // start with no constraints. + return if let Some(result) = self.stack[stack_depth].provisional_result { + result + } else if is_coinductive_cycle { + Self::response_no_constraints(tcx, input, Certainty::Yes) + } else { + Self::response_no_constraints(tcx, input, Certainty::overflow(false)) + }; + } else { + // No entry, we push this goal on the stack and try to prove it. + let depth = self.stack.next_index(); + let entry = StackEntry { + input, + available_depth, + reached_depth: depth, + non_root_cycle_participant: None, + encountered_overflow: false, + has_been_used: HasBeenUsed::empty(), + cycle_participants: Default::default(), + provisional_result: None, + }; + assert_eq!(self.stack.push(entry), depth); + cache_entry.stack_depth = Some(depth); + } + + // This is for global caching, so we properly track query dependencies. + // Everything that affects the `result` should be performed within this + // `with_anon_task` closure. If computing this goal depends on something + // not tracked by the cache key and from outside of this anon task, it + // must not be added to the global cache. Notably, this is the case for + // trait solver cycles participants. + let ((final_entry, result), dep_node) = tcx.with_cached_task(|| { + for _ in 0..FIXPOINT_STEP_LIMIT { + match self.fixpoint_step_in_task(tcx, input, inspect, &mut prove_goal) { + StepResult::Done(final_entry, result) => return (final_entry, result), + StepResult::HasChanged => debug!("fixpoint changed provisional results"), + } + } + + debug!("canonical cycle overflow"); + let current_entry = self.pop_stack(); + debug_assert!(current_entry.has_been_used.is_empty()); + let result = Self::response_no_constraints(tcx, input, Certainty::overflow(false)); + (current_entry, result) + }); + + let proof_tree = inspect.finalize_canonical_goal_evaluation(tcx); + + // We're now done with this goal. In case this goal is involved in a larger cycle + // do not remove it from the provisional cache and update its provisional result. + // We only add the root of cycles to the global cache. + if let Some(head) = final_entry.non_root_cycle_participant { + let coinductive_stack = Self::stack_coinductive_from(tcx, &self.stack, head); + + let entry = self.provisional_cache.get_mut(&input).unwrap(); + entry.stack_depth = None; + if coinductive_stack { + entry.with_coinductive_stack = Some(DetachedEntry { head, result }); + } else { + entry.with_inductive_stack = Some(DetachedEntry { head, result }); + } + } else { + self.provisional_cache.remove(&input); + let reached_depth = final_entry.reached_depth.as_usize() - self.stack.len(); + // When encountering a cycle, both inductive and coinductive, we only + // move the root into the global cache. We also store all other cycle + // participants involved. + // + // We must not use the global cache entry of a root goal if a cycle + // participant is on the stack. This is necessary to prevent unstable + // results. See the comment of `StackEntry::cycle_participants` for + // more details. + self.global_cache(tcx).insert( + tcx, + input, + proof_tree, + reached_depth, + final_entry.encountered_overflow, + final_entry.cycle_participants, + dep_node, + result, + ) + } + + self.check_invariants(); + + result + } + + /// Try to fetch a previously computed result from the global cache, + /// making sure to only do so if it would match the result of reevaluating + /// this goal. + fn lookup_global_cache>( + &mut self, + tcx: I, + input: CanonicalInput, + available_depth: SolverLimit, + inspect: &mut ProofTreeBuilder, + ) -> Option> { + let CacheData { result, proof_tree, additional_depth, encountered_overflow } = self + .global_cache(tcx) + // FIXME: Awkward `Limit -> usize -> Limit`. + .get(tcx, input, self.stack.iter().map(|e| e.input), available_depth.0)?; + + // If we're building a proof tree and the current cache entry does not + // contain a proof tree, we do not use the entry but instead recompute + // the goal. We simply overwrite the existing entry once we're done, + // caching the proof tree. + if !inspect.is_noop() { + if let Some(final_revision) = proof_tree { + let kind = inspect::WipCanonicalGoalEvaluationKind::Interned { final_revision }; + inspect.canonical_goal_evaluation_kind(kind); + } else { + return None; + } + } + + // Update the reached depth of the current goal to make sure + // its state is the same regardless of whether we've used the + // global cache or not. + let reached_depth = self.stack.next_index().plus(additional_depth); + if let Some(last) = self.stack.raw.last_mut() { + last.reached_depth = last.reached_depth.max(reached_depth); + last.encountered_overflow |= encountered_overflow; + } + + Some(result) + } +} + +enum StepResult { + Done(StackEntry, QueryResult), + HasChanged, +} + +impl SearchGraph { + /// When we encounter a coinductive cycle, we have to fetch the + /// result of that cycle while we are still computing it. Because + /// of this we continuously recompute the cycle until the result + /// of the previous iteration is equal to the final result, at which + /// point we are done. + fn fixpoint_step_in_task( + &mut self, + tcx: I, + input: CanonicalInput, + inspect: &mut ProofTreeBuilder, + prove_goal: &mut F, + ) -> StepResult + where + Infcx: SolverDelegate, + F: FnMut(&mut Self, &mut ProofTreeBuilder) -> QueryResult, + { + let result = prove_goal(self, inspect); + let stack_entry = self.pop_stack(); + debug_assert_eq!(stack_entry.input, input); + + // If the current goal is not the root of a cycle, we are done. + if stack_entry.has_been_used.is_empty() { + return StepResult::Done(stack_entry, result); + } + + // If it is a cycle head, we have to keep trying to prove it until + // we reach a fixpoint. We need to do so for all cycle heads, + // not only for the root. + // + // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs + // for an example. + + // Start by clearing all provisional cache entries which depend on this + // the current goal. + Self::clear_dependent_provisional_results( + &mut self.provisional_cache, + self.stack.next_index(), + ); + + // Check whether we reached a fixpoint, either because the final result + // is equal to the provisional result of the previous iteration, or because + // this was only the root of either coinductive or inductive cycles, and the + // final result is equal to the initial response for that case. + let reached_fixpoint = if let Some(r) = stack_entry.provisional_result { + r == result + } else if stack_entry.has_been_used == HasBeenUsed::COINDUCTIVE_CYCLE { + Self::response_no_constraints(tcx, input, Certainty::Yes) == result + } else if stack_entry.has_been_used == HasBeenUsed::INDUCTIVE_CYCLE { + Self::response_no_constraints(tcx, input, Certainty::overflow(false)) == result + } else { + false + }; + + // If we did not reach a fixpoint, update the provisional result and reevaluate. + if reached_fixpoint { + StepResult::Done(stack_entry, result) + } else { + let depth = self.stack.push(StackEntry { + has_been_used: HasBeenUsed::empty(), + provisional_result: Some(result), + ..stack_entry + }); + debug_assert_eq!(self.provisional_cache[&input].stack_depth, Some(depth)); + StepResult::HasChanged + } + } + + fn response_no_constraints( + tcx: I, + goal: CanonicalInput, + certainty: Certainty, + ) -> QueryResult { + Ok(super::response_no_constraints_raw(tcx, goal.max_universe, goal.variables, certainty)) + } + + #[allow(rustc::potential_query_instability)] + fn check_invariants(&self) { + if !cfg!(debug_assertions) { + return; + } + + let SearchGraph { mode: _, stack, provisional_cache } = self; + if stack.is_empty() { + assert!(provisional_cache.is_empty()); + } + + for (depth, entry) in stack.iter_enumerated() { + let StackEntry { + input, + available_depth: _, + reached_depth: _, + non_root_cycle_participant, + encountered_overflow: _, + has_been_used, + ref cycle_participants, + provisional_result, + } = *entry; + let cache_entry = provisional_cache.get(&entry.input).unwrap(); + assert_eq!(cache_entry.stack_depth, Some(depth)); + if let Some(head) = non_root_cycle_participant { + assert!(head < depth); + assert!(cycle_participants.is_empty()); + assert_ne!(stack[head].has_been_used, HasBeenUsed::empty()); + + let mut current_root = head; + while let Some(parent) = stack[current_root].non_root_cycle_participant { + current_root = parent; + } + assert!(stack[current_root].cycle_participants.contains(&input)); + } + + if !cycle_participants.is_empty() { + assert!(provisional_result.is_some() || !has_been_used.is_empty()); + for entry in stack.iter().take(depth.as_usize()) { + assert_eq!(cycle_participants.get(&entry.input), None); + } + } + } + + for (&input, entry) in &self.provisional_cache { + let ProvisionalCacheEntry { stack_depth, with_coinductive_stack, with_inductive_stack } = + entry; + assert!( + stack_depth.is_some() + || with_coinductive_stack.is_some() + || with_inductive_stack.is_some() + ); + + if let &Some(stack_depth) = stack_depth { + assert_eq!(stack[stack_depth].input, input); + } + + let check_detached = |detached_entry: &DetachedEntry| { + let DetachedEntry { head, result: _ } = *detached_entry; + assert_ne!(stack[head].has_been_used, HasBeenUsed::empty()); + }; + + if let Some(with_coinductive_stack) = with_coinductive_stack { + check_detached(with_coinductive_stack); + } + + if let Some(with_inductive_stack) = with_inductive_stack { + check_detached(with_inductive_stack); + } + } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs similarity index 74% rename from compiler/rustc_trait_selection/src/solve/trait_goals.rs rename to compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 9139c75d3999..d1419bf5db9a 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1,60 +1,60 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. -use crate::traits::supertrait_def_ids; - -use super::assembly::structural_traits::AsyncCallableRelevantTypes; -use super::assembly::{self, structural_traits, Candidate}; -use super::{EvalCtxt, GoalSource, SolverMode}; +use rustc_ast_ir::Movability; use rustc_data_structures::fx::FxIndexSet; -use rustc_hir::def_id::DefId; -use rustc_hir::{LangItem, Movability}; -use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::solve::MaybeCause; -use rustc_middle::bug; -use rustc_middle::traits::solve::inspect::ProbeKind; -use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal, QueryResult}; -use rustc_middle::traits::{BuiltinImplSource, Reveal}; -use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; -use rustc_middle::ty::{self, Ty, TyCtxt, Upcast}; -use rustc_middle::ty::{TraitPredicate, TypeVisitableExt}; -use rustc_span::ErrorGuaranteed; +use rustc_type_ir::inherent::*; +use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::visit::TypeVisitableExt as _; +use rustc_type_ir::{self as ty, Interner, TraitPredicate, Upcast as _}; +use tracing::{instrument, trace}; -impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { - fn self_ty(self) -> Ty<'tcx> { +use crate::infcx::SolverDelegate; +use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes}; +use crate::solve::assembly::{self, Candidate}; +use crate::solve::inspect::ProbeKind; +use crate::solve::{ + BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, + NoSolution, QueryResult, Reveal, SolverMode, +}; + +impl assembly::GoalKind for TraitPredicate +where + Infcx: SolverDelegate, + I: Interner, +{ + fn self_ty(self) -> I::Ty { self.self_ty() } - fn trait_ref(self, _: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> { + fn trait_ref(self, _: I) -> ty::TraitRef { self.trait_ref } - fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { + fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self { self.with_self_ty(tcx, self_ty) } - fn trait_def_id(self, _: TyCtxt<'tcx>) -> DefId { + fn trait_def_id(self, _: I) -> I::DefId { self.def_id() } fn consider_impl_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, TraitPredicate<'tcx>>, - impl_def_id: DefId, - ) -> Result, NoSolution> { - let tcx = ecx.tcx(); + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal>, + impl_def_id: I::DefId, + ) -> Result, NoSolution> { + let tcx = ecx.interner(); - let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap(); - let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; - if !drcx.args_may_unify( - goal.predicate.trait_ref.args, - impl_trait_header.trait_ref.skip_binder().args, - ) { + let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); + if !tcx + .args_may_unify_deep(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args) + { return Err(NoSolution); } // An upper bound of the certainty of this goal, used to lower the certainty // of reservation impl to ambiguous during coherence. - let impl_polarity = impl_trait_header.polarity; + let impl_polarity = tcx.impl_polarity(impl_def_id); let maximal_certainty = match (impl_polarity, goal.predicate.polarity) { // In intercrate mode, this is ambiguous. But outside of intercrate, // it's not a real impl. @@ -77,14 +77,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { let impl_args = ecx.fresh_args_for_item(impl_def_id); ecx.record_impl_args(impl_args); - let impl_trait_ref = impl_trait_header.trait_ref.instantiate(tcx, impl_args); + let impl_trait_ref = impl_trait_ref.instantiate(tcx, &impl_args); ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?; let where_clause_bounds = tcx .predicates_of(impl_def_id) - .instantiate(tcx, impl_args) - .predicates - .into_iter() + .iter_instantiated(tcx, &impl_args) .map(|pred| goal.with(tcx, pred)); ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); @@ -93,21 +91,21 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_error_guaranteed_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - _guar: ErrorGuaranteed, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + _guar: I::ErrorGuaranteed, + ) -> Result, NoSolution> { // FIXME: don't need to enter a probe here. ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } fn probe_and_match_goal_against_assumption( - ecx: &mut EvalCtxt<'_, 'tcx>, - source: CandidateSource<'tcx>, - goal: Goal<'tcx, Self>, - assumption: ty::Clause<'tcx>, - then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + source: CandidateSource, + goal: Goal, + assumption: I::Clause, + then: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> QueryResult, + ) -> Result, NoSolution> { if let Some(trait_clause) = assumption.as_trait_clause() { if trait_clause.def_id() == goal.predicate.def_id() && trait_clause.polarity() == goal.predicate.polarity @@ -130,9 +128,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_auto_trait_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -159,7 +157,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() { if matches!(goal.param_env.reveal(), Reveal::All) || matches!(ecx.solver_mode(), SolverMode::Coherence) - || ecx.can_define_opaque_ty(opaque_ty.def_id) + || opaque_ty + .def_id + .as_local() + .is_some_and(|def_id| ecx.can_define_opaque_ty(def_id)) { return Err(NoSolution); } @@ -173,32 +174,30 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_trait_alias_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } - let tcx = ecx.tcx(); + let tcx = ecx.interner(); ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { let nested_obligations = tcx .predicates_of(goal.predicate.def_id()) - .instantiate(tcx, goal.predicate.trait_ref.args); + .iter_instantiated(tcx, &goal.predicate.trait_ref.args) + .map(|p| goal.with(tcx, p)); // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? - ecx.add_goals( - GoalSource::Misc, - nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)), - ); + ecx.add_goals(GoalSource::Misc, nested_obligations); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } fn consider_builtin_sized_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -211,9 +210,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_copy_clone_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -226,28 +225,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_pointer_like_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } - // The regions of a type don't affect the size of the type - let tcx = ecx.tcx(); - // We should erase regions from both the param-env and type, since both - // may have infer regions. Specifically, after canonicalizing and instantiating, - // early bound regions turn into region vars in both the new and old solver. - let key = tcx.erase_regions(goal.param_env.and(goal.predicate.self_ty())); + let tcx = ecx.interner(); // But if there are inference variables, we have to wait until it's resolved. - if key.has_non_region_infer() { + if (goal.param_env, goal.predicate.self_ty()).has_non_region_infer() { return ecx.forced_ambiguity(MaybeCause::Ambiguity); } - if let Ok(layout) = tcx.layout_of(key) - && layout.layout.is_pointer_like(&tcx.data_layout) - { - // FIXME: We could make this faster by making a no-constraints response + if tcx.layout_is_pointer_like(goal.param_env, goal.predicate.self_ty()) { ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } else { @@ -256,9 +247,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_fn_ptr_trait_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); match goal.predicate.polarity { // impl FnPtr for FnPtr {} @@ -287,15 +278,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_fn_trait_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, goal_kind: ty::ClosureKind, - ) -> Result, NoSolution> { + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } - let tcx = ecx.tcx(); + let tcx = ecx.interner(); let tupled_inputs_and_output = match structural_traits::extract_tupled_inputs_and_output_from_callable( tcx, @@ -308,7 +299,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } }; let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| { - ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, None), [output]) + ty::TraitRef::new(tcx, tcx.require_lang_item(TraitSolverLangItem::Sized), [output]) }); let pred = tupled_inputs_and_output @@ -328,28 +319,28 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_async_fn_trait_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, goal_kind: ty::ClosureKind, - ) -> Result, NoSolution> { + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } - let tcx = ecx.tcx(); + let tcx = ecx.interner(); let (tupled_inputs_and_output_and_coroutine, nested_preds) = structural_traits::extract_tupled_inputs_and_output_from_async_callable( tcx, goal.predicate.self_ty(), goal_kind, // This region doesn't matter because we're throwing away the coroutine type - tcx.lifetimes.re_static, + Region::new_static(tcx), )?; let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound( |AsyncCallableRelevantTypes { output_coroutine_ty, .. }| { ty::TraitRef::new( tcx, - tcx.require_lang_item(LangItem::Sized, None), + tcx.require_lang_item(TraitSolverLangItem::Sized), [output_coroutine_ty], ) }, @@ -379,11 +370,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_async_fn_kind_helper_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { let [closure_fn_kind_ty, goal_kind_ty] = **goal.predicate.trait_ref.args else { - bug!(); + panic!(); }; let Some(closure_kind) = closure_fn_kind_ty.expect_ty().to_opt_closure_kind() else { @@ -406,9 +397,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { /// impl Tuple for (T1, .., Tn) {} /// ``` fn consider_builtin_tuple_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -422,9 +413,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_pointee_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -434,19 +425,19 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_future_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } - let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else { + let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else { return Err(NoSolution); }; // Coroutines are not futures unless they come from `async` desugaring - let tcx = ecx.tcx(); + let tcx = ecx.interner(); if !tcx.coroutine_is_async(def_id) { return Err(NoSolution); } @@ -460,19 +451,19 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } - let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else { + let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else { return Err(NoSolution); }; // Coroutines are not iterators unless they come from `gen` desugaring - let tcx = ecx.tcx(); + let tcx = ecx.interner(); if !tcx.coroutine_is_gen(def_id) { return Err(NoSolution); } @@ -486,19 +477,19 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_fused_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } - let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else { + let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else { return Err(NoSolution); }; // Coroutines are not iterators unless they come from `gen` desugaring - let tcx = ecx.tcx(); + let tcx = ecx.interner(); if !tcx.coroutine_is_gen(def_id) { return Err(NoSolution); } @@ -510,19 +501,19 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_async_iterator_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } - let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else { + let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else { return Err(NoSolution); }; // Coroutines are not iterators unless they come from `gen` desugaring - let tcx = ecx.tcx(); + let tcx = ecx.interner(); if !tcx.coroutine_is_async_gen(def_id) { return Err(NoSolution); } @@ -536,20 +527,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_coroutine_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } let self_ty = goal.predicate.self_ty(); - let ty::Coroutine(def_id, args) = *self_ty.kind() else { + let ty::Coroutine(def_id, args) = self_ty.kind() else { return Err(NoSolution); }; // `async`-desugared coroutines do not implement the coroutine trait - let tcx = ecx.tcx(); + let tcx = ecx.interner(); if !tcx.is_general_coroutine(def_id) { return Err(NoSolution); } @@ -568,9 +559,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_discriminant_kind_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -581,9 +572,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_async_destruct_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -594,9 +585,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_destruct_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -610,9 +601,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_builtin_transmute_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Result, NoSolution> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Result, NoSolution> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -622,22 +613,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { return Err(NoSolution); } - // Erase regions because we compute layouts in `rustc_transmute`, - // which will ICE for region vars. - let args = ecx.tcx().erase_regions(goal.predicate.trait_ref.args); - - let Some(assume) = - rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(2)) - else { - return Err(NoSolution); - }; - // FIXME: This actually should destructure the `Result` we get from transmutability and // register candiates. We probably need to register >1 since we may have an OR of ANDs. ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { let certainty = ecx.is_transmutable( - rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) }, - assume, + goal.param_env, + goal.predicate.trait_ref.args.type_at(0), + goal.predicate.trait_ref.args.type_at(1), + goal.predicate.trait_ref.args.const_at(2), )?; ecx.evaluate_added_goals_and_make_canonical_response(certainty) }) @@ -651,9 +634,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { /// impl<'a, T: Trait + 'a> Unsize for T {} /// ``` fn consider_structural_builtin_unsize_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Vec> { + ecx: &mut EvalCtxt<'_, Infcx>, + goal: Goal, + ) -> Vec> { if goal.predicate.polarity != ty::PredicatePolarity::Positive { return vec![]; } @@ -674,9 +657,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { return vec![]; }; - let goal = goal.with(ecx.tcx(), (a_ty, b_ty)); + let goal = goal.with(ecx.interner(), (a_ty, b_ty)); match (a_ty.kind(), b_ty.kind()) { - (ty::Infer(ty::TyVar(..)), ..) => bug!("unexpected infer {a_ty:?} {b_ty:?}"), + (ty::Infer(ty::TyVar(..)), ..) => panic!("unexpected infer {a_ty:?} {b_ty:?}"), (_, ty::Infer(ty::TyVar(..))) => { result_to_single(ecx.forced_ambiguity(MaybeCause::Ambiguity)) @@ -684,24 +667,24 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`. ( - &ty::Dynamic(a_data, a_region, ty::Dyn), - &ty::Dynamic(b_data, b_region, ty::Dyn), + ty::Dynamic(a_data, a_region, ty::Dyn), + ty::Dynamic(b_data, b_region, ty::Dyn), ) => ecx.consider_builtin_dyn_upcast_candidates( goal, a_data, a_region, b_data, b_region, ), // `T` -> `dyn Trait` unsizing. - (_, &ty::Dynamic(b_region, b_data, ty::Dyn)) => result_to_single( + (_, ty::Dynamic(b_region, b_data, ty::Dyn)) => result_to_single( ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data), ), // `[T; N]` -> `[T]` unsizing - (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { + (ty::Array(a_elem_ty, ..), ty::Slice(b_elem_ty)) => { result_to_single(ecx.consider_builtin_array_unsize(goal, a_elem_ty, b_elem_ty)) } // `Struct` -> `Struct` where `T: Unsize` - (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) + (ty::Adt(a_def, a_args), ty::Adt(b_def, b_args)) if a_def.is_struct() && a_def == b_def => { result_to_single( @@ -710,7 +693,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } // `(A, B, T)` -> `(A, B, U)` where `T: Unsize` - (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) + (ty::Tuple(a_tys), ty::Tuple(b_tys)) if a_tys.len() == b_tys.len() && !a_tys.is_empty() => { result_to_single(ecx.consider_builtin_tuple_unsize(goal, a_tys, b_tys)) @@ -722,7 +705,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } } -impl<'tcx> EvalCtxt<'_, 'tcx> { +impl EvalCtxt<'_, Infcx> +where + Infcx: SolverDelegate, + I: Interner, +{ /// Trait upcasting allows for coercions between trait objects: /// ```ignore (builtin impl example) /// trait Super {} @@ -734,13 +721,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// ``` fn consider_builtin_dyn_upcast_candidates( &mut self, - goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, - a_data: &'tcx ty::List>, - a_region: ty::Region<'tcx>, - b_data: &'tcx ty::List>, - b_region: ty::Region<'tcx>, - ) -> Vec> { - let tcx = self.tcx(); + goal: Goal, + a_data: I::BoundExistentialPredicates, + a_region: I::Region, + b_data: I::BoundExistentialPredicates, + b_region: I::Region, + ) -> Vec> { + let tcx = self.interner(); let Goal { predicate: (a_ty, _b_ty), .. } = goal; let mut responses = vec![]; @@ -757,24 +744,22 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { a_data.principal(), )); } else if let Some(a_principal) = a_data.principal() { - self.walk_vtable( - a_principal.with_self_ty(tcx, a_ty), - |ecx, new_a_principal, _, vtable_vptr_slot| { - responses.extend(ecx.consider_builtin_upcast_to_principal( - goal, - CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting { - vtable_vptr_slot, - }), - a_data, - a_region, - b_data, - b_region, - Some(new_a_principal.map_bound(|trait_ref| { - ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) - })), - )); - }, - ); + for new_a_principal in + Infcx::elaborate_supertraits(self.interner(), a_principal.with_self_ty(tcx, a_ty)) + .skip(1) + { + responses.extend(self.consider_builtin_upcast_to_principal( + goal, + CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting), + a_data, + a_region, + b_data, + b_region, + Some(new_a_principal.map_bound(|trait_ref| { + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + })), + )); + } } responses @@ -782,15 +767,15 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { fn consider_builtin_unsize_to_dyn_candidate( &mut self, - goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, - b_data: &'tcx ty::List>, - b_region: ty::Region<'tcx>, - ) -> Result, NoSolution> { - let tcx = self.tcx(); + goal: Goal, + b_data: I::BoundExistentialPredicates, + b_region: I::Region, + ) -> Result, NoSolution> { + let tcx = self.interner(); let Goal { predicate: (a_ty, _), .. } = goal; // Can only unsize to an object-safe trait. - if b_data.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) { + if b_data.principal_def_id().is_some_and(|def_id| !tcx.trait_is_object_safe(def_id)) { return Err(NoSolution); } @@ -799,18 +784,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // (i.e. the principal, all of the associated types match, and any auto traits) ecx.add_goals( GoalSource::ImplWhereBound, - b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))), + b_data.into_iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))), ); // The type must be `Sized` to be unsized. - if let Some(sized_def_id) = tcx.lang_items().sized_trait() { - ecx.add_goal( - GoalSource::ImplWhereBound, - goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])), - ); - } else { - return Err(NoSolution); - } + ecx.add_goal( + GoalSource::ImplWhereBound, + goal.with( + tcx, + ty::TraitRef::new( + tcx, + tcx.require_lang_item(TraitSolverLangItem::Sized), + [a_ty], + ), + ), + ); // The type must outlive the lifetime of the `dyn` we're unsizing into. ecx.add_goal(GoalSource::Misc, goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region))); @@ -820,24 +808,27 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { fn consider_builtin_upcast_to_principal( &mut self, - goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, - source: CandidateSource<'tcx>, - a_data: &'tcx ty::List>, - a_region: ty::Region<'tcx>, - b_data: &'tcx ty::List>, - b_region: ty::Region<'tcx>, - upcast_principal: Option>, - ) -> Result, NoSolution> { + goal: Goal, + source: CandidateSource, + a_data: I::BoundExistentialPredicates, + a_region: I::Region, + b_data: I::BoundExistentialPredicates, + b_region: I::Region, + upcast_principal: Option>>, + ) -> Result, NoSolution> { let param_env = goal.param_env; // We may upcast to auto traits that are either explicitly listed in // the object type's bounds, or implied by the principal trait ref's // supertraits. - let a_auto_traits: FxIndexSet = a_data + let a_auto_traits: FxIndexSet = a_data .auto_traits() + .into_iter() .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| { - supertrait_def_ids(self.tcx(), principal_def_id) - .filter(|def_id| self.tcx().trait_is_auto(*def_id)) + self.interner() + .supertrait_def_ids(principal_def_id) + .into_iter() + .filter(|def_id| self.interner().trait_is_auto(*def_id)) })) .collect(); @@ -846,9 +837,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // having any inference side-effects. We process obligations because // unification may initially succeed due to deferred projection equality. let projection_may_match = - |ecx: &mut EvalCtxt<'_, 'tcx>, - source_projection: ty::PolyExistentialProjection<'tcx>, - target_projection: ty::PolyExistentialProjection<'tcx>| { + |ecx: &mut EvalCtxt<'_, Infcx>, + source_projection: ty::Binder>, + target_projection: ty::Binder>| { source_projection.item_def_id() == target_projection.item_def_id() && ecx .probe(|_| ProbeKind::UpcastProjectionCompatibility) @@ -880,7 +871,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty::ExistentialPredicate::Projection(target_projection) => { let target_projection = bound.rebind(target_projection); let mut matching_projections = - a_data.projection_bounds().filter(|source_projection| { + a_data.projection_bounds().into_iter().filter(|source_projection| { projection_may_match(ecx, *source_projection, target_projection) }); let Some(source_projection) = matching_projections.next() else { @@ -905,11 +896,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // Also require that a_ty's lifetime outlives b_ty's lifetime. ecx.add_goal( GoalSource::ImplWhereBound, - Goal::new( - ecx.tcx(), - param_env, - ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), - ), + Goal::new(ecx.interner(), param_env, ty::OutlivesPredicate(a_region, b_region)), ); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) @@ -926,10 +913,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// `#[rustc_deny_explicit_impl]` in this case. fn consider_builtin_array_unsize( &mut self, - goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, - a_elem_ty: Ty<'tcx>, - b_elem_ty: Ty<'tcx>, - ) -> Result, NoSolution> { + goal: Goal, + a_elem_ty: I::Ty, + b_elem_ty: I::Ty, + ) -> Result, NoSolution> { self.eq(goal.param_env, a_elem_ty, b_elem_ty)?; self.probe_builtin_trait_candidate(BuiltinImplSource::Misc) .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) @@ -950,26 +937,25 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// ``` fn consider_builtin_struct_unsize( &mut self, - goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, - def: ty::AdtDef<'tcx>, - a_args: ty::GenericArgsRef<'tcx>, - b_args: ty::GenericArgsRef<'tcx>, - ) -> Result, NoSolution> { - let tcx = self.tcx(); + goal: Goal, + def: I::AdtDef, + a_args: I::GenericArgs, + b_args: I::GenericArgs, + ) -> Result, NoSolution> { + let tcx = self.interner(); let Goal { predicate: (_a_ty, b_ty), .. } = goal; - let unsizing_params = tcx.unsizing_params_for_adt(def.did()); + let unsizing_params = tcx.unsizing_params_for_adt(def.def_id()); // We must be unsizing some type parameters. This also implies // that the struct has a tail field. if unsizing_params.is_empty() { return Err(NoSolution); } - let tail_field = def.non_enum_variant().tail(); - let tail_field_ty = tcx.type_of(tail_field.did); + let tail_field_ty = def.struct_tail_ty(tcx).unwrap(); - let a_tail_ty = tail_field_ty.instantiate(tcx, a_args); - let b_tail_ty = tail_field_ty.instantiate(tcx, b_args); + let a_tail_ty = tail_field_ty.instantiate(tcx, &a_args); + let b_tail_ty = tail_field_ty.instantiate(tcx, &b_args); // Instantiate just the unsizing params from B into A. The type after // this instantiation must be equal to B. This is so we don't unsize @@ -978,7 +964,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { a_args .iter() .enumerate() - .map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { a }), + .map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { *a }), ); let unsized_a_ty = Ty::new_adt(tcx, def, new_a_args); @@ -991,7 +977,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { tcx, ty::TraitRef::new( tcx, - tcx.lang_items().unsize_trait().unwrap(), + tcx.require_lang_item(TraitSolverLangItem::Unsize), [a_tail_ty, b_tail_ty], ), ), @@ -1012,11 +998,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// ``` fn consider_builtin_tuple_unsize( &mut self, - goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, - a_tys: &'tcx ty::List>, - b_tys: &'tcx ty::List>, - ) -> Result, NoSolution> { - let tcx = self.tcx(); + goal: Goal, + a_tys: I::Tys, + b_tys: I::Tys, + ) -> Result, NoSolution> { + let tcx = self.interner(); let Goal { predicate: (_a_ty, b_ty), .. } = goal; let (&a_last_ty, a_rest_tys) = a_tys.split_last().unwrap(); @@ -1034,7 +1020,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { tcx, ty::TraitRef::new( tcx, - tcx.lang_items().unsize_trait().unwrap(), + tcx.require_lang_item(TraitSolverLangItem::Unsize), [a_last_ty, b_last_ty], ), ), @@ -1049,10 +1035,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // the type's constituent types. fn disqualify_auto_trait_candidate_due_to_possible_impl( &mut self, - goal: Goal<'tcx, TraitPredicate<'tcx>>, - ) -> Option, NoSolution>> { + goal: Goal>, + ) -> Option, NoSolution>> { let self_ty = goal.predicate.self_ty(); - match *self_ty.kind() { + match self_ty.kind() { // Stall int and float vars until they are resolved to a concrete // numerical type. That's because the check for impls below treats // int vars as matching any impl. Even if we filtered such impls, @@ -1070,15 +1056,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Alias(ty::Projection | ty::Weak | ty::Inherent, ..) | ty::Placeholder(..) => Some(Err(NoSolution)), - ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"), + ty::Infer(_) | ty::Bound(_, _) => panic!("unexpected type `{self_ty:?}`"), // Coroutines have one special built-in candidate, `Unpin`, which // takes precedence over the structural auto trait candidate being // assembled. ty::Coroutine(def_id, _) - if Some(goal.predicate.def_id()) == self.tcx().lang_items().unpin_trait() => + if self + .interner() + .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::Unpin) => { - match self.tcx().coroutine_movability(def_id) { + match self.interner().coroutine_movability(def_id) { Movability::Static => Some(Err(NoSolution)), Movability::Movable => Some( self.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { @@ -1123,7 +1111,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Tuple(_) | ty::Adt(_, _) => { let mut disqualifying_impl = None; - self.tcx().for_each_relevant_impl( + self.interner().for_each_relevant_impl( goal.predicate.def_id(), goal.predicate.self_ty(), |impl_def_id| { @@ -1149,13 +1137,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// wrapped in one. fn probe_and_evaluate_goal_for_constituent_tys( &mut self, - source: CandidateSource<'tcx>, - goal: Goal<'tcx, TraitPredicate<'tcx>>, + source: CandidateSource, + goal: Goal>, constituent_tys: impl Fn( - &EvalCtxt<'_, 'tcx>, - Ty<'tcx>, - ) -> Result>>, NoSolution>, - ) -> Result, NoSolution> { + &EvalCtxt<'_, Infcx>, + I::Ty, + ) -> Result>, NoSolution>, + ) -> Result, NoSolution> { self.probe_trait_candidate(source).enter(|ecx| { ecx.add_goals( GoalSource::ImplWhereBound, @@ -1163,7 +1151,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .into_iter() .map(|ty| { ecx.enter_forall(ty, |ty| { - goal.with(ecx.tcx(), goal.predicate.with_self_ty(ecx.tcx(), ty)) + goal.with( + ecx.interner(), + goal.predicate.with_self_ty(ecx.interner(), ty), + ) }) }) .collect::>(), @@ -1175,8 +1166,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "trace", skip(self))] pub(super) fn compute_trait_goal( &mut self, - goal: Goal<'tcx, TraitPredicate<'tcx>>, - ) -> QueryResult<'tcx> { + goal: Goal>, + ) -> QueryResult { let candidates = self.assemble_and_evaluate_candidates(goal); self.merge_candidates(candidates) } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 04f855e4f559..efb9526eabcf 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -133,6 +133,8 @@ parse_dot_dot_dot_for_remaining_fields = expected field pattern, found `{$token_ parse_dot_dot_dot_range_to_pattern_not_allowed = range-to patterns with `...` are not allowed .suggestion = use `..=` instead +parse_dot_dot_range_attribute = attributes are not allowed on range expressions starting with `..` + parse_dotdotdot = unexpected token: `...` .suggest_exclusive_range = use `..` for an exclusive range .suggest_inclusive_range = or `..=` for an inclusive range @@ -622,8 +624,6 @@ parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allo parse_out_of_range_hex_escape = out of range hex escape .label = must be a character in the range [\x00-\x7f] -parse_outer_attr_ambiguous = ambiguous outer attributes - parse_outer_attr_explanation = outer attributes, like `#[test]`, annotate the item following them parse_outer_attribute_not_allowed_on_if_else = outer attributes are not allowed on `if` and `else` branches diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 3f08a830b0c9..46e157348530 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use rustc_ast::token::Token; use rustc_ast::{Path, Visibility}; use rustc_errors::{ - codes::*, Applicability, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level, + codes::*, Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, SubdiagMessageOp, Subdiagnostic, }; use rustc_macros::{Diagnostic, Subdiagnostic}; @@ -495,15 +495,6 @@ pub(crate) struct OuterAttributeNotAllowedOnIfElse { pub attributes: Span, } -#[derive(Diagnostic)] -#[diag(parse_outer_attr_ambiguous)] -pub(crate) struct AmbiguousOuterAttributes { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub sugg: WrapInParentheses, -} - #[derive(Diagnostic)] #[diag(parse_missing_in_in_for_loop)] pub(crate) struct MissingInInForLoop { @@ -1061,7 +1052,7 @@ pub(crate) struct ExpectedIdentifier { impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedIdentifier { #[track_caller] - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let token_descr = TokenDescription::from_token(&self.token); let mut diag = Diag::new( @@ -1121,7 +1112,7 @@ pub(crate) struct ExpectedSemi { impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedSemi { #[track_caller] - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let token_descr = TokenDescription::from_token(&self.token); let mut diag = Diag::new( @@ -2708,12 +2699,13 @@ pub(crate) struct SingleColonImportPath { #[derive(Diagnostic)] #[diag(parse_bad_item_kind)] -#[help] pub(crate) struct BadItemKind { #[primary_span] pub span: Span, pub descr: &'static str, pub ctx: &'static str, + #[help] + pub help: Option<()>, } #[derive(Diagnostic)] @@ -2998,3 +2990,10 @@ pub(crate) struct ExprRArrowCall { #[suggestion(style = "short", applicability = "machine-applicable", code = ".")] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(parse_dot_dot_range_attribute)] +pub(crate) struct DotDotRangeAttribute { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index d2d200a91aff..511805cf8d6e 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -7,7 +7,7 @@ use rustc_ast::ast::{self, AttrStyle}; use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::util::unicode::contains_text_flow_control_chars; -use rustc_errors::{codes::*, Applicability, Diag, DiagCtxt, StashKey}; +use rustc_errors::{codes::*, Applicability, Diag, DiagCtxtHandle, StashKey}; use rustc_lexer::unescape::{self, EscapeError, Mode}; use rustc_lexer::{Base, DocStyle, RawStrError}; use rustc_lexer::{Cursor, LiteralKind}; @@ -18,6 +18,7 @@ use rustc_session::lint::BuiltinLintDiag; use rustc_session::parse::ParseSess; use rustc_span::symbol::Symbol; use rustc_span::{edition::Edition, BytePos, Pos, Span}; +use tracing::debug; mod diagnostics; mod tokentrees; @@ -41,7 +42,7 @@ pub(crate) struct UnmatchedDelim { pub candidate_span: Option, } -pub(crate) fn parse_token_trees<'psess, 'src>( +pub(crate) fn lex_token_trees<'psess, 'src>( psess: &'psess ParseSess, mut src: &'src str, mut start_pos: BytePos, @@ -65,7 +66,7 @@ pub(crate) fn parse_token_trees<'psess, 'src>( last_lifetime: None, }; let (stream, res, unmatched_delims) = - tokentrees::TokenTreesReader::parse_all_token_trees(string_reader); + tokentrees::TokenTreesReader::lex_all_token_trees(string_reader); match res { Ok(()) if unmatched_delims.is_empty() => Ok(stream), _ => { @@ -112,8 +113,8 @@ struct StringReader<'psess, 'src> { } impl<'psess, 'src> StringReader<'psess, 'src> { - fn dcx(&self) -> &'psess DiagCtxt { - &self.psess.dcx + fn dcx(&self) -> DiagCtxtHandle<'psess> { + self.psess.dcx() } fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span { @@ -247,8 +248,8 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let suffix = if suffix_start < self.pos { let string = self.str_from(suffix_start); if string == "_" { - self.psess - .dcx + self + .dcx() .emit_err(errors::UnderscoreLiteralSuffix { span: self.mk_sp(suffix_start, self.pos) }); None } else { @@ -370,11 +371,10 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let content = self.str_from(content_start); if contains_text_flow_control_chars(content) { let span = self.mk_sp(start, self.pos); - self.psess.buffer_lint_with_diagnostic( + self.psess.buffer_lint( TEXT_DIRECTION_CODEPOINT_IN_COMMENT, span, ast::CRATE_NODE_ID, - "unicode codepoint changing visible direction of text present in comment", BuiltinLintDiag::UnicodeTextFlow(span, content.to_string()), ); } @@ -597,8 +597,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { } fn report_non_started_raw_string(&self, start: BytePos, bad_char: char) -> ! { - self.psess - .dcx + self.dcx() .struct_span_fatal( self.mk_sp(start, self.pos), format!( @@ -723,12 +722,11 @@ impl<'psess, 'src> StringReader<'psess, 'src> { self.dcx().emit_err(errors::UnknownPrefix { span: prefix_span, prefix, sugg }); } else { // Before Rust 2021, only emit a lint for migration. - self.psess.buffer_lint_with_diagnostic( + self.psess.buffer_lint( RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, prefix_span, ast::CRATE_NODE_ID, - format!("prefix `{prefix}` is unknown"), - BuiltinLintDiag::ReservedPrefix(prefix_span), + BuiltinLintDiag::ReservedPrefix(prefix_span, prefix.to_string()), ); } } diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index eabe0226b2fb..8e5434546913 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -2,6 +2,7 @@ use super::diagnostics::report_suspicious_mismatch_block; use super::diagnostics::same_indentation_level; use super::diagnostics::TokenTreeDiagInfo; use super::{StringReader, UnmatchedDelim}; +use crate::Parser; use rustc_ast::token::{self, Delimiter, Token}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast_pretty::pprust::token_to_string; @@ -17,7 +18,7 @@ pub(super) struct TokenTreesReader<'psess, 'src> { } impl<'psess, 'src> TokenTreesReader<'psess, 'src> { - pub(super) fn parse_all_token_trees( + pub(super) fn lex_all_token_trees( string_reader: StringReader<'psess, 'src>, ) -> (TokenStream, Result<(), Vec>>, Vec) { let mut tt_reader = TokenTreesReader { @@ -25,14 +26,13 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { token: Token::dummy(), diag_info: TokenTreeDiagInfo::default(), }; - let (_open_spacing, stream, res) = - tt_reader.parse_token_trees(/* is_delimited */ false); + let (_open_spacing, stream, res) = tt_reader.lex_token_trees(/* is_delimited */ false); (stream, res, tt_reader.diag_info.unmatched_delims) } - // Parse a stream of tokens into a list of `TokenTree`s. The `Spacing` in - // the result is that of the opening delimiter. - fn parse_token_trees( + // Lex into a token stream. The `Spacing` in the result is that of the + // opening delimiter. + fn lex_token_trees( &mut self, is_delimited: bool, ) -> (Spacing, TokenStream, Result<(), Vec>>) { @@ -42,12 +42,10 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { let mut buf = Vec::new(); loop { match self.token.kind { - token::OpenDelim(delim) => { - buf.push(match self.parse_token_tree_open_delim(delim) { - Ok(val) => val, - Err(errs) => return (open_spacing, TokenStream::new(buf), Err(errs)), - }) - } + token::OpenDelim(delim) => buf.push(match self.lex_token_tree_open_delim(delim) { + Ok(val) => val, + Err(errs) => return (open_spacing, TokenStream::new(buf), Err(errs)), + }), token::CloseDelim(delim) => { return ( open_spacing, @@ -73,7 +71,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { fn eof_err(&mut self) -> PErr<'psess> { let msg = "this file contains an unclosed delimiter"; - let mut err = self.string_reader.psess.dcx.struct_span_err(self.token.span, msg); + let mut err = self.string_reader.dcx().struct_span_err(self.token.span, msg); for &(_, sp) in &self.diag_info.open_braces { err.span_label(sp, "unclosed delimiter"); self.diag_info.unmatched_delims.push(UnmatchedDelim { @@ -95,24 +93,24 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { err } - fn parse_token_tree_open_delim( + fn lex_token_tree_open_delim( &mut self, open_delim: Delimiter, ) -> Result>> { - // The span for beginning of the delimited section + // The span for beginning of the delimited section. let pre_span = self.token.span; self.diag_info.open_braces.push((open_delim, self.token.span)); - // Parse the token trees within the delimiters. + // Lex the token trees within the delimiters. // We stop at any delimiter so we can try to recover if the user // uses an incorrect delimiter. - let (open_spacing, tts, res) = self.parse_token_trees(/* is_delimited */ true); + let (open_spacing, tts, res) = self.lex_token_trees(/* is_delimited */ true); if let Err(errs) = res { return Err(self.unclosed_delim_err(tts, errs)); } - // Expand to cover the entire delimited token tree + // Expand to cover the entire delimited token tree. let delim_span = DelimSpan::from_pair(pre_span, self.token.span); let sm = self.string_reader.psess.source_map(); @@ -150,7 +148,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { self.diag_info.last_unclosed_found_span = Some(self.token.span); // This is a conservative error: only report the last unclosed // delimiter. The previous unclosed delimiters could actually be - // closed! The parser just hasn't gotten to them yet. + // closed! The lexer just hasn't gotten to them yet. if let Some(&(_, sp)) = self.diag_info.open_braces.last() { unclosed_delimiter = Some(sp); }; @@ -234,11 +232,11 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { ) -> Vec> { // If there are unclosed delims, see if there are diff markers and if so, point them // out instead of complaining about the unclosed delims. - let mut parser = crate::stream_to_parser(self.string_reader.psess, tts, None); + let mut parser = Parser::new(self.string_reader.psess, tts, None); let mut diff_errs = vec![]; - // Suggest removing a `{` we think appears in an `if`/`while` condition - // We want to suggest removing a `{` only if we think we're in an `if`/`while` condition, but - // we have no way of tracking this in the lexer itself, so we piggyback on the parser + // Suggest removing a `{` we think appears in an `if`/`while` condition. + // We want to suggest removing a `{` only if we think we're in an `if`/`while` condition, + // but we have no way of tracking this in the lexer itself, so we piggyback on the parser. let mut in_cond = false; while parser.token != token::Eof { if let Err(diff_err) = parser.err_vcs_conflict_marker() { @@ -249,14 +247,15 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { parser.token.kind, token::CloseDelim(Delimiter::Brace) | token::FatArrow ) { - // end of the `if`/`while` body, or the end of a `match` guard + // End of the `if`/`while` body, or the end of a `match` guard. in_cond = false; } else if in_cond && parser.token == token::OpenDelim(Delimiter::Brace) { // Store the `&&` and `let` to use their spans later when creating the diagnostic let maybe_andand = parser.look_ahead(1, |t| t.clone()); let maybe_let = parser.look_ahead(2, |t| t.clone()); if maybe_andand == token::OpenDelim(Delimiter::Brace) { - // This might be the beginning of the `if`/`while` body (i.e., the end of the condition) + // This might be the beginning of the `if`/`while` body (i.e., the end of the + // condition). in_cond = false; } else if maybe_andand == token::AndAnd && maybe_let.is_keyword(kw::Let) { let mut err = parser.dcx().struct_span_err( @@ -288,11 +287,10 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { } fn close_delim_err(&mut self, delim: Delimiter) -> PErr<'psess> { - // An unexpected closing delimiter (i.e., there is no - // matching opening delimiter). + // An unexpected closing delimiter (i.e., there is no matching opening delimiter). let token_str = token_to_string(&self.token); let msg = format!("unexpected closing delimiter: `{token_str}`"); - let mut err = self.string_reader.psess.dcx.struct_span_err(self.token.span, msg); + let mut err = self.string_reader.dcx().struct_span_err(self.token.span, msg); report_suspicious_mismatch_block( &mut err, diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index fa242a32a181..b7a790fcf83a 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -3,14 +3,15 @@ use std::iter::once; use std::ops::Range; -use rustc_errors::{Applicability, DiagCtxt, ErrorGuaranteed}; +use rustc_errors::{Applicability, DiagCtxtHandle, ErrorGuaranteed}; use rustc_lexer::unescape::{EscapeError, Mode}; use rustc_span::{BytePos, Span}; +use tracing::debug; use crate::errors::{MoreThanOneCharNote, MoreThanOneCharSugg, NoBraceUnicodeSub, UnescapeError}; pub(crate) fn emit_unescape_error( - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, // interior part of the literal, between quotes lit: &str, // full span of the literal, including quotes and any prefix diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs index c9470151a7ba..0a82ede3b75d 100644 --- a/compiler/rustc_parse/src/lexer/unicode_chars.rs +++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs @@ -129,42 +129,42 @@ pub(super) const UNICODE_ARRAY: &[(char, &str, &str)] = &[ ('。', "Ideographic Full Stop", "."), ('︒', "Presentation Form For Vertical Ideographic Full Stop", "."), - ('՝', "Armenian Comma", "\'"), - (''', "Fullwidth Apostrophe", "\'"), - ('‘', "Left Single Quotation Mark", "\'"), - ('’', "Right Single Quotation Mark", "\'"), - ('‛', "Single High-Reversed-9 Quotation Mark", "\'"), - ('′', "Prime", "\'"), - ('‵', "Reversed Prime", "\'"), - ('՚', "Armenian Apostrophe", "\'"), - ('׳', "Hebrew Punctuation Geresh", "\'"), - ('`', "Grave Accent", "\'"), - ('`', "Greek Varia", "\'"), - ('`', "Fullwidth Grave Accent", "\'"), - ('´', "Acute Accent", "\'"), - ('΄', "Greek Tonos", "\'"), - ('´', "Greek Oxia", "\'"), - ('᾽', "Greek Koronis", "\'"), - ('᾿', "Greek Psili", "\'"), - ('῾', "Greek Dasia", "\'"), - ('ʹ', "Modifier Letter Prime", "\'"), - ('ʹ', "Greek Numeral Sign", "\'"), - ('ˈ', "Modifier Letter Vertical Line", "\'"), - ('ˊ', "Modifier Letter Acute Accent", "\'"), - ('ˋ', "Modifier Letter Grave Accent", "\'"), - ('˴', "Modifier Letter Middle Grave Accent", "\'"), - ('ʻ', "Modifier Letter Turned Comma", "\'"), - ('ʽ', "Modifier Letter Reversed Comma", "\'"), - ('ʼ', "Modifier Letter Apostrophe", "\'"), - ('ʾ', "Modifier Letter Right Half Ring", "\'"), - ('ꞌ', "Latin Small Letter Saltillo", "\'"), - ('י', "Hebrew Letter Yod", "\'"), - ('ߴ', "Nko High Tone Apostrophe", "\'"), - ('ߵ', "Nko Low Tone Apostrophe", "\'"), - ('ᑊ', "Canadian Syllabics West-Cree P", "\'"), - ('ᛌ', "Runic Letter Short-Twig-Sol S", "\'"), - ('𖽑', "Miao Sign Aspiration", "\'"), - ('𖽒', "Miao Sign Reformed Voicing", "\'"), + ('՝', "Armenian Comma", "'"), + (''', "Fullwidth Apostrophe", "'"), + ('‘', "Left Single Quotation Mark", "'"), + ('’', "Right Single Quotation Mark", "'"), + ('‛', "Single High-Reversed-9 Quotation Mark", "'"), + ('′', "Prime", "'"), + ('‵', "Reversed Prime", "'"), + ('՚', "Armenian Apostrophe", "'"), + ('׳', "Hebrew Punctuation Geresh", "'"), + ('`', "Grave Accent", "'"), + ('`', "Greek Varia", "'"), + ('`', "Fullwidth Grave Accent", "'"), + ('´', "Acute Accent", "'"), + ('΄', "Greek Tonos", "'"), + ('´', "Greek Oxia", "'"), + ('᾽', "Greek Koronis", "'"), + ('᾿', "Greek Psili", "'"), + ('῾', "Greek Dasia", "'"), + ('ʹ', "Modifier Letter Prime", "'"), + ('ʹ', "Greek Numeral Sign", "'"), + ('ˈ', "Modifier Letter Vertical Line", "'"), + ('ˊ', "Modifier Letter Acute Accent", "'"), + ('ˋ', "Modifier Letter Grave Accent", "'"), + ('˴', "Modifier Letter Middle Grave Accent", "'"), + ('ʻ', "Modifier Letter Turned Comma", "'"), + ('ʽ', "Modifier Letter Reversed Comma", "'"), + ('ʼ', "Modifier Letter Apostrophe", "'"), + ('ʾ', "Modifier Letter Right Half Ring", "'"), + ('ꞌ', "Latin Small Letter Saltillo", "'"), + ('י', "Hebrew Letter Yod", "'"), + ('ߴ', "Nko High Tone Apostrophe", "'"), + ('ߵ', "Nko Low Tone Apostrophe", "'"), + ('ᑊ', "Canadian Syllabics West-Cree P", "'"), + ('ᛌ', "Runic Letter Short-Twig-Sol S", "'"), + ('𖽑', "Miao Sign Aspiration", "'"), + ('𖽒', "Miao Sign Reformed Voicing", "'"), ('᳓', "Vedic Sign Nihshvasa", "\""), ('"', "Fullwidth Quotation Mark", "\""), @@ -298,6 +298,7 @@ pub(super) const UNICODE_ARRAY: &[(char, &str, &str)] = &[ ('〉', "Right Angle Bracket", ">"), ('》', "Right Double Angle Bracket", ">"), ('>', "Fullwidth Greater-Than Sign", ">"), + ('⩵', "Two Consecutive Equals Signs", "==") ]; @@ -332,7 +333,7 @@ const ASCII_ARRAY: &[(&str, &str, Option)] = &[ (">", "Greater-Than Sign", Some(token::Gt)), // FIXME: Literals are already lexed by this point, so we can't recover gracefully just by // spitting the correct token out. - ("\'", "Single Quote", None), + ("'", "Single Quote", None), ("\"", "Quotation Mark", None), ]; @@ -350,7 +351,7 @@ pub(super) fn check_for_substitution( let Some((_, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(s, _, _)| s == ascii_str) else { let msg = format!("substitution character not found for '{ch}'"); - reader.psess.dcx.span_bug(span, msg); + reader.dcx().span_bug(span, msg); }; // special help suggestion for "directed" double quotes diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 0f973dfcd796..5522127be83e 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -1,5 +1,6 @@ //! The main parser interface. +// tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] @@ -9,9 +10,7 @@ #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(let_chains)] - -#[macro_use] -extern crate tracing; +// tidy-alphabetical-end use rustc_ast as ast; use rustc_ast::token; @@ -37,147 +36,92 @@ mod errors; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } -// A bunch of utility functions of the form `parse__from_` -// where includes crate, expr, item, stmt, tts, and one that -// uses a HOF to parse anything, and includes file and -// `source_str`. - -/// A variant of 'panictry!' that works on a `Vec` instead of a single `Diag`. -macro_rules! panictry_buffer { - ($e:expr) => {{ - use std::result::Result::{Err, Ok}; - match $e { - Ok(e) => e, - Err(errs) => { - for e in errs { - e.emit(); - } - FatalError.raise() +// Unwrap the result if `Ok`, otherwise emit the diagnostics and abort. +pub fn unwrap_or_emit_fatal(expr: Result>>) -> T { + match expr { + Ok(expr) => expr, + Err(errs) => { + for err in errs { + err.emit(); } + FatalError.raise() } - }}; + } } -pub fn parse_crate_from_file<'a>(input: &Path, psess: &'a ParseSess) -> PResult<'a, ast::Crate> { - let mut parser = new_parser_from_file(psess, input, None); - parser.parse_crate_mod() -} - -pub fn parse_crate_attrs_from_file<'a>( - input: &Path, - psess: &'a ParseSess, -) -> PResult<'a, ast::AttrVec> { - let mut parser = new_parser_from_file(psess, input, None); - parser.parse_inner_attributes() -} - -pub fn parse_crate_from_source_str( - name: FileName, - source: String, - psess: &ParseSess, -) -> PResult<'_, ast::Crate> { - new_parser_from_source_str(psess, name, source).parse_crate_mod() -} - -pub fn parse_crate_attrs_from_source_str( - name: FileName, - source: String, - psess: &ParseSess, -) -> PResult<'_, ast::AttrVec> { - new_parser_from_source_str(psess, name, source).parse_inner_attributes() -} - -pub fn parse_stream_from_source_str( - name: FileName, - source: String, - psess: &ParseSess, - override_span: Option, -) -> TokenStream { - source_file_to_stream(psess, psess.source_map().new_source_file(name, source), override_span) -} - -/// Creates a new parser from a source string. -pub fn new_parser_from_source_str(psess: &ParseSess, name: FileName, source: String) -> Parser<'_> { - panictry_buffer!(maybe_new_parser_from_source_str(psess, name, source)) -} - -/// Creates a new parser from a source string. Returns any buffered errors from lexing the initial -/// token stream; these must be consumed via `emit`, `cancel`, etc., otherwise a panic will occur -/// when they are dropped. -pub fn maybe_new_parser_from_source_str( +/// Creates a new parser from a source string. On failure, the errors must be consumed via +/// `unwrap_or_emit_fatal`, `emit`, `cancel`, etc., otherwise a panic will occur when they are +/// dropped. +pub fn new_parser_from_source_str( psess: &ParseSess, name: FileName, source: String, ) -> Result, Vec>> { - maybe_source_file_to_parser(psess, psess.source_map().new_source_file(name, source)) + let source_file = psess.source_map().new_source_file(name, source); + new_parser_from_source_file(psess, source_file) } -/// Creates a new parser, aborting if the file doesn't exist. If a span is given, that is used on -/// an error as the source of the problem. -pub fn new_parser_from_file<'a>(psess: &'a ParseSess, path: &Path, sp: Option) -> Parser<'a> { +/// Creates a new parser from a filename. On failure, the errors must be consumed via +/// `unwrap_or_emit_fatal`, `emit`, `cancel`, etc., otherwise a panic will occur when they are +/// dropped. +/// +/// If a span is given, that is used on an error as the source of the problem. +pub fn new_parser_from_file<'a>( + psess: &'a ParseSess, + path: &Path, + sp: Option, +) -> Result, Vec>> { let source_file = psess.source_map().load_file(path).unwrap_or_else(|e| { let msg = format!("couldn't read {}: {}", path.display(), e); - let mut err = psess.dcx.struct_fatal(msg); + let mut err = psess.dcx().struct_fatal(msg); if let Some(sp) = sp { err.span(sp); } err.emit(); }); - - panictry_buffer!(maybe_source_file_to_parser(psess, source_file)) + new_parser_from_source_file(psess, source_file) } /// Given a session and a `source_file`, return a parser. Returns any buffered errors from lexing /// the initial token stream. -fn maybe_source_file_to_parser( +fn new_parser_from_source_file( psess: &ParseSess, source_file: Lrc, ) -> Result, Vec>> { let end_pos = source_file.end_position(); - let stream = maybe_file_to_stream(psess, source_file, None)?; - let mut parser = stream_to_parser(psess, stream, None); + let stream = source_file_to_stream(psess, source_file, None)?; + let mut parser = Parser::new(psess, stream, None); if parser.token == token::Eof { parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt(), None); } - Ok(parser) } -// Base abstractions - -/// Given a `source_file`, produces a sequence of token trees. -pub fn source_file_to_stream( +pub fn source_str_to_stream( psess: &ParseSess, - source_file: Lrc, + name: FileName, + source: String, override_span: Option, -) -> TokenStream { - panictry_buffer!(maybe_file_to_stream(psess, source_file, override_span)) +) -> Result>> { + let source_file = psess.source_map().new_source_file(name, source); + source_file_to_stream(psess, source_file, override_span) } /// Given a source file, produces a sequence of token trees. Returns any buffered errors from /// parsing the token stream. -fn maybe_file_to_stream<'psess>( +fn source_file_to_stream<'psess>( psess: &'psess ParseSess, source_file: Lrc, override_span: Option, ) -> Result>> { let src = source_file.src.as_ref().unwrap_or_else(|| { - psess.dcx.bug(format!( + psess.dcx().bug(format!( "cannot lex `source_file` without source: {}", psess.source_map().filename_for_diagnostics(&source_file.name) )); }); - lexer::parse_token_trees(psess, src.as_str(), source_file.start_pos, override_span) -} - -/// Given a stream and the `ParseSess`, produces a parser. -pub fn stream_to_parser<'a>( - psess: &'a ParseSess, - stream: TokenStream, - subparser_name: Option<&'static str>, -) -> Parser<'a> { - Parser::new(psess, stream, subparser_name) + lexer::lex_token_trees(psess, src.as_str(), source_file.start_pos, override_span) } /// Runs the given subparser `f` on the tokens of the given `attr`'s item. @@ -198,19 +142,28 @@ pub fn parse_in<'a, T>( pub fn fake_token_stream_for_item(psess: &ParseSess, item: &ast::Item) -> TokenStream { let source = pprust::item_to_string(item); let filename = FileName::macro_expansion_source_code(&source); - parse_stream_from_source_str(filename, source, psess, Some(item.span)) + unwrap_or_emit_fatal(source_str_to_stream(psess, filename, source, Some(item.span))) } pub fn fake_token_stream_for_crate(psess: &ParseSess, krate: &ast::Crate) -> TokenStream { let source = pprust::crate_to_string_for_macros(krate); let filename = FileName::macro_expansion_source_code(&source); - parse_stream_from_source_str(filename, source, psess, Some(krate.spans.inner_span)) + unwrap_or_emit_fatal(source_str_to_stream( + psess, + filename, + source, + Some(krate.spans.inner_span), + )) } pub fn parse_cfg_attr( attr: &Attribute, psess: &ParseSess, ) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> { + const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]"; + const CFG_ATTR_NOTE_REF: &str = "for more information, visit \ + "; + match attr.get_normal_item().args { ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens }) if !tokens.is_empty() => @@ -225,16 +178,12 @@ pub fn parse_cfg_attr( } } } - _ => error_malformed_cfg_attr_missing(attr.span, psess), + _ => { + psess.dcx().emit_err(errors::MalformedCfgAttr { + span: attr.span, + sugg: CFG_ATTR_GRAMMAR_HELP, + }); + } } None } - -const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]"; -const CFG_ATTR_NOTE_REF: &str = "for more information, visit \ - "; - -fn error_malformed_cfg_attr_missing(span: Span, psess: &ParseSess) { - psess.dcx.emit_err(errors::MalformedCfgAttr { span, sugg: CFG_ATTR_GRAMMAR_HELP }); -} diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index a57eb70c7053..58fef9b6c456 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -7,7 +7,7 @@ use rustc_ast as ast; use rustc_ast::attr; use rustc_ast::token::{self, Delimiter}; use rustc_errors::{codes::*, Diag, PResult}; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{sym, symbol::kw, BytePos, Span}; use thin_vec::ThinVec; use tracing::debug; @@ -252,9 +252,23 @@ impl<'a> Parser<'a> { maybe_whole!(self, NtMeta, |attr| attr.into_inner()); let do_parse = |this: &mut Self| { + let is_unsafe = this.eat_keyword(kw::Unsafe); + let unsafety = if is_unsafe { + let unsafe_span = this.prev_token.span; + this.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span); + this.expect(&token::OpenDelim(Delimiter::Parenthesis))?; + + ast::Safety::Unsafe(unsafe_span) + } else { + ast::Safety::Default + }; + let path = this.parse_path(PathStyle::Mod)?; let args = this.parse_attr_args()?; - Ok(ast::AttrItem { path, args, tokens: None }) + if is_unsafe { + this.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + } + Ok(ast::AttrItem { unsafety, path, args, tokens: None }) }; // Attr items don't have attributes if capture_tokens { self.collect_tokens_no_attrs(do_parse) } else { do_parse(self) } @@ -265,7 +279,7 @@ impl<'a> Parser<'a> { /// terminated by a semicolon. /// /// Matches `inner_attrs*`. - pub(crate) fn parse_inner_attributes(&mut self) -> PResult<'a, ast::AttrVec> { + pub fn parse_inner_attributes(&mut self) -> PResult<'a, ast::AttrVec> { let mut attrs = ast::AttrVec::new(); loop { let start_pos: u32 = self.num_bump_calls.try_into().unwrap(); @@ -375,10 +389,25 @@ impl<'a> Parser<'a> { } let lo = self.token.span; + let is_unsafe = self.eat_keyword(kw::Unsafe); + let unsafety = if is_unsafe { + let unsafe_span = self.prev_token.span; + self.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span); + self.expect(&token::OpenDelim(Delimiter::Parenthesis))?; + + ast::Safety::Unsafe(unsafe_span) + } else { + ast::Safety::Default + }; + let path = self.parse_path(PathStyle::Mod)?; let kind = self.parse_meta_item_kind()?; + if is_unsafe { + self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; + } let span = lo.to(self.prev_token.span); - Ok(ast::MetaItem { path, kind, span }) + + Ok(ast::MetaItem { unsafety, path, kind, span }) } pub(crate) fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> { diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 62c8f9f5dacb..b5480b6b7d21 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -15,7 +15,7 @@ use std::ops::Range; /// for the attribute target. This allows us to perform cfg-expansion on /// a token stream before we invoke a derive proc-macro. /// -/// This wrapper prevents direct access to the underlying `ast::AttrVec>`. +/// This wrapper prevents direct access to the underlying `ast::AttrVec`. /// Parsing code can only get access to the underlying attributes /// by passing an `AttrWrapper` to `collect_tokens_trailing_tokens`. /// This makes it difficult to accidentally construct an AST node @@ -41,7 +41,7 @@ impl AttrWrapper { } pub(crate) fn take_for_recovery(self, psess: &ParseSess) -> AttrVec { - psess.dcx.span_delayed_bug( + psess.dcx().span_delayed_bug( self.attrs.get(0).map(|attr| attr.span).unwrap_or(DUMMY_SP), "AttrVec is taken for recovery but no error is produced", ); @@ -177,6 +177,10 @@ impl<'a> Parser<'a> { /// into a `LazyAttrTokenStream`, and returned along with the result /// of the callback. /// + /// The `attrs` passed in are in `AttrWrapper` form, which is opaque. The + /// `AttrVec` within is passed to `f`. See the comment on `AttrWrapper` for + /// details. + /// /// Note: If your callback consumes an opening delimiter /// (including the case where you call `collect_tokens` /// when the current token is an opening delimiter), diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index ac12787f2ef0..c2b91488a117 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1,7 +1,6 @@ use super::pat::Expected; use super::{ - BlockMode, CommaRecoveryMode, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, - TokenExpectType, TokenType, + BlockMode, CommaRecoveryMode, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenType, }; use crate::errors::{ AmbiguousPlus, AsyncMoveBlockIn2015, AttributeOnParamType, BadQPathStage2, BadTypePlus, @@ -35,7 +34,7 @@ use rustc_ast::{ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ - pluralize, Applicability, Diag, DiagCtxt, ErrorGuaranteed, FatalError, PErr, PResult, + pluralize, Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, PErr, PResult, Subdiagnostic, }; use rustc_session::errors::ExprParenthesesNeeded; @@ -45,6 +44,7 @@ use rustc_span::{BytePos, Span, SpanSnippetError, Symbol, DUMMY_SP}; use std::mem::take; use std::ops::{Deref, DerefMut}; use thin_vec::{thin_vec, ThinVec}; +use tracing::{debug, trace}; /// Creates a placeholder argument. pub(super) fn dummy_arg(ident: Ident, guar: ErrorGuaranteed) -> Param { @@ -129,14 +129,14 @@ pub enum AttemptLocalParseRecovery { } impl AttemptLocalParseRecovery { - pub fn yes(&self) -> bool { + pub(super) fn yes(&self) -> bool { match self { AttemptLocalParseRecovery::Yes => true, AttemptLocalParseRecovery::No => false, } } - pub fn no(&self) -> bool { + pub(super) fn no(&self) -> bool { match self { AttemptLocalParseRecovery::Yes => false, AttemptLocalParseRecovery::No => true, @@ -240,8 +240,8 @@ impl<'a> DerefMut for SnapshotParser<'a> { } impl<'a> Parser<'a> { - pub fn dcx(&self) -> &'a DiagCtxt { - &self.psess.dcx + pub fn dcx(&self) -> DiagCtxtHandle<'a> { + self.psess.dcx() } /// Replace `self` with `snapshot.parser`. @@ -666,7 +666,7 @@ impl<'a> Parser<'a> { { err.note("you may be trying to write a c-string literal"); err.note("c-string literals require Rust 2021 or later"); - err.subdiagnostic(self.dcx(), HelpUseLatestEdition::new()); + err.subdiagnostic(HelpUseLatestEdition::new()); } // `pub` may be used for an item or `pub(crate)` @@ -890,7 +890,7 @@ impl<'a> Parser<'a> { } } - pub fn maybe_suggest_struct_literal( + pub(super) fn maybe_suggest_struct_literal( &mut self, lo: Span, s: BlockCheckMode, @@ -1044,9 +1044,7 @@ impl<'a> Parser<'a> { /// passes through any errors encountered. Used for error recovery. pub(super) fn eat_to_tokens(&mut self, kets: &[&TokenKind]) { if let Err(err) = - self.parse_seq_to_before_tokens(kets, SeqSep::none(), TokenExpectType::Expect, |p| { - Ok(p.parse_token_tree()) - }) + self.parse_seq_to_before_tokens(kets, &[], SeqSep::none(), |p| Ok(p.parse_token_tree())) { err.cancel(); } @@ -2359,7 +2357,7 @@ impl<'a> Parser<'a> { let mut err = self.dcx().struct_span_err(span, msg); let sp = self.psess.source_map().start_point(self.token.span); if let Some(sp) = self.psess.ambiguous_block_expr_parse.borrow().get(&sp) { - err.subdiagnostic(self.dcx(), ExprParenthesesNeeded::surrounding(*sp)); + err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } err.span_label(span, "expected expression"); @@ -2458,7 +2456,7 @@ impl<'a> Parser<'a> { /// Handle encountering a symbol in a generic argument list that is not a `,` or `>`. In this /// case, we emit an error and try to suggest enclosing a const argument in braces if it looks /// like the user has forgotten them. - pub fn handle_ambiguous_unbraced_const_arg( + pub(super) fn handle_ambiguous_unbraced_const_arg( &mut self, args: &mut ThinVec, ) -> PResult<'a, bool> { @@ -2499,9 +2497,10 @@ impl<'a> Parser<'a> { /// - Single-segment paths (i.e. standalone generic const parameters). /// All other expressions that can be parsed will emit an error suggesting the expression be /// wrapped in braces. - pub fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P> { + pub(super) fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P> { let start = self.token.span; - let expr = self.parse_expr_res(Restrictions::CONST_EXPR, None).map_err(|mut err| { + let attrs = self.parse_outer_attributes()?; + let expr = self.parse_expr_res(Restrictions::CONST_EXPR, attrs).map_err(|mut err| { err.span_label( start.shrink_to_lo(), "while parsing a const generic argument starting here", @@ -2558,7 +2557,7 @@ impl<'a> Parser<'a> { Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })) } - pub fn recover_const_param_declaration( + pub(super) fn recover_const_param_declaration( &mut self, ty_generics: Option<&Generics>, ) -> PResult<'a, Option> { @@ -2588,7 +2587,11 @@ impl<'a> Parser<'a> { /// When encountering code like `foo::< bar + 3 >` or `foo::< bar - baz >` we suggest /// `foo::<{ bar + 3 }>` and `foo::<{ bar - baz }>`, respectively. We only provide a suggestion /// if we think that the resulting expression would be well formed. - pub fn recover_const_arg(&mut self, start: Span, mut err: Diag<'a>) -> PResult<'a, GenericArg> { + pub(super) fn recover_const_arg( + &mut self, + start: Span, + mut err: Diag<'a>, + ) -> PResult<'a, GenericArg> { let is_op_or_dot = AssocOp::from_token(&self.token) .and_then(|op| { if let AssocOp::Greater @@ -2619,7 +2622,10 @@ impl<'a> Parser<'a> { if is_op_or_dot { self.bump(); } - match self.parse_expr_res(Restrictions::CONST_EXPR, None) { + match (|| { + let attrs = self.parse_outer_attributes()?; + self.parse_expr_res(Restrictions::CONST_EXPR, attrs) + })() { Ok(expr) => { // Find a mistake like `MyTrait`. if token::EqEq == snapshot.token.kind { @@ -2673,7 +2679,10 @@ impl<'a> Parser<'a> { &mut self, mut snapshot: SnapshotParser<'a>, ) -> Option> { - match snapshot.parse_expr_res(Restrictions::CONST_EXPR, None) { + match (|| { + let attrs = self.parse_outer_attributes()?; + snapshot.parse_expr_res(Restrictions::CONST_EXPR, attrs) + })() { // Since we don't know the exact reason why we failed to parse the type or the // expression, employ a simple heuristic to weed out some pathological cases. Ok(expr) if let token::Comma | token::Gt = snapshot.token.kind => { @@ -2689,7 +2698,7 @@ impl<'a> Parser<'a> { } /// Creates a dummy const argument, and reports that the expression must be enclosed in braces - pub fn dummy_const_arg_needs_braces(&self, mut err: Diag<'a>, span: Span) -> GenericArg { + pub(super) fn dummy_const_arg_needs_braces(&self, mut err: Diag<'a>, span: Span) -> GenericArg { err.multipart_suggestion( "expressions must be enclosed in braces to be used as const generic \ arguments", @@ -2960,7 +2969,7 @@ impl<'a> Parser<'a> { /// * `=====` /// * `<<<<<` /// - pub fn is_vcs_conflict_marker( + pub(super) fn is_vcs_conflict_marker( &mut self, long_kind: &TokenKind, short_kind: &TokenKind, @@ -2980,14 +2989,14 @@ impl<'a> Parser<'a> { None } - pub fn recover_vcs_conflict_marker(&mut self) { + pub(super) fn recover_vcs_conflict_marker(&mut self) { if let Err(err) = self.err_vcs_conflict_marker() { err.emit(); FatalError.raise(); } } - pub fn err_vcs_conflict_marker(&mut self) -> PResult<'a, ()> { + pub(crate) fn err_vcs_conflict_marker(&mut self) -> PResult<'a, ()> { let Some(start) = self.conflict_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) else { return Ok(()); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index d2d21624150b..09f706143fa7 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -4,7 +4,7 @@ use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, - SemiColonMode, SeqSep, TokenExpectType, TokenType, Trailing, TrailingToken, + SemiColonMode, SeqSep, TokenType, Trailing, TrailingToken, }; use crate::errors; @@ -36,6 +36,7 @@ use rustc_span::source_map::{self, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, ErrorGuaranteed, Pos, Span}; use thin_vec::{thin_vec, ThinVec}; +use tracing::instrument; /// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression /// dropped into the token stream, which happens while parsing the result of @@ -69,28 +70,10 @@ macro_rules! maybe_whole_expr { #[derive(Debug)] pub(super) enum LhsExpr { - NotYetParsed, - AttributesParsed(AttrWrapper), - AlreadyParsed { expr: P, starts_statement: bool }, -} - -impl From> for LhsExpr { - /// Converts `Some(attrs)` into `LhsExpr::AttributesParsed(attrs)` - /// and `None` into `LhsExpr::NotYetParsed`. - /// - /// This conversion does not allocate. - fn from(o: Option) -> Self { - if let Some(attrs) = o { LhsExpr::AttributesParsed(attrs) } else { LhsExpr::NotYetParsed } - } -} - -impl From> for LhsExpr { - /// Converts the `expr: P` into `LhsExpr::AlreadyParsed { expr, starts_statement: false }`. - /// - /// This conversion does not allocate. - fn from(expr: P) -> Self { - LhsExpr::AlreadyParsed { expr, starts_statement: false } - } + // Already parsed just the outer attributes. + Unparsed { attrs: AttrWrapper }, + // Already parsed the expression. + Parsed { expr: P, starts_statement: bool }, } #[derive(Debug)] @@ -111,12 +94,16 @@ impl<'a> Parser<'a> { pub fn parse_expr(&mut self) -> PResult<'a, P> { self.current_closure.take(); - self.parse_expr_res(Restrictions::empty(), None) + let attrs = self.parse_outer_attributes()?; + self.parse_expr_res(Restrictions::empty(), attrs) } - /// Parses an expression, forcing tokens to be collected + /// Parses an expression, forcing tokens to be collected. pub fn parse_expr_force_collect(&mut self) -> PResult<'a, P> { - self.collect_tokens_no_attrs(|this| this.parse_expr()) + self.current_closure.take(); + + let attrs = self.parse_outer_attributes()?; + self.collect_tokens_no_attrs(|this| this.parse_expr_res(Restrictions::empty(), attrs)) } pub fn parse_expr_anon_const(&mut self) -> PResult<'a, AnonConst> { @@ -124,7 +111,8 @@ impl<'a> Parser<'a> { } fn parse_expr_catch_underscore(&mut self, restrictions: Restrictions) -> PResult<'a, P> { - match self.parse_expr_res(restrictions, None) { + let attrs = self.parse_outer_attributes()?; + match self.parse_expr_res(restrictions, attrs) { Ok(expr) => Ok(expr), Err(err) => match self.token.ident() { Some((Ident { name: kw::Underscore, .. }, IdentIsRaw::No)) @@ -151,21 +139,9 @@ impl<'a> Parser<'a> { pub(super) fn parse_expr_res( &mut self, r: Restrictions, - already_parsed_attrs: Option, + attrs: AttrWrapper, ) -> PResult<'a, P> { - self.with_res(r, |this| this.parse_expr_assoc(already_parsed_attrs)) - } - - /// Parses an associative expression. - /// - /// This parses an expression accounting for associativity and precedence of the operators in - /// the expression. - #[inline] - fn parse_expr_assoc( - &mut self, - already_parsed_attrs: Option, - ) -> PResult<'a, P> { - self.parse_expr_assoc_with(0, already_parsed_attrs.into()) + self.with_res(r, |this| this.parse_expr_assoc_with(0, LhsExpr::Unparsed { attrs })) } /// Parses an associative expression with operators of at least `min_prec` precedence. @@ -175,18 +151,17 @@ impl<'a> Parser<'a> { lhs: LhsExpr, ) -> PResult<'a, P> { let mut starts_stmt = false; - let mut lhs = if let LhsExpr::AlreadyParsed { expr, starts_statement } = lhs { - starts_stmt = starts_statement; - expr - } else { - let attrs = match lhs { - LhsExpr::AttributesParsed(attrs) => Some(attrs), - _ => None, - }; - if self.token.is_range_separator() { - return self.parse_expr_prefix_range(attrs); - } else { - self.parse_expr_prefix(attrs)? + let mut lhs = match lhs { + LhsExpr::Parsed { expr, starts_statement } => { + starts_stmt = starts_statement; + expr + } + LhsExpr::Unparsed { attrs } => { + if self.token.is_range_separator() { + return self.parse_expr_prefix_range(attrs); + } else { + self.parse_expr_prefix(attrs)? + } } }; @@ -324,12 +299,11 @@ impl<'a> Parser<'a> { Fixity::None => 1, }; let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| { - this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::NotYetParsed) + let attrs = this.parse_outer_attributes()?; + this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::Unparsed { attrs }) })?; - self.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span); - let span = lhs_span.to(rhs.span); - + let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); lhs = match op { AssocOp::Add | AssocOp::Subtract @@ -428,23 +402,11 @@ impl<'a> Parser<'a> { }); } - fn error_ambiguous_outer_attrs(&self, lhs: &P, lhs_span: Span, rhs_span: Span) { - if let Some(attr) = lhs.attrs.iter().find(|a| a.style == AttrStyle::Outer) { - self.dcx().emit_err(errors::AmbiguousOuterAttributes { - span: attr.span.to(rhs_span), - sugg: errors::WrapInParentheses::Expression { - left: attr.span.shrink_to_lo(), - right: lhs_span.shrink_to_hi(), - }, - }); - } - } - /// Possibly translate the current token to an associative operator. /// The method does not advance the current token. /// /// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively. - pub fn check_assoc_op(&self) -> Option> { + pub(super) fn check_assoc_op(&self) -> Option> { let (op, span) = match (AssocOp::from_token(&self.token), self.token.ident()) { // When parsing const expressions, stop parsing when encountering `>`. ( @@ -511,16 +473,16 @@ impl<'a> Parser<'a> { ) -> PResult<'a, P> { let rhs = if self.is_at_start_of_range_notation_rhs() { let maybe_lt = self.token.clone(); + let attrs = self.parse_outer_attributes()?; Some( - self.parse_expr_assoc_with(prec + 1, LhsExpr::NotYetParsed) + self.parse_expr_assoc_with(prec + 1, LhsExpr::Unparsed { attrs }) .map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?, ) } else { None }; let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span); - self.error_ambiguous_outer_attrs(&lhs, lhs.span, rhs_span); - let span = lhs.span.to(rhs_span); + let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span); let limits = if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; let range = self.mk_range(Some(lhs), rhs, limits); @@ -540,7 +502,12 @@ impl<'a> Parser<'a> { } /// Parses prefix-forms of range notation: `..expr`, `..`, `..=expr`. - fn parse_expr_prefix_range(&mut self, attrs: Option) -> PResult<'a, P> { + fn parse_expr_prefix_range(&mut self, attrs: AttrWrapper) -> PResult<'a, P> { + if !attrs.is_empty() { + let err = errors::DotDotRangeAttribute { span: self.token.span }; + self.dcx().emit_err(err); + } + // Check for deprecated `...` syntax. if self.token == token::DotDotDot { self.err_dotdotdot_syntax(self.token.span); @@ -557,20 +524,20 @@ impl<'a> Parser<'a> { _ => RangeLimits::Closed, }; let op = AssocOp::from_token(&self.token); - // FIXME: `parse_prefix_range_expr` is called when the current - // token is `DotDot`, `DotDotDot`, or `DotDotEq`. If we haven't already - // parsed attributes, then trying to parse them here will always fail. - // We should figure out how we want attributes on range expressions to work. - let attrs = self.parse_or_use_outer_attributes(attrs)?; + let attrs = self.parse_outer_attributes()?; self.collect_tokens_for_expr(attrs, |this, attrs| { let lo = this.token.span; let maybe_lt = this.look_ahead(1, |t| t.clone()); this.bump(); let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() { // RHS must be parsed with more associativity than the dots. - this.parse_expr_assoc_with(op.unwrap().precedence() + 1, LhsExpr::NotYetParsed) - .map(|x| (lo.to(x.span), Some(x))) - .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))? + let attrs = this.parse_outer_attributes()?; + this.parse_expr_assoc_with( + op.unwrap().precedence() + 1, + LhsExpr::Unparsed { attrs }, + ) + .map(|x| (lo.to(x.span), Some(x))) + .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))? } else { (lo, None) }; @@ -580,8 +547,7 @@ impl<'a> Parser<'a> { } /// Parses a prefix-unary-operator expr. - fn parse_expr_prefix(&mut self, attrs: Option) -> PResult<'a, P> { - let attrs = self.parse_or_use_outer_attributes(attrs)?; + fn parse_expr_prefix(&mut self, attrs: AttrWrapper) -> PResult<'a, P> { let lo = self.token.span; macro_rules! make_it { @@ -630,7 +596,8 @@ impl<'a> Parser<'a> { this.dcx().emit_err(err); this.bump(); - this.parse_expr_prefix(None) + let attrs = this.parse_outer_attributes()?; + this.parse_expr_prefix(attrs) } // Recover from `++x`: token::BinOp(token::Plus) @@ -643,7 +610,7 @@ impl<'a> Parser<'a> { this.bump(); this.bump(); - let operand_expr = this.parse_expr_dot_or_call(Default::default())?; + let operand_expr = this.parse_expr_dot_or_call(attrs)?; this.recover_from_prefix_increment(operand_expr, pre_span, starts_stmt) } token::Ident(..) if this.token.is_keyword(kw::Box) => { @@ -652,13 +619,14 @@ impl<'a> Parser<'a> { token::Ident(..) if this.may_recover() && this.is_mistaken_not_ident_negation() => { make_it!(this, attrs, |this, _| this.recover_not_expr(lo)) } - _ => return this.parse_expr_dot_or_call(Some(attrs)), + _ => return this.parse_expr_dot_or_call(attrs), } } fn parse_expr_prefix_common(&mut self, lo: Span) -> PResult<'a, (Span, P)> { self.bump(); - let expr = self.parse_expr_prefix(None)?; + let attrs = self.parse_outer_attributes()?; + let expr = self.parse_expr_prefix(attrs)?; let span = self.interpolated_or_expr_span(&expr); Ok((lo.to(span), expr)) } @@ -738,8 +706,7 @@ impl<'a> Parser<'a> { expr_kind: fn(P, P) -> ExprKind, ) -> PResult<'a, P> { let mk_expr = |this: &mut Self, lhs: P, rhs: P| { - this.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span); - this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs)) + this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, rhs.span), expr_kind(lhs, rhs)) }; // Save the state of the parser before parsing type normally, in case there is a @@ -909,10 +876,11 @@ impl<'a> Parser<'a> { let has_lifetime = self.token.is_lifetime() && self.look_ahead(1, |t| t != &token::Colon); let lifetime = has_lifetime.then(|| self.expect_lifetime()); // For recovery, see below. let (borrow_kind, mutbl) = self.parse_borrow_modifiers(lo); + let attrs = self.parse_outer_attributes()?; let expr = if self.token.is_range_separator() { - self.parse_expr_prefix_range(None) + self.parse_expr_prefix_range(attrs) } else { - self.parse_expr_prefix(None) + self.parse_expr_prefix(attrs) }?; let hi = self.interpolated_or_expr_span(&expr); let span = lo.to(hi); @@ -942,8 +910,7 @@ impl<'a> Parser<'a> { } /// Parses `a.b` or `a(13)` or `a[4]` or just `a`. - fn parse_expr_dot_or_call(&mut self, attrs: Option) -> PResult<'a, P> { - let attrs = self.parse_or_use_outer_attributes(attrs)?; + fn parse_expr_dot_or_call(&mut self, attrs: AttrWrapper) -> PResult<'a, P> { self.collect_tokens_for_expr(attrs, |this, attrs| { let base = this.parse_expr_bottom()?; let span = this.interpolated_or_expr_span(&base); @@ -1021,7 +988,11 @@ impl<'a> Parser<'a> { } } - pub fn parse_dot_suffix_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> { + pub(super) fn parse_dot_suffix_expr( + &mut self, + lo: Span, + base: P, + ) -> PResult<'a, P> { // At this point we've consumed something like `expr.` and `self.token` holds the token // after the dot. match self.token.uninterpolate().kind { @@ -1472,7 +1443,7 @@ impl<'a> Parser<'a> { // If the input is something like `if a { 1 } else { 2 } | if a { 3 } else { 4 }` // then suggest parens around the lhs. if let Some(sp) = this.psess.ambiguous_block_expr_parse.borrow().get(&lo) { - err.subdiagnostic(this.dcx(), ExprParenthesesNeeded::surrounding(*sp)); + err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } err }) @@ -1913,11 +1884,10 @@ impl<'a> Parser<'a> { | ExprKind::Block(_, None) ) { - self.psess.buffer_lint_with_diagnostic( + self.psess.buffer_lint( BREAK_WITH_LABEL_AND_LOOP, lo.to(expr.span), ast::CRATE_NODE_ID, - "this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression", BuiltinLintDiag::BreakWithLabelAndLoop(expr.span), ); } @@ -2377,7 +2347,8 @@ impl<'a> Parser<'a> { self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET; let prev = self.prev_token.clone(); let token = self.token.clone(); - match self.parse_expr_res(restrictions, None) { + let attrs = self.parse_outer_attributes()?; + match self.parse_expr_res(restrictions, attrs) { Ok(expr) => expr, Err(err) => self.recover_closure_body(err, before, prev, token, lo, decl_hi)?, } @@ -2468,9 +2439,9 @@ impl<'a> Parser<'a> { self.expect(&token::BinOp(token::Or))?; let args = self .parse_seq_to_before_tokens( - &[&token::BinOp(token::Or), &token::OrOr], + &[&token::BinOp(token::Or)], + &[&token::OrOr], SeqSep::trailing_allowed(token::Comma), - TokenExpectType::NoExpect, |p| p.parse_fn_block_param(), )? .0; @@ -2625,8 +2596,9 @@ impl<'a> Parser<'a> { /// Parses the condition of a `if` or `while` expression. fn parse_expr_cond(&mut self) -> PResult<'a, P> { + let attrs = self.parse_outer_attributes()?; let mut cond = - self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, None)?; + self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?; CondChecker::new(self).visit_expr(&mut cond); @@ -2673,7 +2645,11 @@ impl<'a> Parser<'a> { } else { self.expect(&token::Eq)?; } - let expr = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), None.into())?; + let attrs = self.parse_outer_attributes()?; + let expr = self.parse_expr_assoc_with( + 1 + prec_let_scrutinee_needs_par(), + LhsExpr::Unparsed { attrs }, + )?; let span = lo.to(expr.span); Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered))) } @@ -2806,7 +2782,8 @@ impl<'a> Parser<'a> { (Err(err), Some((start_span, left))) if self.eat_keyword(kw::In) => { // We know for sure we have seen `for ($SOMETHING in`. In the happy path this would // happen right before the return of this method. - let expr = match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None) { + let attrs = self.parse_outer_attributes()?; + let expr = match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs) { Ok(expr) => expr, Err(expr_err) => { // We don't know what followed the `in`, so cancel and bubble up the @@ -2840,7 +2817,8 @@ impl<'a> Parser<'a> { self.error_missing_in_for_loop(); } self.check_for_for_in_in_typo(self.prev_token.span); - let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; + let attrs = self.parse_outer_attributes()?; + let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs)?; Ok((pat, expr)) } @@ -2952,7 +2930,8 @@ impl<'a> Parser<'a> { /// Parses a `match ... { ... }` expression (`match` token already eaten). fn parse_expr_match(&mut self) -> PResult<'a, P> { let match_span = self.prev_token.span; - let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; + let attrs = self.parse_outer_attributes()?; + let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs)?; self.parse_match_block(match_span, match_span, scrutinee, MatchKind::Prefix) } @@ -3156,8 +3135,9 @@ impl<'a> Parser<'a> { let arrow_span = this.prev_token.span; let arm_start_span = this.token.span; + let attrs = this.parse_outer_attributes()?; let expr = - this.parse_expr_res(Restrictions::STMT_EXPR, None).map_err(|mut err| { + this.parse_expr_res(Restrictions::STMT_EXPR, attrs).map_err(|mut err| { err.span_label(arrow_span, "while parsing the `match` arm starting here"); err })?; @@ -3362,7 +3342,8 @@ impl<'a> Parser<'a> { } fn parse_match_guard_condition(&mut self) -> PResult<'a, P> { - self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, None).map_err( + let attrs = self.parse_outer_attributes()?; + self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, attrs).map_err( |mut err| { if self.prev_token == token::OpenDelim(Delimiter::Brace) { let sugg_sp = self.prev_token.span.shrink_to_lo(); @@ -3858,6 +3839,16 @@ impl<'a> Parser<'a> { self.mk_expr(span, ExprKind::Err(guar)) } + /// Create expression span ensuring the span of the parent node + /// is larger than the span of lhs and rhs, including the attributes. + fn mk_expr_sp(&self, lhs: &P, lhs_span: Span, rhs_span: Span) -> Span { + lhs.attrs + .iter() + .find(|a| a.style == AttrStyle::Outer) + .map_or(lhs_span, |a| a.span) + .to(rhs_span) + } + fn collect_tokens_for_expr( &mut self, attrs: AttrWrapper, diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 93a15c938ecf..fde16ac957df 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -62,7 +62,7 @@ impl<'a> Parser<'a> { let snapshot = self.create_snapshot_for_diagnostic(); match self.parse_ty() { Ok(p) => { - if let TyKind::ImplTrait(_, bounds, None) = &p.kind { + if let TyKind::ImplTrait(_, bounds) = &p.kind { let span = impl_span.to(self.token.span.shrink_to_lo()); let mut err = self.dcx().struct_span_err( span, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index a46c104b6d9c..2db777a9f70c 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -23,6 +23,7 @@ use rustc_span::{Span, DUMMY_SP}; use std::fmt::Write; use std::mem; use thin_vec::{thin_vec, ThinVec}; +use tracing::debug; impl<'a> Parser<'a> { /// Parses a source module as a crate. This is the main entry point for the parser. @@ -58,9 +59,15 @@ impl<'a> Parser<'a> { let attrs = self.parse_inner_attributes()?; let post_attr_lo = self.token.span; - let mut items = ThinVec::new(); - while let Some(item) = self.parse_item(ForceCollect::No)? { - self.maybe_consume_incorrect_semicolon(Some(&item)); + let mut items: ThinVec> = ThinVec::new(); + + // There shouldn't be any stray semicolons before or after items. + // `parse_item` consumes the appropriate semicolons so any leftover is an error. + loop { + while self.maybe_consume_incorrect_semicolon(items.last().map(|x| &**x)) {} // Eat all bad semicolons + let Some(item) = self.parse_item(ForceCollect::No)? else { + break; + }; items.push(item); } @@ -219,10 +226,11 @@ impl<'a> Parser<'a> { self.expect_keyword(kw::Extern)?; self.parse_item_foreign_mod(attrs, safety)? } else if self.is_static_global() { + let safety = self.parse_safety(Case::Sensitive); // STATIC ITEM self.bump(); // `static` let mutability = self.parse_mutability(); - let (ident, item) = self.parse_static_item(mutability)?; + let (ident, item) = self.parse_static_item(safety, mutability)?; (ident, ItemKind::Static(Box::new(item))) } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) { // CONST ITEM @@ -625,7 +633,7 @@ impl<'a> Parser<'a> { // This notably includes paths passed through `ty` macro fragments (#46438). TyKind::Path(None, path) => path, other => { - if let TyKind::ImplTrait(_, bounds, None) = other + if let TyKind::ImplTrait(_, bounds) = other && let [bound] = bounds.as_slice() { // Suggest removing extra `impl` keyword: @@ -699,15 +707,25 @@ impl<'a> Parser<'a> { }; let (ident, item_kind) = if self.eat(&token::PathSep) { - let (suffixes, _) = self.parse_delim_comma_seq(Delimiter::Brace, |p| { - Ok((p.parse_path_segment_ident()?, rename(p)?)) - })?; + let suffixes = if self.eat(&token::BinOp(token::Star)) { + None + } else { + let parse_suffix = |p: &mut Self| Ok((p.parse_path_segment_ident()?, rename(p)?)); + Some(self.parse_delim_comma_seq(Delimiter::Brace, parse_suffix)?.0) + }; let deleg = DelegationMac { qself, prefix: path, suffixes, body: body(self)? }; (Ident::empty(), ItemKind::DelegationMac(Box::new(deleg))) } else { let rename = rename(self)?; let ident = rename.unwrap_or_else(|| path.segments.last().unwrap().ident); - let deleg = Delegation { id: DUMMY_NODE_ID, qself, path, rename, body: body(self)? }; + let deleg = Delegation { + id: DUMMY_NODE_ID, + qself, + path, + rename, + body: body(self)?, + from_glob: false, + }; (ident, ItemKind::Delegation(Box::new(deleg))) }; @@ -945,7 +963,7 @@ impl<'a> Parser<'a> { let kind = match AssocItemKind::try_from(kind) { Ok(kind) => kind, Err(kind) => match kind { - ItemKind::Static(box StaticItem { ty, mutability: _, expr }) => { + ItemKind::Static(box StaticItem { ty, safety: _, mutability: _, expr }) => { self.dcx().emit_err(errors::AssociatedStaticItemNotAllowed { span }); AssocItemKind::Const(Box::new(ConstItem { defaultness: Defaultness::Final, @@ -1214,6 +1232,7 @@ impl<'a> Parser<'a> { ty, mutability: Mutability::Not, expr, + safety: Safety::Default, })) } _ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"), @@ -1228,7 +1247,11 @@ impl<'a> Parser<'a> { // FIXME(#100717): needs variant for each `ItemKind` (instead of using `ItemKind::descr()`) let span = self.psess.source_map().guess_head_span(span); let descr = kind.descr(); - self.dcx().emit_err(errors::BadItemKind { span, descr, ctx }); + let help = match kind { + ItemKind::DelegationMac(deleg) if deleg.suffixes.is_none() => None, + _ => Some(()), + }; + self.dcx().emit_err(errors::BadItemKind { span, descr, ctx, help }); None } @@ -1251,7 +1274,10 @@ impl<'a> Parser<'a> { matches!(token.kind, token::BinOp(token::Or) | token::OrOr) }) } else { - false + let quals: &[Symbol] = &[kw::Unsafe, kw::Safe]; + // `$qual static` + quals.iter().any(|&kw| self.check_keyword(kw)) + && self.look_ahead(1, |t| t.is_keyword(kw::Static)) } } @@ -1312,7 +1338,11 @@ impl<'a> Parser<'a> { /// ```ebnf /// Static = "static" "mut"? $ident ":" $ty (= $expr)? ";" ; /// ``` - fn parse_static_item(&mut self, mutability: Mutability) -> PResult<'a, (Ident, StaticItem)> { + fn parse_static_item( + &mut self, + safety: Safety, + mutability: Mutability, + ) -> PResult<'a, (Ident, StaticItem)> { let ident = self.parse_ident()?; if self.token.kind == TokenKind::Lt && self.may_recover() { @@ -1333,7 +1363,7 @@ impl<'a> Parser<'a> { self.expect_semi()?; - Ok((ident, StaticItem { ty, mutability, expr })) + Ok((ident, StaticItem { ty, safety, mutability, expr })) } /// Parse a constant item with the prefix `"const"` already parsed. @@ -1950,7 +1980,7 @@ impl<'a> Parser<'a> { if self.token.kind == token::Not { if let Err(mut err) = self.unexpected() { // Encounter the macro invocation - err.subdiagnostic(self.dcx(), MacroExpandsToAdtField { adt_ty }); + err.subdiagnostic(MacroExpandsToAdtField { adt_ty }); return Err(err); } } @@ -2249,7 +2279,7 @@ pub(crate) struct FnParseMode { /// to true. /// * The span is from Edition 2015. In particular, you can get a /// 2015 span inside a 2021 crate using macros. - pub req_name: ReqName, + pub(super) req_name: ReqName, /// If this flag is set to `true`, then plain, semicolon-terminated function /// prototypes are not allowed here. /// @@ -2268,7 +2298,7 @@ pub(crate) struct FnParseMode { /// This field should only be set to false if the item is inside of a trait /// definition or extern block. Within an impl block or a module, it should /// always be set to true. - pub req_body: bool, + pub(super) req_body: bool, } /// Parsing of functions and methods. @@ -2366,13 +2396,10 @@ impl<'a> Parser<'a> { .into_iter() .any(|s| self.prev_token.is_ident_named(s)); - err.subdiagnostic( - self.dcx(), - errors::FnTraitMissingParen { - span: self.prev_token.span, - machine_applicable, - }, - ); + err.subdiagnostic(errors::FnTraitMissingParen { + span: self.prev_token.span, + machine_applicable, + }); } return Err(err); } @@ -2393,9 +2420,9 @@ impl<'a> Parser<'a> { // `pub` is added in case users got confused with the ordering like `async pub fn`, // only if it wasn't preceded by `default` as `default pub` is invalid. let quals: &[Symbol] = if check_pub { - &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Extern] + &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern] } else { - &[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Extern] + &[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern] }; self.check_keyword_case(kw::Fn, case) // Definitely an `fn`. // `$qual fn` or `$qual $qual`: @@ -2530,11 +2557,27 @@ impl<'a> Parser<'a> { } else if self.check_keyword(kw::Unsafe) { match safety { Safety::Unsafe(sp) => Some(WrongKw::Duplicated(sp)), + Safety::Safe(sp) => { + recover_safety = Safety::Unsafe(self.token.span); + Some(WrongKw::Misplaced(sp)) + } Safety::Default => { recover_safety = Safety::Unsafe(self.token.span); Some(WrongKw::Misplaced(ext_start_sp)) } } + } else if self.check_keyword(kw::Safe) { + match safety { + Safety::Safe(sp) => Some(WrongKw::Duplicated(sp)), + Safety::Unsafe(sp) => { + recover_safety = Safety::Safe(self.token.span); + Some(WrongKw::Misplaced(sp)) + } + Safety::Default => { + recover_safety = Safety::Safe(self.token.span); + Some(WrongKw::Misplaced(ext_start_sp)) + } + } } else { None }; diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index c2183258eef1..2f12459da573 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -11,14 +11,13 @@ mod stmt; mod ty; use crate::lexer::UnmatchedDelim; -pub use attr_wrapper::AttrWrapper; +use attr_wrapper::AttrWrapper; pub use diagnostics::AttemptLocalParseRecovery; pub(crate) use expr::ForbiddenLetReason; pub(crate) use item::FnParseMode; pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; -pub use path::PathStyle; +use path::PathStyle; -use core::fmt; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind}; use rustc_ast::tokenstream::{AttributesData, DelimSpacing, DelimSpan, Spacing}; @@ -37,7 +36,7 @@ use rustc_session::parse::ParseSess; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use std::ops::Range; -use std::{mem, slice}; +use std::{fmt, mem, slice}; use thin_vec::ThinVec; use tracing::debug; @@ -146,7 +145,7 @@ pub struct Parser<'a> { /// The current token. pub token: Token, /// The spacing for the current token. - pub token_spacing: Spacing, + token_spacing: Spacing, /// The previous token. pub prev_token: Token, pub capture_cfg: bool, @@ -187,7 +186,7 @@ pub struct Parser<'a> { current_closure: Option, /// Whether the parser is allowed to do recovery. /// This is disabled when parsing macro arguments, see #103534 - pub recovery: Recovery, + recovery: Recovery, } // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure @@ -197,10 +196,10 @@ rustc_data_structures::static_assert_size!(Parser<'_>, 264); /// Stores span information about a closure. #[derive(Clone, Debug)] -pub struct ClosureSpans { - pub whole_closure: Span, - pub closing_pipe: Span, - pub body: Span, +struct ClosureSpans { + whole_closure: Span, + closing_pipe: Span, + body: Span, } /// Indicates a range of tokens that should be replaced by @@ -220,13 +219,13 @@ pub struct ClosureSpans { /// the first macro inner attribute to invoke a proc-macro). /// When create a `TokenStream`, the inner attributes get inserted /// into the proper place in the token stream. -pub type ReplaceRange = (Range, Vec<(FlatToken, Spacing)>); +type ReplaceRange = (Range, Vec<(FlatToken, Spacing)>); /// Controls how we capture tokens. Capturing can be expensive, /// so we try to avoid performing capturing in cases where /// we will never need an `AttrTokenStream`. #[derive(Copy, Clone, Debug)] -pub enum Capturing { +enum Capturing { /// We aren't performing any capturing - this is the default mode. No, /// We are capturing tokens @@ -336,18 +335,6 @@ impl TokenType { } } -/// Used by [`Parser::expect_any_with_type`]. -#[derive(Copy, Clone, Debug)] -enum TokenExpectType { - /// Unencountered tokens are inserted into [`Parser::expected_tokens`]. - /// See [`Parser::check`]. - Expect, - - /// Unencountered tokens are not inserted into [`Parser::expected_tokens`]. - /// See [`Parser::check_noexpect`]. - NoExpect, -} - /// A sequence separator. #[derive(Debug)] struct SeqSep { @@ -374,13 +361,13 @@ pub enum FollowedByType { } #[derive(Copy, Clone, Debug)] -pub enum Trailing { +enum Trailing { No, Yes, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum TokenDescription { +pub(super) enum TokenDescription { ReservedIdentifier, Keyword, ReservedKeyword, @@ -388,7 +375,7 @@ pub enum TokenDescription { } impl TokenDescription { - pub fn from_token(token: &Token) -> Option { + pub(super) fn from_token(token: &Token) -> Option { match token.kind { _ if token.is_special_ident() => Some(TokenDescription::ReservedIdentifier), _ if token.is_used_keyword() => Some(TokenDescription::Keyword), @@ -502,7 +489,7 @@ impl<'a> Parser<'a> { /// Expect next token to be edible or inedible token. If edible, /// then consume it; if inedible, then return without consuming /// anything. Signal a fatal error if next token is unexpected. - pub fn expect_one_of( + fn expect_one_of( &mut self, edible: &[TokenKind], inedible: &[TokenKind], @@ -572,7 +559,7 @@ impl<'a> Parser<'a> { /// the main purpose of this function is to reduce the cluttering of the suggestions list /// which using the normal eat method could introduce in some cases. #[inline] - pub fn eat_noexpect(&mut self, tok: &TokenKind) -> bool { + fn eat_noexpect(&mut self, tok: &TokenKind) -> bool { let is_present = self.check_noexpect(tok); if is_present { self.bump() @@ -808,11 +795,13 @@ impl<'a> Parser<'a> { } /// Checks if the next token is contained within `kets`, and returns `true` if so. - fn expect_any_with_type(&mut self, kets: &[&TokenKind], expect: TokenExpectType) -> bool { - kets.iter().any(|k| match expect { - TokenExpectType::Expect => self.check(k), - TokenExpectType::NoExpect => self.check_noexpect(k), - }) + fn expect_any_with_type( + &mut self, + kets_expected: &[&TokenKind], + kets_not_expected: &[&TokenKind], + ) -> bool { + kets_expected.iter().any(|k| self.check(k)) + || kets_not_expected.iter().any(|k| self.check_noexpect(k)) } /// Parses a sequence until the specified delimiters. The function @@ -820,9 +809,9 @@ impl<'a> Parser<'a> { /// closing bracket. fn parse_seq_to_before_tokens( &mut self, - kets: &[&TokenKind], + kets_expected: &[&TokenKind], + kets_not_expected: &[&TokenKind], sep: SeqSep, - expect: TokenExpectType, mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (ThinVec, Trailing, Recovered)> { let mut first = true; @@ -830,7 +819,7 @@ impl<'a> Parser<'a> { let mut trailing = Trailing::No; let mut v = ThinVec::new(); - while !self.expect_any_with_type(kets, expect) { + while !self.expect_any_with_type(kets_expected, kets_not_expected) { if let token::CloseDelim(..) | token::Eof = self.token.kind { break; } @@ -928,7 +917,8 @@ impl<'a> Parser<'a> { if self.token == token::Colon { // we will try to recover in `maybe_recover_struct_lit_bad_delims` return Err(expect_err); - } else if let [token::CloseDelim(Delimiter::Parenthesis)] = kets + } else if let [token::CloseDelim(Delimiter::Parenthesis)] = + kets_expected { return Err(expect_err); } else { @@ -941,7 +931,9 @@ impl<'a> Parser<'a> { } } } - if sep.trailing_sep_allowed && self.expect_any_with_type(kets, expect) { + if sep.trailing_sep_allowed + && self.expect_any_with_type(kets_expected, kets_not_expected) + { trailing = Trailing::Yes; break; } @@ -1021,7 +1013,7 @@ impl<'a> Parser<'a> { sep: SeqSep, f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, (ThinVec, Trailing, Recovered)> { - self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f) + self.parse_seq_to_before_tokens(&[ket], &[], sep, f) } /// Parses a sequence, including only the closing delimiter. The function @@ -1221,6 +1213,8 @@ impl<'a> Parser<'a> { fn parse_safety(&mut self, case: Case) -> Safety { if self.eat_keyword_case(kw::Unsafe, case) { Safety::Unsafe(self.prev_token.uninterpolated_span()) + } else if self.eat_keyword_case(kw::Safe, case) { + Safety::Safe(self.prev_token.uninterpolated_span()) } else { Safety::Default } @@ -1336,17 +1330,6 @@ impl<'a> Parser<'a> { }) } - fn parse_or_use_outer_attributes( - &mut self, - already_parsed_attrs: Option, - ) -> PResult<'a, AttrWrapper> { - if let Some(attrs) = already_parsed_attrs { - Ok(attrs) - } else { - self.parse_outer_attributes() - } - } - /// Parses a single token tree from the input. pub fn parse_token_tree(&mut self) -> TokenTree { match self.token.kind { @@ -1518,7 +1501,7 @@ impl<'a> Parser<'a> { } } - pub fn collect_tokens_no_attrs( + fn collect_tokens_no_attrs( &mut self, f: impl FnOnce(&mut Self) -> PResult<'a, R>, ) -> PResult<'a, R> { @@ -1539,8 +1522,10 @@ impl<'a> Parser<'a> { }) } - // debug view of the parser's token stream, up to `{lookahead}` tokens - pub fn debug_lookahead(&self, lookahead: usize) -> impl fmt::Debug + '_ { + // Debug view of the parser's token stream, up to `{lookahead}` tokens. + // Only used when debugging. + #[allow(unused)] + pub(crate) fn debug_lookahead(&self, lookahead: usize) -> impl fmt::Debug + '_ { struct DebugParser<'dbg> { parser: &'dbg Parser<'dbg>, lookahead: usize, @@ -1600,7 +1585,7 @@ pub(crate) fn make_unclosed_delims_error( if let Some(sp) = unmatched.unclosed_span { spans.push(sp); }; - let err = psess.dcx.create_err(MismatchedClosingDelimiter { + let err = psess.dcx().create_err(MismatchedClosingDelimiter { spans, delimiter: pprust::token_kind_to_string(&token::CloseDelim(found_delim)).to_string(), unmatched: unmatched.found_span, @@ -1616,7 +1601,7 @@ pub(crate) fn make_unclosed_delims_error( /// is then 'parsed' to build up an `AttrTokenStream` with nested /// `AttrTokenTree::Delimited` tokens. #[derive(Debug, Clone)] -pub enum FlatToken { +enum FlatToken { /// A token - this holds both delimiter (e.g. '{' and '}') /// and non-delimiter tokens Token(Token), diff --git a/compiler/rustc_parse/src/parser/mut_visit/tests.rs b/compiler/rustc_parse/src/parser/mut_visit/tests.rs index b3cb28af657e..677bcdf7fcdf 100644 --- a/compiler/rustc_parse/src/parser/mut_visit/tests.rs +++ b/compiler/rustc_parse/src/parser/mut_visit/tests.rs @@ -21,14 +21,12 @@ impl MutVisitor for ToZzIdentMutVisitor { } } -// Maybe add to `expand.rs`. -macro_rules! assert_pred { - ($pred:expr, $predname:expr, $a:expr , $b:expr) => {{ - let pred_val = $pred; +macro_rules! assert_matches_codepattern { + ($a:expr , $b:expr) => {{ let a_val = $a; let b_val = $b; - if !(pred_val(&a_val, &b_val)) { - panic!("expected args satisfying {}, got {} and {}", $predname, a_val, b_val); + if !matches_codepattern(&a_val, &b_val) { + panic!("expected args satisfying `matches_codepattern`, got {} and {}", a_val, b_val); } }}; } @@ -41,9 +39,7 @@ fn ident_transformation() { let mut krate = string_to_crate("#[a] mod b {fn c (d : e, f : g) {h!(i,j,k);l;m}}".to_string()); zz_visitor.visit_crate(&mut krate); - assert_pred!( - matches_codepattern, - "matches_codepattern", + assert_matches_codepattern!( print_crate_items(&krate), "#[zz]mod zz{fn zz(zz:zz,zz:zz){zz!(zz,zz,zz);zz;zz}}".to_string() ); @@ -61,9 +57,7 @@ fn ident_transformation_in_defs() { .to_string(), ); zz_visitor.visit_crate(&mut krate); - assert_pred!( - matches_codepattern, - "matches_codepattern", + assert_matches_codepattern!( print_crate_items(&krate), "macro_rules! zz{(zz$zz:zz$(zz $zz:zz)zz+=>(zz$(zz$zz$zz)+))}".to_string() ); diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 619c4c63e511..a0b704aeea5f 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -47,7 +47,7 @@ impl<'a> Parser<'a> { token.can_begin_expr() // This exception is here for backwards compatibility. && !token.is_keyword(kw::Let) - && (token.span.edition().at_least_rust_2024() || !token.is_keyword(kw::Const)) + && (!token.is_keyword(kw::Const) || token.span.edition().at_least_rust_2024()) } NonterminalKind::Ty => token.can_begin_type(), NonterminalKind::Ident => get_macro_ident(token).is_some(), diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 8af415f7c9dd..03aea0888d95 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -10,7 +10,7 @@ use crate::errors::{ UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, }; -use crate::parser::expr::could_be_unclosed_char_literal; +use crate::parser::expr::{could_be_unclosed_char_literal, LhsExpr}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor}; use rustc_ast::ptr::P; @@ -398,9 +398,8 @@ impl<'a> Parser<'a> { // Parse an associative expression such as `+ expr`, `% expr`, ... // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`]. - if let Ok(expr) = - snapshot.parse_expr_assoc_with(0, expr.into()).map_err(|err| err.cancel()) - { + let lhs = LhsExpr::Parsed { expr, starts_statement: false }; + if let Ok(expr) = snapshot.parse_expr_assoc_with(0, lhs).map_err(|err| err.cancel()) { // We got a valid expression. self.restore_snapshot(snapshot); self.restrictions.remove(Restrictions::IS_PAT); @@ -851,7 +850,7 @@ impl<'a> Parser<'a> { let sp = self.psess.source_map().start_point(self.token.span); if let Some(sp) = self.psess.ambiguous_block_expr_parse.borrow().get(&sp) { - err.subdiagnostic(self.dcx(), ExprParenthesesNeeded::surrounding(*sp)); + err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } Err(err) diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index d845e8ab90d5..da8d1194325b 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -7,8 +7,8 @@ use ast::token::IdentIsRaw; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::{ - self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocConstraint, - AssocConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs, + self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocItemConstraint, + AssocItemConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs, Path, PathSegment, QSelf, }; use rustc_errors::{Applicability, Diag, PResult}; @@ -20,7 +20,7 @@ use tracing::debug; /// Specifies how to parse a path. #[derive(Copy, Clone, PartialEq)] -pub enum PathStyle { +pub(super) enum PathStyle { /// In some contexts, notably in expressions, paths with generic arguments are ambiguous /// with something else. For example, in expressions `segment < ....` can be interpreted /// as a comparison and `segment ( ....` can be interpreted as a function call. @@ -720,10 +720,7 @@ impl<'a> Parser<'a> { )); } let kind = if self.eat(&token::Colon) { - // Parse associated type constraint bound. - - let bounds = self.parse_generic_bounds()?; - AssocConstraintKind::Bound { bounds } + AssocItemConstraintKind::Bound { bounds: self.parse_generic_bounds()? } } else if self.eat(&token::Eq) { self.parse_assoc_equality_term( ident, @@ -735,17 +732,17 @@ impl<'a> Parser<'a> { }; let span = lo.to(self.prev_token.span); - // Gate associated type bounds, e.g., `Iterator`. - if let AssocConstraintKind::Bound { .. } = kind { - if let Some(ast::GenericArgs::Parenthesized(args)) = &gen_args - && args.inputs.is_empty() - && matches!(args.output, ast::FnRetTy::Default(..)) - { - self.psess.gated_spans.gate(sym::return_type_notation, span); - } + + if let AssocItemConstraintKind::Bound { .. } = kind + && let Some(ast::GenericArgs::Parenthesized(args)) = &gen_args + && args.inputs.is_empty() + && let ast::FnRetTy::Default(..) = args.output + { + self.psess.gated_spans.gate(sym::return_type_notation, span); } + let constraint = - AssocConstraint { id: ast::DUMMY_NODE_ID, ident, gen_args, kind, span }; + AssocItemConstraint { id: ast::DUMMY_NODE_ID, ident, gen_args, kind, span }; Ok(Some(AngleBracketedArg::Constraint(constraint))) } else { // we only want to suggest `:` and `=` in contexts where the previous token @@ -772,7 +769,7 @@ impl<'a> Parser<'a> { ident: Ident, gen_args: Option<&GenericArgs>, eq: Span, - ) -> PResult<'a, AssocConstraintKind> { + ) -> PResult<'a, AssocItemConstraintKind> { let arg = self.parse_generic_arg(None)?; let span = ident.span.to(self.prev_token.span); let term = match arg { @@ -820,7 +817,7 @@ impl<'a> Parser<'a> { return Err(err); } }; - Ok(AssocConstraintKind::Equality { term }) + Ok(AssocItemConstraintKind::Equality { term }) } /// We do not permit arbitrary expressions as const arguments. They must be one of: @@ -923,7 +920,8 @@ impl<'a> Parser<'a> { // Fall back by trying to parse a const-expr expression. If we successfully do so, // then we should report an error that it needs to be wrapped in braces. let snapshot = self.create_snapshot_for_diagnostic(); - match self.parse_expr_res(Restrictions::CONST_EXPR, None) { + let attrs = self.parse_outer_attributes()?; + match self.parse_expr_res(Restrictions::CONST_EXPR, attrs) { Ok(expr) => { return Ok(Some(self.dummy_const_arg_needs_braces( self.dcx().struct_span_err(expr.span, "invalid const generic expression"), @@ -941,7 +939,7 @@ impl<'a> Parser<'a> { } /// Given a arg inside of generics, we try to destructure it as if it were the LHS in - /// `LHS = ...`, i.e. an associated type binding. + /// `LHS = ...`, i.e. an associated item binding. /// This returns a bool indicating if there are any `for<'a, 'b>` binder args, the /// identifier, and any GAT arguments. fn get_ident_from_generic_arg( diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 7424fbea9b05..d65f6ff68eee 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -15,7 +15,7 @@ use ast::Label; use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, TokenKind}; -use rustc_ast::util::classify; +use rustc_ast::util::classify::{self, TrailingBrace}; use rustc_ast::{AttrStyle, AttrVec, LocalKind, MacCall, MacCallStmt, MacStmtStyle}; use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Recovered, Stmt}; use rustc_ast::{StmtKind, DUMMY_NODE_ID}; @@ -31,7 +31,7 @@ impl<'a> Parser<'a> { /// Parses a statement. This stops just before trailing semicolons on everything but items. /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed. // Public for rustfmt usage. - pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option> { + pub(super) fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option> { Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|e| { e.emit(); self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); @@ -126,9 +126,9 @@ impl<'a> Parser<'a> { // Remainder are line-expr stmts. let e = match force_collect { ForceCollect::Yes => self.collect_tokens_no_attrs(|this| { - this.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs)) + this.parse_expr_res(Restrictions::STMT_EXPR, attrs) })?, - ForceCollect::No => self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs))?, + ForceCollect::No => self.parse_expr_res(Restrictions::STMT_EXPR, attrs)?, }; if matches!(e.kind, ExprKind::Assign(..)) && self.eat_keyword(kw::Else) { let bl = self.parse_block()?; @@ -174,10 +174,7 @@ impl<'a> Parser<'a> { // Perform this outside of the `collect_tokens_trailing_token` closure, // since our outer attributes do not apply to this part of the expression let expr = self.with_res(Restrictions::STMT_EXPR, |this| { - this.parse_expr_assoc_with( - 0, - LhsExpr::AlreadyParsed { expr, starts_statement: true }, - ) + this.parse_expr_assoc_with(0, LhsExpr::Parsed { expr, starts_statement: true }) })?; Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr))) } else { @@ -210,10 +207,8 @@ impl<'a> Parser<'a> { let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac)); let e = self.maybe_recover_from_bad_qpath(e)?; let e = self.parse_expr_dot_or_call_with(e, lo, attrs)?; - let e = self.parse_expr_assoc_with( - 0, - LhsExpr::AlreadyParsed { expr: e, starts_statement: false }, - )?; + let e = self + .parse_expr_assoc_with(0, LhsExpr::Parsed { expr: e, starts_statement: false })?; StmtKind::Expr(e) }; Ok(self.mk_stmt(lo.to(hi), kind)) @@ -407,18 +402,24 @@ impl<'a> Parser<'a> { fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) { if let Some(trailing) = classify::expr_trailing_brace(init) { - let sugg = match &trailing.kind { - ExprKind::MacCall(mac) => errors::WrapInParentheses::MacroArgs { - left: mac.args.dspan.open, - right: mac.args.dspan.close, - }, - _ => errors::WrapInParentheses::Expression { - left: trailing.span.shrink_to_lo(), - right: trailing.span.shrink_to_hi(), - }, + let (span, sugg) = match trailing { + TrailingBrace::MacCall(mac) => ( + mac.span(), + errors::WrapInParentheses::MacroArgs { + left: mac.args.dspan.open, + right: mac.args.dspan.close, + }, + ), + TrailingBrace::Expr(expr) => ( + expr.span, + errors::WrapInParentheses::Expression { + left: expr.span.shrink_to_lo(), + right: expr.span.shrink_to_hi(), + }, + ), }; self.dcx().emit_err(errors::InvalidCurlyInLetElse { - span: trailing.span.with_lo(trailing.span.hi() - BytePos(1)), + span: span.with_lo(span.hi() - BytePos(1)), sugg, }); } diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index a31e350541ab..3a4690670af3 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -1,5 +1,7 @@ use crate::parser::ForceCollect; -use crate::{new_parser_from_source_str, parser::Parser, source_file_to_stream}; +use crate::{ + new_parser_from_source_str, parser::Parser, source_str_to_stream, unwrap_or_emit_fatal, +}; use ast::token::IdentIsRaw; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token}; @@ -29,7 +31,11 @@ fn psess() -> ParseSess { /// Map string to parser (via tts). fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> { - new_parser_from_source_str(psess, PathBuf::from("bogofile").into(), source_str) + unwrap_or_emit_fatal(new_parser_from_source_str( + psess, + PathBuf::from("bogofile").into(), + source_str, + )) } fn create_test_handler() -> (DiagCtxt, Lrc, Arc>>) { @@ -55,7 +61,7 @@ where { let mut p = string_to_parser(&psess, s); let x = f(&mut p).unwrap(); - p.psess.dcx.abort_if_errors(); + p.dcx().abort_if_errors(); x } @@ -82,11 +88,12 @@ where /// Maps a string to tts, using a made-up filename. pub(crate) fn string_to_stream(source_str: String) -> TokenStream { let psess = psess(); - source_file_to_stream( + unwrap_or_emit_fatal(source_str_to_stream( &psess, - psess.source_map().new_source_file(PathBuf::from("bogofile").into(), source_str), + PathBuf::from("bogofile").into(), + source_str, None, - ) + )) } /// Parses a string, returns a crate. @@ -186,7 +193,7 @@ impl Write for Shared { #[allow(rustc::untranslatable_diagnostic)] // no translation needed for tests fn test_harness(file_text: &str, span_labels: Vec, expected_output: &str) { create_default_session_globals_then(|| { - let (handler, source_map, output) = create_test_handler(); + let (dcx, source_map, output) = create_test_handler(); source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned()); let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end); @@ -198,7 +205,7 @@ fn test_harness(file_text: &str, span_labels: Vec, expected_output: & println!("text: {:?}", source_map.span_to_snippet(span)); } - handler.span_err(msp, "foo"); + dcx.handle().span_err(msp, "foo"); assert!( expected_output.chars().next() == Some('\n'), @@ -1068,7 +1075,8 @@ fn parse_item_from_source_str( source: String, psess: &ParseSess, ) -> PResult<'_, Option>> { - new_parser_from_source_str(psess, name, source).parse_item(ForceCollect::No) + unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source)) + .parse_item(ForceCollect::No) } // Produces a `rustc_span::span`. @@ -1349,7 +1357,7 @@ fn ttdelim_span() { source: String, psess: &ParseSess, ) -> PResult<'_, P> { - new_parser_from_source_str(psess, name, source).parse_expr() + unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source)).parse_expr() } create_default_session_globals_then(|| { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 2df8f58507b0..fcd623b477f5 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -9,7 +9,7 @@ use crate::errors::{ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, Token, TokenKind}; +use rustc_ast::token::{self, BinOpToken, Delimiter, Token, TokenKind}; use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, FnRetTy, GenericBound, @@ -127,7 +127,7 @@ impl<'a> Parser<'a> { /// Parse a type suitable for a field definition. /// The difference from `parse_ty` is that this version /// allows anonymous structs and unions. - pub fn parse_ty_for_field_def(&mut self) -> PResult<'a, P> { + pub(super) fn parse_ty_for_field_def(&mut self) -> PResult<'a, P> { if self.can_begin_anon_struct_or_union() { self.parse_anon_struct_or_union() } else { @@ -194,7 +194,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_ty_for_where_clause(&mut self) -> PResult<'a, P> { self.parse_ty_common( AllowPlus::Yes, - AllowCVariadic::Yes, + AllowCVariadic::No, RecoverQPath::Yes, RecoverReturnSign::OnlyFatArrow, None, @@ -316,7 +316,7 @@ impl<'a> Parser<'a> { TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn) } (TyKind::TraitObject(bounds, _), kw::Impl) => { - TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, None) + TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds) } _ => return Err(err), }; @@ -344,8 +344,9 @@ impl<'a> Parser<'a> { match allow_c_variadic { AllowCVariadic::Yes => TyKind::CVarArgs, AllowCVariadic::No => { - // FIXME(Centril): Should we just allow `...` syntactically + // FIXME(c_variadic): Should we just allow `...` syntactically // anywhere in a type and use semantic restrictions instead? + // NOTE: This may regress certain MBE calls if done incorrectly. let guar = self .dcx() .emit_err(NestedCVariadicType { span: lo.to(self.prev_token.span) }); @@ -669,33 +670,26 @@ impl<'a> Parser<'a> { }) } - // parse precise captures, if any. This is `use<'lt, 'lt, P, P>`; a list of - // lifetimes and ident params (including SelfUpper). These are validated later - // for order, duplication, and whether they actually reference params. - let precise_capturing = if self.eat_keyword(kw::Use) { - let use_span = self.prev_token.span; - self.psess.gated_spans.gate(sym::precise_capturing, use_span); - let (args, args_span) = self.parse_precise_capturing_args()?; - Some(P((args, use_span.to(args_span)))) - } else { - None - }; - // Always parse bounds greedily for better error recovery. let bounds = self.parse_generic_bounds()?; *impl_dyn_multi = bounds.len() > 1 || self.prev_token.kind == TokenKind::BinOp(token::Plus); - Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, precise_capturing)) + Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)) } fn parse_precise_capturing_args( &mut self, ) -> PResult<'a, (ThinVec, Span)> { let lo = self.token.span; - let (args, _) = self.parse_unspanned_seq( - &TokenKind::Lt, - &TokenKind::Gt, + self.expect_lt()?; + let (args, _, _) = self.parse_seq_to_before_tokens( + &[&TokenKind::Gt], + &[ + &TokenKind::Ge, + &TokenKind::BinOp(BinOpToken::Shr), + &TokenKind::BinOpEq(BinOpToken::Shr), + ], SeqSep::trailing_allowed(token::Comma), |self_| { if self_.check_keyword(kw::SelfUpper) { @@ -716,6 +710,7 @@ impl<'a> Parser<'a> { } }, )?; + self.expect_gt()?; Ok((args, lo.to(self.prev_token.span))) } @@ -827,6 +822,7 @@ impl<'a> Parser<'a> { || self.check(&token::OpenDelim(Delimiter::Parenthesis)) || self.check_keyword(kw::Const) || self.check_keyword(kw::Async) + || self.check_keyword(kw::Use) } /// Parses a bound according to the grammar: @@ -843,6 +839,14 @@ impl<'a> Parser<'a> { let bound = if self.token.is_lifetime() { self.error_lt_bound_with_modifiers(modifiers); self.parse_generic_lt_bound(lo, inner_lo, has_parens)? + } else if self.eat_keyword(kw::Use) { + // parse precise captures, if any. This is `use<'lt, 'lt, P, P>`; a list of + // lifetimes and ident params (including SelfUpper). These are validated later + // for order, duplication, and whether they actually reference params. + let use_span = self.prev_token.span; + self.psess.gated_spans.gate(sym::precise_capturing, use_span); + let (args, args_span) = self.parse_precise_capturing_args()?; + GenericBound::Use(args, use_span.to(args_span)) } else { self.parse_generic_ty_bound(lo, has_parens, modifiers, &leading_token)? }; @@ -1002,7 +1006,7 @@ impl<'a> Parser<'a> { Applicability::MaybeIncorrect, ) } - TyKind::ImplTrait(_, bounds, None) + TyKind::ImplTrait(_, bounds) if let [GenericBound::Trait(tr, ..), ..] = bounds.as_slice() => { ( diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index f88edf29dceb..4ca52146039c 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -10,6 +10,7 @@ use rustc_errors::{Applicability, FatalError, PResult}; use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_session::errors::report_lit_error; use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; +use rustc_session::lint::BuiltinLintDiag; use rustc_session::parse::ParseSess; use rustc_span::{sym, Span, Symbol}; @@ -24,15 +25,21 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) { match attr_info { // `rustc_dummy` doesn't have any restrictions specific to built-in attributes. Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => { - check_builtin_attribute(psess, attr, *name, *template) + match parse_meta(psess, attr) { + Ok(meta) => check_builtin_meta_item(psess, &meta, attr.style, *name, *template), + Err(err) => { + err.emit(); + } + } } _ if let AttrArgs::Eq(..) = attr.get_normal_item().args => { // All key-value attributes are restricted to meta-item syntax. - parse_meta(psess, attr) - .map_err(|err| { + match parse_meta(psess, attr) { + Ok(_) => {} + Err(err) => { err.emit(); - }) - .ok(); + } + } } _ => {} } @@ -41,6 +48,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) { pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> { let item = attr.get_normal_item(); Ok(MetaItem { + unsafety: item.unsafety, span: attr.span, path: item.path.clone(), kind: match &item.args { @@ -57,7 +65,7 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met let res = match res { Ok(lit) => { if token_lit.suffix.is_some() { - let mut err = psess.dcx.struct_span_err( + let mut err = psess.dcx().struct_span_err( expr.span, "suffixed literals are not allowed in attributes", ); @@ -90,7 +98,7 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met // the error because an earlier error will have already // been reported. let msg = "attribute value must be a literal"; - let mut err = psess.dcx.struct_span_err(expr.span, msg); + let mut err = psess.dcx().struct_span_err(expr.span, msg); if let ast::ExprKind::Err(_) = expr.kind { err.downgrade_to_delayed_bug(); } @@ -102,21 +110,21 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met }) } -pub fn check_meta_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) { +fn check_meta_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) { if let Delimiter::Parenthesis = delim { return; } - psess.dcx.emit_err(errors::MetaBadDelim { + psess.dcx().emit_err(errors::MetaBadDelim { span: span.entire(), sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close }, }); } -pub fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) { +pub(super) fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) { if let Delimiter::Parenthesis = delim { return; } - psess.dcx.emit_err(errors::CfgAttrBadDelim { + psess.dcx().emit_err(errors::CfgAttrBadDelim { span: span.entire(), sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close }, }); @@ -132,20 +140,6 @@ fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaIte } } -pub fn check_builtin_attribute( - psess: &ParseSess, - attr: &Attribute, - name: Symbol, - template: AttributeTemplate, -) { - match parse_meta(psess, attr) { - Ok(meta) => check_builtin_meta_item(psess, &meta, attr.style, name, template), - Err(err) => { - err.emit(); - } - } -} - pub fn check_builtin_meta_item( psess: &ParseSess, meta: &MetaItem, @@ -176,39 +170,28 @@ fn emit_malformed_attribute( }; let error_msg = format!("malformed `{name}` attribute input"); - let mut msg = "attribute must be of the form ".to_owned(); let mut suggestions = vec![]; - let mut first = true; let inner = if style == ast::AttrStyle::Inner { "!" } else { "" }; if template.word { - first = false; - let code = format!("#{inner}[{name}]"); - msg.push_str(&format!("`{code}`")); - suggestions.push(code); + suggestions.push(format!("#{inner}[{name}]")); } if let Some(descr) = template.list { - if !first { - msg.push_str(" or "); - } - first = false; - let code = format!("#{inner}[{name}({descr})]"); - msg.push_str(&format!("`{code}`")); - suggestions.push(code); + suggestions.push(format!("#{inner}[{name}({descr})]")); } if let Some(descr) = template.name_value_str { - if !first { - msg.push_str(" or "); - } - let code = format!("#{inner}[{name} = \"{descr}\"]"); - msg.push_str(&format!("`{code}`")); - suggestions.push(code); + suggestions.push(format!("#{inner}[{name} = \"{descr}\"]")); } - suggestions.sort(); if should_warn(name) { - psess.buffer_lint(ILL_FORMED_ATTRIBUTE_INPUT, span, ast::CRATE_NODE_ID, msg); + psess.buffer_lint( + ILL_FORMED_ATTRIBUTE_INPUT, + span, + ast::CRATE_NODE_ID, + BuiltinLintDiag::IllFormedAttributeInput { suggestions: suggestions.clone() }, + ); } else { + suggestions.sort(); psess - .dcx + .dcx() .struct_span_err(span, error_msg) .with_span_suggestions( span, diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index faf6ca784670..7e22644977d1 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -4,14 +4,16 @@ //! Parsing does not happen at runtime: structures of `std::fmt::rt` are //! generated instead. +// tidy-alphabetical-start +// We want to be able to build this crate with a stable compiler, +// so no `#![feature]` attributes should be added. +#![deny(unstable_features)] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", html_playground_url = "https://play.rust-lang.org/", test(attr(deny(warnings))) )] -// We want to be able to build this crate with a stable compiler, -// so no `#![feature]` attributes should be added. -#![deny(unstable_features)] +// tidy-alphabetical-end use rustc_lexer::unescape; pub use Alignment::*; @@ -286,13 +288,11 @@ impl<'a> Iterator for Parser<'a> { lbrace_byte_pos.to(InnerOffset(rbrace_byte_pos.0 + width)), ); } - } else { - if let Some(&(_, maybe)) = self.cur.peek() { - match maybe { - '?' => self.suggest_format_debug(), - '<' | '^' | '>' => self.suggest_format_align(maybe), - _ => self.suggest_positional_arg_instead_of_captured_arg(arg), - } + } else if let Some(&(_, maybe)) = self.cur.peek() { + match maybe { + '?' => self.suggest_format_debug(), + '<' | '^' | '>' => self.suggest_format_align(maybe), + _ => self.suggest_positional_arg_instead_of_captured_arg(arg), } } Some(NextArgument(Box::new(arg))) @@ -1028,7 +1028,7 @@ fn find_width_map_from_snippet( if next_c == '{' { // consume up to 6 hexanumeric chars let digits_len = - s.clone().take(6).take_while(|(_, c)| c.is_digit(16)).count(); + s.clone().take(6).take_while(|(_, c)| c.is_ascii_hexdigit()).count(); let len_utf8 = s .as_str() @@ -1047,14 +1047,14 @@ fn find_width_map_from_snippet( width += required_skips + 2; s.nth(digits_len); - } else if next_c.is_digit(16) { + } else if next_c.is_ascii_hexdigit() { width += 1; // We suggest adding `{` and `}` when appropriate, accept it here as if // it were correct let mut i = 0; // consume up to 6 hexanumeric chars while let (Some((_, c)), _) = (s.next(), i < 6) { - if c.is_digit(16) { + if c.is_ascii_hexdigit() { width += 1; } else { break; diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 3deefcaa06c3..84dbd192723e 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -341,7 +341,7 @@ passes_implied_feature_not_exist = feature `{$implied_by}` implying `{$feature}` does not exist passes_incorrect_do_not_recommend_location = - `#[do_not_recommend]` can only be placed on trait implementations + `#[diagnostic::do_not_recommend]` can only be placed on trait implementations passes_incorrect_meta_item = expected a quoted string literal passes_incorrect_meta_item_suggestion = consider surrounding this with quotes @@ -384,6 +384,10 @@ passes_invalid_attr_at_crate_level = passes_invalid_attr_at_crate_level_item = the inner attribute doesn't annotate this {$kind} +passes_invalid_attr_unsafe = `{$name}` is not an unsafe attribute + .suggestion = remove the `unsafe(...)` + .note = extraneous unsafe is not allowed in attributes + passes_invalid_macro_export_arguments = `{$name}` isn't a valid `#[macro_export]` argument passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments @@ -591,6 +595,15 @@ passes_pass_by_value = passes_proc_macro_bad_sig = {$kind} has incorrect signature +passes_remove_fields = + consider removing { $num -> + [one] this + *[other] these + } { $num -> + [one] field + *[other] fields + } + passes_repr_conflicting = conflicting representation hints diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index aadbd7473139..a0b3470df6db 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -8,9 +8,11 @@ use crate::{errors, fluent_generated as fluent}; use rustc_ast::{ast, AttrKind, AttrStyle, Attribute, LitKind}; use rustc_ast::{MetaItemKind, MetaItemLit, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::StashKey; -use rustc_errors::{Applicability, DiagCtxt, IntoDiagArg, MultiSpan}; -use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; +use rustc_errors::{Applicability, IntoDiagArg, MultiSpan}; +use rustc_errors::{DiagCtxtHandle, StashKey}; +use rustc_feature::{ + is_unsafe_attr, AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP, +}; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self as hir}; @@ -39,15 +41,13 @@ use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::ObligationCtxt; use std::cell::Cell; use std::collections::hash_map::Entry; +use tracing::debug; #[derive(LintDiagnostic)] #[diag(passes_diagnostic_diagnostic_on_unimplemented_only_for_traits)] -pub struct DiagnosticOnUnimplementedOnlyForTraits; +struct DiagnosticOnUnimplementedOnlyForTraits; -pub(crate) fn target_from_impl_item<'tcx>( - tcx: TyCtxt<'tcx>, - impl_item: &hir::ImplItem<'_>, -) -> Target { +fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target { match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, hir::ImplItemKind::Fn(..) => { @@ -99,7 +99,7 @@ struct CheckAttrVisitor<'tcx> { } impl<'tcx> CheckAttrVisitor<'tcx> { - pub fn dcx(&self) -> &'tcx DiagCtxt { + fn dcx(&self) -> DiagCtxtHandle<'tcx> { self.tcx.dcx() } @@ -116,92 +116,98 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let mut seen = FxHashMap::default(); let attrs = self.tcx.hir().attrs(hir_id); for attr in attrs { - if attr.path_matches(&[sym::diagnostic, sym::on_unimplemented]) { - self.check_diagnostic_on_unimplemented(attr.span, hir_id, target); - } - match attr.name_or_empty() { - sym::do_not_recommend => self.check_do_not_recommend(attr.span, target), - sym::inline => self.check_inline(hir_id, attr, span, target), - sym::coverage => self.check_coverage(hir_id, attr, span, target), - sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target), - sym::marker => self.check_marker(hir_id, attr, span, target), - sym::target_feature => self.check_target_feature(hir_id, attr, span, target, attrs), - sym::thread_local => self.check_thread_local(attr, span, target), - sym::track_caller => { + self.check_unsafe_attr(attr); + + match attr.path().as_slice() { + [sym::diagnostic, sym::do_not_recommend] => { + self.check_do_not_recommend(attr.span, hir_id, target) + } + [sym::diagnostic, sym::on_unimplemented] => { + self.check_diagnostic_on_unimplemented(attr.span, hir_id, target) + } + [sym::inline] => self.check_inline(hir_id, attr, span, target), + [sym::coverage] => self.check_coverage(hir_id, attr, span, target), + [sym::non_exhaustive] => self.check_non_exhaustive(hir_id, attr, span, target), + [sym::marker] => self.check_marker(hir_id, attr, span, target), + [sym::target_feature] => { + self.check_target_feature(hir_id, attr, span, target, attrs) + } + [sym::thread_local] => self.check_thread_local(attr, span, target), + [sym::track_caller] => { self.check_track_caller(hir_id, attr.span, attrs, span, target) } - sym::doc => self.check_doc_attrs( + [sym::doc] => self.check_doc_attrs( attr, hir_id, target, &mut specified_inline, &mut doc_aliases, ), - sym::no_link => self.check_no_link(hir_id, attr, span, target), - sym::export_name => self.check_export_name(hir_id, attr, span, target), - sym::rustc_layout_scalar_valid_range_start - | sym::rustc_layout_scalar_valid_range_end => { + [sym::no_link] => self.check_no_link(hir_id, attr, span, target), + [sym::export_name] => self.check_export_name(hir_id, attr, span, target), + [sym::rustc_layout_scalar_valid_range_start] + | [sym::rustc_layout_scalar_valid_range_end] => { self.check_rustc_layout_scalar_valid_range(attr, span, target) } - sym::allow_internal_unstable => { + [sym::allow_internal_unstable] => { self.check_allow_internal_unstable(hir_id, attr, span, target, attrs) } - sym::debugger_visualizer => self.check_debugger_visualizer(attr, target), - sym::rustc_allow_const_fn_unstable => { + [sym::debugger_visualizer] => self.check_debugger_visualizer(attr, target), + [sym::rustc_allow_const_fn_unstable] => { self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target) } - sym::rustc_std_internal_symbol => { + [sym::rustc_std_internal_symbol] => { self.check_rustc_std_internal_symbol(attr, span, target) } - sym::naked => self.check_naked(hir_id, attr, span, target), - sym::rustc_never_returns_null_ptr => { + [sym::naked] => self.check_naked(hir_id, attr, span, target), + [sym::rustc_never_returns_null_ptr] => { self.check_applied_to_fn_or_method(hir_id, attr, span, target) } - sym::rustc_legacy_const_generics => { + [sym::rustc_legacy_const_generics] => { self.check_rustc_legacy_const_generics(hir_id, attr, span, target, item) } - sym::rustc_lint_query_instability => { + [sym::rustc_lint_query_instability] => { self.check_rustc_lint_query_instability(hir_id, attr, span, target) } - sym::rustc_lint_diagnostics => { + [sym::rustc_lint_diagnostics] => { self.check_rustc_lint_diagnostics(hir_id, attr, span, target) } - sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(attr, span, target), - sym::rustc_lint_opt_deny_field_access => { + [sym::rustc_lint_opt_ty] => self.check_rustc_lint_opt_ty(attr, span, target), + [sym::rustc_lint_opt_deny_field_access] => { self.check_rustc_lint_opt_deny_field_access(attr, span, target) } - sym::rustc_clean - | sym::rustc_dirty - | sym::rustc_if_this_changed - | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(attr), - sym::rustc_coinductive - | sym::rustc_must_implement_one_of - | sym::rustc_deny_explicit_impl - | sym::const_trait => self.check_must_be_applied_to_trait(attr, span, target), - sym::cmse_nonsecure_entry => { + [sym::rustc_clean] + | [sym::rustc_dirty] + | [sym::rustc_if_this_changed] + | [sym::rustc_then_this_would_need] => self.check_rustc_dirty_clean(attr), + [sym::rustc_coinductive] + | [sym::rustc_must_implement_one_of] + | [sym::rustc_deny_explicit_impl] + | [sym::const_trait] => self.check_must_be_applied_to_trait(attr, span, target), + [sym::cmse_nonsecure_entry] => { self.check_cmse_nonsecure_entry(hir_id, attr, span, target) } - sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target), - sym::must_not_suspend => self.check_must_not_suspend(attr, span, target), - sym::must_use => self.check_must_use(hir_id, attr, target), - sym::rustc_pass_by_value => self.check_pass_by_value(attr, span, target), - sym::rustc_allow_incoherent_impl => { + [sym::collapse_debuginfo] => self.check_collapse_debuginfo(attr, span, target), + [sym::must_not_suspend] => self.check_must_not_suspend(attr, span, target), + [sym::must_use] => self.check_must_use(hir_id, attr, target), + [sym::rustc_pass_by_value] => self.check_pass_by_value(attr, span, target), + [sym::rustc_allow_incoherent_impl] => { self.check_allow_incoherent_impl(attr, span, target) } - sym::rustc_has_incoherent_inherent_impls => { + [sym::rustc_has_incoherent_inherent_impls] => { self.check_has_incoherent_inherent_impls(attr, span, target) } - sym::ffi_pure => self.check_ffi_pure(attr.span, attrs, target), - sym::ffi_const => self.check_ffi_const(attr.span, target), - sym::rustc_const_unstable - | sym::rustc_const_stable - | sym::unstable - | sym::stable - | sym::rustc_allowed_through_unstable_modules - | sym::rustc_promotable => self.check_stability_promotable(attr, target), - sym::link_ordinal => self.check_link_ordinal(attr, span, target), - sym::rustc_confusables => self.check_confusables(attr, target), - sym::rustc_safe_intrinsic => { + [sym::ffi_pure] => self.check_ffi_pure(attr.span, attrs, target), + [sym::ffi_const] => self.check_ffi_const(attr.span, target), + [sym::rustc_const_unstable] + | [sym::rustc_const_stable] + | [sym::unstable] + | [sym::stable] + | [sym::rustc_allowed_through_unstable_modules] + | [sym::rustc_promotable] => self.check_stability_promotable(attr, target), + [sym::link_ordinal] => self.check_link_ordinal(attr, span, target), + [sym::rustc_confusables] => self.check_confusables(attr, target), + [sym::rustc_safe_intrinsic] => { self.check_rustc_safe_intrinsic(hir_id, attr, span, target) } _ => true, @@ -293,18 +299,41 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ); } - /// Checks if `#[do_not_recommend]` is applied on a trait impl. - fn check_do_not_recommend(&self, attr_span: Span, target: Target) -> bool { - if let Target::Impl = target { - true - } else { - self.dcx().emit_err(errors::IncorrectDoNotRecommendLocation { span: attr_span }); - false + /// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl. + fn check_do_not_recommend(&self, attr_span: Span, hir_id: HirId, target: Target) -> bool { + if !matches!(target, Target::Impl) { + self.tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + hir_id, + attr_span, + errors::IncorrectDoNotRecommendLocation, + ); + } + true + } + + /// Checks if `unsafe()` is applied to an invalid attribute. + fn check_unsafe_attr(&self, attr: &Attribute) { + if !attr.is_doc_comment() { + let attr_item = attr.get_normal_item(); + if let ast::Safety::Unsafe(unsafe_span) = attr_item.unsafety { + if !is_unsafe_attr(attr.name_or_empty()) { + self.dcx().emit_err(errors::InvalidAttrUnsafe { + span: unsafe_span, + name: attr_item.path.clone(), + }); + } + } } } /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition - fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) { + fn check_diagnostic_on_unimplemented( + &self, + attr_span: Span, + hir_id: HirId, + target: Target, + ) -> bool { if !matches!(target, Target::Trait) { self.tcx.emit_node_span_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, @@ -313,6 +342,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { DiagnosticOnUnimplementedOnlyForTraits, ); } + true } /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid. @@ -2310,7 +2340,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let param_env = ty::ParamEnv::empty(); let infcx = tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(&infcx); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); let span = tcx.def_span(def_id); let fresh_args = infcx.fresh_args_for_item(span, def_id.to_def_id()); diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index eb29a65cb293..3f6eccbd5a52 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -201,7 +201,7 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> { self.recurse_into(kind, None, |this| intravisit::walk_inline_const(this, block)); } - fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) { + fn visit_body(&mut self, body: &hir::Body<'tcx>) { let owner = self.tcx.hir().body_owner_def_id(body.id()); let kind = self.tcx.hir().body_const_context(owner); self.recurse_into(kind, Some(owner), |this| intravisit::walk_body(this, body)); diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 933412f677ce..69386c0fbdb4 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -15,7 +15,7 @@ use rustc_hir::{Node, PatKind, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, AssocItemContainer, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_session::lint::builtin::DEAD_CODE; @@ -24,8 +24,7 @@ use rustc_target::abi::FieldIdx; use std::mem; use crate::errors::{ - ChangeFieldsToBeOfUnitType, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, - UselessAssignment, + ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UselessAssignment, }; // Any local node that may call something in its body block should be @@ -44,16 +43,63 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { ) } -fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool { +struct Publicness { + ty_is_public: bool, + ty_and_all_fields_are_public: bool, +} + +impl Publicness { + fn new(ty_is_public: bool, ty_and_all_fields_are_public: bool) -> Self { + Self { ty_is_public, ty_and_all_fields_are_public } + } +} + +fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool { + // treat PhantomData and positional ZST as public, + // we don't want to lint types which only have them, + // cause it's a common way to use such types to check things like well-formedness + tcx.adt_def(id).all_fields().all(|field| { + let field_type = tcx.type_of(field.did).instantiate_identity(); + if field_type.is_phantom_data() { + return true; + } + let is_positional = field.name.as_str().starts_with(|c: char| c.is_ascii_digit()); + if is_positional + && tcx + .layout_of(tcx.param_env(field.did).and(field_type)) + .map_or(true, |layout| layout.is_zst()) + { + return true; + } + field.vis.is_public() + }) +} + +/// check struct and its fields are public or not, +/// for enum and union, just check they are public, +/// and doesn't solve types like &T for now, just skip them +fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness { if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind && let Res::Def(def_kind, def_id) = path.res && def_id.is_local() - && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) { - tcx.visibility(def_id).is_public() - } else { - true + return match def_kind { + DefKind::Enum | DefKind::Union => { + let ty_is_public = tcx.visibility(def_id).is_public(); + Publicness::new(ty_is_public, ty_is_public) + } + DefKind::Struct => { + let ty_is_public = tcx.visibility(def_id).is_public(); + Publicness::new( + ty_is_public, + ty_is_public && struct_all_fields_are_public(tcx, def_id), + ) + } + _ => Publicness::new(true, true), + }; } + + Publicness::new(true, true) } /// Determine if a work from the worklist is coming from the a `#[allow]` @@ -425,10 +471,13 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { && let ItemKind::Impl(impl_ref) = self.tcx.hir().expect_item(local_impl_id).kind { - if self.tcx.visibility(trait_id).is_public() - && matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) + if !matches!(trait_item.kind, hir::TraitItemKind::Type(..)) && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty) + .ty_and_all_fields_are_public { + // skip impl-items of non pure pub ty, + // cause we don't know the ty is constructed or not, + // check these later in `solve_rest_impl_items` continue; } @@ -485,29 +534,42 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { fn solve_rest_impl_items(&mut self, mut unsolved_impl_items: Vec<(hir::ItemId, LocalDefId)>) { let mut ready; - (ready, unsolved_impl_items) = unsolved_impl_items - .into_iter() - .partition(|&(impl_id, _)| self.impl_item_with_used_self(impl_id)); + (ready, unsolved_impl_items) = + unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| { + self.impl_item_with_used_self(impl_id, impl_item_id) + }); while !ready.is_empty() { self.worklist = ready.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect(); self.mark_live_symbols(); - (ready, unsolved_impl_items) = unsolved_impl_items - .into_iter() - .partition(|&(impl_id, _)| self.impl_item_with_used_self(impl_id)); + (ready, unsolved_impl_items) = + unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| { + self.impl_item_with_used_self(impl_id, impl_item_id) + }); } } - fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId) -> bool { + fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool { if let TyKind::Path(hir::QPath::Resolved(_, path)) = self.tcx.hir().item(impl_id).expect_impl().self_ty.kind && let Res::Def(def_kind, def_id) = path.res && let Some(local_def_id) = def_id.as_local() && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) { - self.live_symbols.contains(&local_def_id) + if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id + && let Some(local_id) = trait_item_id.as_local() + { + // for the local impl item, we can know the trait item is used or not, + // so we mark the method live if the self is used and the trait item is used + self.live_symbols.contains(&local_id) && self.live_symbols.contains(&local_def_id) + } else { + // for the foreign method and inherent pub method, + // we don't know the trait item or the method is used or not, + // so we mark the method live if the self is used + self.live_symbols.contains(&local_def_id) + } } else { false } @@ -732,12 +794,14 @@ fn check_item<'tcx>( .iter() .filter_map(|def_id| def_id.as_local()); - let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir().item(id).expect_impl().self_ty); + let self_ty = tcx.hir().item(id).expect_impl().self_ty; + let Publicness { ty_is_public, ty_and_all_fields_are_public } = + ty_ref_to_pub_struct(tcx, self_ty); // And we access the Map here to get HirId from LocalDefId for local_def_id in local_def_ids { // check the function may construct Self - let mut may_construct_self = true; + let mut may_construct_self = false; if let Some(fn_sig) = tcx.hir().fn_sig_by_hir_id(tcx.local_def_id_to_hir_id(local_def_id)) { @@ -745,20 +809,24 @@ fn check_item<'tcx>( matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None); } - // for impl trait blocks, mark associate functions live if the trait is public - if of_trait - && (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) - || tcx.visibility(local_def_id).is_public() - && (ty_is_pub || may_construct_self)) + // for trait impl blocks, + // mark the method live if the self_ty is public, + // or the method is public and may construct self + if of_trait && matches!(tcx.def_kind(local_def_id), DefKind::AssocTy) + || tcx.visibility(local_def_id).is_public() + && (ty_and_all_fields_are_public || may_construct_self) { + // if the impl item is public, + // and the ty may be constructed or can be constructed in foreign crates, + // mark the impl item live worklist.push((local_def_id, ComesFromAllowExpect::No)); - } else if of_trait && tcx.visibility(local_def_id).is_public() { - // pub method && private ty & methods not construct self - unsolved_impl_items.push((id, local_def_id)); } else if let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, local_def_id) { worklist.push((local_def_id, comes_from_allow)); + } else if of_trait || tcx.visibility(local_def_id).is_public() && ty_is_public { + // private impl items of traits || public impl items not constructs self + unsolved_impl_items.push((id, local_def_id)); } } } @@ -824,6 +892,14 @@ fn create_and_seed_worklist( effective_vis .is_public_at_level(Level::Reachable) .then_some(id) + .filter(|&id| + // checks impls, impl-items and pub structs with all public fields later + match tcx.def_kind(id) { + DefKind::Impl { .. } => false, + DefKind::AssocConst | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), + DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), + _ => true + }) .map(|id| (id, ComesFromAllowExpect::No)) }) // Seed entry point @@ -994,17 +1070,50 @@ impl<'tcx> DeadVisitor<'tcx> { }; let diag = match report_on { - ReportOn::TupleField => MultipleDeadCodes::UnusedTupleStructFields { - multiple, - num, - descr, - participle, - name_list, - change_fields_suggestion: ChangeFieldsToBeOfUnitType { num, spans: spans.clone() }, - parent_info, - ignored_derived_impls, - }, + ReportOn::TupleField => { + let tuple_fields = if let Some(parent_id) = parent_item + && let node = tcx.hir_node_by_def_id(parent_id) + && let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Struct(hir::VariantData::Tuple(fields, _, _), _), + .. + }) = node + { + *fields + } else { + &[] + }; + let trailing_tuple_fields = if tuple_fields.len() >= dead_codes.len() { + LocalDefIdSet::from_iter( + tuple_fields + .iter() + .skip(tuple_fields.len() - dead_codes.len()) + .map(|f| f.def_id), + ) + } else { + LocalDefIdSet::default() + }; + + let fields_suggestion = + // Suggest removal if all tuple fields are at the end. + // Otherwise suggest removal or changing to unit type + if dead_codes.iter().all(|dc| trailing_tuple_fields.contains(&dc.def_id)) { + ChangeFields::Remove { num } + } else { + ChangeFields::ChangeToUnitTypeOrRemove { num, spans: spans.clone() } + }; + + MultipleDeadCodes::UnusedTupleStructFields { + multiple, + num, + descr, + participle, + name_list, + change_fields_suggestion: fields_suggestion, + parent_info, + ignored_derived_impls, + } + } ReportOn::NamedField => MultipleDeadCodes::DeadCodes { multiple, num, @@ -1096,10 +1205,15 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { || (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id)) { for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) { - // We have diagnosed unused methods in traits + // We have diagnosed unused assoc consts and fns in traits if matches!(def_kind, DefKind::Impl { of_trait: true }) - && tcx.def_kind(def_id) == DefKind::AssocFn - || def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn + && matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocFn) + // skip unused public inherent methods, + // cause we have diagnosed unconstructed struct + || matches!(def_kind, DefKind::Impl { of_trait: false }) + && tcx.visibility(def_id).is_public() + && ty_ref_to_pub_struct(tcx, tcx.hir().item(item).expect_impl().self_ty).ty_is_public + || def_kind == DefKind::Trait && tcx.def_kind(def_id) == DefKind::AssocTy { continue; } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index b8586e7e974a..d27b94ebd22a 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -4,10 +4,10 @@ use std::{ }; use crate::fluent_generated as fluent; -use rustc_ast::Label; +use rustc_ast::{ast, Label}; use rustc_errors::{ - codes::*, Applicability, Diag, DiagCtxt, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, - MultiSpan, SubdiagMessageOp, Subdiagnostic, + codes::*, Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, + Level, MultiSpan, SubdiagMessageOp, Subdiagnostic, }; use rustc_hir::{self as hir, ExprKind, Target}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; @@ -17,12 +17,9 @@ use rustc_span::{Span, Symbol, DUMMY_SP}; use crate::check_attr::ProcMacroKind; use crate::lang_items::Duplicate; -#[derive(Diagnostic)] +#[derive(LintDiagnostic)] #[diag(passes_incorrect_do_not_recommend_location)] -pub struct IncorrectDoNotRecommendLocation { - #[primary_span] - pub span: Span, -} +pub struct IncorrectDoNotRecommendLocation; #[derive(LintDiagnostic)] #[diag(passes_outer_crate_level_attr)] @@ -866,6 +863,15 @@ pub struct InvalidAttrAtCrateLevel { pub item: Option, } +#[derive(Diagnostic)] +#[diag(passes_invalid_attr_unsafe)] +#[note] +pub struct InvalidAttrUnsafe { + #[primary_span] + pub span: Span, + pub name: ast::Path, +} + #[derive(Clone, Copy)] pub struct ItemFollowingInnerAttr { pub span: Span, @@ -874,7 +880,7 @@ pub struct ItemFollowingInnerAttr { impl Diagnostic<'_, G> for InvalidAttrAtCrateLevel { #[track_caller] - fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let mut diag = Diag::new(dcx, level, fluent::passes_invalid_attr_at_crate_level); diag.span(self.span); diag.arg("name", self.name); @@ -1024,7 +1030,7 @@ pub struct BreakNonLoop<'a> { impl<'a, G: EmissionGuarantee> Diagnostic<'_, G> for BreakNonLoop<'a> { #[track_caller] - fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let mut diag = Diag::new(dcx, level, fluent::passes_break_non_loop); diag.span(self.span); diag.code(E0571); @@ -1103,7 +1109,7 @@ pub struct BreakInsideCoroutine<'a> { pub struct OutsideLoop<'a> { #[primary_span] #[label] - pub span: Span, + pub spans: Vec, pub name: &'a str, pub is_break: bool, #[subdiagnostic] @@ -1115,7 +1121,7 @@ pub struct OutsideLoopSuggestion { #[suggestion_part(code = "'block: ")] pub block_span: Span, #[suggestion_part(code = " 'block")] - pub break_span: Span, + pub break_spans: Vec, } #[derive(Diagnostic)] @@ -1170,7 +1176,7 @@ pub struct NakedFunctionsAsmBlock { impl Diagnostic<'_, G> for NakedFunctionsAsmBlock { #[track_caller] - fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let mut diag = Diag::new(dcx, level, fluent::passes_naked_functions_asm_block); diag.span(self.span); diag.code(E0787); @@ -1258,7 +1264,7 @@ pub struct NoMainErr { impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NoMainErr { #[track_caller] - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let mut diag = Diag::new(dcx, level, fluent::passes_no_main_function); diag.span(DUMMY_SP); diag.code(E0601); @@ -1316,7 +1322,7 @@ pub struct DuplicateLangItem { impl Diagnostic<'_, G> for DuplicateLangItem { #[track_caller] - fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let mut diag = Diag::new( dcx, level, @@ -1568,7 +1574,7 @@ pub enum MultipleDeadCodes<'tcx> { participle: &'tcx str, name_list: DiagSymbolList, #[subdiagnostic] - change_fields_suggestion: ChangeFieldsToBeOfUnitType, + change_fields_suggestion: ChangeFields, #[subdiagnostic] parent_info: Option>, #[subdiagnostic] @@ -1595,11 +1601,18 @@ pub struct IgnoredDerivedImpls { } #[derive(Subdiagnostic)] -#[multipart_suggestion(passes_change_fields_to_be_of_unit_type, applicability = "has-placeholders")] -pub struct ChangeFieldsToBeOfUnitType { - pub num: usize, - #[suggestion_part(code = "()")] - pub spans: Vec, +pub enum ChangeFields { + #[multipart_suggestion( + passes_change_fields_to_be_of_unit_type, + applicability = "has-placeholders" + )] + ChangeToUnitTypeOrRemove { + num: usize, + #[suggestion_part(code = "()")] + spans: Vec, + }, + #[help(passes_remove_fields)] + Remove { num: usize }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index a980d5dcaba4..0ba61f8e8b40 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -246,7 +246,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_item(self, i) } - fn visit_body(&mut self, b: &'v hir::Body<'v>) { + fn visit_body(&mut self, b: &hir::Body<'v>) { self.record("Body", Id::None, b); hir_visit::walk_body(self, b); } @@ -429,7 +429,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { fn visit_param_bound(&mut self, b: &'v hir::GenericBound<'v>) { record_variants!( (self, b, b, Id::None, hir, GenericBound, GenericBound), - [Trait, Outlives] + [Trait, Outlives, Use] ); hir_visit::walk_param_bound(self, b) } @@ -477,9 +477,9 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_generic_args(self, ga) } - fn visit_assoc_type_binding(&mut self, type_binding: &'v hir::TypeBinding<'v>) { - self.record("TypeBinding", Id::Node(type_binding.hir_id), type_binding); - hir_visit::walk_assoc_type_binding(self, type_binding) + fn visit_assoc_item_constraint(&mut self, constraint: &'v hir::AssocItemConstraint<'v>) { + self.record("AssocItemConstraint", Id::Node(constraint.hir_id), constraint); + hir_visit::walk_assoc_item_constraint(self, constraint) } fn visit_attribute(&mut self, attr: &'v ast::Attribute) { @@ -659,7 +659,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { fn visit_param_bound(&mut self, b: &'v ast::GenericBound, _ctxt: BoundKind) { record_variants!( (self, b, b, Id::None, ast, GenericBound, GenericBound), - [Trait, Outlives] + [Trait, Outlives, Use] ); ast_visit::walk_param_bound(self, b) } @@ -688,7 +688,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ast_visit::walk_path_segment(self, path_segment) } - // `GenericArgs` has one inline use (in `ast::AssocConstraint::gen_args`) and one + // `GenericArgs` has one inline use (in `ast::AssocItemConstraint::gen_args`) and one // non-inline use (in `ast::PathSegment::args`). The latter case is more // common, so we implement `visit_generic_args` and tolerate the double // counting in the former case. diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index a5fa94faeadc..b3722e99e168 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -149,8 +149,9 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> { } }; - // When there's a duplicate lang item, something went very wrong and there's no value in recovering or doing anything. - // Give the user the one message to let them debug the mess they created and then wish them farewell. + // When there's a duplicate lang item, something went very wrong and there's no value + // in recovering or doing anything. Give the user the one message to let them debug the + // mess they created and then wish them farewell. self.tcx.dcx().emit_fatal(DuplicateLangItem { local_span: item_span, lang_item_name, diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index 82d43f078ee2..c9a476504562 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -54,7 +54,7 @@ pub fn ensure_wf<'tcx>( pred, ); let infcx = tcx.infer_ctxt().build(); - let ocx = traits::ObligationCtxt::new(&infcx); + let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx); ocx.register_obligation(obligation); let errors = ocx.select_all_or_error(); if !errors.is_empty() { diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index d3608759eec7..a0f5f98aafc8 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -4,16 +4,15 @@ //! //! This API is completely unstable and subject to change. +// tidy-alphabetical-start +#![allow(internal_features)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(rustdoc_internals)] -#![allow(internal_features)] #![feature(let_chains)] #![feature(map_try_insert)] +#![feature(rustdoc_internals)] #![feature(try_blocks)] - -#[macro_use] -extern crate tracing; +// tidy-alphabetical-end use rustc_middle::query::Providers; diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index b50cb158b1fe..dfa7dfa33981 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -99,10 +99,10 @@ use rustc_middle::ty::{self, RootVariableMinCaptureList, Ty, TyCtxt}; use rustc_session::lint; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{BytePos, Span}; - use std::io; use std::io::prelude::*; use std::rc::Rc; +use tracing::{debug, instrument}; mod rwu_table; @@ -153,9 +153,8 @@ fn check_liveness(tcx: TyCtxt<'_>, def_id: LocalDefId) { } let mut maps = IrMaps::new(tcx); - let body_id = tcx.hir().body_owned_by(def_id); - let hir_id = tcx.hir().body_owner(body_id); - let body = tcx.hir().body(body_id); + let body = tcx.hir().body_owned_by(def_id); + let hir_id = tcx.hir().body_owner(body.id()); if let Some(upvars) = tcx.upvars_mentioned(def_id) { for &var_hir_id in upvars.keys() { @@ -166,17 +165,17 @@ fn check_liveness(tcx: TyCtxt<'_>, def_id: LocalDefId) { // gather up the various local variables, significant expressions, // and so forth: - maps.visit_body(body); + maps.visit_body(&body); // compute liveness let mut lsets = Liveness::new(&mut maps, def_id); - let entry_ln = lsets.compute(body, hir_id); - lsets.log_liveness(entry_ln, body_id.hir_id); + let entry_ln = lsets.compute(&body, hir_id); + lsets.log_liveness(entry_ln, body.id().hir_id); // check for various error conditions - lsets.visit_body(body); + lsets.visit_body(&body); lsets.warn_about_unused_upvars(entry_ln); - lsets.warn_about_unused_args(body, entry_ln); + lsets.warn_about_unused_args(&body, entry_ln); } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs index 3b20112eab7b..2587a18b8c89 100644 --- a/compiler/rustc_passes/src/loops.rs +++ b/compiler/rustc_passes/src/loops.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeMap; +use std::fmt; use Context::*; use rustc_hir as hir; @@ -25,22 +27,55 @@ enum Context { Closure(Span), Coroutine { coroutine_span: Span, kind: hir::CoroutineDesugaring, source: hir::CoroutineSource }, UnlabeledBlock(Span), + UnlabeledIfBlock(Span), LabeledBlock, Constant, } -#[derive(Copy, Clone)] +#[derive(Clone)] +struct BlockInfo { + name: String, + spans: Vec, + suggs: Vec, +} + +#[derive(PartialEq)] +enum BreakContextKind { + Break, + Continue, +} + +impl fmt::Display for BreakContextKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BreakContextKind::Break => "break", + BreakContextKind::Continue => "continue", + } + .fmt(f) + } +} + +#[derive(Clone)] struct CheckLoopVisitor<'a, 'tcx> { sess: &'a Session, tcx: TyCtxt<'tcx>, - cx: Context, + // Keep track of a stack of contexts, so that suggestions + // are not made for contexts where it would be incorrect, + // such as adding a label for an `if`. + // e.g. `if 'foo: {}` would be incorrect. + cx_stack: Vec, + block_breaks: BTreeMap, } fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { - tcx.hir().visit_item_likes_in_module( - module_def_id, - &mut CheckLoopVisitor { sess: tcx.sess, tcx, cx: Normal }, - ); + let mut check = CheckLoopVisitor { + sess: tcx.sess, + tcx, + cx_stack: vec![Normal], + block_breaks: Default::default(), + }; + tcx.hir().visit_item_likes_in_module(module_def_id, &mut check); + check.report_outside_loop_error(); } pub(crate) fn provide(providers: &mut Providers) { @@ -83,6 +118,45 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { match e.kind { + hir::ExprKind::If(cond, then, else_opt) => { + self.visit_expr(cond); + + let get_block = |ck_loop: &CheckLoopVisitor<'a, 'hir>, + expr: &hir::Expr<'hir>| + -> Option<&hir::Block<'hir>> { + if let hir::ExprKind::Block(b, None) = expr.kind + && matches!( + ck_loop.cx_stack.last(), + Some(&Normal) + | Some(&Constant) + | Some(&UnlabeledBlock(_)) + | Some(&UnlabeledIfBlock(_)) + ) + { + Some(b) + } else { + None + } + }; + + if let Some(b) = get_block(self, then) { + self.with_context(UnlabeledIfBlock(b.span.shrink_to_lo()), |v| { + v.visit_block(b) + }); + } else { + self.visit_expr(then); + } + + if let Some(else_expr) = else_opt { + if let Some(b) = get_block(self, else_expr) { + self.with_context(UnlabeledIfBlock(b.span.shrink_to_lo()), |v| { + v.visit_block(b) + }); + } else { + self.visit_expr(else_expr); + } + } + } hir::ExprKind::Loop(ref b, _, source, _) => { self.with_context(Loop(source), |v| v.visit_block(b)); } @@ -101,11 +175,14 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { hir::ExprKind::Block(ref b, Some(_label)) => { self.with_context(LabeledBlock, |v| v.visit_block(b)); } - hir::ExprKind::Block(ref b, None) if matches!(self.cx, Fn) => { + hir::ExprKind::Block(ref b, None) if matches!(self.cx_stack.last(), Some(&Fn)) => { self.with_context(Normal, |v| v.visit_block(b)); } hir::ExprKind::Block(ref b, None) - if matches!(self.cx, Normal | Constant | UnlabeledBlock(_)) => + if matches!( + self.cx_stack.last(), + Some(&Normal) | Some(&Constant) | Some(&UnlabeledBlock(_)) + ) => { self.with_context(UnlabeledBlock(b.span.shrink_to_lo()), |v| v.visit_block(b)); } @@ -178,7 +255,12 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { Some(label) => sp_lo.with_hi(label.ident.span.hi()), None => sp_lo.shrink_to_lo(), }; - self.require_break_cx("break", e.span, label_sp); + self.require_break_cx( + BreakContextKind::Break, + e.span, + label_sp, + self.cx_stack.len() - 1, + ); } hir::ExprKind::Continue(destination) => { self.require_label_in_labeled_block(e.span, &destination, "continue"); @@ -200,7 +282,12 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { } Err(_) => {} } - self.require_break_cx("continue", e.span, e.span) + self.require_break_cx( + BreakContextKind::Continue, + e.span, + e.span, + self.cx_stack.len() - 1, + ) } _ => intravisit::walk_expr(self, e), } @@ -212,18 +299,26 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { where F: FnOnce(&mut CheckLoopVisitor<'a, 'hir>), { - let old_cx = self.cx; - self.cx = cx; + self.cx_stack.push(cx); f(self); - self.cx = old_cx; + self.cx_stack.pop(); } - fn require_break_cx(&self, name: &str, span: Span, break_span: Span) { - let is_break = name == "break"; - match self.cx { + fn require_break_cx( + &mut self, + br_cx_kind: BreakContextKind, + span: Span, + break_span: Span, + cx_pos: usize, + ) { + match self.cx_stack[cx_pos] { LabeledBlock | Loop(_) => {} Closure(closure_span) => { - self.sess.dcx().emit_err(BreakInsideClosure { span, closure_span, name }); + self.sess.dcx().emit_err(BreakInsideClosure { + span, + closure_span, + name: &br_cx_kind.to_string(), + }); } Coroutine { coroutine_span, kind, source } => { let kind = match kind { @@ -239,17 +334,32 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { self.sess.dcx().emit_err(BreakInsideCoroutine { span, coroutine_span, - name, + name: &br_cx_kind.to_string(), kind, source, }); } - UnlabeledBlock(block_span) if is_break && block_span.eq_ctxt(break_span) => { - let suggestion = Some(OutsideLoopSuggestion { block_span, break_span }); - self.sess.dcx().emit_err(OutsideLoop { span, name, is_break, suggestion }); + UnlabeledBlock(block_span) + if br_cx_kind == BreakContextKind::Break && block_span.eq_ctxt(break_span) => + { + let block = self.block_breaks.entry(block_span).or_insert_with(|| BlockInfo { + name: br_cx_kind.to_string(), + spans: vec![], + suggs: vec![], + }); + block.spans.push(span); + block.suggs.push(break_span); } - Normal | Constant | Fn | UnlabeledBlock(_) => { - self.sess.dcx().emit_err(OutsideLoop { span, name, is_break, suggestion: None }); + UnlabeledIfBlock(_) if br_cx_kind == BreakContextKind::Break => { + self.require_break_cx(br_cx_kind, span, break_span, cx_pos - 1); + } + Normal | Constant | Fn | UnlabeledBlock(_) | UnlabeledIfBlock(_) => { + self.sess.dcx().emit_err(OutsideLoop { + spans: vec![span], + name: &br_cx_kind.to_string(), + is_break: br_cx_kind == BreakContextKind::Break, + suggestion: None, + }); } } } @@ -261,7 +371,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { cf_type: &str, ) -> bool { if !span.is_desugaring(DesugaringKind::QuestionMark) - && self.cx == LabeledBlock + && self.cx_stack.last() == Some(&LabeledBlock) && label.label.is_none() { self.sess.dcx().emit_err(UnlabeledInLabeledBlock { span, cf_type }); @@ -269,4 +379,18 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { } false } + + fn report_outside_loop_error(&mut self) { + for (s, block) in &self.block_breaks { + self.sess.dcx().emit_err(OutsideLoop { + spans: block.spans.clone(), + name: &block.name, + is_break: true, + suggestion: Some(OutsideLoopSuggestion { + block_span: *s, + break_spans: block.suggs.clone(), + }), + }); + } + } } diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 55b7c6dce4d9..6dd8eaf7e673 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -32,11 +32,12 @@ use rustc_hir::Node; use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::middle::privacy::{self, Level}; -use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc}; +use rustc_middle::mir::interpret::{ConstAllocation, ErrorHandled, GlobalAlloc}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, ExistentialTraitRef, TyCtxt}; use rustc_privacy::DefIdVisitor; use rustc_session::config::CrateType; +use tracing::debug; /// Determines whether this item is recursive for reachability. See `is_recursively_reachable_local` /// below for details. @@ -156,6 +157,7 @@ impl<'tcx> ReachableContext<'tcx> { } hir::ImplItemKind::Type(_) => false, }, + Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => true, _ => false, } } @@ -204,11 +206,24 @@ impl<'tcx> ReachableContext<'tcx> { } } - // Reachable constants will be inlined into other crates - // unconditionally, so we need to make sure that their - // contents are also reachable. hir::ItemKind::Const(_, _, init) => { - self.visit_nested_body(init); + // Only things actually ending up in the final constant value are reachable + // for codegen. Everything else is only needed during const-eval, so even if + // const-eval happens in a downstream crate, all they need is + // `mir_for_ctfe`. + match self.tcx.const_eval_poly_to_alloc(item.owner_id.def_id.into()) { + Ok(alloc) => { + let alloc = self.tcx.global_alloc(alloc.alloc_id).unwrap_memory(); + self.propagate_from_alloc(alloc); + } + // We can't figure out which value the constant will evaluate to. In + // lieu of that, we have to consider everything mentioned in the const + // initializer reachable, since it *may* end up in the final value. + Err(ErrorHandled::TooGeneric(_)) => self.visit_nested_body(init), + // If there was an error evaluating the const, nothing can be reachable + // via it, and anyway compilation will fail. + Err(ErrorHandled::Reported(..)) => {} + } } hir::ItemKind::Static(..) => { if let Ok(alloc) = self.tcx.eval_static_initializer(item.owner_id.def_id) { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 05c833cdfb65..6bdfaf0c9089 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -25,9 +25,9 @@ use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPR use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use rustc_target::spec::abi::Abi; - use std::mem::replace; use std::num::NonZero; +use tracing::{debug, info}; #[derive(PartialEq)] enum AnnotationKind { diff --git a/compiler/rustc_passes/src/upvars.rs b/compiler/rustc_passes/src/upvars.rs index 2eed58d10bb7..f2454514e4d3 100644 --- a/compiler/rustc_passes/src/upvars.rs +++ b/compiler/rustc_passes/src/upvars.rs @@ -16,17 +16,17 @@ pub fn provide(providers: &mut Providers) { } let local_def_id = def_id.expect_local(); - let body = tcx.hir().body(tcx.hir().maybe_body_owned_by(local_def_id)?); + let body = tcx.hir().maybe_body_owned_by(local_def_id)?; let mut local_collector = LocalCollector::default(); - local_collector.visit_body(body); + local_collector.visit_body(&body); let mut capture_collector = CaptureCollector { tcx, locals: &local_collector.locals, upvars: FxIndexMap::default(), }; - capture_collector.visit_body(body); + capture_collector.visit_body(&body); if !capture_collector.upvars.is_empty() { Some(tcx.arena.alloc(capture_collector.upvars)) diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 4155540886a6..c9590ad06b05 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -1,7 +1,9 @@ //! Analysis of patterns, notably match exhaustiveness checking. -#![allow(rustc::untranslatable_diagnostic)] +// tidy-alphabetical-start #![allow(rustc::diagnostic_outside_of_impl)] +#![allow(rustc::untranslatable_diagnostic)] +// tidy-alphabetical-end pub mod constructor; #[cfg(feature = "rustc")] diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 1d10c9df4ab2..892aacd7ac5d 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -99,7 +99,6 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>( use rustc_errors::LintDiagnostic; let mut err = rcx.tcx.dcx().struct_span_warn(arm.pat.data().span, ""); - err.primary_message(decorator.msg()); decorator.decorate_lint(&mut err); err.emit(); } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index ff68dd81beaf..8391c694c64b 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -6,11 +6,12 @@ use rustc_hir::def_id::DefId; use rustc_hir::HirId; use rustc_index::{Idx, IndexVec}; use rustc_middle::middle::stability::EvalResult; -use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::{self, Const}; use rustc_middle::thir::{self, FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::ty::layout::IntegerExt; -use rustc_middle::ty::{self, FieldDef, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef}; +use rustc_middle::ty::{ + self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeVisitableExt, VariantDef, +}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; @@ -701,9 +702,9 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { ty::Int(_) => miint.as_finite_int(size.bits()).unwrap(), _ => miint.as_finite_uint().unwrap(), }; - match Scalar::try_from_uint(bits, size) { + match ScalarInt::try_from_uint(bits, size) { Some(scalar) => { - let value = mir::Const::from_scalar(tcx, scalar, ty.inner()); + let value = mir::Const::from_scalar(tcx, scalar.into(), ty.inner()); PatRangeBoundary::Finite(value) } // The value doesn't fit. Since `x >= 0` and 0 always encodes the minimum value @@ -737,7 +738,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { // this). We show this to the user as `usize::MAX..` which is slightly incorrect but // probably clear enough. let c = ty.numeric_max_val(cx.tcx).unwrap(); - let value = mir::Const::from_ty_const(c, cx.tcx); + let value = mir::Const::from_ty_const(c, ty.0, cx.tcx); lo = PatRangeBoundary::Finite(value); } let hi = if let Some(hi) = range.hi.minus_one() { diff --git a/compiler/rustc_pattern_analysis/tests/common/mod.rs b/compiler/rustc_pattern_analysis/tests/common/mod.rs index e72fddb9e9a8..6e8bb5050055 100644 --- a/compiler/rustc_pattern_analysis/tests/common/mod.rs +++ b/compiler/rustc_pattern_analysis/tests/common/mod.rs @@ -13,7 +13,6 @@ pub fn init_tracing() { use tracing_subscriber::Layer; let _ = tracing_tree::HierarchicalLayer::default() .with_writer(std::io::stderr) - .with_indent_lines(true) .with_ansi(true) .with_targets(true) .with_indent_amount(2) diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index f631ae76de50..d37056269385 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1,10 +1,12 @@ +// tidy-alphabetical-start +#![allow(internal_features)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(rustdoc_internals)] -#![allow(internal_features)] #![feature(associated_type_defaults)] -#![feature(try_blocks)] #![feature(let_chains)] +#![feature(rustdoc_internals)] +#![feature(try_blocks)] +// tidy-alphabetical-end mod errors; @@ -971,8 +973,12 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { fn visit_nested_body(&mut self, body_id: hir::BodyId) { - let old_maybe_typeck_results = - self.maybe_typeck_results.replace(self.tcx.typeck_body(body_id)); + let new_typeck_results = self.tcx.typeck_body(body_id); + // Do not try reporting privacy violations if we failed to infer types. + if new_typeck_results.tainted_by_errors.is_some() { + return; + } + let old_maybe_typeck_results = self.maybe_typeck_results.replace(new_typeck_results); self.visit_body(self.tcx.hir().body(body_id)); self.maybe_typeck_results = old_maybe_typeck_results; } @@ -1695,7 +1701,7 @@ fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { rustc_ty_utils::sig_types::walk_types(tcx, def_id, &mut visitor); if let Some(body_id) = tcx.hir().maybe_body_owned_by(def_id) { - visitor.visit_nested_body(body_id); + visitor.visit_nested_body(body_id.id()); } } diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index c57f22a0da2b..2bb1be22b985 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" # tidy-alphabetical-start field-offset = "0.3.5" measureme = "11" -rustc-rayon-core = { version = "0.5.0", optional = true } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hir = { path = "../rustc_hir" } @@ -23,5 +22,5 @@ tracing = "0.1" [features] # tidy-alphabetical-start -rustc_use_parallel_compiler = ["rustc-rayon-core", "rustc_query_system/rustc_use_parallel_compiler"] +rustc_use_parallel_compiler = ["rustc_query_system/rustc_use_parallel_compiler"] # tidy-alphabetical-end diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 85f55553af39..825c1e2e9bce 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -1,12 +1,14 @@ //! Support for serializing the dep-graph and reloading it. +// tidy-alphabetical-start +#![allow(internal_features)] +#![allow(rustc::potential_query_instability, unused_parens)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(rustdoc_internals)] #![feature(min_specialization)] #![feature(rustc_attrs)] -#![allow(rustc::potential_query_instability, unused_parens)] -#![allow(internal_features)] +#![feature(rustdoc_internals)] +// tidy-alphabetical-end use crate::plumbing::{__rust_begin_short_backtrace, encode_all_query_results, try_mark_green}; use crate::profiling_support::QueryKeyStringCache; diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 86531bd95900..62e393772147 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -620,7 +620,9 @@ macro_rules! define_queries { tcx, { let ret = call_provider!([$($modifiers)*][tcx, $name, key]); - tracing::trace!(?ret); + rustc_middle::ty::print::with_reduced_queries!({ + tracing::trace!(?ret); + }); ret } ) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 76227a78c3d6..66fb3136805e 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -64,7 +64,6 @@ pub struct MarkFrame<'a> { parent: Option<&'a MarkFrame<'a>>, } -#[derive(PartialEq)] enum DepNodeColor { Red, Green(DepNodeIndex), @@ -925,7 +924,7 @@ impl DepGraph { /// Returns true if the given node has been marked as red during the /// current compilation session. Used in various assertions pub fn is_red(&self, dep_node: &DepNode) -> bool { - self.node_color(dep_node) == Some(DepNodeColor::Red) + matches!(self.node_color(dep_node), Some(DepNodeColor::Red)) } /// Returns true if the given node has been marked as green during the diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index b426bb888f46..8e91d9dd60b3 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -182,15 +182,13 @@ impl SerializedDepGraph { pub fn decode(d: &mut MemDecoder<'_>) -> Arc { // The last 16 bytes are the node count and edge count. debug!("position: {:?}", d.position()); - let (node_count, edge_count, graph_size) = - d.with_position(d.len() - 3 * IntEncodedWithFixedSize::ENCODED_SIZE, |d| { + let (node_count, edge_count) = + d.with_position(d.len() - 2 * IntEncodedWithFixedSize::ENCODED_SIZE, |d| { debug!("position: {:?}", d.position()); let node_count = IntEncodedWithFixedSize::decode(d).0 as usize; let edge_count = IntEncodedWithFixedSize::decode(d).0 as usize; - let graph_size = IntEncodedWithFixedSize::decode(d).0 as usize; - (node_count, edge_count, graph_size) + (node_count, edge_count) }); - assert_eq!(d.len(), graph_size); debug!("position: {:?}", d.position()); debug!(?node_count, ?edge_count); @@ -606,8 +604,6 @@ impl EncoderState { debug!("position: {:?}", encoder.position()); IntEncodedWithFixedSize(node_count).encode(&mut encoder); IntEncodedWithFixedSize(edge_count).encode(&mut encoder); - let graph_size = encoder.position() + IntEncodedWithFixedSize::ENCODED_SIZE; - IntEncodedWithFixedSize(graph_size as u64).encode(&mut encoder); debug!("position: {:?}", encoder.position()); // Drop the encoder so that nothing is written after the counts. let result = encoder.finish(); diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index fa07877ab9f3..41222e83f7c5 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -1,9 +1,11 @@ +// tidy-alphabetical-start +#![allow(rustc::potential_query_instability, internal_features)] #![feature(assert_matches)] #![feature(core_intrinsics)] #![feature(hash_raw_entry)] -#![feature(min_specialization)] #![feature(let_chains)] -#![allow(rustc::potential_query_instability, internal_features)] +#![feature(min_specialization)] +// tidy-alphabetical-end pub mod cache; pub mod dep_graph; diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 248a741af907..3f44b11850e5 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -4,7 +4,7 @@ use crate::query::plumbing::CycleError; use crate::query::DepKind; use crate::query::{QueryContext, QueryStackFrame}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{Diag, DiagCtxt}; +use rustc_errors::{Diag, DiagCtxtHandle}; use rustc_hir::def::DefKind; use rustc_session::Session; use rustc_span::Span; @@ -600,7 +600,7 @@ pub fn report_cycle<'a>( pub fn print_query_stack( qcx: Qcx, mut current_query: Option, - dcx: &DiagCtxt, + dcx: DiagCtxtHandle<'_>, num_frames: Option, mut file: Option, ) -> usize { diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index f824e4faf5d0..358f25e23343 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -11,6 +11,10 @@ resolve_added_macro_use = resolve_ancestor_only = visibilities can only be restricted to ancestor modules +resolve_anonymous_livetime_non_gat_report_error = + in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + .label = this lifetime must come from the implemented type + resolve_arguments_macro_use_not_allowed = arguments to `macro_use` are not allowed here resolve_associated_const_with_similar_name_exists = @@ -234,6 +238,10 @@ resolve_items_in_traits_are_not_importable = resolve_label_with_similar_name_reachable = a label with a similar name is reachable +resolve_lending_iterator_report_error = + associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type + .note = you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type. + resolve_lifetime_param_in_enum_discriminant = lifetime parameters may not be used in enum discriminant values diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 689109b2840f..e035749fc39e 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -19,6 +19,7 @@ use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind}; use rustc_ast::{Block, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, NodeId}; use rustc_attr as attr; use rustc_data_structures::sync::Lrc; +use rustc_expand::base::ResolverExpand; use rustc_expand::expand::AstFragment; use rustc_hir::def::{self, *}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; @@ -1358,6 +1359,14 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> { self.visit_invoc_in_module(item.id); } AssocCtxt::Impl => { + let invoc_id = item.id.placeholder_to_expn_id(); + if !self.r.glob_delegation_invoc_ids.contains(&invoc_id) { + self.r + .impl_unexpanded_invocations + .entry(self.r.invocation_parent(invoc_id)) + .or_default() + .insert(invoc_id); + } self.visit_invoc(item.id); } } @@ -1379,18 +1388,21 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> { self.r.feed_visibility(feed, vis); } + let ns = match item.kind { + AssocItemKind::Const(..) | AssocItemKind::Delegation(..) | AssocItemKind::Fn(..) => { + ValueNS + } + AssocItemKind::Type(..) => TypeNS, + AssocItemKind::MacCall(_) | AssocItemKind::DelegationMac(..) => bug!(), // handled above + }; if ctxt == AssocCtxt::Trait { - let ns = match item.kind { - AssocItemKind::Const(..) - | AssocItemKind::Delegation(..) - | AssocItemKind::Fn(..) => ValueNS, - AssocItemKind::Type(..) => TypeNS, - AssocItemKind::MacCall(_) | AssocItemKind::DelegationMac(..) => bug!(), // handled above - }; - let parent = self.parent_scope.module; let expansion = self.parent_scope.expansion; self.r.define(parent, item.ident, ns, (self.res(def_id), vis, item.span, expansion)); + } else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob) { + let impl_def_id = self.r.tcx.local_parent(local_def_id); + let key = BindingKey::new(item.ident.normalize_to_macros_2_0(), ns); + self.r.impl_binding_keys.entry(impl_def_id).or_default().insert(key); } visit::walk_assoc_item(self, item, ctxt); diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 5fe68085d653..fc3669fecc2a 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -32,7 +32,7 @@ use rustc_ast as ast; use rustc_ast::visit::{self, Visitor}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{pluralize, MultiSpan}; +use rustc_errors::MultiSpan; use rustc_hir::def::{DefKind, Res}; use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES}; use rustc_session::lint::builtin::{UNUSED_IMPORTS, UNUSED_QUALIFICATIONS}; @@ -151,11 +151,10 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> { // We do this in any edition. if warn_if_unused { if let Some(&span) = maybe_unused_extern_crates.get(&extern_crate.id) { - self.r.lint_buffer.buffer_lint_with_diagnostic( + self.r.lint_buffer.buffer_lint( UNUSED_EXTERN_CRATES, extern_crate.id, span, - "unused extern crate", BuiltinLintDiag::UnusedExternCrate { removal_span: extern_crate.span_with_attributes, }, @@ -204,11 +203,10 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> { .span .find_ancestor_inside(extern_crate.span) .unwrap_or(extern_crate.ident.span); - self.r.lint_buffer.buffer_lint_with_diagnostic( + self.r.lint_buffer.buffer_lint( UNUSED_EXTERN_CRATES, extern_crate.id, extern_crate.span, - "`extern crate` is not idiomatic in the new edition", BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span }, ); } @@ -299,13 +297,13 @@ fn calc_unused_spans( let mut unused_spans = Vec::new(); let mut to_remove = Vec::new(); - let mut used_childs = 0; + let mut used_children = 0; let mut contains_self = false; let mut previous_unused = false; for (pos, (use_tree, use_tree_id)) in nested.iter().enumerate() { let remove = match calc_unused_spans(unused_import, use_tree, *use_tree_id) { UnusedSpanResult::Used => { - used_childs += 1; + used_children += 1; None } UnusedSpanResult::Unused { mut spans, remove } => { @@ -313,7 +311,7 @@ fn calc_unused_spans( Some(remove) } UnusedSpanResult::PartialUnused { mut spans, remove: mut to_remove_extra } => { - used_childs += 1; + used_children += 1; unused_spans.append(&mut spans); to_remove.append(&mut to_remove_extra); None @@ -322,7 +320,7 @@ fn calc_unused_spans( if let Some(remove) = remove { let remove_span = if nested.len() == 1 { remove - } else if pos == nested.len() - 1 || used_childs > 0 { + } else if pos == nested.len() - 1 || used_children > 0 { // Delete everything from the end of the last import, to delete the // previous comma nested[pos - 1].0.span.shrink_to_hi().to(use_tree.span) @@ -346,7 +344,7 @@ fn calc_unused_spans( } if unused_spans.is_empty() { UnusedSpanResult::Used - } else if used_childs == 0 { + } else if used_children == 0 { UnusedSpanResult::Unused { spans: unused_spans, remove: full_span } } else { // If there is only one remaining child that is used, the braces around the use @@ -360,7 +358,7 @@ fn calc_unused_spans( // `self`: `use foo::{self};` is valid Rust syntax, while `use foo::self;` errors // out. We also cannot turn `use foo::{self}` into `use foo`, as the former doesn't // import types with the same name as the module. - if used_childs == 1 && !contains_self { + if used_children == 1 && !contains_self { // Left brace, from the start of the nested group to the first item. to_remove.push( tree_span.shrink_to_lo().to(nested.first().unwrap().0.span.shrink_to_lo()), @@ -394,10 +392,7 @@ impl Resolver<'_, '_> { MACRO_USE_EXTERN_CRATE, import.root_id, import.span, - "deprecated `#[macro_use]` attribute used to \ - import macros should be replaced at use sites \ - with a `use` item to import the macro \ - instead", + BuiltinLintDiag::MacroUseDeprecated, ); } } @@ -414,8 +409,12 @@ impl Resolver<'_, '_> { } } ImportKind::MacroUse { .. } => { - let msg = "unused `#[macro_use]` import"; - self.lint_buffer.buffer_lint(UNUSED_IMPORTS, import.root_id, import.span, msg); + self.lint_buffer.buffer_lint( + UNUSED_IMPORTS, + import.root_id, + import.span, + BuiltinLintDiag::UnusedMacroUse, + ); } _ => {} } @@ -434,20 +433,12 @@ impl Resolver<'_, '_> { visitor.report_unused_extern_crate_items(maybe_unused_extern_crates); for unused in visitor.unused_imports.values() { - let mut fixes = Vec::new(); - let spans = match calc_unused_spans(unused, &unused.use_tree, unused.use_tree_id) { - UnusedSpanResult::Used => continue, - UnusedSpanResult::Unused { spans, remove } => { - fixes.push((remove, String::new())); - spans - } - UnusedSpanResult::PartialUnused { spans, remove } => { - for fix in &remove { - fixes.push((*fix, String::new())); - } - spans - } - }; + let (spans, remove_spans) = + match calc_unused_spans(unused, &unused.use_tree, unused.use_tree_id) { + UnusedSpanResult::Used => continue, + UnusedSpanResult::Unused { spans, remove } => (spans, vec![remove]), + UnusedSpanResult::PartialUnused { spans, remove } => (spans, remove), + }; let ms = MultiSpan::from_spans(spans); @@ -459,23 +450,8 @@ impl Resolver<'_, '_> { .collect::>(); span_snippets.sort(); - let msg = format!( - "unused import{}{}", - pluralize!(ms.primary_spans().len()), - if !span_snippets.is_empty() { - format!(": {}", span_snippets.join(", ")) - } else { - String::new() - } - ); - - let fix_msg = if fixes.len() == 1 && fixes[0].0 == unused.item_span { - "remove the whole `use` item" - } else if ms.primary_spans().len() > 1 { - "remove the unused imports" - } else { - "remove the unused import" - }; + let remove_whole_use = remove_spans.len() == 1 && remove_spans[0] == unused.item_span; + let num_to_remove = ms.primary_spans().len(); // If we are in the `--test` mode, suppress a help that adds the `#[cfg(test)]` // attribute; however, if not, suggest adding the attribute. There is no way to @@ -501,12 +477,17 @@ impl Resolver<'_, '_> { } }; - visitor.r.lint_buffer.buffer_lint_with_diagnostic( + visitor.r.lint_buffer.buffer_lint( UNUSED_IMPORTS, unused.use_tree_id, ms, - msg, - BuiltinLintDiag::UnusedImports(fix_msg.into(), fixes, test_module_span), + BuiltinLintDiag::UnusedImports { + remove_whole_use, + num_to_remove, + remove_spans, + test_module_span, + span_snippets, + }, ); } @@ -552,11 +533,10 @@ impl Resolver<'_, '_> { continue; } - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( UNUSED_QUALIFICATIONS, unn_qua.node_id, unn_qua.path_span, - "unnecessary qualification", BuiltinLintDiag::UnusedQualifications { removal_span: unn_qua.removal_span }, ); } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index bd0622428569..fb6e55f2b7bd 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -2,6 +2,7 @@ use crate::{ImplTraitContext, Resolver}; use rustc_ast::visit::FnKind; use rustc_ast::*; use rustc_expand::expand::AstFragment; +use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::LocalDefId; use rustc_span::hygiene::LocalExpnId; @@ -128,7 +129,11 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { ItemKind::Union(..) => DefKind::Union, ItemKind::ExternCrate(..) => DefKind::ExternCrate, ItemKind::TyAlias(..) => DefKind::TyAlias, - ItemKind::Static(s) => DefKind::Static { mutability: s.mutability, nested: false }, + ItemKind::Static(s) => DefKind::Static { + safety: hir::Safety::Safe, + mutability: s.mutability, + nested: false, + }, ItemKind::Const(..) => DefKind::Const, ItemKind::Fn(..) | ItemKind::Delegation(..) => DefKind::Fn, ItemKind::MacroDef(..) => { @@ -139,8 +144,9 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { } ItemKind::GlobalAsm(..) => DefKind::GlobalAsm, ItemKind::Use(..) => return visit::walk_item(self, i), - ItemKind::MacCall(..) => return self.visit_macro_invoc(i.id), - ItemKind::DelegationMac(..) => unreachable!(), + ItemKind::MacCall(..) | ItemKind::DelegationMac(..) => { + return self.visit_macro_invoc(i.id); + } }; let def_id = self.create_def(i.id, i.ident.name, def_kind, i.span); @@ -211,8 +217,18 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { let def_kind = match fi.kind { - ForeignItemKind::Static(box StaticForeignItem { ty: _, mutability, expr: _ }) => { - DefKind::Static { mutability, nested: false } + ForeignItemKind::Static(box StaticForeignItem { + ty: _, + mutability, + expr: _, + safety, + }) => { + let safety = match safety { + ast::Safety::Unsafe(_) | ast::Safety::Default => hir::Safety::Unsafe, + ast::Safety::Safe(_) => hir::Safety::Safe, + }; + + DefKind::Static { safety, mutability, nested: false } } ForeignItemKind::Fn(_) => DefKind::Fn, ForeignItemKind::TyAlias(_) => DefKind::ForeignTy, @@ -279,8 +295,9 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { AssocItemKind::Fn(..) | AssocItemKind::Delegation(..) => DefKind::AssocFn, AssocItemKind::Const(..) => DefKind::AssocConst, AssocItemKind::Type(..) => DefKind::AssocTy, - AssocItemKind::MacCall(..) => return self.visit_macro_invoc(i.id), - AssocItemKind::DelegationMac(..) => unreachable!(), + AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => { + return self.visit_macro_invoc(i.id); + } }; let def = self.create_def(i.id, i.ident.name, def_kind, i.span); diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index b28312fa473a..263daa11ec31 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -6,7 +6,7 @@ use rustc_ast::{MetaItemKind, NestedMetaItem}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ - codes::*, report_ambiguity_error, struct_span_code_err, Applicability, Diag, DiagCtxt, + codes::*, report_ambiguity_error, struct_span_code_err, Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, MultiSpan, SuggestionStyle, }; use rustc_feature::BUILTIN_ATTRIBUTES; @@ -120,7 +120,7 @@ fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span { } impl<'a, 'tcx> Resolver<'a, 'tcx> { - pub(crate) fn dcx(&self) -> &'tcx DiagCtxt { + pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> { self.tcx.dcx() } @@ -128,13 +128,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.report_with_use_injections(krate); for &(span_use, span_def) in &self.macro_expanded_macro_export_errors { - let msg = "macro-expanded `macro_export` macros from the current crate \ - cannot be referred to by absolute paths"; - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, CRATE_NODE_ID, span_use, - msg, BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def), ); } @@ -145,11 +142,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let NameBindingKind::Import { import, .. } = ambiguity_error.b1.0.kind else { unreachable!() }; - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( AMBIGUOUS_GLOB_IMPORTS, import.root_id, ambiguity_error.ident.span, - diag.msg.to_string(), BuiltinLintDiag::AmbiguousGlobImports { diag }, ); } else { @@ -338,12 +334,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Some((import, _, true)) if should_remove_import && !import.is_glob() => { // Simple case - remove the entire import. Due to the above match arm, this can // only be a single use so just remove it entirely. - err.subdiagnostic( - self.tcx.dcx(), - errors::ToolOnlyRemoveUnnecessaryImport { - span: import.use_span_with_attributes, - }, - ); + err.subdiagnostic(errors::ToolOnlyRemoveUnnecessaryImport { + span: import.use_span_with_attributes, + }); } Some((import, span, _)) => { self.add_suggestion_for_rename_of_use(&mut err, name, import, span); @@ -409,12 +402,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } if let Some(suggestion) = suggestion { - err.subdiagnostic( - self.dcx(), - ChangeImportBindingSuggestion { span: binding_span, suggestion }, - ); + err.subdiagnostic(ChangeImportBindingSuggestion { span: binding_span, suggestion }); } else { - err.subdiagnostic(self.dcx(), ChangeImportBinding { span: binding_span }); + err.subdiagnostic(ChangeImportBinding { span: binding_span }); } } @@ -462,20 +452,19 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // previous imports. if found_closing_brace { if let Some(span) = extend_span_to_previous_binding(self.tcx.sess, span) { - err.subdiagnostic(self.dcx(), errors::ToolOnlyRemoveUnnecessaryImport { span }); + err.subdiagnostic(errors::ToolOnlyRemoveUnnecessaryImport { span }); } else { // Remove the entire line if we cannot extend the span back, this indicates an // `issue_52891::{self}` case. - err.subdiagnostic( - self.dcx(), - errors::RemoveUnnecessaryImport { span: import.use_span_with_attributes }, - ); + err.subdiagnostic(errors::RemoveUnnecessaryImport { + span: import.use_span_with_attributes, + }); } return; } - err.subdiagnostic(self.dcx(), errors::RemoveUnnecessaryImport { span }); + err.subdiagnostic(errors::RemoveUnnecessaryImport { span }); } pub(crate) fn lint_if_path_starts_with_module( @@ -526,12 +515,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } let diag = BuiltinLintDiag::AbsPathWithModule(root_span); - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, node_id, root_span, - "absolute paths must start with `self`, `super`, \ - `crate`, or an external crate name in the 2018 edition", diag, ); } @@ -688,10 +675,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { .dcx() .create_err(errors::VariableIsNotBoundInAllPatterns { multispan: msp, name }); for sp in target_sp { - err.subdiagnostic(self.dcx(), errors::PatternDoesntBindName { span: sp, name }); + err.subdiagnostic(errors::PatternDoesntBindName { span: sp, name }); } for sp in origin_sp { - err.subdiagnostic(self.dcx(), errors::VariableNotInAllPatterns { span: sp }); + err.subdiagnostic(errors::VariableNotInAllPatterns { span: sp }); } if could_be_path { let import_suggestions = self.lookup_import_candidates( @@ -1452,12 +1439,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ); if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules { - err.subdiagnostic(self.dcx(), MaybeMissingMacroRulesName { span: ident.span }); + err.subdiagnostic(MaybeMissingMacroRulesName { span: ident.span }); return; } if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { - err.subdiagnostic(self.dcx(), ExplicitUnsafeTraits { span: ident.span, ident }); + err.subdiagnostic(ExplicitUnsafeTraits { span: ident.span, ident }); return; } @@ -1473,14 +1460,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let scope = self.local_macro_def_scopes[&def_id]; let parent_nearest = parent_scope.module.nearest_parent_mod(); if Some(parent_nearest) == scope.opt_def_id() { - err.subdiagnostic(self.dcx(), MacroDefinedLater { span: unused_ident.span }); - err.subdiagnostic(self.dcx(), MacroSuggMovePosition { span: ident.span, ident }); + err.subdiagnostic(MacroDefinedLater { span: unused_ident.span }); + err.subdiagnostic(MacroSuggMovePosition { span: ident.span, ident }); return; } } if self.macro_names.contains(&ident.normalize_to_macros_2_0()) { - err.subdiagnostic(self.dcx(), AddedMacroUse); + err.subdiagnostic(AddedMacroUse); return; } @@ -1490,13 +1477,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let span = self.def_span(def_id); let source_map = self.tcx.sess.source_map(); let head_span = source_map.guess_head_span(span); - err.subdiagnostic( - self.dcx(), - ConsiderAddingADerive { - span: head_span.shrink_to_lo(), - suggestion: "#[derive(Default)]\n".to_string(), - }, - ); + err.subdiagnostic(ConsiderAddingADerive { + span: head_span.shrink_to_lo(), + suggestion: "#[derive(Default)]\n".to_string(), + }); } for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] { if let Ok(binding) = self.early_resolve_ident_in_lexical_scope( @@ -1539,7 +1523,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { imported_ident: ident, imported_ident_desc: &desc, }; - err.subdiagnostic(self.tcx.dcx(), note); + err.subdiagnostic(note); // Silence the 'unused import' warning we might get, // since this diagnostic already covers that import. self.record_use(ident, binding, Used::Other); @@ -1550,7 +1534,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { imported_ident: ident, imported_ident_desc: &desc, }; - err.subdiagnostic(self.tcx.dcx(), note); + err.subdiagnostic(note); return; } } @@ -1568,6 +1552,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Some(suggestion) if suggestion.candidate == kw::Underscore => return false, Some(suggestion) => suggestion, }; + + let mut did_label_def_span = false; + if let Some(def_span) = suggestion.res.opt_def_id().map(|def_id| self.def_span(def_id)) { if span.overlaps(def_span) { // Don't suggest typo suggestion for itself like in the following: @@ -1601,31 +1588,38 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { errors::DefinedHere::SingleItem { span, candidate_descr, candidate } } }; - err.subdiagnostic(self.tcx.dcx(), label); + did_label_def_span = true; + err.subdiagnostic(label); } - let (span, sugg, post) = if let SuggestionTarget::SimilarlyNamed = suggestion.target + let (span, msg, sugg) = if let SuggestionTarget::SimilarlyNamed = suggestion.target && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) && let Some(span) = suggestion.span && let Some(candidate) = suggestion.candidate.as_str().strip_prefix('_') && snippet == candidate { + let candidate = suggestion.candidate; // When the suggested binding change would be from `x` to `_x`, suggest changing the // original binding definition instead. (#60164) - let post = format!(", consider renaming `{}` into `{snippet}`", suggestion.candidate); - (span, snippet, post) - } else { - (span, suggestion.candidate.to_ident_string(), String::new()) - }; - let msg = match suggestion.target { - SuggestionTarget::SimilarlyNamed => format!( - "{} {} with a similar name exists{post}", - suggestion.res.article(), - suggestion.res.descr() - ), - SuggestionTarget::SingleItem => { - format!("maybe you meant this {}", suggestion.res.descr()) + let msg = format!( + "the leading underscore in `{candidate}` marks it as unused, consider renaming it to `{snippet}`" + ); + if !did_label_def_span { + err.span_label(span, format!("`{candidate}` defined here")); } + (span, msg, snippet) + } else { + let msg = match suggestion.target { + SuggestionTarget::SimilarlyNamed => format!( + "{} {} with a similar name exists", + suggestion.res.article(), + suggestion.res.descr() + ), + SuggestionTarget::SingleItem => { + format!("maybe you meant this {}", suggestion.res.descr()) + } + }; + (span, msg, suggestion.candidate.to_ident_string()) }; err.span_suggestion(span, msg, sugg, Applicability::MaybeIncorrect); true @@ -1786,7 +1780,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { outer_ident_descr: this_res.descr(), outer_ident, }; - err.subdiagnostic(self.tcx.dcx(), label); + err.subdiagnostic(label); } } @@ -1801,14 +1795,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { non_exhaustive = Some(attr.span); } else if let Some(span) = ctor_fields_span { let label = errors::ConstructorPrivateIfAnyFieldPrivate { span }; - err.subdiagnostic(self.tcx.dcx(), label); + err.subdiagnostic(label); if let Res::Def(_, d) = res && let Some(fields) = self.field_visibility_spans.get(&d) { let spans = fields.iter().map(|span| *span).collect(); let sugg = errors::ConsiderMakingTheFieldPublic { spans, number_of_fields: fields.len() }; - err.subdiagnostic(self.tcx.dcx(), sugg); + err.subdiagnostic(sugg); } } @@ -1917,7 +1911,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { first, dots: next_binding.is_some(), }; - err.subdiagnostic(self.tcx.dcx(), note); + err.subdiagnostic(note); } // We prioritize shorter paths, non-core imports and direct imports over the alternatives. sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0] == "core", *reexport)); @@ -1936,7 +1930,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } else { errors::ImportIdent::Directly { span: dedup_span, ident, path } }; - err.subdiagnostic(self.tcx.dcx(), sugg); + err.subdiagnostic(sugg); break; } @@ -2517,14 +2511,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } let note = errors::FoundItemConfigureOut { span: name.span }; - err.subdiagnostic(self.tcx.dcx(), note); + err.subdiagnostic(note); if let MetaItemKind::List(nested) = &cfg.kind && let NestedMetaItem::MetaItem(meta_item) = &nested[0] && let MetaItemKind::NameValue(feature_name) = &meta_item.kind { let note = errors::ItemWasBehindFeature { feature: feature_name.symbol }; - err.subdiagnostic(self.tcx.dcx(), note); + err.subdiagnostic(note); } } } @@ -2779,33 +2773,65 @@ fn show_candidates( // by iterating through a hash map, so make sure they are ordered: for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] { path_strings.sort_by(|a, b| a.0.cmp(&b.0)); + path_strings.dedup_by(|a, b| a.0 == b.0); let core_path_strings = path_strings.extract_if(|p| p.0.starts_with("core::")).collect::>(); - path_strings.extend(core_path_strings); - path_strings.dedup_by(|a, b| a.0 == b.0); + let std_path_strings = + path_strings.extract_if(|p| p.0.starts_with("std::")).collect::>(); + let foreign_crate_path_strings = + path_strings.extract_if(|p| !p.0.starts_with("crate::")).collect::>(); + + // We list the `crate` local paths first. + // Then we list the `std`/`core` paths. + if std_path_strings.len() == core_path_strings.len() { + // Do not list `core::` paths if we are already listing the `std::` ones. + path_strings.extend(std_path_strings); + } else { + path_strings.extend(std_path_strings); + path_strings.extend(core_path_strings); + } + // List all paths from foreign crates last. + path_strings.extend(foreign_crate_path_strings); } - accessible_path_strings.sort(); if !accessible_path_strings.is_empty() { - let (determiner, kind, name, through) = + let (determiner, kind, s, name, through) = if let [(name, descr, _, _, via_import)] = &accessible_path_strings[..] { ( "this", *descr, + "", format!(" `{name}`"), if *via_import { " through its public re-export" } else { "" }, ) } else { - ("one of these", "items", String::new(), "") + // Get the unique item kinds and if there's only one, we use the right kind name + // instead of the more generic "items". + let mut kinds = accessible_path_strings + .iter() + .map(|(_, descr, _, _, _)| *descr) + .collect::>() + .into_iter(); + let kind = if let Some(kind) = kinds.next() + && let None = kinds.next() + { + kind + } else { + "item" + }; + let s = if kind.ends_with('s') { "es" } else { "s" }; + + ("one of these", kind, s, String::new(), "") }; let instead = if let Instead::Yes = instead { " instead" } else { "" }; let mut msg = if let DiagMode::Pattern = mode { format!( - "if you meant to match on {kind}{instead}{name}, use the full path in the pattern", + "if you meant to match on {kind}{s}{instead}{name}, use the full path in the \ + pattern", ) } else { - format!("consider importing {determiner} {kind}{through}{instead}") + format!("consider importing {determiner} {kind}{s}{through}{instead}") }; for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index edfeacec7e3b..0620f3d709eb 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -882,6 +882,23 @@ pub(crate) struct ElidedAnonymousLivetimeReportError { pub(crate) suggestion: Option, } +#[derive(Diagnostic)] +#[diag(resolve_lending_iterator_report_error)] +pub(crate) struct LendingIteratorReportError { + #[primary_span] + pub(crate) lifetime: Span, + #[note] + pub(crate) ty: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_anonymous_livetime_non_gat_report_error)] +pub(crate) struct AnonymousLivetimeNonGatReportError { + #[primary_span] + #[label] + pub(crate) lifetime: Span, +} + #[derive(Subdiagnostic)] #[multipart_suggestion( resolve_elided_anonymous_lifetime_report_error_suggestion, diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index f88725830f1e..7d531385e212 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -524,18 +524,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { match binding { Ok(binding) => { if let Some(lint_id) = derive_fallback_lint_id { - this.lint_buffer.buffer_lint_with_diagnostic( + this.lint_buffer.buffer_lint( PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, lint_id, orig_ident.span, - format!( - "cannot find {} `{}` in this scope", - ns.descr(), - ident - ), - BuiltinLintDiag::ProcMacroDeriveResolutionFallback( - orig_ident.span, - ), + BuiltinLintDiag::ProcMacroDeriveResolutionFallback { + span: orig_ident.span, + ns, + ident, + }, ); } let misc_flags = if module == this.graph_root { @@ -968,6 +965,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // if it can then our result is not determined and can be invalidated. for single_import in &resolution.single_imports { let Some(import_vis) = single_import.vis.get() else { + // This branch handles a cycle in single imports, which occurs + // when we've previously **steal** the `vis` value during an import + // process. + // + // For example: + // ``` + // use a::b; + // use b as a; + // ``` + // 1. Steal the `vis` in `use a::b` and attempt to locate `a` in the + // current module. + // 2. Encounter the import `use b as a`, which is a `single_import` for `a`, + // and try to find `b` in the current module. + // 3. Re-encounter the `use a::b` import since it's a `single_import` of `b`. + // This leads to entering this branch. continue; }; if !self.is_accessible_from(import_vis, parent_scope.module) { @@ -982,15 +994,32 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // named imports. continue; } + let Some(module) = single_import.imported_module.get() else { return Err((Undetermined, Weak::No)); }; - let ImportKind::Single { source: ident, .. } = single_import.kind else { + let ImportKind::Single { source, target, target_bindings, .. } = &single_import.kind + else { unreachable!(); }; + if source != target { + // This branch allows the binding to be defined or updated later if the target name + // can hide the source. + if target_bindings.iter().all(|binding| binding.get().is_none()) { + // None of the target bindings are available, so we can't determine + // if this binding is correct or not. + // See more details in #124840 + return Err((Undetermined, Weak::No)); + } else if target_bindings[ns].get().is_none() && binding.is_some() { + // `binding.is_some()` avoids the condition where the binding + // truly doesn't exist in this namespace and should return `Err(Determined)`. + return Err((Undetermined, Weak::No)); + } + } + match self.resolve_ident_in_module( module, - ident, + *source, ns, &single_import.parent_scope, None, diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index f53bcb0e9d0d..96a4647b9428 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -259,13 +259,18 @@ struct UnresolvedImportError { // Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;` // are permitted for backward-compatibility under a deprecation lint. -fn pub_use_of_private_extern_crate_hack(import: Import<'_>, binding: NameBinding<'_>) -> bool { +fn pub_use_of_private_extern_crate_hack( + import: Import<'_>, + binding: NameBinding<'_>, +) -> Option { match (&import.kind, &binding.kind) { - (ImportKind::Single { .. }, NameBindingKind::Import { import: binding_import, .. }) => { - matches!(binding_import.kind, ImportKind::ExternCrate { .. }) - && import.expect_vis().is_public() + (ImportKind::Single { .. }, NameBindingKind::Import { import: binding_import, .. }) + if let ImportKind::ExternCrate { id, .. } = binding_import.kind + && import.expect_vis().is_public() => + { + Some(id) } - _ => false, + _ => None, } } @@ -275,7 +280,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { pub(crate) fn import(&self, binding: NameBinding<'a>, import: Import<'a>) -> NameBinding<'a> { let import_vis = import.expect_vis().to_def_id(); let vis = if binding.vis.is_at_least(import_vis, self.tcx) - || pub_use_of_private_extern_crate_hack(import, binding) + || pub_use_of_private_extern_crate_hack(import, binding).is_some() { import_vis } else { @@ -352,9 +357,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { (old_glob @ true, false) | (old_glob @ false, true) => { let (glob_binding, nonglob_binding) = if old_glob { (old_binding, binding) } else { (binding, old_binding) }; - if glob_binding.res() != nonglob_binding.res() - && key.ns == MacroNS + if key.ns == MacroNS && nonglob_binding.expansion != LocalExpnId::ROOT + && glob_binding.res() != nonglob_binding.res() { resolution.binding = Some(this.ambiguity( AmbiguityKind::GlobVsExpanded, @@ -537,6 +542,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let determined_imports = mem::take(&mut self.determined_imports); let indeterminate_imports = mem::take(&mut self.indeterminate_imports); + let mut glob_error = false; for (is_indeterminate, import) in determined_imports .iter() .map(|i| (false, i)) @@ -548,6 +554,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.import_dummy_binding(*import, is_indeterminate); if let Some(err) = unresolved_import_error { + glob_error |= import.is_glob(); + if let ImportKind::Single { source, ref source_bindings, .. } = import.kind { if source.name == kw::SelfLower { // Silence `unresolved import` error if E0429 is already emitted @@ -563,7 +571,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { { // In the case of a new import line, throw a diagnostic message // for the previous line. - self.throw_unresolved_import_error(errors); + self.throw_unresolved_import_error(errors, glob_error); errors = vec![]; } if seen_spans.insert(err.span) { @@ -574,7 +582,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } if !errors.is_empty() { - self.throw_unresolved_import_error(errors); + self.throw_unresolved_import_error(errors, glob_error); return; } @@ -600,9 +608,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - if !errors.is_empty() { - self.throw_unresolved_import_error(errors); - } + self.throw_unresolved_import_error(errors, glob_error); } pub(crate) fn check_hidden_glob_reexports( @@ -619,11 +625,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { && binding.res() != Res::Err && exported_ambiguities.contains(&binding) { - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( AMBIGUOUS_GLOB_REEXPORTS, import.root_id, import.root_span, - "ambiguous glob re-exports", BuiltinLintDiag::AmbiguousGlobReexports { name: key.ident.to_string(), namespace: key.ns.descr().to_string(), @@ -655,11 +660,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { && glob_binding.vis.is_public() && !binding.vis.is_public() { - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( HIDDEN_GLOB_REEXPORTS, binding_id, binding.span, - "private item shadows public glob re-export", BuiltinLintDiag::HiddenGlobReexports { name: key.ident.name.to_string(), namespace: key.ns.descr().to_owned(), @@ -674,7 +678,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - fn throw_unresolved_import_error(&mut self, errors: Vec<(Import<'_>, UnresolvedImportError)>) { + fn throw_unresolved_import_error( + &mut self, + errors: Vec<(Import<'_>, UnresolvedImportError)>, + glob_error: bool, + ) { if errors.is_empty() { return; } @@ -753,7 +761,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - diag.emit(); + let guar = diag.emit(); + if glob_error { + self.glob_error = Some(guar); + } } /// Attempts to resolve the given import, returning: @@ -1015,17 +1026,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { && !max_vis.is_at_least(import_vis, self.tcx) { let def_id = self.local_def_id(id); - let msg = format!( - "glob import doesn't reexport anything with visibility `{}` because no imported item is public enough", - import_vis.to_string(def_id, self.tcx) - ); - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( UNUSED_IMPORTS, id, import.span, - msg, BuiltinLintDiag::RedundantImportVisibility { max_vis: max_vis.to_string(def_id, self.tcx), + import_vis: import_vis.to_string(def_id, self.tcx), span: import.span, }, ); @@ -1251,17 +1258,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // All namespaces must be re-exported with extra visibility for an error to occur. if !any_successful_reexport { let (ns, binding) = reexport_error.unwrap(); - if pub_use_of_private_extern_crate_hack(import, binding) { - let msg = format!( - "extern crate `{ident}` is private, and cannot be \ - re-exported (error E0365), consider declaring with \ - `pub`" - ); + if let Some(extern_crate_id) = pub_use_of_private_extern_crate_hack(import, binding) { self.lint_buffer.buffer_lint( PUB_USE_OF_PRIVATE_EXTERN_CRATE, import_id, import.span, - msg, + BuiltinLintDiag::PrivateExternCrateReexport { + source: ident, + extern_crate_span: self.tcx.source_span(self.local_def_id(extern_crate_id)), + }, ); } else { if ns == TypeNS { @@ -1289,12 +1294,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // exclude decl_macro if self.get_macro_by_def_id(def_id).macro_rules => { - err.subdiagnostic(self.dcx(), ConsiderAddingMacroExport { + err.subdiagnostic( ConsiderAddingMacroExport { span: binding.span, }); } _ => { - err.subdiagnostic(self.dcx(), ConsiderMarkingAsPub { + err.subdiagnostic( ConsiderMarkingAsPub { span: import.span, ident, }); @@ -1397,7 +1402,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { UNUSED_IMPORTS, id, import.span, - format!("the item `{source}` is imported redundantly"), BuiltinLintDiag::RedundantImport(redundant_spans, source), ); */ diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 0f585aafdd5c..5ab6ba23a7da 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -9,10 +9,10 @@ use crate::{errors, path_names_to_string, rustdoc, BindingError, Finalize, LexicalScopeBinding}; use crate::{BindingKey, Used}; use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult}; -use crate::{ResolutionError, Resolver, Segment, UseError}; +use crate::{ResolutionError, Resolver, Segment, TyCtxt, UseError}; use rustc_ast::ptr::P; -use rustc_ast::visit::{walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor}; +use rustc_ast::visit::{visit_opt, walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor}; use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::{codes::*, Applicability, DiagArgValue, IntoDiagArg, StashKey}; @@ -24,7 +24,7 @@ use rustc_middle::middle::resolve_bound_vars::Set1; use rustc_middle::ty::DelegationFnSig; use rustc_middle::{bug, span_bug}; use rustc_session::config::{CrateType, ResolveDocLinks}; -use rustc_session::lint; +use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::parse::feature_err; use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -310,9 +310,10 @@ enum LifetimeRibKind { /// error on default object bounds (e.g., `Box`). AnonymousReportError, - /// Resolves elided lifetimes to `'static`, but gives a warning that this behavior - /// is a bug and will be reverted soon. - AnonymousWarn(NodeId), + /// Resolves elided lifetimes to `'static` if there are no other lifetimes in scope, + /// otherwise give a warning that the previous behavior of introducing a new early-bound + /// lifetime is a bug and will be removed (if `emit_lint` is enabled). + StaticIfNoLifetimeInScope { lint_id: NodeId, emit_lint: bool }, /// Signal we cannot find which should be the anonymous lifetime. ElisionFailure, @@ -629,6 +630,9 @@ struct DiagMetadata<'ast> { in_assignment: Option<&'ast Expr>, is_assign_rhs: bool, + /// If we are setting an associated type in trait impl, is it a non-GAT type? + in_non_gat_assoc_type: Option, + /// Used to detect possible `.` -> `..` typo when calling methods. in_range: Option<(&'ast Expr, &'ast Expr)>, @@ -795,7 +799,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, self.r.record_partial_res(ty.id, PartialRes::new(res)); visit::walk_ty(self, ty) } - TyKind::ImplTrait(node_id, _, _) => { + TyKind::ImplTrait(node_id, _) => { let candidates = self.lifetime_elision_candidates.take(); visit::walk_ty(self, ty); self.record_lifetime_params_for_impl_trait(*node_id); @@ -1148,7 +1152,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, self.diag_metadata.currently_processing_generic_args = prev; } - fn visit_assoc_constraint(&mut self, constraint: &'ast AssocConstraint) { + fn visit_assoc_item_constraint(&mut self, constraint: &'ast AssocItemConstraint) { self.visit_ident(constraint.ident); if let Some(ref gen_args) = constraint.gen_args { // Forbid anonymous lifetimes in GAT parameters until proper semantics are decided. @@ -1157,13 +1161,13 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, }); } match constraint.kind { - AssocConstraintKind::Equality { ref term } => match term { + AssocItemConstraintKind::Equality { ref term } => match term { Term::Ty(ty) => self.visit_ty(ty), Term::Const(c) => { self.resolve_anon_const(c, AnonConstKind::ConstArg(IsRepeatExpr::No)) } }, - AssocConstraintKind::Bound { ref bounds } => { + AssocItemConstraintKind::Bound { ref bounds } => { self.record_lifetime_params_for_impl_trait(constraint.id); walk_list!(self, visit_param_bound, bounds, BoundKind::Bound); } @@ -1209,7 +1213,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, } LifetimeRibKind::AnonymousCreateParameter { .. } | LifetimeRibKind::AnonymousReportError - | LifetimeRibKind::AnonymousWarn(_) + | LifetimeRibKind::StaticIfNoLifetimeInScope { .. } | LifetimeRibKind::Elided(_) | LifetimeRibKind::ElisionFailure | LifetimeRibKind::ConcreteAnonConst(_) @@ -1577,7 +1581,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // lifetime would be illegal. LifetimeRibKind::Item | LifetimeRibKind::AnonymousReportError - | LifetimeRibKind::AnonymousWarn(_) + | LifetimeRibKind::StaticIfNoLifetimeInScope { .. } | LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many), // An anonymous lifetime is legal here, and bound to the right // place, go ahead. @@ -1640,7 +1644,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { | LifetimeRibKind::Generics { .. } | LifetimeRibKind::ElisionFailure | LifetimeRibKind::AnonymousReportError - | LifetimeRibKind::AnonymousWarn(_) => {} + | LifetimeRibKind::StaticIfNoLifetimeInScope { .. } => {} } } @@ -1674,22 +1678,36 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { self.record_lifetime_res(lifetime.id, res, elision_candidate); return; } - LifetimeRibKind::AnonymousWarn(node_id) => { - let msg = if elided { - "`&` without an explicit lifetime name cannot be used here" - } else { - "`'_` cannot be used here" - }; - self.r.lint_buffer.buffer_lint_with_diagnostic( - lint::builtin::ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, - node_id, - lifetime.ident.span, - msg, - lint::BuiltinLintDiag::AssociatedConstElidedLifetime { - elided, - span: lifetime.ident.span, - }, - ); + LifetimeRibKind::StaticIfNoLifetimeInScope { lint_id: node_id, emit_lint } => { + let mut lifetimes_in_scope = vec![]; + for rib in &self.lifetime_ribs[..i] { + lifetimes_in_scope.extend(rib.bindings.iter().map(|(ident, _)| ident.span)); + // Consider any anonymous lifetimes, too + if let LifetimeRibKind::AnonymousCreateParameter { binder, .. } = rib.kind + && let Some(extra) = self.r.extra_lifetime_params_map.get(&binder) + { + lifetimes_in_scope.extend(extra.iter().map(|(ident, _, _)| ident.span)); + } + } + if lifetimes_in_scope.is_empty() { + self.record_lifetime_res( + lifetime.id, + LifetimeRes::Static, + elision_candidate, + ); + return; + } else if emit_lint { + self.r.lint_buffer.buffer_lint( + lint::builtin::ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, + node_id, + lifetime.ident.span, + lint::BuiltinLintDiag::AssociatedConstElidedLifetime { + elided, + span: lifetime.ident.span, + lifetimes_in_scope: lifetimes_in_scope.into(), + }, + ); + } } LifetimeRibKind::AnonymousReportError => { if elided { @@ -1709,10 +1727,35 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { break; } } - self.r.dcx().emit_err(errors::ElidedAnonymousLivetimeReportError { - span: lifetime.ident.span, - suggestion, - }); + + // are we trying to use an anonymous lifetime + // on a non GAT associated trait type? + if !self.in_func_body + && let Some((module, _)) = &self.current_trait_ref + && let Some(ty) = &self.diag_metadata.current_self_type + && Some(true) == self.diag_metadata.in_non_gat_assoc_type + && let crate::ModuleKind::Def(DefKind::Trait, trait_id, _) = module.kind + { + if def_id_matches_path( + self.r.tcx, + trait_id, + &["core", "iter", "traits", "iterator", "Iterator"], + ) { + self.r.dcx().emit_err(errors::LendingIteratorReportError { + lifetime: lifetime.ident.span, + ty: ty.span(), + }); + } else { + self.r.dcx().emit_err(errors::AnonymousLivetimeNonGatReportError { + lifetime: lifetime.ident.span, + }); + } + } else { + self.r.dcx().emit_err(errors::ElidedAnonymousLivetimeReportError { + span: lifetime.ident.span, + suggestion, + }); + } } else { self.r.dcx().emit_err(errors::ExplicitAnonymousLivetimeReportError { span: lifetime.ident.span, @@ -1882,7 +1925,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // impl Foo for std::cell::Ref // note lack of '_ // async fn foo(_: std::cell::Ref) { ... } LifetimeRibKind::AnonymousCreateParameter { report_in_path: true, .. } - | LifetimeRibKind::AnonymousWarn(_) => { + | LifetimeRibKind::StaticIfNoLifetimeInScope { .. } => { let sess = self.r.tcx.sess; let subdiag = rustc_errors::elided_lifetime_in_path_suggestion( sess.source_map(), @@ -1966,11 +2009,10 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } if should_lint { - self.r.lint_buffer.buffer_lint_with_diagnostic( + self.r.lint_buffer.buffer_lint( lint::builtin::ELIDED_LIFETIMES_IN_PATHS, segment_id, elided_lifetime_span, - "hidden lifetime parameters in types are deprecated", lint::BuiltinLintDiag::ElidedLifetimesInPaths( expected_lifetimes, path_span, @@ -2817,19 +2859,27 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { kind: LifetimeBinderKind::ConstItem, }, |this| { - this.visit_generics(generics); - this.visit_ty(ty); + this.with_lifetime_rib( + LifetimeRibKind::StaticIfNoLifetimeInScope { + lint_id: item.id, + emit_lint: false, + }, + |this| { + this.visit_generics(generics); + this.visit_ty(ty); - // Only impose the restrictions of `ConstRibKind` for an - // actual constant expression in a provided default. - if let Some(expr) = expr { - // We allow arbitrary const expressions inside of associated consts, - // even if they are potentially not const evaluatable. - // - // Type parameters can already be used and as associated consts are - // not used as part of the type system, this is far less surprising. - this.resolve_const_body(expr, None); - } + // Only impose the restrictions of `ConstRibKind` for an + // actual constant expression in a provided default. + if let Some(expr) = expr { + // We allow arbitrary const expressions inside of associated consts, + // even if they are potentially not const evaluatable. + // + // Type parameters can already be used and as associated consts are + // not used as part of the type system, this is far less surprising. + this.resolve_const_body(expr, None); + } + }, + ) }, ); } @@ -3009,30 +3059,37 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { kind: LifetimeBinderKind::ConstItem, }, |this| { - this.with_lifetime_rib(LifetimeRibKind::AnonymousWarn(item.id), |this| { - // If this is a trait impl, ensure the const - // exists in trait - this.check_trait_item( - item.id, - item.ident, - &item.kind, - ValueNS, - item.span, - seen_trait_items, - |i, s, c| ConstNotMemberOfTrait(i, s, c), - ); + this.with_lifetime_rib( + LifetimeRibKind::StaticIfNoLifetimeInScope { + lint_id: item.id, + // In impls, it's not a hard error yet due to backcompat. + emit_lint: true, + }, + |this| { + // If this is a trait impl, ensure the const + // exists in trait + this.check_trait_item( + item.id, + item.ident, + &item.kind, + ValueNS, + item.span, + seen_trait_items, + |i, s, c| ConstNotMemberOfTrait(i, s, c), + ); - this.visit_generics(generics); - this.visit_ty(ty); - if let Some(expr) = expr { - // We allow arbitrary const expressions inside of associated consts, - // even if they are potentially not const evaluatable. - // - // Type parameters can already be used and as associated consts are - // not used as part of the type system, this is far less surprising. - this.resolve_const_body(expr, None); - } - }); + this.visit_generics(generics); + this.visit_ty(ty); + if let Some(expr) = expr { + // We allow arbitrary const expressions inside of associated consts, + // even if they are potentially not const evaluatable. + // + // Type parameters can already be used and as associated consts are + // not used as part of the type system, this is far less surprising. + this.resolve_const_body(expr, None); + } + }, + ); }, ); } @@ -3065,6 +3122,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { ); } AssocItemKind::Type(box TyAlias { generics, .. }) => { + self.diag_metadata.in_non_gat_assoc_type = Some(generics.params.is_empty()); debug!("resolve_implementation AssocItemKind::Type"); // We also need a new scope for the impl item type parameters. self.with_generic_param_rib( @@ -3093,6 +3151,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { }); }, ); + self.diag_metadata.in_non_gat_assoc_type = None; } AssocItemKind::Delegation(box delegation) => { debug!("resolve_implementation AssocItemKind::Delegation"); @@ -3258,17 +3317,19 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } self.visit_path(&delegation.path, delegation.id); if let Some(body) = &delegation.body { - // `PatBoundCtx` is not necessary in this context - let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; + self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { + // `PatBoundCtx` is not necessary in this context + let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - let span = delegation.path.segments.last().unwrap().ident.span; - self.fresh_binding( - Ident::new(kw::SelfLower, span), - delegation.id, - PatternSource::FnParam, - &mut bindings, - ); - self.visit_block(body); + let span = delegation.path.segments.last().unwrap().ident.span; + this.fresh_binding( + Ident::new(kw::SelfLower, span), + delegation.id, + PatternSource::FnParam, + &mut bindings, + ); + this.visit_block(body); + }); } } @@ -3287,7 +3348,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { fn resolve_local(&mut self, local: &'ast Local) { debug!("resolving local ({:?})", local); // Resolve the type. - walk_list!(self, visit_ty, &local.ty); + visit_opt!(self, visit_ty, &local.ty); // Resolve the initializer. if let Some((init, els)) = local.kind.init_else_opt() { @@ -3486,8 +3547,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { fn resolve_arm(&mut self, arm: &'ast Arm) { self.with_rib(ValueNS, RibKind::Normal, |this| { this.resolve_pattern_top(&arm.pat, PatternSource::Match); - walk_list!(this, visit_expr, &arm.guard); - walk_list!(this, visit_expr, &arm.body); + visit_opt!(this, visit_expr, &arm.guard); + visit_opt!(this, visit_expr, &arm.body); }); } @@ -4040,9 +4101,12 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } #[inline] - /// If we're actually rustdoc then avoid giving a name resolution error for `cfg()` items. + /// If we're actually rustdoc then avoid giving a name resolution error for `cfg()` items or + // an invalid `use foo::*;` was found, which can cause unbounded ammounts of "item not found" + // errors. We silence them all. fn should_report_errs(&self) -> bool { !(self.r.tcx.sess.opts.actually_rustdoc && self.in_func_body) + && !self.r.glob_error.is_some() } // Resolve in alternative namespaces if resolution in the primary namespace fails. @@ -4822,7 +4886,24 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { late_resolution_visitor.resolve_doc_links(&krate.attrs, MaybeExported::Ok(CRATE_NODE_ID)); visit::walk_crate(&mut late_resolution_visitor, krate); for (id, span) in late_resolution_visitor.diag_metadata.unused_labels.iter() { - self.lint_buffer.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label"); + self.lint_buffer.buffer_lint( + lint::builtin::UNUSED_LABELS, + *id, + *span, + BuiltinLintDiag::UnusedLabel, + ); } } } + +/// Check if definition matches a path +fn def_id_matches_path(tcx: TyCtxt<'_>, mut def_id: DefId, expected_path: &[&str]) -> bool { + let mut path = expected_path.iter().rev(); + while let (Some(parent), Some(next_step)) = (tcx.opt_parent(def_id), path.next()) { + if !tcx.opt_item_name(def_id).map_or(false, |n| n.as_str() == *next_step) { + return false; + } + def_id = parent; + } + return true; +} diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 1958fdf1cbc4..764cc350182a 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -17,6 +17,7 @@ use rustc_ast::{ }; use rustc_ast_pretty::pprust::where_bound_predicate_to_string; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, SuggestionStyle, @@ -31,7 +32,7 @@ use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use rustc_middle::ty; @@ -507,7 +508,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let ast::AngleBracketedArg::Constraint(constraint) = param else { continue; }; - let ast::AssocConstraintKind::Bound { bounds } = &constraint.kind else { + let ast::AssocItemConstraintKind::Bound { bounds } = &constraint.kind else { continue; }; for bound in bounds { @@ -828,7 +829,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { auto-traits; structs and enums can't be bound in that way", ); if bounds.iter().all(|bound| match bound { - ast::GenericBound::Outlives(_) => true, + ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..) => true, ast::GenericBound::Trait(tr, _) => tr.span == base_error.span, }) { let mut sugg = vec![]; @@ -1020,12 +1021,14 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { }, ); let is_assoc_fn = self.self_type_is_available(); + let self_from_macro = "a `self` parameter, but a macro invocation can only \ + access identifiers it receives from parameters"; if let Some((fn_kind, span)) = &self.diag_metadata.current_function { // The current function has a `self` parameter, but we were unable to resolve // a reference to `self`. This can only happen if the `self` identifier we // are resolving came from a different hygiene context. if fn_kind.decl().inputs.get(0).is_some_and(|p| p.is_self()) { - err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters"); + err.span_label(*span, format!("this function has {self_from_macro}")); } else { let doesnt = if is_assoc_fn { let (span, sugg) = fn_kind @@ -1067,14 +1070,18 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } } } else if let Some(item_kind) = self.diag_metadata.current_item { - err.span_label( - item_kind.ident.span, - format!( - "`self` not allowed in {} {}", - item_kind.kind.article(), - item_kind.kind.descr() - ), - ); + if matches!(item_kind.kind, ItemKind::Delegation(..)) { + err.span_label(item_kind.span, format!("delegation supports {self_from_macro}")); + } else { + err.span_label( + item_kind.ident.span, + format!( + "`self` not allowed in {} {}", + item_kind.kind.article(), + item_kind.kind.descr() + ), + ); + } } true } @@ -1102,14 +1109,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { Side::Start => (segment.ident.span.between(range.span), " @ ".into()), Side::End => (range.span.to(segment.ident.span), format!("{} @ ..", segment.ident)), }; - err.subdiagnostic( - self.r.dcx(), - errors::UnexpectedResUseAtOpInSlicePatWithRangeSugg { - span, - ident: segment.ident, - snippet, - }, - ); + err.subdiagnostic(errors::UnexpectedResUseAtOpInSlicePatWithRangeSugg { + span, + ident: segment.ident, + snippet, + }); } enum Side { @@ -1201,13 +1205,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { }); if let Some(param) = param { - err.subdiagnostic( - self.r.dcx(), - errors::UnexpectedResChangeTyToConstParamSugg { - span: param.shrink_to_lo(), - applicability, - }, - ); + err.subdiagnostic(errors::UnexpectedResChangeTyToConstParamSugg { + span: param.shrink_to_lo(), + applicability, + }); } } @@ -2040,8 +2041,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn { called }, ast::AssocItemKind::Type(..) => AssocSuggestion::AssocType, ast::AssocItemKind::Delegation(..) - if self.r.delegation_fn_sigs[&self.r.local_def_id(assoc_item.id)] - .has_self => + if self + .r + .delegation_fn_sigs + .get(&self.r.local_def_id(assoc_item.id)) + .map_or(false, |sig| sig.has_self) => { AssocSuggestion::MethodWithSelf { called } } @@ -2651,15 +2655,15 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let deletion_span = if param.bounds.is_empty() { deletion_span() } else { None }; - self.r.lint_buffer.buffer_lint_with_diagnostic( + self.r.lint_buffer.buffer_lint( lint::builtin::SINGLE_USE_LIFETIMES, param.id, param.ident.span, - format!("lifetime parameter `{}` only used once", param.ident), lint::BuiltinLintDiag::SingleUseLifetime { param_span: param.ident.span, use_span: Some((use_span, elidable)), deletion_span, + ident: param.ident, }, ); } @@ -2669,15 +2673,15 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { // if the lifetime originates from expanded code, we won't be able to remove it #104432 if deletion_span.is_some_and(|sp| !sp.in_derive_expansion()) { - self.r.lint_buffer.buffer_lint_with_diagnostic( + self.r.lint_buffer.buffer_lint( lint::builtin::UNUSED_LIFETIMES, param.id, param.ident.span, - format!("lifetime parameter `{}` never used", param.ident), lint::BuiltinLintDiag::SingleUseLifetime { param_span: param.ident.span, use_span: None, deletion_span, + ident: param.ident, }, ); } @@ -2714,8 +2718,17 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { self.suggest_introducing_lifetime( &mut err, Some(lifetime_ref.ident.name.as_str()), - |err, _, span, message, suggestion| { - err.span_suggestion(span, message, suggestion, Applicability::MaybeIncorrect); + |err, _, span, message, suggestion, span_suggs| { + err.multipart_suggestion_with_style( + message, + std::iter::once((span, suggestion)).chain(span_suggs.clone()).collect(), + Applicability::MaybeIncorrect, + if span_suggs.is_empty() { + SuggestionStyle::ShowCode + } else { + SuggestionStyle::ShowAlways + }, + ); true }, ); @@ -2726,13 +2739,20 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { &self, err: &mut Diag<'_>, name: Option<&str>, - suggest: impl Fn(&mut Diag<'_>, bool, Span, Cow<'static, str>, String) -> bool, + suggest: impl Fn( + &mut Diag<'_>, + bool, + Span, + Cow<'static, str>, + String, + Vec<(Span, String)>, + ) -> bool, ) { let mut suggest_note = true; for rib in self.lifetime_ribs.iter().rev() { let mut should_continue = true; match rib.kind { - LifetimeRibKind::Generics { binder: _, span, kind } => { + LifetimeRibKind::Generics { binder, span, kind } => { // Avoid suggesting placing lifetime parameters on constant items unless the relevant // feature is enabled. Suggest the parent item as a possible location if applicable. if let LifetimeBinderKind::ConstItem = kind @@ -2761,11 +2781,53 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { | LifetimeBinderKind::PolyTrait | LifetimeBinderKind::WhereBound ); + + let mut rm_inner_binders: FxIndexSet = Default::default(); let (span, sugg) = if span.is_empty() { + let mut binder_idents: FxIndexSet = Default::default(); + binder_idents.insert(Ident::from_str(name.unwrap_or("'a"))); + + // We need to special case binders in the following situation: + // Change `T: for<'a> Trait + 'b` to `for<'a, 'b> T: Trait + 'b` + // T: for<'a> Trait + 'b + // ^^^^^^^ remove existing inner binder `for<'a>` + // for<'a, 'b> T: Trait + 'b + // ^^^^^^^^^^^ suggest outer binder `for<'a, 'b>` + if let LifetimeBinderKind::WhereBound = kind + && let Some(ast::WherePredicate::BoundPredicate( + ast::WhereBoundPredicate { bounded_ty, bounds, .. }, + )) = self.diag_metadata.current_where_predicate + && bounded_ty.id == binder + { + for bound in bounds { + if let ast::GenericBound::Trait(poly_trait_ref, _) = bound + && let span = poly_trait_ref + .span + .with_hi(poly_trait_ref.trait_ref.path.span.lo()) + && !span.is_empty() + { + rm_inner_binders.insert(span); + poly_trait_ref.bound_generic_params.iter().for_each(|v| { + binder_idents.insert(v.ident); + }); + } + } + } + + let binders_sugg = binder_idents.into_iter().enumerate().fold( + "".to_string(), + |mut binders, (i, x)| { + if i != 0 { + binders += ", "; + } + binders += x.as_str(); + binders + }, + ); let sugg = format!( "{}<{}>{}", if higher_ranked { "for" } else { "" }, - name.unwrap_or("'a"), + binders_sugg, if higher_ranked { " " } else { "" }, ); (span, sugg) @@ -2780,13 +2842,28 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let sugg = format!("{}, ", name.unwrap_or("'a")); (span, sugg) }; + if higher_ranked { let message = Cow::from(format!( "consider making the {} lifetime-generic with a new `{}` lifetime", kind.descr(), name.unwrap_or("'a"), )); - should_continue = suggest(err, true, span, message, sugg); + should_continue = suggest( + err, + true, + span, + message, + sugg, + if !rm_inner_binders.is_empty() { + rm_inner_binders + .into_iter() + .map(|v| (v, "".to_string())) + .collect::>() + } else { + vec![] + }, + ); err.note_once( "for more information on higher-ranked polymorphism, visit \ https://doc.rust-lang.org/nomicon/hrtb.html", @@ -2794,10 +2871,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } else if let Some(name) = name { let message = Cow::from(format!("consider introducing lifetime `{name}` here")); - should_continue = suggest(err, false, span, message, sugg); + should_continue = suggest(err, false, span, message, sugg, vec![]); } else { let message = Cow::from("consider introducing a named lifetime parameter"); - should_continue = suggest(err, false, span, message, sugg); + should_continue = suggest(err, false, span, message, sugg, vec![]); } } LifetimeRibKind::Item | LifetimeRibKind::ConstParamTy => break, @@ -3033,11 +3110,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { self.suggest_introducing_lifetime( err, None, - |err, higher_ranked, span, message, intro_sugg| { + |err, higher_ranked, span, message, intro_sugg, _| { err.multipart_suggestion_verbose( message, std::iter::once((span, intro_sugg)) - .chain(spans_suggs.iter().cloned()) + .chain(spans_suggs.clone()) .collect(), Applicability::MaybeIncorrect, ); @@ -3127,7 +3204,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { .inputs .iter() .filter_map(|param| match ¶m.ty.kind { - TyKind::ImplTrait(_, bounds, _) => Some(bounds), + TyKind::ImplTrait(_, bounds) => Some(bounds), _ => None, }) .flat_map(|bounds| bounds.into_iter()) @@ -3161,11 +3238,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { self.suggest_introducing_lifetime( err, None, - |err, higher_ranked, span, message, intro_sugg| { + |err, higher_ranked, span, message, intro_sugg, _| { err.multipart_suggestion_verbose( message, std::iter::once((span, intro_sugg)) - .chain(spans_suggs.iter().cloned()) + .chain(spans_suggs.clone()) .collect(), Applicability::MaybeIncorrect, ); @@ -3309,7 +3386,6 @@ fn mk_where_bound_predicate( poly_trait_ref: &ast::PolyTraitRef, ty: &Ty, ) -> Option { - use rustc_span::DUMMY_SP; let modified_segments = { let mut segments = path.segments.clone(); let [preceding @ .., second_last, last] = segments.as_mut_slice() else { @@ -3317,11 +3393,11 @@ fn mk_where_bound_predicate( }; let mut segments = ThinVec::from(preceding); - let added_constraint = ast::AngleBracketedArg::Constraint(ast::AssocConstraint { + let added_constraint = ast::AngleBracketedArg::Constraint(ast::AssocItemConstraint { id: DUMMY_NODE_ID, ident: last.ident, gen_args: None, - kind: ast::AssocConstraintKind::Equality { + kind: ast::AssocItemConstraintKind::Equality { term: ast::Term::Ty(ast::ptr::P(ast::Ty { kind: ast::TyKind::Path(None, poly_trait_ref.trait_ref.path.clone()), id: DUMMY_NODE_ID, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 01bcfec4bdc0..3a831a7f19e0 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -32,7 +32,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{FreezeReadGuard, Lrc}; -use rustc_errors::{Applicability, Diag, ErrCode}; +use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed}; use rustc_expand::base::{DeriveResolution, SyntaxExtension, SyntaxExtensionKind}; use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::def::Namespace::{self, *}; @@ -51,7 +51,7 @@ use rustc_middle::ty::{self, DelegationFnSig, Feed, MainDefinition, RegisteredTo use rustc_middle::ty::{ResolverGlobalCtxt, ResolverOutputs, TyCtxt, TyCtxtFeed}; use rustc_query_system::ich::StableHashingContext; use rustc_session::lint::builtin::PRIVATE_MACRO_USE; -use rustc_session::lint::LintBuffer; +use rustc_session::lint::{BuiltinLintDiag, LintBuffer}; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -1048,6 +1048,7 @@ pub struct Resolver<'a, 'tcx> { /// Maps glob imports to the names of items actually imported. glob_map: FxHashMap>, + glob_error: Option, visibilities_for_hashing: Vec<(LocalDefId, ty::Visibility)>, used_imports: FxHashSet, maybe_unused_trait_imports: FxIndexSet, @@ -1088,7 +1089,7 @@ pub struct Resolver<'a, 'tcx> { single_segment_macro_resolutions: Vec<(Ident, MacroKind, ParentScope<'a>, Option>)>, multi_segment_macro_resolutions: - Vec<(Vec, Span, MacroKind, ParentScope<'a>, Option)>, + Vec<(Vec, Span, MacroKind, ParentScope<'a>, Option, Namespace)>, builtin_attrs: Vec<(Ident, ParentScope<'a>)>, /// `derive(Copy)` marks items they are applied to so they are treated specially later. /// Derive macros cannot modify the item themselves and have to store the markers in the global @@ -1162,6 +1163,15 @@ pub struct Resolver<'a, 'tcx> { doc_link_resolutions: FxHashMap, doc_link_traits_in_scope: FxHashMap>, all_macro_rules: FxHashMap, + + /// Invocation ids of all glob delegations. + glob_delegation_invoc_ids: FxHashSet, + /// Analogue of module `unexpanded_invocations` but in trait impls, excluding glob delegations. + /// Needed because glob delegations wait for all other neighboring macros to expand. + impl_unexpanded_invocations: FxHashMap>, + /// Simplified analogue of module `resolutions` but in trait impls, excluding glob delegations. + /// Needed because glob delegations exclude explicitly defined names. + impl_binding_keys: FxHashMap>, } /// Nothing really interesting here; it just provides memory for the rest of the crate. @@ -1417,6 +1427,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ast_transform_scopes: FxHashMap::default(), glob_map: Default::default(), + glob_error: None, visibilities_for_hashing: Default::default(), used_imports: FxHashSet::default(), maybe_unused_trait_imports: Default::default(), @@ -1502,6 +1513,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { doc_link_traits_in_scope: Default::default(), all_macro_rules: Default::default(), delegation_fn_sigs: Default::default(), + glob_delegation_invoc_ids: Default::default(), + impl_unexpanded_invocations: Default::default(), + impl_binding_keys: Default::default(), }; let root_parent_scope = ParentScope::module(graph_root, &resolver); @@ -1860,8 +1874,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } if let NameBindingKind::Import { import, binding } = used_binding.kind { if let ImportKind::MacroUse { warn_private: true } = import.kind { - let msg = format!("macro `{ident}` is private"); - self.lint_buffer().buffer_lint(PRIVATE_MACRO_USE, import.root_id, ident.span, msg); + self.lint_buffer().buffer_lint( + PRIVATE_MACRO_USE, + import.root_id, + ident.span, + BuiltinLintDiag::MacroIsPrivate(ident), + ); } // Avoid marking `extern crate` items that refer to a name from extern prelude, // but not introduce it, as used if they are accessed from lexical scope. diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index f8d245f94e53..87794d11cea9 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -5,7 +5,7 @@ use crate::errors::CannotDetermineMacroResolution; use crate::errors::{self, AddAsNonDerive, CannotFindIdentInThisScope}; use crate::errors::{MacroExpectedFound, RemoveSurroundingDerive}; use crate::Namespace::*; -use crate::{BuiltinMacroState, Determinacy, MacroData, Used}; +use crate::{BindingKey, BuiltinMacroState, Determinacy, MacroData, Used}; use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet}; use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding}; use rustc_ast::expand::StrippedCfgItem; @@ -198,6 +198,11 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope); parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion); + if let Some(unexpanded_invocations) = + self.impl_unexpanded_invocations.get_mut(&self.invocation_parent(expansion)) + { + unexpanded_invocations.remove(&expansion); + } } fn register_builtin_macro(&mut self, name: Symbol, ext: SyntaxExtensionKind) { @@ -262,15 +267,21 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { } }; - let (path, kind, inner_attr, derives) = match invoc.kind { - InvocationKind::Attr { ref attr, ref derives, .. } => ( - &attr.get_normal_item().path, - MacroKind::Attr, - attr.style == ast::AttrStyle::Inner, - self.arenas.alloc_ast_paths(derives), - ), - InvocationKind::Bang { ref mac, .. } => (&mac.path, MacroKind::Bang, false, &[][..]), - InvocationKind::Derive { ref path, .. } => (path, MacroKind::Derive, false, &[][..]), + let (mut derives, mut inner_attr, mut deleg_impl) = (&[][..], false, None); + let (path, kind) = match invoc.kind { + InvocationKind::Attr { ref attr, derives: ref attr_derives, .. } => { + derives = self.arenas.alloc_ast_paths(attr_derives); + inner_attr = attr.style == ast::AttrStyle::Inner; + (&attr.get_normal_item().path, MacroKind::Attr) + } + InvocationKind::Bang { ref mac, .. } => (&mac.path, MacroKind::Bang), + InvocationKind::Derive { ref path, .. } => (path, MacroKind::Derive), + InvocationKind::GlobDelegation { ref item } => { + let ast::AssocItemKind::DelegationMac(deleg) = &item.kind else { unreachable!() }; + deleg_impl = Some(self.invocation_parent(invoc_id)); + // It is sufficient to consider glob delegation a bang macro for now. + (&deleg.prefix, MacroKind::Bang) + } }; // Derives are not included when `invocations` are collected, so we have to add them here. @@ -286,10 +297,11 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { node_id, force, soft_custom_inner_attributes_gate(path, invoc), + deleg_impl, )?; let span = invoc.span(); - let def_id = res.opt_def_id(); + let def_id = if deleg_impl.is_some() { None } else { res.opt_def_id() }; invoc_id.set_expn_data( ext.expn_data( parent_scope.expansion, @@ -315,7 +327,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { UNUSED_MACROS, node_id, ident.span, - format!("unused macro definition: `{}`", ident.name), + BuiltinLintDiag::UnusedMacroDefinition(ident.name), ); } for (&(def_id, arm_i), &(ident, rule_span)) in self.unused_macro_rules.iter() { @@ -328,7 +340,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { UNUSED_MACRO_RULES, node_id, rule_span, - format!("rule #{} of macro `{}` is never used", arm_i + 1, ident.name), + BuiltinLintDiag::MacroRuleNeverUsed(arm_i, ident.name), ); } } @@ -452,6 +464,45 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { fn registered_tools(&self) -> &RegisteredTools { self.registered_tools } + + fn register_glob_delegation(&mut self, invoc_id: LocalExpnId) { + self.glob_delegation_invoc_ids.insert(invoc_id); + } + + fn glob_delegation_suffixes( + &mut self, + trait_def_id: DefId, + impl_def_id: LocalDefId, + ) -> Result)>, Indeterminate> { + let target_trait = self.expect_module(trait_def_id); + if !target_trait.unexpanded_invocations.borrow().is_empty() { + return Err(Indeterminate); + } + // FIXME: Instead of waiting try generating all trait methods, and pruning + // the shadowed ones a bit later, e.g. when all macro expansion completes. + // Pros: expansion will be stuck less (but only in exotic cases), the implementation may be + // less hacky. + // Cons: More code is generated just to be deleted later, deleting already created `DefId`s + // may be nontrivial. + if let Some(unexpanded_invocations) = self.impl_unexpanded_invocations.get(&impl_def_id) + && !unexpanded_invocations.is_empty() + { + return Err(Indeterminate); + } + + let mut idents = Vec::new(); + target_trait.for_each_child(self, |this, ident, ns, _binding| { + // FIXME: Adjust hygiene for idents from globs, like for glob imports. + if let Some(overriding_keys) = this.impl_binding_keys.get(&impl_def_id) + && overriding_keys.contains(&BindingKey::new(ident.normalize_to_macros_2_0(), ns)) + { + // The name is overridden, do not produce it from the glob delegation. + } else { + idents.push((ident, None)); + } + }); + Ok(idents) + } } impl<'a, 'tcx> Resolver<'a, 'tcx> { @@ -468,15 +519,40 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { node_id: NodeId, force: bool, soft_custom_inner_attributes_gate: bool, + deleg_impl: Option, ) -> Result<(Lrc, Res), Indeterminate> { - let (ext, res) = match self.resolve_macro_path(path, Some(kind), parent_scope, true, force) - { + let (ext, res) = match self.resolve_macro_or_delegation_path( + path, + Some(kind), + parent_scope, + true, + force, + deleg_impl, + ) { Ok((Some(ext), res)) => (ext, res), Ok((None, res)) => (self.dummy_ext(kind), res), Err(Determinacy::Determined) => (self.dummy_ext(kind), Res::Err), Err(Determinacy::Undetermined) => return Err(Indeterminate), }; + // Everything below is irrelevant to glob delegation, take a shortcut. + if deleg_impl.is_some() { + if !matches!(res, Res::Err | Res::Def(DefKind::Trait, _)) { + self.dcx().emit_err(MacroExpectedFound { + span: path.span, + expected: "trait", + article: "a", + found: res.descr(), + macro_path: &pprust::path_to_string(path), + remove_surrounding_derive: None, + add_as_non_derive: None, + }); + return Ok((self.dummy_ext(kind), Res::Err)); + } + + return Ok((ext, res)); + } + // Report errors for the resolved macro. for segment in &path.segments { if let Some(args) = &segment.args { @@ -552,14 +628,25 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // We are trying to avoid reporting this error if other related errors were reported. if res != Res::Err && inner_attr && !self.tcx.features().custom_inner_attributes { - let msg = match res { - Res::Def(..) => "inner macro attributes are unstable", - Res::NonMacroAttr(..) => "custom inner attributes are unstable", + let is_macro = match res { + Res::Def(..) => true, + Res::NonMacroAttr(..) => false, _ => unreachable!(), }; if soft_custom_inner_attributes_gate { - self.tcx.sess.psess.buffer_lint(SOFT_UNSTABLE, path.span, node_id, msg); + self.tcx.sess.psess.buffer_lint( + SOFT_UNSTABLE, + path.span, + node_id, + BuiltinLintDiag::InnerAttributeUnstable { is_macro }, + ); } else { + // FIXME: deduplicate with rustc_lint (`BuiltinLintDiag::InnerAttributeUnstable`) + let msg = if is_macro { + "inner macro attributes are unstable" + } else { + "custom inner attributes are unstable" + }; feature_err(&self.tcx.sess, sym::custom_inner_attributes, path.span, msg).emit(); } } @@ -567,22 +654,20 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) && let [namespace, attribute, ..] = &*path.segments && namespace.ident.name == sym::diagnostic - && attribute.ident.name != sym::on_unimplemented + && !(attribute.ident.name == sym::on_unimplemented + || (attribute.ident.name == sym::do_not_recommend + && self.tcx.features().do_not_recommend)) { let distance = edit_distance(attribute.ident.name.as_str(), sym::on_unimplemented.as_str(), 5); - let help = if distance.is_some() { - BuiltinLintDiag::MaybeTypo { span: attribute.span(), name: sym::on_unimplemented } - } else { - BuiltinLintDiag::Normal - }; - self.tcx.sess.psess.buffer_lint_with_diagnostic( + let typo_name = distance.map(|_| sym::on_unimplemented); + + self.tcx.sess.psess.buffer_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, attribute.span(), node_id, - "unknown diagnostic attribute", - help, + BuiltinLintDiag::UnknownDiagnosticAttribute { span: attribute.span(), typo_name }, ); } @@ -596,12 +681,25 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, trace: bool, force: bool, + ) -> Result<(Option>, Res), Determinacy> { + self.resolve_macro_or_delegation_path(path, kind, parent_scope, trace, force, None) + } + + fn resolve_macro_or_delegation_path( + &mut self, + path: &ast::Path, + kind: Option, + parent_scope: &ParentScope<'a>, + trace: bool, + force: bool, + deleg_impl: Option, ) -> Result<(Option>, Res), Determinacy> { let path_span = path.span; let mut path = Segment::from_path(path); // Possibly apply the macro helper hack - if kind == Some(MacroKind::Bang) + if deleg_impl.is_none() + && kind == Some(MacroKind::Bang) && path.len() == 1 && path[0].ident.span.ctxt().outer_expn_data().local_inner_macros { @@ -609,13 +707,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { path.insert(0, Segment::from_ident(root)); } - let res = if path.len() > 1 { - let res = match self.maybe_resolve_path(&path, Some(MacroNS), parent_scope) { + let res = if deleg_impl.is_some() || path.len() > 1 { + let ns = if deleg_impl.is_some() { TypeNS } else { MacroNS }; + let res = match self.maybe_resolve_path(&path, Some(ns), parent_scope) { PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => Ok(res), PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined), PathResult::NonModule(..) | PathResult::Indeterminate | PathResult::Failed { .. } => Err(Determinacy::Determined), + PathResult::Module(ModuleOrUniformRoot::Module(module)) => { + Ok(module.res().unwrap()) + } PathResult::Module(..) => unreachable!(), }; @@ -627,6 +729,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { kind, *parent_scope, res.ok(), + ns, )); } @@ -661,7 +764,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { res }; - res.map(|res| (self.get_macro(res).map(|macro_data| macro_data.ext.clone()), res)) + let res = res?; + let ext = match deleg_impl { + Some(impl_def_id) => match res { + def::Res::Def(DefKind::Trait, def_id) => { + let edition = self.tcx.sess.edition(); + Some(Lrc::new(SyntaxExtension::glob_delegation(def_id, impl_def_id, edition))) + } + _ => None, + }, + None => self.get_macro(res).map(|macro_data| macro_data.ext.clone()), + }; + Ok((ext, res)) } pub(crate) fn finalize_macro_resolutions(&mut self, krate: &Crate) { @@ -697,14 +811,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }; let macro_resolutions = mem::take(&mut self.multi_segment_macro_resolutions); - for (mut path, path_span, kind, parent_scope, initial_res) in macro_resolutions { + for (mut path, path_span, kind, parent_scope, initial_res, ns) in macro_resolutions { // FIXME: Path resolution will ICE if segment IDs present. for seg in &mut path { seg.id = None; } match self.resolve_path( &path, - Some(MacroNS), + Some(ns), &parent_scope, Some(Finalize::new(ast::CRATE_NODE_ID, path_span)), None, @@ -712,6 +826,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => { check_consistency(self, &path, path_span, kind, initial_res, res) } + // This may be a trait for glob delegation expansions. + PathResult::Module(ModuleOrUniformRoot::Module(module)) => check_consistency( + self, + &path, + path_span, + kind, + initial_res, + module.res().unwrap(), + ), path_res @ (PathResult::NonModule(..) | PathResult::Failed { .. }) => { let mut suggestion = None; let (span, label, module) = @@ -782,11 +905,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { .invocation_parents .get(&parent_scope.expansion) .map_or(ast::CRATE_NODE_ID, |id| self.def_id_to_node_id[id.0]); - self.lint_buffer.buffer_lint_with_diagnostic( + self.lint_buffer.buffer_lint( LEGACY_DERIVE_HELPERS, node_id, ident.span, - "derive helper attribute is used before it is introduced", BuiltinLintDiag::LegacyDeriveHelpers(binding.span), ); } @@ -836,8 +958,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let allowed_by_implication = implied_by.is_some_and(|feature| is_allowed(feature)); if !is_allowed(feature) && !allowed_by_implication { let lint_buffer = &mut self.lint_buffer; - let soft_handler = - |lint, span, msg: String| lint_buffer.buffer_lint(lint, node_id, span, msg); + let soft_handler = |lint, span, msg: String| { + lint_buffer.buffer_lint( + lint, + node_id, + span, + BuiltinLintDiag::UnstableFeature( + // FIXME make this translatable + msg.into(), + ), + ) + }; stability::report_unstable( self.tcx.sess, feature, @@ -853,14 +984,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } if let Some(depr) = &ext.deprecation { let path = pprust::path_to_string(path); - let (message, lint) = stability::deprecation_message_and_lint(depr, "macro", &path); - stability::early_report_deprecation( + stability::early_report_macro_deprecation( &mut self.lint_buffer, - message, - depr.suggestion, - lint, + depr, span, node_id, + path, ); } } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index d67132d2dd4f..0be8b5d57187 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -68,6 +68,8 @@ fn compress<'tcx>( fn encode_args<'tcx>( tcx: TyCtxt<'tcx>, args: GenericArgsRef<'tcx>, + for_def: DefId, + has_erased_self: bool, dict: &mut FxHashMap, usize>, options: EncodeTyOptions, ) -> String { @@ -76,7 +78,8 @@ fn encode_args<'tcx>( let args: Vec> = args.iter().collect(); if !args.is_empty() { s.push('I'); - for arg in args { + let def_generics = tcx.generics_of(for_def); + for (n, arg) in args.iter().enumerate() { match arg.unpack() { GenericArgKind::Lifetime(region) => { s.push_str(&encode_region(region, dict)); @@ -85,7 +88,10 @@ fn encode_args<'tcx>( s.push_str(&encode_ty(tcx, ty, dict, options)); } GenericArgKind::Const(c) => { - s.push_str(&encode_const(tcx, c, dict, options)); + let n = n + (has_erased_self as usize); + let ct_ty = + tcx.type_of(def_generics.param_at(n, tcx).def_id).instantiate_identity(); + s.push_str(&encode_const(tcx, c, ct_ty, dict, options)); } } } @@ -99,6 +105,7 @@ fn encode_args<'tcx>( fn encode_const<'tcx>( tcx: TyCtxt<'tcx>, c: Const<'tcx>, + ct_ty: Ty<'tcx>, dict: &mut FxHashMap, usize>, options: EncodeTyOptions, ) -> String { @@ -111,20 +118,20 @@ fn encode_const<'tcx>( // LE as literal argument // Element type - s.push_str(&encode_ty(tcx, c.ty(), dict, options)); + s.push_str(&encode_ty(tcx, ct_ty, dict, options)); } // Literal arguments - ty::ConstKind::Value(..) => { + ty::ConstKind::Value(ct_ty, ..) => { // L[n]E as literal argument // Element type - s.push_str(&encode_ty(tcx, c.ty(), dict, options)); + s.push_str(&encode_ty(tcx, ct_ty, dict, options)); // The only allowed types of const values are bool, u8, u16, u32, // u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The // bool value false is encoded as 0 and true as 1. - match c.ty().kind() { + match ct_ty.kind() { ty::Int(ity) => { let bits = c.eval_bits(tcx, ty::ParamEnv::reveal_all()); let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; @@ -142,7 +149,7 @@ fn encode_const<'tcx>( let _ = write!(s, "{val}"); } _ => { - bug!("encode_const: unexpected type `{:?}`", c.ty()); + bug!("encode_const: unexpected type `{:?}`", ct_ty); } } } @@ -231,15 +238,21 @@ fn encode_predicate<'tcx>( ty::ExistentialPredicate::Trait(trait_ref) => { let name = encode_ty_name(tcx, trait_ref.def_id); let _ = write!(s, "u{}{}", name.len(), &name); - s.push_str(&encode_args(tcx, trait_ref.args, dict, options)); + s.push_str(&encode_args(tcx, trait_ref.args, trait_ref.def_id, true, dict, options)); } ty::ExistentialPredicate::Projection(projection) => { let name = encode_ty_name(tcx, projection.def_id); let _ = write!(s, "u{}{}", name.len(), &name); - s.push_str(&encode_args(tcx, projection.args, dict, options)); + s.push_str(&encode_args(tcx, projection.args, projection.def_id, true, dict, options)); match projection.term.unpack() { TermKind::Ty(ty) => s.push_str(&encode_ty(tcx, ty, dict, options)), - TermKind::Const(c) => s.push_str(&encode_const(tcx, c, dict, options)), + TermKind::Const(c) => s.push_str(&encode_const( + tcx, + c, + tcx.type_of(projection.def_id).instantiate(tcx, projection.args), + dict, + options, + )), } } ty::ExistentialPredicate::AutoTrait(def_id) => { @@ -485,7 +498,7 @@ pub fn encode_ty<'tcx>( // , as vendor extended type. let name = encode_ty_name(tcx, def_id); let _ = write!(s, "u{}{}", name.len(), &name); - s.push_str(&encode_args(tcx, args, dict, options)); + s.push_str(&encode_args(tcx, args, def_id, false, dict, options)); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); } typeid.push_str(&s); @@ -529,7 +542,7 @@ pub fn encode_ty<'tcx>( let mut s = String::new(); let name = encode_ty_name(tcx, *def_id); let _ = write!(s, "u{}{}", name.len(), &name); - s.push_str(&encode_args(tcx, args, dict, options)); + s.push_str(&encode_args(tcx, args, *def_id, false, dict, options)); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); typeid.push_str(&s); } @@ -541,7 +554,7 @@ pub fn encode_ty<'tcx>( let name = encode_ty_name(tcx, *def_id); let _ = write!(s, "u{}{}", name.len(), &name); let parent_args = tcx.mk_args(args.as_coroutine_closure().parent_args()); - s.push_str(&encode_args(tcx, parent_args, dict, options)); + s.push_str(&encode_args(tcx, parent_args, *def_id, false, dict, options)); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); typeid.push_str(&s); } @@ -556,6 +569,8 @@ pub fn encode_ty<'tcx>( s.push_str(&encode_args( tcx, tcx.mk_args(args.as_coroutine().parent_args()), + *def_id, + false, dict, options, )); diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 21433cfdb613..742ec4c377cc 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -269,7 +269,7 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc /// if a function is member of the group derived from this type id. Therefore, in the first call to /// typeid_for_fnabi (when type ids are attached to functions and methods), it can only include at /// most as much information that would be available in the second call (i.e., during code -/// generation at call sites); otherwise, the type ids would not not match. +/// generation at call sites); otherwise, the type ids would not match. /// /// For this, it: /// @@ -288,9 +288,10 @@ pub fn transform_instance<'tcx>( mut instance: Instance<'tcx>, options: TransformTyOptions, ) -> Instance<'tcx> { - if (matches!(instance.def, ty::InstanceDef::Virtual(..)) - && Some(instance.def_id()) == tcx.lang_items().drop_in_place_fn()) - || matches!(instance.def, ty::InstanceDef::DropGlue(..)) + // FIXME: account for async-drop-glue + if (matches!(instance.def, ty::InstanceKind::Virtual(..)) + && tcx.is_lang_item(instance.def_id(), LangItem::DropInPlace)) + || matches!(instance.def, ty::InstanceKind::DropGlue(..)) { // Adjust the type ids of DropGlues // @@ -316,7 +317,7 @@ pub fn transform_instance<'tcx>( let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn); instance.args = tcx.mk_args_trait(self_ty, List::empty()); - } else if let ty::InstanceDef::Virtual(def_id, _) = instance.def { + } else if let ty::InstanceKind::Virtual(def_id, _) = instance.def { // Transform self into a trait object of the trait that defines the method for virtual // functions to match the type erasure done below. let upcast_ty = match tcx.trait_of_item(def_id) { @@ -343,7 +344,7 @@ pub fn transform_instance<'tcx>( tcx.types.unit }; instance.args = tcx.mk_args_trait(self_ty, instance.args.into_iter().skip(1)); - } else if let ty::InstanceDef::VTableShim(def_id) = instance.def + } else if let ty::InstanceKind::VTableShim(def_id) = instance.def && let Some(trait_id) = tcx.trait_of_item(def_id) { // Adjust the type ids of VTableShims to the type id expected in the call sites for the @@ -367,7 +368,7 @@ pub fn transform_instance<'tcx>( let trait_method = tcx.associated_item(method_id); let trait_id = trait_ref.skip_binder().def_id; if traits::is_vtable_safe_method(tcx, trait_id, trait_method) - && tcx.object_safety_violations(trait_id).is_empty() + && tcx.is_object_safe(trait_id) { // Trait methods will have a Self polymorphic parameter, where the concreteized // implementatation will not. We need to walk back to the more general trait method @@ -387,7 +388,7 @@ pub fn transform_instance<'tcx>( // If we ever *do* start encoding the vtable index, we will need to generate an alias set // based on which vtables we are putting this method into, as there will be more than one // index value when supertraits are involved. - instance.def = ty::InstanceDef::Virtual(method_id, 0); + instance.def = ty::InstanceKind::Virtual(method_id, 0); let abstract_trait_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args); @@ -442,7 +443,7 @@ pub fn transform_instance<'tcx>( .expect("No call-family function on closure-like Fn trait?") .def_id; - instance.def = ty::InstanceDef::Virtual(call, 0); + instance.def = ty::InstanceKind::Virtual(call, 0); instance.args = abstract_args; } } diff --git a/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs b/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs index 436c398e39b0..651ba6124696 100644 --- a/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs +++ b/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs @@ -3,7 +3,7 @@ //! //! For more information about LLVM KCFI and cross-language LLVM KCFI support for the Rust compiler, //! see the tracking issue #123479. -use rustc_middle::ty::{Instance, InstanceDef, ReifyReason, Ty, TyCtxt}; +use rustc_middle::ty::{Instance, InstanceKind, ReifyReason, Ty, TyCtxt}; use rustc_target::abi::call::FnAbi; use std::hash::Hasher; use twox_hash::XxHash64; @@ -44,7 +44,7 @@ pub fn typeid_for_instance<'tcx>( // // This was implemented for KCFI support in #123106 and #123052 (which introduced the // ReifyReason). The tracking issue for KCFI support for Rust is #123479. - if matches!(instance.def, InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr))) { + if matches!(instance.def, InstanceKind::ReifyShim(_, Some(ReifyReason::FnPtr))) { options.insert(TypeIdOptions::USE_CONCRETE_SELF); } // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the diff --git a/compiler/rustc_sanitizers/src/lib.rs b/compiler/rustc_sanitizers/src/lib.rs index 1f73e255490b..e4792563e71e 100644 --- a/compiler/rustc_sanitizers/src/lib.rs +++ b/compiler/rustc_sanitizers/src/lib.rs @@ -1,7 +1,11 @@ -#![feature(let_chains)] //! Sanitizers support for the Rust compiler. //! //! This crate contains the source code for providing support for the sanitizers to the Rust //! compiler. + +// tidy-alphabetical-start +#![feature(let_chains)] +// tidy-alphabetical-end + pub mod cfi; pub mod kcfi; diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index 532b749f9136..f0e1630c6500 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -1,20 +1,22 @@ //! Support code for encoding and decoding types. +// tidy-alphabetical-start +#![allow(internal_features)] +#![allow(rustc::internal)] +#![cfg_attr(test, feature(test))] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", html_playground_url = "https://play.rust-lang.org/", test(attr(allow(unused_variables), deny(warnings))) )] #![doc(rust_logo)] -#![allow(internal_features)] -#![feature(rustdoc_internals)] #![feature(const_option)] #![feature(core_intrinsics)] #![feature(min_specialization)] #![feature(never_type)] #![feature(ptr_sub_ptr)] -#![cfg_attr(test, feature(test))] -#![allow(rustc::internal)] +#![feature(rustdoc_internals)] +// tidy-alphabetical-end pub use self::serialize::{Decodable, Decoder, Encodable, Encoder}; diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index eec83c02d353..1dcb69920d73 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -17,6 +17,8 @@ use crate::int_overflow::DebugStrictAdd; pub type FileEncodeResult = Result; +pub const MAGIC_END_BYTES: &[u8] = b"rust-end-file"; + /// The size of the buffer in `FileEncoder`. const BUF_SIZE: usize = 8192; @@ -181,6 +183,7 @@ impl FileEncoder { } pub fn finish(&mut self) -> FileEncodeResult { + self.write_all(MAGIC_END_BYTES); self.flush(); #[cfg(debug_assertions)] { @@ -261,15 +264,18 @@ pub struct MemDecoder<'a> { impl<'a> MemDecoder<'a> { #[inline] - pub fn new(data: &'a [u8], position: usize) -> MemDecoder<'a> { + pub fn new(data: &'a [u8], position: usize) -> Result, ()> { + let data = data.strip_suffix(MAGIC_END_BYTES).ok_or(())?; let Range { start, end } = data.as_ptr_range(); - MemDecoder { start, current: data[position..].as_ptr(), end, _marker: PhantomData } + Ok(MemDecoder { start, current: data[position..].as_ptr(), end, _marker: PhantomData }) } #[inline] - pub fn data(&self) -> &'a [u8] { - // SAFETY: This recovers the original slice, only using members we never modify. - unsafe { std::slice::from_raw_parts(self.start, self.len()) } + pub fn split_at(&self, position: usize) -> MemDecoder<'a> { + assert!(position <= self.len()); + // SAFETY: We checked above that this offset is within the original slice + let current = unsafe { self.start.add(position) }; + MemDecoder { start: self.start, current, end: self.end, _marker: PhantomData } } #[inline] diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs index 412f7eced433..c84bb26735c5 100644 --- a/compiler/rustc_serialize/src/serialize.rs +++ b/compiler/rustc_serialize/src/serialize.rs @@ -252,7 +252,7 @@ impl Encodable for () { } impl Decodable for () { - fn decode(_: &mut D) -> () {} + fn decode(_: &mut D) {} } impl Encodable for PhantomData { diff --git a/compiler/rustc_serialize/tests/leb128.rs b/compiler/rustc_serialize/tests/leb128.rs index dc9b32a968b5..fafe4b91a95d 100644 --- a/compiler/rustc_serialize/tests/leb128.rs +++ b/compiler/rustc_serialize/tests/leb128.rs @@ -1,4 +1,6 @@ use rustc_serialize::leb128::*; +use rustc_serialize::opaque::MemDecoder; +use rustc_serialize::opaque::MAGIC_END_BYTES; use rustc_serialize::Decoder; macro_rules! impl_test_unsigned_leb128 { @@ -25,13 +27,15 @@ macro_rules! impl_test_unsigned_leb128 { let n = $write_fn_name(&mut buf, x); stream.extend(&buf[..n]); } + let stream_end = stream.len(); + stream.extend(MAGIC_END_BYTES); - let mut decoder = rustc_serialize::opaque::MemDecoder::new(&stream, 0); + let mut decoder = MemDecoder::new(&stream, 0).unwrap(); for &expected in &values { let actual = $read_fn_name(&mut decoder); assert_eq!(expected, actual); } - assert_eq!(stream.len(), decoder.position()); + assert_eq!(stream_end, decoder.position()); } }; } @@ -72,13 +76,15 @@ macro_rules! impl_test_signed_leb128 { let n = $write_fn_name(&mut buf, x); stream.extend(&buf[..n]); } + let stream_end = stream.len(); + stream.extend(MAGIC_END_BYTES); - let mut decoder = rustc_serialize::opaque::MemDecoder::new(&stream, 0); + let mut decoder = MemDecoder::new(&stream, 0).unwrap(); for &expected in &values { let actual = $read_fn_name(&mut decoder); assert_eq!(expected, actual); } - assert_eq!(stream.len(), decoder.position()); + assert_eq!(stream_end, decoder.position()); } }; } diff --git a/compiler/rustc_serialize/tests/opaque.rs b/compiler/rustc_serialize/tests/opaque.rs index 45ff85f38d2b..833151d82be3 100644 --- a/compiler/rustc_serialize/tests/opaque.rs +++ b/compiler/rustc_serialize/tests/opaque.rs @@ -42,7 +42,7 @@ fn check_round_trip< encoder.finish().unwrap(); let data = fs::read(&tmpfile).unwrap(); - let mut decoder = MemDecoder::new(&data[..], 0); + let mut decoder = MemDecoder::new(&data[..], 0).unwrap(); for value in values { let decoded = Decodable::decode(&mut decoder); assert_eq!(value, decoded); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 7dd9fdf60f93..24143808ef49 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -149,7 +149,14 @@ pub enum InstrumentCoverage { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] pub struct CoverageOptions { pub level: CoverageLevel, - // Other boolean or enum-valued options might be added here. + + /// `-Z coverage-options=no-mir-spans`: Don't extract block coverage spans + /// from MIR statements/terminators, making it easier to inspect/debug + /// branch and MC/DC coverage mappings. + /// + /// For internal debugging only. If other code changes would make it hard + /// to keep supporting this flag, remove it. + pub no_mir_spans: bool, } /// Controls whether branch coverage or MC/DC coverage is enabled. @@ -159,7 +166,23 @@ pub enum CoverageLevel { Block, /// Also instrument branch points (includes block coverage). Branch, - /// Instrument for MC/DC. Mostly a superset of branch coverage, but might + /// Same as branch coverage, but also adds branch instrumentation for + /// certain boolean expressions that are not directly used for branching. + /// + /// For example, in the following code, `b` does not directly participate + /// in a branch, but condition coverage will instrument it as its own + /// artificial branch: + /// ``` + /// # let (a, b) = (false, true); + /// let x = a && b; + /// // ^ last operand + /// ``` + /// + /// This level is mainly intended to be a stepping-stone towards full MC/DC + /// instrumentation, so it might be removed in the future when MC/DC is + /// sufficiently complete, or if it is making MC/DC changes difficult. + Condition, + /// Instrument for MC/DC. Mostly a superset of condition coverage, but might /// differ in some corner cases. Mcdc, } @@ -465,6 +488,7 @@ impl FromStr for SplitDwarfKind { #[derive(Encodable, Decodable)] pub enum OutputType { Bitcode, + ThinLinkBitcode, Assembly, LlvmAssembly, Mir, @@ -492,6 +516,7 @@ impl OutputType { match *self { OutputType::Exe | OutputType::DepInfo | OutputType::Metadata => true, OutputType::Bitcode + | OutputType::ThinLinkBitcode | OutputType::Assembly | OutputType::LlvmAssembly | OutputType::Mir @@ -502,6 +527,7 @@ impl OutputType { pub fn shorthand(&self) -> &'static str { match *self { OutputType::Bitcode => "llvm-bc", + OutputType::ThinLinkBitcode => "thin-link-bitcode", OutputType::Assembly => "asm", OutputType::LlvmAssembly => "llvm-ir", OutputType::Mir => "mir", @@ -518,6 +544,7 @@ impl OutputType { "llvm-ir" => OutputType::LlvmAssembly, "mir" => OutputType::Mir, "llvm-bc" => OutputType::Bitcode, + "thin-link-bitcode" => OutputType::ThinLinkBitcode, "obj" => OutputType::Object, "metadata" => OutputType::Metadata, "link" => OutputType::Exe, @@ -528,8 +555,9 @@ impl OutputType { fn shorthands_display() -> String { format!( - "`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`", + "`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`", OutputType::Bitcode.shorthand(), + OutputType::ThinLinkBitcode.shorthand(), OutputType::Assembly.shorthand(), OutputType::LlvmAssembly.shorthand(), OutputType::Mir.shorthand(), @@ -543,6 +571,7 @@ impl OutputType { pub fn extension(&self) -> &'static str { match *self { OutputType::Bitcode => "bc", + OutputType::ThinLinkBitcode => "indexing.o", OutputType::Assembly => "s", OutputType::LlvmAssembly => "ll", OutputType::Mir => "mir", @@ -559,9 +588,11 @@ impl OutputType { | OutputType::LlvmAssembly | OutputType::Mir | OutputType::DepInfo => true, - OutputType::Bitcode | OutputType::Object | OutputType::Metadata | OutputType::Exe => { - false - } + OutputType::Bitcode + | OutputType::ThinLinkBitcode + | OutputType::Object + | OutputType::Metadata + | OutputType::Exe => false, } } } @@ -644,6 +675,7 @@ impl OutputTypes { pub fn should_codegen(&self) -> bool { self.0.keys().any(|k| match *k { OutputType::Bitcode + | OutputType::ThinLinkBitcode | OutputType::Assembly | OutputType::LlvmAssembly | OutputType::Mir @@ -657,6 +689,7 @@ impl OutputTypes { pub fn should_link(&self) -> bool { self.0.keys().any(|k| match *k { OutputType::Bitcode + | OutputType::ThinLinkBitcode | OutputType::Assembly | OutputType::LlvmAssembly | OutputType::Mir @@ -763,6 +796,7 @@ pub enum PrintKind { TargetLibdir, CrateName, Cfg, + CheckCfg, CallingConventions, TargetList, TargetCPUs, @@ -786,16 +820,6 @@ pub struct NextSolverConfig { /// Whether the new trait solver should be enabled everywhere. /// This is only `true` if `coherence` is also enabled. pub globally: bool, - /// Whether to dump proof trees after computing a proof tree. - pub dump_tree: DumpSolverProofTree, -} - -#[derive(Default, Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum DumpSolverProofTree { - Always, - OnError, - #[default] - Never, } #[derive(Clone)] @@ -1287,6 +1311,20 @@ pub fn build_target_config(early_dcx: &EarlyDiagCtxt, opts: &Options, sysroot: & for warning in warnings.warning_messages() { early_dcx.early_warn(warning) } + + // The `wasm32-wasi` target is being renamed to `wasm32-wasip1` as + // part of rust-lang/compiler-team#607 and + // rust-lang/compiler-team#695. Warn unconditionally on usage to + // raise awareness of the renaming. This code will be deleted in + // October 2024. + if opts.target_triple.triple() == "wasm32-wasi" { + early_dcx.early_warn( + "the `wasm32-wasi` target is being renamed to \ + `wasm32-wasip1` and the `wasm32-wasi` target will be \ + removed from nightly in October 2024 and removed from \ + stable Rust in January 2025", + ) + } if !matches!(target.pointer_width, 16 | 32 | 64) { early_dcx.early_fatal(format!( "target specification was invalid: unrecognized target-pointer-width {}", @@ -1443,7 +1481,7 @@ pub fn rustc_short_optgroups() -> Vec { "", "print", "Compiler information to print on stdout", - "[crate-name|file-names|sysroot|target-libdir|cfg|calling-conventions|\ + "[crate-name|file-names|sysroot|target-libdir|cfg|check-cfg|calling-conventions|\ target-list|target-cpus|target-features|relocation-models|code-models|\ tls-models|target-spec-json|all-target-specs-json|native-static-libs|\ stack-protector-strategies|link-args|deployment-target]", @@ -1769,6 +1807,12 @@ fn parse_output_types( display = OutputType::shorthands_display(), )) }); + if output_type == OutputType::ThinLinkBitcode && !unstable_opts.unstable_options { + early_dcx.early_fatal(format!( + "{} requested but -Zunstable-options not specified", + OutputType::ThinLinkBitcode.shorthand() + )); + } output_types.insert(output_type, path); } } @@ -1853,6 +1897,7 @@ fn collect_print_requests( ("all-target-specs-json", PrintKind::AllTargetSpecs), ("calling-conventions", PrintKind::CallingConventions), ("cfg", PrintKind::Cfg), + ("check-cfg", PrintKind::CheckCfg), ("code-models", PrintKind::CodeModels), ("crate-name", PrintKind::CrateName), ("deployment-target", PrintKind::DeploymentTarget), @@ -1902,6 +1947,16 @@ fn collect_print_requests( ); } } + Some((_, PrintKind::CheckCfg)) => { + if unstable_opts.unstable_options { + PrintKind::CheckCfg + } else { + early_dcx.early_fatal( + "the `-Z unstable-options` flag must also be passed to \ + enable the check-cfg print option", + ); + } + } Some(&(_, print_kind)) => print_kind, None => { let prints = diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index dce56382a53a..4cbc1b570225 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -3,8 +3,8 @@ use std::num::NonZero; use rustc_ast::token; use rustc_ast::util::literal::LitError; use rustc_errors::{ - codes::*, Diag, DiagCtxt, DiagMessage, Diagnostic, EmissionGuarantee, ErrorGuaranteed, Level, - MultiSpan, + codes::*, Diag, DiagCtxtHandle, DiagMessage, Diagnostic, EmissionGuarantee, ErrorGuaranteed, + Level, MultiSpan, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; @@ -19,7 +19,7 @@ pub(crate) struct FeatureGateError { impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for FeatureGateError { #[track_caller] - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { Diag::new(dcx, level, self.explain).with_span(self.span).with_code(E0658) } } @@ -401,7 +401,7 @@ pub fn report_lit_error( valid.then(|| format!("0{}{}", base_char.to_ascii_lowercase(), &suffix[1..])) } - let dcx = &psess.dcx; + let dcx = psess.dcx(); match err { LitError::InvalidSuffix(suffix) => { dcx.emit_err(InvalidLiteralSuffix { span, kind: lit.kind.descr(), suffix }) diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index f4e2436efb9f..6f63776bedcc 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -12,7 +12,7 @@ use tracing::debug; pub struct FileSearch<'a> { sysroot: &'a Path, triple: &'a str, - search_paths: &'a [SearchPath], + cli_search_paths: &'a [SearchPath], tlib_path: &'a SearchPath, kind: PathKind, } @@ -20,7 +20,7 @@ pub struct FileSearch<'a> { impl<'a> FileSearch<'a> { pub fn search_paths(&self) -> impl Iterator { let kind = self.kind; - self.search_paths + self.cli_search_paths .iter() .filter(move |sp| sp.kind.matches(kind)) .chain(std::iter::once(self.tlib_path)) @@ -37,18 +37,26 @@ impl<'a> FileSearch<'a> { pub fn new( sysroot: &'a Path, triple: &'a str, - search_paths: &'a [SearchPath], + cli_search_paths: &'a [SearchPath], tlib_path: &'a SearchPath, kind: PathKind, ) -> FileSearch<'a> { debug!("using sysroot = {}, triple = {}", sysroot.display(), triple); - FileSearch { sysroot, triple, search_paths, tlib_path, kind } + FileSearch { sysroot, triple, cli_search_paths, tlib_path, kind } } } pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf { - let rustlib_path = rustc_target::target_rustlib_path(sysroot, target_triple); - PathBuf::from_iter([sysroot, Path::new(&rustlib_path), Path::new("lib")]) + let rustlib_path = rustc_target::relative_target_rustlib_path(sysroot, target_triple); + sysroot.join(rustlib_path).join("lib") +} + +/// Returns a path to the target's `bin` folder within its `rustlib` path in the sysroot. This is +/// where binaries are usually installed, e.g. the self-contained linkers, lld-wrappers, LLVM tools, +/// etc. +pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf { + let rustlib_path = rustc_target::relative_target_rustlib_path(sysroot, target_triple); + sysroot.join(rustlib_path).join("bin") } #[cfg(unix)] @@ -267,7 +275,7 @@ pub fn get_or_default_sysroot() -> Result { p.pop(); p.pop(); // Look for the target rustlib directory in the suspected sysroot. - let mut rustlib_path = rustc_target::target_rustlib_path(&p, "dummy"); + let mut rustlib_path = rustc_target::relative_target_rustlib_path(&p, "dummy"); rustlib_path.pop(); // pop off the dummy target. rustlib_path.exists().then_some(p) } diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index ce866906e1e6..d93b3eac0804 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -1,10 +1,11 @@ +// tidy-alphabetical-start +#![allow(internal_features)] +#![feature(iter_intersperse)] #![feature(let_chains)] -#![feature(lazy_cell)] +#![feature(map_many_mut)] #![feature(option_get_or_insert_default)] #![feature(rustc_attrs)] -#![feature(map_many_mut)] -#![feature(iter_intersperse)] -#![allow(internal_features)] +// tidy-alphabetical-end pub mod errors; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 65660286dd73..145af50117cb 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -395,11 +395,13 @@ mod desc { pub const parse_optimization_fuel: &str = "crate=integer"; pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`"; pub const parse_instrument_coverage: &str = parse_bool; - pub const parse_coverage_options: &str = "`block` | `branch` | `mcdc`"; + pub const parse_coverage_options: &str = + "`block` | `branch` | `condition` | `mcdc` | `no-mir-spans`"; pub const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`"; pub const parse_unpretty: &str = "`string` or `string=string`"; pub const parse_treat_err_as_bug: &str = "either no value or a non-negative number"; - pub const parse_next_solver_config: &str = "a comma separated list of solver configurations: `globally` (default), `coherence`, `dump-tree`, `dump-tree-on-error"; + pub const parse_next_solver_config: &str = + "a comma separated list of solver configurations: `globally` (default), and `coherence`"; pub const parse_lto: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, `fat`, or omitted"; pub const parse_linker_plugin_lto: &str = @@ -960,7 +962,9 @@ mod parse { match option { "block" => slot.level = CoverageLevel::Block, "branch" => slot.level = CoverageLevel::Branch, + "condition" => slot.level = CoverageLevel::Condition, "mcdc" => slot.level = CoverageLevel::Mcdc, + "no-mir-spans" => slot.no_mir_spans = true, _ => return false, } } @@ -1058,7 +1062,6 @@ mod parse { if let Some(config) = v { let mut coherence = false; let mut globally = true; - let mut dump_tree = None; for c in config.split(',') { match c { "globally" => globally = true, @@ -1066,31 +1069,13 @@ mod parse { globally = false; coherence = true; } - "dump-tree" => { - if dump_tree.replace(DumpSolverProofTree::Always).is_some() { - return false; - } - } - "dump-tree-on-error" => { - if dump_tree.replace(DumpSolverProofTree::OnError).is_some() { - return false; - } - } _ => return false, } } - *slot = Some(NextSolverConfig { - coherence: coherence || globally, - globally, - dump_tree: dump_tree.unwrap_or_default(), - }); + *slot = Some(NextSolverConfig { coherence: coherence || globally, globally }); } else { - *slot = Some(NextSolverConfig { - coherence: true, - globally: true, - dump_tree: Default::default(), - }); + *slot = Some(NextSolverConfig { coherence: true, globally: true }); } true @@ -1696,6 +1681,8 @@ options! { fewer_names: Option = (None, parse_opt_bool, [TRACKED], "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ (default: no)"), + fixed_x18: bool = (false, parse_bool, [TRACKED], + "make the x18 register reserved on AArch64 (default: no)"), flatten_format_args: bool = (true, parse_bool, [TRACKED], "flatten nested format_args!() and literals into a simplified format_args!() call \ (default: yes)"), diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index f6053f43fbd1..200505aaea20 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -15,8 +15,8 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::sync::{AppendOnlyVec, Lock, Lrc}; use rustc_errors::emitter::{stderr_destination, HumanEmitter, SilentEmitter}; use rustc_errors::{ - fallback_fluent_bundle, ColorConfig, Diag, DiagCtxt, DiagMessage, EmissionGuarantee, MultiSpan, - StashKey, + fallback_fluent_bundle, ColorConfig, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, + EmissionGuarantee, MultiSpan, StashKey, }; use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures}; use rustc_span::edition::Edition; @@ -106,12 +106,12 @@ pub fn feature_err_issue( // Cancel an earlier warning for this same error, if it exists. if let Some(span) = span.primary_span() { - if let Some(err) = sess.psess.dcx.steal_non_err(span, StashKey::EarlySyntaxWarning) { + if let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) { err.cancel() } } - let mut err = sess.psess.dcx.create_err(FeatureGateError { span, explain: explain.into() }); + let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() }); add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None); err } @@ -140,7 +140,7 @@ pub fn feature_warn_issue( issue: GateIssue, explain: &'static str, ) { - let mut err = sess.psess.dcx.struct_span_warn(span, explain); + let mut err = sess.dcx().struct_span_warn(span, explain); add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None); // Decorate this as a future-incompatibility lint as in rustc_middle::lint::lint_level @@ -178,30 +178,30 @@ pub fn add_feature_diagnostics_for_issue( inject_span: Option, ) { if let Some(n) = find_feature_issue(feature, issue) { - err.subdiagnostic(sess.dcx(), FeatureDiagnosticForIssue { n }); + err.subdiagnostic(FeatureDiagnosticForIssue { n }); } // #23973: do not suggest `#![feature(...)]` if we are in beta/stable if sess.psess.unstable_features.is_nightly_build() { if feature_from_cli { - err.subdiagnostic(sess.dcx(), CliFeatureDiagnosticHelp { feature }); + err.subdiagnostic(CliFeatureDiagnosticHelp { feature }); } else if let Some(span) = inject_span { - err.subdiagnostic(sess.dcx(), FeatureDiagnosticSuggestion { feature, span }); + err.subdiagnostic(FeatureDiagnosticSuggestion { feature, span }); } else { - err.subdiagnostic(sess.dcx(), FeatureDiagnosticHelp { feature }); + err.subdiagnostic(FeatureDiagnosticHelp { feature }); } if sess.opts.unstable_opts.ui_testing { - err.subdiagnostic(sess.dcx(), SuggestUpgradeCompiler::ui_testing()); + err.subdiagnostic(SuggestUpgradeCompiler::ui_testing()); } else if let Some(suggestion) = SuggestUpgradeCompiler::new() { - err.subdiagnostic(sess.dcx(), suggestion); + err.subdiagnostic(suggestion); } } } /// Info about a parsing session. pub struct ParseSess { - pub dcx: DiagCtxt, + dcx: DiagCtxt, pub unstable_features: UnstableFeatures, pub config: Cfg, pub check_config: CheckCfg, @@ -305,32 +305,12 @@ impl ParseSess { lint: &'static Lint, span: impl Into, node_id: NodeId, - msg: impl Into, - ) { - self.buffered_lints.with_lock(|buffered_lints| { - buffered_lints.push(BufferedEarlyLint { - span: span.into(), - node_id, - msg: msg.into(), - lint_id: LintId::of(lint), - diagnostic: BuiltinLintDiag::Normal, - }); - }); - } - - pub fn buffer_lint_with_diagnostic( - &self, - lint: &'static Lint, - span: impl Into, - node_id: NodeId, - msg: impl Into, diagnostic: BuiltinLintDiag, ) { self.buffered_lints.with_lock(|buffered_lints| { buffered_lints.push(BufferedEarlyLint { span: span.into(), node_id, - msg: msg.into(), lint_id: LintId::of(lint), diagnostic, }); @@ -346,4 +326,8 @@ impl ParseSess { // AppendOnlyVec, so we resort to this scheme. self.proc_macro_quoted_spans.iter_enumerated() } + + pub fn dcx(&self) -> DiagCtxtHandle<'_> { + self.dcx.handle() + } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index db01bb90d6fa..89d029fa6e0e 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -22,8 +22,8 @@ use rustc_errors::emitter::{stderr_destination, DynEmitter, HumanEmitter, HumanR use rustc_errors::json::JsonEmitter; use rustc_errors::registry::Registry; use rustc_errors::{ - codes::*, fallback_fluent_bundle, Diag, DiagCtxt, DiagMessage, Diagnostic, ErrorGuaranteed, - FatalAbort, FluentBundle, LazyFallbackBundle, TerminalUrl, + codes::*, fallback_fluent_bundle, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, + ErrorGuaranteed, FatalAbort, FluentBundle, LazyFallbackBundle, TerminalUrl, }; use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; @@ -328,8 +328,8 @@ impl Session { } #[inline] - pub fn dcx(&self) -> &DiagCtxt { - &self.psess.dcx + pub fn dcx(&self) -> DiagCtxtHandle<'_> { + self.psess.dcx() } #[inline] @@ -353,11 +353,21 @@ impl Session { && self.opts.unstable_opts.coverage_options.level >= CoverageLevel::Branch } + pub fn instrument_coverage_condition(&self) -> bool { + self.instrument_coverage() + && self.opts.unstable_opts.coverage_options.level >= CoverageLevel::Condition + } + pub fn instrument_coverage_mcdc(&self) -> bool { self.instrument_coverage() && self.opts.unstable_opts.coverage_options.level >= CoverageLevel::Mcdc } + /// True if `-Zcoverage-options=no-mir-spans` was passed. + pub fn coverage_no_mir_spans(&self) -> bool { + self.opts.unstable_opts.coverage_options.no_mir_spans + } + pub fn is_sanitizer_cfi_enabled(&self) -> bool { self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI) } @@ -449,15 +459,24 @@ impl Session { ) } - /// Returns a list of directories where target-specific tool binaries are located. + /// Returns a list of directories where target-specific tool binaries are located. Some fallback + /// directories are also returned, for example if `--sysroot` is used but tools are missing + /// (#125246): we also add the bin directories to the sysroot where rustc is located. pub fn get_tools_search_paths(&self, self_contained: bool) -> Vec { - let rustlib_path = rustc_target::target_rustlib_path(&self.sysroot, config::host_triple()); - let p = PathBuf::from_iter([ - Path::new(&self.sysroot), - Path::new(&rustlib_path), - Path::new("bin"), - ]); - if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p] } + let bin_path = filesearch::make_target_bin_path(&self.sysroot, config::host_triple()); + let fallback_sysroot_paths = filesearch::sysroot_candidates() + .into_iter() + .map(|sysroot| filesearch::make_target_bin_path(&sysroot, config::host_triple())); + let search_paths = std::iter::once(bin_path).chain(fallback_sysroot_paths); + + if self_contained { + // The self-contained tools are expected to be e.g. in `bin/self-contained` in the + // sysroot's `rustlib` path, so we add such a subfolder to the bin path, and the + // fallback paths. + search_paths.flat_map(|path| [path.clone(), path.join("self-contained")]).collect() + } else { + search_paths.collect() + } } pub fn init_incr_comp_session(&self, session_dir: PathBuf, lock_file: flock::Lock) { @@ -1051,7 +1070,7 @@ pub fn build_session( match profiler { Ok(profiler) => Some(Arc::new(profiler)), Err(e) => { - dcx.emit_warn(errors::FailedToCreateProfiler { err: e.to_string() }); + dcx.handle().emit_warn(errors::FailedToCreateProfiler { err: e.to_string() }); None } } @@ -1183,9 +1202,9 @@ fn validate_commandline_args_with_session_available(sess: &Session) { }); } } - // Cannot mix and match sanitizers. - let mut sanitizer_iter = sess.opts.unstable_opts.sanitizer.into_iter(); - if let (Some(first), Some(second)) = (sanitizer_iter.next(), sanitizer_iter.next()) { + + // Cannot mix and match mutually-exclusive sanitizers. + if let Some((first, second)) = sess.opts.unstable_opts.sanitizer.mutually_exclusive() { sess.dcx().emit_err(errors::CannotMixAndMatchSanitizers { first: first.to_string(), second: second.to_string(), @@ -1220,14 +1239,6 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::SanitizerCfiRequiresSingleCodegenUnit); } - // LLVM CFI is incompatible with LLVM KCFI. - if sess.is_sanitizer_cfi_enabled() && sess.is_sanitizer_kcfi_enabled() { - sess.dcx().emit_err(errors::CannotMixAndMatchSanitizers { - first: "cfi".to_string(), - second: "kcfi".to_string(), - }); - } - // Canonical jump tables requires CFI. if sess.is_sanitizer_cfi_canonical_jump_tables_disabled() { if !sess.is_sanitizer_cfi_enabled() { @@ -1360,7 +1371,7 @@ impl EarlyDiagCtxt { /// format. Any errors prior to that will cause an abort and all stashed diagnostics of the /// previous dcx will be emitted. pub fn abort_if_error_and_set_error_format(&mut self, output: ErrorOutputType) { - self.dcx.abort_if_errors(); + self.dcx.handle().abort_if_errors(); let emitter = mk_emitter(output); self.dcx = DiagCtxt::new(emitter); @@ -1369,44 +1380,44 @@ impl EarlyDiagCtxt { #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] pub fn early_note(&self, msg: impl Into) { - self.dcx.note(msg) + self.dcx.handle().note(msg) } #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] pub fn early_help(&self, msg: impl Into) { - self.dcx.struct_help(msg).emit() + self.dcx.handle().struct_help(msg).emit() } #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] #[must_use = "ErrorGuaranteed must be returned from `run_compiler` in order to exit with a non-zero status code"] pub fn early_err(&self, msg: impl Into) -> ErrorGuaranteed { - self.dcx.err(msg) + self.dcx.handle().err(msg) } #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] pub fn early_fatal(&self, msg: impl Into) -> ! { - self.dcx.fatal(msg) + self.dcx.handle().fatal(msg) } #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] pub fn early_struct_fatal(&self, msg: impl Into) -> Diag<'_, FatalAbort> { - self.dcx.struct_fatal(msg) + self.dcx.handle().struct_fatal(msg) } #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] pub fn early_warn(&self, msg: impl Into) { - self.dcx.warn(msg) + self.dcx.handle().warn(msg) } #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] pub fn early_struct_warn(&self, msg: impl Into) -> Diag<'_, ()> { - self.dcx.struct_warn(msg) + self.dcx.handle().struct_warn(msg) } } diff --git a/compiler/rustc_session/src/version.rs b/compiler/rustc_session/src/version.rs index 39e4541349ec..e244c77f7f95 100644 --- a/compiler/rustc_session/src/version.rs +++ b/compiler/rustc_session/src/version.rs @@ -1,5 +1,10 @@ use rustc_macros::{current_rustc_version, Decodable, Encodable, HashStable_Generic}; -use std::fmt::{self, Display}; +use std::{ + borrow::Cow, + fmt::{self, Display}, +}; + +use rustc_errors::IntoDiagArg; #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(HashStable_Generic)] @@ -18,3 +23,9 @@ impl Display for RustcVersion { write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch) } } + +impl IntoDiagArg for RustcVersion { + fn into_diag_arg(self) -> rustc_errors::DiagArgValue { + rustc_errors::DiagArgValue::Str(Cow::Owned(self.to_string())) + } +} diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs index ddd5ea5510a9..9f8888753061 100644 --- a/compiler/rustc_smir/src/lib.rs +++ b/compiler/rustc_smir/src/lib.rs @@ -6,14 +6,16 @@ //! //! This API is still completely unstable and subject to change. +// tidy-alphabetical-start +#![allow(internal_features)] +#![allow(rustc::usage_of_ty_tykind)] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(attr(allow(unused_variables), deny(warnings))) )] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![allow(internal_features)] -#![allow(rustc::usage_of_ty_tykind)] +// tidy-alphabetical-end pub mod rustc_internal; diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index 6b73c1ebd1ca..c33a52f4a7a8 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -5,17 +5,17 @@ // Prefer importing stable_mir over internal rustc constructs to make this file more readable. use crate::rustc_smir::Tables; -use rustc_middle::ty::{self as rustc_ty, Ty as InternalTy, TyCtxt}; +use rustc_middle::ty::{self as rustc_ty, Const as InternalConst, Ty as InternalTy, TyCtxt}; use rustc_span::Symbol; use stable_mir::abi::Layout; use stable_mir::mir::alloc::AllocId; use stable_mir::mir::mono::{Instance, MonoItem, StaticDef}; -use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, Safety}; +use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, Safety, UnOp}; use stable_mir::ty::{ - Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const, - DynKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, - GenericArgKind, GenericArgs, IndexedVal, IntTy, Movability, Pattern, Region, RigidTy, Span, - TermKind, TraitRef, Ty, UintTy, VariantDef, VariantIdx, + Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, DynKind, + ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, + GenericArgKind, GenericArgs, IndexedVal, IntTy, MirConst, Movability, Pattern, Region, RigidTy, + Span, TermKind, TraitRef, Ty, TyConst, UintTy, VariantDef, VariantIdx, }; use stable_mir::{CrateItem, CrateNum, DefId}; @@ -55,7 +55,7 @@ impl RustcInternal for GenericArgKind { let arg: rustc_ty::GenericArg<'tcx> = match self { GenericArgKind::Lifetime(reg) => reg.internal(tables, tcx).into(), GenericArgKind::Type(ty) => ty.internal(tables, tcx).into(), - GenericArgKind::Const(cnst) => ty_const(cnst, tables, tcx).into(), + GenericArgKind::Const(cnst) => cnst.internal(tables, tcx).into(), }; tcx.lift(arg).unwrap() } @@ -76,13 +76,20 @@ impl RustcInternal for Ty { } } +impl RustcInternal for TyConst { + type T<'tcx> = InternalConst<'tcx>; + fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { + tcx.lift(tables.ty_consts[self.id]).unwrap() + } +} + impl RustcInternal for Pattern { type T<'tcx> = rustc_ty::Pattern<'tcx>; fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { tcx.mk_pat(match self { Pattern::Range { start, end, include_end } => rustc_ty::PatternKind::Range { - start: start.as_ref().map(|c| ty_const(c, tables, tcx)), - end: end.as_ref().map(|c| ty_const(c, tables, tcx)), + start: start.as_ref().map(|c| c.internal(tables, tcx)), + end: end.as_ref().map(|c| c.internal(tables, tcx)), include_end: *include_end, }, }) @@ -101,7 +108,7 @@ impl RustcInternal for RigidTy { RigidTy::Float(float_ty) => rustc_ty::TyKind::Float(float_ty.internal(tables, tcx)), RigidTy::Never => rustc_ty::TyKind::Never, RigidTy::Array(ty, cnst) => { - rustc_ty::TyKind::Array(ty.internal(tables, tcx), ty_const(cnst, tables, tcx)) + rustc_ty::TyKind::Array(ty.internal(tables, tcx), cnst.internal(tables, tcx)) } RigidTy::Pat(ty, pat) => { rustc_ty::TyKind::Pat(ty.internal(tables, tcx), pat.internal(tables, tcx)) @@ -239,25 +246,14 @@ impl RustcInternal for VariantDef { } } -fn ty_const<'tcx>( - constant: &Const, - tables: &mut Tables<'_>, - tcx: TyCtxt<'tcx>, -) -> rustc_ty::Const<'tcx> { - match constant.internal(tables, tcx) { - rustc_middle::mir::Const::Ty(c) => c, - cnst => { - panic!("Trying to convert constant `{constant:?}` to type constant, but found {cnst:?}") - } - } -} - -impl RustcInternal for Const { +impl RustcInternal for MirConst { type T<'tcx> = rustc_middle::mir::Const<'tcx>; fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { - let constant = tables.constants[self.id]; + let constant = tables.mir_consts[self.id]; match constant { - rustc_middle::mir::Const::Ty(ty) => rustc_middle::mir::Const::Ty(tcx.lift(ty).unwrap()), + rustc_middle::mir::Const::Ty(ty, ct) => { + rustc_middle::mir::Const::Ty(tcx.lift(ty).unwrap(), tcx.lift(ct).unwrap()) + } rustc_middle::mir::Const::Unevaluated(uneval, ty) => { rustc_middle::mir::Const::Unevaluated( tcx.lift(uneval).unwrap(), @@ -392,7 +388,7 @@ impl RustcInternal for TermKind { fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { match self { TermKind::Type(ty) => ty.internal(tables, tcx).into(), - TermKind::Const(const_) => ty_const(const_, tables, tcx).into(), + TermKind::Const(cnst) => cnst.internal(tables, tcx).into(), } } } @@ -582,6 +578,18 @@ impl RustcInternal for BinOp { } } +impl RustcInternal for UnOp { + type T<'tcx> = rustc_middle::mir::UnOp; + + fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { + match self { + UnOp::Not => rustc_middle::mir::UnOp::Not, + UnOp::Neg => rustc_middle::mir::UnOp::Neg, + UnOp::PtrMetadata => rustc_middle::mir::UnOp::PtrMetadata, + } + } +} + impl RustcInternal for &T where T: RustcInternal, diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 6e870728bafc..810ffc142a09 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -214,7 +214,8 @@ where spans: IndexMap::default(), types: IndexMap::default(), instances: IndexMap::default(), - constants: IndexMap::default(), + ty_consts: IndexMap::default(), + mir_consts: IndexMap::default(), layouts: IndexMap::default(), })); stable_mir::compiler_interface::run(&tables, || init(&tables, f)) diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs index 221224eed017..42fa6989ddcf 100644 --- a/compiler/rustc_smir/src/rustc_smir/builder.rs +++ b/compiler/rustc_smir/src/rustc_smir/builder.rs @@ -19,7 +19,7 @@ impl<'tcx> BodyBuilder<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>) -> Self { let instance = match instance.def { // To get the fallback body of an intrinsic, we need to convert it to an item. - ty::InstanceDef::Intrinsic(def_id) => ty::Instance::new(def_id, instance.args), + ty::InstanceKind::Intrinsic(def_id) => ty::Instance::new(def_id, instance.args), _ => instance, }; BodyBuilder { tcx, instance } @@ -52,7 +52,11 @@ impl<'tcx> BodyBuilder<'tcx> { } impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> { - fn visit_constant(&mut self, constant: &mut mir::ConstOperand<'tcx>, location: mir::Location) { + fn visit_const_operand( + &mut self, + constant: &mut mir::ConstOperand<'tcx>, + location: mir::Location, + ) { let const_ = constant.const_; let val = match const_.eval(self.tcx, ty::ParamEnv::reveal_all(), constant.span) { Ok(v) => v, @@ -63,7 +67,7 @@ impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> { }; let ty = constant.ty(); constant.const_ = mir::Const::Val(val, ty); - self.super_constant(constant, location); + self.super_const_operand(constant, location); } fn tcx(&self) -> TyCtxt<'tcx> { diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index fd31c020f899..9afd507ce113 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -6,7 +6,7 @@ #![allow(rustc::usage_of_qualified_ty)] use rustc_abi::HasDataLayout; -use rustc_middle::ty; +use rustc_hir::LangItem; use rustc_middle::ty::layout::{ FnAbiOf, FnAbiOfHelpers, HasParamEnv, HasTyCtxt, LayoutOf, LayoutOfHelpers, }; @@ -14,17 +14,18 @@ use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths}; use rustc_middle::ty::{ GenericPredicates, Instance, List, ParamEnv, ScalarInt, TyCtxt, TypeVisitableExt, ValTree, }; +use rustc_middle::{mir, ty}; use rustc_span::def_id::LOCAL_CRATE; use stable_mir::abi::{FnAbi, Layout, LayoutShape}; use stable_mir::compiler_interface::Context; use stable_mir::mir::alloc::GlobalAlloc; use stable_mir::mir::mono::{InstanceDef, StaticDef}; -use stable_mir::mir::{BinOp, Body, Place}; +use stable_mir::mir::{BinOp, Body, Place, UnOp}; use stable_mir::target::{MachineInfo, MachineSize}; use stable_mir::ty::{ - AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, - ForeignItemKind, GenericArgs, LineInfo, PolyFnSig, RigidTy, Span, Ty, TyKind, UintTy, - VariantDef, + AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef, + ForeignItemKind, GenericArgs, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, Ty, + TyConst, TyKind, UintTy, VariantDef, }; use stable_mir::{Crate, CrateDef, CrateItem, CrateNum, DefId, Error, Filename, ItemKind, Symbol}; use std::cell::RefCell; @@ -59,13 +60,14 @@ impl<'tcx> Context for TablesWrapper<'tcx> { fn mir_body(&self, item: stable_mir::DefId) -> stable_mir::mir::Body { let mut tables = self.0.borrow_mut(); let def_id = tables[item]; - tables.tcx.instance_mir(rustc_middle::ty::InstanceDef::Item(def_id)).stable(&mut tables) + tables.tcx.instance_mir(rustc_middle::ty::InstanceKind::Item(def_id)).stable(&mut tables) } fn has_body(&self, def: DefId) -> bool { - let tables = self.0.borrow(); - let def_id = tables[def]; - tables.tcx.is_mir_available(def_id) + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let def_id = def.internal(&mut *tables, tcx); + tables.item_has_body(def_id) } fn foreign_modules(&self, crate_num: CrateNum) -> Vec { @@ -295,7 +297,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let def_id = def.0.internal(&mut *tables, tcx); - tables.tcx.lang_items().c_str() == Some(def_id) + tables.tcx.is_lang_item(def_id, LangItem::CStr) } fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig { @@ -307,6 +309,21 @@ impl<'tcx> Context for TablesWrapper<'tcx> { sig.stable(&mut *tables) } + fn intrinsic(&self, def: DefId) -> Option { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let def_id = def.internal(&mut *tables, tcx); + let intrinsic = tcx.intrinsic_raw(def_id); + intrinsic.map(|_| IntrinsicDef(def)) + } + + fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let def_id = def.0.internal(&mut *tables, tcx); + tcx.intrinsic(def_id).unwrap().name.to_string() + } + fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; @@ -333,7 +350,15 @@ impl<'tcx> Context for TablesWrapper<'tcx> { def.internal(&mut *tables, tcx).fields.iter().map(|f| f.stable(&mut *tables)).collect() } - fn eval_target_usize(&self, cnst: &Const) -> Result { + fn eval_target_usize(&self, cnst: &MirConst) -> Result { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let mir_const = cnst.internal(&mut *tables, tcx); + mir_const + .try_eval_target_usize(tables.tcx, ParamEnv::empty()) + .ok_or_else(|| Error::new(format!("Const `{cnst:?}` cannot be encoded as u64"))) + } + fn eval_target_usize_ty(&self, cnst: &TyConst) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let mir_const = cnst.internal(&mut *tables, tcx); @@ -342,7 +367,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .ok_or_else(|| Error::new(format!("Const `{cnst:?}` cannot be encoded as u64"))) } - fn try_new_const_zst(&self, ty: Ty) -> Result { + fn try_new_const_zst(&self, ty: Ty) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let ty_internal = ty.internal(&mut *tables, tcx); @@ -363,25 +388,46 @@ impl<'tcx> Context for TablesWrapper<'tcx> { ))); } - Ok(ty::Const::zero_sized(tables.tcx, ty_internal).stable(&mut *tables)) + Ok(mir::Const::Ty(ty_internal, ty::Const::zero_sized(tables.tcx, ty_internal)) + .stable(&mut *tables)) } - fn new_const_str(&self, value: &str) -> Const { + fn new_const_str(&self, value: &str) -> MirConst { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let ty = ty::Ty::new_static_str(tcx); let bytes = value.as_bytes(); let val_tree = ty::ValTree::from_raw_bytes(tcx, bytes); - ty::Const::new_value(tcx, val_tree, ty).stable(&mut *tables) + let ct = ty::Const::new_value(tcx, val_tree, ty); + super::convert::mir_const_from_ty_const(&mut *tables, ct, ty) } - fn new_const_bool(&self, value: bool) -> Const { + fn new_const_bool(&self, value: bool) -> MirConst { let mut tables = self.0.borrow_mut(); - ty::Const::from_bool(tables.tcx, value).stable(&mut *tables) + let ct = ty::Const::from_bool(tables.tcx, value); + let ty = tables.tcx.types.bool; + super::convert::mir_const_from_ty_const(&mut *tables, ct, ty) } - fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result { + fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let ty = ty::Ty::new_uint(tcx, uint_ty.internal(&mut *tables, tcx)); + let size = tables.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap().size; + + // We don't use Const::from_bits since it doesn't have any error checking. + let scalar = ScalarInt::try_from_uint(value, size).ok_or_else(|| { + Error::new(format!("Value overflow: cannot convert `{value}` to `{ty}`.")) + })?; + let ct = ty::Const::new_value(tables.tcx, ValTree::from_scalar_int(scalar), ty); + Ok(super::convert::mir_const_from_ty_const(&mut *tables, ct, ty)) + } + fn try_new_ty_const_uint( + &self, + value: u128, + uint_ty: UintTy, + ) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let ty = ty::Ty::new_uint(tcx, uint_ty.internal(&mut *tables, tcx)); @@ -426,7 +472,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .stable(&mut *tables) } - fn const_pretty(&self, cnst: &stable_mir::ty::Const) -> String { + fn mir_const_pretty(&self, cnst: &stable_mir::ty::MirConst) -> String { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; cnst.internal(&mut *tables, tcx).to_string() @@ -447,6 +493,11 @@ impl<'tcx> Context for TablesWrapper<'tcx> { tables.types[ty].kind().stable(&mut *tables) } + fn ty_const_pretty(&self, ct: stable_mir::ty::TyConstId) -> String { + let tables = self.0.borrow_mut(); + tables.ty_consts[ct].to_string() + } + fn rigid_ty_discriminant_ty(&self, ty: &RigidTy) -> stable_mir::ty::Ty { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; @@ -459,7 +510,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let mut tables = self.0.borrow_mut(); let instance = tables.instances[def]; tables - .has_body(instance) + .instance_has_body(instance) .then(|| BodyBuilder::new(tables.tcx, instance).build(&mut *tables)) } @@ -497,13 +548,13 @@ impl<'tcx> Context for TablesWrapper<'tcx> { fn is_empty_drop_shim(&self, def: InstanceDef) -> bool { let tables = self.0.borrow_mut(); let instance = tables.instances[def]; - matches!(instance.def, ty::InstanceDef::DropGlue(_, None)) + matches!(instance.def, ty::InstanceKind::DropGlue(_, None)) } fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool { let tables = self.0.borrow_mut(); let instance = tables.instances[def]; - matches!(instance.def, ty::InstanceDef::AsyncDropGlueCtorShim(_, None)) + matches!(instance.def, ty::InstanceKind::AsyncDropGlueCtorShim(_, None)) } fn mono_instance(&self, def_id: stable_mir::DefId) -> stable_mir::mir::mono::Instance { @@ -645,16 +696,6 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } } - /// Retrieve the plain intrinsic name of an instance. - /// - /// This assumes that the instance is an intrinsic. - fn intrinsic_name(&self, def: InstanceDef) -> Symbol { - let tables = self.0.borrow_mut(); - let instance = tables.instances[def]; - let intrinsic = tables.tcx.intrinsic(instance.def_id()).unwrap(); - intrinsic.name.to_string() - } - fn ty_layout(&self, ty: Ty) -> Result { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; @@ -683,6 +724,14 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let ty = bin_op.internal(&mut *tables, tcx).ty(tcx, rhs_internal, lhs_internal); ty.stable(&mut *tables) } + + fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let arg_internal = arg.internal(&mut *tables, tcx); + let ty = un_op.internal(&mut *tables, tcx).ty(tcx, arg_internal); + ty.stable(&mut *tables) + } } pub struct TablesWrapper<'tcx>(pub RefCell>); diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 452ab04c44c5..f15b82d0c031 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -1,11 +1,12 @@ //! Conversion of internal Rust compiler `mir` items to stable ones. +use rustc_middle::bug; use rustc_middle::mir; use rustc_middle::mir::interpret::alloc_range; use rustc_middle::mir::mono::MonoItem; use stable_mir::mir::alloc::GlobalAlloc; use stable_mir::mir::{ConstOperand, Statement, UserTypeProjection, VarDebugInfoFragment}; -use stable_mir::ty::{Allocation, Const, ConstantKind}; +use stable_mir::ty::{Allocation, ConstantKind, MirConst}; use stable_mir::{opaque, Error}; use crate::rustc_smir::{alloc, Stable, Tables}; @@ -183,16 +184,21 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { op.stable(tables), ty.stable(tables), ), - BinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::BinaryOp( - bin_op.stable(tables), - ops.0.stable(tables), - ops.1.stable(tables), - ), - CheckedBinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::CheckedBinaryOp( - bin_op.stable(tables), - ops.0.stable(tables), - ops.1.stable(tables), - ), + BinaryOp(bin_op, ops) => { + if let Some(bin_op) = bin_op.overflowing_to_wrapping() { + stable_mir::mir::Rvalue::CheckedBinaryOp( + bin_op.stable(tables), + ops.0.stable(tables), + ops.1.stable(tables), + ) + } else { + stable_mir::mir::Rvalue::BinaryOp( + bin_op.stable(tables), + ops.0.stable(tables), + ops.1.stable(tables), + ) + } + } NullaryOp(null_op, ty) => { stable_mir::mir::Rvalue::NullaryOp(null_op.stable(tables), ty.stable(tables)) } @@ -322,13 +328,13 @@ impl<'tcx> Stable<'tcx> for mir::Operand<'tcx> { } impl<'tcx> Stable<'tcx> for mir::ConstOperand<'tcx> { - type T = stable_mir::mir::Constant; + type T = stable_mir::mir::ConstOperand; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { - stable_mir::mir::Constant { + stable_mir::mir::ConstOperand { span: self.span.stable(tables), user_ty: self.user_ty.map(|u| u.as_usize()).or(None), - literal: self.const_.stable(tables), + const_: self.const_.stable(tables), } } } @@ -485,10 +491,13 @@ impl<'tcx> Stable<'tcx> for mir::BinOp { match self { BinOp::Add => stable_mir::mir::BinOp::Add, BinOp::AddUnchecked => stable_mir::mir::BinOp::AddUnchecked, + BinOp::AddWithOverflow => bug!("AddWithOverflow should have been translated already"), BinOp::Sub => stable_mir::mir::BinOp::Sub, BinOp::SubUnchecked => stable_mir::mir::BinOp::SubUnchecked, + BinOp::SubWithOverflow => bug!("AddWithOverflow should have been translated already"), BinOp::Mul => stable_mir::mir::BinOp::Mul, BinOp::MulUnchecked => stable_mir::mir::BinOp::MulUnchecked, + BinOp::MulWithOverflow => bug!("AddWithOverflow should have been translated already"), BinOp::Div => stable_mir::mir::BinOp::Div, BinOp::Rem => stable_mir::mir::BinOp::Rem, BinOp::BitXor => stable_mir::mir::BinOp::BitXor, @@ -517,6 +526,7 @@ impl<'tcx> Stable<'tcx> for mir::UnOp { match self { UnOp::Not => stable_mir::mir::UnOp::Not, UnOp::Neg => stable_mir::mir::UnOp::Neg, + UnOp::PtrMetadata => stable_mir::mir::UnOp::PtrMetadata, } } } @@ -714,11 +724,16 @@ impl<'tcx> Stable<'tcx> for mir::interpret::GlobalAlloc<'tcx> { } impl<'tcx> Stable<'tcx> for rustc_middle::mir::Const<'tcx> { - type T = stable_mir::ty::Const; + type T = stable_mir::ty::MirConst; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { + let id = tables.intern_mir_const(tables.tcx.lift(*self).unwrap()); match *self { - mir::Const::Ty(c) => c.stable(tables), + mir::Const::Ty(ty, c) => MirConst::new( + stable_mir::ty::ConstantKind::Ty(c.stable(tables)), + ty.stable(tables), + id, + ), mir::Const::Unevaluated(unev_const, ty) => { let kind = stable_mir::ty::ConstantKind::Unevaluated(stable_mir::ty::UnevaluatedConst { @@ -727,21 +742,18 @@ impl<'tcx> Stable<'tcx> for rustc_middle::mir::Const<'tcx> { promoted: unev_const.promoted.map(|u| u.as_u32()), }); let ty = ty.stable(tables); - let id = tables.intern_const(tables.tcx.lift(*self).unwrap()); - Const::new(kind, ty, id) + MirConst::new(kind, ty, id) } mir::Const::Val(mir::ConstValue::ZeroSized, ty) => { let ty = ty.stable(tables); - let id = tables.intern_const(tables.tcx.lift(*self).unwrap()); - Const::new(ConstantKind::ZeroSized, ty, id) + MirConst::new(ConstantKind::ZeroSized, ty, id) } mir::Const::Val(val, ty) => { let ty = tables.tcx.lift(ty).unwrap(); let val = tables.tcx.lift(val).unwrap(); let kind = ConstantKind::Allocated(alloc::new_allocation(ty, val, tables)); let ty = ty.stable(tables); - let id = tables.intern_const(tables.tcx.lift(*self).unwrap()); - Const::new(kind, ty, id) + MirConst::new(kind, ty, id) } } } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs index 736378a530f0..50687935473a 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs @@ -9,6 +9,8 @@ mod error; mod mir; mod ty; +pub use ty::mir_const_from_ty_const; + impl<'tcx> Stable<'tcx> for rustc_hir::Safety { type T = stable_mir::mir::Safety; fn stable(&self, _: &mut Tables<'_>) -> Self::T { diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index d59318be7200..9cd841bba109 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -3,8 +3,7 @@ use rustc_middle::ty::Ty; use rustc_middle::{mir, ty}; use stable_mir::ty::{ - AdtKind, Const, ConstantKind, FloatTy, GenericArgs, GenericParamDef, IntTy, Region, RigidTy, - TyKind, UintTy, + AdtKind, FloatTy, GenericArgs, GenericParamDef, IntTy, Region, RigidTy, TyKind, UintTy, }; use crate::rustc_smir::{alloc, Stable, Tables}; @@ -194,7 +193,7 @@ where } } -impl<'tcx, S, V> Stable<'tcx> for ty::EarlyBinder +impl<'tcx, S, V> Stable<'tcx> for ty::EarlyBinder<'tcx, S> where S: Stable<'tcx, T = V>, { @@ -410,45 +409,85 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> { } } +pub fn mir_const_from_ty_const<'tcx>( + tables: &mut Tables<'tcx>, + ty_const: ty::Const<'tcx>, + ty: Ty<'tcx>, +) -> stable_mir::ty::MirConst { + let kind = match ty_const.kind() { + ty::Value(ty, val) => { + let val = match val { + ty::ValTree::Leaf(scalar) => ty::ValTree::Leaf(scalar), + ty::ValTree::Branch(branch) => { + ty::ValTree::Branch(tables.tcx.lift(branch).unwrap()) + } + }; + let ty = tables.tcx.lift(ty).unwrap(); + let const_val = tables.tcx.valtree_to_const_val((ty, val)); + if matches!(const_val, mir::ConstValue::ZeroSized) { + stable_mir::ty::ConstantKind::ZeroSized + } else { + stable_mir::ty::ConstantKind::Allocated(alloc::new_allocation( + ty, const_val, tables, + )) + } + } + ty::ParamCt(param) => stable_mir::ty::ConstantKind::Param(param.stable(tables)), + ty::ErrorCt(_) => unreachable!(), + ty::InferCt(_) => unreachable!(), + ty::BoundCt(_, _) => unimplemented!(), + ty::PlaceholderCt(_) => unimplemented!(), + ty::Unevaluated(uv) => { + stable_mir::ty::ConstantKind::Unevaluated(stable_mir::ty::UnevaluatedConst { + def: tables.const_def(uv.def), + args: uv.args.stable(tables), + promoted: None, + }) + } + ty::ExprCt(_) => unimplemented!(), + }; + let stable_ty = tables.intern_ty(ty); + let id = tables.intern_mir_const(mir::Const::Ty(ty, ty_const)); + stable_mir::ty::MirConst::new(kind, stable_ty, id) +} + impl<'tcx> Stable<'tcx> for ty::Const<'tcx> { - type T = stable_mir::ty::Const; + type T = stable_mir::ty::TyConst; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { let kind = match self.kind() { - ty::Value(val) => { + ty::Value(ty, val) => { let val = match val { ty::ValTree::Leaf(scalar) => ty::ValTree::Leaf(scalar), ty::ValTree::Branch(branch) => { ty::ValTree::Branch(tables.tcx.lift(branch).unwrap()) } }; - let ty = tables.tcx.lift(self.ty()).unwrap(); + + let ty = tables.tcx.lift(ty).unwrap(); let const_val = tables.tcx.valtree_to_const_val((ty, val)); if matches!(const_val, mir::ConstValue::ZeroSized) { - ConstantKind::ZeroSized + stable_mir::ty::TyConstKind::ZSTValue(ty.stable(tables)) } else { - stable_mir::ty::ConstantKind::Allocated(alloc::new_allocation( - ty, const_val, tables, - )) + stable_mir::ty::TyConstKind::Value( + ty.stable(tables), + alloc::new_allocation(ty, const_val, tables), + ) } } - ty::ParamCt(param) => stable_mir::ty::ConstantKind::Param(param.stable(tables)), + ty::ParamCt(param) => stable_mir::ty::TyConstKind::Param(param.stable(tables)), + ty::Unevaluated(uv) => stable_mir::ty::TyConstKind::Unevaluated( + tables.const_def(uv.def), + uv.args.stable(tables), + ), ty::ErrorCt(_) => unreachable!(), ty::InferCt(_) => unreachable!(), ty::BoundCt(_, _) => unimplemented!(), ty::PlaceholderCt(_) => unimplemented!(), - ty::Unevaluated(uv) => { - stable_mir::ty::ConstantKind::Unevaluated(stable_mir::ty::UnevaluatedConst { - def: tables.const_def(uv.def), - args: uv.args.stable(tables), - promoted: None, - }) - } ty::ExprCt(_) => unimplemented!(), }; - let ty = self.ty().stable(tables); - let id = tables.intern_const(mir::Const::Ty(tables.tcx.lift(*self).unwrap())); - Const::new(kind, ty, id) + let id = tables.intern_ty_const(tables.tcx.lift(*self).unwrap()); + stable_mir::ty::TyConst::new(kind, id) } } @@ -505,6 +544,7 @@ impl<'tcx> Stable<'tcx> for ty::TraitDef { is_marker: self.is_marker, is_coinductive: self.is_coinductive, skip_array_during_method_dispatch: self.skip_array_during_method_dispatch, + skip_boxed_slice_during_method_dispatch: self.skip_boxed_slice_during_method_dispatch, specialization_kind: self.specialization_kind.stable(tables), must_implement_one_of: self .must_implement_one_of @@ -706,12 +746,11 @@ impl<'tcx> Stable<'tcx> for ty::TraitPredicate<'tcx> { } } -impl<'tcx, A, B, U, V> Stable<'tcx> for ty::OutlivesPredicate +impl<'tcx, T> Stable<'tcx> for ty::OutlivesPredicate<'tcx, T> where - A: Stable<'tcx, T = U>, - B: Stable<'tcx, T = V>, + T: Stable<'tcx>, { - type T = stable_mir::ty::OutlivesPredicate; + type T = stable_mir::ty::OutlivesPredicate; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { let ty::OutlivesPredicate(a, b) = self; @@ -771,7 +810,6 @@ impl<'tcx> Stable<'tcx> for ty::RegionKind<'tcx> { use stable_mir::ty::{BoundRegion, EarlyParamRegion, RegionKind}; match self { ty::ReEarlyParam(early_reg) => RegionKind::ReEarlyParam(EarlyParamRegion { - def_id: tables.region_def(early_reg.def_id), index: early_reg.index, name: early_reg.name.to_string(), }), @@ -801,22 +839,22 @@ impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> { fn stable(&self, tables: &mut Tables<'_>) -> Self::T { let def = tables.instance_def(tables.tcx.lift(*self).unwrap()); let kind = match self.def { - ty::InstanceDef::Item(..) => stable_mir::mir::mono::InstanceKind::Item, - ty::InstanceDef::Intrinsic(..) => stable_mir::mir::mono::InstanceKind::Intrinsic, - ty::InstanceDef::Virtual(_def_id, idx) => { + ty::InstanceKind::Item(..) => stable_mir::mir::mono::InstanceKind::Item, + ty::InstanceKind::Intrinsic(..) => stable_mir::mir::mono::InstanceKind::Intrinsic, + ty::InstanceKind::Virtual(_def_id, idx) => { stable_mir::mir::mono::InstanceKind::Virtual { idx } } - ty::InstanceDef::VTableShim(..) - | ty::InstanceDef::ReifyShim(..) - | ty::InstanceDef::FnPtrAddrShim(..) - | ty::InstanceDef::ClosureOnceShim { .. } - | ty::InstanceDef::ConstructCoroutineInClosureShim { .. } - | ty::InstanceDef::CoroutineKindShim { .. } - | ty::InstanceDef::ThreadLocalShim(..) - | ty::InstanceDef::DropGlue(..) - | ty::InstanceDef::CloneShim(..) - | ty::InstanceDef::FnPtrShim(..) - | ty::InstanceDef::AsyncDropGlueCtorShim(..) => { + ty::InstanceKind::VTableShim(..) + | ty::InstanceKind::ReifyShim(..) + | ty::InstanceKind::FnPtrAddrShim(..) + | ty::InstanceKind::ClosureOnceShim { .. } + | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } + | ty::InstanceKind::CoroutineKindShim { .. } + | ty::InstanceKind::ThreadLocalShim(..) + | ty::InstanceKind::DropGlue(..) + | ty::InstanceKind::CloneShim(..) + | ty::InstanceKind::FnPtrShim(..) + | ty::InstanceKind::AsyncDropGlueCtorShim(..) => { stable_mir::mir::mono::InstanceKind::Shim } }; @@ -828,10 +866,10 @@ impl<'tcx> Stable<'tcx> for ty::Variance { type T = stable_mir::mir::Variance; fn stable(&self, _: &mut Tables<'_>) -> Self::T { match self { - ty::Variance::Bivariant => stable_mir::mir::Variance::Bivariant, - ty::Variance::Contravariant => stable_mir::mir::Variance::Contravariant, - ty::Variance::Covariant => stable_mir::mir::Variance::Covariant, - ty::Variance::Invariant => stable_mir::mir::Variance::Invariant, + ty::Bivariant => stable_mir::mir::Variance::Bivariant, + ty::Contravariant => stable_mir::mir::Variance::Contravariant, + ty::Covariant => stable_mir::mir::Variance::Covariant, + ty::Invariant => stable_mir::mir::Variance::Invariant, } } } diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index aba7e7dc9c21..82522e995d63 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE}; use stable_mir::abi::Layout; use stable_mir::mir::mono::InstanceDef; -use stable_mir::ty::{ConstId, Span}; +use stable_mir::ty::{MirConstId, Span, TyConstId}; use stable_mir::{CtorKind, ItemKind}; use std::ops::RangeInclusive; use tracing::debug; @@ -33,7 +33,8 @@ pub struct Tables<'tcx> { pub(crate) spans: IndexMap, pub(crate) types: IndexMap, stable_mir::ty::Ty>, pub(crate) instances: IndexMap, InstanceDef>, - pub(crate) constants: IndexMap, ConstId>, + pub(crate) ty_consts: IndexMap, TyConstId>, + pub(crate) mir_consts: IndexMap, MirConstId>, pub(crate) layouts: IndexMap, Layout>, } @@ -42,20 +43,41 @@ impl<'tcx> Tables<'tcx> { self.types.create_or_fetch(ty) } - pub(crate) fn intern_const(&mut self, constant: mir::Const<'tcx>) -> ConstId { - self.constants.create_or_fetch(constant) + pub(crate) fn intern_ty_const(&mut self, ct: ty::Const<'tcx>) -> TyConstId { + self.ty_consts.create_or_fetch(ct) } - pub(crate) fn has_body(&self, instance: Instance<'tcx>) -> bool { + pub(crate) fn intern_mir_const(&mut self, constant: mir::Const<'tcx>) -> MirConstId { + self.mir_consts.create_or_fetch(constant) + } + + /// Return whether the instance as a body available. + /// + /// Items and intrinsics may have a body available from its definition. + /// Shims body may be generated depending on their type. + pub(crate) fn instance_has_body(&self, instance: Instance<'tcx>) -> bool { let def_id = instance.def_id(); - self.tcx.is_mir_available(def_id) + self.item_has_body(def_id) || !matches!( instance.def, - ty::InstanceDef::Virtual(..) - | ty::InstanceDef::Intrinsic(..) - | ty::InstanceDef::Item(..) + ty::InstanceKind::Virtual(..) + | ty::InstanceKind::Intrinsic(..) + | ty::InstanceKind::Item(..) ) } + + /// Return whether the item has a body defined by the user. + /// + /// Note that intrinsics may have a placeholder body that shouldn't be used in practice. + /// In StableMIR, we handle this case as if the body is not available. + pub(crate) fn item_has_body(&self, def_id: DefId) -> bool { + let must_override = if let Some(intrinsic) = self.tcx.intrinsic(def_id) { + intrinsic.must_be_overridden + } else { + false + }; + !must_override && self.tcx.is_mir_available(def_id) + } } /// Build a stable mir crate from a given crate number. diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index aa4bcefab939..ba0ad9230c86 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -40,6 +40,7 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; use std::fmt; use std::hash::Hash; +use tracing::{debug, trace}; /// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks". #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -690,6 +691,11 @@ impl SyntaxContext { SyntaxContext(raw) } + #[inline] + pub(crate) const fn from_u16(raw: u16) -> SyntaxContext { + SyntaxContext(raw as u32) + } + /// Extend a syntax context with a given expansion and transparency. pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext { HygieneData::with(|data| data.apply_mark(self, expn_id, transparency)) diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index f83bacdcebe7..99ac973497e1 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -33,15 +33,16 @@ #![feature(rustdoc_internals)] // tidy-alphabetical-end +// The code produced by the `Encodable`/`Decodable` derive macros refer to +// `rustc_span::Span{Encoder,Decoder}`. That's fine outside this crate, but doesn't work inside +// this crate without this line making `rustc_span` available. extern crate self as rustc_span; -#[macro_use] -extern crate tracing; - use rustc_data_structures::{outline, AtomicRef}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_serialize::opaque::{FileEncoder, MemDecoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; +use tracing::debug; mod caching_source_map_view; pub mod source_map; @@ -519,8 +520,9 @@ impl SpanData { pub fn with_hi(&self, hi: BytePos) -> Span { Span::new(self.lo, hi, self.ctxt, self.parent) } + /// Avoid if possible, `Span::map_ctxt` should be preferred. #[inline] - pub fn with_ctxt(&self, ctxt: SyntaxContext) -> Span { + fn with_ctxt(&self, ctxt: SyntaxContext) -> Span { Span::new(self.lo, self.hi, ctxt, self.parent) } #[inline] @@ -576,7 +578,7 @@ impl Span { } #[inline] pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span { - self.data_untracked().with_ctxt(ctxt) + self.map_ctxt(|_| ctxt) } #[inline] pub fn parent(self) -> Option { @@ -681,6 +683,13 @@ impl Span { if span.hi > other.hi { Some(span.with_lo(cmp::max(span.lo, other.hi))) } else { None } } + /// Returns `Some(span)`, where the end is trimmed by the start of `other`. + pub fn trim_end(self, other: Span) -> Option { + let span = self.data(); + let other = other.data(); + if span.lo < other.lo { Some(span.with_hi(cmp::min(span.hi, other.lo))) } else { None } + } + /// Returns the source span -- this is either the supplied span, or the span for /// the macro callsite that expanded to it. pub fn source_callsite(self) -> Span { @@ -958,7 +967,7 @@ impl Span { /// This span, but in a larger context, may switch to the metavariable span if suitable. pub fn with_neighbor(self, neighbor: Span) -> Span { match Span::prepare_to_combine(self, neighbor) { - Ok((this, ..)) => Span::new(this.lo, this.hi, this.ctxt, this.parent), + Ok((this, ..)) => this.span(), Err(_) => self, } } @@ -1051,39 +1060,46 @@ impl Span { #[inline] pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> Span { - let span = self.data(); - span.with_ctxt(span.ctxt.apply_mark(expn_id, transparency)) + self.map_ctxt(|ctxt| ctxt.apply_mark(expn_id, transparency)) } #[inline] pub fn remove_mark(&mut self) -> ExpnId { - let mut span = self.data(); - let mark = span.ctxt.remove_mark(); - *self = Span::new(span.lo, span.hi, span.ctxt, span.parent); + let mut mark = ExpnId::root(); + *self = self.map_ctxt(|mut ctxt| { + mark = ctxt.remove_mark(); + ctxt + }); mark } #[inline] pub fn adjust(&mut self, expn_id: ExpnId) -> Option { - let mut span = self.data(); - let mark = span.ctxt.adjust(expn_id); - *self = Span::new(span.lo, span.hi, span.ctxt, span.parent); + let mut mark = None; + *self = self.map_ctxt(|mut ctxt| { + mark = ctxt.adjust(expn_id); + ctxt + }); mark } #[inline] pub fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option { - let mut span = self.data(); - let mark = span.ctxt.normalize_to_macros_2_0_and_adjust(expn_id); - *self = Span::new(span.lo, span.hi, span.ctxt, span.parent); + let mut mark = None; + *self = self.map_ctxt(|mut ctxt| { + mark = ctxt.normalize_to_macros_2_0_and_adjust(expn_id); + ctxt + }); mark } #[inline] pub fn glob_adjust(&mut self, expn_id: ExpnId, glob_span: Span) -> Option> { - let mut span = self.data(); - let mark = span.ctxt.glob_adjust(expn_id, glob_span); - *self = Span::new(span.lo, span.hi, span.ctxt, span.parent); + let mut mark = None; + *self = self.map_ctxt(|mut ctxt| { + mark = ctxt.glob_adjust(expn_id, glob_span); + ctxt + }); mark } @@ -1093,22 +1109,22 @@ impl Span { expn_id: ExpnId, glob_span: Span, ) -> Option> { - let mut span = self.data(); - let mark = span.ctxt.reverse_glob_adjust(expn_id, glob_span); - *self = Span::new(span.lo, span.hi, span.ctxt, span.parent); + let mut mark = None; + *self = self.map_ctxt(|mut ctxt| { + mark = ctxt.reverse_glob_adjust(expn_id, glob_span); + ctxt + }); mark } #[inline] pub fn normalize_to_macros_2_0(self) -> Span { - let span = self.data(); - span.with_ctxt(span.ctxt.normalize_to_macros_2_0()) + self.map_ctxt(|ctxt| ctxt.normalize_to_macros_2_0()) } #[inline] pub fn normalize_to_macro_rules(self) -> Span { - let span = self.data(); - span.with_ctxt(span.ctxt.normalize_to_macro_rules()) + self.map_ctxt(|ctxt| ctxt.normalize_to_macro_rules()) } } @@ -1346,7 +1362,7 @@ impl fmt::Debug for Span { impl fmt::Debug for SpanData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&Span::new(self.lo, self.hi, self.ctxt, self.parent), f) + fmt::Debug::fmt(&self.span(), f) } } diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 2093dcf0e831..fb212d67997a 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -16,6 +16,7 @@ use rustc_macros::{Decodable, Encodable}; use std::fs; use std::io::{self, BorrowedBuf, Read}; use std::path; +use tracing::{debug, instrument, trace}; #[cfg(test)] mod tests; diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index 788a52faf568..116056a69c1f 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -87,6 +87,152 @@ pub struct Span { ctxt_or_parent_or_marker: u16, } +// Convenience structures for all span formats. +#[derive(Clone, Copy)] +struct InlineCtxt { + lo: u32, + len: u16, + ctxt: u16, +} + +#[derive(Clone, Copy)] +struct InlineParent { + lo: u32, + len_with_tag: u16, + parent: u16, +} + +#[derive(Clone, Copy)] +struct PartiallyInterned { + index: u32, + ctxt: u16, +} + +#[derive(Clone, Copy)] +struct Interned { + index: u32, +} + +impl InlineCtxt { + #[inline] + fn data(self) -> SpanData { + let len = self.len as u32; + debug_assert!(len <= MAX_LEN); + SpanData { + lo: BytePos(self.lo), + hi: BytePos(self.lo.debug_strict_add(len)), + ctxt: SyntaxContext::from_u16(self.ctxt), + parent: None, + } + } + #[inline] + fn span(lo: u32, len: u16, ctxt: u16) -> Span { + Span { lo_or_index: lo, len_with_tag_or_marker: len, ctxt_or_parent_or_marker: ctxt } + } + #[inline] + fn from_span(span: Span) -> InlineCtxt { + let (lo, len, ctxt) = + (span.lo_or_index, span.len_with_tag_or_marker, span.ctxt_or_parent_or_marker); + InlineCtxt { lo, len, ctxt } + } +} + +impl InlineParent { + #[inline] + fn data(self) -> SpanData { + let len = (self.len_with_tag & !PARENT_TAG) as u32; + debug_assert!(len <= MAX_LEN); + SpanData { + lo: BytePos(self.lo), + hi: BytePos(self.lo.debug_strict_add(len)), + ctxt: SyntaxContext::root(), + parent: Some(LocalDefId { local_def_index: DefIndex::from_u16(self.parent) }), + } + } + #[inline] + fn span(lo: u32, len: u16, parent: u16) -> Span { + let (lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker) = + (lo, PARENT_TAG | len, parent); + Span { lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker } + } + #[inline] + fn from_span(span: Span) -> InlineParent { + let (lo, len_with_tag, parent) = + (span.lo_or_index, span.len_with_tag_or_marker, span.ctxt_or_parent_or_marker); + InlineParent { lo, len_with_tag, parent } + } +} + +impl PartiallyInterned { + #[inline] + fn data(self) -> SpanData { + SpanData { + ctxt: SyntaxContext::from_u16(self.ctxt), + ..with_span_interner(|interner| interner.spans[self.index as usize]) + } + } + #[inline] + fn span(index: u32, ctxt: u16) -> Span { + let (lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker) = + (index, BASE_LEN_INTERNED_MARKER, ctxt); + Span { lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker } + } + #[inline] + fn from_span(span: Span) -> PartiallyInterned { + PartiallyInterned { index: span.lo_or_index, ctxt: span.ctxt_or_parent_or_marker } + } +} + +impl Interned { + #[inline] + fn data(self) -> SpanData { + with_span_interner(|interner| interner.spans[self.index as usize]) + } + #[inline] + fn span(index: u32) -> Span { + let (lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker) = + (index, BASE_LEN_INTERNED_MARKER, CTXT_INTERNED_MARKER); + Span { lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker } + } + #[inline] + fn from_span(span: Span) -> Interned { + Interned { index: span.lo_or_index } + } +} + +// This code is very hot, and converting span to an enum and matching on it doesn't optimize away +// properly. So we are using a macro emulating such a match, but expand it directly to an if-else +// chain. +macro_rules! match_span_kind { + ( + $span:expr, + InlineCtxt($span1:ident) => $arm1:expr, + InlineParent($span2:ident) => $arm2:expr, + PartiallyInterned($span3:ident) => $arm3:expr, + Interned($span4:ident) => $arm4:expr, + ) => { + if $span.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER { + if $span.len_with_tag_or_marker & PARENT_TAG == 0 { + // Inline-context format. + let $span1 = InlineCtxt::from_span($span); + $arm1 + } else { + // Inline-parent format. + let $span2 = InlineParent::from_span($span); + $arm2 + } + } else if $span.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER { + // Partially-interned format. + let $span3 = PartiallyInterned::from_span($span); + $arm3 + } else { + // Interned format. + let $span4 = Interned::from_span($span); + $arm4 + } + }; +} + // `MAX_LEN` is chosen so that `PARENT_TAG | MAX_LEN` is distinct from // `BASE_LEN_INTERNED_MARKER`. (If `MAX_LEN` was 1 higher, this wouldn't be true.) const MAX_LEN: u32 = 0b0111_1111_1111_1110; @@ -111,42 +257,29 @@ impl Span { std::mem::swap(&mut lo, &mut hi); } - let (lo2, len, ctxt2) = (lo.0, hi.0 - lo.0, ctxt.as_u32()); - + // Small len may enable one of fully inline formats (or may not). + let (len, ctxt32) = (hi.0 - lo.0, ctxt.as_u32()); if len <= MAX_LEN { - if ctxt2 <= MAX_CTXT && parent.is_none() { - // Inline-context format. - return Span { - lo_or_index: lo2, - len_with_tag_or_marker: len as u16, - ctxt_or_parent_or_marker: ctxt2 as u16, - }; - } else if ctxt2 == SyntaxContext::root().as_u32() + if ctxt32 <= MAX_CTXT && parent.is_none() { + return InlineCtxt::span(lo.0, len as u16, ctxt32 as u16); + } else if ctxt32 == 0 && let Some(parent) = parent - && let parent2 = parent.local_def_index.as_u32() - && parent2 <= MAX_CTXT + && let parent32 = parent.local_def_index.as_u32() + && parent32 <= MAX_CTXT { - // Inline-parent format. - return Span { - lo_or_index: lo2, - len_with_tag_or_marker: PARENT_TAG | len as u16, - ctxt_or_parent_or_marker: parent2 as u16, - }; + return InlineParent::span(lo.0, len as u16, parent32 as u16); } } - // Partially-interned or fully-interned format. - let index = - with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt, parent })); - let ctxt_or_parent_or_marker = if ctxt2 <= MAX_CTXT { - ctxt2 as u16 // partially-interned - } else { - CTXT_INTERNED_MARKER // fully-interned + // Otherwise small ctxt may enable the partially inline format. + let index = |ctxt| { + with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt, parent })) }; - Span { - lo_or_index: index, - len_with_tag_or_marker: BASE_LEN_INTERNED_MARKER, - ctxt_or_parent_or_marker, + if ctxt32 <= MAX_CTXT { + // Interned ctxt should never be read, so it can use any value. + PartiallyInterned::span(index(SyntaxContext::from_u32(u32::MAX)), ctxt32 as u16) + } else { + Interned::span(index(ctxt)) } } @@ -163,37 +296,12 @@ impl Span { /// This function must not be used outside the incremental engine. #[inline] pub fn data_untracked(self) -> SpanData { - if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER { - if self.len_with_tag_or_marker & PARENT_TAG == 0 { - // Inline-context format. - let len = self.len_with_tag_or_marker as u32; - debug_assert!(len <= MAX_LEN); - SpanData { - lo: BytePos(self.lo_or_index), - hi: BytePos(self.lo_or_index.debug_strict_add(len)), - ctxt: SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32), - parent: None, - } - } else { - // Inline-parent format. - let len = (self.len_with_tag_or_marker & !PARENT_TAG) as u32; - debug_assert!(len <= MAX_LEN); - let parent = LocalDefId { - local_def_index: DefIndex::from_u32(self.ctxt_or_parent_or_marker as u32), - }; - SpanData { - lo: BytePos(self.lo_or_index), - hi: BytePos(self.lo_or_index.debug_strict_add(len)), - ctxt: SyntaxContext::root(), - parent: Some(parent), - } - } - } else { - // Fully-interned or partially-interned format. In either case, - // the interned value contains all the data, so we don't need to - // distinguish them. - let index = self.lo_or_index; - with_span_interner(|interner| interner.spans[index as usize]) + match_span_kind! { + self, + InlineCtxt(span) => span.data(), + InlineParent(span) => span.data(), + PartiallyInterned(span) => span.data(), + Interned(span) => span.data(), } } @@ -214,26 +322,42 @@ impl Span { } } + // For optimization we are interested in cases in which the context is inline and the context + // update doesn't change format. All non-inline or format changing scenarios require accessing + // interner and can fall back to `Span::new`. + #[inline] + pub fn map_ctxt(self, update: impl FnOnce(SyntaxContext) -> SyntaxContext) -> Span { + match_span_kind! { + self, + InlineCtxt(span) => { + let updated_ctxt32 = update(SyntaxContext::from_u16(span.ctxt)).as_u32(); + // Any small new context including zero will preserve the format. + return if updated_ctxt32 <= MAX_CTXT { + InlineCtxt::span(span.lo, span.len, updated_ctxt32 as u16) + } else { + span.data().with_ctxt(SyntaxContext::from_u32(updated_ctxt32)) + }; + }, + InlineParent(_span) => {}, + PartiallyInterned(_span) => {}, + Interned(_span) => {}, + } + + let data = self.data_untracked(); + data.with_ctxt(update(data.ctxt)) + } + // Returns either syntactic context, if it can be retrieved without taking the interner lock, // or an index into the interner if it cannot. + #[inline] fn inline_ctxt(self) -> Result { - Ok(if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER { - if self.len_with_tag_or_marker & PARENT_TAG == 0 { - // Inline-context format. - SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32) - } else { - // Inline-parent format. We know that the SyntaxContext is root. - SyntaxContext::root() - } - } else if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER { - // Partially-interned format. This path avoids looking up the - // interned value, and is the whole point of the - // partially-interned format. - SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32) - } else { - // Fully-interned format. - return Err(self.lo_or_index as usize); - }) + match_span_kind! { + self, + InlineCtxt(span) => Ok(SyntaxContext::from_u16(span.ctxt)), + InlineParent(_span) => Ok(SyntaxContext::root()), + PartiallyInterned(span) => Ok(SyntaxContext::from_u16(span.ctxt)), + Interned(span) => Err(span.index as usize), + } } /// This function is used as a fast path when decoding the full `SpanData` is not necessary. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 68b1b32baf2d..55eba257ca21 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -103,6 +103,7 @@ symbols! { MacroRules: "macro_rules", Raw: "raw", Reuse: "reuse", + Safe: "safe", Union: "union", Yeet: "yeet", } @@ -210,7 +211,6 @@ symbols! { FsPermissions, FusedIterator, Future, - FutureOutput, GlobalAlloc, Hash, HashMap, @@ -430,6 +430,7 @@ symbols! { async_drop, async_drop_chain, async_drop_defer, + async_drop_deferred_drop_in_place, async_drop_either, async_drop_fuse, async_drop_in_place, @@ -439,8 +440,10 @@ symbols! { async_fn, async_fn_in_trait, async_fn_kind_helper, + async_fn_kind_upvars, async_fn_mut, async_fn_once, + async_fn_once_output, async_fn_track_caller, async_fn_traits, async_for_loop, @@ -498,6 +501,8 @@ symbols! { call, call_mut, call_once, + call_once_future, + call_ref_future, caller_location, capture_disjoint_fields, catch_unwind, @@ -630,7 +635,9 @@ symbols! { coroutine, coroutine_clone, coroutine_resume, + coroutine_return, coroutine_state, + coroutine_yield, coroutines, cosf128, cosf16, @@ -911,6 +918,7 @@ symbols! { fundamental, fused_iterator, future, + future_output, future_trait, gdb_script_file, ge, @@ -930,6 +938,7 @@ symbols! { global_alloc_ty, global_allocator, global_asm, + global_registration, globs, gt, half_open_range_patterns, @@ -1109,6 +1118,7 @@ symbols! { macro_lifetime_matcher, macro_literal_matcher, macro_metavar_expr, + macro_metavar_expr_concat, macro_reexport, macro_use, macro_vis_matcher, @@ -1178,6 +1188,7 @@ symbols! { mir_make_place, mir_move, mir_offset, + mir_ptr_metadata, mir_retag, mir_return, mir_return_to, @@ -1296,6 +1307,7 @@ symbols! { offset_of, offset_of_enum, offset_of_nested, + offset_of_slice, ok_or_else, omit_gdb_pretty_printer_section, on, @@ -1432,6 +1444,7 @@ symbols! { ptr_guaranteed_cmp, ptr_is_null, ptr_mask, + ptr_metadata, ptr_null, ptr_null_mut, ptr_offset_from, @@ -1572,14 +1585,13 @@ symbols! { rustc_def_path, rustc_default_body_unstable, rustc_deny_explicit_impl, + rustc_deprecated_safe_2024, rustc_diagnostic_item, rustc_diagnostic_macros, rustc_dirty, rustc_do_not_const_check, rustc_doc_primitive, rustc_dummy, - rustc_dump_env_program_clauses, - rustc_dump_program_clauses, rustc_dump_user_args, rustc_dump_vtable, rustc_effective_visibility, @@ -1632,7 +1644,7 @@ symbols! { rustc_reservation_impl, rustc_safe_intrinsic, rustc_serialize, - rustc_skip_array_during_method_dispatch, + rustc_skip_during_method_dispatch, rustc_specialization_trait, rustc_std_internal_symbol, rustc_strict_coherence, @@ -1664,6 +1676,7 @@ symbols! { shadow_call_stack, shl, shl_assign, + shorter_tail_lifetimes, should_panic, shr, shr_assign, @@ -1681,6 +1694,7 @@ symbols! { simd_cast_ptr, simd_ceil, simd_ctlz, + simd_ctpop, simd_cttz, simd_div, simd_eq, @@ -1951,9 +1965,11 @@ symbols! { unreachable_display, unreachable_macro, unrestricted_attribute_tokens, + unsafe_attributes, unsafe_block_in_unsafe_fn, unsafe_cell, unsafe_cell_raw_get, + unsafe_extern_blocks, unsafe_no_drop_flag, unsafe_pin_internals, unsize, @@ -2203,6 +2219,7 @@ impl fmt::Display for IdentPrinter { pub struct MacroRulesNormalizedIdent(Ident); impl MacroRulesNormalizedIdent { + #[inline] pub fn new(ident: Ident) -> Self { Self(ident.normalize_to_macro_rules()) } diff --git a/compiler/rustc_span/src/tests.rs b/compiler/rustc_span/src/tests.rs index cb88fa89058d..48fa786fb1c8 100644 --- a/compiler/rustc_span/src/tests.rs +++ b/compiler/rustc_span/src/tests.rs @@ -42,3 +42,60 @@ fn test_normalize_newlines() { check("\r\r\n", "\r\n", &[2]); check("hello\rworld", "hello\rworld", &[]); } + +#[test] +fn test_trim() { + let span = |lo: usize, hi: usize| { + Span::new(BytePos::from_usize(lo), BytePos::from_usize(hi), SyntaxContext::root(), None) + }; + + // Various positions, named for their relation to `start` and `end`. + let well_before = 1; + let before = 3; + let start = 5; + let mid = 7; + let end = 9; + let after = 11; + let well_after = 13; + + // The resulting span's context should be that of `self`, not `other`. + let other = span(start, end).with_ctxt(SyntaxContext::from_u32(999)); + + // Test cases for `trim_end`. + + assert_eq!(span(well_before, before).trim_end(other), Some(span(well_before, before))); + assert_eq!(span(well_before, start).trim_end(other), Some(span(well_before, start))); + assert_eq!(span(well_before, mid).trim_end(other), Some(span(well_before, start))); + assert_eq!(span(well_before, end).trim_end(other), Some(span(well_before, start))); + assert_eq!(span(well_before, after).trim_end(other), Some(span(well_before, start))); + + assert_eq!(span(start, mid).trim_end(other), None); + assert_eq!(span(start, end).trim_end(other), None); + assert_eq!(span(start, after).trim_end(other), None); + + assert_eq!(span(mid, end).trim_end(other), None); + assert_eq!(span(mid, after).trim_end(other), None); + + assert_eq!(span(end, after).trim_end(other), None); + + assert_eq!(span(after, well_after).trim_end(other), None); + + // Test cases for `trim_start`. + + assert_eq!(span(after, well_after).trim_start(other), Some(span(after, well_after))); + assert_eq!(span(end, well_after).trim_start(other), Some(span(end, well_after))); + assert_eq!(span(mid, well_after).trim_start(other), Some(span(end, well_after))); + assert_eq!(span(start, well_after).trim_start(other), Some(span(end, well_after))); + assert_eq!(span(before, well_after).trim_start(other), Some(span(end, well_after))); + + assert_eq!(span(mid, end).trim_start(other), None); + assert_eq!(span(start, end).trim_start(other), None); + assert_eq!(span(before, end).trim_start(other), None); + + assert_eq!(span(start, mid).trim_start(other), None); + assert_eq!(span(before, mid).trim_start(other), None); + + assert_eq!(span(before, start).trim_start(other), None); + + assert_eq!(span(well_before, before).trim_start(other), None); +} diff --git a/compiler/rustc_symbol_mangling/src/errors.rs b/compiler/rustc_symbol_mangling/src/errors.rs index b5721ee489db..9b87a1419fa3 100644 --- a/compiler/rustc_symbol_mangling/src/errors.rs +++ b/compiler/rustc_symbol_mangling/src/errors.rs @@ -1,6 +1,6 @@ //! Errors emitted by symbol_mangling. -use rustc_errors::{Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level}; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level}; use rustc_span::Span; use std::fmt; @@ -14,7 +14,7 @@ pub struct TestOutput { // natural language, and (b) it's only used in tests. So we construct it // manually and avoid the fluent machinery. impl Diagnostic<'_, G> for TestOutput { - fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let TestOutput { span, kind, content } = self; #[allow(rustc::untranslatable_diagnostic)] diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 0ed1f67bb821..9edd2ff9b1a5 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -56,8 +56,8 @@ pub(super) fn mangle<'tcx>( printer .print_def_path( def_id, - if let ty::InstanceDef::DropGlue(_, _) | ty::InstanceDef::AsyncDropGlueCtorShim(_, _) = - instance.def + if let ty::InstanceKind::DropGlue(_, _) + | ty::InstanceKind::AsyncDropGlueCtorShim(_, _) = instance.def { // Add the name of the dropped type to the symbol name &*instance.args @@ -68,13 +68,13 @@ pub(super) fn mangle<'tcx>( .unwrap(); match instance.def { - ty::InstanceDef::ThreadLocalShim(..) => { + ty::InstanceKind::ThreadLocalShim(..) => { printer.write_str("{{tls-shim}}").unwrap(); } - ty::InstanceDef::VTableShim(..) => { + ty::InstanceKind::VTableShim(..) => { printer.write_str("{{vtable-shim}}").unwrap(); } - ty::InstanceDef::ReifyShim(_, reason) => { + ty::InstanceKind::ReifyShim(_, reason) => { printer.write_str("{{reify-shim").unwrap(); match reason { Some(ReifyReason::FnPtr) => printer.write_str("-fnptr").unwrap(), @@ -85,8 +85,8 @@ pub(super) fn mangle<'tcx>( } // FIXME(async_closures): This shouldn't be needed when we fix // `Instance::ty`/`Instance::def_id`. - ty::InstanceDef::ConstructCoroutineInClosureShim { .. } - | ty::InstanceDef::CoroutineKindShim { .. } => { + ty::InstanceKind::ConstructCoroutineInClosureShim { .. } + | ty::InstanceKind::CoroutineKindShim { .. } => { printer.write_str("{{fn-once-shim}}").unwrap(); } _ => {} @@ -270,15 +270,15 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> { fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> { // only print integers - match (ct.kind(), ct.ty().kind()) { - (ty::ConstKind::Value(ty::ValTree::Leaf(scalar)), ty::Int(_) | ty::Uint(_)) => { + match ct.kind() { + ty::ConstKind::Value(ty, ty::ValTree::Leaf(scalar)) if ty.is_integral() => { // The `pretty_print_const` formatting depends on -Zverbose-internals // flag, so we cannot reuse it here. - let signed = matches!(ct.ty().kind(), ty::Int(_)); + let signed = matches!(ty.kind(), ty::Int(_)); write!( self, "{:#?}", - ty::ConstInt::new(scalar, signed, ct.ty().is_ptr_sized_integral()) + ty::ConstInt::new(scalar, signed, ty.is_ptr_sized_integral()) )?; } _ => self.write_str("_")?, diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 745ae41085b2..e65d3080a0a1 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -87,11 +87,13 @@ //! virtually impossible. Thus, symbol hash generation exclusively relies on //! DefPaths which are much more robust in the face of changes to the code base. +// tidy-alphabetical-start +#![allow(internal_features)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(rustdoc_internals)] #![feature(let_chains)] -#![allow(internal_features)] +#![feature(rustdoc_internals)] +// tidy-alphabetical-end use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 57b1542ff5a4..42c4fa83d1bf 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -43,14 +43,14 @@ pub(super) fn mangle<'tcx>( // Append `::{shim:...#0}` to shims that can coexist with a non-shim instance. let shim_kind = match instance.def { - ty::InstanceDef::ThreadLocalShim(_) => Some("tls"), - ty::InstanceDef::VTableShim(_) => Some("vtable"), - ty::InstanceDef::ReifyShim(_, None) => Some("reify"), - ty::InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr)) => Some("reify_fnptr"), - ty::InstanceDef::ReifyShim(_, Some(ReifyReason::Vtable)) => Some("reify_vtable"), + ty::InstanceKind::ThreadLocalShim(_) => Some("tls"), + ty::InstanceKind::VTableShim(_) => Some("vtable"), + ty::InstanceKind::ReifyShim(_, None) => Some("reify"), + ty::InstanceKind::ReifyShim(_, Some(ReifyReason::FnPtr)) => Some("reify_fnptr"), + ty::InstanceKind::ReifyShim(_, Some(ReifyReason::Vtable)) => Some("reify_vtable"), - ty::InstanceDef::ConstructCoroutineInClosureShim { .. } - | ty::InstanceDef::CoroutineKindShim { .. } => Some("fn_once"), + ty::InstanceKind::ConstructCoroutineInClosureShim { .. } + | ty::InstanceKind::CoroutineKindShim { .. } => Some("fn_once"), _ => None, }; @@ -541,8 +541,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> { // We only mangle a typed value if the const can be evaluated. let ct = ct.normalize(self.tcx, ty::ParamEnv::reveal_all()); - match ct.kind() { - ty::ConstKind::Value(_) => {} + let (ct_ty, valtree) = match ct.kind() { + ty::ConstKind::Value(ty, val) => (ty, val), // Placeholders (should be demangled as `_`). // NOTE(eddyb) despite `Unevaluated` having a `DefId` (and therefore @@ -559,7 +559,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { self.push("p"); return Ok(()); } - } + }; if let Some(&i) = self.consts.get(&ct) { self.print_backref(i)?; @@ -567,16 +567,15 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { } let start = self.out.len(); - let ty = ct.ty(); - match ty.kind() { + match ct_ty.kind() { ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => { - ty.print(self)?; + ct_ty.print(self)?; let mut bits = ct.eval_bits(self.tcx, ty::ParamEnv::reveal_all()); // Negative integer values are mangled using `n` as a "sign prefix". - if let ty::Int(ity) = ty.kind() { + if let ty::Int(ity) = ct_ty.kind() { let val = Integer::from_int_ty(&self.tcx, *ity).size().sign_extend(bits) as i128; if val < 0 { @@ -598,40 +597,32 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { match inner_ty.kind() { ty::Str if mutbl.is_not() => { - match ct.kind() { - ty::ConstKind::Value(valtree) => { - let slice = - valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { - bug!( - "expected to get raw bytes from valtree {:?} for type {:}", - valtree, ty - ) - }); - let s = std::str::from_utf8(slice) - .expect("non utf8 str from MIR interpreter"); + let slice = + valtree.try_to_raw_bytes(self.tcx(), ct_ty).unwrap_or_else(|| { + bug!( + "expected to get raw bytes from valtree {:?} for type {:}", + valtree, + ct_ty + ) + }); + let s = + std::str::from_utf8(slice).expect("non utf8 str from MIR interpreter"); - self.push("e"); + self.push("e"); - // FIXME(eddyb) use a specialized hex-encoding loop. - for byte in s.bytes() { - let _ = write!(self.out, "{byte:02x}"); - } - - self.push("_"); - } - - _ => { - bug!("symbol_names: unsupported `&str` constant: {:?}", ct); - } + // FIXME(eddyb) use a specialized hex-encoding loop. + for byte in s.bytes() { + let _ = write!(self.out, "{byte:02x}"); } + + self.push("_"); } _ => { - let pointee_ty = ct - .ty() + let pointee_ty = ct_ty .builtin_deref(true) .expect("tried to dereference on non-ptr type"); - // FIXME(const_generics): add an assert that we only do this for valtrees. - let dereferenced_const = self.tcx.mk_ct_from_kind(ct.kind(), pointee_ty); + let dereferenced_const = + ty::Const::new_value(self.tcx, valtree, pointee_ty); dereferenced_const.print(self)?; } } @@ -649,7 +640,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { Ok(()) }; - match *ct.ty().kind() { + match *ct_ty.kind() { ty::Array(..) | ty::Slice(_) => { self.push("A"); print_field_list(self)?; @@ -698,7 +689,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { } } _ => { - bug!("symbol_names: unsupported constant of type `{}` ({:?})", ct.ty(), ct); + bug!("symbol_names: unsupported constant of type `{}` ({:?})", ct_ty, ct); } } diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index fc79c9232d1b..5713542c17d6 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -29,6 +29,7 @@ mod wasm; mod x86; mod x86_64; mod x86_win64; +mod xtensa; #[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] pub enum PassMode { @@ -903,6 +904,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { } } "hexagon" => hexagon::compute_abi_info(self), + "xtensa" => xtensa::compute_abi_info(cx, self), "riscv32" | "riscv64" => riscv::compute_abi_info(cx, self), "wasm32" | "wasm64" => { if cx.target_spec().adjust_abi(cx, abi, self.c_variadic) == spec::abi::Abi::Wasm { diff --git a/compiler/rustc_target/src/abi/call/nvptx64.rs b/compiler/rustc_target/src/abi/call/nvptx64.rs index f85fa2419f0f..ac68e8879f69 100644 --- a/compiler/rustc_target/src/abi/call/nvptx64.rs +++ b/compiler/rustc_target/src/abi/call/nvptx64.rs @@ -1,21 +1,51 @@ use crate::abi::call::{ArgAbi, FnAbi, PassMode, Reg, Size, Uniform}; use crate::abi::{HasDataLayout, TyAbiInterface}; +use super::{ArgAttribute, ArgAttributes, ArgExtension, CastTarget}; + fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { - if ret.layout.is_aggregate() && ret.layout.size.bits() > 64 { - ret.make_indirect(); - } else { - // FIXME: this is wrong! Need to decide which ABI we really want here. - ret.make_direct_deprecated(); + if ret.layout.is_aggregate() && ret.layout.is_sized() { + classify_aggregate(ret) + } else if ret.layout.size.bits() < 32 && ret.layout.is_sized() { + ret.extend_integer_width_to(32); } } fn classify_arg(arg: &mut ArgAbi<'_, Ty>) { - if arg.layout.is_aggregate() && arg.layout.size.bits() > 64 { - arg.make_indirect(); + if arg.layout.is_aggregate() && arg.layout.is_sized() { + classify_aggregate(arg) + } else if arg.layout.size.bits() < 32 && arg.layout.is_sized() { + arg.extend_integer_width_to(32); + } +} + +/// the pass mode used for aggregates in arg and ret position +fn classify_aggregate(arg: &mut ArgAbi<'_, Ty>) { + let align_bytes = arg.layout.align.abi.bytes(); + let size = arg.layout.size; + + let reg = match align_bytes { + 1 => Reg::i8(), + 2 => Reg::i16(), + 4 => Reg::i32(), + 8 => Reg::i64(), + 16 => Reg::i128(), + _ => unreachable!("Align is given as power of 2 no larger than 16 bytes"), + }; + + if align_bytes == size.bytes() { + arg.cast_to(CastTarget { + prefix: [Some(reg), None, None, None, None, None, None, None], + rest: Uniform::new(Reg::i8(), Size::from_bytes(0)), + attrs: ArgAttributes { + regular: ArgAttribute::default(), + arg_ext: ArgExtension::None, + pointee_size: Size::ZERO, + pointee_align: None, + }, + }); } else { - // FIXME: this is wrong! Need to decide which ABI we really want here. - arg.make_direct_deprecated(); + arg.cast_to(Uniform::new(reg, size)); } } diff --git a/compiler/rustc_target/src/abi/call/xtensa.rs b/compiler/rustc_target/src/abi/call/xtensa.rs new file mode 100644 index 000000000000..addbe6989254 --- /dev/null +++ b/compiler/rustc_target/src/abi/call/xtensa.rs @@ -0,0 +1,123 @@ +//! The Xtensa ABI implementation +//! +//! This ABI implementation is based on the following sources: +//! +//! Section 8.1.4 & 8.1.5 of the Xtensa ISA reference manual, as well as snippets from +//! Section 2.3 from the Xtensa programmers guide. + +use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform}; +use crate::abi::{Abi, HasDataLayout, Size, TyAbiInterface}; +use crate::spec::HasTargetSpec; + +const NUM_ARG_GPRS: u64 = 6; +const NUM_RET_GPRS: u64 = 4; +const MAX_ARG_IN_REGS_SIZE: u64 = NUM_ARG_GPRS * 32; +const MAX_RET_IN_REGS_SIZE: u64 = NUM_RET_GPRS * 32; + +fn classify_ret_ty<'a, Ty, C>(arg: &mut ArgAbi<'_, Ty>) +where + Ty: TyAbiInterface<'a, C> + Copy, +{ + if arg.is_ignore() { + return; + } + + // The rules for return and argument types are the same, + // so defer to `classify_arg_ty`. + let mut arg_gprs_left = NUM_RET_GPRS; + classify_arg_ty(arg, &mut arg_gprs_left, MAX_RET_IN_REGS_SIZE); + // Ret args cannot be passed via stack, we lower to indirect and let the backend handle the invisble reference + match arg.mode { + super::PassMode::Indirect { attrs: _, meta_attrs: _, ref mut on_stack } => { + *on_stack = false; + } + _ => {} + } +} + +fn classify_arg_ty<'a, Ty, C>(arg: &mut ArgAbi<'_, Ty>, arg_gprs_left: &mut u64, max_size: u64) +where + Ty: TyAbiInterface<'a, C> + Copy, +{ + assert!(*arg_gprs_left <= NUM_ARG_GPRS, "Arg GPR tracking underflow"); + + // Ignore empty structs/unions. + if arg.layout.is_zst() { + return; + } + + let size = arg.layout.size.bits(); + let needed_align = arg.layout.align.abi.bits(); + let mut must_use_stack = false; + + // Determine the number of GPRs needed to pass the current argument + // according to the ABI. 2*XLen-aligned varargs are passed in "aligned" + // register pairs, so may consume 3 registers. + let mut needed_arg_gprs = (size + 32 - 1) / 32; + if needed_align == 64 { + needed_arg_gprs += *arg_gprs_left % 2; + } + + if needed_arg_gprs > *arg_gprs_left + || needed_align > 128 + || (*arg_gprs_left < (max_size / 32) && needed_align == 128) + { + must_use_stack = true; + needed_arg_gprs = *arg_gprs_left; + } + *arg_gprs_left -= needed_arg_gprs; + + if must_use_stack { + arg.make_indirect_byval(None); + } else { + if is_xtensa_aggregate(arg) { + // Aggregates which are <= max_size will be passed in + // registers if possible, so coerce to integers. + + // Use a single `xlen` int if possible, 2 * `xlen` if 2 * `xlen` alignment + // is required, and a 2-element `xlen` array if only `xlen` alignment is + // required. + if size <= 32 { + arg.cast_to(Reg::i32()); + } else { + let reg = if needed_align == 2 * 32 { Reg::i64() } else { Reg::i32() }; + let total = Size::from_bits(((size + 32 - 1) / 32) * 32); + arg.cast_to(Uniform::new(reg, total)); + } + } else { + // All integral types are promoted to `xlen` + // width. + // + // We let the LLVM backend handle integral types >= xlen. + if size < 32 { + arg.extend_integer_width_to(32); + } + } + } +} + +pub fn compute_abi_info<'a, Ty, C>(_cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout + HasTargetSpec, +{ + if !fn_abi.ret.is_ignore() { + classify_ret_ty(&mut fn_abi.ret); + } + + let mut arg_gprs_left = NUM_ARG_GPRS; + + for arg in fn_abi.args.iter_mut() { + if arg.is_ignore() { + continue; + } + classify_arg_ty(arg, &mut arg_gprs_left, MAX_ARG_IN_REGS_SIZE); + } +} + +fn is_xtensa_aggregate<'a, Ty>(arg: &ArgAbi<'a, Ty>) -> bool { + match arg.layout.abi { + Abi::Vector { .. } => true, + _ => arg.layout.is_aggregate(), + } +} diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 666efd9deca2..737e9a8eab02 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -256,29 +256,6 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { Ty::is_transparent(self) } - pub fn offset_of_subfield(self, cx: &C, indices: I) -> Size - where - Ty: TyAbiInterface<'a, C>, - I: Iterator, - { - let mut layout = self; - let mut offset = Size::ZERO; - - for (variant, field) in indices { - layout = layout.for_variant(cx, variant); - let index = field.index(); - offset += layout.fields.offset(index); - layout = layout.field(cx, index); - assert!( - layout.is_sized(), - "offset of unsized field (type {:?}) cannot be computed statically", - layout.ty - ); - } - - offset - } - /// Finds the one field that is not a 1-ZST. /// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields. pub fn non_1zst_field(&self, cx: &C) -> Option<(usize, Self)> diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 5f4ce5ed5997..b057bf94a087 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -707,15 +707,19 @@ pub enum InlineAsmType { I32, I64, I128, + F16, F32, F64, + F128, VecI8(u64), VecI16(u64), VecI32(u64), VecI64(u64), VecI128(u64), + VecF16(u64), VecF32(u64), VecF64(u64), + VecF128(u64), } impl InlineAsmType { @@ -730,15 +734,19 @@ impl InlineAsmType { Self::I32 => 4, Self::I64 => 8, Self::I128 => 16, + Self::F16 => 2, Self::F32 => 4, Self::F64 => 8, + Self::F128 => 16, Self::VecI8(n) => n * 1, Self::VecI16(n) => n * 2, Self::VecI32(n) => n * 4, Self::VecI64(n) => n * 8, Self::VecI128(n) => n * 16, + Self::VecF16(n) => n * 2, Self::VecF32(n) => n * 4, Self::VecF64(n) => n * 8, + Self::VecF128(n) => n * 16, }) } } @@ -751,15 +759,19 @@ impl fmt::Display for InlineAsmType { Self::I32 => f.write_str("i32"), Self::I64 => f.write_str("i64"), Self::I128 => f.write_str("i128"), + Self::F16 => f.write_str("f16"), Self::F32 => f.write_str("f32"), Self::F64 => f.write_str("f64"), + Self::F128 => f.write_str("f128"), Self::VecI8(n) => write!(f, "i8x{n}"), Self::VecI16(n) => write!(f, "i16x{n}"), Self::VecI32(n) => write!(f, "i32x{n}"), Self::VecI64(n) => write!(f, "i64x{n}"), Self::VecI128(n) => write!(f, "i128x{n}"), + Self::VecF16(n) => write!(f, "f16x{n}"), Self::VecF32(n) => write!(f, "f32x{n}"), Self::VecF64(n) => write!(f, "f64x{n}"), + Self::VecF128(n) => write!(f, "f128x{n}"), } } } diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs index 28413a5bcbd1..8452961c17c0 100644 --- a/compiler/rustc_target/src/asm/x86.rs +++ b/compiler/rustc_target/src/asm/x86.rs @@ -107,26 +107,26 @@ impl X86InlineAsmRegClass { match self { Self::reg | Self::reg_abcd => { if arch == InlineAsmArch::X86_64 { - types! { _: I16, I32, I64, F32, F64; } + types! { _: I16, I32, I64, F16, F32, F64; } } else { - types! { _: I16, I32, F32; } + types! { _: I16, I32, F16, F32; } } } Self::reg_byte => types! { _: I8; }, Self::xmm_reg => types! { - sse: I32, I64, F32, F64, - VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2); + sse: I32, I64, F16, F32, F64, F128, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2); }, Self::ymm_reg => types! { - avx: I32, I64, F32, F64, - VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2), - VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4); + avx: I32, I64, F16, F32, F64, F128, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2), + VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF16(16), VecF32(8), VecF64(4); }, Self::zmm_reg => types! { - avx512f: I32, I64, F32, F64, - VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2), - VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4), - VecI8(64), VecI16(32), VecI32(16), VecI64(8), VecF32(16), VecF64(8); + avx512f: I32, I64, F16, F32, F64, F128, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2), + VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF16(16), VecF32(8), VecF64(4), + VecI8(64), VecI16(32), VecI32(16), VecI64(8), VecF16(32), VecF32(16), VecF64(8); }, Self::kreg => types! { avx512f: I8, I16; diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index 46c83be9d95f..ecc91ab9a310 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -41,17 +41,13 @@ const RUST_LIB_DIR: &str = "rustlib"; /// /// For example: `target_sysroot_path("/usr", "x86_64-unknown-linux-gnu")` => /// `"lib*/rustlib/x86_64-unknown-linux-gnu"`. -pub fn target_rustlib_path(sysroot: &Path, target_triple: &str) -> PathBuf { - let libdir = find_libdir(sysroot); - PathBuf::from_iter([ - Path::new(libdir.as_ref()), - Path::new(RUST_LIB_DIR), - Path::new(target_triple), - ]) +pub fn relative_target_rustlib_path(sysroot: &Path, target_triple: &str) -> PathBuf { + let libdir = find_relative_libdir(sysroot); + Path::new(libdir.as_ref()).join(RUST_LIB_DIR).join(target_triple) } /// The name of the directory rustc expects libraries to be located. -fn find_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> { +fn find_relative_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> { // FIXME: This is a quick hack to make the rustc binary able to locate // Rust libraries in Linux environments where libraries might be installed // to lib64/lib32. This would be more foolproof by basing the sysroot off diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index c5b2065080b2..055420090835 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -272,6 +272,7 @@ fn macos_default_deployment_target(arch: Arch) -> (u32, u32) { fn macos_deployment_target(arch: Arch) -> (u32, u32) { // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. + // Note: If bumping this version, remember to update it in the rustc/platform-support docs. from_set_deployment_target("MACOSX_DEPLOYMENT_TARGET") .unwrap_or_else(|| macos_default_deployment_target(arch)) } @@ -320,6 +321,7 @@ fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow]> { fn ios_deployment_target(arch: Arch, abi: &str) -> (u32, u32) { // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. + // Note: If bumping this version, remember to update it in the rustc/platform-support docs. let (major, minor) = match (arch, abi) { (Arm64e, _) => (14, 0), // Mac Catalyst defaults to 13.1 in Clang. @@ -352,6 +354,7 @@ pub fn ios_sim_llvm_target(arch: Arch) -> String { fn tvos_deployment_target() -> (u32, u32) { // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. + // Note: If bumping this version, remember to update it in the rustc platform-support docs. from_set_deployment_target("TVOS_DEPLOYMENT_TARGET").unwrap_or((10, 0)) } @@ -367,6 +370,7 @@ pub fn tvos_sim_llvm_target(arch: Arch) -> String { fn watchos_deployment_target() -> (u32, u32) { // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. + // Note: If bumping this version, remember to update it in the rustc platform-support docs. from_set_deployment_target("WATCHOS_DEPLOYMENT_TARGET").unwrap_or((5, 0)) } @@ -382,6 +386,7 @@ pub fn watchos_sim_llvm_target(arch: Arch) -> String { fn visionos_deployment_target() -> (u32, u32) { // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. + // Note: If bumping this version, remember to update it in the rustc platform-support docs. from_set_deployment_target("XROS_DEPLOYMENT_TARGET").unwrap_or((1, 0)) } diff --git a/compiler/rustc_target/src/spec/base/mod.rs b/compiler/rustc_target/src/spec/base/mod.rs index d137aaa53585..28d10dcf2ff3 100644 --- a/compiler/rustc_target/src/spec/base/mod.rs +++ b/compiler/rustc_target/src/spec/base/mod.rs @@ -35,3 +35,4 @@ pub(crate) mod windows_gnullvm; pub(crate) mod windows_msvc; pub(crate) mod windows_uwp_gnu; pub(crate) mod windows_uwp_msvc; +pub(crate) mod xtensa; diff --git a/compiler/rustc_target/src/spec/base/redox.rs b/compiler/rustc_target/src/spec/base/redox.rs index 468fe478549b..9070791e9dfc 100644 --- a/compiler/rustc_target/src/spec/base/redox.rs +++ b/compiler/rustc_target/src/spec/base/redox.rs @@ -1,4 +1,4 @@ -use crate::spec::{cvs, RelroLevel, TargetOptions}; +use crate::spec::{cvs, Cc, LinkerFlavor, Lld, RelroLevel, TargetOptions}; pub fn opts() -> TargetOptions { TargetOptions { @@ -12,6 +12,8 @@ pub fn opts() -> TargetOptions { has_thread_local: true, crt_static_default: true, crt_static_respected: true, + crt_static_allows_dylibs: true, + late_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-lgcc"]), ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/base/xtensa.rs b/compiler/rustc_target/src/spec/base/xtensa.rs new file mode 100644 index 000000000000..31ad09c52e4f --- /dev/null +++ b/compiler/rustc_target/src/spec/base/xtensa.rs @@ -0,0 +1,17 @@ +use crate::abi::Endian; +use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, TargetOptions}; + +pub fn opts() -> TargetOptions { + TargetOptions { + os: "none".into(), + endian: Endian::Little, + c_int_width: "32".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No), + executables: true, + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + atomic_cas: false, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 910c6aeb7d66..42860b1059ed 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1317,6 +1317,34 @@ bitflags::bitflags! { rustc_data_structures::external_bitflags_debug! { SanitizerSet } impl SanitizerSet { + // Taken from LLVM's sanitizer compatibility logic: + // https://github.com/llvm/llvm-project/blob/release/18.x/clang/lib/Driver/SanitizerArgs.cpp#L512 + const MUTUALLY_EXCLUSIVE: &'static [(SanitizerSet, SanitizerSet)] = &[ + (SanitizerSet::ADDRESS, SanitizerSet::MEMORY), + (SanitizerSet::ADDRESS, SanitizerSet::THREAD), + (SanitizerSet::ADDRESS, SanitizerSet::HWADDRESS), + (SanitizerSet::ADDRESS, SanitizerSet::MEMTAG), + (SanitizerSet::ADDRESS, SanitizerSet::KERNELADDRESS), + (SanitizerSet::ADDRESS, SanitizerSet::SAFESTACK), + (SanitizerSet::LEAK, SanitizerSet::MEMORY), + (SanitizerSet::LEAK, SanitizerSet::THREAD), + (SanitizerSet::LEAK, SanitizerSet::KERNELADDRESS), + (SanitizerSet::LEAK, SanitizerSet::SAFESTACK), + (SanitizerSet::MEMORY, SanitizerSet::THREAD), + (SanitizerSet::MEMORY, SanitizerSet::HWADDRESS), + (SanitizerSet::MEMORY, SanitizerSet::KERNELADDRESS), + (SanitizerSet::MEMORY, SanitizerSet::SAFESTACK), + (SanitizerSet::THREAD, SanitizerSet::HWADDRESS), + (SanitizerSet::THREAD, SanitizerSet::KERNELADDRESS), + (SanitizerSet::THREAD, SanitizerSet::SAFESTACK), + (SanitizerSet::HWADDRESS, SanitizerSet::MEMTAG), + (SanitizerSet::HWADDRESS, SanitizerSet::KERNELADDRESS), + (SanitizerSet::HWADDRESS, SanitizerSet::SAFESTACK), + (SanitizerSet::CFI, SanitizerSet::KCFI), + (SanitizerSet::MEMTAG, SanitizerSet::KERNELADDRESS), + (SanitizerSet::KERNELADDRESS, SanitizerSet::SAFESTACK), + ]; + /// Return sanitizer's name /// /// Returns none if the flags is a set of sanitizers numbering not exactly one. @@ -1337,6 +1365,13 @@ impl SanitizerSet { _ => return None, }) } + + pub fn mutually_exclusive(self) -> Option<(SanitizerSet, SanitizerSet)> { + Self::MUTUALLY_EXCLUSIVE + .into_iter() + .find(|&(a, b)| self.contains(*a) && self.contains(*b)) + .copied() + } } /// Formats a sanitizer set as a comma separated list of sanitizers' names. @@ -1612,6 +1647,7 @@ supported_targets! { ("x86_64-unknown-l4re-uclibc", x86_64_unknown_l4re_uclibc), ("aarch64-unknown-redox", aarch64_unknown_redox), + ("i686-unknown-redox", i686_unknown_redox), ("x86_64-unknown-redox", x86_64_unknown_redox), ("i386-apple-ios", i386_apple_ios), @@ -1731,6 +1767,10 @@ supported_targets! { ("nvptx64-nvidia-cuda", nvptx64_nvidia_cuda), + ("xtensa-esp32-none-elf", xtensa_esp32_none_elf), + ("xtensa-esp32s2-none-elf", xtensa_esp32s2_none_elf), + ("xtensa-esp32s3-none-elf", xtensa_esp32s3_none_elf), + ("i686-wrs-vxworks", i686_wrs_vxworks), ("x86_64-wrs-vxworks", x86_64_wrs_vxworks), ("armv7-wrs-vxworks-eabihf", armv7_wrs_vxworks_eabihf), @@ -3332,7 +3372,7 @@ impl Target { // Additionally look in the sysroot under `lib/rustlib//target.json` // as a fallback. - let rustlib_path = crate::target_rustlib_path(sysroot, target_triple); + let rustlib_path = crate::relative_target_rustlib_path(sysroot, target_triple); let p = PathBuf::from_iter([ Path::new(sysroot), Path::new(&rustlib_path), diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_redox.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_redox.rs new file mode 100644 index 000000000000..83252fadb78e --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_redox.rs @@ -0,0 +1,27 @@ +use crate::spec::{base, Cc, LinkerFlavor, Lld, StackProbeType, Target}; + +pub fn target() -> Target { + let mut base = base::redox::opts(); + base.cpu = "pentiumpro".into(); + base.plt_by_default = false; + base.max_atomic_width = Some(64); + base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32"]); + // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved + base.stack_probes = StackProbeType::Call; + + Target { + llvm_target: "i686-unknown-redox".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: None, + host_tools: None, + std: None, + }, + pointer_width: 32, + data_layout: + "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128" + .into(), + arch: "x86".into(), + options: base, + } +} diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs index f15a87989400..3e88180012e5 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs @@ -10,7 +10,7 @@ pub fn target() -> Target { std: None, }, pointer_width: 64, - data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(), + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), arch: "loongarch64".into(), options: TargetOptions { cpu: "generic".into(), diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs index 032a29708d14..c45bc4383500 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs @@ -10,7 +10,7 @@ pub fn target() -> Target { std: None, }, pointer_width: 64, - data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(), + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), arch: "loongarch64".into(), options: TargetOptions { cpu: "generic".into(), diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs index b2bfc8e42723..69533e82c3ce 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs @@ -11,7 +11,7 @@ pub fn target() -> Target { std: None, }, pointer_width: 64, - data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(), + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), arch: "loongarch64".into(), options: TargetOptions { cpu: "generic".into(), diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs index 02e44c187abe..bc4c5bcde5ed 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs @@ -11,7 +11,7 @@ pub fn target() -> Target { std: None, }, pointer_width: 64, - data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(), + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), arch: "loongarch64".into(), options: TargetOptions { cpu: "generic".into(), diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs index 7cbe9f09e6ca..4c2d222b590e 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs @@ -18,6 +18,7 @@ pub fn target() -> Target { let mut options = base::wasm::options(); options.os = "wasi".into(); + options.env = "p1".into(); options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::Yes), &["--target=wasm32-wasi"]); options.pre_link_objects_self_contained = crt_objects::pre_wasi_self_contained(); diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs index c592b944d44a..38af48ab2665 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs @@ -13,6 +13,7 @@ pub fn target() -> Target { let mut options = base::wasm::options(); options.os = "wasi".into(); + options.env = "p1".into(); options.add_pre_link_args( LinkerFlavor::WasmLld(Cc::No), diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32_none_elf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32_none_elf.rs new file mode 100644 index 000000000000..bf5237163139 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32_none_elf.rs @@ -0,0 +1,24 @@ +use crate::spec::{base::xtensa, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "xtensa-none-elf".into(), + pointer_width: 32, + data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(), + arch: "xtensa".into(), + metadata: crate::spec::TargetMetadata { + description: Some("Xtensa ESP32".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + + options: TargetOptions { + cpu: "esp32".into(), + linker: Some("xtensa-esp32-elf-gcc".into()), + max_atomic_width: Some(32), + atomic_cas: true, + ..xtensa::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_none_elf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_none_elf.rs new file mode 100644 index 000000000000..219b2aa48c1f --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_none_elf.rs @@ -0,0 +1,23 @@ +use crate::spec::{base::xtensa, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "xtensa-none-elf".into(), + pointer_width: 32, + data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(), + arch: "xtensa".into(), + metadata: crate::spec::TargetMetadata { + description: Some("Xtensa ESP32-S2".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + + options: TargetOptions { + cpu: "esp32-s2".into(), + linker: Some("xtensa-esp32s2-elf-gcc".into()), + max_atomic_width: Some(32), + ..xtensa::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_none_elf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_none_elf.rs new file mode 100644 index 000000000000..632eef3a5842 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_none_elf.rs @@ -0,0 +1,24 @@ +use crate::spec::{base::xtensa, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "xtensa-none-elf".into(), + pointer_width: 32, + data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(), + arch: "xtensa".into(), + metadata: crate::spec::TargetMetadata { + description: Some("Xtensa ESP32-S3".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + + options: TargetOptions { + cpu: "esp32-s3".into(), + linker: Some("xtensa-esp32s3-elf-gcc".into()), + max_atomic_width: Some(32), + atomic_cas: true, + ..xtensa::opts() + }, + } +} diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 1b507bb2a155..d9812540e497 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -199,11 +199,9 @@ const X86_ALLOWED_FEATURES: &[(&str, Stability)] = &[ ("avx512bw", Unstable(sym::avx512_target_feature)), ("avx512cd", Unstable(sym::avx512_target_feature)), ("avx512dq", Unstable(sym::avx512_target_feature)), - ("avx512er", Unstable(sym::avx512_target_feature)), ("avx512f", Unstable(sym::avx512_target_feature)), ("avx512fp16", Unstable(sym::avx512_target_feature)), ("avx512ifma", Unstable(sym::avx512_target_feature)), - ("avx512pf", Unstable(sym::avx512_target_feature)), ("avx512vbmi", Unstable(sym::avx512_target_feature)), ("avx512vbmi2", Unstable(sym::avx512_target_feature)), ("avx512vl", Unstable(sym::avx512_target_feature)), diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index b442446f79ba..a46cba35b2d2 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,6 +1,6 @@ use crate::fluent_generated as fluent; use rustc_errors::{ - codes::*, Applicability, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level, + codes::*, Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, SubdiagMessageOp, Subdiagnostic, }; use rustc_macros::{Diagnostic, Subdiagnostic}; @@ -59,7 +59,7 @@ pub struct NegativePositiveConflict<'tcx> { impl Diagnostic<'_, G> for NegativePositiveConflict<'_> { #[track_caller] - fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> { + fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let mut diag = Diag::new(dcx, level, fluent::trait_selection_negative_positive_conflict); diag.arg("trait_desc", self.trait_desc.print_only_trait_path().to_string()); diag.arg("self_desc", self.self_ty.map_or_else(|| "none".to_string(), |ty| ty.to_string())); diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index fc852293dff0..c95649e2ffb8 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -1,14 +1,12 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::{self, ObligationCtxt, SelectionContext}; +use crate::traits::{self, Obligation, ObligationCause, ObligationCtxt, SelectionContext}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_infer::traits::Obligation; use rustc_macros::extension; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse}; use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_middle::ty::{GenericArg, Upcast}; use rustc_span::DUMMY_SP; @@ -94,7 +92,7 @@ impl<'tcx> InferCtxt<'tcx> { ty::TraitRef::new(self.tcx, trait_def_id, [ty]), )) { Ok(Some(selection)) => { - let ocx = ObligationCtxt::new(self); + let ocx = ObligationCtxt::new_with_diagnostics(self); ocx.register_obligations(selection.nested_obligations()); Some(ocx.select_all_or_error()) } diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 521e4ef0c9ed..50c618bb3bde 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -10,12 +10,12 @@ //! //! This API is completely unstable and subject to change. -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![doc(rust_logo)] -#![feature(rustdoc_internals)] +// tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![doc(rust_logo)] #![feature(assert_matches)] #![feature(associated_type_defaults)] #![feature(box_patterns)] @@ -23,10 +23,11 @@ #![feature(extract_if)] #![feature(if_let_guard)] #![feature(let_chains)] -#![feature(option_take_if)] #![feature(never_type)] +#![feature(rustdoc_internals)] #![feature(type_alias_impl_trait)] #![recursion_limit = "512"] // For rustdoc +// tidy-alphabetical-end #[macro_use] extern crate tracing; diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 5e0d7da4f06b..5f986e22f513 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -1,3 +1,4 @@ +use crate::traits::ScrubbedTraitError; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{InferCtxt, RegionResolutionError}; use rustc_macros::extension; @@ -27,7 +28,7 @@ impl<'tcx> InferCtxt<'tcx> { ), ty, ) - .map_err(|_| NoSolution) + .map_err(|_: Vec>| NoSolution) } else { Ok(ty) } diff --git a/compiler/rustc_trait_selection/src/solve.rs b/compiler/rustc_trait_selection/src/solve.rs new file mode 100644 index 000000000000..a7c8cc5a32b6 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve.rs @@ -0,0 +1,12 @@ +pub use rustc_next_trait_solver::solve::*; + +mod fulfill; +mod infcx; +pub mod inspect; +mod normalize; +mod select; + +pub use fulfill::{FulfillmentCtxt, NextSolverError}; +pub(crate) use normalize::deeply_normalize_for_diagnostics; +pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes}; +pub use select::InferCtxtSelectExt; diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 4933080451da..8937ed467a1f 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -1,3 +1,4 @@ +use std::marker::PhantomData; use std::mem; use std::ops::ControlFlow; @@ -5,17 +6,20 @@ use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause}; use rustc_infer::traits::{ - self, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation, - ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError, TraitEngine, + self, FromSolverError, MismatchedProjectionTypes, Obligation, ObligationCause, + ObligationCauseCode, PredicateObligation, SelectionError, TraitEngine, }; use rustc_middle::bug; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, TyCtxt}; +use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _}; use rustc_span::symbol::sym; -use super::eval_ctxt::GenerateProofTree; +use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError}; + +use super::infcx::SolverDelegate; use super::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor}; -use super::{Certainty, InferCtxtEvalExt}; +use super::Certainty; /// A trait engine using the new trait solver. /// @@ -28,7 +32,7 @@ use super::{Certainty, InferCtxtEvalExt}; /// /// It is also likely that we want to use slightly different datastructures /// here as this will have to deal with far more root goals than `evaluate_all`. -pub struct FulfillmentCtxt<'tcx> { +pub struct FulfillmentCtxt<'tcx, E: 'tcx> { obligations: ObligationStorage<'tcx>, /// The snapshot in which this context was created. Using the context @@ -36,6 +40,7 @@ pub struct FulfillmentCtxt<'tcx> { /// gets rolled back. Because of this we explicitly check that we only /// use the context in exactly this snapshot. usable_in_snapshot: usize, + _errors: PhantomData, } #[derive(Default)] @@ -79,7 +84,9 @@ impl<'tcx> ObligationStorage<'tcx> { // change. self.overflowed.extend(self.pending.extract_if(|o| { let goal = o.clone().into(); - let result = infcx.evaluate_root_goal(goal, GenerateProofTree::Never).0; + let result = <&SolverDelegate<'tcx>>::from(infcx) + .evaluate_root_goal(goal, GenerateProofTree::No) + .0; match result { Ok((has_changed, _)) => has_changed, _ => false, @@ -89,8 +96,8 @@ impl<'tcx> ObligationStorage<'tcx> { } } -impl<'tcx> FulfillmentCtxt<'tcx> { - pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> { +impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> { + pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx, E> { assert!( infcx.next_trait_solver(), "new trait solver fulfillment context created when \ @@ -99,6 +106,7 @@ impl<'tcx> FulfillmentCtxt<'tcx> { FulfillmentCtxt { obligations: Default::default(), usable_in_snapshot: infcx.num_open_snapshots(), + _errors: PhantomData, } } @@ -118,7 +126,10 @@ impl<'tcx> FulfillmentCtxt<'tcx> { } } -impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { +impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E> +where + E: FromSolverError<'tcx, NextSolverError<'tcx>>, +{ #[instrument(level = "trace", skip(self, infcx))] fn register_predicate_obligation( &mut self, @@ -129,24 +140,22 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { self.obligations.register(obligation); } - fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec> { - let mut errors: Vec<_> = self - .obligations + fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { + self.obligations .pending .drain(..) - .map(|obligation| fulfillment_error_for_stalled(infcx, obligation)) - .collect(); - - errors.extend(self.obligations.overflowed.drain(..).map(|obligation| FulfillmentError { - obligation: find_best_leaf_obligation(infcx, &obligation, true), - code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) }, - root_obligation: obligation, - })); - - errors + .map(|obligation| NextSolverError::Ambiguity(obligation)) + .chain( + self.obligations + .overflowed + .drain(..) + .map(|obligation| NextSolverError::Overflow(obligation)), + ) + .map(|e| E::from_solver_error(infcx, e)) + .collect() } - fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec> { + fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); let mut errors = Vec::new(); for i in 0.. { @@ -159,12 +168,17 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { let mut has_changed = false; for obligation in self.obligations.unstalled_for_select() { let goal = obligation.clone().into(); - let result = infcx.evaluate_root_goal(goal, GenerateProofTree::IfEnabled).0; + let result = <&SolverDelegate<'tcx>>::from(infcx) + .evaluate_root_goal(goal, GenerateProofTree::No) + .0; self.inspect_evaluated_obligation(infcx, &obligation, &result); let (changed, certainty) = match result { Ok(result) => result, Err(NoSolution) => { - errors.push(fulfillment_error_for_no_solution(infcx, obligation)); + errors.push(E::from_solver_error( + infcx, + NextSolverError::TrueError(obligation), + )); continue; } }; @@ -195,6 +209,39 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { } } +pub enum NextSolverError<'tcx> { + TrueError(PredicateObligation<'tcx>), + Ambiguity(PredicateObligation<'tcx>), + Overflow(PredicateObligation<'tcx>), +} + +impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> { + fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self { + match error { + NextSolverError::TrueError(obligation) => { + fulfillment_error_for_no_solution(infcx, obligation) + } + NextSolverError::Ambiguity(obligation) => { + fulfillment_error_for_stalled(infcx, obligation) + } + NextSolverError::Overflow(obligation) => { + fulfillment_error_for_overflow(infcx, obligation) + } + } + } +} + +impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> { + fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self { + match error { + NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError, + NextSolverError::Ambiguity(_) | NextSolverError::Overflow(_) => { + ScrubbedTraitError::Ambiguity + } + } + } +} + fn fulfillment_error_for_no_solution<'tcx>( infcx: &InferCtxt<'tcx>, root_obligation: PredicateObligation<'tcx>, @@ -246,7 +293,10 @@ fn fulfillment_error_for_stalled<'tcx>( root_obligation: PredicateObligation<'tcx>, ) -> FulfillmentError<'tcx> { let (code, refine_obligation) = infcx.probe(|_| { - match infcx.evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::Never).0 { + match <&SolverDelegate<'tcx>>::from(infcx) + .evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::No) + .0 + { Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => { (FulfillmentErrorCode::Ambiguity { overflow: None }, true) } @@ -280,6 +330,17 @@ fn fulfillment_error_for_stalled<'tcx>( } } +fn fulfillment_error_for_overflow<'tcx>( + infcx: &InferCtxt<'tcx>, + root_obligation: PredicateObligation<'tcx>, +) -> FulfillmentError<'tcx> { + FulfillmentError { + obligation: find_best_leaf_obligation(infcx, &root_obligation, true), + code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) }, + root_obligation, + } +} + fn find_best_leaf_obligation<'tcx>( infcx: &InferCtxt<'tcx>, obligation: &PredicateObligation<'tcx>, @@ -380,7 +441,10 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { source: CandidateSource::Impl(impl_def_id), result: _, } = candidate.kind() - && goal.infcx().tcx.has_attr(impl_def_id, sym::do_not_recommend) + && goal + .infcx() + .tcx + .has_attrs_with_path(impl_def_id, &[sym::diagnostic, sym::do_not_recommend]) { return ControlFlow::Break(self.obligation.clone()); } @@ -404,9 +468,10 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { polarity: ty::PredicatePolarity::Positive, })) } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => { - ChildMode::WellFormedObligation - } + ty::PredicateKind::Clause( + ty::ClauseKind::WellFormed(_) | ty::ClauseKind::Projection(..), + ) + | ty::PredicateKind::AliasRelate(..) => ChildMode::PassThrough, _ => { return ControlFlow::Break(self.obligation.clone()); } @@ -440,7 +505,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { (_, GoalSource::InstantiateHigherRanked) => { obligation = self.obligation.clone(); } - (ChildMode::WellFormedObligation, _) => { + (ChildMode::PassThrough, _) => { obligation = make_obligation(self.obligation.cause.clone()); } } @@ -458,6 +523,32 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; } + // alias-relate may fail because the lhs or rhs can't be normalized, + // and therefore is treated as rigid. + if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() { + if let Some(obligation) = goal + .infcx() + .visit_proof_tree_at_depth( + goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())), + goal.depth() + 1, + self, + ) + .break_value() + { + return ControlFlow::Break(obligation); + } else if let Some(obligation) = goal + .infcx() + .visit_proof_tree_at_depth( + goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())), + goal.depth() + 1, + self, + ) + .break_value() + { + return ControlFlow::Break(obligation); + } + } + ControlFlow::Break(self.obligation.clone()) } } @@ -471,7 +562,7 @@ enum ChildMode<'tcx> { // Skip trying to derive an `ObligationCause` from this obligation, and // report *all* sub-obligations as if they came directly from the parent // obligation. - WellFormedObligation, + PassThrough, } fn derive_cause<'tcx>( diff --git a/compiler/rustc_trait_selection/src/solve/infcx.rs b/compiler/rustc_trait_selection/src/solve/infcx.rs new file mode 100644 index 000000000000..e574166cbfc2 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/infcx.rs @@ -0,0 +1,435 @@ +use std::ops::Deref; + +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_infer::infer::canonical::query_response::make_query_region_constraints; +use rustc_infer::infer::canonical::{ + Canonical, CanonicalExt as _, CanonicalVarInfo, CanonicalVarValues, +}; +use rustc_infer::infer::{ + BoundRegionConversionTime, InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt, +}; +use rustc_infer::traits::solve::Goal; +use rustc_infer::traits::util::supertraits; +use rustc_infer::traits::{ObligationCause, Reveal}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt as _}; +use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; +use rustc_type_ir::relate::Relate; +use rustc_type_ir::solve::{Certainty, NoSolution, SolverMode}; + +use crate::traits::coherence::trait_ref_is_knowable; +use crate::traits::specialization_graph; + +#[repr(transparent)] +pub struct SolverDelegate<'tcx>(InferCtxt<'tcx>); + +impl<'a, 'tcx> From<&'a InferCtxt<'tcx>> for &'a SolverDelegate<'tcx> { + fn from(infcx: &'a InferCtxt<'tcx>) -> Self { + // SAFETY: `repr(transparent)` + unsafe { std::mem::transmute(infcx) } + } +} + +impl<'tcx> Deref for SolverDelegate<'tcx> { + type Target = InferCtxt<'tcx>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'tcx> rustc_next_trait_solver::infcx::SolverDelegate for SolverDelegate<'tcx> { + type Interner = TyCtxt<'tcx>; + + fn interner(&self) -> TyCtxt<'tcx> { + self.0.tcx + } + + type Span = Span; + + fn solver_mode(&self) -> ty::solve::SolverMode { + match self.intercrate { + true => SolverMode::Coherence, + false => SolverMode::Normal, + } + } + + fn build_with_canonical( + interner: TyCtxt<'tcx>, + solver_mode: SolverMode, + canonical: &Canonical<'tcx, V>, + ) -> (Self, V, CanonicalVarValues<'tcx>) + where + V: TypeFoldable>, + { + let (infcx, value, vars) = interner + .infer_ctxt() + .with_next_trait_solver(true) + .intercrate(match solver_mode { + SolverMode::Normal => false, + SolverMode::Coherence => true, + }) + .build_with_canonical(DUMMY_SP, canonical); + (SolverDelegate(infcx), value, vars) + } + + fn universe(&self) -> ty::UniverseIndex { + self.0.universe() + } + + fn create_next_universe(&self) -> ty::UniverseIndex { + self.0.create_next_universe() + } + + fn universe_of_ty(&self, vid: ty::TyVid) -> Option { + // FIXME(BoxyUwU): this is kind of jank and means that printing unresolved + // ty infers will give you the universe of the var it resolved to not the universe + // it actually had. It also means that if you have a `?0.1` and infer it to `u8` then + // try to print out `?0.1` it will just print `?0`. + match self.0.probe_ty_var(vid) { + Err(universe) => Some(universe), + Ok(_) => None, + } + } + + fn universe_of_lt(&self, lt: ty::RegionVid) -> Option { + match self.0.inner.borrow_mut().unwrap_region_constraints().probe_value(lt) { + Err(universe) => Some(universe), + Ok(_) => None, + } + } + + fn universe_of_ct(&self, ct: ty::ConstVid) -> Option { + // Same issue as with `universe_of_ty` + match self.0.probe_const_var(ct) { + Err(universe) => Some(universe), + Ok(_) => None, + } + } + + fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid { + self.0.root_var(var) + } + + fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid { + self.0.root_const_var(var) + } + + fn opportunistic_resolve_ty_var(&self, vid: ty::TyVid) -> Ty<'tcx> { + match self.0.probe_ty_var(vid) { + Ok(ty) => ty, + Err(_) => Ty::new_var(self.0.tcx, self.0.root_var(vid)), + } + } + + fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> Ty<'tcx> { + self.0.opportunistic_resolve_int_var(vid) + } + + fn opportunistic_resolve_float_var(&self, vid: ty::FloatVid) -> Ty<'tcx> { + self.0.opportunistic_resolve_float_var(vid) + } + + fn opportunistic_resolve_ct_var(&self, vid: ty::ConstVid) -> ty::Const<'tcx> { + match self.0.probe_const_var(vid) { + Ok(ct) => ct, + Err(_) => ty::Const::new_var(self.0.tcx, self.0.root_const_var(vid)), + } + } + + fn opportunistic_resolve_effect_var(&self, vid: ty::EffectVid) -> ty::Const<'tcx> { + match self.0.probe_effect_var(vid) { + Some(ct) => ct, + None => ty::Const::new_infer( + self.0.tcx, + ty::InferConst::EffectVar(self.0.root_effect_var(vid)), + ), + } + } + + fn opportunistic_resolve_lt_var(&self, vid: ty::RegionVid) -> ty::Region<'tcx> { + self.0 + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.0.tcx, vid) + } + + fn defining_opaque_types(&self) -> &'tcx ty::List { + self.0.defining_opaque_types() + } + + fn next_ty_infer(&self) -> Ty<'tcx> { + self.0.next_ty_var(DUMMY_SP) + } + + fn next_const_infer(&self) -> ty::Const<'tcx> { + self.0.next_const_var(DUMMY_SP) + } + + fn fresh_args_for_item(&self, def_id: DefId) -> ty::GenericArgsRef<'tcx> { + self.0.fresh_args_for_item(DUMMY_SP, def_id) + } + + fn fresh_var_for_kind_with_span( + &self, + arg: ty::GenericArg<'tcx>, + span: Span, + ) -> ty::GenericArg<'tcx> { + match arg.unpack() { + ty::GenericArgKind::Lifetime(_) => { + self.next_region_var(RegionVariableOrigin::MiscVariable(span)).into() + } + ty::GenericArgKind::Type(_) => self.next_ty_var(span).into(), + ty::GenericArgKind::Const(_) => self.next_const_var(span).into(), + } + } + + fn instantiate_binder_with_infer> + Copy>( + &self, + value: ty::Binder<'tcx, T>, + ) -> T { + self.0.instantiate_binder_with_fresh_vars( + DUMMY_SP, + BoundRegionConversionTime::HigherRankedType, + value, + ) + } + + fn enter_forall> + Copy, U>( + &self, + value: ty::Binder<'tcx, T>, + f: impl FnOnce(T) -> U, + ) -> U { + self.0.enter_forall(value, f) + } + + fn relate>>( + &self, + param_env: ty::ParamEnv<'tcx>, + lhs: T, + variance: ty::Variance, + rhs: T, + ) -> Result>>, NoSolution> { + self.0.at(&ObligationCause::dummy(), param_env).relate_no_trace(lhs, variance, rhs) + } + + fn eq_structurally_relating_aliases>>( + &self, + param_env: ty::ParamEnv<'tcx>, + lhs: T, + rhs: T, + ) -> Result>>, NoSolution> { + self.0 + .at(&ObligationCause::dummy(), param_env) + .eq_structurally_relating_aliases_no_trace(lhs, rhs) + } + + fn resolve_vars_if_possible(&self, value: T) -> T + where + T: TypeFoldable>, + { + self.0.resolve_vars_if_possible(value) + } + + fn probe(&self, probe: impl FnOnce() -> T) -> T { + self.0.probe(|_| probe()) + } + + fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution> { + self.0.leak_check(max_input_universe, None).map_err(|_| NoSolution) + } + + fn elaborate_supertraits( + interner: TyCtxt<'tcx>, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + ) -> impl Iterator>> { + supertraits(interner, trait_ref) + } + + fn try_const_eval_resolve( + &self, + param_env: ty::ParamEnv<'tcx>, + unevaluated: ty::UnevaluatedConst<'tcx>, + ) -> Option> { + use rustc_middle::mir::interpret::ErrorHandled; + match self.const_eval_resolve(param_env, unevaluated, DUMMY_SP) { + Ok(Some(val)) => Some(ty::Const::new_value( + self.tcx, + val, + self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args), + )), + Ok(None) | Err(ErrorHandled::TooGeneric(_)) => None, + Err(ErrorHandled::Reported(e, _)) => Some(ty::Const::new_error(self.tcx, e.into())), + } + } + + fn sub_regions(&self, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>) { + self.0.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), sub, sup) + } + + fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>) { + self.0.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy()); + } + + fn well_formed_goals( + &self, + param_env: ty::ParamEnv<'tcx>, + arg: ty::GenericArg<'tcx>, + ) -> Option>>> { + crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg).map(|obligations| { + obligations.into_iter().map(|obligation| obligation.into()).collect() + }) + } + + fn clone_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { + self.0.clone_opaque_types_for_query_response() + } + + fn make_deduplicated_outlives_constraints( + &self, + ) -> Vec>> { + // Cannot use `take_registered_region_obligations` as we may compute the response + // inside of a `probe` whenever we have multiple choices inside of the solver. + let region_obligations = self.0.inner.borrow().region_obligations().to_owned(); + let region_constraints = self.0.with_region_constraints(|region_constraints| { + make_query_region_constraints( + self.tcx, + region_obligations + .iter() + .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())), + region_constraints, + ) + }); + + assert_eq!(region_constraints.member_constraints, vec![]); + + let mut seen = FxHashSet::default(); + region_constraints + .outlives + .into_iter() + .filter(|&(outlives, _)| seen.insert(outlives)) + .map(|(outlives, _)| outlives) + .collect() + } + + fn instantiate_canonical( + &self, + canonical: Canonical<'tcx, V>, + values: CanonicalVarValues<'tcx>, + ) -> V + where + V: TypeFoldable>, + { + canonical.instantiate(self.tcx, &values) + } + + fn instantiate_canonical_var_with_infer( + &self, + cv_info: CanonicalVarInfo<'tcx>, + universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, + ) -> ty::GenericArg<'tcx> { + self.0.instantiate_canonical_var(DUMMY_SP, cv_info, universe_map) + } + + fn insert_hidden_type( + &self, + opaque_type_key: ty::OpaqueTypeKey<'tcx>, + param_env: ty::ParamEnv<'tcx>, + hidden_ty: Ty<'tcx>, + goals: &mut Vec>>, + ) -> Result<(), NoSolution> { + self.0 + .insert_hidden_type(opaque_type_key, DUMMY_SP, param_env, hidden_ty, goals) + .map_err(|_| NoSolution) + } + + fn add_item_bounds_for_hidden_type( + &self, + def_id: DefId, + args: ty::GenericArgsRef<'tcx>, + param_env: ty::ParamEnv<'tcx>, + hidden_ty: Ty<'tcx>, + goals: &mut Vec>>, + ) { + self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals); + } + + fn inject_new_hidden_type_unchecked(&self, key: ty::OpaqueTypeKey<'tcx>, hidden_ty: Ty<'tcx>) { + self.0.inject_new_hidden_type_unchecked( + key, + ty::OpaqueHiddenType { ty: hidden_ty, span: DUMMY_SP }, + ) + } + + fn reset_opaque_types(&self) { + let _ = self.take_opaque_types(); + } + + fn trait_ref_is_knowable( + &self, + trait_ref: ty::TraitRef<'tcx>, + lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result, E>, + ) -> Result { + trait_ref_is_knowable(&self.0, trait_ref, lazily_normalize_ty) + .map(|is_knowable| is_knowable.is_ok()) + } + + fn fetch_eligible_assoc_item( + &self, + param_env: ty::ParamEnv<'tcx>, + goal_trait_ref: ty::TraitRef<'tcx>, + trait_assoc_def_id: DefId, + impl_def_id: DefId, + ) -> Result, NoSolution> { + let node_item = specialization_graph::assoc_def(self.tcx, impl_def_id, trait_assoc_def_id) + .map_err(|ErrorGuaranteed { .. }| NoSolution)?; + + let eligible = if node_item.is_final() { + // Non-specializable items are always projectable. + true + } else { + // Only reveal a specializable default if we're past type-checking + // and the obligation is monomorphic, otherwise passes such as + // transmute checking and polymorphic MIR optimizations could + // get a result which isn't correct for all monomorphizations. + if param_env.reveal() == Reveal::All { + let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref); + !poly_trait_ref.still_further_specializable() + } else { + trace!(?node_item.item.def_id, "not eligible due to default"); + false + } + }; + + // FIXME: Check for defaultness here may cause diagnostics problems. + if eligible { Ok(Some(node_item.item.def_id)) } else { Ok(None) } + } + + fn is_transmutable( + &self, + param_env: ty::ParamEnv<'tcx>, + dst: Ty<'tcx>, + src: Ty<'tcx>, + assume: ty::Const<'tcx>, + ) -> Result { + // Erase regions because we compute layouts in `rustc_transmute`, + // which will ICE for region vars. + let (dst, src) = self.tcx.erase_regions((dst, src)); + + let Some(assume) = rustc_transmute::Assume::from_const(self.tcx, param_env, assume) else { + return Err(NoSolution); + }; + + // FIXME(transmutability): This really should be returning nested goals for `Answer::If*` + match rustc_transmute::TransmuteTypeEnv::new(&self.0).is_transmutable( + ObligationCause::dummy(), + rustc_transmute::Types { src, dst }, + assume, + ) { + rustc_transmute::Answer::Yes => Ok(Certainty::Yes), + rustc_transmute::Answer::No(_) | rustc_transmute::Answer::If(_) => Err(NoSolution), + } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs new file mode 100644 index 000000000000..f100a8c2ff0e --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/inspect.rs @@ -0,0 +1,4 @@ +pub use rustc_next_trait_solver::solve::inspect::*; + +mod analyse; +pub use analyse::*; diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index e12c66b69285..cb621487125f 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -11,20 +11,18 @@ use rustc_ast_ir::try_visit; use rustc_ast_ir::visit::VisitorResult; -use rustc_infer::infer::resolve::EagerResolver; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_macros::extension; -use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::solve::{inspect, QueryResult}; -use rustc_middle::traits::solve::{Certainty, Goal}; +use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, NoSolution, QueryResult}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{TyCtxt, TypeFoldable}; use rustc_middle::{bug, ty}; +use rustc_next_trait_solver::resolve::EagerResolver; +use rustc_next_trait_solver::solve::inspect::{self, instantiate_canonical_state}; +use rustc_next_trait_solver::solve::{GenerateProofTree, MaybeCause, SolverDelegateEvalExt as _}; use rustc_span::{Span, DUMMY_SP}; -use crate::solve::eval_ctxt::canonical; -use crate::solve::{EvalCtxt, GoalEvaluationKind, GoalSource}; -use crate::solve::{GenerateProofTree, InferCtxtEvalExt}; +use crate::solve::infcx::SolverDelegate; use crate::traits::ObligationCtxt; pub struct InspectConfig { @@ -32,7 +30,7 @@ pub struct InspectConfig { } pub struct InspectGoal<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, + infcx: &'a SolverDelegate<'tcx>, depth: usize, orig_values: Vec>, goal: Goal<'tcx, ty::Predicate<'tcx>>, @@ -89,10 +87,8 @@ impl<'tcx> NormalizesToTermHack<'tcx> { pub struct InspectCandidate<'a, 'tcx> { goal: &'a InspectGoal<'a, 'tcx>, kind: inspect::ProbeKind>, - nested_goals: - Vec<(GoalSource, inspect::CanonicalState, Goal<'tcx, ty::Predicate<'tcx>>>)>, + steps: Vec<&'a inspect::ProbeStep>>, final_state: inspect::CanonicalState, ()>, - impl_args: Option, ty::GenericArgsRef<'tcx>>>, result: QueryResult<'tcx>, shallow_certainty: Certainty, } @@ -148,7 +144,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { #[instrument( level = "debug", skip_all, - fields(goal = ?self.goal.goal, nested_goals = ?self.nested_goals) + fields(goal = ?self.goal.goal, steps = ?self.steps) )] pub fn instantiate_nested_goals_and_opt_impl_args( &self, @@ -157,41 +153,31 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { let infcx = self.goal.infcx; let param_env = self.goal.goal.param_env; let mut orig_values = self.goal.orig_values.to_vec(); - let instantiated_goals: Vec<_> = self - .nested_goals - .iter() - .map(|(source, goal)| { - ( - *source, - canonical::instantiate_canonical_state( + + let mut instantiated_goals = vec![]; + let mut opt_impl_args = None; + for step in &self.steps { + match **step { + inspect::ProbeStep::AddGoal(source, goal) => instantiated_goals.push(( + source, + instantiate_canonical_state(infcx, span, param_env, &mut orig_values, goal), + )), + inspect::ProbeStep::RecordImplArgs { impl_args } => { + opt_impl_args = Some(instantiate_canonical_state( infcx, span, param_env, &mut orig_values, - *goal, - ), - ) - }) - .collect(); + impl_args, + )); + } + inspect::ProbeStep::MakeCanonicalResponse { .. } + | inspect::ProbeStep::NestedProbe(_) => unreachable!(), + } + } - let () = canonical::instantiate_canonical_state( - infcx, - span, - param_env, - &mut orig_values, - self.final_state, - ); - - let impl_args = self.impl_args.map(|impl_args| { - canonical::instantiate_canonical_state( - infcx, - span, - param_env, - &mut orig_values, - impl_args, - ) - .fold_with(&mut EagerResolver::new(infcx)) - }); + let () = + instantiate_canonical_state(infcx, span, param_env, &mut orig_values, self.final_state); if let Some(term_hack) = self.goal.normalizes_to_term_hack { // FIXME: We ignore the expected term of `NormalizesTo` goals @@ -200,13 +186,16 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { let _ = term_hack.constrain(infcx, span, param_env); } + let opt_impl_args = + opt_impl_args.map(|impl_args| impl_args.fold_with(&mut EagerResolver::new(infcx))); + let goals = instantiated_goals .into_iter() .map(|(source, goal)| match goal.predicate.kind().no_bound_vars() { Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => { let unconstrained_term = match term.unpack() { ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(), - ty::TermKind::Const(ct) => infcx.next_const_var(ct.ty(), span).into(), + ty::TermKind::Const(_) => infcx.next_const_var(span).into(), }; let goal = goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term }); @@ -217,16 +206,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { // instantiating the candidate it is already constrained to the result of another // candidate. let proof_tree = infcx - .probe(|_| { - EvalCtxt::enter_root(infcx, GenerateProofTree::Yes, |ecx| { - ecx.evaluate_goal_raw( - GoalEvaluationKind::Root, - GoalSource::Misc, - goal, - ) - }) - }) - .1; + .probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1); InspectGoal::new( infcx, self.goal.depth + 1, @@ -249,7 +229,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { }) .collect(); - (goals, impl_args) + (goals, opt_impl_args) } /// Visit all nested goals of this candidate, rolling back @@ -276,20 +256,28 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { self.source } + pub fn depth(&self) -> usize { + self.depth + } + fn candidates_recur( &'a self, candidates: &mut Vec>, - nested_goals: &mut Vec<( - GoalSource, - inspect::CanonicalState, Goal<'tcx, ty::Predicate<'tcx>>>, - )>, - probe: &inspect::Probe>, + steps: &mut Vec<&'a inspect::ProbeStep>>, + probe: &'a inspect::Probe>, ) { let mut shallow_certainty = None; - let mut impl_args = None; for step in &probe.steps { match *step { - inspect::ProbeStep::AddGoal(source, goal) => nested_goals.push((source, goal)), + inspect::ProbeStep::AddGoal(..) | inspect::ProbeStep::RecordImplArgs { .. } => { + steps.push(step) + } + inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => { + assert!(matches!( + shallow_certainty.replace(c), + None | Some(Certainty::Maybe(MaybeCause::Ambiguity)) + )); + } inspect::ProbeStep::NestedProbe(ref probe) => { match probe.kind { // These never assemble candidates for the goal we're trying to solve. @@ -305,19 +293,12 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { // Nested probes have to prove goals added in their parent // but do not leak them, so we truncate the added goals // afterwards. - let num_goals = nested_goals.len(); - self.candidates_recur(candidates, nested_goals, probe); - nested_goals.truncate(num_goals); + let num_steps = steps.len(); + self.candidates_recur(candidates, steps, probe); + steps.truncate(num_steps); } } } - inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => { - assert_eq!(shallow_certainty.replace(c), None); - } - inspect::ProbeStep::RecordImplArgs { impl_args: i } => { - assert_eq!(impl_args.replace(i), None); - } - inspect::ProbeStep::EvaluateGoals(_) => (), } } @@ -339,11 +320,10 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { candidates.push(InspectCandidate { goal: self, kind: probe.kind, - nested_goals: nested_goals.clone(), + steps: steps.clone(), final_state: probe.final_state, - result, shallow_certainty, - impl_args, + result, }); } } @@ -359,13 +339,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { warn!("unexpected root evaluation: {:?}", self.evaluation_kind); return vec![]; } - inspect::CanonicalGoalEvaluationKind::Evaluation { revisions } => { - if let Some(last) = revisions.last() { - last - } else { - return vec![]; - } - } + inspect::CanonicalGoalEvaluationKind::Evaluation { final_revision } => final_revision, }; let mut nested_goals = vec![]; @@ -392,9 +366,9 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { normalizes_to_term_hack: Option>, source: GoalSource, ) -> Self { - let inspect::GoalEvaluation { uncanonicalized_goal, kind, evaluation } = root; - let inspect::GoalEvaluationKind::Root { orig_values } = kind else { unreachable!() }; + let infcx = <&SolverDelegate<'tcx>>::from(infcx); + let inspect::GoalEvaluation { uncanonicalized_goal, orig_values, evaluation } = root; let result = evaluation.result.and_then(|ok| { if let Some(term_hack) = normalizes_to_term_hack { infcx @@ -446,8 +420,18 @@ impl<'tcx> InferCtxt<'tcx> { goal: Goal<'tcx, ty::Predicate<'tcx>>, visitor: &mut V, ) -> V::Result { - let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes); + self.visit_proof_tree_at_depth(goal, 0, visitor) + } + + fn visit_proof_tree_at_depth>( + &self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + depth: usize, + visitor: &mut V, + ) -> V::Result { + let (_, proof_tree) = + <&SolverDelegate<'tcx>>::from(self).evaluate_root_goal(goal, GenerateProofTree::Yes); let proof_tree = proof_tree.unwrap(); - visitor.visit_goal(&InspectGoal::new(self, 0, proof_tree, None, GoalSource::Misc)) + visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc)) } } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/mod.rs b/compiler/rustc_trait_selection/src/solve/inspect/mod.rs deleted file mode 100644 index 60d52305a6be..000000000000 --- a/compiler/rustc_trait_selection/src/solve/inspect/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub use rustc_middle::traits::solve::inspect::*; - -mod build; -pub(in crate::solve) use build::*; - -mod analyse; -pub use analyse::*; diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 5d5161e092e3..f42edebfcc42 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -1,24 +1,27 @@ +use std::fmt::Debug; +use std::marker::PhantomData; + use crate::traits::error_reporting::{OverflowCause, TypeErrCtxtExt}; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{BoundVarReplacer, PlaceholderReplacer}; +use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::infer::at::At; use rustc_infer::infer::InferCtxt; -use rustc_infer::traits::TraitEngineExt; -use rustc_infer::traits::{FulfillmentError, Obligation, TraitEngine}; +use rustc_infer::traits::{FromSolverError, Obligation, TraitEngine}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex}; use rustc_middle::ty::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{TypeFoldable, TypeVisitableExt}; -use super::FulfillmentCtxt; +use super::{FulfillmentCtxt, NextSolverError}; /// Deeply normalize all aliases in `value`. This does not handle inference and expects /// its input to be already fully resolved. -pub fn deeply_normalize<'tcx, T: TypeFoldable>>( - at: At<'_, 'tcx>, - value: T, -) -> Result>> { +pub fn deeply_normalize<'tcx, T, E>(at: At<'_, 'tcx>, value: T) -> Result> +where + T: TypeFoldable>, + E: FromSolverError<'tcx, NextSolverError<'tcx>>, +{ assert!(!value.has_escaping_bound_vars()); deeply_normalize_with_skipped_universes(at, value, vec![]) } @@ -29,29 +32,35 @@ pub fn deeply_normalize<'tcx, T: TypeFoldable>>( /// Additionally takes a list of universes which represents the binders which have been /// entered before passing `value` to the function. This is currently needed for /// `normalize_erasing_regions`, which skips binders as it walks through a type. -pub fn deeply_normalize_with_skipped_universes<'tcx, T: TypeFoldable>>( +pub fn deeply_normalize_with_skipped_universes<'tcx, T, E>( at: At<'_, 'tcx>, value: T, universes: Vec>, -) -> Result>> { +) -> Result> +where + T: TypeFoldable>, + E: FromSolverError<'tcx, NextSolverError<'tcx>>, +{ let fulfill_cx = FulfillmentCtxt::new(at.infcx); - let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes }; + let mut folder = + NormalizationFolder { at, fulfill_cx, depth: 0, universes, _errors: PhantomData }; value.try_fold_with(&mut folder) } -struct NormalizationFolder<'me, 'tcx> { +struct NormalizationFolder<'me, 'tcx, E> { at: At<'me, 'tcx>, - fulfill_cx: FulfillmentCtxt<'tcx>, + fulfill_cx: FulfillmentCtxt<'tcx, E>, depth: usize, universes: Vec>, + _errors: PhantomData, } -impl<'tcx> NormalizationFolder<'_, 'tcx> { - fn normalize_alias_ty( - &mut self, - alias_ty: Ty<'tcx>, - ) -> Result, Vec>> { +impl<'tcx, E> NormalizationFolder<'_, 'tcx, E> +where + E: FromSolverError<'tcx, NextSolverError<'tcx>>, +{ + fn normalize_alias_ty(&mut self, alias_ty: Ty<'tcx>) -> Result, Vec> { assert!(matches!(alias_ty.kind(), ty::Alias(..))); let infcx = self.at.infcx; @@ -100,9 +109,8 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { fn normalize_unevaluated_const( &mut self, - ty: Ty<'tcx>, uv: ty::UnevaluatedConst<'tcx>, - ) -> Result, Vec>> { + ) -> Result, Vec> { let infcx = self.at.infcx; let tcx = infcx.tcx; let recursion_limit = tcx.recursion_limit(); @@ -117,7 +125,7 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { self.depth += 1; - let new_infer_ct = infcx.next_const_var(ty, self.at.cause.span); + let new_infer_ct = infcx.next_const_var(self.at.cause.span); let obligation = Obligation::new( tcx, self.at.cause.clone(), @@ -134,7 +142,7 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { let ct = infcx.resolve_vars_if_possible(new_infer_ct); ct.try_fold_with(self)? } else { - ty::Const::new_unevaluated(tcx, uv, ty).try_super_fold_with(self)? + ty::Const::new_unevaluated(tcx, uv).try_super_fold_with(self)? }; self.depth -= 1; @@ -142,8 +150,11 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { } } -impl<'tcx> FallibleTypeFolder> for NormalizationFolder<'_, 'tcx> { - type Error = Vec>; +impl<'tcx, E> FallibleTypeFolder> for NormalizationFolder<'_, 'tcx, E> +where + E: FromSolverError<'tcx, NextSolverError<'tcx>> + Debug, +{ + type Error = Vec; fn interner(&self) -> TyCtxt<'tcx> { self.at.infcx.tcx @@ -202,7 +213,7 @@ impl<'tcx> FallibleTypeFolder> for NormalizationFolder<'_, 'tcx> { if uv.has_escaping_bound_vars() { let (uv, mapped_regions, mapped_types, mapped_consts) = BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, uv); - let result = ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv))?; + let result = ensure_sufficient_stack(|| self.normalize_unevaluated_const(uv))?; Ok(PlaceholderReplacer::replace_placeholders( infcx, mapped_regions, @@ -212,7 +223,7 @@ impl<'tcx> FallibleTypeFolder> for NormalizationFolder<'_, 'tcx> { result, )) } else { - ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv)) + ensure_sufficient_stack(|| self.normalize_unevaluated_const(uv)) } } } @@ -243,7 +254,7 @@ impl<'tcx> TypeFolder> for DeeplyNormalizeForDiagnosticsFolder<'_, ty, vec![None; ty.outer_exclusive_binder().as_usize()], ) - .unwrap_or_else(|_| ty.super_fold_with(self)) + .unwrap_or_else(|_: Vec>| ty.super_fold_with(self)) } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { @@ -252,6 +263,6 @@ impl<'tcx> TypeFolder> for DeeplyNormalizeForDiagnosticsFolder<'_, ct, vec![None; ct.outer_exclusive_binder().as_usize()], ) - .unwrap_or_else(|_| ct.super_fold_with(self)) + .unwrap_or_else(|_: Vec>| ct.super_fold_with(self)) } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs deleted file mode 100644 index 0164d44667c3..000000000000 --- a/compiler/rustc_trait_selection/src/solve/search_graph.rs +++ /dev/null @@ -1,497 +0,0 @@ -use crate::solve::FIXPOINT_STEP_LIMIT; - -use super::inspect; -use super::inspect::ProofTreeBuilder; -use super::SolverMode; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::fx::FxHashSet; -use rustc_index::Idx; -use rustc_index::IndexVec; -use rustc_middle::dep_graph::dep_kinds; -use rustc_middle::traits::solve::CacheData; -use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult}; -use rustc_middle::ty::TyCtxt; -use rustc_session::Limit; -use std::mem; - -rustc_index::newtype_index! { - #[orderable] - pub struct StackDepth {} -} - -bitflags::bitflags! { - /// Whether and how this goal has been used as the root of a - /// cycle. We track the kind of cycle as we're otherwise forced - /// to always rerun at least once. - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - struct HasBeenUsed: u8 { - const INDUCTIVE_CYCLE = 1 << 0; - const COINDUCTIVE_CYCLE = 1 << 1; - } -} - -#[derive(Debug)] -struct StackEntry<'tcx> { - input: CanonicalInput<'tcx>, - - available_depth: Limit, - - /// The maximum depth reached by this stack entry, only up-to date - /// for the top of the stack and lazily updated for the rest. - reached_depth: StackDepth, - - /// Whether this entry is a non-root cycle participant. - /// - /// We must not move the result of non-root cycle participants to the - /// global cache. See [SearchGraph::cycle_participants] for more details. - /// We store the highest stack depth of a head of a cycle this goal is involved - /// in. This necessary to soundly cache its provisional result. - non_root_cycle_participant: Option, - - encountered_overflow: bool, - - has_been_used: HasBeenUsed, - /// Starts out as `None` and gets set when rerunning this - /// goal in case we encounter a cycle. - provisional_result: Option>, -} - -/// The provisional result for a goal which is not on the stack. -struct DetachedEntry<'tcx> { - /// The head of the smallest non-trivial cycle involving this entry. - /// - /// Given the following rules, when proving `A` the head for - /// the provisional entry of `C` would be `B`. - /// ```plain - /// A :- B - /// B :- C - /// C :- A + B + C - /// ``` - head: StackDepth, - result: QueryResult<'tcx>, -} - -/// Stores the stack depth of a currently evaluated goal *and* already -/// computed results for goals which depend on other goals still on the stack. -/// -/// The provisional result may depend on whether the stack above it is inductive -/// or coinductive. Because of this, we store separate provisional results for -/// each case. If an provisional entry is not applicable, it may be the case -/// that we already have provisional result while computing a goal. In this case -/// we prefer the provisional result to potentially avoid fixpoint iterations. -/// See tests/ui/traits/next-solver/cycles/mixed-cycles-2.rs for an example. -/// -/// The provisional cache can theoretically result in changes to the observable behavior, -/// see tests/ui/traits/next-solver/cycles/provisional-cache-impacts-behavior.rs. -#[derive(Default)] -struct ProvisionalCacheEntry<'tcx> { - stack_depth: Option, - with_inductive_stack: Option>, - with_coinductive_stack: Option>, -} - -impl<'tcx> ProvisionalCacheEntry<'tcx> { - fn is_empty(&self) -> bool { - self.stack_depth.is_none() - && self.with_inductive_stack.is_none() - && self.with_coinductive_stack.is_none() - } -} - -pub(super) struct SearchGraph<'tcx> { - mode: SolverMode, - /// The stack of goals currently being computed. - /// - /// An element is *deeper* in the stack if its index is *lower*. - stack: IndexVec>, - provisional_cache: FxHashMap, ProvisionalCacheEntry<'tcx>>, - /// We put only the root goal of a coinductive cycle into the global cache. - /// - /// If we were to use that result when later trying to prove another cycle - /// participant, we can end up with unstable query results. - /// - /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for - /// an example of where this is needed. - cycle_participants: FxHashSet>, -} - -impl<'tcx> SearchGraph<'tcx> { - pub(super) fn new(mode: SolverMode) -> SearchGraph<'tcx> { - Self { - mode, - stack: Default::default(), - provisional_cache: Default::default(), - cycle_participants: Default::default(), - } - } - - pub(super) fn solver_mode(&self) -> SolverMode { - self.mode - } - - /// Update the stack and reached depths on cache hits. - #[instrument(level = "trace", skip(self))] - fn on_cache_hit(&mut self, additional_depth: usize, encountered_overflow: bool) { - let reached_depth = self.stack.next_index().plus(additional_depth); - if let Some(last) = self.stack.raw.last_mut() { - last.reached_depth = last.reached_depth.max(reached_depth); - last.encountered_overflow |= encountered_overflow; - } - } - - /// Pops the highest goal from the stack, lazily updating the - /// the next goal in the stack. - /// - /// Directly popping from the stack instead of using this method - /// would cause us to not track overflow and recursion depth correctly. - fn pop_stack(&mut self) -> StackEntry<'tcx> { - let elem = self.stack.pop().unwrap(); - if let Some(last) = self.stack.raw.last_mut() { - last.reached_depth = last.reached_depth.max(elem.reached_depth); - last.encountered_overflow |= elem.encountered_overflow; - } - elem - } - - /// The trait solver behavior is different for coherence - /// so we use a separate cache. Alternatively we could use - /// a single cache and share it between coherence and ordinary - /// trait solving. - pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'tcx> { - match self.mode { - SolverMode::Normal => &tcx.new_solver_evaluation_cache, - SolverMode::Coherence => &tcx.new_solver_coherence_evaluation_cache, - } - } - - pub(super) fn is_empty(&self) -> bool { - if self.stack.is_empty() { - debug_assert!(self.provisional_cache.is_empty()); - debug_assert!(self.cycle_participants.is_empty()); - true - } else { - false - } - } - - /// Returns the remaining depth allowed for nested goals. - /// - /// This is generally simply one less than the current depth. - /// However, if we encountered overflow, we significantly reduce - /// the remaining depth of all nested goals to prevent hangs - /// in case there is exponential blowup. - fn allowed_depth_for_nested( - tcx: TyCtxt<'tcx>, - stack: &IndexVec>, - ) -> Option { - if let Some(last) = stack.raw.last() { - if last.available_depth.0 == 0 { - return None; - } - - Some(if last.encountered_overflow { - Limit(last.available_depth.0 / 4) - } else { - Limit(last.available_depth.0 - 1) - }) - } else { - Some(tcx.recursion_limit()) - } - } - - fn stack_coinductive_from( - tcx: TyCtxt<'tcx>, - stack: &IndexVec>, - head: StackDepth, - ) -> bool { - stack.raw[head.index()..] - .iter() - .all(|entry| entry.input.value.goal.predicate.is_coinductive(tcx)) - } - - // When encountering a solver cycle, the result of the current goal - // depends on goals lower on the stack. - // - // We have to therefore be careful when caching goals. Only the final result - // of the cycle root, i.e. the lowest goal on the stack involved in this cycle, - // is moved to the global cache while all others are stored in a provisional cache. - // - // We update both the head of this cycle to rerun its evaluation until - // we reach a fixpoint and all other cycle participants to make sure that - // their result does not get moved to the global cache. - fn tag_cycle_participants( - stack: &mut IndexVec>, - cycle_participants: &mut FxHashSet>, - usage_kind: HasBeenUsed, - head: StackDepth, - ) { - stack[head].has_been_used |= usage_kind; - debug_assert!(!stack[head].has_been_used.is_empty()); - for entry in &mut stack.raw[head.index() + 1..] { - entry.non_root_cycle_participant = entry.non_root_cycle_participant.max(Some(head)); - cycle_participants.insert(entry.input); - } - } - - fn clear_dependent_provisional_results( - provisional_cache: &mut FxHashMap, ProvisionalCacheEntry<'tcx>>, - head: StackDepth, - ) { - #[allow(rustc::potential_query_instability)] - provisional_cache.retain(|_, entry| { - entry.with_coinductive_stack.take_if(|p| p.head == head); - entry.with_inductive_stack.take_if(|p| p.head == head); - !entry.is_empty() - }); - } - - /// Probably the most involved method of the whole solver. - /// - /// Given some goal which is proven via the `prove_goal` closure, this - /// handles caching, overflow, and coinductive cycles. - pub(super) fn with_new_goal( - &mut self, - tcx: TyCtxt<'tcx>, - input: CanonicalInput<'tcx>, - inspect: &mut ProofTreeBuilder>, - mut prove_goal: impl FnMut(&mut Self, &mut ProofTreeBuilder>) -> QueryResult<'tcx>, - ) -> QueryResult<'tcx> { - // Check for overflow. - let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else { - if let Some(last) = self.stack.raw.last_mut() { - last.encountered_overflow = true; - } - - inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow); - return Self::response_no_constraints(tcx, input, Certainty::overflow(true)); - }; - - // Try to fetch the goal from the global cache. - 'global: { - let Some(CacheData { result, proof_tree, reached_depth, encountered_overflow }) = - self.global_cache(tcx).get( - tcx, - input, - |cycle_participants| { - self.stack.iter().any(|entry| cycle_participants.contains(&entry.input)) - }, - available_depth, - ) - else { - break 'global; - }; - - // If we're building a proof tree and the current cache entry does not - // contain a proof tree, we do not use the entry but instead recompute - // the goal. We simply overwrite the existing entry once we're done, - // caching the proof tree. - if !inspect.is_noop() { - if let Some(revisions) = proof_tree { - inspect.goal_evaluation_kind( - inspect::WipCanonicalGoalEvaluationKind::Interned { revisions }, - ); - } else { - break 'global; - } - } - - self.on_cache_hit(reached_depth, encountered_overflow); - debug!("global cache hit"); - return result; - } - - // Check whether the goal is in the provisional cache. - // The provisional result may rely on the path to its cycle roots, - // so we have to check the path of the current goal matches that of - // the cache entry. - let cache_entry = self.provisional_cache.entry(input).or_default(); - if let Some(entry) = cache_entry - .with_coinductive_stack - .as_ref() - .filter(|p| Self::stack_coinductive_from(tcx, &self.stack, p.head)) - .or_else(|| { - cache_entry - .with_inductive_stack - .as_ref() - .filter(|p| !Self::stack_coinductive_from(tcx, &self.stack, p.head)) - }) - { - debug!("provisional cache hit"); - // We have a nested goal which is already in the provisional cache, use - // its result. We do not provide any usage kind as that should have been - // already set correctly while computing the cache entry. - inspect - .goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::ProvisionalCacheHit); - Self::tag_cycle_participants( - &mut self.stack, - &mut self.cycle_participants, - HasBeenUsed::empty(), - entry.head, - ); - return entry.result; - } else if let Some(stack_depth) = cache_entry.stack_depth { - debug!("encountered cycle with depth {stack_depth:?}"); - // We have a nested goal which directly relies on a goal deeper in the stack. - // - // We start by tagging all cycle participants, as that's necessary for caching. - // - // Finally we can return either the provisional response or the initial response - // in case we're in the first fixpoint iteration for this goal. - inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CycleInStack); - let is_coinductive_cycle = Self::stack_coinductive_from(tcx, &self.stack, stack_depth); - let usage_kind = if is_coinductive_cycle { - HasBeenUsed::COINDUCTIVE_CYCLE - } else { - HasBeenUsed::INDUCTIVE_CYCLE - }; - Self::tag_cycle_participants( - &mut self.stack, - &mut self.cycle_participants, - usage_kind, - stack_depth, - ); - - // Return the provisional result or, if we're in the first iteration, - // start with no constraints. - return if let Some(result) = self.stack[stack_depth].provisional_result { - result - } else if is_coinductive_cycle { - Self::response_no_constraints(tcx, input, Certainty::Yes) - } else { - Self::response_no_constraints(tcx, input, Certainty::overflow(false)) - }; - } else { - // No entry, we push this goal on the stack and try to prove it. - let depth = self.stack.next_index(); - let entry = StackEntry { - input, - available_depth, - reached_depth: depth, - non_root_cycle_participant: None, - encountered_overflow: false, - has_been_used: HasBeenUsed::empty(), - provisional_result: None, - }; - assert_eq!(self.stack.push(entry), depth); - cache_entry.stack_depth = Some(depth); - } - - // This is for global caching, so we properly track query dependencies. - // Everything that affects the `result` should be performed within this - // `with_anon_task` closure. - let ((final_entry, result), dep_node) = - tcx.dep_graph.with_anon_task(tcx, dep_kinds::TraitSelect, || { - // When we encounter a coinductive cycle, we have to fetch the - // result of that cycle while we are still computing it. Because - // of this we continuously recompute the cycle until the result - // of the previous iteration is equal to the final result, at which - // point we are done. - for _ in 0..FIXPOINT_STEP_LIMIT { - let result = prove_goal(self, inspect); - let stack_entry = self.pop_stack(); - debug_assert_eq!(stack_entry.input, input); - - // If the current goal is not the root of a cycle, we are done. - if stack_entry.has_been_used.is_empty() { - return (stack_entry, result); - } - - // If it is a cycle head, we have to keep trying to prove it until - // we reach a fixpoint. We need to do so for all cycle heads, - // not only for the root. - // - // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs - // for an example. - - // Start by clearing all provisional cache entries which depend on this - // the current goal. - Self::clear_dependent_provisional_results( - &mut self.provisional_cache, - self.stack.next_index(), - ); - - // Check whether we reached a fixpoint, either because the final result - // is equal to the provisional result of the previous iteration, or because - // this was only the root of either coinductive or inductive cycles, and the - // final result is equal to the initial response for that case. - let reached_fixpoint = if let Some(r) = stack_entry.provisional_result { - r == result - } else if stack_entry.has_been_used == HasBeenUsed::COINDUCTIVE_CYCLE { - Self::response_no_constraints(tcx, input, Certainty::Yes) == result - } else if stack_entry.has_been_used == HasBeenUsed::INDUCTIVE_CYCLE { - Self::response_no_constraints(tcx, input, Certainty::overflow(false)) - == result - } else { - false - }; - - // If we did not reach a fixpoint, update the provisional result and reevaluate. - if reached_fixpoint { - return (stack_entry, result); - } else { - let depth = self.stack.push(StackEntry { - has_been_used: HasBeenUsed::empty(), - provisional_result: Some(result), - ..stack_entry - }); - debug_assert_eq!(self.provisional_cache[&input].stack_depth, Some(depth)); - } - } - - debug!("canonical cycle overflow"); - let current_entry = self.pop_stack(); - debug_assert!(current_entry.has_been_used.is_empty()); - let result = Self::response_no_constraints(tcx, input, Certainty::overflow(false)); - (current_entry, result) - }); - - let proof_tree = inspect.finalize_evaluation(tcx); - - // We're now done with this goal. In case this goal is involved in a larger cycle - // do not remove it from the provisional cache and update its provisional result. - // We only add the root of cycles to the global cache. - if let Some(head) = final_entry.non_root_cycle_participant { - let coinductive_stack = Self::stack_coinductive_from(tcx, &self.stack, head); - - let entry = self.provisional_cache.get_mut(&input).unwrap(); - entry.stack_depth = None; - if coinductive_stack { - entry.with_coinductive_stack = Some(DetachedEntry { head, result }); - } else { - entry.with_inductive_stack = Some(DetachedEntry { head, result }); - } - } else { - self.provisional_cache.remove(&input); - let reached_depth = final_entry.reached_depth.as_usize() - self.stack.len(); - let cycle_participants = mem::take(&mut self.cycle_participants); - // When encountering a cycle, both inductive and coinductive, we only - // move the root into the global cache. We also store all other cycle - // participants involved. - // - // We must not use the global cache entry of a root goal if a cycle - // participant is on the stack. This is necessary to prevent unstable - // results. See the comment of `SearchGraph::cycle_participants` for - // more details. - self.global_cache(tcx).insert( - tcx, - input, - proof_tree, - reached_depth, - final_entry.encountered_overflow, - cycle_participants, - dep_node, - result, - ) - } - - result - } - - fn response_no_constraints( - tcx: TyCtxt<'tcx>, - goal: CanonicalInput<'tcx>, - certainty: Certainty, - ) -> QueryResult<'tcx> { - Ok(super::response_no_constraints_raw(tcx, goal.max_universe, goal.variables, certainty)) - } -} diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/select.rs similarity index 99% rename from compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs rename to compiler/rustc_trait_selection/src/solve/select.rs index 68c0c8bf09ea..257fd263b944 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/select.rs @@ -114,8 +114,8 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>( // In the old trait solver, we arbitrarily choose lower vtable candidates // over higher ones. ( - CandidateSource::BuiltinImpl(BuiltinImplSource::Object { vtable_base: a }), - CandidateSource::BuiltinImpl(BuiltinImplSource::Object { vtable_base: b }), + CandidateSource::BuiltinImpl(BuiltinImplSource::Object(a)), + CandidateSource::BuiltinImpl(BuiltinImplSource::Object(b)), ) => a >= b, // Prefer dyn candidates over non-dyn candidates. This is necessary to // handle the unsoundness between `impl Any for T` and `dyn Any: Any`. diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 1ea207cc375f..1d32ef2ccd94 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -553,7 +553,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'tcx>) -> bool { - if let Some(ty) = p.term().skip_binder().ty() { + if let Some(ty) = p.term().skip_binder().as_type() { matches!(ty.kind(), ty::Alias(ty::Projection, proj) if proj == &p.skip_binder().projection_term.expect_ty(self.tcx)) } else { false @@ -765,7 +765,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { unevaluated, obligation.cause.span, ) { - Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, c.ty())), + Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args))), Ok(None) => { let tcx = self.tcx; let reported = diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index ebdb032dc0e2..a4177d8a93f4 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -11,15 +11,13 @@ use crate::solve::{deeply_normalize_for_diagnostics, inspect}; use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::NormalizeExt; use crate::traits::SkipLeakCheck; -use crate::traits::{ - Obligation, ObligationCause, PredicateObligation, PredicateObligations, SelectionContext, -}; +use crate::traits::{util, FulfillmentErrorCode}; +use crate::traits::{Obligation, ObligationCause, PredicateObligation, SelectionContext}; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::{util, FulfillmentErrorCode}; use rustc_middle::bug; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal}; @@ -305,7 +303,7 @@ fn equate_impl_headers<'tcx>( param_env: ty::ParamEnv<'tcx>, impl1: &ty::ImplHeader<'tcx>, impl2: &ty::ImplHeader<'tcx>, -) -> Option> { +) -> Option>> { let result = match (impl1.trait_ref, impl2.trait_ref) { (Some(impl1_ref), Some(impl2_ref)) => infcx @@ -360,7 +358,7 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>( let infcx = selcx.infcx; if infcx.next_trait_solver() { - let ocx = ObligationCtxt::new(infcx); + let ocx = ObligationCtxt::new_with_diagnostics(infcx); ocx.register_obligations(obligations.iter().cloned()); let errors_and_ambiguities = ocx.select_all_or_error(); // We only care about the obligations that are *definitely* true errors. @@ -529,7 +527,6 @@ fn plug_infer_with_placeholders<'tcx>( ty::Const::new_placeholder( self.infcx.tcx, ty::Placeholder { universe: self.universe, bound: self.next_var() }, - ct.ty(), ), ) else { @@ -772,8 +769,8 @@ pub struct UncoveredTyParams<'tcx, T> { /// add "non-blanket" impls without breaking negative reasoning in dependent /// crates. This is the "rebalancing coherence" (RFC 1023) restriction. /// -/// For that, we only a allow crate to perform negative reasoning on -/// non-local-non-`#[fundamental]` only if there's a local key parameter as per (2). +/// For that, we only allow a crate to perform negative reasoning on +/// non-local-non-`#[fundamental]` if there's a local key parameter as per (2). /// /// Because we never perform negative reasoning generically (coherence does /// not involve type parameters), this can be interpreted as doing the full @@ -924,11 +921,12 @@ where } } - ty::Alias(kind @ (ty::Projection | ty::Inherent | ty::Weak), ..) => { - if ty.has_type_flags(ty::TypeFlags::HAS_TY_PARAM) { - bug!("unexpected ty param in alias ty"); - } - + // A rigid alias may normalize to anything. + // * If it references an infer var, placeholder or bound ty, it may + // normalize to that, so we have to treat it as an uncovered ty param. + // * Otherwise it may normalize to any non-type-generic type + // be it local or non-local. + ty::Alias(kind, _) => { if ty.has_type_flags( ty::TypeFlags::HAS_TY_PLACEHOLDER | ty::TypeFlags::HAS_TY_BOUND @@ -948,7 +946,24 @@ where } } } else { - ControlFlow::Continue(()) + // Regarding *opaque types* specifically, we choose to treat them as non-local, + // even those that appear within the same crate. This seems somewhat surprising + // at first, but makes sense when you consider that opaque types are supposed + // to hide the underlying type *within the same crate*. When an opaque type is + // used from outside the module where it is declared, it should be impossible to + // observe anything about it other than the traits that it implements. + // + // The alternative would be to look at the underlying type to determine whether + // or not the opaque type itself should be considered local. + // + // However, this could make it a breaking change to switch the underlying hidden + // type from a local type to a remote type. This would violate the rule that + // opaque types should be completely opaque apart from the traits that they + // implement, so we don't use this behavior. + // Addendum: Moreover, revealing the underlying type is likely to cause cycle + // errors as we rely on coherence / the specialization graph during typeck. + + self.found_non_local_ty(ty) } } @@ -990,35 +1005,6 @@ where // auto trait impl applies. There will never be multiple impls, so we can just // act as if it were a local type here. ty::CoroutineWitness(..) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), - ty::Alias(ty::Opaque, ..) => { - // This merits some explanation. - // Normally, opaque types are not involved when performing - // coherence checking, since it is illegal to directly - // implement a trait on an opaque type. However, we might - // end up looking at an opaque type during coherence checking - // if an opaque type gets used within another type (e.g. as - // the type of a field) when checking for auto trait or `Sized` - // impls. This requires us to decide whether or not an opaque - // type should be considered 'local' or not. - // - // We choose to treat all opaque types as non-local, even - // those that appear within the same crate. This seems - // somewhat surprising at first, but makes sense when - // you consider that opaque types are supposed to hide - // the underlying type *within the same crate*. When an - // opaque type is used from outside the module - // where it is declared, it should be impossible to observe - // anything about it other than the traits that it implements. - // - // The alternative would be to look at the underlying type - // to determine whether or not the opaque type itself should - // be considered local. However, this could make it a breaking change - // to switch the underlying ('defining') type from a local type - // to a remote type. This would violate the rule that opaque - // types should be completely opaque apart from the traits - // that they implement, so we don't use this behavior. - self.found_non_local_ty(ty) - } }; // A bit of a hack, the `OrphanChecker` is only used to visit a `TraitRef`, so // the first type we visit is always the self type. diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 8348482386f7..f93bd0a396dd 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -34,7 +34,7 @@ pub fn is_const_evaluatable<'tcx>( ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Value(_) + | ty::ConstKind::Value(_, _) | ty::ConstKind::Error(_) => return Ok(()), ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer), }; @@ -173,8 +173,7 @@ fn satisfied_from_param_env<'tcx>( debug!("is_const_evaluatable: candidate={:?}", c); if self.infcx.probe(|_| { let ocx = ObligationCtxt::new(self.infcx); - ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty()).is_ok() - && ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct).is_ok() + ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct).is_ok() && ocx.select_all_or_error().is_empty() }) { self.single_match = match self.single_match { @@ -215,7 +214,6 @@ fn satisfied_from_param_env<'tcx>( if let Some(Ok(c)) = single_match { let ocx = ObligationCtxt::new(infcx); - assert!(ocx.eq(&ObligationCause::dummy(), param_env, c.ty(), ct.ty()).is_ok()); assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok()); assert!(ocx.select_all_or_error().is_empty()); return true; diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 4684c7171d82..811f61d2bf37 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -1,13 +1,16 @@ use std::cell::RefCell; use std::fmt::Debug; -use super::FulfillmentContext; -use super::TraitEngine; +use super::{FromSolverError, TraitEngine}; +use super::{FulfillmentContext, ScrubbedTraitError}; use crate::regions::InferCtxtRegionExt; use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt; +use crate::solve::NextSolverError; use crate::traits::error_reporting::TypeErrCtxtExt; +use crate::traits::fulfill::OldSolverError; use crate::traits::NormalizeExt; use crate::traits::StructurallyNormalizeExt; +use crate::traits::{FulfillmentError, Obligation, ObligationCause, PredicateObligation}; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -18,9 +21,6 @@ use rustc_infer::infer::canonical::{ use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::RegionResolutionError; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; -use rustc_infer::traits::{ - FulfillmentError, Obligation, ObligationCause, PredicateObligation, TraitEngineExt as _, -}; use rustc_macros::extension; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::traits::query::NoSolution; @@ -30,8 +30,11 @@ use rustc_middle::ty::Upcast; use rustc_middle::ty::Variance; use rustc_middle::ty::{self, Ty, TyCtxt}; -#[extension(pub trait TraitEngineExt<'tcx>)] -impl<'tcx> dyn TraitEngine<'tcx> { +#[extension(pub trait TraitEngineExt<'tcx, E>)] +impl<'tcx, E> dyn TraitEngine<'tcx, E> +where + E: FromSolverError<'tcx, NextSolverError<'tcx>> + FromSolverError<'tcx, OldSolverError<'tcx>>, +{ fn new(infcx: &InferCtxt<'tcx>) -> Box { if infcx.next_trait_solver() { Box::new(NextFulfillmentCtxt::new(infcx)) @@ -49,16 +52,27 @@ impl<'tcx> dyn TraitEngine<'tcx> { /// Used if you want to have pleasant experience when dealing /// with obligations outside of hir or mir typeck. -pub struct ObligationCtxt<'a, 'tcx> { +pub struct ObligationCtxt<'a, 'tcx, E = ScrubbedTraitError<'tcx>> { pub infcx: &'a InferCtxt<'tcx>, - engine: RefCell>>, + engine: RefCell>>, } -impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { - pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { - Self { infcx, engine: RefCell::new(>::new(infcx)) } +impl<'a, 'tcx> ObligationCtxt<'a, 'tcx, FulfillmentError<'tcx>> { + pub fn new_with_diagnostics(infcx: &'a InferCtxt<'tcx>) -> Self { + Self { infcx, engine: RefCell::new(>::new(infcx)) } } +} +impl<'a, 'tcx> ObligationCtxt<'a, 'tcx, ScrubbedTraitError<'tcx>> { + pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { + Self { infcx, engine: RefCell::new(>::new(infcx)) } + } +} + +impl<'a, 'tcx, E> ObligationCtxt<'a, 'tcx, E> +where + E: 'tcx, +{ pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) { self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation); } @@ -110,26 +124,6 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { self.register_infer_ok_obligations(infer_ok) } - pub fn deeply_normalize>>( - &self, - cause: &ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - value: T, - ) -> Result>> { - self.infcx.at(cause, param_env).deeply_normalize(value, &mut **self.engine.borrow_mut()) - } - - pub fn structurally_normalize( - &self, - cause: &ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - value: Ty<'tcx>, - ) -> Result, Vec>> { - self.infcx - .at(cause, param_env) - .structurally_normalize(value, &mut **self.engine.borrow_mut()) - } - pub fn eq>( &self, cause: &ObligationCause<'tcx>, @@ -186,12 +180,12 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { } #[must_use] - pub fn select_where_possible(&self) -> Vec> { + pub fn select_where_possible(&self) -> Vec { self.engine.borrow_mut().select_where_possible(self.infcx) } #[must_use] - pub fn select_all_or_error(&self) -> Vec> { + pub fn select_all_or_error(&self) -> Vec { self.engine.borrow_mut().select_all_or_error(self.infcx) } @@ -235,7 +229,9 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { ) -> Vec> { self.infcx.resolve_regions(outlives_env) } +} +impl<'tcx> ObligationCtxt<'_, 'tcx, FulfillmentError<'tcx>> { pub fn assumed_wf_types_and_report_errors( &self, param_env: ty::ParamEnv<'tcx>, @@ -244,12 +240,35 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { self.assumed_wf_types(param_env, def_id) .map_err(|errors| self.infcx.err_ctxt().report_fulfillment_errors(errors)) } +} +impl<'tcx> ObligationCtxt<'_, 'tcx, ScrubbedTraitError<'tcx>> { + pub fn make_canonicalized_query_response( + &self, + inference_vars: CanonicalVarValues<'tcx>, + answer: T, + ) -> Result, NoSolution> + where + T: Debug + TypeFoldable>, + Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>, + { + self.infcx.make_canonicalized_query_response( + inference_vars, + answer, + &mut **self.engine.borrow_mut(), + ) + } +} + +impl<'tcx, E> ObligationCtxt<'_, 'tcx, E> +where + E: FromSolverError<'tcx, NextSolverError<'tcx>>, +{ pub fn assumed_wf_types( &self, param_env: ty::ParamEnv<'tcx>, def_id: LocalDefId, - ) -> Result>, Vec>> { + ) -> Result>, Vec> { let tcx = self.infcx.tcx; let mut implied_bounds = FxIndexSet::default(); let mut errors = Vec::new(); @@ -281,19 +300,23 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { if errors.is_empty() { Ok(implied_bounds) } else { Err(errors) } } - pub fn make_canonicalized_query_response( + pub fn deeply_normalize>>( &self, - inference_vars: CanonicalVarValues<'tcx>, - answer: T, - ) -> Result, NoSolution> - where - T: Debug + TypeFoldable>, - Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>, - { - self.infcx.make_canonicalized_query_response( - inference_vars, - answer, - &mut **self.engine.borrow_mut(), - ) + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: T, + ) -> Result> { + self.infcx.at(cause, param_env).deeply_normalize(value, &mut **self.engine.borrow_mut()) + } + + pub fn structurally_normalize( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: Ty<'tcx>, + ) -> Result, Vec> { + self.infcx + .at(cause, param_env) + .structurally_normalize(value, &mut **self.engine.borrow_mut()) } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index e50edfdc656c..633d488f7be5 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -7,15 +7,11 @@ pub mod suggestions; mod type_err_ctxt_ext; use super::{Obligation, ObligationCause, ObligationCauseCode, PredicateObligation}; -use crate::infer::InferCtxt; -use crate::solve::{GenerateProofTree, InferCtxtEvalExt}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_middle::traits::solve::Goal; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; -use std::io::Write; use std::ops::ControlFlow; pub use self::infer_ctxt_ext::*; @@ -184,16 +180,3 @@ pub enum DefIdOrName { DefId(DefId), Name(&'static str), } - -pub fn dump_proof_tree<'tcx>(o: &Obligation<'tcx, ty::Predicate<'tcx>>, infcx: &InferCtxt<'tcx>) { - infcx.probe(|_| { - let goal = Goal { predicate: o.predicate, param_env: o.param_env }; - let tree = infcx - .evaluate_root_goal(goal, GenerateProofTree::Yes) - .1 - .expect("proof tree should have been generated"); - let mut lock = std::io::stdout().lock(); - let _ = lock.write_fmt(format_args!("{tree:?}\n")); - let _ = lock.flush(); - }); -} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index f9b6b281f924..038f11c60b80 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -28,11 +28,12 @@ use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk}; use rustc_macros::extension; use rustc_middle::hir::map; use rustc_middle::traits::IsConstable; -use rustc_middle::ty::error::TypeError::{self, Sorts}; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::print::PrintPolyTraitRefExt; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs, - InferTy, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitableExt, TypeckResults, Upcast, + InferTy, IsSuggestable, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitableExt, TypeckResults, Upcast, }; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; @@ -47,7 +48,8 @@ use crate::infer::InferCtxtExt as _; use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_middle::ty::print::{ - with_forced_trimmed_paths, with_no_trimmed_paths, PrintTraitPredicateExt as _, + with_forced_trimmed_paths, with_no_trimmed_paths, PrintPolyTraitPredicateExt as _, + PrintTraitPredicateExt as _, }; use itertools::EitherOrBoth; @@ -218,15 +220,15 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( (_, None) => predicate_constraint(hir_generics, trait_pred.upcast(tcx)), (None, Some((ident, []))) => ( ident.span.shrink_to_hi(), - format!(": {}", trait_pred.print_modifiers_and_trait_path()), + format!(": {}", trait_pred.to_poly_trait_ref().print_trait_sugared()), ), (_, Some((_, [.., bounds]))) => ( bounds.span().shrink_to_hi(), - format!(" + {}", trait_pred.print_modifiers_and_trait_path()), + format!(" + {}", trait_pred.to_poly_trait_ref().print_trait_sugared()), ), (Some(_), Some((_, []))) => ( hir_generics.span.shrink_to_hi(), - format!(": {}", trait_pred.print_modifiers_and_trait_path()), + format!(": {}", trait_pred.to_poly_trait_ref().print_trait_sugared()), ), }; @@ -449,7 +451,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } /// When after several dereferencing, the reference satisfies the trait - /// binding. This function provides dereference suggestion for this + /// bound. This function provides dereference suggestion for this /// specific situation. fn suggest_dereferences( &self, @@ -776,7 +778,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } /// We tried to apply the bound to an `fn` or closure. Check whether calling it would - /// evaluate to a type that *would* satisfy the trait binding. If it would, suggest calling + /// evaluate to a type that *would* satisfy the trait bound. If it would, suggest calling /// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`. fn suggest_fn_call( &self, @@ -792,7 +794,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) = obligation.predicate.kind().skip_binder() - && Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait() + && self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Sized) { // Don't suggest calling to turn an unsized type into a sized type return false; @@ -906,10 +908,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { span.remove_mark(); } let mut expr_finder = FindExprBySpan::new(span, self.tcx); - let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { + let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { return; }; - let body = self.tcx.hir().body(body_id); expr_finder.visit_expr(body.value); let Some(expr) = expr_finder.result else { return; @@ -1105,13 +1106,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .iter() .find_map(|pred| { if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() - && Some(proj.projection_term.def_id) == self.tcx.lang_items().fn_once_output() + && self.tcx.is_lang_item(proj.projection_term.def_id,LangItem::FnOnceOutput) // args tuple will always be args[1] && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind() { Some(( DefIdOrName::DefId(def_id), - pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(proj.term.expect_type()), pred.kind().rebind(args.as_slice()), )) } else { @@ -1122,13 +1123,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ty::Dynamic(data, _, ty::Dyn) => { data.iter().find_map(|pred| { if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() - && Some(proj.def_id) == self.tcx.lang_items().fn_once_output() + && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput) // for existential projection, args are shifted over by 1 && let ty::Tuple(args) = proj.args.type_at(0).kind() { Some(( DefIdOrName::Name("trait object"), - pred.rebind(proj.term.ty().unwrap()), + pred.rebind(proj.term.expect_type()), pred.rebind(args.as_slice()), )) } else { @@ -1149,14 +1150,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; param_env.caller_bounds().iter().find_map(|pred| { if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() - && Some(proj.projection_term.def_id) == self.tcx.lang_items().fn_once_output() + && self.tcx.is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput) && proj.projection_term.self_ty() == found // args tuple will always be args[1] && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind() { Some(( name, - pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(proj.term.expect_type()), pred.kind().rebind(args.as_slice()), )) } else { @@ -1240,7 +1241,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let param_env = obligation.param_env; - // Try to apply the original trait binding obligation by borrowing. + // Try to apply the original trait bound by borrowing. let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>, blacklist: &[DefId]| -> bool { @@ -1368,12 +1369,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Issue #104961, we need to add parentheses properly for compound expressions // for example, `x.starts_with("hi".to_string() + "you")` // should be `x.starts_with(&("hi".to_string() + "you"))` - let Some(body_id) = + let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { return false; }; - let body = self.tcx.hir().body(body_id); let mut expr_finder = FindExprBySpan::new(span, self.tcx); expr_finder.visit_expr(body.value); let Some(expr) = expr_finder.result else { @@ -1475,10 +1475,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { span.remove_mark(); } let mut expr_finder = super::FindExprBySpan::new(span, self.tcx); - let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { + let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { return false; }; - let body = self.tcx.hir().body(body_id); expr_finder.visit_expr(body.value); let mut maybe_suggest = |suggested_ty, count, suggestions| { // Remapping bound vars here @@ -1804,10 +1803,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); } - let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(obligation.cause.body_id)); + let body = self.tcx.hir().body_owned_by(obligation.cause.body_id); let mut visitor = ReturnsVisitor::default(); - visitor.visit_body(body); + visitor.visit_body(&body); let mut sugg = vec![(span.shrink_to_lo(), "Box<".to_string()), (span.shrink_to_hi(), ">".to_string())]; @@ -1823,7 +1822,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { && box_path .res .opt_def_id() - .is_some_and(|def_id| Some(def_id) == self.tcx.lang_items().owned_box()) + .is_some_and(|def_id| self.tcx.is_lang_item(def_id, LangItem::OwnedBox)) { // Don't box `Box::new` vec![] @@ -2347,13 +2346,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ?span, ); - let coroutine_body = coroutine_did - .as_local() - .and_then(|def_id| hir.maybe_body_owned_by(def_id)) - .map(|body_id| hir.body(body_id)); + let coroutine_body = + coroutine_did.as_local().and_then(|def_id| hir.maybe_body_owned_by(def_id)); let mut visitor = AwaitsVisitor::default(); if let Some(body) = coroutine_body { - visitor.visit_body(body); + visitor.visit_body(&body); } debug!(awaits = ?visitor.awaits); @@ -2740,7 +2737,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::ObjectTypeBound(..) => {} ObligationCauseCode::RustCall => { if let Some(pred) = predicate.as_trait_clause() - && Some(pred.def_id()) == tcx.lang_items().sized_trait() + && tcx.is_lang_item(pred.def_id(), LangItem::Sized) { err.note("argument required to be sized due to `extern \"rust-call\"` ABI"); } @@ -2793,7 +2790,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Check for foreign traits being reachable. tcx.visible_parent_map(()).get(&def_id).is_some() }; - if Some(def_id) == tcx.lang_items().sized_trait() { + if tcx.is_lang_item(def_id, LangItem::Sized) { // Check if this is an implicit bound, even in foreign crates. if tcx .generics_of(item_def_id) @@ -3600,7 +3597,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, - trait_ref: &ty::PolyTraitRef<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, ) { let rhs_span = match obligation.cause.code() { ObligationCauseCode::BinOp { rhs_span: Some(span), rhs_is_lit, .. } if *rhs_is_lit => { @@ -3843,9 +3840,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }) } else if let Some(where_pred) = where_pred.as_projection_clause() && let Some(failed_pred) = failed_pred.as_projection_clause() - && let Some(found) = failed_pred.skip_binder().term.ty() + && let Some(found) = failed_pred.skip_binder().term.as_type() { - type_diffs = vec![Sorts(ty::error::ExpectedFound { + type_diffs = vec![TypeError::Sorts(ty::error::ExpectedFound { expected: where_pred .skip_binder() .projection_term @@ -3988,7 +3985,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { continue; }; for diff in type_diffs { - let Sorts(expected_found) = diff else { + let TypeError::Sorts(expected_found) = diff else { continue; }; if tcx.is_diagnostic_item(sym::IteratorItem, *def_id) @@ -4168,7 +4165,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; if primary_spans.is_empty() || type_diffs.iter().any(|diff| { - let Sorts(expected_found) = diff else { + let TypeError::Sorts(expected_found) = diff else { return false; }; self.can_eq(param_env, expected_found.found, ty) @@ -4201,7 +4198,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let assoc = with_forced_trimmed_paths!(self.tcx.def_path_str(assoc)); if !self.can_eq(param_env, ty, *prev_ty) { if type_diffs.iter().any(|diff| { - let Sorts(expected_found) = diff else { + let TypeError::Sorts(expected_found) = diff else { return false; }; self.can_eq(param_env, expected_found.found, ty) @@ -4251,7 +4248,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let ocx = ObligationCtxt::new(self.infcx); let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len()); for diff in type_diffs { - let Sorts(expected_found) = diff else { + let TypeError::Sorts(expected_found) = diff else { continue; }; let ty::Alias(ty::Projection, proj) = expected_found.expected.kind() else { @@ -4590,6 +4587,46 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { _ => "/* value */".to_string(), }) } + + fn suggest_add_result_as_return_type( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diag<'_>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) { + if ObligationCauseCode::QuestionMark != *obligation.cause.code().peel_derives() { + return; + } + + let node = self.tcx.hir_node_by_def_id(obligation.cause.body_id); + if let hir::Node::Item(item) = node + && let hir::ItemKind::Fn(sig, _, body_id) = item.kind + && let hir::FnRetTy::DefaultReturn(ret_span) = sig.decl.output + && self.tcx.is_diagnostic_item(sym::FromResidual, trait_pred.def_id()) + && trait_pred.skip_binder().trait_ref.args.type_at(0).is_unit() + && let ty::Adt(def, _) = trait_pred.skip_binder().trait_ref.args.type_at(1).kind() + && self.tcx.is_diagnostic_item(sym::Result, def.did()) + { + let body = self.tcx.hir().body(body_id); + let mut sugg_spans = + vec![(ret_span, " -> Result<(), Box>".to_string())]; + + if let hir::ExprKind::Block(b, _) = body.value.kind + && b.expr.is_none() + { + sugg_spans.push(( + // The span will point to the closing curly brace `}` of the block. + b.span.shrink_to_hi().with_lo(b.span.hi() - BytePos(1)), + "\n Ok(())\n}".to_string(), + )); + } + err.multipart_suggestion_verbose( + format!("consider adding return type"), + sugg_spans, + Applicability::MaybeIncorrect, + ); + } + } } /// Add a hint to add a missing borrow or remove an unnecessary one. @@ -4668,14 +4705,11 @@ fn hint_missing_borrow<'tcx>( } if !to_borrow.is_empty() { - err.subdiagnostic(infcx.dcx(), errors::AdjustSignatureBorrow::Borrow { to_borrow }); + err.subdiagnostic(errors::AdjustSignatureBorrow::Borrow { to_borrow }); } if !remove_borrow.is_empty() { - err.subdiagnostic( - infcx.dcx(), - errors::AdjustSignatureBorrow::RemoveBorrow { remove_borrow }, - ); + err.subdiagnostic(errors::AdjustSignatureBorrow::RemoveBorrow { remove_borrow }); } } @@ -4747,7 +4781,7 @@ impl<'v> Visitor<'v> for ReturnsVisitor<'v> { } } - fn visit_body(&mut self, body: &'v hir::Body<'v>) { + fn visit_body(&mut self, body: &hir::Body<'v>) { assert!(!self.in_block_tail); self.in_block_tail = true; hir::intravisit::walk_body(self, body); @@ -4825,14 +4859,13 @@ impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> { pub(super) fn get_explanation_based_on_obligation<'tcx>( tcx: TyCtxt<'tcx>, obligation: &PredicateObligation<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - trait_predicate: &ty::PolyTraitPredicate<'tcx>, + trait_predicate: ty::PolyTraitPredicate<'tcx>, pre_message: String, ) -> String { if let ObligationCauseCode::MainFunctionType = obligation.cause.code() { "consider using `()`, or a `Result`".to_owned() } else { - let ty_desc = match trait_ref.skip_binder().self_ty().kind() { + let ty_desc = match trait_predicate.self_ty().skip_binder().kind() { ty::FnDef(_, _) => Some("fn item"), ty::Closure(_, _) => Some("closure"), _ => None, @@ -4857,7 +4890,7 @@ pub(super) fn get_explanation_based_on_obligation<'tcx>( format!( "{pre_message}the trait `{}` is not implemented for{desc} `{}`{post}", trait_predicate.print_modifiers_and_trait_path(), - tcx.short_ty_string(trait_ref.skip_binder().self_ty(), &mut None), + tcx.short_ty_string(trait_predicate.self_ty().skip_binder(), &mut None), ) } else { // "the trait bound `T: !Send` is not satisfied" reads better than "`!Send` is @@ -4907,14 +4940,12 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( // `async fn` should always lower to a single bound... but don't ICE. return None; }; - let Some(hir::PathSegment { args: Some(generics), .. }) = - trait_ref.trait_ref.path.segments.last() + let Some(hir::PathSegment { args: Some(args), .. }) = trait_ref.trait_ref.path.segments.last() else { // desugaring to a single path segment for `Future<...>`. return None; }; - let Some(hir::TypeBindingKind::Equality { term: hir::Term::Ty(future_output_ty) }) = - generics.bindings.get(0).map(|binding| binding.kind) + let Some(future_output_ty) = args.constraints.first().and_then(|constraint| constraint.ty()) else { // Also should never happen. return None; diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index d693bac90dc4..e38f79511977 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -24,10 +24,10 @@ use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart}; use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, FatalError, StashKey}; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; +use rustc_hir::{self as hir, LangItem}; use rustc_hir::{GenericParam, Item, Node}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::{InferOk, TypeTrace}; @@ -46,7 +46,6 @@ use rustc_middle::ty::{ TypeVisitableExt, Upcast, }; use rustc_middle::{bug, span_bug}; -use rustc_session::config::DumpSolverProofTree; use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; @@ -56,8 +55,8 @@ use std::fmt; use std::iter; use super::{ - dump_proof_tree, ArgKind, CandidateSimilarity, FindExprBySpan, FindTypeParam, - GetSafeTransmuteErrorAndReason, HasNumericInferVisitor, ImplCandidate, UnsatisfiedConst, + ArgKind, CandidateSimilarity, FindExprBySpan, FindTypeParam, GetSafeTransmuteErrorAndReason, + HasNumericInferVisitor, ImplCandidate, UnsatisfiedConst, }; pub use rustc_infer::traits::error_reporting::*; @@ -119,7 +118,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // with more relevant type information and hide redundant E0282 errors. errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) - if Some(pred.def_id()) == self.tcx.lang_items().sized_trait() => + if self.tcx.is_lang_item(pred.def_id(), LangItem::Sized) => { 1 } @@ -369,13 +368,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { error: &SelectionError<'tcx>, ) -> ErrorGuaranteed { let tcx = self.tcx; - - if tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default() - == DumpSolverProofTree::OnError - { - dump_proof_tree(root_obligation, self.infcx); - } - let mut span = obligation.cause.span; let mut err = match *error { @@ -420,8 +412,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => { - let trait_predicate = bound_predicate.rebind(trait_predicate); - let trait_predicate = self.resolve_vars_if_possible(trait_predicate); + let leaf_trait_predicate = + self.resolve_vars_if_possible(bound_predicate.rebind(trait_predicate)); // Let's use the root obligation as the main message, when we care about the // most general case ("X doesn't implement Pattern<'_>") over the case that @@ -432,7 +424,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let (main_trait_predicate, o) = if let ty::PredicateKind::Clause( ty::ClauseKind::Trait(root_pred) ) = root_obligation.predicate.kind().skip_binder() - && !trait_predicate.self_ty().skip_binder().has_escaping_bound_vars() + && !leaf_trait_predicate.self_ty().skip_binder().has_escaping_bound_vars() && !root_pred.self_ty().has_escaping_bound_vars() // The type of the leaf predicate is (roughly) the same as the type // from the root predicate, as a proxy for "we care about the root" @@ -442,20 +434,20 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // `T: Trait` && `&&T: OtherTrait`, we want `OtherTrait` self.can_eq( obligation.param_env, - trait_predicate.self_ty().skip_binder(), + leaf_trait_predicate.self_ty().skip_binder(), root_pred.self_ty().peel_refs(), ) // `&str: Iterator` && `&str: IntoIterator`, we want `IntoIterator` || self.can_eq( obligation.param_env, - trait_predicate.self_ty().skip_binder(), + leaf_trait_predicate.self_ty().skip_binder(), root_pred.self_ty(), ) ) // The leaf trait and the root trait are different, so as to avoid // talking about `&mut T: Trait` and instead remain talking about // `T: Trait` instead - && trait_predicate.def_id() != root_pred.def_id() + && leaf_trait_predicate.def_id() != root_pred.def_id() // The root trait is not `Unsize`, as to avoid talking about it in // `tests/ui/coercion/coerce-issue-49593-box-never.rs`. && Some(root_pred.def_id()) != self.tcx.lang_items().unsize_trait() @@ -467,13 +459,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { root_obligation, ) } else { - (trait_predicate, &obligation) + (leaf_trait_predicate, &obligation) }; - let trait_ref = main_trait_predicate.to_poly_trait_ref(); + let main_trait_ref = main_trait_predicate.to_poly_trait_ref(); + let leaf_trait_ref = leaf_trait_predicate.to_poly_trait_ref(); if let Some(guar) = self.emit_specialized_closure_kind_error( &obligation, - trait_ref, + leaf_trait_ref, ) { return guar; } @@ -481,7 +474,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // FIXME(effects) let predicate_is_const = false; - if let Err(guar) = trait_predicate.error_reported() + if let Err(guar) = leaf_trait_predicate.error_reported() { return guar; } @@ -515,16 +508,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { notes, parent_label, append_const_msg, - } = self.on_unimplemented_note(trait_ref, o, &mut long_ty_file); + } = self.on_unimplemented_note(main_trait_ref, o, &mut long_ty_file); + let have_alt_message = message.is_some() || label.is_some(); - let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id()); + let is_try_conversion = self.is_try_conversion(span, main_trait_ref.def_id()); let is_unsize = - Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait(); + self.tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Unsize); let (message, notes, append_const_msg) = if is_try_conversion { ( Some(format!( "`?` couldn't convert the error to `{}`", - trait_ref.skip_binder().self_ty(), + main_trait_ref.skip_binder().self_ty(), )), vec![ "the question mark operation (`?`) implicitly performs a \ @@ -538,20 +532,20 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; let err_msg = self.get_standard_error_message( - &main_trait_predicate, + main_trait_predicate, message, predicate_is_const, append_const_msg, post_message, ); - let (err_msg, safe_transmute_explanation) = if Some(trait_ref.def_id()) + let (err_msg, safe_transmute_explanation) = if Some(main_trait_ref.def_id()) == self.tcx.lang_items().transmute_trait() { // Recompute the safe transmute reason and use that for the error reporting match self.get_safe_transmute_error_and_reason( obligation.clone(), - trait_ref, + main_trait_ref, span, ) { GetSafeTransmuteErrorAndReason::Silent => { @@ -579,7 +573,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } let mut suggested = false; if is_try_conversion { - suggested = self.try_conversion_context(&obligation, trait_ref.skip_binder(), &mut err); + suggested = self.try_conversion_context(&obligation, main_trait_ref.skip_binder(), &mut err); } if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) { @@ -587,19 +581,19 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ret_span, format!( "expected `{}` because of this", - trait_ref.skip_binder().self_ty() + main_trait_ref.skip_binder().self_ty() ), ); } - if Some(trait_ref.def_id()) == tcx.lang_items().tuple_trait() { + if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Tuple) { self.add_tuple_trait_message( obligation.cause.code().peel_derives(), &mut err, ); } - if Some(trait_ref.def_id()) == tcx.lang_items().drop_trait() + if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Drop) && predicate_is_const { err.note("`~const Drop` was renamed to `~const Destruct`"); @@ -609,20 +603,25 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let explanation = get_explanation_based_on_obligation( self.tcx, &obligation, - trait_ref, - &trait_predicate, + leaf_trait_predicate, pre_message, ); self.check_for_binding_assigned_block_without_tail_expression( &obligation, &mut err, - trait_predicate, + leaf_trait_predicate, ); + self.suggest_add_result_as_return_type( + &obligation, + &mut err, + leaf_trait_predicate, + ); + if self.suggest_add_reference_to_arg( &obligation, &mut err, - trait_predicate, + leaf_trait_predicate, have_alt_message, ) { self.note_obligation_cause(&mut err, &obligation); @@ -634,7 +633,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // If it has a custom `#[rustc_on_unimplemented]` // error message, let's display it as the label! err.span_label(span, s); - if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { + if !matches!(leaf_trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { // When the self type is a type param We don't need to "the trait // `std::marker::Sized` is not implemented for `T`" as we will point // at the type param with a label to suggest constraining it. @@ -649,7 +648,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let ObligationCauseCode::Coercion { source, target } = *obligation.cause.code().peel_derives() { - if Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { + if self.tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Sized) { self.suggest_borrowing_for_object_cast( &mut err, root_obligation, @@ -661,7 +660,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let UnsatisfiedConst(unsatisfied_const) = self .maybe_add_note_for_unsatisfied_const( - &trait_predicate, + leaf_trait_predicate, &mut err, span, ); @@ -678,15 +677,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { err.span_label(tcx.def_span(body), s); } - self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref); - self.suggest_dereferencing_index(&obligation, &mut err, trait_predicate); - suggested |= self.suggest_dereferences(&obligation, &mut err, trait_predicate); - suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate); - let impl_candidates = self.find_similar_impl_candidates(trait_predicate); + self.suggest_floating_point_literal(&obligation, &mut err, leaf_trait_ref); + self.suggest_dereferencing_index(&obligation, &mut err, leaf_trait_predicate); + suggested |= self.suggest_dereferences(&obligation, &mut err, leaf_trait_predicate); + suggested |= self.suggest_fn_call(&obligation, &mut err, leaf_trait_predicate); + let impl_candidates = self.find_similar_impl_candidates(leaf_trait_predicate); suggested = if let &[cand] = &impl_candidates[..] { let cand = cand.trait_ref; if let (ty::FnPtr(_), ty::FnDef(..)) = - (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) + (cand.self_ty().kind(), main_trait_ref.self_ty().skip_binder().kind()) { err.span_suggestion( span.shrink_to_hi(), @@ -706,31 +705,31 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { false } || suggested; suggested |= - self.suggest_remove_reference(&obligation, &mut err, trait_predicate); + self.suggest_remove_reference(&obligation, &mut err, leaf_trait_predicate); suggested |= self.suggest_semicolon_removal( &obligation, &mut err, span, - trait_predicate, + leaf_trait_predicate, ); - self.note_version_mismatch(&mut err, &trait_ref); + self.note_version_mismatch(&mut err, leaf_trait_ref); self.suggest_remove_await(&obligation, &mut err); - self.suggest_derive(&obligation, &mut err, trait_predicate); + self.suggest_derive(&obligation, &mut err, leaf_trait_predicate); - if Some(trait_ref.def_id()) == tcx.lang_items().try_trait() { + if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Try) { self.suggest_await_before_try( &mut err, &obligation, - trait_predicate, + leaf_trait_predicate, span, ); } - if self.suggest_add_clone_to_arg(&obligation, &mut err, trait_predicate) { + if self.suggest_add_clone_to_arg(&obligation, &mut err, leaf_trait_predicate) { return err.emit(); } - if self.suggest_impl_trait(&mut err, &obligation, trait_predicate) { + if self.suggest_impl_trait(&mut err, &obligation, leaf_trait_predicate) { return err.emit(); } @@ -745,9 +744,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); } - let is_fn_trait = tcx.is_fn_trait(trait_ref.def_id()); + let is_fn_trait = tcx.is_fn_trait(leaf_trait_ref.def_id()); let is_target_feature_fn = if let ty::FnDef(def_id, _) = - *trait_ref.skip_binder().self_ty().kind() + *leaf_trait_ref.skip_binder().self_ty().kind() { !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() } else { @@ -761,8 +760,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.try_to_add_help_message( &obligation, - trait_ref, - &trait_predicate, + leaf_trait_predicate, &mut err, span, is_fn_trait, @@ -773,17 +771,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Changing mutability doesn't make a difference to whether we have // an `Unsize` impl (Fixes ICE in #71036) if !is_unsize { - self.suggest_change_mut(&obligation, &mut err, trait_predicate); + self.suggest_change_mut(&obligation, &mut err, leaf_trait_predicate); } // If this error is due to `!: Trait` not implemented but `(): Trait` is // implemented, and fallback has occurred, then it could be due to a // variable that used to fallback to `()` now falling back to `!`. Issue a // note informing about the change in behaviour. - if trait_predicate.skip_binder().self_ty().is_never() + if leaf_trait_predicate.skip_binder().self_ty().is_never() && self.fallback_has_occurred { - let predicate = trait_predicate.map_bound(|trait_pred| { + let predicate = leaf_trait_predicate.map_bound(|trait_pred| { trait_pred.with_self_ty(self.tcx, tcx.types.unit) }); let unit_obligation = obligation.with(tcx, predicate); @@ -798,8 +796,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - self.explain_hrtb_projection(&mut err, trait_predicate, obligation.param_env, &obligation.cause); - self.suggest_desugaring_async_fn_in_trait(&mut err, trait_ref); + self.explain_hrtb_projection(&mut err, leaf_trait_predicate, obligation.param_env, &obligation.cause); + self.suggest_desugaring_async_fn_in_trait(&mut err, main_trait_ref); // Return early if the trait is Debug or Display and the invocation // originates within a standard library macro, because the output @@ -817,15 +815,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if in_std_macro && matches!( - self.tcx.get_diagnostic_name(trait_ref.def_id()), + self.tcx.get_diagnostic_name(leaf_trait_ref.def_id()), Some(sym::Debug | sym::Display) ) { return err.emit(); } - - err } @@ -884,56 +880,25 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) => { - // Errors for `ConstEvaluatable` predicates show up as - // `SelectionError::ConstEvalFailure`, - // not `Unimplemented`. + // Errors for `ConstEvaluatable` predicates show up as + // `SelectionError::ConstEvalFailure`, + // not `Unimplemented`. + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) + // Errors for `ConstEquate` predicates show up as + // `SelectionError::ConstEvalFailure`, + // not `Unimplemented`. + | ty::PredicateKind::ConstEquate { .. } + // Ambiguous predicates should never error + | ty::PredicateKind::Ambiguous + | ty::PredicateKind::NormalizesTo { .. } + | ty::PredicateKind::AliasRelate { .. } + | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType { .. }) => { span_bug!( span, - "const-evaluatable requirement gave wrong error: `{:?}`", + "Unexpected `Predicate` for `SelectionError`: `{:?}`", obligation ) } - - ty::PredicateKind::ConstEquate(..) => { - // Errors for `ConstEquate` predicates show up as - // `SelectionError::ConstEvalFailure`, - // not `Unimplemented`. - span_bug!( - span, - "const-equate requirement gave wrong error: `{:?}`", - obligation - ) - } - - ty::PredicateKind::Ambiguous => span_bug!(span, "ambiguous"), - - ty::PredicateKind::NormalizesTo(..) => span_bug!( - span, - "NormalizesTo predicate should never be the predicate cause of a SelectionError" - ), - - ty::PredicateKind::AliasRelate(..) => span_bug!( - span, - "AliasRelate predicate should never be the predicate cause of a SelectionError" - ), - - ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { - let mut diag = self.dcx().struct_span_err( - span, - format!("the constant `{ct}` is not of type `{ty}`"), - ); - self.note_type_err( - &mut diag, - &obligation.cause, - None, - None, - TypeError::Sorts(ty::error::ExpectedFound::new(true, ty, ct.ty())), - false, - false, - ); - diag - } } } @@ -996,6 +961,24 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { Overflow(_) => { bug!("overflow should be handled before the `report_selection_error` path"); } + + SelectionError::ConstArgHasWrongType { ct, ct_ty, expected_ty } => { + let mut diag = self.dcx().struct_span_err( + span, + format!("the constant `{ct}` is not of type `{expected_ty}`"), + ); + + self.note_type_err( + &mut diag, + &obligation.cause, + None, + None, + TypeError::Sorts(ty::error::ExpectedFound::new(true, expected_ty, ct_ty)), + false, + false, + ); + diag + } }; self.note_obligation_cause(&mut err, &obligation); @@ -1003,6 +986,31 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { err.emit() } + fn apply_do_not_recommend(&self, obligation: &mut PredicateObligation<'tcx>) -> bool { + let mut base_cause = obligation.cause.code().clone(); + let mut applied_do_not_recommend = false; + loop { + if let ObligationCauseCode::ImplDerived(ref c) = base_cause { + if self.tcx.has_attrs_with_path( + c.impl_or_alias_def_id, + &[sym::diagnostic, sym::do_not_recommend], + ) { + let code = (*c.derived.parent_code).clone(); + obligation.cause.map_code(|_| code); + obligation.predicate = c.derived.parent_trait_pred.upcast(self.tcx); + applied_do_not_recommend = true; + } + } + if let Some((parent_cause, _parent_pred)) = base_cause.parent() { + base_cause = parent_cause.clone(); + } else { + break; + } + } + + applied_do_not_recommend + } + fn emit_specialized_closure_kind_error( &self, obligation: &PredicateObligation<'tcx>, @@ -1012,7 +1020,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // doesn't extend the goal kind. This is worth reporting, but we can only do so // if we actually know which closure this goal comes from, so look at the cause // to see if we can extract that information. - if Some(trait_ref.def_id()) == self.tcx.lang_items().async_fn_kind_helper() + if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::AsyncFnKindHelper) && let Some(found_kind) = trait_ref.skip_binder().args.type_at(0).to_opt_closure_kind() && let Some(expected_kind) = trait_ref.skip_binder().args.type_at(1).to_opt_closure_kind() @@ -1500,10 +1508,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { #[instrument(skip(self), level = "debug")] fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) -> ErrorGuaranteed { - if self.tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default() - == DumpSolverProofTree::OnError + let mut error = FulfillmentError { + obligation: error.obligation.clone(), + code: error.code.clone(), + root_obligation: error.root_obligation.clone(), + }; + if matches!( + error.code, + FulfillmentErrorCode::Select(crate::traits::SelectionError::Unimplemented) + | FulfillmentErrorCode::Project(_) + ) && self.apply_do_not_recommend(&mut error.obligation) { - dump_proof_tree(&error.root_obligation, self.infcx); + error.code = FulfillmentErrorCode::Select(SelectionError::Unimplemented); } match error.code { @@ -1728,7 +1744,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let self_ty = pred.projection_term.self_ty(); with_forced_trimmed_paths! { - if Some(pred.projection_term.def_id) == self.tcx.lang_items().fn_once_output() { + if self.tcx.is_lang_item(pred.projection_term.def_id,LangItem::FnOnceOutput) { let fn_kind = self_ty.prefix_string(self.tcx); let item = match self_ty.kind() { ty::FnDef(def, _) => self.tcx.item_name(*def).to_string(), @@ -1738,7 +1754,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { "expected `{item}` to be a {fn_kind} that returns `{expected_ty}`, but it \ returns `{normalized_ty}`", )) - } else if Some(trait_def_id) == self.tcx.lang_items().future_trait() { + } else if self.tcx.is_lang_item(trait_def_id, LangItem::Future) { Some(format!( "expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it \ resolves to `{normalized_ty}`" @@ -1767,7 +1783,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ty::Bool => Some(0), ty::Char => Some(1), ty::Str => Some(2), - ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => Some(2), + ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => Some(2), ty::Int(..) | ty::Uint(..) | ty::Float(..) @@ -2052,17 +2068,21 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if all_traits_equal { format!("\n {}", c.self_ty()) } else { - format!("\n {c}") + format!("\n `{}` implements `{}`", c.self_ty(), c.print_only_trait_path()) } }) .collect(); - let end = if candidates.len() <= 9 { candidates.len() } else { 8 }; + let end = if candidates.len() <= 9 || self.tcx.sess.opts.verbose { + candidates.len() + } else { + 8 + }; err.help(format!( "the following {other}types implement trait `{}`:{}{}", trait_ref.print_trait_sugared(), candidates[..end].join(""), - if candidates.len() > 9 { + if candidates.len() > 9 && !self.tcx.sess.opts.verbose { format!("\nand {} others", candidates.len() - 8) } else { String::new() @@ -2216,11 +2236,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait /// with the same path as `trait_ref`, a help message about /// a probable version mismatch is added to `err` - fn note_version_mismatch( - &self, - err: &mut Diag<'_>, - trait_ref: &ty::PolyTraitRef<'tcx>, - ) -> bool { + fn note_version_mismatch(&self, err: &mut Diag<'_>, trait_ref: ty::PolyTraitRef<'tcx>) -> bool { let get_trait_impls = |trait_def_id| { let mut trait_impls = vec![]; self.tcx.for_each_relevant_impl( @@ -2326,7 +2342,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // avoid inundating the user with unnecessary errors, but we now // check upstream for type errors and don't add the obligations to // begin with in those cases. - if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { + if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) { match self.tainted_by_errors() { None => { let err = self.emit_inference_failure_err( @@ -2436,11 +2452,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack()) - && let Some(body_id) = - self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) + && let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) { let mut expr_finder = FindExprBySpan::new(span, self.tcx); - expr_finder.visit_expr(self.tcx.hir().body(body_id).value); + expr_finder.visit_expr(&body.value); if let Some(hir::Expr { kind: @@ -2670,6 +2685,38 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .with_span_label(span, format!("cannot satisfy `{predicate}`")) } } + + // Given some `ConstArgHasType(?x, usize)`, we should not emit an error such as + // "type annotations needed: cannot satisfy the constant `_` has type `usize`" + // Instead we should emit a normal error suggesting the user to turbofish the + // const parameter that is currently being inferred. Unfortunately we cannot + // nicely emit such an error so we delay an ICE incase nobody else reports it + // for us. + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { + return self.tcx.sess.dcx().span_delayed_bug( + span, + format!( + "`ambiguous ConstArgHasType({:?}, {:?}) unaccompanied by inference error`", + ct, ty + ), + ); + } + + ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) + if term.is_infer() => + { + if let Some(e) = self.tainted_by_errors() { + return e; + } + struct_span_code_err!( + self.dcx(), + span, + E0284, + "type annotations needed: cannot normalize `{alias}`", + ) + .with_span_label(span, format!("cannot normalize `{alias}`")) + } + _ => { if let Some(e) = self.tainted_by_errors() { return e; @@ -2878,7 +2925,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let (Some(node), true) = ( self.tcx.hir().get_if_local(item_def_id), - Some(pred.def_id()) == self.tcx.lang_items().sized_trait(), + self.tcx.is_lang_item(pred.def_id(), LangItem::Sized), ) else { return; }; @@ -3009,7 +3056,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn get_standard_error_message( &self, - trait_predicate: &ty::PolyTraitPredicate<'tcx>, + trait_predicate: ty::PolyTraitPredicate<'tcx>, message: Option, predicate_is_const: bool, append_const_msg: Option, @@ -3180,8 +3227,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn try_to_add_help_message( &self, obligation: &PredicateObligation<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - trait_predicate: &ty::PolyTraitPredicate<'tcx>, + trait_predicate: ty::PolyTraitPredicate<'tcx>, err: &mut Diag<'_>, span: Span, is_fn_trait: bool, @@ -3198,16 +3244,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; // Try to report a help message + let trait_def_id = trait_predicate.def_id(); if is_fn_trait && let Ok((implemented_kind, params)) = self.type_implements_fn_trait( obligation.param_env, - trait_ref.self_ty(), + trait_predicate.self_ty(), trait_predicate.skip_binder().polarity, ) { - self.add_help_message_for_fn_trait(trait_ref, err, implemented_kind, params); - } else if !trait_ref.has_non_region_infer() - && self.predicate_can_apply(obligation.param_env, *trait_predicate) + self.add_help_message_for_fn_trait( + trait_predicate.to_poly_trait_ref(), + err, + implemented_kind, + params, + ); + } else if !trait_predicate.has_non_region_infer() + && self.predicate_can_apply(obligation.param_env, trait_predicate) { // If a where-clause may be useful, remind the // user that they can add it. @@ -3218,25 +3270,25 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // which is somewhat confusing. self.suggest_restricting_param_bound( err, - *trait_predicate, + trait_predicate, None, obligation.cause.body_id, ); - } else if trait_ref.def_id().is_local() - && self.tcx.trait_impls_of(trait_ref.def_id()).is_empty() - && !self.tcx.trait_is_auto(trait_ref.def_id()) - && !self.tcx.trait_is_alias(trait_ref.def_id()) + } else if trait_def_id.is_local() + && self.tcx.trait_impls_of(trait_def_id).is_empty() + && !self.tcx.trait_is_auto(trait_def_id) + && !self.tcx.trait_is_alias(trait_def_id) { err.span_help( - self.tcx.def_span(trait_ref.def_id()), + self.tcx.def_span(trait_def_id), crate::fluent_generated::trait_selection_trait_has_no_impls, ); } else if !suggested && !unsatisfied_const { // Can't show anything else useful, try to find similar impls. - let impl_candidates = self.find_similar_impl_candidates(*trait_predicate); + let impl_candidates = self.find_similar_impl_candidates(trait_predicate); if !self.report_similar_impl_candidates( &impl_candidates, - trait_ref, + trait_predicate.to_poly_trait_ref(), body_def_id, err, true, @@ -3244,7 +3296,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) { self.report_similar_impl_candidates_for_root_obligation( obligation, - *trait_predicate, + trait_predicate, body_def_id, err, ); @@ -3253,7 +3305,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.suggest_convert_to_slice( err, obligation, - trait_ref, + trait_predicate.to_poly_trait_ref(), impl_candidates.as_slice(), span, ); @@ -3318,7 +3370,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn maybe_add_note_for_unsatisfied_const( &self, - _trait_predicate: &ty::PolyTraitPredicate<'tcx>, + _trait_predicate: ty::PolyTraitPredicate<'tcx>, _err: &mut Diag<'_>, _span: Span, ) -> UnsatisfiedConst { diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 07fcf109fdaa..bce5c7101cc5 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -6,7 +6,7 @@ use rustc_data_structures::obligation_forest::ProcessResult; use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome}; use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; use rustc_infer::infer::DefineOpaqueTypes; -use rustc_infer::traits::ProjectionCacheKey; +use rustc_infer::traits::{FromSolverError, ProjectionCacheKey}; use rustc_infer::traits::{PolyTraitObligation, SelectionError, TraitEngine}; use rustc_middle::bug; use rustc_middle::mir::interpret::ErrorHandled; @@ -16,13 +16,13 @@ use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, Binder, Const, TypeVisitableExt}; use std::marker::PhantomData; -use super::const_evaluatable; use super::project::{self, ProjectAndUnifyResult}; use super::select::SelectionContext; use super::wf; use super::EvaluationResult; use super::PredicateObligation; use super::Unimplemented; +use super::{const_evaluatable, ScrubbedTraitError}; use super::{FulfillmentError, FulfillmentErrorCode}; use crate::traits::project::PolyProjectionObligation; @@ -50,7 +50,7 @@ impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> { /// along. Once all type inference constraints have been generated, the /// method `select_all_or_error` can be used to report any remaining /// ambiguous cases as errors. -pub struct FulfillmentContext<'tcx> { +pub struct FulfillmentContext<'tcx, E: 'tcx> { /// A list of all obligations that have been registered with this /// fulfillment context. predicates: ObligationForest>, @@ -60,6 +60,8 @@ pub struct FulfillmentContext<'tcx> { /// gets rolled back. Because of this we explicitly check that we only /// use the context in exactly this snapshot. usable_in_snapshot: usize, + + _errors: PhantomData, } #[derive(Clone, Debug)] @@ -76,9 +78,12 @@ pub struct PendingPredicateObligation<'tcx> { #[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(PendingPredicateObligation<'_>, 72); -impl<'tcx> FulfillmentContext<'tcx> { +impl<'tcx, E> FulfillmentContext<'tcx, E> +where + E: FromSolverError<'tcx, OldSolverError<'tcx>>, +{ /// Creates a new fulfillment context. - pub(super) fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentContext<'tcx> { + pub(super) fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentContext<'tcx, E> { assert!( !infcx.next_trait_solver(), "old trait solver fulfillment context created when \ @@ -87,13 +92,15 @@ impl<'tcx> FulfillmentContext<'tcx> { FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: infcx.num_open_snapshots(), + _errors: PhantomData, } } /// Attempts to select obligations using `selcx`. - fn select(&mut self, selcx: SelectionContext<'_, 'tcx>) -> Vec> { + fn select(&mut self, selcx: SelectionContext<'_, 'tcx>) -> Vec { let span = debug_span!("select", obligation_forest_size = ?self.predicates.len()); let _enter = span.enter(); + let infcx = selcx.infcx; // Process pending obligations. let outcome: Outcome<_, _> = @@ -102,8 +109,11 @@ impl<'tcx> FulfillmentContext<'tcx> { // FIXME: if we kept the original cache key, we could mark projection // obligations as complete for the projection cache here. - let errors: Vec> = - outcome.errors.into_iter().map(to_fulfillment_error).collect(); + let errors: Vec = outcome + .errors + .into_iter() + .map(|err| E::from_solver_error(infcx, OldSolverError(err))) + .collect(); debug!( "select({} predicates remaining, {} errors) done", @@ -115,7 +125,10 @@ impl<'tcx> FulfillmentContext<'tcx> { } } -impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { +impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentContext<'tcx, E> +where + E: FromSolverError<'tcx, OldSolverError<'tcx>>, +{ #[inline] fn register_predicate_obligation( &mut self, @@ -134,18 +147,15 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { .register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] }); } - fn collect_remaining_errors( - &mut self, - _infcx: &InferCtxt<'tcx>, - ) -> Vec> { + fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { self.predicates .to_errors(FulfillmentErrorCode::Ambiguity { overflow: None }) .into_iter() - .map(to_fulfillment_error) + .map(|err| E::from_solver_error(infcx, OldSolverError(err))) .collect() } - fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec> { + fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { let selcx = SelectionContext::new(infcx); self.select(selcx) } @@ -411,7 +421,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } ty::PredicateKind::ObjectSafe(trait_def_id) => { - if !self.selcx.tcx().check_is_object_safe(trait_def_id) { + if !self.selcx.tcx().is_object_safe(trait_def_id) { ProcessResult::Error(FulfillmentErrorCode::Select(Unimplemented)) } else { ProcessResult::Changed(vec![]) @@ -429,15 +439,49 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { // This is because this is not ever a useful obligation to report // as the cause of an overflow. ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { - match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq( + let ct = infcx.shallow_resolve_const(ct); + let ct_ty = match ct.kind() { + ty::ConstKind::Infer(var) => { + let var = match var { + ty::InferConst::Var(vid) => TyOrConstInferVar::Const(vid), + ty::InferConst::EffectVar(vid) => TyOrConstInferVar::Effect(vid), + ty::InferConst::Fresh(_) => { + bug!("encountered fresh const in fulfill") + } + }; + pending_obligation.stalled_on.clear(); + pending_obligation.stalled_on.extend([var]); + return ProcessResult::Unchanged; + } + ty::ConstKind::Error(_) => return ProcessResult::Changed(vec![]), + ty::ConstKind::Value(ty, _) => ty, + ty::ConstKind::Unevaluated(uv) => { + infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args) + } + // FIXME(generic_const_exprs): we should construct an alias like + // `>::Output` when this is an `Expr` representing + // `lhs + rhs`. + ty::ConstKind::Expr(_) => { + return ProcessResult::Changed(mk_pending(vec![])); + } + ty::ConstKind::Placeholder(_) => { + bug!("placeholder const {:?} in old solver", ct) + } + ty::ConstKind::Bound(_, _) => bug!("escaping bound vars in {:?}", ct), + ty::ConstKind::Param(param_ct) => { + param_ct.find_ty_from_env(obligation.param_env) + } + }; + + match infcx.at(&obligation.cause, obligation.param_env).eq( // Only really excercised by generic_const_exprs DefineOpaqueTypes::Yes, - ct.ty(), + ct_ty, ty, ) { Ok(inf_ok) => ProcessResult::Changed(mk_pending(inf_ok.into_obligations())), Err(_) => ProcessResult::Error(FulfillmentErrorCode::Select( - SelectionError::Unimplemented, + SelectionError::ConstArgHasWrongType { ct, ct_ty, expected_ty: ty }, )), } } @@ -566,10 +610,13 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { { if let Ok(new_obligations) = infcx .at(&obligation.cause, obligation.param_env) - .trace(c1, c2) // Can define opaque types as this is only reachable with // `generic_const_exprs` - .eq(DefineOpaqueTypes::Yes, a.args, b.args) + .eq( + DefineOpaqueTypes::Yes, + ty::AliasTerm::from(a), + ty::AliasTerm::from(b), + ) { return ProcessResult::Changed(mk_pending( new_obligations.into_obligations(), @@ -599,7 +646,6 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { match self.selcx.infcx.try_const_eval_resolve( obligation.param_env, unevaluated, - c.ty(), obligation.cause.span, ) { Ok(val) => Ok(val), @@ -816,13 +862,31 @@ fn args_infer_vars<'a, 'tcx>( .filter_map(TyOrConstInferVar::maybe_from_generic_arg) } -fn to_fulfillment_error<'tcx>( - error: Error, FulfillmentErrorCode<'tcx>>, -) -> FulfillmentError<'tcx> { - let mut iter = error.backtrace.into_iter(); - let obligation = iter.next().unwrap().obligation; - // The root obligation is the last item in the backtrace - if there's only - // one item, then it's the same as the main obligation - let root_obligation = iter.next_back().map_or_else(|| obligation.clone(), |e| e.obligation); - FulfillmentError::new(obligation, error.error, root_obligation) +#[derive(Debug)] +pub struct OldSolverError<'tcx>( + Error, FulfillmentErrorCode<'tcx>>, +); + +impl<'tcx> FromSolverError<'tcx, OldSolverError<'tcx>> for FulfillmentError<'tcx> { + fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: OldSolverError<'tcx>) -> Self { + let mut iter = error.0.backtrace.into_iter(); + let obligation = iter.next().unwrap().obligation; + // The root obligation is the last item in the backtrace - if there's only + // one item, then it's the same as the main obligation + let root_obligation = iter.next_back().map_or_else(|| obligation.clone(), |e| e.obligation); + FulfillmentError::new(obligation, error.0.error, root_obligation) + } +} + +impl<'tcx> FromSolverError<'tcx, OldSolverError<'tcx>> for ScrubbedTraitError<'tcx> { + fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: OldSolverError<'tcx>) -> Self { + match error.0.error { + FulfillmentErrorCode::Select(_) + | FulfillmentErrorCode::Project(_) + | FulfillmentErrorCode::Subtype(_, _) + | FulfillmentErrorCode::ConstEquate(_, _) => ScrubbedTraitError::TrueError, + FulfillmentErrorCode::Ambiguity { overflow: _ } => ScrubbedTraitError::Ambiguity, + FulfillmentErrorCode::Cycle(cycle) => ScrubbedTraitError::Cycle(cycle), + } + } } diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index da2b004761fc..baec2268629d 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -1,17 +1,14 @@ //! Miscellaneous type-system utilities that are too small to deserve their own modules. use crate::regions::InferCtxtRegionExt; -use crate::traits::{self, ObligationCause, ObligationCtxt}; +use crate::traits::{self, FulfillmentError, ObligationCause}; use hir::LangItem; use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; -use rustc_infer::infer::canonical::Canonical; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; -use rustc_infer::traits::query::NoSolution; -use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError}; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::DUMMY_SP; use super::outlives_bounds::InferCtxtExt; @@ -140,7 +137,7 @@ pub fn all_fields_implement_trait<'tcx>( for field in &variant.fields { // Do this per-field to get better error messages. let infcx = tcx.infer_ctxt().build(); - let ocx = traits::ObligationCtxt::new(&infcx); + let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx); let unnormalized_ty = field.ty(tcx, args); if unnormalized_ty.references_error() { @@ -207,19 +204,3 @@ pub fn all_fields_implement_trait<'tcx>( if infringing.is_empty() { Ok(()) } else { Err(infringing) } } - -pub fn check_tys_might_be_eq<'tcx>( - tcx: TyCtxt<'tcx>, - canonical: Canonical<'tcx, ty::ParamEnvAnd<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, -) -> Result<(), NoSolution> { - let (infcx, key, _) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical); - let (param_env, (ty_a, ty_b)) = key.into_parts(); - let ocx = ObligationCtxt::new(&infcx); - - let result = ocx.eq(&ObligationCause::dummy(), param_env, ty_a, ty_b); - // use `select_where_possible` instead of `select_all_or_error` so that - // we don't get errors from obligations being ambiguous. - let errors = ocx.select_where_possible(); - - if errors.len() > 0 || result.is_err() { Err(NoSolution) } else { Ok(()) } -} diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 204bb487c860..af6bfdae4402 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -32,6 +32,7 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_errors::ErrorGuaranteed; use rustc_middle::query::Providers; use rustc_middle::span_bug; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder, TypeSuperVisitable, Upcast}; @@ -46,7 +47,7 @@ pub use self::coherence::{add_placeholder_note, orphan_check_trait_ref, overlapp pub use self::coherence::{InCrate, IsFirstInputType, UncoveredTyParams}; pub use self::coherence::{OrphanCheckErr, OrphanCheckMode, OverlapResult}; pub use self::engine::{ObligationCtxt, TraitEngineExt}; -pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation}; +pub use self::fulfill::{FulfillmentContext, OldSolverError, PendingPredicateObligation}; pub use self::normalize::NormalizeExt; pub use self::object_safety::hir_ty_lowering_object_safety_violations; pub use self::object_safety::is_vtable_safe_method; @@ -64,15 +65,86 @@ pub use self::structural_match::search_for_structural_match_violation; pub use self::structural_normalize::StructurallyNormalizeExt; pub use self::util::elaborate; pub use self::util::{expand_trait_aliases, TraitAliasExpander, TraitAliasExpansionInfo}; -pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices}; -pub use self::util::{ - supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item, - SupertraitDefIds, -}; +pub use self::util::{impl_item_is_final, upcast_choices}; +pub use self::util::{supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item}; pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer}; pub use rustc_infer::traits::*; +pub struct FulfillmentError<'tcx> { + pub obligation: PredicateObligation<'tcx>, + pub code: FulfillmentErrorCode<'tcx>, + /// Diagnostics only: the 'root' obligation which resulted in + /// the failure to process `obligation`. This is the obligation + /// that was initially passed to `register_predicate_obligation` + pub root_obligation: PredicateObligation<'tcx>, +} + +impl<'tcx> FulfillmentError<'tcx> { + pub fn new( + obligation: PredicateObligation<'tcx>, + code: FulfillmentErrorCode<'tcx>, + root_obligation: PredicateObligation<'tcx>, + ) -> FulfillmentError<'tcx> { + FulfillmentError { obligation, code, root_obligation } + } + + pub fn is_true_error(&self) -> bool { + match self.code { + FulfillmentErrorCode::Select(_) + | FulfillmentErrorCode::Project(_) + | FulfillmentErrorCode::Subtype(_, _) + | FulfillmentErrorCode::ConstEquate(_, _) => true, + FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => { + false + } + } + } +} + +impl<'tcx> Debug for FulfillmentError<'tcx> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "FulfillmentError({:?},{:?})", self.obligation, self.code) + } +} + +#[derive(Clone)] +pub enum FulfillmentErrorCode<'tcx> { + /// Inherently impossible to fulfill; this trait is implemented if and only + /// if it is already implemented. + Cycle(Vec>), + Select(SelectionError<'tcx>), + Project(MismatchedProjectionTypes<'tcx>), + Subtype(ExpectedFound>, TypeError<'tcx>), // always comes from a SubtypePredicate + ConstEquate(ExpectedFound>, TypeError<'tcx>), + Ambiguity { + /// Overflow is only `Some(suggest_recursion_limit)` when using the next generation + /// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by + /// emitting a fatal error instead. + overflow: Option, + }, +} + +impl<'tcx> Debug for FulfillmentErrorCode<'tcx> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + FulfillmentErrorCode::Select(ref e) => write!(f, "{e:?}"), + FulfillmentErrorCode::Project(ref e) => write!(f, "{e:?}"), + FulfillmentErrorCode::Subtype(ref a, ref b) => { + write!(f, "CodeSubtypeError({a:?}, {b:?})") + } + FulfillmentErrorCode::ConstEquate(ref a, ref b) => { + write!(f, "CodeConstEquateError({a:?}, {b:?})") + } + FulfillmentErrorCode::Ambiguity { overflow: None } => write!(f, "Ambiguity"), + FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => { + write!(f, "Overflow({suggest_increasing_limit})") + } + FulfillmentErrorCode::Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"), + } + } +} + /// Whether to skip the leak check, as part of a future compatibility warning step. /// /// The "default" for skip-leak-check corresponds to the current @@ -185,6 +257,7 @@ fn do_normalize_predicates<'tcx>( predicates: Vec>, ) -> Result>, ErrorGuaranteed> { let span = cause.span; + // FIXME. We should really... do something with these region // obligations. But this call just continues the older // behavior (i.e., doesn't cause any new bugs), and it would @@ -286,7 +359,7 @@ pub fn normalize_param_env_or_error<'tcx>( // `ty::Const::normalize` can only work with properly preserved binders. if c.has_escaping_bound_vars() { - return ty::Const::new_misc_error(self.0, c.ty()); + return ty::Const::new_misc_error(self.0); } // While it is pretty sus to be evaluating things with an empty param env, it // should actually be okay since without `feature(generic_const_exprs)` the only @@ -409,7 +482,7 @@ pub fn fully_normalize<'tcx, T>( where T: TypeFoldable>, { - let ocx = ObligationCtxt::new(infcx); + let ocx = ObligationCtxt::new_with_diagnostics(infcx); debug!(?value); let normalized_value = ocx.normalize(&cause, param_env, value); debug!(?normalized_value); @@ -550,8 +623,8 @@ pub fn provide(providers: &mut Providers) { *providers = Providers { specialization_graph_of: specialize::specialization_graph_provider, specializes: specialize::specializes, + specialization_enabled_in: specialize::specialization_enabled_in, instantiate_and_check_impossible_predicates, - check_tys_might_be_eq: misc::check_tys_might_be_eq, is_impossible_associated_item, ..*providers }; diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index d10aee2d4e29..e7ab0b7791c4 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -3,11 +3,13 @@ use super::error_reporting::OverflowCause; use super::error_reporting::TypeErrCtxtExt; use super::SelectionContext; use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer}; +use crate::solve::NextSolverError; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::infer::at::At; use rustc_infer::infer::InferOk; +use rustc_infer::traits::FromSolverError; use rustc_infer::traits::PredicateObligation; -use rustc_infer::traits::{FulfillmentError, Normalized, Obligation, TraitEngine}; +use rustc_infer::traits::{Normalized, Obligation, TraitEngine}; use rustc_macros::extension; use rustc_middle::traits::{ObligationCause, ObligationCauseCode, Reveal}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder}; @@ -44,11 +46,15 @@ impl<'tcx> At<'_, 'tcx> { /// existing fulfillment context in the old solver. Once we also eagerly prove goals with /// the old solver or have removed the old solver, remove `traits::fully_normalize` and /// rename this function to `At::fully_normalize`. - fn deeply_normalize>>( + fn deeply_normalize( self, value: T, - fulfill_cx: &mut dyn TraitEngine<'tcx>, - ) -> Result>> { + fulfill_cx: &mut dyn TraitEngine<'tcx, E>, + ) -> Result> + where + T: TypeFoldable>, + E: FromSolverError<'tcx, NextSolverError<'tcx>>, + { if self.infcx.next_trait_solver() { crate::solve::deeply_normalize(self, value) } else { @@ -253,7 +259,7 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx obligations.len = ?self.obligations.len(), "AssocTypeNormalizer: normalized type" ); - normalized_ty.ty().unwrap() + normalized_ty.expect_type() } ty::Projection => { @@ -283,7 +289,7 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx ) .ok() .flatten() - .map(|term| term.ty().unwrap()) + .map(|term| term.expect_type()) .map(|normalized_ty| { PlaceholderReplacer::replace_placeholders( infcx, diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index abb19c7efd86..fc5a2875b67a 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -13,7 +13,7 @@ use super::elaborate; use crate::infer::TyCtxtInferExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{self, Obligation, ObligationCause}; -use rustc_errors::{DelayDm, FatalError, MultiSpan}; +use rustc_errors::FatalError; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::query::Providers; @@ -23,9 +23,9 @@ use rustc_middle::ty::{ }; use rustc_middle::ty::{GenericArg, GenericArgs}; use rustc_middle::ty::{TypeVisitableExt, Upcast}; -use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; use rustc_span::symbol::Symbol; use rustc_span::Span; +use rustc_target::abi::Abi; use smallvec::SmallVec; use std::iter; @@ -44,7 +44,8 @@ pub fn hir_ty_lowering_object_safety_violations( trait_def_id: DefId, ) -> Vec { debug_assert!(tcx.generics_of(trait_def_id).has_self); - let violations = traits::supertrait_def_ids(tcx, trait_def_id) + let violations = tcx + .supertrait_def_ids(trait_def_id) .map(|def_id| predicates_reference_self(tcx, def_id, true)) .filter(|spans| !spans.is_empty()) .map(ObjectSafetyViolation::SupertraitSelf) @@ -58,50 +59,19 @@ fn object_safety_violations(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &'_ [Object debug!("object_safety_violations: {:?}", trait_def_id); tcx.arena.alloc_from_iter( - traits::supertrait_def_ids(tcx, trait_def_id) + tcx.supertrait_def_ids(trait_def_id) .flat_map(|def_id| object_safety_violations_for_trait(tcx, def_id)), ) } -fn check_is_object_safe(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { - let violations = tcx.object_safety_violations(trait_def_id); - - if violations.is_empty() { - return true; - } - - // If the trait contains any other violations, then let the error reporting path - // report it instead of emitting a warning here. - if violations.iter().all(|violation| { - matches!( - violation, - ObjectSafetyViolation::Method(_, MethodViolationCode::WhereClauseReferencesSelf, _) - ) - }) { - for violation in violations { - if let ObjectSafetyViolation::Method( - _, - MethodViolationCode::WhereClauseReferencesSelf, - span, - ) = violation - { - lint_object_unsafe_trait(tcx, *span, trait_def_id, violation); - } - } - return true; - } - - false +fn is_object_safe(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { + tcx.object_safety_violations(trait_def_id).is_empty() } /// We say a method is *vtable safe* if it can be invoked on a trait /// object. Note that object-safe traits can have some /// non-vtable-safe methods, so long as they require `Self: Sized` or /// otherwise ensure that they cannot be used when `Self = Trait`. -/// -/// [`MethodViolationCode::WhereClauseReferencesSelf`] is considered object safe due to backwards -/// compatibility, see and -/// [`WHERE_CLAUSES_OBJECT_SAFETY`]. pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::AssocItem) -> bool { debug_assert!(tcx.generics_of(trait_def_id).has_self); debug!("is_vtable_safe_method({:?}, {:?})", trait_def_id, method); @@ -110,9 +80,7 @@ pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::A return false; } - virtual_call_violations_for_method(tcx, trait_def_id, method) - .iter() - .all(|v| matches!(v, MethodViolationCode::WhereClauseReferencesSelf)) + virtual_call_violations_for_method(tcx, trait_def_id, method).is_empty() } fn object_safety_violations_for_trait( @@ -145,6 +113,14 @@ fn object_safety_violations_for_trait( violations.push(ObjectSafetyViolation::SupertraitNonLifetimeBinder(spans)); } + if violations.is_empty() { + for item in tcx.associated_items(trait_def_id).in_definition_order() { + if let ty::AssocKind::Fn = item.kind { + check_receiver_correct(tcx, trait_def_id, *item); + } + } + } + debug!( "object_safety_violations_for_trait(trait_def_id={:?}) = {:?}", trait_def_id, violations @@ -153,52 +129,6 @@ fn object_safety_violations_for_trait( violations } -/// Lint object-unsafe trait. -fn lint_object_unsafe_trait( - tcx: TyCtxt<'_>, - span: Span, - trait_def_id: DefId, - violation: &ObjectSafetyViolation, -) { - // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id. - // It's also hard to get a use site span, so we use the method definition span. - tcx.node_span_lint( - WHERE_CLAUSES_OBJECT_SAFETY, - hir::CRATE_HIR_ID, - span, - DelayDm(|| format!("the trait `{}` cannot be made into an object", tcx.def_path_str(trait_def_id))), - |err| { - let node = tcx.hir().get_if_local(trait_def_id); - let mut spans = MultiSpan::from_span(span); - if let Some(hir::Node::Item(item)) = node { - spans.push_span_label( - item.ident.span, - "this trait cannot be made into an object...", - ); - spans.push_span_label(span, format!("...because {}", violation.error_msg())); - } else { - spans.push_span_label( - span, - format!( - "the trait cannot be made into an object because {}", - violation.error_msg() - ), - ); - }; - err.span_note( - spans, - "for a trait to be \"object safe\" it needs to allow building a vtable to allow the \ - call to be resolvable dynamically; for more information visit \ - ", - ); - if node.is_some() { - // Only provide the help if its a local trait, otherwise it's not - violation.solution().add_to(err); - } - }, - ); -} - fn sized_trait_bound_spans<'tcx>( tcx: TyCtxt<'tcx>, bounds: hir::GenericBounds<'tcx>, @@ -264,7 +194,7 @@ fn predicates_reference_self( predicates .predicates .iter() - .map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, &trait_ref), sp)) + .map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, trait_ref), sp)) .filter_map(|predicate| predicate_references_self(tcx, predicate)) .collect() } @@ -398,7 +328,7 @@ pub fn object_safety_violations_for_assoc_item( // Associated types can only be object safe if they have `Self: Sized` bounds. ty::AssocKind::Type => { if !tcx.features().generic_associated_types_extended - && !tcx.generics_of(item.def_id).own_params.is_empty() + && !tcx.generics_of(item.def_id).is_own_empty() && !item.is_impl_trait_in_trait() { vec![ObjectSafetyViolation::GAT(item.name, item.ident(tcx).span)] @@ -498,59 +428,8 @@ fn virtual_call_violations_for_method<'tcx>( }; errors.push(MethodViolationCode::UndispatchableReceiver(span)); } else { - // Do sanity check to make sure the receiver actually has the layout of a pointer. - - use rustc_target::abi::Abi; - - let param_env = tcx.param_env(method.def_id); - - let abi_of_ty = |ty: Ty<'tcx>| -> Option { - match tcx.layout_of(param_env.and(ty)) { - Ok(layout) => Some(layout.abi), - Err(err) => { - // #78372 - tcx.dcx().span_delayed_bug( - tcx.def_span(method.def_id), - format!("error: {err}\n while computing layout for type {ty:?}"), - ); - None - } - } - }; - - // e.g., `Rc<()>` - let unit_receiver_ty = - receiver_for_self_ty(tcx, receiver_ty, tcx.types.unit, method.def_id); - - match abi_of_ty(unit_receiver_ty) { - Some(Abi::Scalar(..)) => (), - abi => { - tcx.dcx().span_delayed_bug( - tcx.def_span(method.def_id), - format!( - "receiver when `Self = ()` should have a Scalar ABI; found {abi:?}" - ), - ); - } - } - - let trait_object_ty = object_ty_for_trait(tcx, trait_def_id, tcx.lifetimes.re_static); - - // e.g., `Rc` - let trait_object_receiver = - receiver_for_self_ty(tcx, receiver_ty, trait_object_ty, method.def_id); - - match abi_of_ty(trait_object_receiver) { - Some(Abi::ScalarPair(..)) => (), - abi => { - tcx.dcx().span_delayed_bug( - tcx.def_span(method.def_id), - format!( - "receiver when `Self = {trait_object_ty}` should have a ScalarPair ABI; found {abi:?}" - ), - ); - } - } + // We confirm that the `receiver_is_dispatchable` is accurate later, + // see `check_receiver_correct`. It should be kept in sync with this code. } } @@ -611,6 +490,55 @@ fn virtual_call_violations_for_method<'tcx>( errors } +/// This code checks that `receiver_is_dispatchable` is correctly implemented. +/// +/// This check is outlined from the object safety check to avoid cycles with +/// layout computation, which relies on knowing whether methods are object safe. +pub fn check_receiver_correct<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, method: ty::AssocItem) { + if !is_vtable_safe_method(tcx, trait_def_id, method) { + return; + } + + let method_def_id = method.def_id; + let sig = tcx.fn_sig(method_def_id).instantiate_identity(); + let param_env = tcx.param_env(method_def_id); + let receiver_ty = tcx.liberate_late_bound_regions(method_def_id, sig.input(0)); + + if receiver_ty == tcx.types.self_param { + // Assumed OK, may change later if unsized_locals permits `self: Self` as dispatchable. + return; + } + + // e.g., `Rc<()>` + let unit_receiver_ty = receiver_for_self_ty(tcx, receiver_ty, tcx.types.unit, method_def_id); + match tcx.layout_of(param_env.and(unit_receiver_ty)).map(|l| l.abi) { + Ok(Abi::Scalar(..)) => (), + abi => { + tcx.dcx().span_delayed_bug( + tcx.def_span(method_def_id), + format!("receiver {unit_receiver_ty:?} when `Self = ()` should have a Scalar ABI; found {abi:?}"), + ); + } + } + + let trait_object_ty = object_ty_for_trait(tcx, trait_def_id, tcx.lifetimes.re_static); + + // e.g., `Rc` + let trait_object_receiver = + receiver_for_self_ty(tcx, receiver_ty, trait_object_ty, method_def_id); + match tcx.layout_of(param_env.and(trait_object_receiver)).map(|l| l.abi) { + Ok(Abi::ScalarPair(..)) => (), + abi => { + tcx.dcx().span_delayed_bug( + tcx.def_span(method_def_id), + format!( + "receiver {trait_object_receiver:?} when `Self = {trait_object_ty}` should have a ScalarPair ABI; found {abi:?}" + ), + ); + } + } +} + /// Performs a type instantiation to produce the version of `receiver_ty` when `Self = self_ty`. /// For example, for `receiver_ty = Rc` and `self_ty = Foo`, returns `Rc`. fn receiver_for_self_ty<'tcx>( @@ -926,7 +854,7 @@ pub fn contains_illegal_impl_trait_in_trait<'tcx>( pub fn provide(providers: &mut Providers) { *providers = Providers { object_safety_violations, - check_is_object_safe, + is_object_safe, generics_require_sized_self, ..*providers }; diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 77ac4be35ea1..8ab324e6601b 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -523,16 +523,9 @@ fn normalize_to_error<'a, 'tcx>( | ty::AliasTermKind::InherentTy | ty::AliasTermKind::OpaqueTy | ty::AliasTermKind::WeakTy => selcx.infcx.next_ty_var(cause.span).into(), - ty::AliasTermKind::UnevaluatedConst | ty::AliasTermKind::ProjectionConst => selcx - .infcx - .next_const_var( - selcx - .tcx() - .type_of(projection_term.def_id) - .instantiate(selcx.tcx(), projection_term.args), - cause.span, - ) - .into(), + ty::AliasTermKind::UnevaluatedConst | ty::AliasTermKind::ProjectionConst => { + selcx.infcx.next_const_var(cause.span).into() + } }; let trait_obligation = Obligation { cause, @@ -744,8 +737,6 @@ fn project<'cx, 'tcx>( obligation.predicate.def_id, obligation.predicate.args, ), - tcx.type_of(obligation.predicate.def_id) - .instantiate(tcx, obligation.predicate.args), ) .into(), kind => { @@ -1024,6 +1015,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // not eligible. let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); + let tcx = selcx.tcx(); let lang_items = selcx.tcx().lang_items(); if [ lang_items.coroutine_trait(), @@ -1040,7 +1032,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( .contains(&Some(trait_ref.def_id)) { true - } else if lang_items.async_fn_kind_helper() == Some(trait_ref.def_id) { + } else if tcx.is_lang_item(trait_ref.def_id, LangItem::AsyncFnKindHelper) { // FIXME(async_closures): Validity constraints here could be cleaned up. if obligation.predicate.args.type_at(0).is_ty_var() || obligation.predicate.args.type_at(4).is_ty_var() @@ -1052,7 +1044,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( obligation.predicate.args.type_at(0).to_opt_closure_kind().is_some() && obligation.predicate.args.type_at(1).to_opt_closure_kind().is_some() } - } else if lang_items.discriminant_kind_trait() == Some(trait_ref.def_id) { + } else if tcx.is_lang_item(trait_ref.def_id, LangItem::DiscriminantKind) { match self_ty.kind() { ty::Bool | ty::Char @@ -1089,7 +1081,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Infer(..) | ty::Error(_) => false, } - } else if lang_items.async_destruct_trait() == Some(trait_ref.def_id) { + } else if tcx.is_lang_item(trait_ref.def_id, LangItem::AsyncDestruct) { match self_ty.kind() { ty::Bool | ty::Char @@ -1125,7 +1117,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Infer(_) | ty::Error(_) => false, } - } else if lang_items.pointee_trait() == Some(trait_ref.def_id) { + } else if tcx.is_lang_item(trait_ref.def_id, LangItem::PointeeTrait) { let tail = selcx.tcx().struct_tail_with_normalize( self_ty, |ty| { @@ -1309,15 +1301,15 @@ fn confirm_select_candidate<'cx, 'tcx>( match impl_source { ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data), ImplSource::Builtin(BuiltinImplSource::Misc, data) => { - let trait_def_id = obligation.predicate.trait_def_id(selcx.tcx()); - let lang_items = selcx.tcx().lang_items(); - if lang_items.coroutine_trait() == Some(trait_def_id) { + let tcx = selcx.tcx(); + let trait_def_id = obligation.predicate.trait_def_id(tcx); + if tcx.is_lang_item(trait_def_id, LangItem::Coroutine) { confirm_coroutine_candidate(selcx, obligation, data) - } else if lang_items.future_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, LangItem::Future) { confirm_future_candidate(selcx, obligation, data) - } else if lang_items.iterator_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, LangItem::Iterator) { confirm_iterator_candidate(selcx, obligation, data) - } else if lang_items.async_iterator_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncIterator) { confirm_async_iterator_candidate(selcx, obligation, data) } else if selcx.tcx().fn_trait_kind_from_def_id(trait_def_id).is_some() { if obligation.predicate.self_ty().is_closure() @@ -1329,7 +1321,7 @@ fn confirm_select_candidate<'cx, 'tcx>( } } else if selcx.tcx().async_fn_trait_kind_from_def_id(trait_def_id).is_some() { confirm_async_closure_candidate(selcx, obligation, data) - } else if lang_items.async_fn_kind_helper() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncFnKindHelper) { confirm_async_fn_kind_helper_candidate(selcx, obligation, data) } else { confirm_builtin_candidate(selcx, obligation, data) @@ -1382,15 +1374,15 @@ fn confirm_coroutine_candidate<'cx, 'tcx>( coroutine_sig, ); - let name = tcx.associated_item(obligation.predicate.def_id).name; - let ty = if name == sym::Return { + let ty = if tcx.is_lang_item(obligation.predicate.def_id, LangItem::CoroutineReturn) { return_ty - } else if name == sym::Yield { + } else if tcx.is_lang_item(obligation.predicate.def_id, LangItem::CoroutineYield) { yield_ty } else { span_bug!( tcx.def_span(obligation.predicate.def_id), - "unexpected associated type: `Coroutine::{name}`" + "unexpected associated type: `Coroutine::{}`", + tcx.item_name(obligation.predicate.def_id), ); }; @@ -1547,21 +1539,20 @@ fn confirm_builtin_candidate<'cx, 'tcx>( ) -> Progress<'tcx> { let tcx = selcx.tcx(); let self_ty = obligation.predicate.self_ty(); - let lang_items = tcx.lang_items(); let item_def_id = obligation.predicate.def_id; let trait_def_id = tcx.trait_of_item(item_def_id).unwrap(); let args = tcx.mk_args(&[self_ty.into()]); - let (term, obligations) = if lang_items.discriminant_kind_trait() == Some(trait_def_id) { + let (term, obligations) = if tcx.is_lang_item(trait_def_id, LangItem::DiscriminantKind) { let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None); assert_eq!(discriminant_def_id, item_def_id); (self_ty.discriminant_ty(tcx).into(), Vec::new()) - } else if lang_items.async_destruct_trait() == Some(trait_def_id) { + } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncDestruct) { let destructor_def_id = tcx.associated_item_def_ids(trait_def_id)[0]; assert_eq!(destructor_def_id, item_def_id); - (self_ty.async_destructor_ty(tcx, obligation.param_env).into(), Vec::new()) - } else if lang_items.pointee_trait() == Some(trait_def_id) { + (self_ty.async_destructor_ty(tcx).into(), Vec::new()) + } else if tcx.is_lang_item(trait_def_id, LangItem::PointeeTrait) { let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); assert_eq!(metadata_def_id, item_def_id); @@ -1680,14 +1671,8 @@ fn confirm_closure_candidate<'cx, 'tcx>( args.coroutine_captures_by_ref_ty(), ) } else { - let async_fn_kind_trait_def_id = - tcx.require_lang_item(LangItem::AsyncFnKindHelper, None); - let upvars_projection_def_id = tcx - .associated_items(async_fn_kind_trait_def_id) - .filter_by_name_unhygienic(sym::Upvars) - .next() - .unwrap() - .def_id; + let upvars_projection_def_id = + tcx.require_lang_item(LangItem::AsyncFnKindUpvars, None); let tupled_upvars_ty = Ty::new_projection( tcx, upvars_projection_def_id, @@ -1816,14 +1801,8 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( args.coroutine_captures_by_ref_ty(), ) } else { - let async_fn_kind_trait_def_id = - tcx.require_lang_item(LangItem::AsyncFnKindHelper, None); - let upvars_projection_def_id = tcx - .associated_items(async_fn_kind_trait_def_id) - .filter_by_name_unhygienic(sym::Upvars) - .next() - .unwrap() - .def_id; + let upvars_projection_def_id = + tcx.require_lang_item(LangItem::AsyncFnKindUpvars, None); // When we don't know the closure kind (and therefore also the closure's upvars, // which are computed at the same time), we must delay the computation of the // generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait @@ -1880,13 +1859,7 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( let term = match item_name { sym::CallOnceFuture | sym::CallRefFuture => sig.output(), sym::Output => { - let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None); - let future_output_def_id = tcx - .associated_items(future_trait_def_id) - .filter_by_name_unhygienic(sym::Output) - .next() - .unwrap() - .def_id; + let future_output_def_id = tcx.require_lang_item(LangItem::FutureOutput, None); Ty::new_projection(tcx, future_output_def_id, [sig.output()]) } name => bug!("no such associated type: {name}"), @@ -1919,13 +1892,7 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( let term = match item_name { sym::CallOnceFuture | sym::CallRefFuture => sig.output(), sym::Output => { - let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None); - let future_output_def_id = tcx - .associated_items(future_trait_def_id) - .filter_by_name_unhygienic(sym::Output) - .next() - .unwrap() - .def_id; + let future_output_def_id = tcx.require_lang_item(LangItem::FutureOutput, None); Ty::new_projection(tcx, future_output_def_id, [sig.output()]) } name => bug!("no such associated type: {name}"), @@ -2095,15 +2062,14 @@ fn confirm_impl_candidate<'cx, 'tcx>( // * `args` ends up as `[u32, S]` let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args); let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_ty.defining_node); - let ty = tcx.type_of(assoc_ty.item.def_id); let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst); - let term: ty::EarlyBinder> = if is_const { + let term: ty::EarlyBinder<'tcx, ty::Term<'tcx>> = if is_const { let did = assoc_ty.item.def_id; let identity_args = crate::traits::GenericArgs::identity_for_item(tcx, did); let uv = ty::UnevaluatedConst::new(did, identity_args); - ty.map_bound(|ty| ty::Const::new_unevaluated(tcx, uv, ty).into()) + ty::EarlyBinder::bind(ty::Const::new_unevaluated(tcx, uv).into()) } else { - ty.map_bound(|ty| ty.into()) + tcx.type_of(assoc_ty.item.def_id).map_bound(|ty| ty.into()) }; if !tcx.check_args_compatible(assoc_ty.item.def_id, args) { let err = Ty::new_error_with_message( diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 326c68e01dbf..7dc051e6fe95 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -55,7 +55,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { } ty::Adt(def, _) => { - if Some(def.did()) == tcx.lang_items().manually_drop() { + if def.is_manually_drop() { // `ManuallyDrop` never has a dtor. true } else { diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 1b5ffeebc01f..e170d7cae937 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -8,11 +8,11 @@ use crate::infer::{InferCtxt, InferOk}; use crate::traits::error_reporting::OverflowCause; use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::normalize::needs_normalization; -use crate::traits::{BoundVarReplacer, PlaceholderReplacer}; +use crate::traits::Normalized; +use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError}; use crate::traits::{ObligationCause, PredicateObligation, Reveal}; use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_infer::traits::Normalized; use rustc_macros::extension; use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt}; @@ -76,7 +76,9 @@ impl<'cx, 'tcx> At<'cx, 'tcx> { }; if self.infcx.next_trait_solver() { - match crate::solve::deeply_normalize_with_skipped_universes(self, value, universes) { + match crate::solve::deeply_normalize_with_skipped_universes::<_, ScrubbedTraitError<'tcx>>( + self, value, universes, + ) { Ok(value) => return Ok(Normalized { value, obligations: vec![] }), Err(_errors) => { return Err(NoSolution); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index f7e84a46639d..b38841db9233 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -1,4 +1,3 @@ -use crate::solve; use crate::traits::query::NoSolution; use crate::traits::wf; use crate::traits::ObligationCtxt; @@ -162,8 +161,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default(); let mut wf_args = vec![ty.into()]; - let mut outlives_bounds: Vec, ty::Region<'tcx>>> = - vec![]; + let mut outlives_bounds: Vec>> = vec![]; while let Some(arg) = wf_args.pop() { if !checked_wf_args.insert(arg) { @@ -263,11 +261,9 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( let mut ty_a = ocx.infcx.resolve_vars_if_possible(ty_a); // Need to manually normalize in the new solver as `wf::obligations` does not. if ocx.infcx.next_trait_solver() { - ty_a = solve::deeply_normalize( - ocx.infcx.at(&ObligationCause::dummy(), param_env), - ty_a, - ) - .map_err(|_errs| NoSolution)?; + ty_a = ocx + .deeply_normalize(&ObligationCause::dummy(), param_env, ty_a) + .map_err(|_| NoSolution)?; } let mut components = smallvec![]; push_outlives_components(tcx, ty_a, &mut components); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index ae4cdb9258e2..c1b1bfd300ba 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -5,7 +5,7 @@ use crate::infer::{InferCtxt, InferOk}; use crate::traits::{ObligationCause, ObligationCtxt}; use rustc_errors::ErrorGuaranteed; use rustc_infer::infer::canonical::Certainty; -use rustc_infer::traits::PredicateObligations; +use rustc_infer::traits::PredicateObligation; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; @@ -103,7 +103,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable> + 't ( Self::QueryResponse, Option>>, - PredicateObligations<'tcx>, + Vec>, Certainty, ), NoSolution, diff --git a/compiler/rustc_middle/src/ty/_match.rs b/compiler/rustc_trait_selection/src/traits/select/_match.rs similarity index 81% rename from compiler/rustc_middle/src/ty/_match.rs rename to compiler/rustc_trait_selection/src/traits/select/_match.rs index e28e4d66fafc..50d8e96aaf91 100644 --- a/compiler/rustc_middle/src/ty/_match.rs +++ b/compiler/rustc_trait_selection/src/traits/select/_match.rs @@ -1,6 +1,9 @@ -use crate::ty::error::TypeError; -use crate::ty::relate::{self, Relate, RelateResult, TypeRelation}; -use crate::ty::{self, InferConst, Ty, TyCtxt}; +use rustc_infer::infer::relate::{ + self, structurally_relate_tys, Relate, RelateResult, TypeRelation, +}; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; +use tracing::{debug, instrument}; /// A type "A" *matches* "B" if the fresh types in B could be /// instantiated with values so as to make it equal to A. Matching is @@ -28,7 +31,7 @@ impl<'tcx> MatchAgainstFreshVars<'tcx> { } } -impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> { +impl<'tcx> TypeRelation> for MatchAgainstFreshVars<'tcx> { fn tag(&self) -> &'static str { "MatchAgainstFreshVars" } @@ -37,10 +40,10 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> { self.tcx } - fn relate_with_variance>( + fn relate_with_variance>>( &mut self, _: ty::Variance, - _: ty::VarianceDiagInfo<'tcx>, + _: ty::VarianceDiagInfo>, a: T, b: T, ) -> RelateResult<'tcx, T> { @@ -71,12 +74,12 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> { ) => Ok(a), (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { - Err(TypeError::Sorts(relate::expected_found(a, b))) + Err(TypeError::Sorts(ExpectedFound::new(true, a, b))) } (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(self.tcx(), guar)), - _ => relate::structurally_relate_tys(self, a, b), + _ => structurally_relate_tys(self, a, b), } } @@ -96,7 +99,7 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> { } (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => { - return Err(TypeError::ConstMismatch(relate::expected_found(a, b))); + return Err(TypeError::ConstMismatch(ExpectedFound::new(true, a, b))); } _ => {} @@ -111,7 +114,7 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> { b: ty::Binder<'tcx, T>, ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> where - T: Relate<'tcx>, + T: Relate>, { Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index b9e853a06787..c53939bfe602 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -67,9 +67,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Other bounds. Consider both in-scope bounds from fn decl // and applicable impls. There is a certain set of precedence rules here. let def_id = obligation.predicate.def_id(); - let lang_items = self.tcx().lang_items(); + let tcx = self.tcx(); - if lang_items.copy_trait() == Some(def_id) { + if tcx.is_lang_item(def_id, LangItem::Copy) { debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty()); // User-defined copy impls are permitted, but only for @@ -79,16 +79,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // For other types, we'll use the builtin rules. let copy_conditions = self.copy_clone_conditions(obligation); self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates); - } else if lang_items.discriminant_kind_trait() == Some(def_id) { + } else if tcx.is_lang_item(def_id, LangItem::DiscriminantKind) { // `DiscriminantKind` is automatically implemented for every type. candidates.vec.push(BuiltinCandidate { has_nested: false }); - } else if lang_items.async_destruct_trait() == Some(def_id) { + } else if tcx.is_lang_item(def_id, LangItem::AsyncDestruct) { // `AsyncDestruct` is automatically implemented for every type. candidates.vec.push(BuiltinCandidate { has_nested: false }); - } else if lang_items.pointee_trait() == Some(def_id) { + } else if tcx.is_lang_item(def_id, LangItem::PointeeTrait) { // `Pointee` is automatically implemented for every type. candidates.vec.push(BuiltinCandidate { has_nested: false }); - } else if lang_items.sized_trait() == Some(def_id) { + } else if tcx.is_lang_item(def_id, LangItem::Sized) { // Sized is never implementable by end-users, it is // always automatically computed. @@ -101,22 +101,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let sized_conditions = self.sized_conditions(obligation); self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates); - } else if lang_items.unsize_trait() == Some(def_id) { + } else if tcx.is_lang_item(def_id, LangItem::Unsize) { self.assemble_candidates_for_unsizing(obligation, &mut candidates); - } else if lang_items.destruct_trait() == Some(def_id) { + } else if tcx.is_lang_item(def_id, LangItem::Destruct) { self.assemble_const_destruct_candidates(obligation, &mut candidates); - } else if lang_items.transmute_trait() == Some(def_id) { + } else if tcx.is_lang_item(def_id, LangItem::TransmuteTrait) { // User-defined transmutability impls are permitted. self.assemble_candidates_from_impls(obligation, &mut candidates); self.assemble_candidates_for_transmutability(obligation, &mut candidates); - } else if lang_items.tuple_trait() == Some(def_id) { + } else if tcx.is_lang_item(def_id, LangItem::Tuple) { self.assemble_candidate_for_tuple(obligation, &mut candidates); - } else if lang_items.pointer_like() == Some(def_id) { + } else if tcx.is_lang_item(def_id, LangItem::PointerLike) { self.assemble_candidate_for_pointer_like(obligation, &mut candidates); - } else if lang_items.fn_ptr_trait() == Some(def_id) { + } else if tcx.is_lang_item(def_id, LangItem::FnPtrTrait) { self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates); } else { - if lang_items.clone_trait() == Some(def_id) { + if tcx.is_lang_item(def_id, LangItem::Clone) { // Same builtin conditions as `Copy`, i.e., every type which has builtin support // for `Copy` also has builtin support for `Clone`, and tuples/arrays of `Clone` // types have builtin support for `Clone`. @@ -124,17 +124,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates); } - if lang_items.coroutine_trait() == Some(def_id) { + if tcx.is_lang_item(def_id, LangItem::Coroutine) { self.assemble_coroutine_candidates(obligation, &mut candidates); - } else if lang_items.future_trait() == Some(def_id) { + } else if tcx.is_lang_item(def_id, LangItem::Future) { self.assemble_future_candidates(obligation, &mut candidates); - } else if lang_items.iterator_trait() == Some(def_id) { + } else if tcx.is_lang_item(def_id, LangItem::Iterator) { self.assemble_iterator_candidates(obligation, &mut candidates); - } else if lang_items.fused_iterator_trait() == Some(def_id) { + } else if tcx.is_lang_item(def_id, LangItem::FusedIterator) { self.assemble_fused_iterator_candidates(obligation, &mut candidates); - } else if lang_items.async_iterator_trait() == Some(def_id) { + } else if tcx.is_lang_item(def_id, LangItem::AsyncIterator) { self.assemble_async_iterator_candidates(obligation, &mut candidates); - } else if lang_items.async_fn_kind_helper() == Some(def_id) { + } else if tcx.is_lang_item(def_id, LangItem::AsyncFnKindHelper) { self.assemble_async_fn_kind_helper_candidates(obligation, &mut candidates); } @@ -239,24 +239,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Ok(()); } - let all_bounds = stack + let bounds = stack .obligation .param_env .caller_bounds() .iter() .filter(|p| !p.references_error()) - .filter_map(|p| p.as_trait_clause()); - - // Micro-optimization: filter out predicates relating to different traits. - let matching_bounds = - all_bounds.filter(|p| p.def_id() == stack.obligation.predicate.def_id()); + .filter_map(|p| p.as_trait_clause()) + // Micro-optimization: filter out predicates relating to different traits. + .filter(|p| p.def_id() == stack.obligation.predicate.def_id()) + .filter(|p| p.polarity() == stack.obligation.predicate.polarity()); // Keep only those bounds which may apply, and propagate overflow if it occurs. - for bound in matching_bounds { - if bound.skip_binder().polarity != stack.obligation.predicate.skip_binder().polarity { - continue; - } - + for bound in bounds { // FIXME(oli-obk): it is suspicious that we are dropping the constness and // polarity here. let wc = self.where_clause_may_apply(stack, bound.map_bound(|t| t.trait_ref))?; @@ -418,20 +413,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Ambiguity if upvars haven't been constrained yet && !args.tupled_upvars_ty().is_ty_var() { - let no_borrows = match args.tupled_upvars_ty().kind() { - ty::Tuple(tys) => tys.is_empty(), - ty::Error(_) => false, - _ => bug!("tuple_fields called on non-tuple"), - }; // A coroutine-closure implements `FnOnce` *always*, since it may // always be called once. It additionally implements `Fn`/`FnMut` - // only if it has no upvars (therefore no borrows from the closure - // that would need to be represented with a lifetime) and if the - // closure kind permits it. - // FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut` - // if it takes all of its upvars by copy, and none by ref. This would - // require us to record a bit more information during upvar analysis. - if no_borrows && closure_kind.extends(kind) { + // only if it has no upvars referencing the closure-env lifetime, + // and if the closure kind permits it. + if closure_kind.extends(kind) && !args.has_self_borrows() { candidates.vec.push(ClosureCandidate { is_const }); } else if kind == ty::ClosureKind::FnOnce { candidates.vec.push(ClosureCandidate { is_const }); @@ -764,7 +750,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates.ambiguous = true; } ty::Coroutine(coroutine_def_id, _) - if self.tcx().lang_items().unpin_trait() == Some(def_id) => + if self.tcx().is_lang_item(def_id, LangItem::Unpin) => { match self.tcx().coroutine_movability(coroutine_def_id) { hir::Movability::Static => { @@ -879,7 +865,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let Some(principal) = data.principal() { if !self.infcx.tcx.features().object_safe_for_dispatch { principal.with_self_ty(self.tcx(), self_ty) - } else if self.tcx().check_is_object_safe(principal.def_id()) { + } else if self.tcx().is_object_safe(principal.def_id()) { principal.with_self_ty(self.tcx(), self_ty) } else { return; @@ -930,6 +916,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { param_env: ty::ParamEnv<'tcx>, cause: &ObligationCause<'tcx>, ) -> Option> { + // Don't drop any candidates in intercrate mode, as it's incomplete. + // (Not that it matters, since `Unsize` is not a stable trait.) + if self.infcx.intercrate { + return None; + } + let tcx = self.tcx(); if tcx.features().trait_upcasting { return None; @@ -955,7 +947,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // since we don't actually use them. &mut vec![], ) - .ty() + .as_type() .unwrap(); if let ty::Dynamic(data, ..) = ty.kind() { data.principal() } else { None } @@ -1013,7 +1005,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let a_auto_traits: FxIndexSet = a_data .auto_traits() .chain(principal_def_id_a.into_iter().flat_map(|principal_def_id| { - util::supertrait_def_ids(self.tcx(), principal_def_id) + self.tcx() + .supertrait_def_ids(principal_def_id) .filter(|def_id| self.tcx().trait_is_auto(*def_id)) })) .collect(); diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 65048ffdfba5..af599108c49d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -22,10 +22,6 @@ use rustc_span::def_id::DefId; use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::util::{self, closure_trait_ref_and_return_type}; -use crate::traits::vtable::{ - count_own_vtable_entries, prepare_vtable_segments, vtable_trait_first_method_offset, - VtblSegment, -}; use crate::traits::{ ImplDerivedCause, ImplSource, ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, PolyTraitObligation, PredicateObligation, Selection, SelectionError, @@ -258,16 +254,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> Vec> { debug!(?obligation, ?has_nested, "confirm_builtin_candidate"); - let lang_items = self.tcx().lang_items(); + let tcx = self.tcx(); let obligations = if has_nested { let trait_def = obligation.predicate.def_id(); - let conditions = if Some(trait_def) == lang_items.sized_trait() { + let conditions = if tcx.is_lang_item(trait_def, LangItem::Sized) { self.sized_conditions(obligation) - } else if Some(trait_def) == lang_items.copy_trait() { + } else if tcx.is_lang_item(trait_def, LangItem::Copy) { self.copy_clone_conditions(obligation) - } else if Some(trait_def) == lang_items.clone_trait() { + } else if tcx.is_lang_item(trait_def, LangItem::Clone) { self.copy_clone_conditions(obligation) - } else if Some(trait_def) == lang_items.fused_iterator_trait() { + } else if tcx.is_lang_item(trait_def, LangItem::FusedIterator) { self.fused_iterator_conditions(obligation) } else { bug!("unexpected builtin trait {:?}", trait_def) @@ -338,7 +334,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let make_freeze_obl = |ty| { let trait_ref = ty::TraitRef::new( tcx, - tcx.lang_items().freeze_trait().unwrap(), + tcx.require_lang_item(LangItem::Freeze, None), [ty::GenericArg::from(ty)], ); Obligation::with_depth( @@ -619,7 +615,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // higher-ranked things. // Prevent, e.g., `dyn Iterator`. for bound in self.tcx().item_bounds(assoc_type).transpose_iter() { - let arg_bound = if defs.count() == 0 { + let arg_bound = if defs.is_empty() { bound.instantiate(tcx, trait_predicate.trait_ref.args) } else { let mut args = smallvec::SmallVec::with_capacity(defs.count()); @@ -665,9 +661,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { tcx, ty::INNERMOST, ty::BoundVar::from_usize(bound_vars.len() - 1), - tcx.type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"), ) .into() } @@ -692,13 +685,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?nested, "object nested obligations"); - let vtable_base = vtable_trait_first_method_offset( - tcx, - unnormalized_upcast_trait_ref, - ty::Binder::dummy(object_trait_ref), - ); - - Ok(ImplSource::Builtin(BuiltinImplSource::Object { vtable_base: vtable_base }, nested)) + Ok(ImplSource::Builtin(BuiltinImplSource::Object(index), nested)) } fn confirm_fn_pointer_candidate( @@ -1128,36 +1115,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { )? .expect("did not expect ambiguity during confirmation"); - let vtable_segment_callback = { - let mut vptr_offset = 0; - move |segment| { - match segment { - VtblSegment::MetadataDSA => { - vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); - } - VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - vptr_offset += count_own_vtable_entries(tcx, trait_ref); - if trait_ref == unnormalized_upcast_principal { - if emit_vptr { - return ControlFlow::Break(Some(vptr_offset)); - } else { - return ControlFlow::Break(None); - } - } - - if emit_vptr { - vptr_offset += 1; - } - } - } - ControlFlow::Continue(()) - } - }; - - let vtable_vptr_slot = - prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap(); - - Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, nested)) + Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting, nested)) } fn confirm_builtin_unsize_candidate( @@ -1222,7 +1180,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // `T` -> `Trait` (_, &ty::Dynamic(data, r, ty::Dyn)) => { let mut object_dids = data.auto_traits().chain(data.principal_def_id()); - if let Some(did) = object_dids.find(|did| !tcx.check_is_object_safe(*did)) { + if let Some(did) = object_dids.find(|did| !tcx.is_object_safe(*did)) { return Err(TraitNotObjectSafe(did)); } @@ -1447,7 +1405,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Foreign(_) => {} // `ManuallyDrop` is trivially drop - ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().manually_drop() => {} + ty::Adt(def, _) if def.is_manually_drop() => {} // These types are built-in, so we can fast-track by registering // nested predicates for their constituent type(s) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 19affac7970b..fe047f9966f3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -19,7 +19,7 @@ use super::{ }; use crate::infer::{InferCtxt, InferOk, TypeFreshener}; -use crate::solve::InferCtxtSelectExt; +use crate::solve::InferCtxtSelectExt as _; use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::normalize::normalize_with_depth; use crate::traits::normalize::normalize_with_depth_to; @@ -32,6 +32,8 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::LangItem; +use rustc_infer::infer::relate::TypeRelation; use rustc_infer::infer::BoundRegionConversionTime; use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType; use rustc_infer::infer::DefineOpaqueTypes; @@ -40,10 +42,9 @@ use rustc_middle::bug; use rustc_middle::dep_graph::dep_kinds; use rustc_middle::dep_graph::DepNodeIndex; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::ty::_match::MatchAgainstFreshVars; use rustc_middle::ty::abstract_const::NotConstEvaluatable; +use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::print::PrintTraitRefExt as _; -use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, PolyProjectionPredicate, Upcast}; use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; @@ -59,6 +60,7 @@ use std::ops::ControlFlow; pub use rustc_middle::traits::select::*; use rustc_middle::ty::print::with_no_trimmed_paths; +mod _match; mod candidate_assembly; mod confirmation; @@ -798,7 +800,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::PredicateKind::ObjectSafe(trait_def_id) => { - if self.tcx().check_is_object_safe(trait_def_id) { + if self.tcx().is_object_safe(trait_def_id) { Ok(EvaluatedToOk) } else { Ok(EvaluatedToErr) @@ -910,10 +912,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let Ok(InferOk { obligations, value: () }) = self .infcx .at(&obligation.cause, obligation.param_env) - .trace(c1, c2) // Can define opaque types as this is only reachable with // `generic_const_exprs` - .eq(DefineOpaqueTypes::Yes, a.args, b.args) + .eq( + DefineOpaqueTypes::Yes, + ty::AliasTerm::from(a), + ty::AliasTerm::from(b), + ) { return self.evaluate_predicates_recursively( previous_stack, @@ -944,7 +949,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match self.infcx.try_const_eval_resolve( obligation.param_env, unevaluated, - c.ty(), obligation.cause.span, ) { Ok(val) => Ok(val), @@ -992,10 +996,31 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig), ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { + let ct = self.infcx.shallow_resolve_const(ct); + let ct_ty = match ct.kind() { + ty::ConstKind::Infer(_) => { + return Ok(EvaluatedToAmbig); + } + ty::ConstKind::Error(_) => return Ok(EvaluatedToOk), + ty::ConstKind::Value(ty, _) => ty, + ty::ConstKind::Unevaluated(uv) => { + self.tcx().type_of(uv.def).instantiate(self.tcx(), uv.args) + } + // FIXME(generic_const_exprs): See comment in `fulfill.rs` + ty::ConstKind::Expr(_) => return Ok(EvaluatedToOk), + ty::ConstKind::Placeholder(_) => { + bug!("placeholder const {:?} in old solver", ct) + } + ty::ConstKind::Bound(_, _) => bug!("escaping bound vars in {:?}", ct), + ty::ConstKind::Param(param_ct) => { + param_ct.find_ty_from_env(obligation.param_env) + } + }; + match self.infcx.at(&obligation.cause, obligation.param_env).eq( // Only really excercised by generic_const_exprs DefineOpaqueTypes::Yes, - ct.ty(), + ct_ty, ty, ) { Ok(inf_ok) => self.evaluate_predicates_recursively( @@ -1781,7 +1806,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // FIXME(generic-associated-types): This only detects one layer of inference, // which is probably not what we actually want, but fixing it causes some ambiguity: // . - if !generics.own_params.is_empty() + if !generics.is_own_empty() && obligation.predicate.args[generics.parent_count..].iter().any(|&p| { p.has_non_region_infer() && match p.unpack() { @@ -1842,7 +1867,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // the param_env so that it can be given the lowest priority. See // #50825 for the motivation for this. let is_global = - |cand: &ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_bound_vars(); + |cand: ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_bound_vars(); // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`, // `DiscriminantKindCandidate`, `ConstDestructCandidate` @@ -1885,7 +1910,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } ( - ParamCandidate(ref other_cand), + ParamCandidate(other_cand), ImplCandidate(..) | AutoImplCandidate | ClosureCandidate { .. } @@ -1910,12 +1935,12 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // // Global bounds from the where clause should be ignored // here (see issue #50825). - DropVictim::drop_if(!is_global(other_cand)) + DropVictim::drop_if(!is_global(*other_cand)) } - (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref victim_cand)) => { + (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(victim_cand)) => { // Prefer these to a global where-clause bound // (see issue #50825). - if is_global(victim_cand) { DropVictim::Yes } else { DropVictim::No } + if is_global(*victim_cand) { DropVictim::Yes } else { DropVictim::No } } ( ImplCandidate(_) @@ -1933,12 +1958,12 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate, - ParamCandidate(ref victim_cand), + ParamCandidate(victim_cand), ) => { // Prefer these to a global where-clause bound // (see issue #50825). DropVictim::drop_if( - is_global(victim_cand) && other.evaluation.must_apply_modulo_regions(), + is_global(*victim_cand) && other.evaluation.must_apply_modulo_regions(), ) } @@ -2571,8 +2596,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { let a_auto_traits: FxIndexSet = a_data .auto_traits() .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| { - util::supertrait_def_ids(tcx, principal_def_id) - .filter(|def_id| tcx.trait_is_auto(*def_id)) + tcx.supertrait_def_ids(principal_def_id).filter(|def_id| tcx.trait_is_auto(*def_id)) })) .collect(); @@ -2594,7 +2618,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { self.infcx .at(&obligation.cause, obligation.param_env) .eq( - DefineOpaqueTypes::No, + DefineOpaqueTypes::Yes, upcast_principal.map_bound(|trait_ref| { ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) }), @@ -2631,7 +2655,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { nested.extend( self.infcx .at(&obligation.cause, obligation.param_env) - .eq(DefineOpaqueTypes::No, source_projection, target_projection) + .eq(DefineOpaqueTypes::Yes, source_projection, target_projection) .map_err(|_| SelectionError::Unimplemented)? .into_obligations(), ); @@ -2696,7 +2720,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { previous: ty::PolyTraitPredicate<'tcx>, current: ty::PolyTraitPredicate<'tcx>, ) -> bool { - let mut matcher = MatchAgainstFreshVars::new(self.tcx()); + let mut matcher = _match::MatchAgainstFreshVars::new(self.tcx()); matcher.relate(previous, current).is_ok() } @@ -2777,19 +2801,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> { let predicates = predicates.instantiate_own(tcx, args); let mut obligations = Vec::with_capacity(predicates.len()); for (index, (predicate, span)) in predicates.into_iter().enumerate() { - let cause = - if Some(parent_trait_pred.def_id()) == tcx.lang_items().coerce_unsized_trait() { - cause.clone() - } else { - cause.clone().derived_cause(parent_trait_pred, |derived| { - ObligationCauseCode::ImplDerived(Box::new(ImplDerivedCause { - derived, - impl_or_alias_def_id: def_id, - impl_def_predicate_index: Some(index), - span, - })) - }) - }; + let cause = if tcx.is_lang_item(parent_trait_pred.def_id(), LangItem::CoerceUnsized) { + cause.clone() + } else { + cause.clone().derived_cause(parent_trait_pred, |derived| { + ObligationCauseCode::ImplDerived(Box::new(ImplDerivedCause { + derived, + impl_or_alias_def_id: def_id, + impl_def_predicate_index: Some(index), + span, + })) + }) + }; let clause = normalize_with_depth_to( self, param_env, diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 826bb706f48b..c9bb0d330e1f 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -21,9 +21,10 @@ use crate::traits::{ self, coherence, FutureCompatOverlapErrorKind, ObligationCause, ObligationCtxt, }; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{codes::*, DelayDm, Diag, EmissionGuarantee}; +use rustc_errors::{codes::*, Diag, EmissionGuarantee}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::bug; +use rustc_middle::query::LocalCrate; use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{GenericArgs, GenericArgsRef}; use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; @@ -136,6 +137,10 @@ pub fn translate_args_with_cause<'tcx>( source_args.rebase_onto(infcx.tcx, source_impl, target_args) } +pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool { + tcx.features().specialization || tcx.features().min_specialization +} + /// Is `impl1` a specialization of `impl2`? /// /// Specialization is determined by the sets of types to which the impls apply; @@ -143,31 +148,18 @@ pub fn translate_args_with_cause<'tcx>( /// to. #[instrument(skip(tcx), level = "debug")] pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool { - // The feature gate should prevent introducing new specializations, but not - // taking advantage of upstream ones. - // If specialization is enabled for this crate then no extra checks are needed. - // If it's not, and either of the `impl`s is local to this crate, then this definitely - // isn't specializing - unless specialization is enabled for the `impl` span, - // e.g. if it comes from an `allow_internal_unstable` macro - let features = tcx.features(); - let specialization_enabled = features.specialization || features.min_specialization; - if !specialization_enabled { - if impl1_def_id.is_local() { - let span = tcx.def_span(impl1_def_id); - if !span.allows_unstable(sym::specialization) - && !span.allows_unstable(sym::min_specialization) - { - return false; - } - } - - if impl2_def_id.is_local() { - let span = tcx.def_span(impl2_def_id); - if !span.allows_unstable(sym::specialization) - && !span.allows_unstable(sym::min_specialization) - { - return false; - } + // We check that the specializing impl comes from a crate that has specialization enabled, + // or if the specializing impl is marked with `allow_internal_unstable`. + // + // We don't really care if the specialized impl (the parent) is in a crate that has + // specialization enabled, since it's not being specialized, and it's already been checked + // for coherence. + if !tcx.specialization_enabled_in(impl1_def_id.krate) { + let span = tcx.def_span(impl1_def_id); + if !span.allows_unstable(sym::specialization) + && !span.allows_unstable(sym::min_specialization) + { + return false; } } @@ -449,7 +441,7 @@ fn report_conflicting_impls<'tcx>( } } - let msg = DelayDm(|| { + let msg = || { format!( "conflicting implementations of trait `{}`{}{}", overlap.trait_ref.print_trait_sugared(), @@ -459,7 +451,7 @@ fn report_conflicting_impls<'tcx>( _ => "", } ) - }); + }; // Don't report overlap errors if the header references error if let Err(err) = (overlap.trait_ref, overlap.self_ty).error_reported() { @@ -471,7 +463,7 @@ fn report_conflicting_impls<'tcx>( let reported = if overlap.with_impl.is_local() || tcx.ensure().orphan_check_impl(impl_def_id).is_ok() { - let mut err = tcx.dcx().struct_span_err(impl_span, msg); + let mut err = tcx.dcx().struct_span_err(impl_span, msg()); err.code(E0119); decorate(tcx, &overlap, impl_span, &mut err); err.emit() @@ -485,15 +477,10 @@ fn report_conflicting_impls<'tcx>( FutureCompatOverlapErrorKind::OrderDepTraitObjects => ORDER_DEPENDENT_TRAIT_OBJECTS, FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK, }; - tcx.node_span_lint( - lint, - tcx.local_def_id_to_hir_id(impl_def_id), - impl_span, - msg, - |err| { - decorate(tcx, &overlap, impl_span, err); - }, - ); + tcx.node_span_lint(lint, tcx.local_def_id_to_hir_id(impl_def_id), impl_span, |err| { + err.primary_message(msg()); + decorate(tcx, &overlap, impl_span, err); + }); Ok(()) } } diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index 96a06e0c1691..9d657ade86bf 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -1,5 +1,5 @@ use rustc_infer::infer::at::At; -use rustc_infer::traits::{FulfillmentError, TraitEngine}; +use rustc_infer::traits::TraitEngine; use rustc_macros::extension; use rustc_middle::ty::{self, Ty}; @@ -7,11 +7,11 @@ use crate::traits::{NormalizeExt, Obligation}; #[extension(pub trait StructurallyNormalizeExt<'tcx>)] impl<'tcx> At<'_, 'tcx> { - fn structurally_normalize( + fn structurally_normalize( &self, ty: Ty<'tcx>, - fulfill_cx: &mut dyn TraitEngine<'tcx>, - ) -> Result, Vec>> { + fulfill_cx: &mut dyn TraitEngine<'tcx, E>, + ) -> Result, Vec> { assert!(!ty.is_ty_var(), "should have resolved vars before calling"); if self.infcx.next_trait_solver() { diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 445fa1761b9f..c3fe816028e3 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use super::NormalizeExt; use super::{ObligationCause, PredicateObligation, SelectionContext}; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Diag; use rustc_hir::def_id::DefId; use rustc_infer::infer::{InferCtxt, InferOk}; @@ -132,7 +132,7 @@ impl<'tcx> TraitAliasExpander<'tcx> { debug!(?predicates); let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| { - pred.instantiate_supertrait(tcx, &trait_ref) + pred.instantiate_supertrait(tcx, trait_ref) .as_trait_clause() .map(|trait_ref| item.clone_and_push(trait_ref.map_bound(|t| t.trait_ref), *span)) }); @@ -161,43 +161,6 @@ impl<'tcx> Iterator for TraitAliasExpander<'tcx> { } } -/////////////////////////////////////////////////////////////////////////// -// Iterator over def-IDs of supertraits -/////////////////////////////////////////////////////////////////////////// - -pub struct SupertraitDefIds<'tcx> { - tcx: TyCtxt<'tcx>, - stack: Vec, - visited: FxHashSet, -} - -pub fn supertrait_def_ids(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SupertraitDefIds<'_> { - SupertraitDefIds { - tcx, - stack: vec![trait_def_id], - visited: Some(trait_def_id).into_iter().collect(), - } -} - -impl Iterator for SupertraitDefIds<'_> { - type Item = DefId; - - fn next(&mut self) -> Option { - let def_id = self.stack.pop()?; - let predicates = self.tcx.super_predicates_of(def_id); - let visited = &mut self.visited; - self.stack.extend( - predicates - .predicates - .iter() - .filter_map(|(pred, _)| pred.as_trait_clause()) - .map(|trait_ref| trait_ref.def_id()) - .filter(|&super_def_id| visited.insert(super_def_id)), - ); - Some(def_id) - } -} - /////////////////////////////////////////////////////////////////////////// // Other /////////////////////////////////////////////////////////////////////////// @@ -245,23 +208,6 @@ pub fn upcast_choices<'tcx>( supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect() } -/// Given an upcast trait object described by `object`, returns the -/// index of the method `method_def_id` (which should be part of -/// `object.upcast_trait_ref`) within the vtable for `object`. -pub fn get_vtable_index_of_object_method<'tcx>( - tcx: TyCtxt<'tcx>, - vtable_base: usize, - method_def_id: DefId, -) -> Option { - // Count number of methods preceding the one we are selecting and - // add them to the total offset. - tcx.own_existential_vtable_entries(tcx.parent(method_def_id)) - .iter() - .copied() - .position(|def_id| def_id == method_def_id) - .map(|index| vtable_base + index) -} - pub fn closure_trait_ref_and_return_type<'tcx>( tcx: TyCtxt<'tcx>, fn_trait_def_id: DefId, @@ -295,7 +241,7 @@ pub fn coroutine_trait_ref_and_outputs<'tcx>( tcx: TyCtxt<'tcx>, fn_trait_def_id: DefId, self_ty: Ty<'tcx>, - sig: ty::GenSig<'tcx>, + sig: ty::GenSig>, ) -> (ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>) { assert!(!self_ty.has_escaping_bound_vars()); let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, sig.resume_ty]); @@ -306,7 +252,7 @@ pub fn future_trait_ref_and_outputs<'tcx>( tcx: TyCtxt<'tcx>, fn_trait_def_id: DefId, self_ty: Ty<'tcx>, - sig: ty::GenSig<'tcx>, + sig: ty::GenSig>, ) -> (ty::TraitRef<'tcx>, Ty<'tcx>) { assert!(!self_ty.has_escaping_bound_vars()); let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty]); @@ -317,7 +263,7 @@ pub fn iterator_trait_ref_and_outputs<'tcx>( tcx: TyCtxt<'tcx>, iterator_def_id: DefId, self_ty: Ty<'tcx>, - sig: ty::GenSig<'tcx>, + sig: ty::GenSig>, ) -> (ty::TraitRef<'tcx>, Ty<'tcx>) { assert!(!self_ty.has_escaping_bound_vars()); let trait_ref = ty::TraitRef::new(tcx, iterator_def_id, [self_ty]); @@ -328,7 +274,7 @@ pub fn async_iterator_trait_ref_and_outputs<'tcx>( tcx: TyCtxt<'tcx>, async_iterator_def_id: DefId, self_ty: Ty<'tcx>, - sig: ty::GenSig<'tcx>, + sig: ty::GenSig>, ) -> (ty::TraitRef<'tcx>, Ty<'tcx>) { assert!(!self_ty.has_escaping_bound_vars()); let trait_ref = ty::TraitRef::new(tcx, async_iterator_def_id, [self_ty]); @@ -520,7 +466,7 @@ impl<'tcx> TypeFolder> for BoundVarReplacer<'_, 'tcx> { let universe = self.universe_for(debruijn); let p = ty::PlaceholderConst { universe, bound: bound_const }; self.mapped_consts.insert(p, bound_const); - ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty()) + ty::Const::new_placeholder(self.infcx.tcx, p) } _ => ct.super_fold_with(self), } @@ -663,7 +609,7 @@ impl<'tcx> TypeFolder> for PlaceholderReplacer<'_, 'tcx> { let db = ty::DebruijnIndex::from_usize( self.universe_indices.len() - index + self.current_index.as_usize() - 1, ); - ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty()) + ty::Const::new_bound(self.infcx.tcx, db, *replace_var) } None => { if ct.has_infer() { diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index c93ec43944ad..017b0a45d1f4 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -1,15 +1,11 @@ use crate::errors::DumpVTableEntries; use crate::traits::{impossible_predicates, is_vtable_safe_method}; use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::LangItem; use rustc_infer::traits::util::PredicateSet; -use rustc_infer::traits::ImplSource; use rustc_middle::bug; use rustc_middle::query::Providers; -use rustc_middle::traits::BuiltinImplSource; -use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::GenericArgs; use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt, Upcast, VtblEntry}; +use rustc_middle::ty::{GenericArgs, TypeVisitableExt}; use rustc_span::{sym, Span}; use smallvec::{smallvec, SmallVec}; @@ -125,7 +121,7 @@ fn prepare_vtable_segments_inner<'tcx, T>( .predicates .into_iter() .filter_map(move |(pred, _)| { - pred.instantiate_supertrait(tcx, &inner_most_trait_ref).as_trait_clause() + pred.instantiate_supertrait(tcx, inner_most_trait_ref).as_trait_clause() }); // Find an unvisited supertrait @@ -320,30 +316,42 @@ fn vtable_entries<'tcx>( tcx.arena.alloc_from_iter(entries) } -/// Find slot base for trait methods within vtable entries of another trait -pub(super) fn vtable_trait_first_method_offset<'tcx>( - tcx: TyCtxt<'tcx>, - trait_to_be_found: ty::PolyTraitRef<'tcx>, - trait_owning_vtable: ty::PolyTraitRef<'tcx>, -) -> usize { - // #90177 - let trait_to_be_found_erased = tcx.erase_regions(trait_to_be_found); +// Given a `dyn Subtrait: Supertrait` trait ref, find corresponding first slot +// for `Supertrait`'s methods in the vtable of `Subtrait`. +pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRef<'tcx>) -> usize { + debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param()); + + let ty::Dynamic(source, _, _) = *key.self_ty().kind() else { + bug!(); + }; + let source_principal = tcx + .normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap()) + .with_self_ty(tcx, tcx.types.trait_object_dummy_self); + + let target_principal = tcx + .normalize_erasing_regions(ty::ParamEnv::reveal_all(), key) + // We don't care about the self type, since it will always be the same thing. + .with_self_ty(tcx, tcx.types.trait_object_dummy_self); let vtable_segment_callback = { - let mut vtable_base = 0; - + let mut vptr_offset = 0; move |segment| { match segment { VtblSegment::MetadataDSA => { - vtable_base += TyCtxt::COMMON_VTABLE_ENTRIES.len(); + vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); } VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - if tcx.erase_regions(trait_ref) == trait_to_be_found_erased { - return ControlFlow::Break(vtable_base); + if tcx + .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), trait_ref) + == target_principal + { + return ControlFlow::Break(vptr_offset); } - vtable_base += count_own_vtable_entries(tcx, trait_ref); + + vptr_offset += tcx.own_existential_vtable_entries(trait_ref.def_id()).len(); + if emit_vptr { - vtable_base += 1; + vptr_offset += 1; } } } @@ -351,55 +359,72 @@ pub(super) fn vtable_trait_first_method_offset<'tcx>( } }; - if let Some(vtable_base) = - prepare_vtable_segments(tcx, trait_owning_vtable, vtable_segment_callback) - { - vtable_base - } else { - bug!("Failed to find info for expected trait in vtable"); - } + prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap() } -/// Find slot offset for trait vptr within vtable entries of another trait -pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>( +/// Given a `dyn Subtrait` and `dyn Supertrait` trait object, find the slot of +/// // the trait vptr in the subtrait's vtable. +pub(crate) fn supertrait_vtable_slot<'tcx>( tcx: TyCtxt<'tcx>, key: ( - Ty<'tcx>, // trait object type whose trait owning vtable - Ty<'tcx>, // trait object for supertrait + Ty<'tcx>, // Source -- `dyn Subtrait`. + Ty<'tcx>, // Target -- `dyn Supertrait` being coerced to. ), ) -> Option { + debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param()); + let (source, target) = key; - assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.has_infer()); - assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.has_infer()); + let ty::Dynamic(source, _, _) = *source.kind() else { + bug!(); + }; + let source_principal = tcx + .normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap()) + .with_self_ty(tcx, tcx.types.trait_object_dummy_self); - // this has been typecked-before, so diagnostics is not really needed. - let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None); + let ty::Dynamic(target, _, _) = *target.kind() else { + bug!(); + }; + let target_principal = tcx + .normalize_erasing_regions(ty::ParamEnv::reveal_all(), target.principal().unwrap()) + .with_self_ty(tcx, tcx.types.trait_object_dummy_self); - let trait_ref = ty::TraitRef::new(tcx, unsize_trait_did, [source, target]); + let vtable_segment_callback = { + let mut vptr_offset = 0; + move |segment| { + match segment { + VtblSegment::MetadataDSA => { + vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); + } + VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { + vptr_offset += tcx.own_existential_vtable_entries(trait_ref.def_id()).len(); + if tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), trait_ref) + == target_principal + { + if emit_vptr { + return ControlFlow::Break(Some(vptr_offset)); + } else { + return ControlFlow::Break(None); + } + } - match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) { - Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, _)) => { - *vtable_vptr_slot + if emit_vptr { + vptr_offset += 1; + } + } + } + ControlFlow::Continue(()) } - otherwise => bug!("expected TraitUpcasting candidate, got {otherwise:?}"), - } -} + }; -/// Given a trait `trait_ref`, returns the number of vtable entries -/// that come from `trait_ref`, excluding its supertraits. Used in -/// computing the vtable base for an upcast trait of a trait object. -pub(crate) fn count_own_vtable_entries<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, -) -> usize { - tcx.own_existential_vtable_entries(trait_ref.def_id()).len() + prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap() } pub(super) fn provide(providers: &mut Providers) { *providers = Providers { own_existential_vtable_entries, vtable_entries, - vtable_trait_upcasting_coercion_new_vptr_slot, + first_method_vtable_slot, + supertrait_vtable_slot, ..*providers }; } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index f4189ff09020..066755f7b3e7 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -286,7 +286,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( // implemented, but rather from a "second order" obligation, where an associated // type has a projection coming from another associated type. // See `tests/ui/traits/assoc-type-in-superbad.rs` for an example. - if let Some(term_ty) = proj.term.ty() + if let Some(term_ty) = proj.term.as_type() && let Some(impl_item_span) = ty_to_impl_span(term_ty) { cause.span = impl_item_span; diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index b96b1b67a740..c73ececd1d15 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -4,13 +4,13 @@ // general routines. use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::traits::FulfillmentErrorCode; use rustc_middle::bug; use rustc_middle::traits::CodegenObligationError; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::{ - ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext, Unimplemented, + ImplSource, Obligation, ObligationCause, ObligationCtxt, ScrubbedTraitError, SelectionContext, + Unimplemented, }; use tracing::debug; @@ -50,6 +50,7 @@ pub fn codegen_select_candidate<'tcx>( // Currently, we use a fulfillment context to completely resolve // all nested obligations. This is because they can inform the // inference of the impl's type parameters. + // FIXME(-Znext-solver): Doesn't need diagnostics if new solver. let ocx = ObligationCtxt::new(&infcx); let impl_source = selection.map(|obligation| { ocx.register_obligation(obligation); @@ -64,7 +65,7 @@ pub fn codegen_select_candidate<'tcx>( // Cycle errors are the only post-monomorphization errors possible; emit them now so // `rustc_ty_utils::resolve_associated_item` doesn't return `None` post-monomorphization. for err in errors { - if let FulfillmentErrorCode::Cycle(cycle) = err.code { + if let ScrubbedTraitError::Cycle(cycle) = err { infcx.err_ctxt().report_overflow_obligation_cycle(&cycle); } } diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs index bc5436f76f1c..fdeda34d2946 100644 --- a/compiler/rustc_traits/src/lib.rs +++ b/compiler/rustc_traits/src/lib.rs @@ -1,6 +1,8 @@ //! Queries that are independent from the main solver code. +// tidy-alphabetical-start #![recursion_limit = "256"] +// tidy-alphabetical-end mod codegen; mod dropck_outlives; diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index fee13078250f..0430e0bb70eb 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -7,9 +7,7 @@ use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::query::{ normalize::NormalizationResult, CanonicalAliasGoal, NoSolution, }; -use rustc_trait_selection::traits::{ - self, FulfillmentErrorCode, ObligationCause, SelectionContext, -}; +use rustc_trait_selection::traits::{self, ObligationCause, ScrubbedTraitError, SelectionContext}; use tracing::debug; pub(crate) fn provide(p: &mut Providers) { @@ -49,7 +47,7 @@ fn normalize_canonicalized_projection_ty<'tcx>( // that impl vars are constrained by the signature, for example). if !tcx.sess.opts.actually_rustdoc { for error in &errors { - if let FulfillmentErrorCode::Cycle(cycle) = &error.code { + if let ScrubbedTraitError::Cycle(cycle) = &error { ocx.infcx.err_ctxt().report_overflow_obligation_cycle(cycle); } } @@ -60,7 +58,7 @@ fn normalize_canonicalized_projection_ty<'tcx>( // FIXME(associated_const_equality): All users of normalize_canonicalized_projection_ty // expected a type, but there is the possibility it could've been a const now. // Maybe change it to a Term later? - Ok(NormalizationResult { normalized_ty: answer.ty().unwrap() }) + Ok(NormalizationResult { normalized_ty: answer.expect_type() }) }, ) } diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 604b68d2cd4a..865f9487213f 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -331,30 +331,55 @@ pub(crate) mod rustc { assert!(def.is_enum()); let layout = ty_and_layout.layout; - if let Variants::Multiple { tag_field, .. } = layout.variants() { - // For enums (but not coroutines), the tag field is - // currently always the first field of the layout. - assert_eq!(*tag_field, 0); - } + // Computes the variant of a given index. + let layout_of_variant = |index| { + let tag = cx.tcx.tag_for_variant((ty_and_layout.ty, index)); + let variant_def = Def::Variant(def.variant(index)); + let variant_ty_and_layout = ty_and_layout.for_variant(&cx, index); + Self::from_variant(variant_def, tag, variant_ty_and_layout, layout.size, cx) + }; - let variants = def.discriminants(cx.tcx()).try_fold( - Self::uninhabited(), - |variants, (idx, ref discriminant)| { - let tag = cx.tcx.tag_for_variant((ty_and_layout.ty, idx)); - let variant_def = Def::Variant(def.variant(idx)); - let variant_ty_and_layout = ty_and_layout.for_variant(&cx, idx); - let variant = Self::from_variant( - variant_def, - tag, - variant_ty_and_layout, - layout.size, - cx, + // We consider three kinds of enums, each demanding a different + // treatment of their layout computation: + // 1. enums that are uninhabited ZSTs + // 2. enums that delegate their layout to a variant + // 3. enums with multiple variants + match layout.variants() { + Variants::Single { .. } + if layout.abi.is_uninhabited() && layout.size == Size::ZERO => + { + // The layout representation of uninhabited, ZST enums is + // defined to be like that of the `!` type, as opposed of a + // typical enum. Consequently, they cannot be descended into + // as if they typical enums. We therefore special-case this + // scenario and simply return an uninhabited `Tree`. + Ok(Self::uninhabited()) + } + Variants::Single { index } => { + // `Variants::Single` on enums with variants denotes that + // the enum delegates its layout to the variant at `index`. + layout_of_variant(*index) + } + Variants::Multiple { tag_field, .. } => { + // `Variants::Multiple` denotes an enum with multiple + // variants. The layout of such an enum is the disjunction + // of the layouts of its tagged variants. + + // For enums (but not coroutines), the tag field is + // currently always the first field of the layout. + assert_eq!(*tag_field, 0); + + let variants = def.discriminants(cx.tcx()).try_fold( + Self::uninhabited(), + |variants, (idx, ref discriminant)| { + let variant = layout_of_variant(idx)?; + Result::::Ok(variants.or(variant)) + }, )?; - Result::::Ok(variants.or(variant)) - }, - )?; - return Ok(Self::def(Def::Adt(def)).then(variants)); + return Ok(Self::def(Def::Adt(def)).then(variants)); + } + } } /// Constructs a `Tree` from a 'variant-like' layout. @@ -420,7 +445,7 @@ pub(crate) mod rustc { fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self { use rustc_target::abi::Endian; let size = tag.size(); - let bits = tag.assert_bits(size); + let bits = tag.to_bits(size); let bytes: [u8; 16]; let bytes = match tcx.data_layout.endian { Endian::Little => { diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index 8d7d81d8f735..2b052412e6b6 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -1,6 +1,8 @@ +// tidy-alphabetical-start +#![allow(unused_variables)] #![feature(alloc_layout_extra)] #![feature(never_type)] -#![allow(unused_variables)] +// tidy-alphabetical-end pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set}; @@ -135,7 +137,7 @@ mod rustc { use rustc_middle::ty::ScalarInt; use rustc_span::symbol::sym; - let Ok(cv) = c.eval(tcx, param_env, DUMMY_SP) else { + let Ok((ty, cv)) = c.eval(tcx, param_env, DUMMY_SP) else { return Some(Self { alignment: true, lifetimes: true, @@ -144,7 +146,7 @@ mod rustc { }); }; - let adt_def = c.ty().ty_adt_def()?; + let adt_def = ty.ty_adt_def()?; assert_eq!( tcx.require_lang_item(LangItem::TransmuteOpts, None), diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 9090bb30f897..f1dd94839fe1 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -5,7 +5,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::layout::{ fn_can_unwind, FnAbiError, HasParamEnv, HasTyCtxt, LayoutCx, LayoutOf, TyAndLayout, }; -use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt}; +use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt}; use rustc_session::config::OptLevel; use rustc_span::def_id::DefId; use rustc_target::abi::call::{ @@ -14,6 +14,7 @@ use rustc_target::abi::call::{ }; use rustc_target::abi::*; use rustc_target::spec::abi::Abi as SpecAbi; +use tracing::debug; use std::iter; @@ -32,7 +33,7 @@ fn fn_sig_for_fn_abi<'tcx>( instance: ty::Instance<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> ty::PolyFnSig<'tcx> { - if let InstanceDef::ThreadLocalShim(..) = instance.def { + if let InstanceKind::ThreadLocalShim(..) = instance.def { return ty::Binder::dummy(tcx.mk_fn_sig( [], tcx.thread_local_ptr_ty(instance.def_id()), @@ -62,7 +63,7 @@ fn fn_sig_for_fn_abi<'tcx>( _ => unreachable!(), }; - if let ty::InstanceDef::VTableShim(..) = instance.def { + if let ty::InstanceKind::VTableShim(..) = instance.def { // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. sig = sig.map_bound(|mut sig| { let mut inputs_and_output = sig.inputs_and_output.to_vec(); @@ -120,7 +121,7 @@ fn fn_sig_for_fn_abi<'tcx>( let mut coroutine_kind = args.as_coroutine_closure().kind(); let env_ty = - if let InstanceDef::ConstructCoroutineInClosureShim { receiver_by_ref, .. } = + if let InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref, .. } = instance.def { coroutine_kind = ty::ClosureKind::FnOnce; @@ -173,7 +174,7 @@ fn fn_sig_for_fn_abi<'tcx>( // make sure we respect the `target_kind` in that shim. // FIXME(async_closures): This shouldn't be needed, and we should be populating // a separate def-id for these bodies. - if let InstanceDef::CoroutineKindShim { .. } = instance.def { + if let InstanceKind::CoroutineKindShim { .. } = instance.def { // Grab the parent coroutine-closure. It has the same args for the purposes // of instantiation, so this will be okay to do. let ty::CoroutineClosure(_, coroutine_closure_args) = *tcx @@ -385,7 +386,7 @@ fn fn_abi_of_instance<'tcx>( extra_args, caller_location, Some(instance.def_id()), - matches!(instance.def, ty::InstanceDef::Virtual(..)), + matches!(instance.def, ty::InstanceKind::Virtual(..)), ) } @@ -519,7 +520,8 @@ fn fn_abi_sanity_check<'tcx>( assert!( matches!(&*cx.tcx.sess.target.arch, "wasm32" | "wasm64") || matches!(spec_abi, SpecAbi::PtxKernel | SpecAbi::Unadjusted), - r#"`PassMode::Direct` for aggregates only allowed for "unadjusted" and "ptx-kernel" functions and on wasm\nProblematic type: {:#?}"#, + "`PassMode::Direct` for aggregates only allowed for \"unadjusted\" and \"ptx-kernel\" functions and on wasm\n\ + Problematic type: {:#?}", arg.layout, ); } diff --git a/compiler/rustc_ty_utils/src/common_traits.rs b/compiler/rustc_ty_utils/src/common_traits.rs index cb95239e9919..51b908881eb4 100644 --- a/compiler/rustc_ty_utils/src/common_traits.rs +++ b/compiler/rustc_ty_utils/src/common_traits.rs @@ -22,17 +22,6 @@ fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) is_item_raw(tcx, query, LangItem::Unpin) } -fn has_surface_async_drop_raw<'tcx>( - tcx: TyCtxt<'tcx>, - query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, -) -> bool { - is_item_raw(tcx, query, LangItem::AsyncDrop) -} - -fn has_surface_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - is_item_raw(tcx, query, LangItem::Drop) -} - fn is_item_raw<'tcx>( tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, @@ -45,13 +34,5 @@ fn is_item_raw<'tcx>( } pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { - is_copy_raw, - is_sized_raw, - is_freeze_raw, - is_unpin_raw, - has_surface_async_drop_raw, - has_surface_drop_raw, - ..*providers - }; + *providers = Providers { is_copy_raw, is_sized_raw, is_freeze_raw, is_unpin_raw, ..*providers }; } diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index fec02f515caf..58f812fc7cfa 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -11,6 +11,7 @@ use rustc_middle::ty::{self, Expr, TyCtxt, TypeVisitableExt}; use rustc_middle::{mir, thir}; use rustc_span::Span; use rustc_target::abi::{VariantIdx, FIRST_VARIANT}; +use tracing::{debug, instrument}; use std::iter; @@ -22,7 +23,7 @@ fn destructure_const<'tcx>( tcx: TyCtxt<'tcx>, const_: ty::Const<'tcx>, ) -> ty::DestructuredConst<'tcx> { - let ty::ConstKind::Value(valtree) = const_.kind() else { + let ty::ConstKind::Value(ct_ty, valtree) = const_.kind() else { bug!("cannot destructure constant {:?}", const_) }; @@ -31,7 +32,7 @@ fn destructure_const<'tcx>( _ => bug!("cannot destructure constant {:?}", const_), }; - let (fields, variant) = match const_.ty().kind() { + let (fields, variant) = match ct_ty.kind() { ty::Array(inner_ty, _) | ty::Slice(inner_ty) => { // construct the consts for the elements of the array/slice let field_consts = branches @@ -46,7 +47,7 @@ fn destructure_const<'tcx>( ty::Adt(def, args) => { let (variant_idx, branches) = if def.is_enum() { let (head, rest) = branches.split_first().unwrap(); - (VariantIdx::from_u32(head.unwrap_leaf().try_to_u32().unwrap()), rest) + (VariantIdx::from_u32(head.unwrap_leaf().to_u32()), rest) } else { (FIRST_VARIANT, branches) }; @@ -81,9 +82,9 @@ fn destructure_const<'tcx>( fn check_binop(op: mir::BinOp) -> bool { use mir::BinOp::*; match op { - Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor - | BitAnd | BitOr | Shl | ShlUnchecked | Shr | ShrUnchecked | Eq | Lt | Le | Ne | Ge - | Gt | Cmp => true, + Add | AddUnchecked | AddWithOverflow | Sub | SubUnchecked | SubWithOverflow | Mul + | MulUnchecked | MulWithOverflow | Div | Rem | BitXor | BitAnd | BitOr | Shl + | ShlUnchecked | Shr | ShrUnchecked | Eq | Lt | Le | Ne | Ge | Gt | Cmp => true, Offset => false, } } @@ -93,7 +94,7 @@ fn check_binop(op: mir::BinOp) -> bool { fn check_unop(op: mir::UnOp) -> bool { use mir::UnOp::*; match op { - Not | Neg => true, + Not | Neg | PtrMetadata => true, } } @@ -120,7 +121,7 @@ fn recurse_build<'tcx>( let sp = node.span; match tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) { Ok(c) => c, - Err(LitToConstError::Reported(guar)) => ty::Const::new_error(tcx, guar, node.ty), + Err(LitToConstError::Reported(guar)) => ty::Const::new_error(tcx, guar), Err(LitToConstError::TypeError) => { bug!("encountered type error in lit_to_const") } @@ -136,28 +137,31 @@ fn recurse_build<'tcx>( } &ExprKind::NamedConst { def_id, args, user_ty: _ } => { let uneval = ty::UnevaluatedConst::new(def_id, args); - ty::Const::new_unevaluated(tcx, uneval, node.ty) + ty::Const::new_unevaluated(tcx, uneval) } - ExprKind::ConstParam { param, .. } => ty::Const::new_param(tcx, *param, node.ty), + ExprKind::ConstParam { param, .. } => ty::Const::new_param(tcx, *param), ExprKind::Call { fun, args, .. } => { + let fun_ty = body.exprs[*fun].ty; let fun = recurse_build(tcx, body, *fun, root_span)?; let mut new_args = Vec::>::with_capacity(args.len()); for &id in args.iter() { new_args.push(recurse_build(tcx, body, id, root_span)?); } - let new_args = tcx.mk_const_list(&new_args); - ty::Const::new_expr(tcx, Expr::FunctionCall(fun, new_args), node.ty) + ty::Const::new_expr(tcx, Expr::new_call(tcx, fun_ty, fun, new_args)) } &ExprKind::Binary { op, lhs, rhs } if check_binop(op) => { + let lhs_ty = body.exprs[lhs].ty; let lhs = recurse_build(tcx, body, lhs, root_span)?; + let rhs_ty = body.exprs[rhs].ty; let rhs = recurse_build(tcx, body, rhs, root_span)?; - ty::Const::new_expr(tcx, Expr::Binop(op, lhs, rhs), node.ty) + ty::Const::new_expr(tcx, Expr::new_binop(tcx, op, lhs_ty, rhs_ty, lhs, rhs)) } &ExprKind::Unary { op, arg } if check_unop(op) => { + let arg_ty = body.exprs[arg].ty; let arg = recurse_build(tcx, body, arg, root_span)?; - ty::Const::new_expr(tcx, Expr::UnOp(op, arg), node.ty) + ty::Const::new_expr(tcx, Expr::new_unop(tcx, op, arg_ty, arg)) } // This is necessary so that the following compiles: // @@ -177,12 +181,14 @@ fn recurse_build<'tcx>( // "coercion cast" i.e. using a coercion or is a no-op. // This is important so that `N as usize as usize` doesn't unify with `N as usize`. (untested) &ExprKind::Use { source } => { - let arg = recurse_build(tcx, body, source, root_span)?; - ty::Const::new_expr(tcx, Expr::Cast(CastKind::Use, arg, node.ty), node.ty) + let value_ty = body.exprs[source].ty; + let value = recurse_build(tcx, body, source, root_span)?; + ty::Const::new_expr(tcx, Expr::new_cast(tcx, CastKind::Use, value_ty, value, node.ty)) } &ExprKind::Cast { source } => { - let arg = recurse_build(tcx, body, source, root_span)?; - ty::Const::new_expr(tcx, Expr::Cast(CastKind::As, arg, node.ty), node.ty) + let value_ty = body.exprs[source].ty; + let value = recurse_build(tcx, body, source, root_span)?; + ty::Const::new_expr(tcx, Expr::new_cast(tcx, CastKind::As, value_ty, value, node.ty)) } ExprKind::Borrow { arg, .. } => { let arg_node = &body.exprs[*arg]; @@ -397,10 +403,10 @@ impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { } /// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead. -fn thir_abstract_const( - tcx: TyCtxt<'_>, +fn thir_abstract_const<'tcx>( + tcx: TyCtxt<'tcx>, def: LocalDefId, -) -> Result>>, ErrorGuaranteed> { +) -> Result>>, ErrorGuaranteed> { if !tcx.features().generic_const_exprs { return Ok(None); } diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index e87058f9ba48..6f71951b5162 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -79,11 +79,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' orig_lt, ty::Region::new_early_param( tcx, - ty::EarlyParamRegion { - def_id: param.def_id, - index: param.index, - name: param.name, - }, + ty::EarlyParamRegion { index: param.index, name: param.name }, ), ); } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 41f482d8a706..c50a490a9dc0 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -1,14 +1,17 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; +use rustc_hir::LangItem; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::traits::{BuiltinImplSource, CodegenObligationError}; +use rustc_middle::ty::util::AsyncDropGlueMorphology; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, Instance, TyCtxt, TypeVisitableExt}; use rustc_span::sym; use rustc_trait_selection::traits; use rustc_type_ir::ClosureKind; +use tracing::debug; use traits::{translate_args, Reveal}; use crate::errors::UnexpectedFnPtrAssociatedItem; @@ -31,8 +34,8 @@ fn resolve_instance<'tcx>( } else { let def = if tcx.intrinsic(def_id).is_some() { debug!(" => intrinsic"); - ty::InstanceDef::Intrinsic(def_id) - } else if Some(def_id) == tcx.lang_items().drop_in_place_fn() { + ty::InstanceKind::Intrinsic(def_id) + } else if tcx.is_lang_item(def_id, LangItem::DropInPlace) { let ty = args.type_at(0); if ty.needs_drop(tcx, param_env) { @@ -50,15 +53,15 @@ fn resolve_instance<'tcx>( _ => return Ok(None), } - ty::InstanceDef::DropGlue(def_id, Some(ty)) + ty::InstanceKind::DropGlue(def_id, Some(ty)) } else { debug!(" => trivial drop glue"); - ty::InstanceDef::DropGlue(def_id, None) + ty::InstanceKind::DropGlue(def_id, None) } - } else if Some(def_id) == tcx.lang_items().async_drop_in_place_fn() { + } else if tcx.is_lang_item(def_id, LangItem::AsyncDropInPlace) { let ty = args.type_at(0); - if !ty.is_async_destructor_noop(tcx, param_env) { + if ty.async_drop_glue_morphology(tcx) != AsyncDropGlueMorphology::Noop { match *ty.kind() { ty::Closure(..) | ty::CoroutineClosure(..) @@ -72,15 +75,15 @@ fn resolve_instance<'tcx>( _ => return Ok(None), } debug!(" => nontrivial async drop glue ctor"); - ty::InstanceDef::AsyncDropGlueCtorShim(def_id, Some(ty)) + ty::InstanceKind::AsyncDropGlueCtorShim(def_id, Some(ty)) } else { debug!(" => trivial async drop glue ctor"); - ty::InstanceDef::AsyncDropGlueCtorShim(def_id, None) + ty::InstanceKind::AsyncDropGlueCtorShim(def_id, None) } } else { debug!(" => free item"); // FIXME(effects): we may want to erase the effect param if that is present on this item. - ty::InstanceDef::Item(def_id) + ty::InstanceKind::Item(def_id) }; Ok(Some(Instance { def, args })) @@ -210,17 +213,26 @@ fn resolve_associated_item<'tcx>( Some(ty::Instance::new(leaf_def.item.def_id, args)) } - traits::ImplSource::Builtin(BuiltinImplSource::Object { vtable_base }, _) => { - traits::get_vtable_index_of_object_method(tcx, *vtable_base, trait_item_id).map( - |index| Instance { - def: ty::InstanceDef::Virtual(trait_item_id, index), + traits::ImplSource::Builtin(BuiltinImplSource::Object(_), _) => { + let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_args); + if trait_ref.has_non_region_infer() || trait_ref.has_non_region_param() { + // We only resolve totally substituted vtable entries. + None + } else { + let vtable_base = tcx.first_method_vtable_slot(trait_ref); + let offset = tcx + .own_existential_vtable_entries(trait_id) + .iter() + .copied() + .position(|def_id| def_id == trait_item_id); + offset.map(|offset| Instance { + def: ty::InstanceKind::Virtual(trait_item_id, vtable_base + offset), args: rcvr_args, - }, - ) + }) + } } traits::ImplSource::Builtin(BuiltinImplSource::Misc, _) => { - let lang_items = tcx.lang_items(); - if Some(trait_ref.def_id) == lang_items.clone_trait() { + if tcx.is_lang_item(trait_ref.def_id, LangItem::Clone) { // FIXME(eddyb) use lang items for methods instead of names. let name = tcx.item_name(trait_item_id); if name == sym::clone { @@ -236,7 +248,7 @@ fn resolve_associated_item<'tcx>( }; Some(Instance { - def: ty::InstanceDef::CloneShim(trait_item_id, self_ty), + def: ty::InstanceKind::CloneShim(trait_item_id, self_ty), args: rcvr_args, }) } else { @@ -246,14 +258,14 @@ fn resolve_associated_item<'tcx>( let args = tcx.erase_regions(rcvr_args); Some(ty::Instance::new(trait_item_id, args)) } - } else if Some(trait_ref.def_id) == lang_items.fn_ptr_trait() { - if lang_items.fn_ptr_addr() == Some(trait_item_id) { + } else if tcx.is_lang_item(trait_ref.def_id, LangItem::FnPtrTrait) { + if tcx.is_lang_item(trait_item_id, LangItem::FnPtrAddr) { let self_ty = trait_ref.self_ty(); if !matches!(self_ty.kind(), ty::FnPtr(..)) { return Ok(None); } Some(Instance { - def: ty::InstanceDef::FnPtrAddrShim(trait_item_id, self_ty), + def: ty::InstanceKind::FnPtrAddrShim(trait_item_id, self_ty), args: rcvr_args, }) } else { @@ -271,7 +283,7 @@ fn resolve_associated_item<'tcx>( { // For compiler developers who'd like to add new items to `Fn`/`FnMut`/`FnOnce`, // you either need to generate a shim body, or perhaps return - // `InstanceDef::Item` pointing to a trait default method body if + // `InstanceKind::Item` pointing to a trait default method body if // it is given a default implementation by the trait. bug!( "no definition for `{trait_ref}::{}` for built-in callable type", @@ -283,7 +295,7 @@ fn resolve_associated_item<'tcx>( Some(Instance::resolve_closure(tcx, closure_def_id, args, target_kind)) } ty::FnDef(..) | ty::FnPtr(..) => Some(Instance { - def: ty::InstanceDef::FnPtrShim(trait_item_id, rcvr_args.type_at(0)), + def: ty::InstanceKind::FnPtrShim(trait_item_id, rcvr_args.type_at(0)), args: rcvr_args, }), ty::CoroutineClosure(coroutine_closure_def_id, args) => { @@ -296,7 +308,7 @@ fn resolve_associated_item<'tcx>( Some(Instance::new(coroutine_closure_def_id, args)) } else { Some(Instance { - def: ty::InstanceDef::ConstructCoroutineInClosureShim { + def: ty::InstanceKind::ConstructCoroutineInClosureShim { coroutine_closure_def_id, receiver_by_ref: target_kind != ty::ClosureKind::FnOnce, }, @@ -319,7 +331,7 @@ fn resolve_associated_item<'tcx>( // If we're computing `AsyncFnOnce` for a by-ref closure then // construct a new body that has the right return types. Some(Instance { - def: ty::InstanceDef::ConstructCoroutineInClosureShim { + def: ty::InstanceKind::ConstructCoroutineInClosureShim { coroutine_closure_def_id, receiver_by_ref: false, }, @@ -333,7 +345,7 @@ fn resolve_associated_item<'tcx>( Some(Instance::resolve_closure(tcx, closure_def_id, args, target_kind)) } ty::FnDef(..) | ty::FnPtr(..) => Some(Instance { - def: ty::InstanceDef::FnPtrShim(trait_item_id, rcvr_args.type_at(0)), + def: ty::InstanceKind::FnPtrShim(trait_item_id, rcvr_args.type_at(0)), args: rcvr_args, }), _ => bug!( diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 1ef22497a8fc..6045abc50a9d 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -10,12 +10,14 @@ use rustc_middle::ty::layout::{ }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ - self, AdtDef, EarlyBinder, FieldDef, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, + self, AdtDef, CoroutineArgsExt, EarlyBinder, FieldDef, GenericArgsRef, Ty, TyCtxt, + TypeVisitableExt, }; use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; use rustc_span::sym; use rustc_span::symbol::Symbol; use rustc_target::abi::*; +use tracing::{debug, instrument, trace}; use std::fmt::Debug; use std::iter; diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index e8c5c54d3a09..ad0bcbfbbc2a 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -4,10 +4,10 @@ //! //! This API is completely unstable and subject to change. +// tidy-alphabetical-start +#![allow(internal_features)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(rustdoc_internals)] -#![allow(internal_features)] #![feature(assert_matches)] #![feature(associated_type_defaults)] #![feature(box_patterns)] @@ -15,9 +15,8 @@ #![feature(iterator_try_collect)] #![feature(let_chains)] #![feature(never_type)] - -#[macro_use] -extern crate tracing; +#![feature(rustdoc_internals)] +// tidy-alphabetical-end use rustc_middle::query::Providers; diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 4e23fb303836..205b3f2760f3 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -9,6 +9,7 @@ use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt}; use rustc_session::Limit; use rustc_span::sym; +use tracing::debug; use crate::errors::NeedsDropOverflow; @@ -29,6 +30,21 @@ fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx> res } +fn needs_async_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + // If we don't know a type doesn't need async drop, for example if it's a + // type parameter without a `Copy` bound, then we conservatively return that + // it needs async drop. + let adt_has_async_dtor = + |adt_def: ty::AdtDef<'tcx>| adt_def.async_destructor(tcx).map(|_| DtorType::Significant); + let res = drop_tys_helper(tcx, query.value, query.param_env, adt_has_async_dtor, false) + .filter(filter_array_elements(tcx, query.param_env)) + .next() + .is_some(); + + debug!("needs_drop_raw({:?}) = {:?}", query, res); + res +} + /// HACK: in order to not mistakenly assume that `[PhantomData; N]` requires drop glue /// we check the element type for drop glue. The correct fix would be looking at the /// entirety of the code around `needs_drop_components` and this file and come up with @@ -388,6 +404,7 @@ fn adt_significant_drop_tys( pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { needs_drop_raw, + needs_async_drop_raw, has_significant_drop_raw, adt_drop_tys, adt_significant_drop_tys, diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index be227ec8b9aa..686f2f04ad9a 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -8,6 +8,7 @@ use rustc_middle::ty::util::{CheckRegions, NotUniqueParam}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_span::Span; +use tracing::{instrument, trace}; use crate::errors::{DuplicateArg, NotParam}; @@ -108,7 +109,7 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { #[instrument(level = "trace", skip(self))] fn collect_taits_declared_in_body(&mut self) { - let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(self.item)).value; + let body = self.tcx.hir().body_owned_by(self.item).value; struct TaitInBodyFinder<'a, 'tcx> { collector: &'a mut OpaqueTypeCollector<'tcx>, } diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index 2d8c78267d9f..97a1b94263e9 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -8,6 +8,7 @@ use rustc_middle::span_bug; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::Span; use rustc_type_ir::visit::TypeVisitable; +use tracing::{instrument, trace}; pub trait SpannedTypeVisitor<'tcx> { type Result: VisitorResult = (); diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 3094956fa5f3..38950c97c9d5 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -10,6 +10,7 @@ use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, Upcast}; use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_span::DUMMY_SP; use rustc_trait_selection::traits; +use tracing::{debug, instrument}; #[instrument(level = "debug", skip(tcx), ret)] fn sized_constraint_for_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option> { @@ -76,7 +77,7 @@ fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness { fn adt_sized_constraint<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, -) -> Option>> { +) -> Option>> { if let Some(def_id) = def_id.as_local() { if let ty::Representability::Infinite(_) = tcx.representability(def_id) { return None; @@ -252,7 +253,7 @@ fn param_env_reveal_all_normalized(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamE fn self_ty_of_trait_impl_enabling_order_dep_trait_object_hack( tcx: TyCtxt<'_>, def_id: DefId, -) -> Option>> { +) -> Option>> { let impl_ = tcx.impl_trait_header(def_id).unwrap_or_else(|| bug!("called on inherent impl {def_id:?}")); diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index cef3f4e8ce05..18a09067a2c9 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -15,6 +15,7 @@ rustc_serialize = { path = "../rustc_serialize", optional = true } rustc_span = { path = "../rustc_span", optional = true } rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } smallvec = { version = "1.8.1", default-features = false } +tracing = "0.1" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs new file mode 100644 index 000000000000..5042a38fdb8e --- /dev/null +++ b/compiler/rustc_type_ir/src/binder.rs @@ -0,0 +1,856 @@ +use std::fmt::Debug; +use std::hash::Hash; +use std::marker::PhantomData; +use std::ops::{ControlFlow, Deref}; + +#[cfg(feature = "nightly")] +use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_serialize::Decodable; +use tracing::debug; + +use crate::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable}; +use crate::inherent::*; +use crate::lift::Lift; +use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; +use crate::{self as ty, Interner, SsoHashSet}; + +/// Binder is a binder for higher-ranked lifetimes or types. It is part of the +/// compiler's representation for things like `for<'a> Fn(&'a isize)` +/// (which would be represented by the type `PolyTraitRef == +/// Binder`). Note that when we instantiate, +/// erase, or otherwise "discharge" these bound vars, we change the +/// type from `Binder` to just `T` (see +/// e.g., `liberate_late_bound_regions`). +/// +/// `Decodable` and `Encodable` are implemented for `Binder` using the `impl_binder_encode_decode!` macro. +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "T: Clone"), + Copy(bound = "T: Copy"), + Hash(bound = "T: Hash"), + PartialEq(bound = "T: PartialEq"), + Eq(bound = "T: Eq"), + Debug(bound = "T: Debug") +)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +pub struct Binder { + value: T, + bound_vars: I::BoundVarKinds, +} + +// FIXME: We manually derive `Lift` because the `derive(Lift_Generic)` doesn't +// understand how to turn `T` to `T::Lifted` in the output `type Lifted`. +impl Lift for Binder +where + T: Lift, + I::BoundVarKinds: Lift, +{ + type Lifted = Binder; + + fn lift_to_tcx(self, tcx: U) -> Option { + Some(Binder { + value: self.value.lift_to_tcx(tcx)?, + bound_vars: self.bound_vars.lift_to_tcx(tcx)?, + }) + } +} + +macro_rules! impl_binder_encode_decode { + ($($t:ty),+ $(,)?) => { + $( + impl> rustc_serialize::Encodable for ty::Binder + where + $t: rustc_serialize::Encodable, + I::BoundVarKinds: rustc_serialize::Encodable, + { + fn encode(&self, e: &mut E) { + self.bound_vars().encode(e); + self.as_ref().skip_binder().encode(e); + } + } + impl> Decodable for ty::Binder + where + $t: TypeVisitable + rustc_serialize::Decodable, + I::BoundVarKinds: rustc_serialize::Decodable, + { + fn decode(decoder: &mut D) -> Self { + let bound_vars = Decodable::decode(decoder); + ty::Binder::bind_with_vars(<$t>::decode(decoder), bound_vars) + } + } + )* + } +} + +impl_binder_encode_decode! { + ty::FnSig, + ty::TraitPredicate, + ty::ExistentialPredicate, + ty::TraitRef, + ty::ExistentialTraitRef, +} + +impl Binder +where + T: TypeVisitable, +{ + /// Wraps `value` in a binder, asserting that `value` does not + /// contain any bound vars that would be bound by the + /// binder. This is commonly used to 'inject' a value T into a + /// different binding level. + #[track_caller] + pub fn dummy(value: T) -> Binder { + assert!( + !value.has_escaping_bound_vars(), + "`{value:?}` has escaping bound vars, so it cannot be wrapped in a dummy binder." + ); + Binder { value, bound_vars: Default::default() } + } + + pub fn bind_with_vars(value: T, bound_vars: I::BoundVarKinds) -> Binder { + if cfg!(debug_assertions) { + let mut validator = ValidateBoundVars::new(bound_vars); + value.visit_with(&mut validator); + } + Binder { value, bound_vars } + } +} + +impl> TypeFoldable for Binder { + fn try_fold_with>(self, folder: &mut F) -> Result { + folder.try_fold_binder(self) + } +} + +impl> TypeVisitable for Binder { + fn visit_with>(&self, visitor: &mut V) -> V::Result { + visitor.visit_binder(self) + } +} + +impl> TypeSuperFoldable for Binder { + fn try_super_fold_with>( + self, + folder: &mut F, + ) -> Result { + self.try_map_bound(|ty| ty.try_fold_with(folder)) + } +} + +impl> TypeSuperVisitable for Binder { + fn super_visit_with>(&self, visitor: &mut V) -> V::Result { + self.as_ref().skip_binder().visit_with(visitor) + } +} + +impl Binder { + /// Skips the binder and returns the "bound" value. This is a + /// risky thing to do because it's easy to get confused about + /// De Bruijn indices and the like. It is usually better to + /// discharge the binder using `no_bound_vars` or + /// `instantiate_bound_regions` or something like + /// that. `skip_binder` is only valid when you are either + /// extracting data that has nothing to do with bound vars, you + /// are doing some sort of test that does not involve bound + /// regions, or you are being very careful about your depth + /// accounting. + /// + /// Some examples where `skip_binder` is reasonable: + /// + /// - extracting the `DefId` from a PolyTraitRef; + /// - comparing the self type of a PolyTraitRef to see if it is equal to + /// a type parameter `X`, since the type `X` does not reference any regions + pub fn skip_binder(self) -> T { + self.value + } + + pub fn bound_vars(&self) -> I::BoundVarKinds { + self.bound_vars + } + + pub fn as_ref(&self) -> Binder { + Binder { value: &self.value, bound_vars: self.bound_vars } + } + + pub fn as_deref(&self) -> Binder + where + T: Deref, + { + Binder { value: &self.value, bound_vars: self.bound_vars } + } + + pub fn map_bound_ref>(&self, f: F) -> Binder + where + F: FnOnce(&T) -> U, + { + self.as_ref().map_bound(f) + } + + pub fn map_bound>(self, f: F) -> Binder + where + F: FnOnce(T) -> U, + { + let Binder { value, bound_vars } = self; + let value = f(value); + if cfg!(debug_assertions) { + let mut validator = ValidateBoundVars::new(bound_vars); + value.visit_with(&mut validator); + } + Binder { value, bound_vars } + } + + pub fn try_map_bound, E>(self, f: F) -> Result, E> + where + F: FnOnce(T) -> Result, + { + let Binder { value, bound_vars } = self; + let value = f(value)?; + if cfg!(debug_assertions) { + let mut validator = ValidateBoundVars::new(bound_vars); + value.visit_with(&mut validator); + } + Ok(Binder { value, bound_vars }) + } + + /// Wraps a `value` in a binder, using the same bound variables as the + /// current `Binder`. This should not be used if the new value *changes* + /// the bound variables. Note: the (old or new) value itself does not + /// necessarily need to *name* all the bound variables. + /// + /// This currently doesn't do anything different than `bind`, because we + /// don't actually track bound vars. However, semantically, it is different + /// because bound vars aren't allowed to change here, whereas they are + /// in `bind`. This may be (debug) asserted in the future. + pub fn rebind(&self, value: U) -> Binder + where + U: TypeVisitable, + { + Binder::bind_with_vars(value, self.bound_vars) + } + + /// Unwraps and returns the value within, but only if it contains + /// no bound vars at all. (In other words, if this binder -- + /// and indeed any enclosing binder -- doesn't bind anything at + /// all.) Otherwise, returns `None`. + /// + /// (One could imagine having a method that just unwraps a single + /// binder, but permits late-bound vars bound by enclosing + /// binders, but that would require adjusting the debruijn + /// indices, and given the shallow binding structure we often use, + /// would not be that useful.) + pub fn no_bound_vars(self) -> Option + where + T: TypeVisitable, + { + // `self.value` is equivalent to `self.skip_binder()` + if self.value.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) } + } + + /// Splits the contents into two things that share the same binder + /// level as the original, returning two distinct binders. + /// + /// `f` should consider bound regions at depth 1 to be free, and + /// anything it produces with bound regions at depth 1 will be + /// bound in the resulting return values. + pub fn split(self, f: F) -> (Binder, Binder) + where + F: FnOnce(T) -> (U, V), + { + let Binder { value, bound_vars } = self; + let (u, v) = f(value); + (Binder { value: u, bound_vars }, Binder { value: v, bound_vars }) + } +} + +impl Binder> { + pub fn transpose(self) -> Option> { + let Binder { value, bound_vars } = self; + value.map(|value| Binder { value, bound_vars }) + } +} + +impl Binder { + pub fn iter(self) -> impl Iterator> { + let Binder { value, bound_vars } = self; + value.into_iter().map(move |value| Binder { value, bound_vars }) + } +} + +pub struct ValidateBoundVars { + bound_vars: I::BoundVarKinds, + binder_index: ty::DebruijnIndex, + // We may encounter the same variable at different levels of binding, so + // this can't just be `Ty` + visited: SsoHashSet<(ty::DebruijnIndex, I::Ty)>, +} + +impl ValidateBoundVars { + pub fn new(bound_vars: I::BoundVarKinds) -> Self { + ValidateBoundVars { + bound_vars, + binder_index: ty::INNERMOST, + visited: SsoHashSet::default(), + } + } +} + +impl TypeVisitor for ValidateBoundVars { + type Result = ControlFlow<()>; + + fn visit_binder>(&mut self, t: &Binder) -> Self::Result { + self.binder_index.shift_in(1); + let result = t.super_visit_with(self); + self.binder_index.shift_out(1); + result + } + + fn visit_ty(&mut self, t: I::Ty) -> Self::Result { + if t.outer_exclusive_binder() < self.binder_index + || !self.visited.insert((self.binder_index, t)) + { + return ControlFlow::Break(()); + } + match t.kind() { + ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { + let idx = bound_ty.var().as_usize(); + if self.bound_vars.len() <= idx { + panic!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars); + } + bound_ty.assert_eq(self.bound_vars[idx]); + } + _ => {} + }; + + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: I::Region) -> Self::Result { + match r.kind() { + ty::ReBound(index, br) if index == self.binder_index => { + let idx = br.var().as_usize(); + if self.bound_vars.len() <= idx { + panic!("Not enough bound vars: {:?} not found in {:?}", r, self.bound_vars); + } + br.assert_eq(self.bound_vars[idx]); + } + + _ => (), + }; + + ControlFlow::Continue(()) + } +} + +/// Similar to [`super::Binder`] except that it tracks early bound generics, i.e. `struct Foo(T)` +/// needs `T` instantiated immediately. This type primarily exists to avoid forgetting to call +/// `instantiate`. +/// +/// If you don't have anything to `instantiate`, you may be looking for +/// [`instantiate_identity`](EarlyBinder::instantiate_identity) or [`skip_binder`](EarlyBinder::skip_binder). +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "T: Clone"), + Copy(bound = "T: Copy"), + PartialEq(bound = "T: PartialEq"), + Eq(bound = "T: Eq"), + Ord(bound = "T: Ord"), + PartialOrd(bound = "T: Ord"), + Hash(bound = "T: Hash"), + Debug(bound = "T: Debug") +)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +pub struct EarlyBinder { + value: T, + #[derivative(Debug = "ignore")] + _tcx: PhantomData, +} + +/// For early binders, you should first call `instantiate` before using any visitors. +#[cfg(feature = "nightly")] +impl !TypeFoldable for ty::EarlyBinder {} + +/// For early binders, you should first call `instantiate` before using any visitors. +#[cfg(feature = "nightly")] +impl !TypeVisitable for ty::EarlyBinder {} + +impl EarlyBinder { + pub fn bind(value: T) -> EarlyBinder { + EarlyBinder { value, _tcx: PhantomData } + } + + pub fn as_ref(&self) -> EarlyBinder { + EarlyBinder { value: &self.value, _tcx: PhantomData } + } + + pub fn map_bound_ref(&self, f: F) -> EarlyBinder + where + F: FnOnce(&T) -> U, + { + self.as_ref().map_bound(f) + } + + pub fn map_bound(self, f: F) -> EarlyBinder + where + F: FnOnce(T) -> U, + { + let value = f(self.value); + EarlyBinder { value, _tcx: PhantomData } + } + + pub fn try_map_bound(self, f: F) -> Result, E> + where + F: FnOnce(T) -> Result, + { + let value = f(self.value)?; + Ok(EarlyBinder { value, _tcx: PhantomData }) + } + + pub fn rebind(&self, value: U) -> EarlyBinder { + EarlyBinder { value, _tcx: PhantomData } + } + + /// Skips the binder and returns the "bound" value. + /// This can be used to extract data that does not depend on generic parameters + /// (e.g., getting the `DefId` of the inner value or getting the number of + /// arguments of an `FnSig`). Otherwise, consider using + /// [`instantiate_identity`](EarlyBinder::instantiate_identity). + /// + /// To skip the binder on `x: &EarlyBinder` to obtain `&T`, leverage + /// [`EarlyBinder::as_ref`](EarlyBinder::as_ref): `x.as_ref().skip_binder()`. + /// + /// See also [`Binder::skip_binder`](super::Binder::skip_binder), which is + /// the analogous operation on [`super::Binder`]. + pub fn skip_binder(self) -> T { + self.value + } +} + +impl EarlyBinder> { + pub fn transpose(self) -> Option> { + self.value.map(|value| EarlyBinder { value, _tcx: PhantomData }) + } +} + +impl<'s, I: Interner, Iter: IntoIterator> EarlyBinder +where + Iter::Item: TypeFoldable, +{ + pub fn iter_instantiated( + self, + tcx: I, + args: &'s [I::GenericArg], + ) -> IterInstantiated<'s, I, Iter> { + IterInstantiated { it: self.value.into_iter(), tcx, args } + } + + /// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity), + /// but on an iterator of `TypeFoldable` values. + pub fn instantiate_identity_iter(self) -> Iter::IntoIter { + self.value.into_iter() + } +} + +pub struct IterInstantiated<'s, I: Interner, Iter: IntoIterator> { + it: Iter::IntoIter, + tcx: I, + args: &'s [I::GenericArg], +} + +impl Iterator for IterInstantiated<'_, I, Iter> +where + Iter::Item: TypeFoldable, +{ + type Item = Iter::Item; + + fn next(&mut self) -> Option { + Some( + EarlyBinder { value: self.it.next()?, _tcx: PhantomData } + .instantiate(self.tcx, self.args), + ) + } + + fn size_hint(&self) -> (usize, Option) { + self.it.size_hint() + } +} + +impl DoubleEndedIterator for IterInstantiated<'_, I, Iter> +where + Iter::IntoIter: DoubleEndedIterator, + Iter::Item: TypeFoldable, +{ + fn next_back(&mut self) -> Option { + Some( + EarlyBinder { value: self.it.next_back()?, _tcx: PhantomData } + .instantiate(self.tcx, self.args), + ) + } +} + +impl ExactSizeIterator for IterInstantiated<'_, I, Iter> +where + Iter::IntoIter: ExactSizeIterator, + Iter::Item: TypeFoldable, +{ +} + +impl<'s, I: Interner, Iter: IntoIterator> EarlyBinder +where + Iter::Item: Deref, + ::Target: Copy + TypeFoldable, +{ + pub fn iter_instantiated_copied( + self, + tcx: I, + args: &'s [I::GenericArg], + ) -> IterInstantiatedCopied<'s, I, Iter> { + IterInstantiatedCopied { it: self.value.into_iter(), tcx, args } + } + + /// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity), + /// but on an iterator of values that deref to a `TypeFoldable`. + pub fn instantiate_identity_iter_copied( + self, + ) -> impl Iterator::Target> { + self.value.into_iter().map(|v| *v) + } +} + +pub struct IterInstantiatedCopied<'a, I: Interner, Iter: IntoIterator> { + it: Iter::IntoIter, + tcx: I, + args: &'a [I::GenericArg], +} + +impl Iterator for IterInstantiatedCopied<'_, I, Iter> +where + Iter::Item: Deref, + ::Target: Copy + TypeFoldable, +{ + type Item = ::Target; + + fn next(&mut self) -> Option { + self.it.next().map(|value| { + EarlyBinder { value: *value, _tcx: PhantomData }.instantiate(self.tcx, self.args) + }) + } + + fn size_hint(&self) -> (usize, Option) { + self.it.size_hint() + } +} + +impl DoubleEndedIterator for IterInstantiatedCopied<'_, I, Iter> +where + Iter::IntoIter: DoubleEndedIterator, + Iter::Item: Deref, + ::Target: Copy + TypeFoldable, +{ + fn next_back(&mut self) -> Option { + self.it.next_back().map(|value| { + EarlyBinder { value: *value, _tcx: PhantomData }.instantiate(self.tcx, self.args) + }) + } +} + +impl ExactSizeIterator for IterInstantiatedCopied<'_, I, Iter> +where + Iter::IntoIter: ExactSizeIterator, + Iter::Item: Deref, + ::Target: Copy + TypeFoldable, +{ +} + +pub struct EarlyBinderIter { + t: T, + _tcx: PhantomData, +} + +impl EarlyBinder { + pub fn transpose_iter(self) -> EarlyBinderIter { + EarlyBinderIter { t: self.value.into_iter(), _tcx: PhantomData } + } +} + +impl Iterator for EarlyBinderIter { + type Item = EarlyBinder; + + fn next(&mut self) -> Option { + self.t.next().map(|value| EarlyBinder { value, _tcx: PhantomData }) + } + + fn size_hint(&self) -> (usize, Option) { + self.t.size_hint() + } +} + +impl> ty::EarlyBinder { + pub fn instantiate(self, tcx: I, args: &[I::GenericArg]) -> T { + let mut folder = ArgFolder { tcx, args, binders_passed: 0 }; + self.value.fold_with(&mut folder) + } + + /// Makes the identity replacement `T0 => T0, ..., TN => TN`. + /// Conceptually, this converts universally bound variables into placeholders + /// when inside of a given item. + /// + /// For example, consider `for fn foo(){ .. }`: + /// - Outside of `foo`, `T` is bound (represented by the presence of `EarlyBinder`). + /// - Inside of the body of `foo`, we treat `T` as a placeholder by calling + /// `instantiate_identity` to discharge the `EarlyBinder`. + pub fn instantiate_identity(self) -> T { + self.value + } + + /// Returns the inner value, but only if it contains no bound vars. + pub fn no_bound_vars(self) -> Option { + if !self.value.has_param() { Some(self.value) } else { None } + } +} + +/////////////////////////////////////////////////////////////////////////// +// The actual instantiation engine itself is a type folder. + +struct ArgFolder<'a, I: Interner> { + tcx: I, + args: &'a [I::GenericArg], + + /// Number of region binders we have passed through while doing the instantiation + binders_passed: u32, +} + +impl<'a, I: Interner> TypeFolder for ArgFolder<'a, I> { + #[inline] + fn interner(&self) -> I { + self.tcx + } + + fn fold_binder>(&mut self, t: ty::Binder) -> ty::Binder { + self.binders_passed += 1; + let t = t.super_fold_with(self); + self.binders_passed -= 1; + t + } + + fn fold_region(&mut self, r: I::Region) -> I::Region { + // Note: This routine only handles regions that are bound on + // type declarations and other outer declarations, not those + // bound in *fn types*. Region instantiation of the bound + // regions that appear in a function signature is done using + // the specialized routine `ty::replace_late_regions()`. + match r.kind() { + ty::ReEarlyParam(data) => { + let rk = self.args.get(data.index() as usize).map(|k| k.kind()); + match rk { + Some(ty::GenericArgKind::Lifetime(lt)) => self.shift_region_through_binders(lt), + Some(other) => self.region_param_expected(data, r, other), + None => self.region_param_out_of_range(data, r), + } + } + ty::ReBound(..) + | ty::ReLateParam(_) + | ty::ReStatic + | ty::RePlaceholder(_) + | ty::ReErased + | ty::ReError(_) => r, + ty::ReVar(_) => panic!("unexpected region: {r:?}"), + } + } + + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { + if !t.has_param() { + return t; + } + + match t.kind() { + ty::Param(p) => self.ty_for_param(p, t), + _ => t.super_fold_with(self), + } + } + + fn fold_const(&mut self, c: I::Const) -> I::Const { + if let ty::ConstKind::Param(p) = c.kind() { + self.const_for_param(p, c) + } else { + c.super_fold_with(self) + } + } +} + +impl<'a, I: Interner> ArgFolder<'a, I> { + fn ty_for_param(&self, p: I::ParamTy, source_ty: I::Ty) -> I::Ty { + // Look up the type in the args. It really should be in there. + let opt_ty = self.args.get(p.index() as usize).map(|k| k.kind()); + let ty = match opt_ty { + Some(ty::GenericArgKind::Type(ty)) => ty, + Some(kind) => self.type_param_expected(p, source_ty, kind), + None => self.type_param_out_of_range(p, source_ty), + }; + + self.shift_vars_through_binders(ty) + } + + #[cold] + #[inline(never)] + fn type_param_expected(&self, p: I::ParamTy, ty: I::Ty, kind: ty::GenericArgKind) -> ! { + panic!( + "expected type for `{:?}` ({:?}/{}) but found {:?} when instantiating, args={:?}", + p, + ty, + p.index(), + kind, + self.args, + ) + } + + #[cold] + #[inline(never)] + fn type_param_out_of_range(&self, p: I::ParamTy, ty: I::Ty) -> ! { + panic!( + "type parameter `{:?}` ({:?}/{}) out of range when instantiating, args={:?}", + p, + ty, + p.index(), + self.args, + ) + } + + fn const_for_param(&self, p: I::ParamConst, source_ct: I::Const) -> I::Const { + // Look up the const in the args. It really should be in there. + let opt_ct = self.args.get(p.index() as usize).map(|k| k.kind()); + let ct = match opt_ct { + Some(ty::GenericArgKind::Const(ct)) => ct, + Some(kind) => self.const_param_expected(p, source_ct, kind), + None => self.const_param_out_of_range(p, source_ct), + }; + + self.shift_vars_through_binders(ct) + } + + #[cold] + #[inline(never)] + fn const_param_expected( + &self, + p: I::ParamConst, + ct: I::Const, + kind: ty::GenericArgKind, + ) -> ! { + panic!( + "expected const for `{:?}` ({:?}/{}) but found {:?} when instantiating args={:?}", + p, + ct, + p.index(), + kind, + self.args, + ) + } + + #[cold] + #[inline(never)] + fn const_param_out_of_range(&self, p: I::ParamConst, ct: I::Const) -> ! { + panic!( + "const parameter `{:?}` ({:?}/{}) out of range when instantiating args={:?}", + p, + ct, + p.index(), + self.args, + ) + } + + #[cold] + #[inline(never)] + fn region_param_expected( + &self, + ebr: I::EarlyParamRegion, + r: I::Region, + kind: ty::GenericArgKind, + ) -> ! { + panic!( + "expected region for `{:?}` ({:?}/{}) but found {:?} when instantiating args={:?}", + ebr, + r, + ebr.index(), + kind, + self.args, + ) + } + + #[cold] + #[inline(never)] + fn region_param_out_of_range(&self, ebr: I::EarlyParamRegion, r: I::Region) -> ! { + panic!( + "const parameter `{:?}` ({:?}/{}) out of range when instantiating args={:?}", + ebr, + r, + ebr.index(), + self.args, + ) + } + + /// It is sometimes necessary to adjust the De Bruijn indices during instantiation. This occurs + /// when we are instantating a type with escaping bound vars into a context where we have + /// passed through binders. That's quite a mouthful. Let's see an example: + /// + /// ``` + /// type Func = fn(A); + /// type MetaFunc = for<'a> fn(Func<&'a i32>); + /// ``` + /// + /// The type `MetaFunc`, when fully expanded, will be + /// ```ignore (illustrative) + /// for<'a> fn(fn(&'a i32)) + /// // ^~ ^~ ^~~ + /// // | | | + /// // | | DebruijnIndex of 2 + /// // Binders + /// ``` + /// Here the `'a` lifetime is bound in the outer function, but appears as an argument of the + /// inner one. Therefore, that appearance will have a DebruijnIndex of 2, because we must skip + /// over the inner binder (remember that we count De Bruijn indices from 1). However, in the + /// definition of `MetaFunc`, the binder is not visible, so the type `&'a i32` will have a + /// De Bruijn index of 1. It's only during the instantiation that we can see we must increase the + /// depth by 1 to account for the binder that we passed through. + /// + /// As a second example, consider this twist: + /// + /// ``` + /// type FuncTuple = (A,fn(A)); + /// type MetaFuncTuple = for<'a> fn(FuncTuple<&'a i32>); + /// ``` + /// + /// Here the final type will be: + /// ```ignore (illustrative) + /// for<'a> fn((&'a i32, fn(&'a i32))) + /// // ^~~ ^~~ + /// // | | + /// // DebruijnIndex of 1 | + /// // DebruijnIndex of 2 + /// ``` + /// As indicated in the diagram, here the same type `&'a i32` is instantiated once, but in the + /// first case we do not increase the De Bruijn index and in the second case we do. The reason + /// is that only in the second case have we passed through a fn binder. + fn shift_vars_through_binders>(&self, val: T) -> T { + debug!( + "shift_vars(val={:?}, binders_passed={:?}, has_escaping_bound_vars={:?})", + val, + self.binders_passed, + val.has_escaping_bound_vars() + ); + + if self.binders_passed == 0 || !val.has_escaping_bound_vars() { + return val; + } + + let result = ty::fold::shift_vars(TypeFolder::interner(self), val, self.binders_passed); + debug!("shift_vars: shifted result = {:?}", result); + + result + } + + fn shift_region_through_binders(&self, region: I::Region) -> I::Region { + if self.binders_passed == 0 || !region.has_escaping_bound_vars() { + return region; + } + ty::fold::shift_region(self.tcx, region, self.binders_passed) + } +} diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index 1c30f03c6939..61ae36265ec7 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -115,8 +115,8 @@ impl CanonicalVarInfo { CanonicalVarKind::PlaceholderTy(_) => false, CanonicalVarKind::Region(_) => true, CanonicalVarKind::PlaceholderRegion(..) => false, - CanonicalVarKind::Const(..) => true, - CanonicalVarKind::PlaceholderConst(_, _) => false, + CanonicalVarKind::Const(_) => true, + CanonicalVarKind::PlaceholderConst(_) => false, CanonicalVarKind::Effect => true, } } @@ -126,8 +126,8 @@ impl CanonicalVarInfo { CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true, CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) - | CanonicalVarKind::Const(_, _) - | CanonicalVarKind::PlaceholderConst(_, _) + | CanonicalVarKind::Const(_) + | CanonicalVarKind::PlaceholderConst(_) | CanonicalVarKind::Effect => false, } } @@ -136,12 +136,12 @@ impl CanonicalVarInfo { match self.kind { CanonicalVarKind::Ty(_) | CanonicalVarKind::Region(_) - | CanonicalVarKind::Const(_, _) + | CanonicalVarKind::Const(_) | CanonicalVarKind::Effect => panic!("expected placeholder: {self:?}"), CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.var().as_usize(), CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.var().as_usize(), - CanonicalVarKind::PlaceholderConst(placeholder, _) => placeholder.var().as_usize(), + CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.var().as_usize(), } } } @@ -169,13 +169,13 @@ pub enum CanonicalVarKind { PlaceholderRegion(I::PlaceholderRegion), /// Some kind of const inference variable. - Const(UniverseIndex, I::Ty), + Const(UniverseIndex), /// Effect variable `'?E`. Effect, /// A "placeholder" that represents "any const". - PlaceholderConst(I::PlaceholderConst, I::Ty), + PlaceholderConst(I::PlaceholderConst), } impl PartialEq for CanonicalVarKind { @@ -185,10 +185,8 @@ impl PartialEq for CanonicalVarKind { (Self::PlaceholderTy(l0), Self::PlaceholderTy(r0)) => l0 == r0, (Self::Region(l0), Self::Region(r0)) => l0 == r0, (Self::PlaceholderRegion(l0), Self::PlaceholderRegion(r0)) => l0 == r0, - (Self::Const(l0, l1), Self::Const(r0, r1)) => l0 == r0 && l1 == r1, - (Self::PlaceholderConst(l0, l1), Self::PlaceholderConst(r0, r1)) => { - l0 == r0 && l1 == r1 - } + (Self::Const(l0), Self::Const(r0)) => l0 == r0, + (Self::PlaceholderConst(l0), Self::PlaceholderConst(r0)) => l0 == r0, _ => std::mem::discriminant(self) == std::mem::discriminant(other), } } @@ -199,10 +197,10 @@ impl CanonicalVarKind { match self { CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) => ui, CanonicalVarKind::Region(ui) => ui, - CanonicalVarKind::Const(ui, _) => ui, + CanonicalVarKind::Const(ui) => ui, CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe(), CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe(), - CanonicalVarKind::PlaceholderConst(placeholder, _) => placeholder.universe(), + CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.universe(), CanonicalVarKind::Ty(CanonicalTyVarKind::Float | CanonicalTyVarKind::Int) => { UniverseIndex::ROOT } @@ -220,7 +218,7 @@ impl CanonicalVarKind { CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) } CanonicalVarKind::Region(_) => CanonicalVarKind::Region(ui), - CanonicalVarKind::Const(_, ty) => CanonicalVarKind::Const(ui, ty), + CanonicalVarKind::Const(_) => CanonicalVarKind::Const(ui), CanonicalVarKind::PlaceholderTy(placeholder) => { CanonicalVarKind::PlaceholderTy(placeholder.with_updated_universe(ui)) @@ -228,8 +226,8 @@ impl CanonicalVarKind { CanonicalVarKind::PlaceholderRegion(placeholder) => { CanonicalVarKind::PlaceholderRegion(placeholder.with_updated_universe(ui)) } - CanonicalVarKind::PlaceholderConst(placeholder, ty) => { - CanonicalVarKind::PlaceholderConst(placeholder.with_updated_universe(ui), ty) + CanonicalVarKind::PlaceholderConst(placeholder) => { + CanonicalVarKind::PlaceholderConst(placeholder.with_updated_universe(ui)) } CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) | CanonicalVarKind::Effect => { @@ -345,21 +343,14 @@ impl CanonicalVarValues { Region::new_anon_bound(tcx, ty::INNERMOST, ty::BoundVar::from_usize(i)) .into() } - CanonicalVarKind::Effect => Const::new_anon_bound( - tcx, - ty::INNERMOST, - ty::BoundVar::from_usize(i), - Ty::new_bool(tcx), - ) - .into(), - CanonicalVarKind::Const(_, ty) - | CanonicalVarKind::PlaceholderConst(_, ty) => Const::new_anon_bound( - tcx, - ty::INNERMOST, - ty::BoundVar::from_usize(i), - ty, - ) - .into(), + CanonicalVarKind::Effect => { + Const::new_anon_bound(tcx, ty::INNERMOST, ty::BoundVar::from_usize(i)) + .into() + } + CanonicalVarKind::Const(_) | CanonicalVarKind::PlaceholderConst(_) => { + Const::new_anon_bound(tcx, ty::INNERMOST, ty::BoundVar::from_usize(i)) + .into() + } } }, )), diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index af07e9ff96bf..f1683f5449f5 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -5,7 +5,7 @@ use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use std::fmt; -use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; +use crate::{self as ty, DebruijnIndex, Interner}; use self::ConstKind::*; @@ -29,10 +29,10 @@ pub enum ConstKind { /// An unnormalized const item such as an anon const or assoc const or free const item. /// Right now anything other than anon consts does not actually work properly but this /// should - Unevaluated(I::AliasConst), + Unevaluated(ty::UnevaluatedConst), /// Used to hold computed value. - Value(I::ValueConst), + Value(I::Ty, I::ValueConst), /// A placeholder for a const which could not be computed; this is /// propagated to avoid useless error messages. @@ -51,7 +51,7 @@ impl PartialEq for ConstKind { (Bound(l0, l1), Bound(r0, r1)) => l0 == r0 && l1 == r1, (Placeholder(l0), Placeholder(r0)) => l0 == r0, (Unevaluated(l0), Unevaluated(r0)) => l0 == r0, - (Value(l0), Value(r0)) => l0 == r0, + (Value(l0, l1), Value(r0, r1)) => l0 == r0 && l1 == r1, (Error(l0), Error(r0)) => l0 == r0, (Expr(l0), Expr(r0)) => l0 == r0, _ => false, @@ -61,28 +61,19 @@ impl PartialEq for ConstKind { impl fmt::Debug for ConstKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} - -impl DebugWithInfcx for ConstKind { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { use ConstKind::*; - match this.data { + match self { Param(param) => write!(f, "{param:?}"), - Infer(var) => write!(f, "{:?}", &this.wrap(var)), + Infer(var) => write!(f, "{:?}", &var), Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var), Placeholder(placeholder) => write!(f, "{placeholder:?}"), Unevaluated(uv) => { - write!(f, "{:?}", &this.wrap(uv)) + write!(f, "{:?}", &uv) } - Value(valtree) => write!(f, "{valtree:?}"), + Value(ty, valtree) => write!(f, "({valtree:?}: {:?})", &ty), Error(_) => write!(f, "{{const error}}"), - Expr(expr) => write!(f, "{:?}", &this.wrap(expr)), + Expr(expr) => write!(f, "{:?}", &expr), } } } @@ -112,17 +103,9 @@ impl UnevaluatedConst { impl fmt::Debug for UnevaluatedConst { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} -impl DebugWithInfcx for UnevaluatedConst { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { f.debug_struct("UnevaluatedConst") - .field("def", &this.data.def) - .field("args", &this.wrap(this.data.args)) + .field("def", &self.def) + .field("args", &self.args) .finish() } } @@ -175,23 +158,6 @@ impl fmt::Debug for InferConst { } } } -impl DebugWithInfcx for InferConst { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - match *this.data { - InferConst::Var(vid) => match this.infcx.universe_of_ct(vid) { - None => write!(f, "{:?}", this.data), - Some(universe) => write!(f, "?{}_{}c", vid.index(), universe.index()), - }, - InferConst::EffectVar(vid) => write!(f, "?{}e", vid.index()), - InferConst::Fresh(_) => { - unreachable!() - } - } - } -} #[cfg(feature = "nightly")] impl HashStable for InferConst { diff --git a/compiler/rustc_type_ir/src/debug.rs b/compiler/rustc_type_ir/src/debug.rs deleted file mode 100644 index 9298360f749a..000000000000 --- a/compiler/rustc_type_ir/src/debug.rs +++ /dev/null @@ -1,140 +0,0 @@ -use crate::{ConstVid, InferCtxtLike, Interner, TyVid, UniverseIndex}; - -use core::fmt; -use std::marker::PhantomData; - -pub struct NoInfcx(PhantomData); - -impl InferCtxtLike for NoInfcx { - type Interner = I; - - fn interner(&self) -> Self::Interner { - unreachable!() - } - - fn universe_of_ty(&self, _ty: TyVid) -> Option { - None - } - - fn universe_of_lt(&self, _lt: I::InferRegion) -> Option { - None - } - - fn universe_of_ct(&self, _ct: ConstVid) -> Option { - None - } - - fn root_ty_var(&self, vid: TyVid) -> TyVid { - vid - } - - fn probe_ty_var(&self, _vid: TyVid) -> Option { - None - } - - fn opportunistic_resolve_lt_var(&self, _vid: I::InferRegion) -> Option { - None - } - - fn root_ct_var(&self, vid: ConstVid) -> ConstVid { - vid - } - - fn probe_ct_var(&self, _vid: ConstVid) -> Option { - None - } - - fn defining_opaque_types(&self) -> ::DefiningOpaqueTypes { - Default::default() - } -} - -pub trait DebugWithInfcx: fmt::Debug { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result; -} - -impl + ?Sized> DebugWithInfcx for &'_ T { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - >::fmt(this.map(|&data| data), f) - } -} - -impl> DebugWithInfcx for [T] { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - match f.alternate() { - true => { - write!(f, "[\n")?; - for element in this.data.iter() { - write!(f, "{:?},\n", &this.wrap(element))?; - } - write!(f, "]") - } - false => { - write!(f, "[")?; - if this.data.len() > 0 { - for element in &this.data[..(this.data.len() - 1)] { - write!(f, "{:?}, ", &this.wrap(element))?; - } - if let Some(element) = this.data.last() { - write!(f, "{:?}", &this.wrap(element))?; - } - } - write!(f, "]") - } - } - } -} - -pub struct WithInfcx<'a, Infcx: InferCtxtLike, T> { - pub data: T, - pub infcx: &'a Infcx, -} - -impl Copy for WithInfcx<'_, Infcx, T> {} - -impl Clone for WithInfcx<'_, Infcx, T> { - fn clone(&self) -> Self { - Self { data: self.data.clone(), infcx: self.infcx } - } -} - -impl<'a, I: Interner, T> WithInfcx<'a, NoInfcx, T> { - pub fn with_no_infcx(data: T) -> Self { - Self { data, infcx: &NoInfcx(PhantomData) } - } -} - -impl<'a, Infcx: InferCtxtLike, T> WithInfcx<'a, Infcx, T> { - pub fn new(data: T, infcx: &'a Infcx) -> Self { - Self { data, infcx } - } - - pub fn wrap(self, u: U) -> WithInfcx<'a, Infcx, U> { - WithInfcx { data: u, infcx: self.infcx } - } - - pub fn map(self, f: impl FnOnce(T) -> U) -> WithInfcx<'a, Infcx, U> { - WithInfcx { data: f(self.data), infcx: self.infcx } - } - - pub fn as_ref(&self) -> WithInfcx<'a, Infcx, &T> { - WithInfcx { data: &self.data, infcx: self.infcx } - } -} - -impl> fmt::Debug - for WithInfcx<'_, Infcx, T> -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - DebugWithInfcx::fmt(self.as_ref(), f) - } -} diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs new file mode 100644 index 000000000000..27623ea9cacb --- /dev/null +++ b/compiler/rustc_type_ir/src/error.rs @@ -0,0 +1,106 @@ +use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; + +use crate::solve::NoSolution; +use crate::{self as ty, Interner}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(TypeFoldable_Generic, TypeVisitable_Generic)] +pub struct ExpectedFound { + pub expected: T, + pub found: T, +} + +impl ExpectedFound { + pub fn new(a_is_expected: bool, a: T, b: T) -> Self { + if a_is_expected { + ExpectedFound { expected: a, found: b } + } else { + ExpectedFound { expected: b, found: a } + } + } +} + +// Data structures used in type unification +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Copy(bound = ""), + PartialEq(bound = ""), + Eq(bound = ""), + Debug(bound = "") +)] +#[derive(TypeVisitable_Generic)] +#[rustc_pass_by_value] +pub enum TypeError { + Mismatch, + ConstnessMismatch(ExpectedFound), + PolarityMismatch(ExpectedFound), + SafetyMismatch(ExpectedFound), + AbiMismatch(ExpectedFound), + Mutability, + ArgumentMutability(usize), + TupleSize(ExpectedFound), + FixedArraySize(ExpectedFound), + ArgCount, + + RegionsDoesNotOutlive(I::Region, I::Region), + RegionsInsufficientlyPolymorphic(I::BoundRegion, I::Region), + RegionsPlaceholderMismatch, + + Sorts(ExpectedFound), + ArgumentSorts(ExpectedFound, usize), + Traits(ExpectedFound), + VariadicMismatch(ExpectedFound), + + /// Instantiating a type variable with the given type would have + /// created a cycle (because it appears somewhere within that + /// type). + CyclicTy(I::Ty), + CyclicConst(I::Const), + ProjectionMismatched(ExpectedFound), + ExistentialMismatch(ExpectedFound), + ConstMismatch(ExpectedFound), + + IntrinsicCast, + /// Safe `#[target_feature]` functions are not assignable to safe function pointers. + TargetFeatureCast(I::DefId), +} + +impl TypeError { + pub fn involves_regions(self) -> bool { + match self { + TypeError::RegionsDoesNotOutlive(_, _) + | TypeError::RegionsInsufficientlyPolymorphic(_, _) + | TypeError::RegionsPlaceholderMismatch => true, + _ => false, + } + } + + pub fn must_include_note(self) -> bool { + use self::TypeError::*; + match self { + CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | ConstnessMismatch(_) + | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_) + | ArgumentSorts(..) | Sorts(_) | VariadicMismatch(_) | TargetFeatureCast(_) => false, + + Mutability + | ArgumentMutability(_) + | TupleSize(_) + | ArgCount + | RegionsDoesNotOutlive(..) + | RegionsInsufficientlyPolymorphic(..) + | RegionsPlaceholderMismatch + | Traits(_) + | ProjectionMismatched(_) + | ExistentialMismatch(_) + | ConstMismatch(_) + | IntrinsicCast => true, + } + } +} + +impl From> for NoSolution { + fn from(_: TypeError) -> NoSolution { + NoSolution + } +} diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 01bb3d73dbdf..ee3e5ce66d03 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -47,9 +47,11 @@ use rustc_index::{Idx, IndexVec}; use std::mem; +use tracing::debug; -use crate::Lrc; -use crate::{visit::TypeVisitable, Interner}; +use crate::inherent::*; +use crate::visit::{TypeVisitable, TypeVisitableExt as _}; +use crate::{self as ty, Interner, Lrc}; #[cfg(feature = "nightly")] type Never = !; @@ -128,10 +130,9 @@ pub trait TypeSuperFoldable: TypeFoldable { pub trait TypeFolder: FallibleTypeFolder { fn interner(&self) -> I; - fn fold_binder(&mut self, t: I::Binder) -> I::Binder + fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder where T: TypeFoldable, - I::Binder: TypeSuperFoldable, { t.super_fold_with(self) } @@ -167,10 +168,9 @@ pub trait FallibleTypeFolder: Sized { fn interner(&self) -> I; - fn try_fold_binder(&mut self, t: I::Binder) -> Result, Self::Error> + fn try_fold_binder(&mut self, t: ty::Binder) -> Result, Self::Error> where T: TypeFoldable, - I::Binder: TypeSuperFoldable, { t.try_super_fold_with(self) } @@ -206,10 +206,9 @@ where TypeFolder::interner(self) } - fn try_fold_binder(&mut self, t: I::Binder) -> Result, Never> + fn try_fold_binder(&mut self, t: ty::Binder) -> Result, Never> where T: TypeFoldable, - I::Binder: TypeSuperFoldable, { Ok(self.fold_binder(t)) } @@ -328,3 +327,95 @@ impl, Ix: Idx> TypeFoldable for IndexVec { + tcx: I, + current_index: ty::DebruijnIndex, + amount: u32, +} + +impl Shifter { + pub fn new(tcx: I, amount: u32) -> Self { + Shifter { tcx, current_index: ty::INNERMOST, amount } + } +} + +impl TypeFolder for Shifter { + fn interner(&self) -> I { + self.tcx + } + + fn fold_binder>(&mut self, t: ty::Binder) -> ty::Binder { + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_region(&mut self, r: I::Region) -> I::Region { + match r.kind() { + ty::ReBound(debruijn, br) if debruijn >= self.current_index => { + let debruijn = debruijn.shifted_in(self.amount); + Region::new_bound(self.tcx, debruijn, br) + } + _ => r, + } + } + + fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + match ty.kind() { + ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { + let debruijn = debruijn.shifted_in(self.amount); + Ty::new_bound(self.tcx, debruijn, bound_ty) + } + + _ if ty.has_vars_bound_at_or_above(self.current_index) => ty.super_fold_with(self), + _ => ty, + } + } + + fn fold_const(&mut self, ct: I::Const) -> I::Const { + match ct.kind() { + ty::ConstKind::Bound(debruijn, bound_ct) if debruijn >= self.current_index => { + let debruijn = debruijn.shifted_in(self.amount); + Const::new_bound(self.tcx, debruijn, bound_ct) + } + _ => ct.super_fold_with(self), + } + } + + fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { + if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } + } +} + +pub fn shift_region(tcx: I, region: I::Region, amount: u32) -> I::Region { + match region.kind() { + ty::ReBound(debruijn, br) if amount > 0 => { + Region::new_bound(tcx, debruijn.shifted_in(amount), br) + } + _ => region, + } +} + +pub fn shift_vars(tcx: I, value: T, amount: u32) -> T +where + T: TypeFoldable, +{ + debug!("shift_vars(value={:?}, amount={})", value, amount); + + if amount == 0 || !value.has_escaping_bound_vars() { + return value; + } + + value.fold_with(&mut Shifter::new(tcx, amount)) +} diff --git a/compiler/rustc_type_ir/src/generic_arg.rs b/compiler/rustc_type_ir/src/generic_arg.rs index 622a40806083..cc8c44446579 100644 --- a/compiler/rustc_type_ir/src/generic_arg.rs +++ b/compiler/rustc_type_ir/src/generic_arg.rs @@ -16,3 +16,17 @@ pub enum GenericArgKind { Type(I::Ty), Const(I::Const), } + +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Copy(bound = ""), + Debug(bound = ""), + Eq(bound = ""), + PartialEq(bound = "") +)] +#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +pub enum TermKind { + Ty(I::Ty), + Const(I::Const), +} diff --git a/compiler/rustc_type_ir/src/infcx.rs b/compiler/rustc_type_ir/src/infcx.rs deleted file mode 100644 index a53287c19878..000000000000 --- a/compiler/rustc_type_ir/src/infcx.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::{ConstVid, Interner, TyVid, UniverseIndex}; - -pub trait InferCtxtLike { - type Interner: Interner; - - fn interner(&self) -> Self::Interner; - - fn universe_of_ty(&self, ty: TyVid) -> Option; - - /// Resolve `TyVid` to its root `TyVid`. - fn root_ty_var(&self, vid: TyVid) -> TyVid; - - /// Resolve `TyVid` to its inferred type, if it has been equated with a non-infer type. - fn probe_ty_var(&self, vid: TyVid) -> Option<::Ty>; - - fn universe_of_lt( - &self, - lt: ::InferRegion, - ) -> Option; - - /// Resolve `InferRegion` to its inferred region, if it has been equated with - /// a non-infer region. - /// - /// FIXME: This has slightly different semantics than `{probe,resolve}_{ty,ct}_var`, - /// that has to do with the fact unlike `Ty` or `Const` vars, in rustc, we may - /// not always be able to *name* the root region var from the universe of the - /// var we're trying to resolve. That's why it's called *opportunistic*. - fn opportunistic_resolve_lt_var( - &self, - vid: ::InferRegion, - ) -> Option<::Region>; - - fn universe_of_ct(&self, ct: ConstVid) -> Option; - - /// Resolve `ConstVid` to its root `ConstVid`. - fn root_ct_var(&self, vid: ConstVid) -> ConstVid; - - /// Resolve `ConstVid` to its inferred type, if it has been equated with a non-infer type. - fn probe_ct_var(&self, vid: ConstVid) -> Option<::Const>; - - fn defining_opaque_types(&self) -> ::DefiningOpaqueTypes; -} diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 5289dfd932f9..64d3400976a8 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -7,105 +7,470 @@ use std::fmt::Debug; use std::hash::Hash; use std::ops::Deref; -use crate::fold::TypeSuperFoldable; -use crate::visit::{Flags, TypeSuperVisitable}; -use crate::{ - AliasTy, AliasTyKind, BoundVar, ConstKind, DebruijnIndex, DebugWithInfcx, Interner, RegionKind, - TyKind, UnevaluatedConst, UniverseIndex, -}; +use rustc_ast_ir::Mutability; +use rustc_data_structures::fx::FxHashSet; + +use crate::fold::{TypeFoldable, TypeSuperFoldable}; +use crate::relate::Relate; +use crate::solve::{CacheData, CanonicalInput, QueryResult, Reveal}; +use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; +use crate::{self as ty, CollectAndApply, Interner, UpcastFrom}; pub trait Ty>: Copy - + DebugWithInfcx + + Debug + Hash + Eq + Into + Into - + IntoKind> + + IntoKind> + TypeSuperVisitable + TypeSuperFoldable + + Relate + Flags { + fn new_unit(interner: I) -> Self; + fn new_bool(interner: I) -> Self; - fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar) -> Self; + fn new_u8(interner: I) -> Self; - fn new_alias(interner: I, kind: AliasTyKind, alias_ty: AliasTy) -> Self; + fn new_usize(interner: I) -> Self; + + fn new_infer(interner: I, var: ty::InferTy) -> Self; + + fn new_var(interner: I, var: ty::TyVid) -> Self; + + fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundTy) -> Self; + + fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; + + fn new_alias(interner: I, kind: ty::AliasTyKind, alias_ty: ty::AliasTy) -> Self; + + fn new_projection( + interner: I, + def_id: I::DefId, + args: impl IntoIterator>, + ) -> Self { + Ty::new_alias( + interner, + ty::AliasTyKind::Projection, + ty::AliasTy::new(interner, def_id, args), + ) + } + + fn new_error(interner: I, guar: I::ErrorGuaranteed) -> Self; + + fn new_adt(interner: I, adt_def: I::AdtDef, args: I::GenericArgs) -> Self; + + fn new_foreign(interner: I, def_id: I::DefId) -> Self; + + fn new_dynamic( + interner: I, + preds: I::BoundExistentialPredicates, + region: I::Region, + kind: ty::DynKind, + ) -> Self; + + fn new_coroutine(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + + fn new_coroutine_closure(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + + fn new_closure(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + + fn new_coroutine_witness(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + + fn new_ptr(interner: I, ty: Self, mutbl: Mutability) -> Self; + + fn new_ref(interner: I, region: I::Region, ty: Self, mutbl: Mutability) -> Self; + + fn new_array_with_const_len(interner: I, ty: Self, len: I::Const) -> Self; + + fn new_slice(interner: I, ty: Self) -> Self; + + fn new_tup(interner: I, tys: &[I::Ty]) -> Self; + + fn new_tup_from_iter(interner: I, iter: It) -> T::Output + where + It: Iterator, + T: CollectAndApply; + + fn new_fn_def(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + + fn new_fn_ptr(interner: I, sig: ty::Binder>) -> Self; + + fn new_pat(interner: I, ty: Self, pat: I::Pat) -> Self; + + fn tuple_fields(self) -> I::Tys; + + fn to_opt_closure_kind(self) -> Option; + + fn from_closure_kind(interner: I, kind: ty::ClosureKind) -> Self; + + fn from_coroutine_closure_kind(interner: I, kind: ty::ClosureKind) -> Self; + + fn is_ty_var(self) -> bool { + matches!(self.kind(), ty::Infer(ty::TyVar(_))) + } + + fn is_fn_ptr(self) -> bool { + matches!(self.kind(), ty::FnPtr(_)) + } + + fn fn_sig(self, interner: I) -> ty::Binder> { + match self.kind() { + ty::FnPtr(sig) => sig, + ty::FnDef(def_id, args) => interner.fn_sig(def_id).instantiate(interner, &args), + ty::Error(_) => { + // ignore errors (#54954) + ty::Binder::dummy(ty::FnSig { + inputs_and_output: Default::default(), + c_variadic: false, + safety: I::Safety::safe(), + abi: I::Abi::rust(), + }) + } + ty::Closure(..) => panic!( + "to get the signature of a closure, use `args.as_closure().sig()` not `fn_sig()`", + ), + _ => panic!("Ty::fn_sig() called on non-fn type: {:?}", self), + } + } + + fn discriminant_ty(self, interner: I) -> I::Ty; + + fn async_destructor_ty(self, interner: I) -> I::Ty; + + /// Returns `true` when the outermost type cannot be further normalized, + /// resolved, or instantiated. This includes all primitive types, but also + /// things like ADTs and trait objects, sice even if their arguments or + /// nested types may be further simplified, the outermost [`ty::TyKind`] or + /// type constructor remains the same. + fn is_known_rigid(self) -> bool { + match self.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Pat(_, _) + | ty::Slice(_) + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(_) => true, + + ty::Error(_) + | ty::Infer(_) + | ty::Alias(_, _) + | ty::Param(_) + | ty::Bound(_, _) + | ty::Placeholder(_) => false, + } + } } pub trait Tys>: - Copy + Debug + Hash + Eq + IntoIterator + Deref> + Copy + + Debug + + Hash + + Eq + + IntoIterator + + Deref> + + TypeFoldable + + Default { fn split_inputs_and_output(self) -> (I::FnInputTys, I::Ty); } -pub trait Abi>: Copy + Debug + Hash + Eq { +pub trait Abi>: Copy + Debug + Hash + Eq + Relate { + fn rust() -> Self; + /// Whether this ABI is `extern "Rust"`. fn is_rust(self) -> bool; } -pub trait Safety>: Copy + Debug + Hash + Eq { +pub trait Safety>: Copy + Debug + Hash + Eq + Relate { + fn safe() -> Self; + + fn is_safe(self) -> bool; + fn prefix_str(self) -> &'static str; } pub trait Region>: - Copy + DebugWithInfcx + Hash + Eq + Into + IntoKind> + Flags + Copy + + Debug + + Hash + + Eq + + Into + + IntoKind> + + Flags + + Relate { - fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar) -> Self; + fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundRegion) -> Self; + + fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; fn new_static(interner: I) -> Self; } pub trait Const>: Copy - + DebugWithInfcx + + Debug + Hash + Eq + Into + Into - + IntoKind> + + IntoKind> + TypeSuperVisitable + TypeSuperFoldable + + Relate + Flags { - fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar, ty: I::Ty) -> Self; + fn try_to_target_usize(self, interner: I) -> Option; - fn new_unevaluated(interner: I, uv: UnevaluatedConst, ty: I::Ty) -> Self; + fn new_infer(interner: I, var: ty::InferConst) -> Self; - fn ty(self) -> I::Ty; + fn new_var(interner: I, var: ty::ConstVid) -> Self; + + fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundConst) -> Self; + + fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; + + fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst) -> Self; + + fn new_expr(interner: I, expr: I::ExprConst) -> Self; + + fn new_error(interner: I, guar: I::ErrorGuaranteed) -> Self; + + fn new_error_with_message(interner: I, msg: impl ToString) -> Self { + Self::new_error(interner, interner.delay_bug(msg)) + } + + fn is_ct_var(self) -> bool { + matches!(self.kind(), ty::ConstKind::Infer(ty::InferConst::Var(_))) + } } pub trait GenericsOf> { fn count(&self) -> usize; } +pub trait GenericArg>: + Copy + + Debug + + Hash + + Eq + + IntoKind> + + TypeVisitable + + Relate + + From + + From + + From +{ + fn as_type(&self) -> Option { + if let ty::GenericArgKind::Type(ty) = self.kind() { Some(ty) } else { None } + } + + fn expect_ty(&self) -> I::Ty { + self.as_type().expect("expected a type") + } + + fn as_const(&self) -> Option { + if let ty::GenericArgKind::Const(c) = self.kind() { Some(c) } else { None } + } + + fn expect_const(&self) -> I::Const { + self.as_const().expect("expected a const") + } + + fn as_region(&self) -> Option { + if let ty::GenericArgKind::Lifetime(c) = self.kind() { Some(c) } else { None } + } + + fn expect_region(&self) -> I::Region { + self.as_region().expect("expected a const") + } + + fn is_non_region_infer(self) -> bool { + match self.kind() { + ty::GenericArgKind::Lifetime(_) => false, + ty::GenericArgKind::Type(ty) => ty.is_ty_var(), + ty::GenericArgKind::Const(ct) => ct.is_ct_var(), + } + } +} + +pub trait Term>: + Copy + Debug + Hash + Eq + IntoKind> + TypeFoldable + Relate +{ + fn as_type(&self) -> Option { + if let ty::TermKind::Ty(ty) = self.kind() { Some(ty) } else { None } + } + + fn expect_ty(&self) -> I::Ty { + self.as_type().expect("expected a type, but found a const") + } + + fn as_const(&self) -> Option { + if let ty::TermKind::Const(c) = self.kind() { Some(c) } else { None } + } + + fn expect_const(&self) -> I::Const { + self.as_const().expect("expected a const, but found a type") + } + + fn is_infer(self) -> bool { + match self.kind() { + ty::TermKind::Ty(ty) => ty.is_ty_var(), + ty::TermKind::Const(ct) => ct.is_ct_var(), + } + } + + fn to_alias_term(self) -> Option> { + match self.kind() { + ty::TermKind::Ty(ty) => match ty.kind() { + ty::Alias(_kind, alias_ty) => Some(alias_ty.into()), + _ => None, + }, + ty::TermKind::Const(ct) => match ct.kind() { + ty::ConstKind::Unevaluated(uv) => Some(uv.into()), + _ => None, + }, + } + } +} + pub trait GenericArgs>: Copy - + DebugWithInfcx + + Debug + Hash + Eq + IntoIterator + Deref> + Default + + Relate { + fn rebase_onto( + self, + interner: I, + source_def_id: I::DefId, + target: I::GenericArgs, + ) -> I::GenericArgs; + fn type_at(self, i: usize) -> I::Ty; + fn region_at(self, i: usize) -> I::Region; + + fn const_at(self, i: usize) -> I::Const; + fn identity_for_item(interner: I, def_id: I::DefId) -> I::GenericArgs; + + fn extend_with_error( + interner: I, + def_id: I::DefId, + original_args: &[I::GenericArg], + ) -> I::GenericArgs; + + fn split_closure_args(self) -> ty::ClosureArgsParts; + fn split_coroutine_closure_args(self) -> ty::CoroutineClosureArgsParts; + fn split_coroutine_args(self) -> ty::CoroutineArgsParts; + + fn as_closure(self) -> ty::ClosureArgs { + ty::ClosureArgs { args: self } + } + fn as_coroutine_closure(self) -> ty::CoroutineClosureArgs { + ty::CoroutineClosureArgs { args: self } + } + fn as_coroutine(self) -> ty::CoroutineArgs { + ty::CoroutineArgs { args: self } + } } pub trait Predicate>: - Copy + Debug + Hash + Eq + TypeSuperVisitable + TypeSuperFoldable + Flags + Copy + + Debug + + Hash + + Eq + + TypeSuperVisitable + + TypeSuperFoldable + + Flags + + UpcastFrom> + + UpcastFrom>> + + UpcastFrom> + + UpcastFrom>> + + UpcastFrom + + UpcastFrom> + + UpcastFrom> + + UpcastFrom>> + + UpcastFrom> + + UpcastFrom> + + UpcastFrom> + + IntoKind>> { + fn is_coinductive(self, interner: I) -> bool; + + // FIXME: Eventually uplift the impl out of rustc and make this defaulted. + fn allow_normalization(self) -> bool; +} + +pub trait Clause>: + Copy + + Debug + + Hash + + Eq + + TypeFoldable + // FIXME: Remove these, uplift the `Upcast` impls. + + UpcastFrom> + + UpcastFrom>> + + UpcastFrom> + + UpcastFrom>> + + IntoKind>> +{ + fn as_trait_clause(self) -> Option>> { + self.kind() + .map_bound(|clause| { + if let ty::ClauseKind::Trait(t) = clause { + Some(t) + } else { + None + } + }) + .transpose() + } + fn as_projection_clause(self) -> Option>> { + self.kind() + .map_bound(|clause| { + if let ty::ClauseKind::Projection(p) = clause { + Some(p) + } else { + None + } + }) + .transpose() + } } /// Common capabilities of placeholder kinds pub trait PlaceholderLike: Copy + Debug + Hash + Eq { - fn universe(self) -> UniverseIndex; - fn var(self) -> BoundVar; + fn universe(self) -> ty::UniverseIndex; + fn var(self) -> ty::BoundVar; - fn with_updated_universe(self, ui: UniverseIndex) -> Self; + fn with_updated_universe(self, ui: ty::UniverseIndex) -> Self; - fn new(ui: UniverseIndex, var: BoundVar) -> Self; + fn new(ui: ty::UniverseIndex, var: ty::BoundVar) -> Self; } pub trait IntoKind { @@ -114,12 +479,94 @@ pub trait IntoKind { fn kind(self) -> Self::Kind; } -pub trait BoundVars { - fn bound_vars(&self) -> I::BoundVars; - - fn has_no_bound_vars(&self) -> bool; -} - pub trait BoundVarLike { - fn var(self) -> BoundVar; + fn var(self) -> ty::BoundVar; + + fn assert_eq(self, var: I::BoundVarKind); +} + +pub trait ParamLike { + fn index(self) -> u32; +} + +pub trait AdtDef: Copy + Debug + Hash + Eq { + fn def_id(self) -> I::DefId; + + fn is_struct(self) -> bool; + + /// Returns the type of the struct tail. + /// + /// Expects the `AdtDef` to be a struct. If it is not, then this will panic. + fn struct_tail_ty(self, interner: I) -> Option>; + + fn is_phantom_data(self) -> bool; + + // FIXME: perhaps use `all_fields` and expose `FieldDef`. + fn all_field_tys(self, interner: I) -> ty::EarlyBinder>; + + fn sized_constraint(self, interner: I) -> Option>; +} + +pub trait ParamEnv: Copy + Debug + Hash + Eq + TypeFoldable { + fn reveal(self) -> Reveal; + + fn caller_bounds(self) -> impl IntoIterator; +} + +pub trait Features: Copy { + fn generic_const_exprs(self) -> bool; + + fn coroutine_clone(self) -> bool; + + fn associated_const_equality(self) -> bool; +} + +pub trait EvaluationCache { + /// Insert a final result into the global cache. + fn insert( + &self, + tcx: I, + key: CanonicalInput, + proof_tree: Option, + additional_depth: usize, + encountered_overflow: bool, + cycle_participants: FxHashSet>, + dep_node: I::DepNodeIndex, + result: QueryResult, + ); + + /// Try to fetch a cached result, checking the recursion limit + /// and handling root goals of coinductive cycles. + /// + /// If this returns `Some` the cache result can be used. + fn get( + &self, + tcx: I, + key: CanonicalInput, + stack_entries: impl IntoIterator>, + available_depth: usize, + ) -> Option>; +} + +pub trait DefId: Copy + Debug + Hash + Eq + TypeFoldable { + fn as_local(self) -> Option; +} + +pub trait BoundExistentialPredicates: + Copy + + Debug + + Hash + + Eq + + Relate + + IntoIterator>> +{ + fn principal_def_id(self) -> Option; + + fn principal(self) -> Option>>; + + fn auto_traits(self) -> impl IntoIterator; + + fn projection_bounds( + self, + ) -> impl IntoIterator>>; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 9acf7c04dd61..59ca95c09cd7 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -1,121 +1,173 @@ +use rustc_ast_ir::Movability; +use rustc_index::bit_set::BitSet; use smallvec::SmallVec; use std::fmt::Debug; use std::hash::Hash; use std::ops::Deref; +use crate::fold::TypeFoldable; use crate::inherent::*; use crate::ir_print::IrPrint; -use crate::solve::inspect::GoalEvaluationStep; +use crate::lang_items::TraitSolverLangItem; +use crate::relate::Relate; +use crate::solve::inspect::CanonicalGoalEvaluationStep; +use crate::solve::{ExternalConstraintsData, PredefinedOpaquesData, SolverMode}; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; -use crate::{ - AliasTerm, AliasTermKind, AliasTy, AliasTyKind, CanonicalVarInfo, CoercePredicate, - DebugWithInfcx, ExistentialProjection, ExistentialTraitRef, FnSig, GenericArgKind, - NormalizesTo, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, -}; +use crate::{self as ty}; pub trait Interner: Sized + Copy - + IrPrint> - + IrPrint> - + IrPrint> - + IrPrint> - + IrPrint> - + IrPrint> - + IrPrint> - + IrPrint> - + IrPrint> - + IrPrint> - + IrPrint> + + IrPrint> + + IrPrint> + + IrPrint> + + IrPrint> + + IrPrint> + + IrPrint> + + IrPrint> + + IrPrint> + + IrPrint> + + IrPrint> + + IrPrint> { - type DefId: Copy + Debug + Hash + Eq; - type AdtDef: Copy + Debug + Hash + Eq; + type DefId: DefId; + type LocalDefId: Copy + Debug + Hash + Eq + Into + TypeFoldable; type GenericArgs: GenericArgs; - /// The slice of args for a specific item. For a GAT like `type Foo<'a>`, it will be `['a]`, - /// not including the args from the parent item (trait or impl). - type OwnItemArgs: Copy + Debug + Hash + Eq; - type GenericArg: Copy + DebugWithInfcx + Hash + Eq + IntoKind>; - type Term: Copy + Debug + Hash + Eq; + type GenericArgsSlice: Copy + Debug + Hash + Eq + Deref; + type GenericArg: GenericArg; + type Term: Term; - type Binder>: BoundVars + TypeSuperVisitable; - type BoundVars: IntoIterator; - type BoundVar; + type BoundVarKinds: Copy + + Debug + + Hash + + Eq + + Deref> + + Default; + type BoundVarKind: Copy + Debug + Hash + Eq; - type CanonicalVars: Copy + Debug + Hash + Eq + IntoIterator>; - type PredefinedOpaques: Copy + Debug + Hash + Eq; - type DefiningOpaqueTypes: Copy + Debug + Hash + Default + Eq + TypeVisitable; - type ExternalConstraints: Copy + Debug + Hash + Eq; - type GoalEvaluationSteps: Copy + Debug + Hash + Eq + Deref]>; + type PredefinedOpaques: Copy + + Debug + + Hash + + Eq + + TypeFoldable + + Deref>; + fn mk_predefined_opaques_in_body( + self, + data: PredefinedOpaquesData, + ) -> Self::PredefinedOpaques; + + type DefiningOpaqueTypes: Copy + + Debug + + Hash + + Default + + Eq + + TypeVisitable + + Deref>; + type CanonicalGoalEvaluationStepRef: Copy + + Debug + + Hash + + Eq + + Deref>; + + type CanonicalVars: Copy + + Debug + + Hash + + Eq + + IntoIterator> + + Deref]>> + + Default; + fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo]) -> Self::CanonicalVars; + + type ExternalConstraints: Copy + + Debug + + Hash + + Eq + + TypeFoldable + + Deref>; + fn mk_external_constraints( + self, + data: ExternalConstraintsData, + ) -> Self::ExternalConstraints; + + type DepNodeIndex; + fn with_cached_task(self, task: impl FnOnce() -> T) -> (T, Self::DepNodeIndex); // Kinds of tys type Ty: Ty; type Tys: Tys; - type FnInputTys: Copy + Debug + Hash + Eq + Deref; - type ParamTy: Copy + Debug + Hash + Eq; + type FnInputTys: Copy + Debug + Hash + Eq + Deref + TypeVisitable; + type ParamTy: Copy + Debug + Hash + Eq + ParamLike; type BoundTy: Copy + Debug + Hash + Eq + BoundVarLike; type PlaceholderTy: PlaceholderLike; // Things stored inside of tys type ErrorGuaranteed: Copy + Debug + Hash + Eq; - type BoundExistentialPredicates: Copy + DebugWithInfcx + Hash + Eq; - type PolyFnSig: Copy + DebugWithInfcx + Hash + Eq; + type BoundExistentialPredicates: BoundExistentialPredicates; type AllocId: Copy + Debug + Hash + Eq; - type Pat: Copy + Debug + Hash + Eq + DebugWithInfcx; + type Pat: Copy + Debug + Hash + Eq + Debug + Relate; type Safety: Safety; type Abi: Abi; // Kinds of consts type Const: Const; - type AliasConst: Copy + DebugWithInfcx + Hash + Eq; type PlaceholderConst: PlaceholderLike; - type ParamConst: Copy + Debug + Hash + Eq; + type ParamConst: Copy + Debug + Hash + Eq + ParamLike; type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike; type ValueConst: Copy + Debug + Hash + Eq; - type ExprConst: Copy + DebugWithInfcx + Hash + Eq; + type ExprConst: Copy + Debug + Hash + Eq + Relate; // Kinds of regions type Region: Region; - type EarlyParamRegion: Copy + Debug + Hash + Eq; + type EarlyParamRegion: Copy + Debug + Hash + Eq + ParamLike; type LateParamRegion: Copy + Debug + Hash + Eq; type BoundRegion: Copy + Debug + Hash + Eq + BoundVarLike; - type InferRegion: Copy + DebugWithInfcx + Hash + Eq; type PlaceholderRegion: PlaceholderLike; // Predicates - type ParamEnv: Copy + Debug + Hash + Eq; + type ParamEnv: ParamEnv; type Predicate: Predicate; - type TraitPredicate: Copy + Debug + Hash + Eq; - type RegionOutlivesPredicate: Copy + Debug + Hash + Eq; - type TypeOutlivesPredicate: Copy + Debug + Hash + Eq; - type ProjectionPredicate: Copy + Debug + Hash + Eq; - type NormalizesTo: Copy + Debug + Hash + Eq; - type SubtypePredicate: Copy + Debug + Hash + Eq; - type CoercePredicate: Copy + Debug + Hash + Eq; - type ClosureKind: Copy + Debug + Hash + Eq; + type Clause: Clause; type Clauses: Copy + Debug + Hash + Eq + TypeSuperVisitable + Flags; - fn mk_canonical_var_infos(self, infos: &[CanonicalVarInfo]) -> Self::CanonicalVars; + type EvaluationCache: EvaluationCache; + fn evaluation_cache(self, mode: SolverMode) -> Self::EvaluationCache; + + fn expand_abstract_consts>(self, t: T) -> T; type GenericsOf: GenericsOf; fn generics_of(self, def_id: Self::DefId) -> Self::GenericsOf; - // FIXME: Remove after uplifting `EarlyBinder` - fn type_of_instantiated(self, def_id: Self::DefId, args: Self::GenericArgs) -> Self::Ty; + type VariancesOf: Copy + + Debug + + Deref + // FIXME: This is terrible! + + IntoIterator>; + fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf; - fn alias_ty_kind(self, alias: AliasTy) -> AliasTyKind; + fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder; - fn alias_term_kind(self, alias: AliasTerm) -> AliasTermKind; + type AdtDef: AdtDef; + fn adt_def(self, adt_def_id: Self::DefId) -> Self::AdtDef; + + fn alias_ty_kind(self, alias: ty::AliasTy) -> ty::AliasTyKind; + + fn alias_term_kind(self, alias: ty::AliasTerm) -> ty::AliasTermKind; fn trait_ref_and_own_args_for_alias( self, def_id: Self::DefId, args: Self::GenericArgs, - ) -> (TraitRef, Self::OwnItemArgs); + ) -> (ty::TraitRef, Self::GenericArgsSlice); fn mk_args(self, args: &[Self::GenericArg]) -> Self::GenericArgs; - fn mk_args_from_iter(self, args: impl Iterator) -> Self::GenericArgs; + fn mk_args_from_iter(self, args: I) -> T::Output + where + I: Iterator, + T: CollectAndApply; + + fn check_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) -> bool; fn check_and_mk_args( self, @@ -123,7 +175,122 @@ pub trait Interner: args: impl IntoIterator>, ) -> Self::GenericArgs; + fn intern_canonical_goal_evaluation_step( + self, + step: CanonicalGoalEvaluationStep, + ) -> Self::CanonicalGoalEvaluationStepRef; + + fn mk_type_list_from_iter(self, args: I) -> T::Output + where + I: Iterator, + T: CollectAndApply; + fn parent(self, def_id: Self::DefId) -> Self::DefId; + + fn recursion_limit(self) -> usize; + + type Features: Features; + fn features(self) -> Self::Features; + + fn bound_coroutine_hidden_types( + self, + def_id: Self::DefId, + ) -> impl IntoIterator>>; + + fn fn_sig( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder>>; + + fn coroutine_movability(self, def_id: Self::DefId) -> Movability; + + fn coroutine_for_closure(self, def_id: Self::DefId) -> Self::DefId; + + fn generics_require_sized_self(self, def_id: Self::DefId) -> bool; + + fn item_bounds( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder>; + + fn predicates_of( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder>; + + fn own_predicates_of( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder>; + + // FIXME: Rename this so it's obvious it's only *immediate* super predicates. + fn super_predicates_of( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder>; + + fn has_target_features(self, def_id: Self::DefId) -> bool; + + fn require_lang_item(self, lang_item: TraitSolverLangItem) -> Self::DefId; + + fn is_lang_item(self, def_id: Self::DefId, lang_item: TraitSolverLangItem) -> bool; + + fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator; + + // FIXME: move `fast_reject` into `rustc_type_ir`. + fn args_may_unify_deep( + self, + obligation_args: Self::GenericArgs, + impl_args: Self::GenericArgs, + ) -> bool; + + fn for_each_relevant_impl( + self, + trait_def_id: Self::DefId, + self_ty: Self::Ty, + f: impl FnMut(Self::DefId), + ); + + fn has_item_definition(self, def_id: Self::DefId) -> bool; + + fn impl_is_default(self, impl_def_id: Self::DefId) -> bool; + + fn impl_trait_ref(self, impl_def_id: Self::DefId) -> ty::EarlyBinder>; + + fn impl_polarity(self, impl_def_id: Self::DefId) -> ty::ImplPolarity; + + fn trait_is_auto(self, trait_def_id: Self::DefId) -> bool; + + fn trait_is_alias(self, trait_def_id: Self::DefId) -> bool; + + fn trait_is_object_safe(self, trait_def_id: Self::DefId) -> bool; + + fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool; + + fn fn_trait_kind_from_def_id(self, trait_def_id: Self::DefId) -> Option; + + fn async_fn_trait_kind_from_def_id(self, trait_def_id: Self::DefId) -> Option; + + fn supertrait_def_ids(self, trait_def_id: Self::DefId) + -> impl IntoIterator; + + fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed; + + fn is_general_coroutine(self, coroutine_def_id: Self::DefId) -> bool; + fn coroutine_is_async(self, coroutine_def_id: Self::DefId) -> bool; + fn coroutine_is_gen(self, coroutine_def_id: Self::DefId) -> bool; + fn coroutine_is_async_gen(self, coroutine_def_id: Self::DefId) -> bool; + + fn layout_is_pointer_like(self, param_env: Self::ParamEnv, ty: Self::Ty) -> bool; + + type UnsizingParams: Deref>; + fn unsizing_params_for_adt(self, adt_def_id: Self::DefId) -> Self::UnsizingParams; + + fn find_const_ty_from_env( + self, + param_env: Self::ParamEnv, + placeholder: Self::PlaceholderConst, + ) -> Self::Ty; } /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs index af4b9eef14b9..d57d0816680b 100644 --- a/compiler/rustc_type_ir/src/ir_print.rs +++ b/compiler/rustc_type_ir/src/ir_print.rs @@ -1,8 +1,9 @@ use std::fmt; use crate::{ - AliasTerm, AliasTy, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, - Interner, NormalizesTo, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, + AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, + Interner, NormalizesTo, OutlivesPredicate, ProjectionPredicate, SubtypePredicate, + TraitPredicate, TraitRef, }; pub trait IrPrint { @@ -22,6 +23,15 @@ macro_rules! define_display_via_print { } } +impl fmt::Display for Binder +where + I: IrPrint>, +{ + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + >>::print(self, fmt) + } +} + macro_rules! define_debug_via_print { ($($ty:ident),+ $(,)?) => { $( @@ -49,3 +59,12 @@ define_display_via_print!( ); define_debug_via_print!(TraitRef, ExistentialTraitRef, ExistentialProjection); + +impl fmt::Display for OutlivesPredicate +where + I: IrPrint>, +{ + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + >>::print(self, fmt) + } +} diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs new file mode 100644 index 000000000000..cf5ec1ab3feb --- /dev/null +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -0,0 +1,36 @@ +/// Lang items used by the new trait solver. This can be mapped to whatever internal +/// representation of `LangItem`s used in the underlying compiler implementation. +pub enum TraitSolverLangItem { + // tidy-alphabetical-start + AsyncDestruct, + AsyncFnKindHelper, + AsyncFnKindUpvars, + AsyncFnOnceOutput, + AsyncIterator, + CallOnceFuture, + CallRefFuture, + Clone, + Copy, + Coroutine, + CoroutineReturn, + CoroutineYield, + Destruct, + DiscriminantKind, + DynMetadata, + FnPtrTrait, + FusedIterator, + Future, + FutureOutput, + Iterator, + Metadata, + Option, + PointeeTrait, + PointerLike, + Poll, + Sized, + TransmuteTrait, + Tuple, + Unpin, + Unsize, + // tidy-alphabetical-end +} diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index fa9bda9a2f75..130ea231bf7e 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -1,57 +1,68 @@ +// tidy-alphabetical-start +#![allow(rustc::usage_of_ty_tykind)] #![cfg_attr( feature = "nightly", - feature(associated_type_defaults, min_specialization, never_type, rustc_attrs) + feature(associated_type_defaults, min_specialization, never_type, rustc_attrs, negative_impls) )] -#![allow(rustc::usage_of_ty_tykind)] #![cfg_attr(feature = "nightly", allow(internal_features))] +// tidy-alphabetical-end #[cfg(feature = "nightly")] extern crate self as rustc_type_ir; +#[cfg(feature = "nightly")] +use rustc_data_structures::sso::SsoHashSet; #[cfg(feature = "nightly")] use rustc_data_structures::sync::Lrc; #[cfg(feature = "nightly")] use rustc_macros::{Decodable, Encodable, HashStable_NoContext}; +#[cfg(not(feature = "nightly"))] +use std::collections::HashSet as SsoHashSet; use std::fmt; use std::hash::Hash; #[cfg(not(feature = "nightly"))] use std::sync::Arc as Lrc; +// These modules are `pub` since they are not glob-imported. #[macro_use] pub mod visit; #[cfg(feature = "nightly")] pub mod codec; +pub mod error; pub mod fold; pub mod inherent; pub mod ir_print; +pub mod lang_items; pub mod lift; +pub mod relate; pub mod solve; -pub mod ty_info; -pub mod ty_kind; +// These modules are not `pub` since they are glob-imported. #[macro_use] mod macros; +mod binder; mod canonical; mod const_kind; -mod debug; mod flags; mod generic_arg; -mod infcx; mod interner; +mod opaque_ty; mod predicate; mod predicate_kind; mod region_kind; +mod ty_info; +mod ty_kind; mod upcast; +pub use binder::*; pub use canonical::*; #[cfg(feature = "nightly")] pub use codec::*; pub use const_kind::*; -pub use debug::{DebugWithInfcx, WithInfcx}; pub use flags::*; pub use generic_arg::*; -pub use infcx::InferCtxtLike; pub use interner::*; +pub use opaque_ty::*; pub use predicate::*; pub use predicate_kind::*; pub use region_kind::*; @@ -63,6 +74,7 @@ pub use DynKind::*; pub use InferTy::*; pub use RegionKind::*; pub use TyKind::*; +pub use Variance::*; rustc_index::newtype_index! { /// A [De Bruijn index][dbi] is a standard means of representing @@ -374,6 +386,10 @@ impl inherent::BoundVarLike for BoundVar { fn var(self) -> BoundVar { self } + + fn assert_eq(self, _var: I::BoundVarKind) { + unreachable!("FIXME: We really should have a separate `BoundConst` for consts") + } } /// Represents the various closure traits in the language. This diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs index f2f7b165de52..aae5aeb5fb36 100644 --- a/compiler/rustc_type_ir/src/macros.rs +++ b/compiler/rustc_type_ir/src/macros.rs @@ -48,10 +48,22 @@ TrivialTypeTraversalImpls! { u32, u64, String, - crate::DebruijnIndex, crate::AliasRelationDirection, - crate::UniverseIndex, - rustc_ast_ir::Mutability, - rustc_ast_ir::Movability, + crate::AliasTyKind, + crate::BoundConstness, + crate::DebruijnIndex, + crate::FloatTy, + crate::InferTy, + crate::IntVarValue, crate::PredicatePolarity, + crate::RegionVid, + crate::solve::BuiltinImplSource, + crate::solve::Certainty, + crate::solve::GoalSource, + crate::solve::MaybeCause, + crate::solve::NoSolution, + crate::UniverseIndex, + crate::Variance, + rustc_ast_ir::Movability, + rustc_ast_ir::Mutability, } diff --git a/compiler/rustc_type_ir/src/opaque_ty.rs b/compiler/rustc_type_ir/src/opaque_ty.rs new file mode 100644 index 000000000000..607370665975 --- /dev/null +++ b/compiler/rustc_type_ir/src/opaque_ty.rs @@ -0,0 +1,51 @@ +use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; +use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; + +use crate::inherent::*; +use crate::{self as ty, Interner}; + +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Hash(bound = ""), + PartialEq(bound = ""), + Eq(bound = ""), + Debug(bound = ""), + Copy(bound = "") +)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +pub struct OpaqueTypeKey { + pub def_id: I::LocalDefId, + pub args: I::GenericArgs, +} + +impl OpaqueTypeKey { + pub fn iter_captured_args(self, tcx: I) -> impl Iterator { + let variances = tcx.variances_of(self.def_id.into()); + std::iter::zip(self.args, variances.into_iter()).enumerate().filter_map(|(i, (arg, v))| { + match (arg.kind(), *v) { + (_, ty::Invariant) => Some((i, arg)), + (ty::GenericArgKind::Lifetime(_), ty::Bivariant) => None, + _ => panic!("unexpected opaque type arg variance"), + } + }) + } + + pub fn fold_captured_lifetime_args( + self, + tcx: I, + mut f: impl FnMut(I::Region) -> I::Region, + ) -> Self { + let Self { def_id, args } = self; + let variances = tcx.variances_of(def_id.into()); + let args = + std::iter::zip(args, variances.into_iter()).map(|(arg, v)| match (arg.kind(), *v) { + (ty::GenericArgKind::Lifetime(_), ty::Bivariant) => arg, + (ty::GenericArgKind::Lifetime(lt), _) => f(lt).into(), + _ => arg, + }); + let args = tcx.mk_args_from_iter(args); + Self { def_id, args } + } +} diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index c0619d782c68..7a3376c7218d 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -6,10 +6,38 @@ use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEn use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use crate::inherent::*; +use crate::lift::Lift; +use crate::upcast::{Upcast, UpcastFrom}; use crate::visit::TypeVisitableExt as _; -use crate::{ - AliasTy, AliasTyKind, DebugWithInfcx, InferCtxtLike, Interner, UnevaluatedConst, WithInfcx, -}; +use crate::{self as ty, Interner}; + +/// `A: 'region` +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "A: Clone"), + Copy(bound = "A: Copy"), + Hash(bound = "A: Hash"), + PartialEq(bound = "A: PartialEq"), + Eq(bound = "A: Eq"), + Debug(bound = "A: fmt::Debug") +)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +pub struct OutlivesPredicate(pub A, pub I::Region); + +// FIXME: We manually derive `Lift` because the `derive(Lift_Generic)` doesn't +// understand how to turn `A` to `A::Lifted` in the output `type Lifted`. +impl Lift for OutlivesPredicate +where + A: Lift, + I::Region: Lift, +{ + type Lifted = OutlivesPredicate; + + fn lift_to_tcx(self, tcx: U) -> Option { + Some(OutlivesPredicate(self.0.lift_to_tcx(tcx)?, self.1.lift_to_tcx(tcx)?)) + } +} /// A complete reference to a trait. These take numerous guises in syntax, /// but perhaps the most recognizable form is in a where-clause: @@ -75,6 +103,16 @@ impl TraitRef { } } +impl ty::Binder> { + pub fn self_ty(&self) -> ty::Binder { + self.map_bound_ref(|tr| tr.self_ty()) + } + + pub fn def_id(&self) -> I::DefId { + self.skip_binder().def_id + } +} + #[derive(derivative::Derivative)] #[derivative( Clone(bound = ""), @@ -112,6 +150,28 @@ impl TraitPredicate { } } +impl ty::Binder> { + pub fn def_id(self) -> I::DefId { + // Ok to skip binder since trait `DefId` does not care about regions. + self.skip_binder().def_id() + } + + pub fn self_ty(self) -> ty::Binder { + self.map_bound(|trait_ref| trait_ref.self_ty()) + } + + #[inline] + pub fn polarity(self) -> PredicatePolarity { + self.skip_binder().polarity + } +} + +impl UpcastFrom> for TraitPredicate { + fn upcast_from(from: TraitRef, _tcx: I) -> Self { + TraitPredicate { trait_ref: from, polarity: PredicatePolarity::Positive } + } +} + impl fmt::Debug for TraitPredicate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // FIXME(effects) printing? @@ -194,13 +254,31 @@ pub enum ExistentialPredicate { AutoTrait(I::DefId), } -// FIXME: Implement this the right way after -impl DebugWithInfcx for ExistentialPredicate { - fn fmt>( - this: rustc_type_ir::WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - fmt::Debug::fmt(&this.data, f) +impl ty::Binder> { + /// Given an existential predicate like `?Self: PartialEq` (e.g., derived from `dyn PartialEq`), + /// and a concrete type `self_ty`, returns a full predicate where the existentially quantified variable `?Self` + /// has been replaced with `self_ty` (e.g., `self_ty: PartialEq`, in our example). + pub fn with_self_ty(&self, tcx: I, self_ty: I::Ty) -> I::Clause { + match self.skip_binder() { + ExistentialPredicate::Trait(tr) => { + self.rebind(tr).with_self_ty(tcx, self_ty).upcast(tcx) + } + ExistentialPredicate::Projection(p) => { + self.rebind(p.with_self_ty(tcx, self_ty)).upcast(tcx) + } + ExistentialPredicate::AutoTrait(did) => { + let generics = tcx.generics_of(did); + let trait_ref = if generics.count() == 1 { + ty::TraitRef::new(tcx, did, [self_ty]) + } else { + // If this is an ill-formed auto trait, then synthesize + // new error args for the missing generics. + let err_args = GenericArgs::extend_with_error(tcx, did, &[self_ty.into()]); + ty::TraitRef::new(tcx, did, err_args) + }; + self.rebind(trait_ref).upcast(tcx) + } + } } } @@ -253,6 +331,20 @@ impl ExistentialTraitRef { } } +impl ty::Binder> { + pub fn def_id(&self) -> I::DefId { + self.skip_binder().def_id + } + + /// Object types don't have a self type specified. Therefore, when + /// we convert the principal trait-ref into a normal trait-ref, + /// you must give *some* self type. A common choice is `mk_err()` + /// or some placeholder type. + pub fn with_self_ty(&self, tcx: I, self_ty: I::Ty) -> ty::Binder> { + self.map_bound(|trait_ref| trait_ref.with_self_ty(tcx, self_ty)) + } +} + /// A `ProjectionPredicate` for an `ExistentialTraitRef`. #[derive(derivative::Derivative)] #[derivative( @@ -308,6 +400,16 @@ impl ExistentialProjection { } } +impl ty::Binder> { + pub fn with_self_ty(&self, tcx: I, self_ty: I::Ty) -> ty::Binder> { + self.map_bound(|p| p.with_self_ty(tcx, self_ty)) + } + + pub fn item_def_id(&self) -> I::DefId { + self.skip_binder().def_id + } +} + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum AliasTermKind { @@ -353,7 +455,8 @@ impl AliasTermKind { Copy(bound = ""), Hash(bound = ""), PartialEq(bound = ""), - Eq(bound = "") + Eq(bound = ""), + Debug(bound = "") )] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] @@ -382,28 +485,11 @@ pub struct AliasTerm { /// aka. `interner.parent(def_id)`. pub def_id: I::DefId, - /// This field exists to prevent the creation of `AliasTerm` without using - /// [AliasTerm::new]. + /// This field exists to prevent the creation of `AliasTerm` without using [`AliasTerm::new`]. + #[derivative(Debug = "ignore")] _use_alias_term_new_instead: (), } -impl std::fmt::Debug for AliasTerm { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} -impl DebugWithInfcx for AliasTerm { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result { - f.debug_struct("AliasTerm") - .field("args", &this.map(|data| data.args)) - .field("def_id", &this.data.def_id) - .finish() - } -} - impl AliasTerm { pub fn new( interner: I, @@ -414,7 +500,7 @@ impl AliasTerm { AliasTerm { def_id, args, _use_alias_term_new_instead: () } } - pub fn expect_ty(self, interner: I) -> AliasTy { + pub fn expect_ty(self, interner: I) -> ty::AliasTy { match self.kind(interner) { AliasTermKind::ProjectionTy | AliasTermKind::InherentTy @@ -424,7 +510,7 @@ impl AliasTerm { panic!("Cannot turn `UnevaluatedConst` into `AliasTy`") } } - AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () } + ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () } } pub fn kind(self, interner: I) -> AliasTermKind { @@ -435,33 +521,32 @@ impl AliasTerm { match self.kind(interner) { AliasTermKind::ProjectionTy => Ty::new_alias( interner, - AliasTyKind::Projection, - AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, + ty::AliasTyKind::Projection, + ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, ) .into(), AliasTermKind::InherentTy => Ty::new_alias( interner, - AliasTyKind::Inherent, - AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, + ty::AliasTyKind::Inherent, + ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, ) .into(), AliasTermKind::OpaqueTy => Ty::new_alias( interner, - AliasTyKind::Opaque, - AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, + ty::AliasTyKind::Opaque, + ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, ) .into(), AliasTermKind::WeakTy => Ty::new_alias( interner, - AliasTyKind::Weak, - AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, + ty::AliasTyKind::Weak, + ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, ) .into(), AliasTermKind::UnevaluatedConst | AliasTermKind::ProjectionConst => { I::Const::new_unevaluated( interner, - UnevaluatedConst::new(self.def_id, self.args), - interner.type_of_instantiated(self.def_id, self.args), + ty::UnevaluatedConst::new(self.def_id, self.args), ) .into() } @@ -498,7 +583,7 @@ impl AliasTerm { /// For example, if this is a projection of `::Item<'a>`, /// then this function would return a `T: StreamingIterator` trait reference and /// `['a]` as the own args. - pub fn trait_ref_and_own_args(self, interner: I) -> (TraitRef, I::OwnItemArgs) { + pub fn trait_ref_and_own_args(self, interner: I) -> (TraitRef, I::GenericArgsSlice) { interner.trait_ref_and_own_args_for_alias(self.def_id, self.args) } @@ -514,14 +599,14 @@ impl AliasTerm { } } -impl From> for AliasTerm { - fn from(ty: AliasTy) -> Self { +impl From> for AliasTerm { + fn from(ty: ty::AliasTy) -> Self { AliasTerm { args: ty.args, def_id: ty.def_id, _use_alias_term_new_instead: () } } } -impl From> for AliasTerm { - fn from(ct: UnevaluatedConst) -> Self { +impl From> for AliasTerm { + fn from(ct: ty::UnevaluatedConst) -> Self { AliasTerm { args: ct.args, def_id: ct.def, _use_alias_term_new_instead: () } } } @@ -571,6 +656,40 @@ impl ProjectionPredicate { } } +impl ty::Binder> { + /// Returns the `DefId` of the trait of the associated item being projected. + #[inline] + pub fn trait_def_id(&self, tcx: I) -> I::DefId { + self.skip_binder().projection_term.trait_def_id(tcx) + } + + /// Get the trait ref required for this projection to be well formed. + /// Note that for generic associated types the predicates of the associated + /// type also need to be checked. + #[inline] + pub fn required_poly_trait_ref(&self, tcx: I) -> ty::Binder> { + // Note: unlike with `TraitRef::to_poly_trait_ref()`, + // `self.0.trait_ref` is permitted to have escaping regions. + // This is because here `self` has a `Binder` and so does our + // return value, so we are preserving the number of binding + // levels. + self.map_bound(|predicate| predicate.projection_term.trait_ref(tcx)) + } + + pub fn term(&self) -> ty::Binder { + self.map_bound(|predicate| predicate.term) + } + + /// The `DefId` of the `TraitItem` for the associated type. + /// + /// Note that this is not the `DefId` of the `TraitRef` containing this + /// associated type, which is in `tcx.associated_item(projection_def_id()).container`. + pub fn projection_def_id(&self) -> I::DefId { + // Ok to skip binder since trait `DefId` does not care about regions. + self.skip_binder().projection_term.def_id + } +} + impl fmt::Debug for ProjectionPredicate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ProjectionPredicate({:?}, {:?})", self.projection_term, self.term) @@ -654,3 +773,36 @@ pub struct CoercePredicate { pub a: I::Ty, pub b: I::Ty, } + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))] +pub enum BoundConstness { + /// `Type: Trait` + NotConst, + /// `Type: const Trait` + Const, + /// `Type: ~const Trait` + /// + /// Requires resolving to const only when we are in a const context. + ConstIfConst, +} + +impl BoundConstness { + pub fn as_str(self) -> &'static str { + match self { + Self::NotConst => "", + Self::Const => "const", + Self::ConstIfConst => "~const", + } + } +} + +impl fmt::Display for BoundConstness { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::NotConst => f.write_str("normal"), + Self::Const => f.write_str("const"), + Self::ConstIfConst => f.write_str("~const"), + } + } +} diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index c477ab14153e..efe270ed6083 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -3,7 +3,7 @@ use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEn use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use std::fmt; -use crate::Interner; +use crate::{self as ty, Interner}; /// A clause is something that can appear in where bounds or be inferred /// by implied bounds. @@ -15,17 +15,17 @@ pub enum ClauseKind { /// Corresponds to `where Foo: Bar`. `Foo` here would be /// the `Self` type of the trait reference and `A`, `B`, and `C` /// would be the type parameters. - Trait(I::TraitPredicate), + Trait(ty::TraitPredicate), - /// `where 'a: 'b` - RegionOutlives(I::RegionOutlivesPredicate), + /// `where 'a: 'r` + RegionOutlives(ty::OutlivesPredicate), - /// `where T: 'a` - TypeOutlives(I::TypeOutlivesPredicate), + /// `where T: 'r` + TypeOutlives(ty::OutlivesPredicate), /// `where ::Name == X`, approximately. /// See the `ProjectionPredicate` struct for details. - Projection(I::ProjectionPredicate), + Projection(ty::ProjectionPredicate), /// Ensures that a const generic argument to a parameter `const N: u8` /// is of type `u8`. @@ -75,7 +75,7 @@ pub enum PredicateKind { /// This obligation is created most often when we have two /// unresolved type variables and hence don't have enough /// information to process the subtyping obligation yet. - Subtype(I::SubtypePredicate), + Subtype(ty::SubtypePredicate), /// `T1` coerced to `T2` /// @@ -85,7 +85,7 @@ pub enum PredicateKind { /// obligation yet. At the moment, we actually process coercions /// very much like subtyping and don't handle the full coercion /// logic. - Coerce(I::CoercePredicate), + Coerce(ty::CoercePredicate), /// Constants must be equal. The first component is the const that is expected. ConstEquate(I::Const, I::Const), @@ -102,7 +102,7 @@ pub enum PredicateKind { /// `T as Trait>::Assoc`, `Projection(::Assoc, ?x)` constrains `?x` /// to `::Assoc` while `NormalizesTo(::Assoc, ?x)` /// results in `NoSolution`. - NormalizesTo(I::NormalizesTo), + NormalizesTo(ty::NormalizesTo), /// Separate from `ClauseKind::Projection` which is used for normalization in new solver. /// This predicate requires two terms to be equal to eachother. diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index eaae4ee0130b..7abcc370c886 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -1,13 +1,23 @@ #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; #[cfg(feature = "nightly")] -use rustc_macros::{TyDecodable, TyEncodable}; +use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use std::fmt; -use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; +use crate::{DebruijnIndex, Interner}; use self::RegionKind::*; +rustc_index::newtype_index! { + /// A **region** **v**ariable **ID**. + #[encodable] + #[orderable] + #[debug_format = "'?{}"] + #[gate_rustc_only] + #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] + pub struct RegionVid {} +} + /// Representation of regions. Note that the NLL checker uses a distinct /// representation of regions. For this reason, it internally replaces all the /// regions with inference variables -- the index of the variable is then used @@ -144,7 +154,7 @@ pub enum RegionKind { /// parameters via `tcx.liberate_late_bound_regions`. They are then treated /// the same way as `ReEarlyParam` while inside of the function. /// - /// See for + /// See for /// more info about early and late bound lifetime parameters. ReLateParam(I::LateParamRegion), @@ -152,7 +162,7 @@ pub enum RegionKind { ReStatic, /// A region variable. Should not exist outside of type inference. - ReVar(I::InferRegion), + ReVar(RegionVid), /// A placeholder region -- the higher-ranked version of `ReLateParam`. /// Should not exist outside of type inference. @@ -208,12 +218,9 @@ impl PartialEq for RegionKind { } } -impl DebugWithInfcx for RegionKind { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut core::fmt::Formatter<'_>, - ) -> core::fmt::Result { - match this.data { +impl fmt::Debug for RegionKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { ReEarlyParam(data) => write!(f, "{data:?}"), ReBound(binder_id, bound_region) => { @@ -225,7 +232,7 @@ impl DebugWithInfcx for RegionKind { ReStatic => f.write_str("'static"), - ReVar(vid) => write!(f, "{:?}", &this.wrap(vid)), + ReVar(vid) => write!(f, "{:?}", &vid), RePlaceholder(placeholder) => write!(f, "{placeholder:?}"), @@ -238,11 +245,6 @@ impl DebugWithInfcx for RegionKind { } } } -impl fmt::Debug for RegionKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} #[cfg(feature = "nightly")] // This is not a derived impl because a derive would require `I: HashStable` @@ -251,7 +253,6 @@ where I::EarlyParamRegion: HashStable, I::BoundRegion: HashStable, I::LateParamRegion: HashStable, - I::InferRegion: HashStable, I::PlaceholderRegion: HashStable, { #[inline] diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs new file mode 100644 index 000000000000..8a6ba87b60e0 --- /dev/null +++ b/compiler/rustc_type_ir/src/relate.rs @@ -0,0 +1,666 @@ +use std::iter; + +use rustc_ast_ir::Mutability; +use rustc_type_ir::error::{ExpectedFound, TypeError}; +use rustc_type_ir::fold::TypeFoldable; +use rustc_type_ir::inherent::*; +use rustc_type_ir::{self as ty, Interner}; +use tracing::{debug, instrument}; + +pub type RelateResult = Result>; + +/// Extra information about why we ended up with a particular variance. +/// This is only used to add more information to error messages, and +/// has no effect on soundness. While choosing the 'wrong' `VarianceDiagInfo` +/// may lead to confusing notes in error messages, it will never cause +/// a miscompilation or unsoundness. +/// +/// When in doubt, use `VarianceDiagInfo::default()` +#[derive(derivative::Derivative)] +#[derivative( + Copy(bound = ""), + Clone(bound = ""), + Debug(bound = ""), + Default(bound = ""), + PartialEq(bound = ""), + Eq(bound = "") +)] +pub enum VarianceDiagInfo { + /// No additional information - this is the default. + /// We will not add any additional information to error messages. + #[derivative(Default)] + None, + /// We switched our variance because a generic argument occurs inside + /// the invariant generic argument of another type. + Invariant { + /// The generic type containing the generic parameter + /// that changes the variance (e.g. `*mut T`, `MyStruct`) + ty: I::Ty, + /// The index of the generic parameter being used + /// (e.g. `0` for `*mut T`, `1` for `MyStruct<'CovariantParam, 'InvariantParam>`) + param_index: u32, + }, +} + +impl VarianceDiagInfo { + /// Mirrors `Variance::xform` - used to 'combine' the existing + /// and new `VarianceDiagInfo`s when our variance changes. + pub fn xform(self, other: VarianceDiagInfo) -> VarianceDiagInfo { + // For now, just use the first `VarianceDiagInfo::Invariant` that we see + match self { + VarianceDiagInfo::None => other, + VarianceDiagInfo::Invariant { .. } => self, + } + } +} + +pub trait TypeRelation: Sized { + fn tcx(&self) -> I; + + /// Returns a static string we can use for printouts. + fn tag(&self) -> &'static str; + + /// Generic relation routine suitable for most anything. + fn relate>(&mut self, a: T, b: T) -> RelateResult { + Relate::relate(self, a, b) + } + + /// Relate the two args for the given item. The default + /// is to look up the variance for the item and proceed + /// accordingly. + fn relate_item_args( + &mut self, + item_def_id: I::DefId, + a_arg: I::GenericArgs, + b_arg: I::GenericArgs, + ) -> RelateResult { + debug!( + "relate_item_args(item_def_id={:?}, a_arg={:?}, b_arg={:?})", + item_def_id, a_arg, b_arg + ); + + let tcx = self.tcx(); + let opt_variances = tcx.variances_of(item_def_id); + relate_args_with_variances(self, item_def_id, &opt_variances, a_arg, b_arg, true) + } + + /// Switch variance for the purpose of relating `a` and `b`. + fn relate_with_variance>( + &mut self, + variance: ty::Variance, + info: VarianceDiagInfo, + a: T, + b: T, + ) -> RelateResult; + + // Overridable relations. You shouldn't typically call these + // directly, instead call `relate()`, which in turn calls + // these. This is both more uniform but also allows us to add + // additional hooks for other types in the future if needed + // without making older code, which called `relate`, obsolete. + + fn tys(&mut self, a: I::Ty, b: I::Ty) -> RelateResult; + + fn regions(&mut self, a: I::Region, b: I::Region) -> RelateResult; + + fn consts(&mut self, a: I::Const, b: I::Const) -> RelateResult; + + fn binders( + &mut self, + a: ty::Binder, + b: ty::Binder, + ) -> RelateResult> + where + T: Relate; +} + +pub trait Relate: TypeFoldable + PartialEq + Copy { + fn relate>(relation: &mut R, a: Self, b: Self) -> RelateResult; +} + +/////////////////////////////////////////////////////////////////////////// +// Relate impls + +#[inline] +pub fn relate_args_invariantly>( + relation: &mut R, + a_arg: I::GenericArgs, + b_arg: I::GenericArgs, +) -> RelateResult { + relation.tcx().mk_args_from_iter(iter::zip(a_arg, b_arg).map(|(a, b)| { + relation.relate_with_variance(ty::Invariant, VarianceDiagInfo::default(), a, b) + })) +} + +pub fn relate_args_with_variances>( + relation: &mut R, + ty_def_id: I::DefId, + variances: &[ty::Variance], + a_arg: I::GenericArgs, + b_arg: I::GenericArgs, + fetch_ty_for_diag: bool, +) -> RelateResult { + let tcx = relation.tcx(); + + let mut cached_ty = None; + let params = iter::zip(a_arg, b_arg).enumerate().map(|(i, (a, b))| { + let variance = variances[i]; + let variance_info = if variance == ty::Invariant && fetch_ty_for_diag { + let ty = + *cached_ty.get_or_insert_with(|| tcx.type_of(ty_def_id).instantiate(tcx, &a_arg)); + VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() } + } else { + VarianceDiagInfo::default() + }; + relation.relate_with_variance(variance, variance_info, a, b) + }); + + tcx.mk_args_from_iter(params) +} + +impl Relate for ty::FnSig { + fn relate>( + relation: &mut R, + a: ty::FnSig, + b: ty::FnSig, + ) -> RelateResult> { + let tcx = relation.tcx(); + + if a.c_variadic != b.c_variadic { + return Err(TypeError::VariadicMismatch({ + let a = a.c_variadic; + let b = b.c_variadic; + ExpectedFound::new(true, a, b) + })); + } + let safety = relation.relate(a.safety, b.safety)?; + let abi = relation.relate(a.abi, b.abi)?; + + let a_inputs = a.inputs(); + let b_inputs = b.inputs(); + + if a_inputs.len() != b_inputs.len() { + return Err(TypeError::ArgCount); + } + + let inputs_and_output = iter::zip(a_inputs.iter(), b_inputs.iter()) + .map(|(&a, &b)| ((a, b), false)) + .chain(iter::once(((a.output(), b.output()), true))) + .map(|((a, b), is_output)| { + if is_output { + relation.relate(a, b) + } else { + relation.relate_with_variance( + ty::Contravariant, + VarianceDiagInfo::default(), + a, + b, + ) + } + }) + .enumerate() + .map(|(i, r)| match r { + Err(TypeError::Sorts(exp_found) | TypeError::ArgumentSorts(exp_found, _)) => { + Err(TypeError::ArgumentSorts(exp_found, i)) + } + Err(TypeError::Mutability | TypeError::ArgumentMutability(_)) => { + Err(TypeError::ArgumentMutability(i)) + } + r => r, + }); + Ok(ty::FnSig { + inputs_and_output: tcx.mk_type_list_from_iter(inputs_and_output)?, + c_variadic: a.c_variadic, + safety, + abi, + }) + } +} + +impl Relate for ty::BoundConstness { + fn relate>( + _relation: &mut R, + a: ty::BoundConstness, + b: ty::BoundConstness, + ) -> RelateResult { + if a != b { + Err(TypeError::ConstnessMismatch(ExpectedFound::new(true, a, b))) + } else { + Ok(a) + } + } +} + +impl Relate for ty::AliasTy { + fn relate>( + relation: &mut R, + a: ty::AliasTy, + b: ty::AliasTy, + ) -> RelateResult> { + if a.def_id != b.def_id { + Err(TypeError::ProjectionMismatched({ + let a = a.def_id; + let b = b.def_id; + ExpectedFound::new(true, a, b) + })) + } else { + let args = match a.kind(relation.tcx()) { + ty::Opaque => relate_args_with_variances( + relation, + a.def_id, + &relation.tcx().variances_of(a.def_id), + a.args, + b.args, + false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle + )?, + ty::Projection | ty::Weak | ty::Inherent => { + relate_args_invariantly(relation, a.args, b.args)? + } + }; + Ok(ty::AliasTy::new(relation.tcx(), a.def_id, args)) + } + } +} + +impl Relate for ty::AliasTerm { + fn relate>( + relation: &mut R, + a: ty::AliasTerm, + b: ty::AliasTerm, + ) -> RelateResult> { + if a.def_id != b.def_id { + Err(TypeError::ProjectionMismatched({ + let a = a.def_id; + let b = b.def_id; + ExpectedFound::new(true, a, b) + })) + } else { + let args = match a.kind(relation.tcx()) { + ty::AliasTermKind::OpaqueTy => relate_args_with_variances( + relation, + a.def_id, + &relation.tcx().variances_of(a.def_id), + a.args, + b.args, + false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle + )?, + ty::AliasTermKind::ProjectionTy + | ty::AliasTermKind::WeakTy + | ty::AliasTermKind::InherentTy + | ty::AliasTermKind::UnevaluatedConst + | ty::AliasTermKind::ProjectionConst => { + relate_args_invariantly(relation, a.args, b.args)? + } + }; + Ok(ty::AliasTerm::new(relation.tcx(), a.def_id, args)) + } + } +} + +impl Relate for ty::ExistentialProjection { + fn relate>( + relation: &mut R, + a: ty::ExistentialProjection, + b: ty::ExistentialProjection, + ) -> RelateResult> { + if a.def_id != b.def_id { + Err(TypeError::ProjectionMismatched({ + let a = a.def_id; + let b = b.def_id; + ExpectedFound::new(true, a, b) + })) + } else { + let term = relation.relate_with_variance( + ty::Invariant, + VarianceDiagInfo::default(), + a.term, + b.term, + )?; + let args = relation.relate_with_variance( + ty::Invariant, + VarianceDiagInfo::default(), + a.args, + b.args, + )?; + Ok(ty::ExistentialProjection { def_id: a.def_id, args, term }) + } + } +} + +impl Relate for ty::TraitRef { + fn relate>( + relation: &mut R, + a: ty::TraitRef, + b: ty::TraitRef, + ) -> RelateResult> { + // Different traits cannot be related. + if a.def_id != b.def_id { + Err(TypeError::Traits({ + let a = a.def_id; + let b = b.def_id; + ExpectedFound::new(true, a, b) + })) + } else { + let args = relate_args_invariantly(relation, a.args, b.args)?; + Ok(ty::TraitRef::new(relation.tcx(), a.def_id, args)) + } + } +} + +impl Relate for ty::ExistentialTraitRef { + fn relate>( + relation: &mut R, + a: ty::ExistentialTraitRef, + b: ty::ExistentialTraitRef, + ) -> RelateResult> { + // Different traits cannot be related. + if a.def_id != b.def_id { + Err(TypeError::Traits({ + let a = a.def_id; + let b = b.def_id; + ExpectedFound::new(true, a, b) + })) + } else { + let args = relate_args_invariantly(relation, a.args, b.args)?; + Ok(ty::ExistentialTraitRef { def_id: a.def_id, args }) + } + } +} + +/// Relates `a` and `b` structurally, calling the relation for all nested values. +/// Any semantic equality, e.g. of projections, and inference variables have to be +/// handled by the caller. +#[instrument(level = "trace", skip(relation), ret)] +pub fn structurally_relate_tys>( + relation: &mut R, + a: I::Ty, + b: I::Ty, +) -> RelateResult { + let tcx = relation.tcx(); + match (a.kind(), b.kind()) { + (ty::Infer(_), _) | (_, ty::Infer(_)) => { + // The caller should handle these cases! + panic!("var types encountered in structurally_relate_tys") + } + + (ty::Bound(..), _) | (_, ty::Bound(..)) => { + panic!("bound types encountered in structurally_relate_tys") + } + + (ty::Error(guar), _) | (_, ty::Error(guar)) => Ok(Ty::new_error(tcx, guar)), + + (ty::Never, _) + | (ty::Char, _) + | (ty::Bool, _) + | (ty::Int(_), _) + | (ty::Uint(_), _) + | (ty::Float(_), _) + | (ty::Str, _) + if a == b => + { + Ok(a) + } + + (ty::Param(a_p), ty::Param(b_p)) if a_p.index() == b_p.index() => { + // FIXME: Put this back + //debug_assert_eq!(a_p.name(), b_p.name(), "param types with same index differ in name"); + Ok(a) + } + + (ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a), + + (ty::Adt(a_def, a_args), ty::Adt(b_def, b_args)) if a_def == b_def => { + let args = relation.relate_item_args(a_def.def_id(), a_args, b_args)?; + Ok(Ty::new_adt(tcx, a_def, args)) + } + + (ty::Foreign(a_id), ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(tcx, a_id)), + + (ty::Dynamic(a_obj, a_region, a_repr), ty::Dynamic(b_obj, b_region, b_repr)) + if a_repr == b_repr => + { + Ok(Ty::new_dynamic( + tcx, + relation.relate(a_obj, b_obj)?, + relation.relate(a_region, b_region)?, + a_repr, + )) + } + + (ty::Coroutine(a_id, a_args), ty::Coroutine(b_id, b_args)) if a_id == b_id => { + // All Coroutine types with the same id represent + // the (anonymous) type of the same coroutine expression. So + // all of their regions should be equated. + let args = relate_args_invariantly(relation, a_args, b_args)?; + Ok(Ty::new_coroutine(tcx, a_id, args)) + } + + (ty::CoroutineWitness(a_id, a_args), ty::CoroutineWitness(b_id, b_args)) + if a_id == b_id => + { + // All CoroutineWitness types with the same id represent + // the (anonymous) type of the same coroutine expression. So + // all of their regions should be equated. + let args = relate_args_invariantly(relation, a_args, b_args)?; + Ok(Ty::new_coroutine_witness(tcx, a_id, args)) + } + + (ty::Closure(a_id, a_args), ty::Closure(b_id, b_args)) if a_id == b_id => { + // All Closure types with the same id represent + // the (anonymous) type of the same closure expression. So + // all of their regions should be equated. + let args = relate_args_invariantly(relation, a_args, b_args)?; + Ok(Ty::new_closure(tcx, a_id, args)) + } + + (ty::CoroutineClosure(a_id, a_args), ty::CoroutineClosure(b_id, b_args)) + if a_id == b_id => + { + let args = relate_args_invariantly(relation, a_args, b_args)?; + Ok(Ty::new_coroutine_closure(tcx, a_id, args)) + } + + (ty::RawPtr(a_ty, a_mutbl), ty::RawPtr(b_ty, b_mutbl)) => { + if a_mutbl != b_mutbl { + return Err(TypeError::Mutability); + } + + let (variance, info) = match a_mutbl { + Mutability::Not => (ty::Covariant, VarianceDiagInfo::None), + Mutability::Mut => { + (ty::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) + } + }; + + let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?; + + Ok(Ty::new_ptr(tcx, ty, a_mutbl)) + } + + (ty::Ref(a_r, a_ty, a_mutbl), ty::Ref(b_r, b_ty, b_mutbl)) => { + if a_mutbl != b_mutbl { + return Err(TypeError::Mutability); + } + + let (variance, info) = match a_mutbl { + Mutability::Not => (ty::Covariant, VarianceDiagInfo::None), + Mutability::Mut => { + (ty::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) + } + }; + + let r = relation.relate(a_r, b_r)?; + let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?; + + Ok(Ty::new_ref(tcx, r, ty, a_mutbl)) + } + + (ty::Array(a_t, sz_a), ty::Array(b_t, sz_b)) => { + let t = relation.relate(a_t, b_t)?; + match relation.relate(sz_a, sz_b) { + Ok(sz) => Ok(Ty::new_array_with_const_len(tcx, t, sz)), + Err(err) => { + // Check whether the lengths are both concrete/known values, + // but are unequal, for better diagnostics. + let sz_a = sz_a.try_to_target_usize(tcx); + let sz_b = sz_b.try_to_target_usize(tcx); + + match (sz_a, sz_b) { + (Some(sz_a_val), Some(sz_b_val)) if sz_a_val != sz_b_val => Err( + TypeError::FixedArraySize(ExpectedFound::new(true, sz_a_val, sz_b_val)), + ), + _ => Err(err), + } + } + } + } + + (ty::Slice(a_t), ty::Slice(b_t)) => { + let t = relation.relate(a_t, b_t)?; + Ok(Ty::new_slice(tcx, t)) + } + + (ty::Tuple(as_), ty::Tuple(bs)) => { + if as_.len() == bs.len() { + Ok(Ty::new_tup_from_iter( + tcx, + iter::zip(as_, bs).map(|(a, b)| relation.relate(a, b)), + )?) + } else if !(as_.is_empty() || bs.is_empty()) { + Err(TypeError::TupleSize(ExpectedFound::new(true, as_.len(), bs.len()))) + } else { + Err(TypeError::Sorts(ExpectedFound::new(true, a, b))) + } + } + + (ty::FnDef(a_def_id, a_args), ty::FnDef(b_def_id, b_args)) if a_def_id == b_def_id => { + let args = relation.relate_item_args(a_def_id, a_args, b_args)?; + Ok(Ty::new_fn_def(tcx, a_def_id, args)) + } + + (ty::FnPtr(a_fty), ty::FnPtr(b_fty)) => { + let fty = relation.relate(a_fty, b_fty)?; + Ok(Ty::new_fn_ptr(tcx, fty)) + } + + // Alias tend to mostly already be handled downstream due to normalization. + (ty::Alias(a_kind, a_data), ty::Alias(b_kind, b_data)) => { + let alias_ty = relation.relate(a_data, b_data)?; + assert_eq!(a_kind, b_kind); + Ok(Ty::new_alias(tcx, a_kind, alias_ty)) + } + + (ty::Pat(a_ty, a_pat), ty::Pat(b_ty, b_pat)) => { + let ty = relation.relate(a_ty, b_ty)?; + let pat = relation.relate(a_pat, b_pat)?; + Ok(Ty::new_pat(tcx, ty, pat)) + } + + _ => Err(TypeError::Sorts(ExpectedFound::new(true, a, b))), + } +} + +/// Relates `a` and `b` structurally, calling the relation for all nested values. +/// Any semantic equality, e.g. of unevaluated consts, and inference variables have +/// to be handled by the caller. +/// +/// FIXME: This is not totally structual, which probably should be fixed. +/// See the HACKs below. +pub fn structurally_relate_consts>( + relation: &mut R, + mut a: I::Const, + mut b: I::Const, +) -> RelateResult { + debug!("{}.structurally_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b); + let tcx = relation.tcx(); + + if tcx.features().generic_const_exprs() { + a = tcx.expand_abstract_consts(a); + b = tcx.expand_abstract_consts(b); + } + + debug!("{}.structurally_relate_consts(normed_a = {:?}, normed_b = {:?})", relation.tag(), a, 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 is_match = match (a.kind(), b.kind()) { + (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => { + // The caller should handle these cases! + panic!("var types encountered in structurally_relate_consts: {:?} {:?}", a, b) + } + + (ty::ConstKind::Error(_), _) => return Ok(a), + (_, ty::ConstKind::Error(_)) => return Ok(b), + + (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index() == b_p.index() => { + // FIXME: Put this back + // debug_assert_eq!(a_p.name, b_p.name, "param types with same index differ in name"); + true + } + (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2, + (ty::ConstKind::Value(_, a_val), ty::ConstKind::Value(_, b_val)) => a_val == b_val, + + // While this is slightly incorrect, it shouldn't matter for `min_const_generics` + // and is the better alternative to waiting until `generic_const_exprs` can + // be stabilized. + (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if au.def == bu.def => { + if cfg!(debug_assertions) { + let a_ty = tcx.type_of(au.def).instantiate(tcx, &au.args); + let b_ty = tcx.type_of(bu.def).instantiate(tcx, &bu.args); + assert_eq!(a_ty, b_ty); + } + + let args = relation.relate_with_variance( + ty::Invariant, + VarianceDiagInfo::default(), + au.args, + bu.args, + )?; + return Ok(Const::new_unevaluated(tcx, ty::UnevaluatedConst { def: au.def, args })); + } + (ty::ConstKind::Expr(ae), ty::ConstKind::Expr(be)) => { + let expr = relation.relate(ae, be)?; + return Ok(Const::new_expr(tcx, expr)); + } + _ => false, + }; + if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(ExpectedFound::new(true, a, b))) } +} + +impl> Relate for ty::Binder { + fn relate>( + relation: &mut R, + a: ty::Binder, + b: ty::Binder, + ) -> RelateResult> { + relation.binders(a, b) + } +} + +impl Relate for ty::PredicatePolarity { + fn relate>( + _relation: &mut R, + a: ty::PredicatePolarity, + b: ty::PredicatePolarity, + ) -> RelateResult { + if a != b { + Err(TypeError::PolarityMismatch(ExpectedFound::new(true, a, b))) + } else { + Ok(a) + } + } +} + +impl Relate for ty::TraitPredicate { + fn relate>( + relation: &mut R, + a: ty::TraitPredicate, + b: ty::TraitPredicate, + ) -> RelateResult> { + Ok(ty::TraitPredicate { + trait_ref: relation.relate(a.trait_ref, b.trait_ref)?, + polarity: relation.relate(a.polarity, b.polarity)?, + }) + } +} diff --git a/compiler/rustc_type_ir/src/solve.rs b/compiler/rustc_type_ir/src/solve.rs index 3c24e851d7b6..7934f996f0bd 100644 --- a/compiler/rustc_type_ir/src/solve.rs +++ b/compiler/rustc_type_ir/src/solve.rs @@ -7,7 +7,68 @@ use std::hash::Hash; use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; -use crate::{Canonical, CanonicalVarValues, Interner, Upcast}; +use crate::{self as ty, Canonical, CanonicalVarValues, Interner, Upcast}; + +/// Depending on the stage of compilation, we want projection to be +/// more or less conservative. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +pub enum Reveal { + /// At type-checking time, we refuse to project any associated + /// type that is marked `default`. Non-`default` ("final") types + /// are always projected. This is necessary in general for + /// soundness of specialization. However, we *could* allow + /// projections in fully-monomorphic cases. We choose not to, + /// because we prefer for `default type` to force the type + /// definition to be treated abstractly by any consumers of the + /// impl. Concretely, that means that the following example will + /// fail to compile: + /// + /// ```compile_fail,E0308 + /// #![feature(specialization)] + /// trait Assoc { + /// type Output; + /// } + /// + /// impl Assoc for T { + /// default type Output = bool; + /// } + /// + /// fn main() { + /// let x: <() as Assoc>::Output = true; + /// } + /// ``` + /// + /// We also do not reveal the hidden type of opaque types during + /// type-checking. + UserFacing, + + /// At codegen time, all monomorphic projections will succeed. + /// Also, `impl Trait` is normalized to the concrete type, + /// which has to be already collected by type-checking. + /// + /// NOTE: as `impl Trait`'s concrete type should *never* + /// be observable directly by the user, `Reveal::All` + /// should not be used by checks which may expose + /// type equality or type contents to the user. + /// There are some exceptions, e.g., around auto traits and + /// transmute-checking, which expose some details, but + /// not the whole concrete type of the `impl Trait`. + All, +} + +#[derive(Debug, Clone, Copy)] +pub enum SolverMode { + /// Ordinary trait solving, using everywhere except for coherence. + Normal, + /// Trait solving during coherence. There are a few notable differences + /// between coherence and ordinary trait solving. + /// + /// Most importantly, trait solving during coherence must not be incomplete, + /// i.e. return `Err(NoSolution)` for goals for which a solution exists. + /// This means that we must not make any guesses or arbitrary choices. + Coherence, +} pub type CanonicalInput::Predicate> = Canonical>; pub type CanonicalResponse = Canonical>; @@ -20,7 +81,6 @@ pub type CanonicalResponse = Canonical>; pub type QueryResult = Result, NoSolution>; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -#[derive(TypeFoldable_Generic, TypeVisitable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct NoSolution; @@ -60,7 +120,7 @@ impl Goal { /// /// This is necessary as we treat nested goals different depending on /// their source. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum GoalSource { Misc, @@ -96,6 +156,22 @@ pub struct QueryInput { pub predefined_opaques_in_body: I::PredefinedOpaques, } +/// Opaques that are defined in the inference context before a query is called. +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Hash(bound = ""), + PartialEq(bound = ""), + Eq(bound = ""), + Debug(bound = ""), + Default(bound = "") +)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +pub struct PredefinedOpaquesData { + pub opaque_types: Vec<(ty::OpaqueTypeKey, I::Ty)>, +} + /// Possible ways the given goal can be proven. #[derive(derivative::Derivative)] #[derivative( @@ -170,28 +246,22 @@ pub enum CandidateSource { } #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))] pub enum BuiltinImplSource { - /// Some builtin impl we don't need to differentiate. This should be used + /// Some built-in impl we don't need to differentiate. This should be used /// unless more specific information is necessary. Misc, - /// A builtin impl for trait objects. + /// A built-in impl for trait objects. The index is only used in winnowing. + Object(usize), + /// A built-in implementation of `Upcast` for trait objects to other trait objects. /// - /// The vtable is formed by concatenating together the method lists of - /// the base object trait and all supertraits, pointers to supertrait vtable will - /// be provided when necessary; this is the start of `upcast_trait_ref`'s methods - /// in that vtable. - Object { vtable_base: usize }, - /// The vtable is formed by concatenating together the method lists of - /// the base object trait and all supertraits, pointers to supertrait vtable will - /// be provided when necessary; this is the position of `upcast_trait_ref`'s vtable - /// within that vtable. - TraitUpcasting { vtable_vptr_slot: Option }, + /// This can be removed when `feature(dyn_upcasting)` is stabilized, since we only + /// use it to detect when upcasting traits in hir typeck. + TraitUpcasting, /// Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y`. /// - /// This needs to be a separate variant as it is still unstable and we need to emit - /// a feature error when using it on stable. + /// This can be removed when `feature(tuple_unsizing)` is stabilized, since we only + /// use it to detect when unsizing tuples in hir typeck. TupleUnsizing, } @@ -213,9 +283,49 @@ pub struct Response { pub external_constraints: I::ExternalConstraints, } -#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] +/// Additional constraints returned on success. +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Hash(bound = ""), + PartialEq(bound = ""), + Eq(bound = ""), + Debug(bound = ""), + Default(bound = "") +)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +pub struct ExternalConstraintsData { + pub region_constraints: Vec>, + pub opaque_types: Vec<(ty::OpaqueTypeKey, I::Ty)>, + pub normalization_nested_goals: NestedNormalizationGoals, +} + +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Hash(bound = ""), + PartialEq(bound = ""), + Eq(bound = ""), + Debug(bound = ""), + Default(bound = "") +)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +pub struct NestedNormalizationGoals(pub Vec<(GoalSource, Goal)>); + +impl NestedNormalizationGoals { + pub fn empty() -> Self { + NestedNormalizationGoals(vec![]) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum Certainty { Yes, Maybe(MaybeCause), @@ -252,7 +362,6 @@ impl Certainty { /// Why we failed to evaluate a goal. #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum MaybeCause { /// We failed due to ambiguity. This ambiguity can either @@ -276,3 +385,12 @@ impl MaybeCause { } } } + +#[derive(derivative::Derivative)] +#[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))] +pub struct CacheData { + pub result: QueryResult, + pub proof_tree: Option, + pub additional_depth: usize, + pub encountered_overflow: bool, +} diff --git a/compiler/rustc_type_ir/src/solve/inspect.rs b/compiler/rustc_type_ir/src/solve/inspect.rs index c4f6ee2669be..0733c730064b 100644 --- a/compiler/rustc_type_ir/src/solve/inspect.rs +++ b/compiler/rustc_type_ir/src/solve/inspect.rs @@ -1,36 +1,29 @@ //! Data structure used to inspect trait solver behavior. //! //! During trait solving we optionally build "proof trees", the root of -//! which is a [GoalEvaluation] with [GoalEvaluationKind::Root]. These -//! trees are used to improve the debug experience and are also used by -//! the compiler itself to provide necessary context for error messages. +//! which is a [GoalEvaluation]. These trees are used by the compiler +//! to inspect the behavior of the trait solver and to access its internal +//! state, e.g. for diagnostics and when selecting impls during codegen. //! //! Because each nested goal in the solver gets [canonicalized] separately //! and we discard inference progress via "probes", we cannot mechanically //! use proof trees without somehow "lifting up" data local to the current -//! `InferCtxt`. Any data used mechanically is therefore canonicalized and -//! stored as [CanonicalState]. As printing canonicalized data worsens the -//! debugging dumps, we do not simply canonicalize everything. +//! `InferCtxt`. To use the data from evaluation we therefore canonicalize +//! it and store it as a [CanonicalState]. //! -//! This means proof trees contain inference variables and placeholders -//! local to a different `InferCtxt` which must not be used with the -//! current one. +//! Proof trees are only shallow, we do not compute the proof tree for nested +//! goals. Visiting proof trees instead recomputes nested goals in the parents +//! inference context when necessary. //! //! [canonicalized]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html -mod format; - -use std::fmt::{Debug, Write}; -use std::hash::Hash; - -use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; - -use self::format::ProofTreeFormatter; use crate::solve::{ - CandidateSource, CanonicalInput, Certainty, Goal, GoalSource, NoSolution, QueryInput, - QueryResult, + CandidateSource, CanonicalInput, Certainty, Goal, GoalSource, QueryInput, QueryResult, }; use crate::{Canonical, CanonicalVarValues, Interner}; +use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; +use std::fmt::Debug; +use std::hash::Hash; /// Some `data` together with information about how they relate to the input /// of the canonical query. @@ -55,23 +48,15 @@ pub struct State { pub type CanonicalState = Canonical>; -/// When evaluating the root goals we also store the -/// original values for the `CanonicalVarValues` of the -/// canonicalized goal. We use this to map any [CanonicalState] -/// from the local `InferCtxt` of the solver query to -/// the `InferCtxt` of the caller. -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Hash(bound = ""), Debug(bound = ""))] -pub enum GoalEvaluationKind { - Root { orig_values: Vec }, - Nested, -} - +/// When evaluating a goal we also store the original values +/// for the `CanonicalVarValues` of the canonicalized goal. +/// We use this to map any [CanonicalState] from the local `InferCtxt` +/// of the solver query to the `InferCtxt` of the caller. #[derive(derivative::Derivative)] #[derivative(PartialEq(bound = ""), Eq(bound = ""), Hash(bound = ""))] pub struct GoalEvaluation { pub uncanonicalized_goal: Goal, - pub kind: GoalEvaluationKind, + pub orig_values: Vec, pub evaluation: CanonicalGoalEvaluation, } @@ -89,24 +74,12 @@ pub enum CanonicalGoalEvaluationKind { Overflow, CycleInStack, ProvisionalCacheHit, - Evaluation { revisions: I::GoalEvaluationSteps }, -} -impl Debug for GoalEvaluation { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - ProofTreeFormatter::new(f).format_goal_evaluation(self) - } + Evaluation { final_revision: I::CanonicalGoalEvaluationStepRef }, } #[derive(derivative::Derivative)] #[derivative(PartialEq(bound = ""), Eq(bound = ""), Hash(bound = ""), Debug(bound = ""))] -pub struct AddedGoalsEvaluation { - pub evaluations: Vec>>, - pub result: Result, -} - -#[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Hash(bound = ""), Debug(bound = ""))] -pub struct GoalEvaluationStep { +pub struct CanonicalGoalEvaluationStep { pub instantiated_goal: QueryInput, /// The actual evaluation of the goal, always `ProbeKind::Root`. @@ -117,7 +90,7 @@ pub struct GoalEvaluationStep { /// corresponds to a `EvalCtxt::probe(_X)` call or the root evaluation /// of a goal. #[derive(derivative::Derivative)] -#[derivative(PartialEq(bound = ""), Eq(bound = ""), Hash(bound = ""))] +#[derivative(Debug(bound = ""), PartialEq(bound = ""), Eq(bound = ""), Hash(bound = ""))] pub struct Probe { /// What happened inside of this probe in chronological order. pub steps: Vec>, @@ -125,23 +98,15 @@ pub struct Probe { pub final_state: CanonicalState, } -impl Debug for Probe { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - ProofTreeFormatter::new(f).format_probe(self) - } -} - #[derive(derivative::Derivative)] #[derivative(PartialEq(bound = ""), Eq(bound = ""), Hash(bound = ""), Debug(bound = ""))] pub enum ProbeStep { /// We added a goal to the `EvalCtxt` which will get proven /// the next time `EvalCtxt::try_evaluate_added_goals` is called. AddGoal(GoalSource, CanonicalState>), - /// The inside of a `EvalCtxt::try_evaluate_added_goals` call. - EvaluateGoals(AddedGoalsEvaluation), /// A call to `probe` while proving the current goal. This is /// used whenever there are multiple candidates to prove the - /// current goalby . + /// current goal. NestedProbe(Probe), /// A trait goal was satisfied by an impl candidate. RecordImplArgs { impl_args: CanonicalState }, diff --git a/compiler/rustc_type_ir/src/solve/inspect/format.rs b/compiler/rustc_type_ir/src/solve/inspect/format.rs deleted file mode 100644 index 44ade04cf98c..000000000000 --- a/compiler/rustc_type_ir/src/solve/inspect/format.rs +++ /dev/null @@ -1,173 +0,0 @@ -use std::marker::PhantomData; - -use super::*; - -pub(super) struct ProofTreeFormatter<'a, 'b, I> { - f: &'a mut (dyn Write + 'b), - _interner: PhantomData, -} - -enum IndentorState { - StartWithNewline, - OnNewline, - Inline, -} - -/// A formatter which adds 4 spaces of indentation to its input before -/// passing it on to its nested formatter. -/// -/// We can use this for arbitrary levels of indentation by nesting it. -struct Indentor<'a, 'b> { - f: &'a mut (dyn Write + 'b), - state: IndentorState, -} - -impl Write for Indentor<'_, '_> { - fn write_str(&mut self, s: &str) -> std::fmt::Result { - for line in s.split_inclusive('\n') { - match self.state { - IndentorState::StartWithNewline => self.f.write_str("\n ")?, - IndentorState::OnNewline => self.f.write_str(" ")?, - IndentorState::Inline => {} - } - self.state = - if line.ends_with('\n') { IndentorState::OnNewline } else { IndentorState::Inline }; - self.f.write_str(line)?; - } - - Ok(()) - } -} - -impl<'a, 'b, I: Interner> ProofTreeFormatter<'a, 'b, I> { - pub(super) fn new(f: &'a mut (dyn Write + 'b)) -> Self { - ProofTreeFormatter { f, _interner: PhantomData } - } - - fn nested(&mut self, func: F) -> std::fmt::Result - where - F: FnOnce(&mut ProofTreeFormatter<'_, '_, I>) -> std::fmt::Result, - { - write!(self.f, " {{")?; - func(&mut ProofTreeFormatter { - f: &mut Indentor { f: self.f, state: IndentorState::StartWithNewline }, - _interner: PhantomData, - })?; - writeln!(self.f, "}}") - } - - pub(super) fn format_goal_evaluation(&mut self, eval: &GoalEvaluation) -> std::fmt::Result { - let goal_text = match eval.kind { - GoalEvaluationKind::Root { orig_values: _ } => "ROOT GOAL", - GoalEvaluationKind::Nested => "GOAL", - }; - write!(self.f, "{}: {:?}", goal_text, eval.uncanonicalized_goal)?; - self.nested(|this| this.format_canonical_goal_evaluation(&eval.evaluation)) - } - - pub(super) fn format_canonical_goal_evaluation( - &mut self, - eval: &CanonicalGoalEvaluation, - ) -> std::fmt::Result { - writeln!(self.f, "GOAL: {:?}", eval.goal)?; - - match &eval.kind { - CanonicalGoalEvaluationKind::Overflow => { - writeln!(self.f, "OVERFLOW: {:?}", eval.result) - } - CanonicalGoalEvaluationKind::CycleInStack => { - writeln!(self.f, "CYCLE IN STACK: {:?}", eval.result) - } - CanonicalGoalEvaluationKind::ProvisionalCacheHit => { - writeln!(self.f, "PROVISIONAL CACHE HIT: {:?}", eval.result) - } - CanonicalGoalEvaluationKind::Evaluation { revisions } => { - for (n, step) in revisions.iter().enumerate() { - write!(self.f, "REVISION {n}")?; - self.nested(|this| this.format_evaluation_step(step))?; - } - writeln!(self.f, "RESULT: {:?}", eval.result) - } - } - } - - pub(super) fn format_evaluation_step( - &mut self, - evaluation_step: &GoalEvaluationStep, - ) -> std::fmt::Result { - writeln!(self.f, "INSTANTIATED: {:?}", evaluation_step.instantiated_goal)?; - self.format_probe(&evaluation_step.evaluation) - } - - pub(super) fn format_probe(&mut self, probe: &Probe) -> std::fmt::Result { - match &probe.kind { - ProbeKind::Root { result } => { - write!(self.f, "ROOT RESULT: {result:?}") - } - ProbeKind::TryNormalizeNonRigid { result } => { - write!(self.f, "TRY NORMALIZE NON-RIGID: {result:?}") - } - ProbeKind::NormalizedSelfTyAssembly => { - write!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:") - } - ProbeKind::UnsizeAssembly => { - write!(self.f, "ASSEMBLING CANDIDATES FOR UNSIZING:") - } - ProbeKind::UpcastProjectionCompatibility => { - write!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:") - } - ProbeKind::OpaqueTypeStorageLookup { result } => { - write!(self.f, "PROBING FOR AN EXISTING OPAQUE: {result:?}") - } - ProbeKind::TraitCandidate { source, result } => { - write!(self.f, "CANDIDATE {source:?}: {result:?}") - } - ProbeKind::ShadowedEnvProbing => { - write!(self.f, "PROBING FOR IMPLS SHADOWED BY PARAM-ENV CANDIDATE:") - } - }?; - - self.nested(|this| { - for step in &probe.steps { - match step { - ProbeStep::AddGoal(source, goal) => { - let source = match source { - GoalSource::Misc => "misc", - GoalSource::ImplWhereBound => "impl where-bound", - GoalSource::InstantiateHigherRanked => "higher-ranked goal", - }; - writeln!(this.f, "ADDED GOAL ({source}): {goal:?}")? - } - ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?, - ProbeStep::NestedProbe(probe) => this.format_probe(probe)?, - ProbeStep::MakeCanonicalResponse { shallow_certainty } => { - writeln!(this.f, "EVALUATE GOALS AND MAKE RESPONSE: {shallow_certainty:?}")? - } - ProbeStep::RecordImplArgs { impl_args } => { - writeln!(this.f, "RECORDED IMPL ARGS: {impl_args:?}")? - } - } - } - Ok(()) - }) - } - - pub(super) fn format_added_goals_evaluation( - &mut self, - added_goals_evaluation: &AddedGoalsEvaluation, - ) -> std::fmt::Result { - writeln!(self.f, "TRY_EVALUATE_ADDED_GOALS: {:?}", added_goals_evaluation.result)?; - - for (n, iterations) in added_goals_evaluation.evaluations.iter().enumerate() { - write!(self.f, "ITERATION {n}")?; - self.nested(|this| { - for goal_evaluation in iterations { - this.format_goal_evaluation(goal_evaluation)?; - } - Ok(()) - })?; - } - - Ok(()) - } -} diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 629ea9fb839c..915888ab59b9 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -1,19 +1,21 @@ #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; #[cfg(feature = "nightly")] -use rustc_data_structures::unify::{EqUnifyValue, UnifyKey}; +use rustc_data_structures::unify::{NoError, UnifyKey, UnifyValue}; #[cfg(feature = "nightly")] use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use std::fmt; -use crate::inherent::*; -use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, TraitRef, WithInfcx}; - +pub use self::closure::*; use self::TyKind::*; +use crate::inherent::*; +use crate::{self as ty, DebruijnIndex, Interner}; use rustc_ast_ir::Mutability; +mod closure; + /// Specifies how a trait object is represented. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] @@ -141,7 +143,7 @@ pub enum TyKind { /// fn foo() -> i32 { 1 } /// let bar: fn() -> i32 = foo; /// ``` - FnPtr(I::PolyFnSig), + FnPtr(ty::Binder>), /// A trait object. Written as `dyn for<'b> Trait<'b, Assoc = u32> + Send + 'a`. Dynamic(I::BoundExistentialPredicates, I::Region, DynKind), @@ -339,12 +341,10 @@ impl PartialEq for TyKind { } } -impl DebugWithInfcx for TyKind { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - match this.data { +// This is manually implemented because a derive would require `I: Debug` +impl fmt::Debug for TyKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { Bool => write!(f, "bool"), Char => write!(f, "char"), Int(i) => write!(f, "{i:?}"), @@ -367,27 +367,23 @@ impl DebugWithInfcx for TyKind { } Foreign(d) => f.debug_tuple("Foreign").field(d).finish(), Str => write!(f, "str"), - Array(t, c) => write!(f, "[{:?}; {:?}]", &this.wrap(t), &this.wrap(c)), - Pat(t, p) => write!(f, "pattern_type!({:?} is {:?})", &this.wrap(t), &this.wrap(p)), - Slice(t) => write!(f, "[{:?}]", &this.wrap(t)), - RawPtr(ty, mutbl) => write!(f, "*{} {:?}", mutbl.ptr_str(), this.wrap(ty)), - Ref(r, t, m) => write!(f, "&{:?} {}{:?}", this.wrap(r), m.prefix_str(), this.wrap(t)), - FnDef(d, s) => f.debug_tuple("FnDef").field(d).field(&this.wrap(s)).finish(), - FnPtr(s) => write!(f, "{:?}", &this.wrap(s)), + Array(t, c) => write!(f, "[{:?}; {:?}]", &t, &c), + Pat(t, p) => write!(f, "pattern_type!({:?} is {:?})", &t, &p), + Slice(t) => write!(f, "[{:?}]", &t), + RawPtr(ty, mutbl) => write!(f, "*{} {:?}", mutbl.ptr_str(), ty), + Ref(r, t, m) => write!(f, "&{:?} {}{:?}", r, m.prefix_str(), t), + FnDef(d, s) => f.debug_tuple("FnDef").field(d).field(&s).finish(), + FnPtr(s) => write!(f, "{:?}", &s), Dynamic(p, r, repr) => match repr { - DynKind::Dyn => write!(f, "dyn {:?} + {:?}", &this.wrap(p), &this.wrap(r)), + DynKind::Dyn => write!(f, "dyn {:?} + {:?}", &p, &r), DynKind::DynStar => { - write!(f, "dyn* {:?} + {:?}", &this.wrap(p), &this.wrap(r)) + write!(f, "dyn* {:?} + {:?}", &p, &r) } }, - Closure(d, s) => f.debug_tuple("Closure").field(d).field(&this.wrap(s)).finish(), - CoroutineClosure(d, s) => { - f.debug_tuple("CoroutineClosure").field(d).field(&this.wrap(s)).finish() - } - Coroutine(d, s) => f.debug_tuple("Coroutine").field(d).field(&this.wrap(s)).finish(), - CoroutineWitness(d, s) => { - f.debug_tuple("CoroutineWitness").field(d).field(&this.wrap(s)).finish() - } + Closure(d, s) => f.debug_tuple("Closure").field(d).field(&s).finish(), + CoroutineClosure(d, s) => f.debug_tuple("CoroutineClosure").field(d).field(&s).finish(), + Coroutine(d, s) => f.debug_tuple("Coroutine").field(d).field(&s).finish(), + CoroutineWitness(d, s) => f.debug_tuple("CoroutineWitness").field(d).field(&s).finish(), Never => write!(f, "!"), Tuple(t) => { write!(f, "(")?; @@ -396,7 +392,7 @@ impl DebugWithInfcx for TyKind { if count > 0 { write!(f, ", ")?; } - write!(f, "{:?}", &this.wrap(ty))?; + write!(f, "{:?}", &ty)?; count += 1; } // unary tuples need a trailing comma @@ -405,23 +401,16 @@ impl DebugWithInfcx for TyKind { } write!(f, ")") } - Alias(i, a) => f.debug_tuple("Alias").field(i).field(&this.wrap(a)).finish(), + Alias(i, a) => f.debug_tuple("Alias").field(i).field(&a).finish(), Param(p) => write!(f, "{p:?}"), Bound(d, b) => crate::debug_bound_var(f, *d, b), Placeholder(p) => write!(f, "{p:?}"), - Infer(t) => write!(f, "{:?}", this.wrap(t)), + Infer(t) => write!(f, "{:?}", t), TyKind::Error(_) => write!(f, "{{type error}}"), } } } -// This is manually implemented because a derive would require `I: Debug` -impl fmt::Debug for TyKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} - /// Represents the projection of an associated, opaque, or lazy-type-alias type. /// /// * For a projection, this would be `>::N<...>`. @@ -433,7 +422,8 @@ impl fmt::Debug for TyKind { Copy(bound = ""), Hash(bound = ""), PartialEq(bound = ""), - Eq(bound = "") + Eq(bound = ""), + Debug(bound = "") )] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] @@ -462,8 +452,8 @@ pub struct AliasTy { /// aka. `interner.parent(def_id)`. pub def_id: I::DefId, - /// This field exists to prevent the creation of `AliasTy` without using - /// [AliasTy::new]. + /// This field exists to prevent the creation of `AliasTy` without using [`AliasTy::new`]. + #[derivative(Debug = "ignore")] pub(crate) _use_alias_ty_new_instead: (), } @@ -514,7 +504,7 @@ impl AliasTy { /// For example, if this is a projection of `::Item<'a>`, /// then this function would return a `T: StreamingIterator` trait reference and /// `['a]` as the own args. - pub fn trait_ref_and_own_args(self, interner: I) -> (TraitRef, I::OwnItemArgs) { + pub fn trait_ref_and_own_args(self, interner: I) -> (ty::TraitRef, I::GenericArgsSlice) { debug_assert_eq!(self.kind(interner), AliasTyKind::Projection); interner.trait_ref_and_own_args_for_alias(self.def_id, self.args) } @@ -526,7 +516,7 @@ impl AliasTy { /// WARNING: This will drop the args for generic associated types /// consider calling [Self::trait_ref_and_own_args] to get those /// as well. - pub fn trait_ref(self, interner: I) -> TraitRef { + pub fn trait_ref(self, interner: I) -> ty::TraitRef { self.trait_ref_and_own_args(interner).0 } } @@ -553,23 +543,6 @@ impl AliasTy { } } -impl fmt::Debug for AliasTy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} -impl DebugWithInfcx for AliasTy { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - f.debug_struct("AliasTy") - .field("args", &this.map(|data| data.args)) - .field("def_id", &this.data.def_id) - .finish() - } -} - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum IntTy { @@ -715,14 +688,44 @@ impl FloatTy { } } -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum IntVarValue { + Unknown, IntType(IntTy), UintType(UintTy), } -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct FloatVarValue(pub FloatTy); +impl IntVarValue { + pub fn is_known(self) -> bool { + match self { + IntVarValue::IntType(_) | IntVarValue::UintType(_) => true, + IntVarValue::Unknown => false, + } + } + + pub fn is_unknown(self) -> bool { + !self.is_known() + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum FloatVarValue { + Unknown, + Known(FloatTy), +} + +impl FloatVarValue { + pub fn is_known(self) -> bool { + match self { + FloatVarValue::Known(_) => true, + FloatVarValue::Unknown => false, + } + } + + pub fn is_unknown(self) -> bool { + !self.is_known() + } +} rustc_index::newtype_index! { /// A **ty**pe **v**ariable **ID**. @@ -807,11 +810,28 @@ impl UnifyKey for TyVid { } #[cfg(feature = "nightly")] -impl EqUnifyValue for IntVarValue {} +impl UnifyValue for IntVarValue { + type Error = NoError; + + fn unify_values(value1: &Self, value2: &Self) -> Result { + match (*value1, *value2) { + (IntVarValue::Unknown, IntVarValue::Unknown) => Ok(IntVarValue::Unknown), + ( + IntVarValue::Unknown, + known @ (IntVarValue::UintType(_) | IntVarValue::IntType(_)), + ) + | ( + known @ (IntVarValue::UintType(_) | IntVarValue::IntType(_)), + IntVarValue::Unknown, + ) => Ok(known), + _ => panic!("differing ints should have been resolved first"), + } + } +} #[cfg(feature = "nightly")] impl UnifyKey for IntVid { - type Value = Option; + type Value = IntVarValue; #[inline] // make this function eligible for inlining - it is quite hot. fn index(&self) -> u32 { self.as_u32() @@ -826,11 +846,26 @@ impl UnifyKey for IntVid { } #[cfg(feature = "nightly")] -impl EqUnifyValue for FloatVarValue {} +impl UnifyValue for FloatVarValue { + type Error = NoError; + + fn unify_values(value1: &Self, value2: &Self) -> Result { + match (*value1, *value2) { + (FloatVarValue::Unknown, FloatVarValue::Unknown) => Ok(FloatVarValue::Unknown), + (FloatVarValue::Unknown, FloatVarValue::Known(known)) + | (FloatVarValue::Known(known), FloatVarValue::Unknown) => { + Ok(FloatVarValue::Known(known)) + } + (FloatVarValue::Known(_), FloatVarValue::Known(_)) => { + panic!("differing floats should have been resolved first") + } + } + } +} #[cfg(feature = "nightly")] impl UnifyKey for FloatVid { - type Value = Option; + type Value = FloatVarValue; #[inline] fn index(&self) -> u32 { self.as_u32() @@ -858,21 +893,6 @@ impl HashStable for InferTy { } } -impl fmt::Debug for IntVarValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - IntVarValue::IntType(ref v) => v.fmt(f), - IntVarValue::UintType(ref v) => v.fmt(f), - } - } -} - -impl fmt::Debug for FloatVarValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - impl fmt::Display for InferTy { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use InferTy::*; @@ -919,24 +939,6 @@ impl fmt::Debug for InferTy { } } -impl DebugWithInfcx for InferTy { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - match this.data { - InferTy::TyVar(vid) => { - if let Some(universe) = this.infcx.universe_of_ty(*vid) { - write!(f, "?{}_{}t", vid.index(), universe.index()) - } else { - write!(f, "{:?}", this.data) - } - } - _ => write!(f, "{:?}", this.data), - } - } -} - #[derive(derivative::Derivative)] #[derivative( Clone(bound = ""), @@ -982,19 +984,54 @@ impl FnSig { pub fn output(self) -> I::Ty { self.split_inputs_and_output().1 } + + pub fn is_fn_trait_compatible(self) -> bool { + let FnSig { safety, abi, c_variadic, .. } = self; + !c_variadic && safety.is_safe() && abi.is_rust() + } +} + +impl ty::Binder> { + #[inline] + pub fn inputs(self) -> ty::Binder { + self.map_bound(|fn_sig| fn_sig.inputs()) + } + + #[inline] + #[track_caller] + pub fn input(self, index: usize) -> ty::Binder { + self.map_bound(|fn_sig| fn_sig.inputs()[index]) + } + + pub fn inputs_and_output(self) -> ty::Binder { + self.map_bound(|fn_sig| fn_sig.inputs_and_output) + } + + #[inline] + pub fn output(self) -> ty::Binder { + self.map_bound(|fn_sig| fn_sig.output()) + } + + pub fn c_variadic(self) -> bool { + self.skip_binder().c_variadic + } + + pub fn safety(self) -> I::Safety { + self.skip_binder().safety + } + + pub fn abi(self) -> I::Abi { + self.skip_binder().abi + } + + pub fn is_fn_trait_compatible(&self) -> bool { + self.skip_binder().is_fn_trait_compatible() + } } impl fmt::Debug for FnSig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - WithInfcx::with_no_infcx(self).fmt(f) - } -} -impl DebugWithInfcx for FnSig { - fn fmt>( - this: WithInfcx<'_, Infcx, &Self>, - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - let sig = this.data; + let sig = self; let FnSig { inputs_and_output: _, c_variadic, safety, abi } = sig; write!(f, "{}", safety.prefix_str())?; @@ -1008,7 +1045,7 @@ impl DebugWithInfcx for FnSig { if i > 0 { write!(f, ", ")?; } - write!(f, "{:?}", &this.wrap(ty))?; + write!(f, "{:?}", &ty)?; } if *c_variadic { if inputs.is_empty() { @@ -1021,7 +1058,7 @@ impl DebugWithInfcx for FnSig { match output.kind() { Tuple(list) if list.is_empty() => Ok(()), - _ => write!(f, " -> {:?}", &this.wrap(sig.output())), + _ => write!(f, " -> {:?}", sig.output()), } } } diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs new file mode 100644 index 000000000000..97752934632c --- /dev/null +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -0,0 +1,696 @@ +use std::ops::ControlFlow; + +use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; + +use crate::fold::{shift_region, TypeFoldable, TypeFolder, TypeSuperFoldable}; +use crate::inherent::*; +use crate::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use crate::{self as ty, Interner}; + +/// A closure can be modeled as a struct that looks like: +/// ```ignore (illustrative) +/// struct Closure<'l0...'li, T0...Tj, CK, CS, U>(...U); +/// ``` +/// where: +/// +/// - 'l0...'li and T0...Tj are the generic parameters +/// in scope on the function that defined the closure, +/// - CK represents the *closure kind* (Fn vs FnMut vs FnOnce). This +/// is rather hackily encoded via a scalar type. See +/// `Ty::to_opt_closure_kind` for details. +/// - CS represents the *closure signature*, representing as a `fn()` +/// type. For example, `fn(u32, u32) -> u32` would mean that the closure +/// implements `CK<(u32, u32), Output = u32>`, where `CK` is the trait +/// specified above. +/// - U is a type parameter representing the types of its upvars, tupled up +/// (borrowed, if appropriate; that is, if a U field represents a by-ref upvar, +/// and the up-var has the type `Foo`, then that field of U will be `&Foo`). +/// +/// So, for example, given this function: +/// ```ignore (illustrative) +/// fn foo<'a, T>(data: &'a mut T) { +/// do(|| data.count += 1) +/// } +/// ``` +/// the type of the closure would be something like: +/// ```ignore (illustrative) +/// struct Closure<'a, T, U>(...U); +/// ``` +/// Note that the type of the upvar is not specified in the struct. +/// You may wonder how the impl would then be able to use the upvar, +/// if it doesn't know it's type? The answer is that the impl is +/// (conceptually) not fully generic over Closure but rather tied to +/// instances with the expected upvar types: +/// ```ignore (illustrative) +/// impl<'b, 'a, T> FnMut() for Closure<'a, T, (&'b mut &'a mut T,)> { +/// ... +/// } +/// ``` +/// You can see that the *impl* fully specified the type of the upvar +/// and thus knows full well that `data` has type `&'b mut &'a mut T`. +/// (Here, I am assuming that `data` is mut-borrowed.) +/// +/// Now, the last question you may ask is: Why include the upvar types +/// in an extra type parameter? The reason for this design is that the +/// upvar types can reference lifetimes that are internal to the +/// creating function. In my example above, for example, the lifetime +/// `'b` represents the scope of the closure itself; this is some +/// subset of `foo`, probably just the scope of the call to the to +/// `do()`. If we just had the lifetime/type parameters from the +/// enclosing function, we couldn't name this lifetime `'b`. Note that +/// there can also be lifetimes in the types of the upvars themselves, +/// if one of them happens to be a reference to something that the +/// creating fn owns. +/// +/// OK, you say, so why not create a more minimal set of parameters +/// that just includes the extra lifetime parameters? The answer is +/// primarily that it would be hard --- we don't know at the time when +/// we create the closure type what the full types of the upvars are, +/// nor do we know which are borrowed and which are not. In this +/// design, we can just supply a fresh type parameter and figure that +/// out later. +/// +/// All right, you say, but why include the type parameters from the +/// original function then? The answer is that codegen may need them +/// when monomorphizing, and they may not appear in the upvars. A +/// closure could capture no variables but still make use of some +/// in-scope type parameter with a bound (e.g., if our example above +/// had an extra `U: Default`, and the closure called `U::default()`). +/// +/// There is another reason. This design (implicitly) prohibits +/// closures from capturing themselves (except via a trait +/// object). This simplifies closure inference considerably, since it +/// means that when we infer the kind of a closure or its upvars, we +/// don't have to handle cycles where the decisions we make for +/// closure C wind up influencing the decisions we ought to make for +/// closure C (which would then require fixed point iteration to +/// handle). Plus it fixes an ICE. :P +/// +/// ## Coroutines +/// +/// Coroutines are handled similarly in `CoroutineArgs`. The set of +/// type parameters is similar, but `CK` and `CS` are replaced by the +/// following type parameters: +/// +/// * `GS`: The coroutine's "resume type", which is the type of the +/// argument passed to `resume`, and the type of `yield` expressions +/// inside the coroutine. +/// * `GY`: The "yield type", which is the type of values passed to +/// `yield` inside the coroutine. +/// * `GR`: The "return type", which is the type of value returned upon +/// completion of the coroutine. +/// * `GW`: The "coroutine witness". +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Copy(bound = ""), + Hash(bound = ""), + PartialEq(bound = ""), + Eq(bound = ""), + Debug(bound = "") +)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +pub struct ClosureArgs { + /// Lifetime and type parameters from the enclosing function, + /// concatenated with a tuple containing the types of the upvars. + /// + /// These are separated out because codegen wants to pass them around + /// when monomorphizing. + pub args: I::GenericArgs, +} + +/// Struct returned by `split()`. +pub struct ClosureArgsParts { + /// This is the args of the typeck root. + pub parent_args: I::GenericArgsSlice, + /// Represents the maximum calling capability of the closure. + pub closure_kind_ty: I::Ty, + /// Captures the closure's signature. This closure signature is "tupled", and + /// thus has a peculiar signature of `extern "rust-call" fn((Args, ...)) -> Ty`. + pub closure_sig_as_fn_ptr_ty: I::Ty, + /// The upvars captured by the closure. Remains an inference variable + /// until the upvar analysis, which happens late in HIR typeck. + pub tupled_upvars_ty: I::Ty, +} + +impl ClosureArgs { + /// Construct `ClosureArgs` from `ClosureArgsParts`, containing `Args` + /// for the closure parent, alongside additional closure-specific components. + pub fn new(tcx: I, parts: ClosureArgsParts) -> ClosureArgs { + ClosureArgs { + args: tcx.mk_args_from_iter(parts.parent_args.iter().copied().chain([ + parts.closure_kind_ty.into(), + parts.closure_sig_as_fn_ptr_ty.into(), + parts.tupled_upvars_ty.into(), + ])), + } + } + + /// Divides the closure args into their respective components. + /// The ordering assumed here must match that used by `ClosureArgs::new` above. + fn split(self) -> ClosureArgsParts { + self.args.split_closure_args() + } + + /// Returns the generic parameters of the closure's parent. + pub fn parent_args(self) -> I::GenericArgsSlice { + self.split().parent_args + } + + /// Returns an iterator over the list of types of captured paths by the closure. + /// In case there was a type error in figuring out the types of the captured path, an + /// empty iterator is returned. + #[inline] + pub fn upvar_tys(self) -> I::Tys { + match self.tupled_upvars_ty().kind() { + ty::Error(_) => Default::default(), + ty::Tuple(tys) => tys, + ty::Infer(_) => panic!("upvar_tys called before capture types are inferred"), + ty => panic!("Unexpected representation of upvar types tuple {:?}", ty), + } + } + + /// Returns the tuple type representing the upvars for this closure. + #[inline] + pub fn tupled_upvars_ty(self) -> I::Ty { + self.split().tupled_upvars_ty + } + + /// 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(args)`. + pub fn kind_ty(self) -> I::Ty { + self.split().closure_kind_ty + } + + /// Returns the `fn` pointer type representing the closure signature for this + /// closure. + // FIXME(eddyb) this should be unnecessary, as the shallowly resolved + // type is known at the time of the creation of `ClosureArgs`, + // see `rustc_hir_analysis::check::closure`. + pub fn sig_as_fn_ptr_ty(self) -> I::Ty { + self.split().closure_sig_as_fn_ptr_ty + } + + /// Returns the closure kind for this closure; only usable outside + /// of an inference context, because in that context we know that + /// there are no type variables. + /// + /// If you have an inference context, use `infcx.closure_kind()`. + pub fn kind(self) -> ty::ClosureKind { + self.kind_ty().to_opt_closure_kind().unwrap() + } + + /// Extracts the signature from the closure. + pub fn sig(self) -> ty::Binder> { + match self.sig_as_fn_ptr_ty().kind() { + ty::FnPtr(sig) => sig, + ty => panic!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {ty:?}"), + } + } +} + +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Copy(bound = ""), + Hash(bound = ""), + PartialEq(bound = ""), + Eq(bound = ""), + Debug(bound = "") +)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +pub struct CoroutineClosureArgs { + pub args: I::GenericArgs, +} + +/// See docs for explanation of how each argument is used. +/// +/// See [`CoroutineClosureSignature`] for how these arguments are put together +/// to make a callable [`ty::FnSig`] suitable for typeck and borrowck. +pub struct CoroutineClosureArgsParts { + /// This is the args of the typeck root. + pub parent_args: I::GenericArgsSlice, + /// Represents the maximum calling capability of the closure. + pub closure_kind_ty: I::Ty, + /// Represents all of the relevant parts of the coroutine returned by this + /// coroutine-closure. This signature parts type will have the general + /// shape of `fn(tupled_inputs, resume_ty) -> (return_ty, yield_ty)`, where + /// `resume_ty`, `return_ty`, and `yield_ty` are the respective types for the + /// coroutine returned by the coroutine-closure. + /// + /// Use `coroutine_closure_sig` to break up this type rather than using it + /// yourself. + pub signature_parts_ty: I::Ty, + /// The upvars captured by the closure. Remains an inference variable + /// until the upvar analysis, which happens late in HIR typeck. + pub tupled_upvars_ty: I::Ty, + /// a function pointer that has the shape `for<'env> fn() -> (&'env T, ...)`. + /// This allows us to represent the binder of the self-captures of the closure. + /// + /// For example, if the coroutine returned by the closure borrows `String` + /// from the closure's upvars, this will be `for<'env> fn() -> (&'env String,)`, + /// while the `tupled_upvars_ty`, representing the by-move version of the same + /// captures, will be `(String,)`. + pub coroutine_captures_by_ref_ty: I::Ty, + /// Witness type returned by the generator produced by this coroutine-closure. + pub coroutine_witness_ty: I::Ty, +} + +impl CoroutineClosureArgs { + pub fn new(tcx: I, parts: CoroutineClosureArgsParts) -> CoroutineClosureArgs { + CoroutineClosureArgs { + args: tcx.mk_args_from_iter(parts.parent_args.iter().copied().chain([ + parts.closure_kind_ty.into(), + parts.signature_parts_ty.into(), + parts.tupled_upvars_ty.into(), + parts.coroutine_captures_by_ref_ty.into(), + parts.coroutine_witness_ty.into(), + ])), + } + } + + fn split(self) -> CoroutineClosureArgsParts { + self.args.split_coroutine_closure_args() + } + + pub fn parent_args(self) -> I::GenericArgsSlice { + self.split().parent_args + } + + #[inline] + pub fn upvar_tys(self) -> I::Tys { + match self.tupled_upvars_ty().kind() { + ty::Error(_) => Default::default(), + ty::Tuple(..) => self.tupled_upvars_ty().tuple_fields(), + ty::Infer(_) => panic!("upvar_tys called before capture types are inferred"), + ty => panic!("Unexpected representation of upvar types tuple {:?}", ty), + } + } + + #[inline] + pub fn tupled_upvars_ty(self) -> I::Ty { + self.split().tupled_upvars_ty + } + + pub fn kind_ty(self) -> I::Ty { + self.split().closure_kind_ty + } + + pub fn kind(self) -> ty::ClosureKind { + self.kind_ty().to_opt_closure_kind().unwrap() + } + + pub fn signature_parts_ty(self) -> I::Ty { + self.split().signature_parts_ty + } + + pub fn coroutine_closure_sig(self) -> ty::Binder> { + let interior = self.coroutine_witness_ty(); + let ty::FnPtr(sig) = self.signature_parts_ty().kind() else { panic!() }; + sig.map_bound(|sig| { + let [resume_ty, tupled_inputs_ty] = *sig.inputs() else { + panic!(); + }; + let [yield_ty, return_ty] = **sig.output().tuple_fields() else { panic!() }; + CoroutineClosureSignature { + interior, + tupled_inputs_ty, + resume_ty, + yield_ty, + return_ty, + c_variadic: sig.c_variadic, + safety: sig.safety, + abi: sig.abi, + } + }) + } + + pub fn coroutine_captures_by_ref_ty(self) -> I::Ty { + self.split().coroutine_captures_by_ref_ty + } + + pub fn coroutine_witness_ty(self) -> I::Ty { + self.split().coroutine_witness_ty + } + + pub fn has_self_borrows(&self) -> bool { + match self.coroutine_captures_by_ref_ty().kind() { + ty::FnPtr(sig) => sig + .skip_binder() + .visit_with(&mut HasRegionsBoundAt { binder: ty::INNERMOST }) + .is_break(), + ty::Error(_) => true, + _ => panic!(), + } + } +} + +/// Unlike `has_escaping_bound_vars` or `outermost_exclusive_binder`, this will +/// detect only regions bound *at* the debruijn index. +struct HasRegionsBoundAt { + binder: ty::DebruijnIndex, +} +// FIXME: Could be optimized to not walk into components with no escaping bound vars. +impl TypeVisitor for HasRegionsBoundAt { + type Result = ControlFlow<()>; + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { + self.binder.shift_in(1); + t.super_visit_with(self)?; + self.binder.shift_out(1); + ControlFlow::Continue(()) + } + + fn visit_region(&mut self, r: I::Region) -> Self::Result { + if matches!(r.kind(), ty::ReBound(binder, _) if self.binder == binder) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + } +} + +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Copy(bound = ""), + Hash(bound = ""), + PartialEq(bound = ""), + Eq(bound = ""), + Debug(bound = "") +)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +pub struct CoroutineClosureSignature { + pub interior: I::Ty, + pub tupled_inputs_ty: I::Ty, + pub resume_ty: I::Ty, + pub yield_ty: I::Ty, + pub return_ty: I::Ty, + + // Like the `fn_sig_as_fn_ptr_ty` of a regular closure, these types + // never actually differ. But we save them rather than recreating them + // from scratch just for good measure. + /// Always false + pub c_variadic: bool, + /// Always `Normal` (safe) + pub safety: I::Safety, + /// Always `RustCall` + pub abi: I::Abi, +} + +impl CoroutineClosureSignature { + /// Construct a coroutine from the closure signature. Since a coroutine signature + /// is agnostic to the type of generator that is returned (by-ref/by-move), + /// the caller must specify what "flavor" of generator that they'd like to + /// create. Additionally, they must manually compute the upvars of the closure. + /// + /// This helper is not really meant to be used directly except for early on + /// during typeck, when we want to put inference vars into the kind and upvars tys. + /// When the kind and upvars are known, use the other helper functions. + pub fn to_coroutine( + self, + tcx: I, + parent_args: I::GenericArgsSlice, + coroutine_kind_ty: I::Ty, + coroutine_def_id: I::DefId, + tupled_upvars_ty: I::Ty, + ) -> I::Ty { + let coroutine_args = ty::CoroutineArgs::new( + tcx, + ty::CoroutineArgsParts { + parent_args, + kind_ty: coroutine_kind_ty, + resume_ty: self.resume_ty, + yield_ty: self.yield_ty, + return_ty: self.return_ty, + witness: self.interior, + tupled_upvars_ty, + }, + ); + + Ty::new_coroutine(tcx, coroutine_def_id, coroutine_args.args) + } + + /// Given known upvars and a [`ClosureKind`](ty::ClosureKind), compute the coroutine + /// returned by that corresponding async fn trait. + /// + /// This function expects the upvars to have been computed already, and doesn't check + /// that the `ClosureKind` is actually supported by the coroutine-closure. + pub fn to_coroutine_given_kind_and_upvars( + self, + tcx: I, + parent_args: I::GenericArgsSlice, + coroutine_def_id: I::DefId, + goal_kind: ty::ClosureKind, + env_region: I::Region, + closure_tupled_upvars_ty: I::Ty, + coroutine_captures_by_ref_ty: I::Ty, + ) -> I::Ty { + let tupled_upvars_ty = Self::tupled_upvars_by_closure_kind( + tcx, + goal_kind, + self.tupled_inputs_ty, + closure_tupled_upvars_ty, + coroutine_captures_by_ref_ty, + env_region, + ); + + self.to_coroutine( + tcx, + parent_args, + Ty::from_coroutine_closure_kind(tcx, goal_kind), + coroutine_def_id, + tupled_upvars_ty, + ) + } + + /// Compute the tupled upvars that a coroutine-closure's output coroutine + /// would return for the given `ClosureKind`. + /// + /// When `ClosureKind` is `FnMut`/`Fn`, then this will use the "captures by ref" + /// to return a set of upvars which are borrowed with the given `env_region`. + /// + /// This ensures that the `AsyncFn::call` will return a coroutine whose upvars' + /// lifetimes are related to the lifetime of the borrow on the closure made for + /// the call. This allows borrowck to enforce the self-borrows correctly. + pub fn tupled_upvars_by_closure_kind( + tcx: I, + kind: ty::ClosureKind, + tupled_inputs_ty: I::Ty, + closure_tupled_upvars_ty: I::Ty, + coroutine_captures_by_ref_ty: I::Ty, + env_region: I::Region, + ) -> I::Ty { + match kind { + ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { + let ty::FnPtr(sig) = coroutine_captures_by_ref_ty.kind() else { + panic!(); + }; + let coroutine_captures_by_ref_ty = + sig.output().skip_binder().fold_with(&mut FoldEscapingRegions { + interner: tcx, + region: env_region, + debruijn: ty::INNERMOST, + }); + Ty::new_tup_from_iter( + tcx, + tupled_inputs_ty + .tuple_fields() + .into_iter() + .chain(coroutine_captures_by_ref_ty.tuple_fields()), + ) + } + ty::ClosureKind::FnOnce => Ty::new_tup_from_iter( + tcx, + tupled_inputs_ty + .tuple_fields() + .into_iter() + .chain(closure_tupled_upvars_ty.tuple_fields()), + ), + } + } +} + +/// Instantiates a `for<'env> ...` binder with a specific region. +// FIXME(async_closures): Get rid of this in favor of `BoundVarReplacerDelegate` +// when that is uplifted. +struct FoldEscapingRegions { + interner: I, + debruijn: ty::DebruijnIndex, + region: I::Region, +} + +impl TypeFolder for FoldEscapingRegions { + fn interner(&self) -> I { + self.interner + } + + fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder + where + T: TypeFoldable, + { + self.debruijn.shift_in(1); + let result = t.super_fold_with(self); + self.debruijn.shift_out(1); + result + } + + fn fold_region(&mut self, r: ::Region) -> ::Region { + if let ty::ReBound(debruijn, _) = r.kind() { + assert!( + debruijn <= self.debruijn, + "cannot instantiate binder with escaping bound vars" + ); + if self.debruijn == debruijn { + shift_region(self.interner, self.region, self.debruijn.as_u32()) + } else { + r + } + } else { + r + } + } +} + +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Copy(bound = ""), + Hash(bound = ""), + PartialEq(bound = ""), + Eq(bound = ""), + Debug(bound = "") +)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +pub struct GenSig { + pub resume_ty: I::Ty, + pub yield_ty: I::Ty, + pub return_ty: I::Ty, +} + +/// Similar to `ClosureArgs`; see the above documentation for more. +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = ""), + Copy(bound = ""), + Hash(bound = ""), + PartialEq(bound = ""), + Eq(bound = ""), + Debug(bound = "") +)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +pub struct CoroutineArgs { + pub args: I::GenericArgs, +} + +pub struct CoroutineArgsParts { + /// This is the args of the typeck root. + pub parent_args: I::GenericArgsSlice, + + /// The coroutines returned by a coroutine-closure's `AsyncFnOnce`/`AsyncFnMut` + /// implementations must be distinguished since the former takes the closure's + /// upvars by move, and the latter takes the closure's upvars by ref. + /// + /// This field distinguishes these fields so that codegen can select the right + /// body for the coroutine. This has the same type representation as the closure + /// kind: `i8`/`i16`/`i32`. + /// + /// For regular coroutines, this field will always just be `()`. + pub kind_ty: I::Ty, + + pub resume_ty: I::Ty, + pub yield_ty: I::Ty, + pub return_ty: I::Ty, + + /// The interior type of the coroutine. + /// Represents all types that are stored in locals + /// in the coroutine's body. + pub witness: I::Ty, + + /// The upvars captured by the closure. Remains an inference variable + /// until the upvar analysis, which happens late in HIR typeck. + pub tupled_upvars_ty: I::Ty, +} + +impl CoroutineArgs { + /// Construct `CoroutineArgs` from `CoroutineArgsParts`, containing `Args` + /// for the coroutine parent, alongside additional coroutine-specific components. + pub fn new(tcx: I, parts: CoroutineArgsParts) -> CoroutineArgs { + CoroutineArgs { + args: tcx.mk_args_from_iter(parts.parent_args.iter().copied().chain([ + parts.kind_ty.into(), + parts.resume_ty.into(), + parts.yield_ty.into(), + parts.return_ty.into(), + parts.witness.into(), + parts.tupled_upvars_ty.into(), + ])), + } + } + + /// Divides the coroutine args into their respective components. + /// The ordering assumed here must match that used by `CoroutineArgs::new` above. + fn split(self) -> CoroutineArgsParts { + self.args.split_coroutine_args() + } + + /// Returns the generic parameters of the coroutine's parent. + pub fn parent_args(self) -> I::GenericArgsSlice { + self.split().parent_args + } + + // Returns the kind of the coroutine. See docs on the `kind_ty` field. + pub fn kind_ty(self) -> I::Ty { + self.split().kind_ty + } + + /// This describes the types that can be contained in a coroutine. + /// It will be a type variable initially and unified in the last stages of typeck of a body. + /// It contains a tuple of all the types that could end up on a coroutine frame. + /// The state transformation MIR pass may only produce layouts which mention types + /// in this tuple. Upvars are not counted here. + pub fn witness(self) -> I::Ty { + self.split().witness + } + + /// Returns an iterator over the list of types of captured paths by the coroutine. + /// In case there was a type error in figuring out the types of the captured path, an + /// empty iterator is returned. + #[inline] + pub fn upvar_tys(self) -> I::Tys { + match self.tupled_upvars_ty().kind() { + ty::Error(_) => Default::default(), + ty::Tuple(tys) => tys, + ty::Infer(_) => panic!("upvar_tys called before capture types are inferred"), + ty => panic!("Unexpected representation of upvar types tuple {:?}", ty), + } + } + + /// Returns the tuple type representing the upvars for this coroutine. + #[inline] + pub fn tupled_upvars_ty(self) -> I::Ty { + self.split().tupled_upvars_ty + } + + /// Returns the type representing the resume type of the coroutine. + pub fn resume_ty(self) -> I::Ty { + self.split().resume_ty + } + + /// Returns the type representing the yield type of the coroutine. + pub fn yield_ty(self) -> I::Ty { + self.split().yield_ty + } + + /// Returns the type representing the return type of the coroutine. + pub fn return_ty(self) -> I::Ty { + self.split().return_ty + } + + /// Returns the "coroutine signature", which consists of its resume, yield + /// and return types. + pub fn sig(self) -> GenSig { + let parts = self.split(); + GenSig { resume_ty: parts.resume_ty, yield_ty: parts.yield_ty, return_ty: parts.return_ty } + } +} diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 3d4125f600ef..6880c7b8cefc 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -90,7 +90,7 @@ pub trait TypeVisitor: Sized { #[cfg(not(feature = "nightly"))] type Result: VisitorResult; - fn visit_binder>(&mut self, t: &I::Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { t.super_visit_with(self) } @@ -376,11 +376,11 @@ impl std::fmt::Debug for HasTypeFlagsVisitor { impl TypeVisitor for HasTypeFlagsVisitor { type Result = ControlFlow; - fn visit_binder>(&mut self, t: &I::Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { // If we're looking for the HAS_BINDER_VARS flag, check if the // binder has vars. This won't be present in the binder's bound // value, so we need to check here too. - if self.flags.intersects(TypeFlags::HAS_BINDER_VARS) && !t.has_no_bound_vars() { + if self.flags.intersects(TypeFlags::HAS_BINDER_VARS) && !t.bound_vars().is_empty() { return ControlFlow::Break(FoundFlags); } @@ -476,7 +476,7 @@ struct HasEscapingVarsVisitor { impl TypeVisitor for HasEscapingVarsVisitor { type Result = ControlFlow; - fn visit_binder>(&mut self, t: &I::Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { self.outer_index.shift_in(1); let result = t.super_visit_with(self); self.outer_index.shift_out(1); diff --git a/compiler/stable_mir/Cargo.toml b/compiler/stable_mir/Cargo.toml index c61e217bf9f0..4ed611527365 100644 --- a/compiler/stable_mir/Cargo.toml +++ b/compiler/stable_mir/Cargo.toml @@ -4,5 +4,4 @@ version = "0.1.0-preview" edition = "2021" [dependencies] -tracing = "0.1" scoped-tls = "1.0" diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index 231d9ba49a39..3e138e3c2e04 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -8,13 +8,13 @@ use std::cell::Cell; use crate::abi::{FnAbi, Layout, LayoutShape}; use crate::mir::alloc::{AllocId, GlobalAlloc}; use crate::mir::mono::{Instance, InstanceDef, StaticDef}; -use crate::mir::{BinOp, Body, Place}; +use crate::mir::{BinOp, Body, Place, UnOp}; use crate::target::MachineInfo; use crate::ty::{ - AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, + AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, FieldDef, FnDef, ForeignDef, ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics, - ImplDef, ImplTrait, LineInfo, PolyFnSig, RigidTy, Span, TraitDecl, TraitDef, Ty, TyKind, - UintTy, VariantDef, + ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, TraitDecl, + TraitDef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef, }; use crate::{ mir, Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls, ItemKind, @@ -88,6 +88,12 @@ pub trait Context { /// Retrieve the function signature for the given generic arguments. fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig; + /// Retrieve the intrinsic definition if the item corresponds one. + fn intrinsic(&self, item: DefId) -> Option; + + /// Retrieve the plain function name of an intrinsic. + fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol; + /// Retrieve the closure signature for the given generic arguments. fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig; @@ -99,19 +105,21 @@ pub trait Context { fn variant_fields(&self, def: VariantDef) -> Vec; /// Evaluate constant as a target usize. - fn eval_target_usize(&self, cnst: &Const) -> Result; + fn eval_target_usize(&self, cnst: &MirConst) -> Result; + fn eval_target_usize_ty(&self, cnst: &TyConst) -> Result; /// Create a new zero-sized constant. - fn try_new_const_zst(&self, ty: Ty) -> Result; + fn try_new_const_zst(&self, ty: Ty) -> Result; /// Create a new constant that represents the given string value. - fn new_const_str(&self, value: &str) -> Const; + fn new_const_str(&self, value: &str) -> MirConst; /// Create a new constant that represents the given boolean value. - fn new_const_bool(&self, value: bool) -> Const; + fn new_const_bool(&self, value: bool) -> MirConst; /// Create a new constant that represents the given value. - fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result; + fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result; + fn try_new_ty_const_uint(&self, value: u128, uint_ty: UintTy) -> Result; /// Create a new type from the given kind. fn new_rigid_ty(&self, kind: RigidTy) -> Ty; @@ -126,11 +134,13 @@ pub trait Context { fn def_ty_with_args(&self, item: DefId, args: &GenericArgs) -> Ty; /// Returns literal value of a const as a string. - fn const_pretty(&self, cnst: &Const) -> String; + fn mir_const_pretty(&self, cnst: &MirConst) -> String; /// `Span` of an item fn span_of_an_item(&self, def_id: DefId) -> Span; + fn ty_const_pretty(&self, ct: TyConstId) -> String; + /// Obtain the representation of a type. fn ty_pretty(&self, ty: Ty) -> String; @@ -198,7 +208,6 @@ pub trait Context { fn vtable_allocation(&self, global_alloc: &GlobalAlloc) -> Option; fn krate(&self, def_id: DefId) -> Crate; fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol; - fn intrinsic_name(&self, def: InstanceDef) -> Symbol; /// Return information about the target machine. fn target_info(&self) -> MachineInfo; @@ -217,6 +226,9 @@ pub trait Context { /// Get the resulting type of binary operation. fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty; + + /// Get the resulting type of unary operation. + fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty; } // A thread local variable that stores a pointer to the tables mapping between TyCtxt diff --git a/compiler/stable_mir/src/crate_def.rs b/compiler/stable_mir/src/crate_def.rs index 70ca9e6825e1..67752a5e629f 100644 --- a/compiler/stable_mir/src/crate_def.rs +++ b/compiler/stable_mir/src/crate_def.rs @@ -1,7 +1,7 @@ //! Module that define a common trait for things that represent a crate definition, //! such as, a function, a trait, an enum, and any other definitions. -use crate::ty::Span; +use crate::ty::{GenericArgs, Span, Ty}; use crate::{with, Crate, Symbol}; /// A unique identification number for each item accessible for the current compilation unit. @@ -52,6 +52,23 @@ pub trait CrateDef { } } +/// A trait that can be used to retrieve a definition's type. +/// +/// Note that not every CrateDef has a type `Ty`. They should not implement this trait. +pub trait CrateDefType: CrateDef { + /// Returns the type of this crate item. + fn ty(&self) -> Ty { + with(|cx| cx.def_ty(self.def_id())) + } + + /// Retrieve the type of this definition by instantiating and normalizing it with `args`. + /// + /// This will panic if instantiation fails. + fn ty_with_args(&self, args: &GenericArgs) -> Ty { + with(|cx| cx.def_ty_with_args(self.def_id(), args)) + } +} + macro_rules! crate_def { ( $(#[$attr:meta])* $vis:vis $name:ident $(;)? @@ -67,3 +84,21 @@ macro_rules! crate_def { } }; } + +macro_rules! crate_def_with_ty { + ( $(#[$attr:meta])* + $vis:vis $name:ident $(;)? + ) => { + $(#[$attr])* + #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] + $vis struct $name(pub DefId); + + impl CrateDef for $name { + fn def_id(&self) -> DefId { + self.0 + } + } + + impl CrateDefType for $name {} + }; +} diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index d9f988935ab1..8385856ae532 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -22,8 +22,7 @@ use std::fmt::Debug; use std::io; use crate::compiler_interface::with; -pub use crate::crate_def::CrateDef; -pub use crate::crate_def::DefId; +pub use crate::crate_def::{CrateDef, CrateDefType, DefId}; pub use crate::error::*; use crate::mir::Body; use crate::mir::Mutability; @@ -115,12 +114,15 @@ pub enum CtorKind { pub type Filename = String; -crate_def! { +crate_def_with_ty! { /// Holds information about an item in a crate. pub CrateItem; } impl CrateItem { + /// This will return the body of an item. + /// + /// This will panic if no body is available. pub fn body(&self) -> mir::Body { with(|cx| cx.mir_body(self.0)) } diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index a1432acf93cb..e0f9e7ae67a0 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -1,8 +1,8 @@ use crate::compiler_interface::with; use crate::mir::pretty::function_body; use crate::ty::{ - AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, RigidTy, Ty, TyKind, - VariantIdx, + AdtDef, ClosureDef, CoroutineDef, GenericArgs, MirConst, Movability, Region, RigidTy, Ty, + TyConst, TyKind, VariantIdx, }; use crate::{Error, Opaque, Span, Symbol}; use std::io; @@ -346,6 +346,15 @@ impl BinOp { pub enum UnOp { Not, Neg, + PtrMetadata, +} + +impl UnOp { + /// Return the type of this operation for the given input Ty. + /// This function does not perform type checking, and it currently doesn't handle SIMD. + pub fn ty(&self, arg_ty: Ty) -> Ty { + with(|ctx| ctx.unop_ty(*self, arg_ty)) + } } #[derive(Clone, Debug, Eq, PartialEq)] @@ -515,7 +524,7 @@ pub enum Rvalue { /// Corresponds to source code like `[x; 32]`. /// /// [#74836]: https://github.com/rust-lang/rust/issues/74836 - Repeat(Operand, Const), + Repeat(Operand, TyConst), /// Transmutes a `*mut u8` into shallow-initialized `Box`. /// @@ -580,7 +589,10 @@ impl Rvalue { let ty = op.ty(lhs_ty, rhs_ty); Ok(Ty::new_tuple(&[ty, Ty::bool_ty()])) } - Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, operand) => operand.ty(locals), + Rvalue::UnaryOp(op, operand) => { + let arg_ty = operand.ty(locals)?; + Ok(op.ty(arg_ty)) + } Rvalue::Discriminant(place) => { let place_ty = place.ty(locals)?; place_ty @@ -625,7 +637,7 @@ pub enum AggregateKind { pub enum Operand { Copy(Place), Move(Place), - Constant(Constant), + Constant(ConstOperand), } #[derive(Clone, Eq, PartialEq)] @@ -641,6 +653,13 @@ impl From for Place { } } +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ConstOperand { + pub span: Span, + pub user_ty: Option, + pub const_: MirConst, +} + /// Debug information pertaining to a user variable. #[derive(Clone, Debug, Eq, PartialEq)] pub struct VarDebugInfo { @@ -702,13 +721,6 @@ pub enum VarDebugInfoContents { Const(ConstOperand), } -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ConstOperand { - pub span: Span, - pub user_ty: Option, - pub const_: Const, -} - // In MIR ProjectionElem is parameterized on the second Field argument and the Index argument. This // is so it can be used for both Places (for which the projection elements are of type // ProjectionElem) and user-provided type annotations (for which the projection elements @@ -817,13 +829,6 @@ pub type FieldIdx = usize; type UserTypeAnnotationIndex = usize; -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Constant { - pub span: Span, - pub user_ty: Option, - pub literal: Const, -} - /// The possible branch sites of a [TerminatorKind::SwitchInt]. #[derive(Clone, Debug, Eq, PartialEq)] pub struct SwitchTargets { @@ -989,9 +994,9 @@ impl Operand { } } -impl Constant { +impl ConstOperand { pub fn ty(&self) -> Ty { - self.literal.ty() + self.const_.ty() } } diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs index 394038926f61..572f1499c5a5 100644 --- a/compiler/stable_mir/src/mir/mono.rs +++ b/compiler/stable_mir/src/mir/mono.rs @@ -106,7 +106,9 @@ impl Instance { /// which is more convenient to match with intrinsic symbols. pub fn intrinsic_name(&self) -> Option { match self.kind { - InstanceKind::Intrinsic => Some(with(|context| context.intrinsic_name(self.def))), + InstanceKind::Intrinsic => { + Some(with(|context| context.intrinsic(self.def.def_id()).unwrap().fn_name())) + } InstanceKind::Item | InstanceKind::Virtual { .. } | InstanceKind::Shim => None, } } diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs index bbca3965852a..83734a0d1382 100644 --- a/compiler/stable_mir/src/mir/pretty.rs +++ b/compiler/stable_mir/src/mir/pretty.rs @@ -1,5 +1,5 @@ use crate::mir::{Operand, Place, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents}; -use crate::ty::{Const, IndexedVal, Ty}; +use crate::ty::{IndexedVal, MirConst, Ty, TyConst}; use crate::{with, Body, Mutability}; use fmt::{Display, Formatter}; use std::fmt::Debug; @@ -46,7 +46,7 @@ pub(crate) fn function_body(writer: &mut W, body: &Body, name: &str) - VarDebugInfoContents::Place(place) => { format!("{place:?}") } - VarDebugInfoContents::Const(constant) => pretty_const(&constant.const_), + VarDebugInfoContents::Const(constant) => pretty_mir_const(&constant.const_), }; writeln!(writer, " debug {} => {};", info.name, content) })?; @@ -156,7 +156,7 @@ fn pretty_terminator(writer: &mut W, terminator: &TerminatorKind) -> i fn pretty_terminator_head(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> { use self::TerminatorKind::*; - const INDENT: &'static str = " "; + const INDENT: &str = " "; match terminator { Goto { .. } => write!(writer, "{INDENT}goto"), SwitchInt { discr, .. } => { @@ -310,12 +310,16 @@ fn pretty_operand(operand: &Operand) -> String { Operand::Move(mv) => { format!("move {:?}", mv) } - Operand::Constant(cnst) => pretty_const(&cnst.literal), + Operand::Constant(cnst) => pretty_mir_const(&cnst.const_), } } -fn pretty_const(literal: &Const) -> String { - with(|cx| cx.const_pretty(&literal)) +fn pretty_mir_const(literal: &MirConst) -> String { + with(|cx| cx.mir_const_pretty(literal)) +} + +fn pretty_ty_const(ct: &TyConst) -> String { + with(|cx| cx.ty_const_pretty(ct.id)) } fn pretty_rvalue(writer: &mut W, rval: &Rvalue) -> io::Result<()> { @@ -359,7 +363,7 @@ fn pretty_rvalue(writer: &mut W, rval: &Rvalue) -> io::Result<()> { write!(writer, "{kind}{:?}", place) } Rvalue::Repeat(op, cnst) => { - write!(writer, "{} \" \" {}", &pretty_operand(op), cnst.ty()) + write!(writer, "{} \" \" {}", &pretty_operand(op), &pretty_ty_const(cnst)) } Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::ThreadLocalRef(item) => { diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs index 24296e9e8778..50d7bae21db7 100644 --- a/compiler/stable_mir/src/mir/visit.rs +++ b/compiler/stable_mir/src/mir/visit.rs @@ -36,7 +36,7 @@ //! variant argument) that does not require visiting. use crate::mir::*; -use crate::ty::{Const, GenericArgs, Region, Ty}; +use crate::ty::{GenericArgs, MirConst, Region, Ty, TyConst}; use crate::{Error, Opaque, Span}; pub trait MirVisitor { @@ -108,12 +108,17 @@ pub trait MirVisitor { self.super_ty(ty) } - fn visit_constant(&mut self, constant: &Constant, location: Location) { - self.super_constant(constant, location) + fn visit_const_operand(&mut self, constant: &ConstOperand, location: Location) { + self.super_const_operand(constant, location) } - fn visit_const(&mut self, constant: &Const, location: Location) { - self.super_const(constant, location) + fn visit_mir_const(&mut self, constant: &MirConst, location: Location) { + self.super_mir_const(constant, location) + } + + fn visit_ty_const(&mut self, constant: &TyConst, location: Location) { + let _ = location; + self.super_ty_const(constant) } fn visit_region(&mut self, region: &Region, location: Location) { @@ -339,7 +344,7 @@ pub trait MirVisitor { } Rvalue::Repeat(op, constant) => { self.visit_operand(op, location); - self.visit_const(constant, location); + self.visit_ty_const(constant, location); } Rvalue::ShallowInitBox(op, ty) => { self.visit_ty(ty, location); @@ -361,7 +366,7 @@ pub trait MirVisitor { self.visit_place(place, PlaceContext::NON_MUTATING, location) } Operand::Constant(constant) => { - self.visit_constant(constant, location); + self.visit_const_operand(constant, location); } } } @@ -375,17 +380,21 @@ pub trait MirVisitor { let _ = ty; } - fn super_constant(&mut self, constant: &Constant, location: Location) { - let Constant { span, user_ty: _, literal } = constant; + fn super_const_operand(&mut self, constant: &ConstOperand, location: Location) { + let ConstOperand { span, user_ty: _, const_ } = constant; self.visit_span(span); - self.visit_const(literal, location); + self.visit_mir_const(const_, location); } - fn super_const(&mut self, constant: &Const, location: Location) { - let Const { kind: _, ty, id: _ } = constant; + fn super_mir_const(&mut self, constant: &MirConst, location: Location) { + let MirConst { kind: _, ty, id: _ } = constant; self.visit_ty(ty, location); } + fn super_ty_const(&mut self, constant: &TyConst) { + let _ = constant; + } + fn super_region(&mut self, region: &Region) { let _ = region; } @@ -407,7 +416,7 @@ pub trait MirVisitor { self.visit_place(place, PlaceContext::NON_USE, location); } VarDebugInfoContents::Const(constant) => { - self.visit_const(&constant.const_, location); + self.visit_mir_const(&constant.const_, location); } } } diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 759e3f166bda..35927237281a 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -3,9 +3,10 @@ use super::{ with, DefId, Error, Symbol, }; use crate::abi::Layout; +use crate::crate_def::{CrateDef, CrateDefType}; use crate::mir::alloc::{read_target_int, read_target_uint, AllocId}; +use crate::mir::mono::StaticDef; use crate::target::MachineInfo; -use crate::{crate_def::CrateDef, mir::mono::StaticDef}; use crate::{Filename, Opaque}; use std::fmt::{self, Debug, Display, Formatter}; use std::ops::Range; @@ -28,11 +29,11 @@ impl Ty { /// Create a new array type. pub fn try_new_array(elem_ty: Ty, size: u64) -> Result { - Ok(Ty::from_rigid_kind(RigidTy::Array(elem_ty, Const::try_from_target_usize(size)?))) + Ok(Ty::from_rigid_kind(RigidTy::Array(elem_ty, TyConst::try_from_target_usize(size)?))) } /// Create a new array type from Const length. - pub fn new_array_with_const_len(elem_ty: Ty, len: Const) -> Ty { + pub fn new_array_with_const_len(elem_ty: Ty, len: TyConst) -> Ty { Ty::from_rigid_kind(RigidTy::Array(elem_ty, len)) } @@ -101,24 +102,66 @@ impl Ty { /// Represents a pattern in the type system #[derive(Clone, Debug, Eq, PartialEq)] pub enum Pattern { - Range { start: Option, end: Option, include_end: bool }, + Range { start: Option, end: Option, include_end: bool }, } -/// Represents a constant in MIR or from the Type system. +/// Represents a constant in the type system #[derive(Clone, Debug, Eq, PartialEq)] -pub struct Const { +pub struct TyConst { + pub(crate) kind: TyConstKind, + pub id: TyConstId, +} + +impl TyConst { + pub fn new(kind: TyConstKind, id: TyConstId) -> TyConst { + Self { kind, id } + } + + /// Retrieve the constant kind. + pub fn kind(&self) -> &TyConstKind { + &self.kind + } + + /// Creates an interned usize constant. + pub fn try_from_target_usize(val: u64) -> Result { + with(|cx| cx.try_new_ty_const_uint(val.into(), UintTy::Usize)) + } + + /// Try to evaluate to a target `usize`. + pub fn eval_target_usize(&self) -> Result { + with(|cx| cx.eval_target_usize_ty(self)) + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum TyConstKind { + Param(ParamConst), + Bound(DebruijnIndex, BoundVar), + Unevaluated(ConstDef, GenericArgs), + + // FIXME: These should be a valtree + Value(Ty, Allocation), + ZSTValue(Ty), +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct TyConstId(usize); + +/// Represents a constant in MIR +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct MirConst { /// The constant kind. pub(crate) kind: ConstantKind, /// The constant type. pub(crate) ty: Ty, /// Used for internal tracking of the internal constant. - pub id: ConstId, + pub id: MirConstId, } -impl Const { +impl MirConst { /// Build a constant. Note that this should only be used by the compiler. - pub fn new(kind: ConstantKind, ty: Ty, id: ConstId) -> Const { - Const { kind, ty, id } + pub fn new(kind: ConstantKind, ty: Ty, id: MirConstId) -> MirConst { + MirConst { kind, ty, id } } /// Retrieve the constant kind. @@ -131,11 +174,6 @@ impl Const { self.ty } - /// Creates an interned usize constant. - fn try_from_target_usize(val: u64) -> Result { - with(|cx| cx.try_new_const_uint(val.into(), UintTy::Usize)) - } - /// Try to evaluate to a target `usize`. pub fn eval_target_usize(&self) -> Result { with(|cx| cx.eval_target_usize(self)) @@ -143,7 +181,7 @@ impl Const { /// Create a constant that represents a new zero-sized constant of type T. /// Fails if the type is not a ZST or if it doesn't have a known size. - pub fn try_new_zero_sized(ty: Ty) -> Result { + pub fn try_new_zero_sized(ty: Ty) -> Result { with(|cx| cx.try_new_const_zst(ty)) } @@ -152,23 +190,23 @@ impl Const { /// Note that there is no guarantee today about duplication of the same constant. /// I.e.: Calling this function multiple times with the same argument may or may not return /// the same allocation. - pub fn from_str(value: &str) -> Const { + pub fn from_str(value: &str) -> MirConst { with(|cx| cx.new_const_str(value)) } /// Build a new constant that represents the given boolean value. - pub fn from_bool(value: bool) -> Const { + pub fn from_bool(value: bool) -> MirConst { with(|cx| cx.new_const_bool(value)) } /// Build a new constant that represents the given unsigned integer. - pub fn try_from_uint(value: u128, uint_ty: UintTy) -> Result { + pub fn try_from_uint(value: u128, uint_ty: UintTy) -> Result { with(|cx| cx.try_new_const_uint(value, uint_ty)) } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct ConstId(usize); +pub struct MirConstId(usize); type Ident = Opaque; @@ -190,7 +228,6 @@ pub(crate) type DebruijnIndex = u32; #[derive(Clone, Debug, Eq, PartialEq)] pub struct EarlyParamRegion { - pub def_id: RegionDef, pub index: u32, pub name: Symbol, } @@ -468,6 +505,15 @@ impl TyKind { pub fn discriminant_ty(&self) -> Option { self.rigid().map(|ty| with(|cx| cx.rigid_ty_discriminant_ty(ty))) } + + /// Deconstruct a function type if this is one. + pub fn fn_def(&self) -> Option<(FnDef, &GenericArgs)> { + if let TyKind::RigidTy(RigidTy::FnDef(def, args)) = self { + Some((*def, args)) + } else { + None + } + } } pub struct TypeAndMut { @@ -485,7 +531,7 @@ pub enum RigidTy { Adt(AdtDef, GenericArgs), Foreign(ForeignDef), Str, - Array(Ty, Const), + Array(Ty, TyConst), Pat(Ty, Pattern), Slice(Ty), RawPtr(Ty, Mutability), @@ -527,7 +573,7 @@ pub enum IntTy { impl IntTy { pub fn num_bytes(self) -> usize { match self { - IntTy::Isize => crate::target::MachineInfo::target_pointer_width().bytes().into(), + IntTy::Isize => crate::target::MachineInfo::target_pointer_width().bytes(), IntTy::I8 => 1, IntTy::I16 => 2, IntTy::I32 => 4, @@ -550,7 +596,7 @@ pub enum UintTy { impl UintTy { pub fn num_bytes(self) -> usize { match self { - UintTy::Usize => crate::target::MachineInfo::target_pointer_width().bytes().into(), + UintTy::Usize => crate::target::MachineInfo::target_pointer_width().bytes(), UintTy::U8 => 1, UintTy::U16 => 2, UintTy::U32 => 4, @@ -593,7 +639,7 @@ impl ForeignModule { } } -crate_def! { +crate_def_with_ty! { /// Hold information about a ForeignItem in a crate. pub ForeignDef; } @@ -611,7 +657,7 @@ pub enum ForeignItemKind { Type(Ty), } -crate_def! { +crate_def_with_ty! { /// Hold information about a function definition in a crate. pub FnDef; } @@ -621,6 +667,52 @@ impl FnDef { pub fn body(&self) -> Option { with(|ctx| ctx.has_body(self.0).then(|| ctx.mir_body(self.0))) } + + // Check if the function body is available. + pub fn has_body(&self) -> bool { + with(|ctx| ctx.has_body(self.0)) + } + + /// Get the information of the intrinsic if this function is a definition of one. + pub fn as_intrinsic(&self) -> Option { + with(|cx| cx.intrinsic(self.def_id())) + } + + /// Check if the function is an intrinsic. + #[inline] + pub fn is_intrinsic(&self) -> bool { + self.as_intrinsic().is_some() + } + + /// Get the function signature for this function definition. + pub fn fn_sig(&self) -> PolyFnSig { + let kind = self.ty().kind(); + kind.fn_sig().unwrap() + } +} + +crate_def_with_ty! { + pub IntrinsicDef; +} + +impl IntrinsicDef { + /// Returns the plain name of the intrinsic. + /// e.g., `transmute` for `core::intrinsics::transmute`. + pub fn fn_name(&self) -> Symbol { + with(|cx| cx.intrinsic_name(*self)) + } + + /// Returns whether the intrinsic has no meaningful body and all backends + /// need to shim all calls to it. + pub fn must_be_overridden(&self) -> bool { + with(|cx| !cx.has_body(self.0)) + } +} + +impl From for FnDef { + fn from(def: IntrinsicDef) -> Self { + FnDef(def.0) + } } crate_def! { @@ -639,7 +731,7 @@ crate_def! { pub BrNamedDef; } -crate_def! { +crate_def_with_ty! { pub AdtDef; } @@ -795,7 +887,7 @@ crate_def! { pub GenericDef; } -crate_def! { +crate_def_with_ty! { pub ConstDef; } @@ -832,7 +924,7 @@ impl std::ops::Index for GenericArgs { } impl std::ops::Index for GenericArgs { - type Output = Const; + type Output = TyConst; fn index(&self, index: ParamConst) -> &Self::Output { self.0[index.index as usize].expect_const() @@ -843,7 +935,7 @@ impl std::ops::Index for GenericArgs { pub enum GenericArgKind { Lifetime(Region), Type(Ty), - Const(Const), + Const(TyConst), } impl GenericArgKind { @@ -860,7 +952,7 @@ impl GenericArgKind { /// Panic if this generic argument is not a const, otherwise /// return the const. #[track_caller] - pub fn expect_const(&self) -> &Const { + pub fn expect_const(&self) -> &TyConst { match self { GenericArgKind::Const(c) => c, _ => panic!("{self:?}"), @@ -879,7 +971,7 @@ impl GenericArgKind { #[derive(Clone, Debug, Eq, PartialEq)] pub enum TermKind { Type(Ty), - Const(Const), + Const(TyConst), } #[derive(Clone, Debug, Eq, PartialEq)] @@ -1151,7 +1243,7 @@ impl Allocation { match self.read_int()? { 0 => Ok(false), 1 => Ok(true), - val @ _ => Err(error!("Unexpected value for bool: `{val}`")), + val => Err(error!("Unexpected value for bool: `{val}`")), } } @@ -1168,6 +1260,7 @@ impl Allocation { #[derive(Clone, Debug, Eq, PartialEq)] pub enum ConstantKind { + Ty(TyConst), Allocated(Allocation), Unevaluated(UnevaluatedConst), Param(ParamConst), @@ -1205,6 +1298,7 @@ pub struct TraitDecl { pub is_marker: bool, pub is_coinductive: bool, pub skip_array_during_method_dispatch: bool, + pub skip_boxed_slice_during_method_dispatch: bool, pub specialization_kind: TraitSpecializationKind, pub must_implement_one_of: Option>, pub implement_via_object: bool, @@ -1300,7 +1394,7 @@ pub enum PredicateKind { ObjectSafe(TraitDef), SubType(SubtypePredicate), Coerce(CoercePredicate), - ConstEquate(Const, Const), + ConstEquate(TyConst, TyConst), Ambiguous, AliasRelate(TermKind, TermKind, AliasRelationDirection), } @@ -1311,9 +1405,9 @@ pub enum ClauseKind { RegionOutlives(RegionOutlivesPredicate), TypeOutlives(TypeOutlivesPredicate), Projection(ProjectionPredicate), - ConstArgHasType(Const, Ty), + ConstArgHasType(TyConst, Ty), WellFormed(GenericArgKind), - ConstEvaluatable(Const), + ConstEvaluatable(TyConst), } #[derive(Clone, Debug, Eq, PartialEq)] @@ -1391,7 +1485,8 @@ macro_rules! index_impl { }; } -index_impl!(ConstId); +index_impl!(TyConstId); +index_impl!(MirConstId); index_impl!(Ty); index_impl!(Span); diff --git a/compiler/stable_mir/src/visitor.rs b/compiler/stable_mir/src/visitor.rs index 2d7159f87fec..fc1da8fafe48 100644 --- a/compiler/stable_mir/src/visitor.rs +++ b/compiler/stable_mir/src/visitor.rs @@ -1,10 +1,10 @@ use std::ops::ControlFlow; -use crate::Opaque; +use crate::{ty::TyConst, Opaque}; use super::ty::{ - Allocation, Binder, Const, ConstDef, ExistentialPredicate, FnSig, GenericArgKind, GenericArgs, - Promoted, Region, RigidTy, TermKind, Ty, UnevaluatedConst, + Allocation, Binder, ConstDef, ExistentialPredicate, FnSig, GenericArgKind, GenericArgs, + MirConst, Promoted, Region, RigidTy, TermKind, Ty, UnevaluatedConst, }; pub trait Visitor: Sized { @@ -12,7 +12,7 @@ pub trait Visitor: Sized { fn visit_ty(&mut self, ty: &Ty) -> ControlFlow { ty.super_visit(self) } - fn visit_const(&mut self, c: &Const) -> ControlFlow { + fn visit_const(&mut self, c: &TyConst) -> ControlFlow { c.super_visit(self) } fn visit_reg(&mut self, reg: &Region) -> ControlFlow { @@ -42,12 +42,32 @@ impl Visitable for Ty { } } -impl Visitable for Const { +impl Visitable for TyConst { fn visit(&self, visitor: &mut V) -> ControlFlow { visitor.visit_const(self) } + fn super_visit(&self, visitor: &mut V) -> ControlFlow { + match &self.kind { + crate::ty::TyConstKind::Param(_) => {} + crate::ty::TyConstKind::Bound(_, _) => {} + crate::ty::TyConstKind::Unevaluated(_, args) => args.visit(visitor)?, + crate::ty::TyConstKind::Value(ty, alloc) => { + alloc.visit(visitor)?; + ty.visit(visitor)?; + } + crate::ty::TyConstKind::ZSTValue(ty) => ty.visit(visitor)?, + } + ControlFlow::Continue(()) + } +} + +impl Visitable for MirConst { + fn visit(&self, visitor: &mut V) -> ControlFlow { + self.super_visit(visitor) + } fn super_visit(&self, visitor: &mut V) -> ControlFlow { match &self.kind() { + super::ty::ConstantKind::Ty(ct) => ct.visit(visitor)?, super::ty::ConstantKind::Allocated(alloc) => alloc.visit(visitor)?, super::ty::ConstantKind::Unevaluated(uv) => uv.visit(visitor)?, super::ty::ConstantKind::Param(_) | super::ty::ConstantKind::ZeroSized => {} diff --git a/config.example.toml b/config.example.toml index 228521747ede..679abcdc7771 100644 --- a/config.example.toml +++ b/config.example.toml @@ -321,8 +321,7 @@ # # If `extended = false`, the only one of these built by default is rustdoc. # -# If `extended = true`, they're all included, with the exception of -# rust-demangler which additionally requires `profiler = true` to be set. +# If `extended = true`, they are all included. # # If any enabled tool fails to build, the installation fails. #tools = [ @@ -334,7 +333,6 @@ # "rust-analyzer-proc-macro-srv", # "analysis", # "src", -# "rust-demangler", # if profiler = true #] # Verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose, 3 == print environment variables on each rustc invocation @@ -389,8 +387,8 @@ # a Nix toolchain on non-NixOS distributions. #patch-binaries-for-nix = false -# Collect information and statistics about the current build and writes it to -# disk. Enabling this or not has no impact on the resulting build output. The +# Collect information and statistics about the current build, and write it to +# disk. Enabling this has no impact on the resulting build output. The # schema of the file generated by the build metrics feature is unstable, and # this is not intended to be used during local development. #metrics = false @@ -657,7 +655,7 @@ # whether to set it as rustc's default linker on `x86_64-unknown-linux-gnu`. This will also only be # when *not* building an external LLVM (so only when using `download-ci-llvm` or building LLVM from # the in-tree source): setting `llvm-config` in the `[target.x86_64-unknown-linux-gnu]` section will -# make this default to false. +# make this default to false. #lld = false in all cases, except on `x86_64-unknown-linux-gnu` as described above, where it is true # Indicates whether LLD will be used to link Rust crates during bootstrap on @@ -676,7 +674,7 @@ #llvm-tools = true # Indicates whether the `self-contained` llvm-bitcode-linker, will be made available -# in the sysroot +# in the sysroot. It is required for running nvptx tests. #llvm-bitcode-linker = false # Whether to deny warnings in crates @@ -904,9 +902,6 @@ # on linux #src-tarball = true -# Whether to allow failures when building tools -#missing-tools = false - # List of compression formats to use when generating dist tarballs. The list of # formats is provided to rust-installer, which must support all of them. # diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index e8afed6b35a8..024b92790e97 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -20,6 +20,10 @@ rand_xorshift = "0.3.0" name = "alloctests" path = "tests/lib.rs" +[[test]] +name = "vec_deque_alloc_error" +path = "tests/vec_deque_alloc_error.rs" + [[bench]] name = "allocbenches" path = "benches/lib.rs" @@ -37,4 +41,17 @@ compiler-builtins-no-asm = ["compiler_builtins/no-asm"] compiler-builtins-mangled-names = ["compiler_builtins/mangled-names"] compiler-builtins-weak-intrinsics = ["compiler_builtins/weak-intrinsics"] # Make panics and failed asserts immediately abort without formatting any message -panic_immediate_abort = [] +panic_immediate_abort = ["core/panic_immediate_abort"] +# Choose algorithms that are optimized for binary size instead of runtime performance +optimize_for_size = ["core/optimize_for_size"] + +[lints.rust.unexpected_cfgs] +level = "warn" +# x.py uses beta cargo, so `check-cfg` entries do not yet take effect +# for rust-lang/rust. But for users of `-Zbuild-std` it does. +check-cfg = [ + 'cfg(bootstrap)', + 'cfg(no_global_oom_handling)', + 'cfg(no_rc)', + 'cfg(no_sync)', +] diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index f1a6df94e117..01a954ed75be 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -135,6 +135,44 @@ //! is not allowed. For more guidance on working with box from unsafe code, see //! [rust-lang/unsafe-code-guidelines#326][ucg#326]. //! +//! # Editions +//! +//! A special case exists for the implementation of `IntoIterator` for arrays on the Rust 2021 +//! edition, as documented [here][array]. Unfortunately, it was later found that a similar +//! workaround should be added for boxed slices, and this was applied in the 2024 edition. +//! +//! Specifically, `IntoIterator` is implemented for `Box<[T]>` on all editions, but specific calls +//! to `into_iter()` for boxed slices will defer to the slice implementation on editions before +//! 2024: +//! +//! ```rust,edition2021 +//! // Rust 2015, 2018, and 2021: +//! +//! # #![allow(boxed_slice_into_iter)] // override our `deny(warnings)` +//! let boxed_slice: Box<[i32]> = vec![0; 3].into_boxed_slice(); +//! +//! // This creates a slice iterator, producing references to each value. +//! for item in boxed_slice.into_iter().enumerate() { +//! let (i, x): (usize, &i32) = item; +//! println!("boxed_slice[{i}] = {x}"); +//! } +//! +//! // The `boxed_slice_into_iter` lint suggests this change for future compatibility: +//! for item in boxed_slice.iter().enumerate() { +//! let (i, x): (usize, &i32) = item; +//! println!("boxed_slice[{i}] = {x}"); +//! } +//! +//! // You can explicitly iterate a boxed slice by value using `IntoIterator::into_iter` +//! for item in IntoIterator::into_iter(boxed_slice).enumerate() { +//! let (i, x): (usize, i32) = item; +//! println!("boxed_slice[{i}] = {x}"); +//! } +//! ``` +//! +//! Similar to the array implementation, this may be modified in the future to remove this override, +//! and it's best to avoid relying on this edition-dependent behavior if you wish to preserve +//! compatibility with future versions of the compiler. //! //! [ucg#198]: https://github.com/rust-lang/unsafe-code-guidelines/issues/198 //! [ucg#326]: https://github.com/rust-lang/unsafe-code-guidelines/issues/326 @@ -165,6 +203,7 @@ use core::ops::{ }; use core::pin::Pin; use core::ptr::{self, addr_of_mut, NonNull, Unique}; +use core::slice; use core::task::{Context, Poll}; #[cfg(not(no_global_oom_handling))] @@ -177,6 +216,7 @@ use crate::raw_vec::RawVec; use crate::str::from_boxed_utf8_unchecked; #[cfg(not(no_global_oom_handling))] use crate::string::String; +use crate::vec; #[cfg(not(no_global_oom_handling))] use crate::vec::Vec; @@ -2080,8 +2120,53 @@ impl FromIterator for Box<[I]> { } } +/// This implementation is required to make sure that the `Box<[I]>: IntoIterator` +/// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket. +#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] +impl !Iterator for Box<[I], A> {} + +/// This implementation is required to make sure that the `&Box<[I]>: IntoIterator` +/// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket. +#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] +impl<'a, I, A: Allocator> !Iterator for &'a Box<[I], A> {} + +/// This implementation is required to make sure that the `&mut Box<[I]>: IntoIterator` +/// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket. +#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] +impl<'a, I, A: Allocator> !Iterator for &'a mut Box<[I], A> {} + +// Note: the `#[rustc_skip_during_method_dispatch(boxed_slice)]` on `trait IntoIterator` +// hides this implementation from explicit `.into_iter()` calls on editions < 2024, +// so those calls will still resolve to the slice implementation, by reference. +#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] +impl IntoIterator for Box<[I], A> { + type IntoIter = vec::IntoIter; + type Item = I; + fn into_iter(self) -> vec::IntoIter { + self.into_vec().into_iter() + } +} + +#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] +impl<'a, I, A: Allocator> IntoIterator for &'a Box<[I], A> { + type IntoIter = slice::Iter<'a, I>; + type Item = &'a I; + fn into_iter(self) -> slice::Iter<'a, I> { + self.iter() + } +} + +#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] +impl<'a, I, A: Allocator> IntoIterator for &'a mut Box<[I], A> { + type IntoIter = slice::IterMut<'a, I>; + type Item = &'a mut I; + fn into_iter(self) -> slice::IterMut<'a, I> { + self.iter_mut() + } +} + #[cfg(not(no_global_oom_handling))] -#[stable(feature = "boxed_str_from_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl FromIterator for Box { fn from_iter>(iter: T) -> Self { String::from_iter(iter).into_boxed_str() @@ -2089,7 +2174,7 @@ impl FromIterator for Box { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "boxed_str_from_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl<'a> FromIterator<&'a char> for Box { fn from_iter>(iter: T) -> Self { String::from_iter(iter).into_boxed_str() @@ -2097,7 +2182,7 @@ impl<'a> FromIterator<&'a char> for Box { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "boxed_str_from_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl<'a> FromIterator<&'a str> for Box { fn from_iter>(iter: T) -> Self { String::from_iter(iter).into_boxed_str() @@ -2105,7 +2190,7 @@ impl<'a> FromIterator<&'a str> for Box { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "boxed_str_from_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl FromIterator for Box { fn from_iter>(iter: T) -> Self { String::from_iter(iter).into_boxed_str() @@ -2113,7 +2198,7 @@ impl FromIterator for Box { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "boxed_str_from_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl FromIterator> for Box { fn from_iter>>(iter: T) -> Self { String::from_iter(iter).into_boxed_str() @@ -2121,7 +2206,7 @@ impl FromIterator> for Box { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "boxed_str_from_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl<'a> FromIterator> for Box { fn from_iter>>(iter: T) -> Self { String::from_iter(iter).into_boxed_str() diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 846b9a1404d2..af01db19139e 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -440,7 +440,7 @@ impl BinaryHeap { /// heap.push(4); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_binary_heap_constructor", issue = "112353")] + #[rustc_const_stable(feature = "const_binary_heap_constructor", since = "1.80.0")] #[must_use] pub const fn new() -> BinaryHeap { BinaryHeap { data: vec![] } @@ -484,7 +484,7 @@ impl BinaryHeap { /// heap.push(4); /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - #[rustc_const_unstable(feature = "const_binary_heap_constructor", issue = "112353")] + #[rustc_const_unstable(feature = "const_binary_heap_new_in", issue = "112353")] #[must_use] pub const fn new_in(alloc: A) -> BinaryHeap { BinaryHeap { data: Vec::new_in(alloc) } @@ -1213,7 +1213,6 @@ impl BinaryHeap { /// Basic usage: /// /// ``` - /// #![feature(binary_heap_as_slice)] /// use std::collections::BinaryHeap; /// use std::io::{self, Write}; /// @@ -1222,7 +1221,7 @@ impl BinaryHeap { /// io::sink().write(heap.as_slice()).unwrap(); /// ``` #[must_use] - #[unstable(feature = "binary_heap_as_slice", issue = "83659")] + #[stable(feature = "binary_heap_as_slice", since = "1.80.0")] pub fn as_slice(&self) -> &[T] { self.data.as_slice() } diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 1c90c171a155..01510a614051 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -1705,7 +1705,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { unsafe { self.current = unlinked_node.as_ref().next; self.list.unlink_node(unlinked_node); - let unlinked_node = Box::from_raw(unlinked_node.as_ptr()); + let unlinked_node = Box::from_raw_in(unlinked_node.as_ptr(), &self.list.alloc); Some(unlinked_node.element) } } @@ -1946,7 +1946,7 @@ where if (self.pred)(&mut node.as_mut().element) { // `unlink_node` is okay with aliasing `element` references. self.list.unlink_node(node); - return Some(Box::from_raw(node.as_ptr()).element); + return Some(Box::from_raw_in(node.as_ptr(), &self.list.alloc).element); } } } diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index 8dcd59d12d92..d3744c5a9d0c 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -1164,3 +1164,42 @@ fn test_drop_panic() { assert_eq!(unsafe { DROPS }, 8); } + +#[test] +fn test_allocator() { + use core::alloc::AllocError; + use core::alloc::Allocator; + use core::alloc::Layout; + use core::cell::Cell; + + struct A { + has_allocated: Cell, + has_deallocated: Cell, + } + + unsafe impl Allocator for A { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + assert!(!self.has_allocated.get()); + self.has_allocated.set(true); + + Global.allocate(layout) + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + assert!(!self.has_deallocated.get()); + self.has_deallocated.set(true); + + unsafe { Global.deallocate(ptr, layout) } + } + } + + let alloc = &A { has_allocated: Cell::new(false), has_deallocated: Cell::new(false) }; + { + let mut list = LinkedList::new_in(alloc); + list.push_back(5u32); + list.remove(0); + } + + assert!(alloc.has_allocated.get()); + assert!(alloc.has_deallocated.get()); +} diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 4643a6bbe2ec..61d05afccfd1 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -982,6 +982,8 @@ impl VecDeque { // `head` and `len` are at most `isize::MAX` and `target_cap < self.capacity()`, so nothing can // overflow. let tail_outside = (target_cap + 1..=self.capacity()).contains(&(self.head + self.len)); + // Used in the drop guard below. + let old_head = self.head; if self.len == 0 { self.head = 0; @@ -1034,12 +1036,74 @@ impl VecDeque { } self.head = new_head; } - self.buf.shrink_to_fit(target_cap); + + struct Guard<'a, T, A: Allocator> { + deque: &'a mut VecDeque, + old_head: usize, + target_cap: usize, + } + + impl Drop for Guard<'_, T, A> { + #[cold] + fn drop(&mut self) { + unsafe { + // SAFETY: This is only called if `buf.shrink_to_fit` unwinds, + // which is the only time it's safe to call `abort_shrink`. + self.deque.abort_shrink(self.old_head, self.target_cap) + } + } + } + + let guard = Guard { deque: self, old_head, target_cap }; + + guard.deque.buf.shrink_to_fit(target_cap); + + // Don't drop the guard if we didn't unwind. + mem::forget(guard); debug_assert!(self.head < self.capacity() || self.capacity() == 0); debug_assert!(self.len <= self.capacity()); } + /// Reverts the deque back into a consistent state in case `shrink_to` failed. + /// This is necessary to prevent UB if the backing allocator returns an error + /// from `shrink` and `handle_alloc_error` subsequently unwinds (see #123369). + /// + /// `old_head` refers to the head index before `shrink_to` was called. `target_cap` + /// is the capacity that it was trying to shrink to. + unsafe fn abort_shrink(&mut self, old_head: usize, target_cap: usize) { + // Moral equivalent of self.head + self.len <= target_cap. Won't overflow + // because `self.len <= target_cap`. + if self.head <= target_cap - self.len { + // The deque's buffer is contiguous, so no need to copy anything around. + return; + } + + // `shrink_to` already copied the head to fit into the new capacity, so this won't overflow. + let head_len = target_cap - self.head; + // `self.head > target_cap - self.len` => `self.len > target_cap - self.head =: head_len` so this must be positive. + let tail_len = self.len - head_len; + + if tail_len <= cmp::min(head_len, self.capacity() - target_cap) { + // There's enough spare capacity to copy the tail to the back (because `tail_len < self.capacity() - target_cap`), + // and copying the tail should be cheaper than copying the head (because `tail_len <= head_len`). + + unsafe { + // The old tail and the new tail can't overlap because the head slice lies between them. The + // head slice ends at `target_cap`, so that's where we copy to. + self.copy_nonoverlapping(0, target_cap, tail_len); + } + } else { + // Either there's not enough spare capacity to make the deque contiguous, or the head is shorter than the tail + // (and therefore hopefully cheaper to copy). + unsafe { + // The old and the new head slice can overlap, so we can't use `copy_nonoverlapping` here. + self.copy(self.head, old_head, head_len); + self.head = old_head; + } + } + } + /// Shortens the deque, keeping the first `len` elements and dropping /// the rest. /// diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index f143e5578717..f1eb195b8846 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -910,6 +910,19 @@ impl From<&CStr> for Rc { } } +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "more_rc_default_impls", since = "1.80.0")] +impl Default for Rc { + /// Creates an empty CStr inside an Rc + /// + /// This may or may not share an allocation with other Rcs on the same thread. + #[inline] + fn default() -> Self { + let c_str: &CStr = Default::default(); + Rc::from(c_str) + } +} + #[cfg(not(test))] #[stable(feature = "default_box_extra", since = "1.17.0")] impl Default for Box { diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 91b83cfe011f..895d1b8d59f2 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -121,7 +121,6 @@ #![feature(deref_pure_trait)] #![feature(dispatch_from_dyn)] #![feature(error_generic_member_access)] -#![feature(error_in_core)] #![feature(exact_size_is_empty)] #![feature(extend_one)] #![feature(fmt_internals)] @@ -160,12 +159,12 @@ #![feature(tuple_trait)] #![feature(unicode_internals)] #![feature(unsize)] +#![feature(unwrap_infallible)] #![feature(vec_pop_if)] // tidy-alphabetical-end // // Language features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(exclusive_range_pattern))] #![cfg_attr(not(test), feature(coroutine_trait))] #![cfg_attr(test, feature(panic_update_hook))] #![cfg_attr(test, feature(test))] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 45b205356758..2b7ab2f6e259 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -257,8 +257,6 @@ use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::{PhantomData, Unsize}; -#[cfg(not(no_global_oom_handling))] -use core::mem::size_of_val; use core::mem::{self, align_of_val_raw, forget, ManuallyDrop}; use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, Receiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; @@ -1356,6 +1354,33 @@ impl Rc { ptr } + /// Consumes the `Rc`, returning the wrapped pointer and allocator. + /// + /// To avoid a memory leak the pointer must be converted back to an `Rc` using + /// [`Rc::from_raw_in`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// use std::rc::Rc; + /// use std::alloc::System; + /// + /// let x = Rc::new_in("hello".to_owned(), System); + /// let (ptr, alloc) = Rc::into_raw_with_allocator(x); + /// assert_eq!(unsafe { &*ptr }, "hello"); + /// let x = unsafe { Rc::from_raw_in(ptr, alloc) }; + /// assert_eq!(&*x, "hello"); + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn into_raw_with_allocator(this: Self) -> (*const T, A) { + let this = mem::ManuallyDrop::new(this); + let ptr = Self::as_ptr(&this); + // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + let alloc = unsafe { ptr::read(&this.alloc) }; + (ptr, alloc) + } + /// Provides a raw pointer to the data. /// /// The counts are not affected in any way and the `Rc` is not consumed. The pointer is valid @@ -2224,6 +2249,31 @@ impl Default for Rc { } } +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "more_rc_default_impls", since = "1.80.0")] +impl Default for Rc { + /// Creates an empty str inside an Rc + /// + /// This may or may not share an allocation with other Rcs on the same thread. + #[inline] + fn default() -> Self { + Rc::from("") + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "more_rc_default_impls", since = "1.80.0")] +impl Default for Rc<[T]> { + /// Creates an empty `[T]` inside an Rc + /// + /// This may or may not share an allocation with other Rcs on the same thread. + #[inline] + fn default() -> Self { + let arr: [T; 0] = []; + Rc::from(arr) + } +} + #[stable(feature = "rust1", since = "1.0.0")] trait RcEqIdent { fn eq(&self, other: &Rc) -> bool; @@ -2999,11 +3049,11 @@ impl Weak { result } - /// Consumes the `Weak` and turns it into a raw pointer. + /// Consumes the `Weak`, returning the wrapped pointer and allocator. /// /// This converts the weak pointer into a raw pointer, while still preserving the ownership of /// one weak reference (the weak count is not modified by this operation). It can be turned - /// back into the `Weak` with [`from_raw`]. + /// back into the `Weak` with [`from_raw_in`]. /// /// The same restrictions of accessing the target of the pointer as with /// [`as_ptr`] apply. @@ -3011,27 +3061,30 @@ impl Weak { /// # Examples /// /// ``` + /// #![feature(allocator_api)] /// use std::rc::{Rc, Weak}; + /// use std::alloc::System; /// - /// let strong = Rc::new("hello".to_owned()); + /// let strong = Rc::new_in("hello".to_owned(), System); /// let weak = Rc::downgrade(&strong); - /// let raw = weak.into_raw(); + /// let (raw, alloc) = weak.into_raw_with_allocator(); /// /// assert_eq!(1, Rc::weak_count(&strong)); /// assert_eq!("hello", unsafe { &*raw }); /// - /// drop(unsafe { Weak::from_raw(raw) }); + /// drop(unsafe { Weak::from_raw_in(raw, alloc) }); /// assert_eq!(0, Rc::weak_count(&strong)); /// ``` /// - /// [`from_raw`]: Weak::from_raw + /// [`from_raw_in`]: Weak::from_raw_in /// [`as_ptr`]: Weak::as_ptr #[inline] #[unstable(feature = "allocator_api", issue = "32838")] - pub fn into_raw_and_alloc(self) -> (*const T, A) { - let rc = mem::ManuallyDrop::new(self); - let result = rc.as_ptr(); - let alloc = unsafe { ptr::read(&rc.alloc) }; + pub fn into_raw_with_allocator(self) -> (*const T, A) { + let this = mem::ManuallyDrop::new(self); + let result = this.as_ptr(); + // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + let alloc = unsafe { ptr::read(&this.alloc) }; (result, alloc) } @@ -3463,7 +3516,7 @@ fn data_offset_align(align: usize) -> usize { layout.size() + layout.padding_needed_for(align) } -/// A uniquely owned `Rc` +/// A uniquely owned [`Rc`]. /// /// This represents an `Rc` that is known to be uniquely owned -- that is, have exactly one strong /// reference. Multiple weak pointers can be created, but attempts to upgrade those to strong @@ -3501,13 +3554,24 @@ fn data_offset_align(align: usize) -> usize { /// including fallible or async constructors. #[unstable(feature = "unique_rc_arc", issue = "112566")] #[derive(Debug)] -pub struct UniqueRc { +pub struct UniqueRc< + T: ?Sized, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { ptr: NonNull>, phantom: PhantomData>, + alloc: A, } +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl, U: ?Sized, A: Allocator> CoerceUnsized> + for UniqueRc +{ +} + +// Depends on A = Global impl UniqueRc { - /// Creates a new `UniqueRc` + /// Creates a new `UniqueRc`. /// /// Weak references to this `UniqueRc` can be created with [`UniqueRc::downgrade`]. Upgrading /// these weak references will fail before the `UniqueRc` has been converted into an [`Rc`]. @@ -3516,34 +3580,36 @@ impl UniqueRc { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "unique_rc_arc", issue = "112566")] pub fn new(value: T) -> Self { - Self { - ptr: Box::leak(Box::new(RcBox { + Self::new_in(value, Global) + } +} + +impl UniqueRc { + /// Creates a new `UniqueRc` in the provided allocator. + /// + /// Weak references to this `UniqueRc` can be created with [`UniqueRc::downgrade`]. Upgrading + /// these weak references will fail before the `UniqueRc` has been converted into an [`Rc`]. + /// After converting the `UniqueRc` into an [`Rc`], any weak references created beforehand will + /// point to the new [`Rc`]. + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "unique_rc_arc", issue = "112566")] + pub fn new_in(value: T, alloc: A) -> Self { + let (ptr, alloc) = Box::into_unique(Box::new_in( + RcBox { strong: Cell::new(0), // keep one weak reference so if all the weak pointers that are created are dropped // the UniqueRc still stays valid. weak: Cell::new(1), value, - })) - .into(), - phantom: PhantomData, - } + }, + alloc, + )); + Self { ptr: ptr.into(), phantom: PhantomData, alloc } } +} - /// Creates a new weak reference to the `UniqueRc` - /// - /// Attempting to upgrade this weak reference will fail before the `UniqueRc` has been converted - /// to a [`Rc`] using [`UniqueRc::into_rc`]. - #[unstable(feature = "unique_rc_arc", issue = "112566")] - pub fn downgrade(this: &Self) -> Weak { - // SAFETY: This pointer was allocated at creation time and we guarantee that we only have - // one strong reference before converting to a regular Rc. - unsafe { - this.ptr.as_ref().inc_weak(); - } - Weak { ptr: this.ptr, alloc: Global } - } - - /// Converts the `UniqueRc` into a regular [`Rc`] +impl UniqueRc { + /// Converts the `UniqueRc` into a regular [`Rc`]. /// /// This consumes the `UniqueRc` and returns a regular [`Rc`] that contains the `value` that /// is passed to `into_rc`. @@ -3551,19 +3617,41 @@ impl UniqueRc { /// Any weak references created before this method is called can now be upgraded to strong /// references. #[unstable(feature = "unique_rc_arc", issue = "112566")] - pub fn into_rc(this: Self) -> Rc { + pub fn into_rc(this: Self) -> Rc { let mut this = ManuallyDrop::new(this); + + // Move the allocator out. + // SAFETY: `this.alloc` will not be accessed again, nor dropped because it is in + // a `ManuallyDrop`. + let alloc: A = unsafe { ptr::read(&this.alloc) }; + // SAFETY: This pointer was allocated at creation time so we know it is valid. unsafe { // Convert our weak reference into a strong reference this.ptr.as_mut().strong.set(1); - Rc::from_inner(this.ptr) + Rc::from_inner_in(this.ptr, alloc) } } } +impl UniqueRc { + /// Creates a new weak reference to the `UniqueRc`. + /// + /// Attempting to upgrade this weak reference will fail before the `UniqueRc` has been converted + /// to a [`Rc`] using [`UniqueRc::into_rc`]. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + pub fn downgrade(this: &Self) -> Weak { + // SAFETY: This pointer was allocated at creation time and we guarantee that we only have + // one strong reference before converting to a regular Rc. + unsafe { + this.ptr.as_ref().inc_weak(); + } + Weak { ptr: this.ptr, alloc: this.alloc.clone() } + } +} + #[unstable(feature = "unique_rc_arc", issue = "112566")] -impl Deref for UniqueRc { +impl Deref for UniqueRc { type Target = T; fn deref(&self) -> &T { @@ -3573,7 +3661,7 @@ impl Deref for UniqueRc { } #[unstable(feature = "unique_rc_arc", issue = "112566")] -impl DerefMut for UniqueRc { +impl DerefMut for UniqueRc { fn deref_mut(&mut self) -> &mut T { // SAFETY: This pointer was allocated at creation time so we know it is valid. We know we // have unique ownership and therefore it's safe to make a mutable reference because @@ -3583,7 +3671,7 @@ impl DerefMut for UniqueRc { } #[unstable(feature = "unique_rc_arc", issue = "112566")] -unsafe impl<#[may_dangle] T> Drop for UniqueRc { +unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for UniqueRc { fn drop(&mut self) { unsafe { // destroy the contained object @@ -3593,7 +3681,7 @@ unsafe impl<#[may_dangle] T> Drop for UniqueRc { self.ptr.as_ref().dec_weak(); if self.ptr.as_ref().weak() == 0 { - Global.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())); + self.alloc.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())); } } } diff --git a/library/alloc/src/rc/tests.rs b/library/alloc/src/rc/tests.rs index c8a40603d9db..0f09be7721fa 100644 --- a/library/alloc/src/rc/tests.rs +++ b/library/alloc/src/rc/tests.rs @@ -606,6 +606,23 @@ fn test_unique_rc_drops_contents() { assert!(dropped); } +/// Exercise the non-default allocator usage. +#[test] +fn test_unique_rc_with_alloc_drops_contents() { + let mut dropped = false; + struct DropMe<'a>(&'a mut bool); + impl Drop for DropMe<'_> { + fn drop(&mut self) { + *self.0 = true; + } + } + { + let rc = UniqueRc::new_in(DropMe(&mut dropped), std::alloc::System); + drop(rc); + } + assert!(dropped); +} + #[test] fn test_unique_rc_weak_clone_holding_ref() { let mut v = UniqueRc::new(0u8); @@ -614,3 +631,12 @@ fn test_unique_rc_weak_clone_holding_ref() { let _ = w.clone(); // touch weak count *r = 123; } + +#[test] +fn test_unique_rc_unsizing_coercion() { + let mut rc: UniqueRc<[u8]> = UniqueRc::new([0u8; 3]); + assert_eq!(rc.len(), 3); + rc[0] = 123; + let rc: Rc<[u8]> = UniqueRc::into_rc(rc); + assert_eq!(*rc, [123, 0, 0]); +} diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index a35c99849b34..f9d884e0ea55 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -18,8 +18,6 @@ use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::{PhantomData, Unsize}; -#[cfg(not(no_global_oom_handling))] -use core::mem::size_of_val; use core::mem::{self, align_of_val_raw}; use core::ops::{CoerceUnsized, Deref, DerefPure, DispatchFromDyn, Receiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; @@ -942,15 +940,18 @@ impl Arc { /// This will succeed even if there are outstanding weak references. /// /// It is strongly recommended to use [`Arc::into_inner`] instead if you don't - /// want to keep the `Arc` in the [`Err`] case. - /// Immediately dropping the [`Err`] payload, like in the expression - /// `Arc::try_unwrap(this).ok()`, can still cause the strong count to - /// drop to zero and the inner value of the `Arc` to be dropped: - /// For instance if two threads each execute this expression in parallel, then - /// there is a race condition. The threads could first both check whether they - /// have the last clone of their `Arc` via `Arc::try_unwrap`, and then - /// both drop their `Arc` in the call to [`ok`][`Result::ok`], - /// taking the strong count from two down to zero. + /// keep the `Arc` in the [`Err`] case. + /// Immediately dropping the [`Err`]-value, as the expression + /// `Arc::try_unwrap(this).ok()` does, can cause the strong count to + /// drop to zero and the inner value of the `Arc` to be dropped. + /// For instance, if two threads execute such an expression in parallel, + /// there is a race condition without the possibility of unsafety: + /// The threads could first both check whether they own the last instance + /// in `Arc::try_unwrap`, determine that they both do not, and then both + /// discard and drop their instance in the call to [`ok`][`Result::ok`]. + /// In this scenario, the value inside the `Arc` is safely destroyed + /// by exactly one of the threads, but neither thread will ever be able + /// to use the value. /// /// # Examples /// @@ -1496,6 +1497,34 @@ impl Arc { ptr } + /// Consumes the `Arc`, returning the wrapped pointer and allocator. + /// + /// To avoid a memory leak the pointer must be converted back to an `Arc` using + /// [`Arc::from_raw_in`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let x = Arc::new_in("hello".to_owned(), System); + /// let (ptr, alloc) = Arc::into_raw_with_allocator(x); + /// assert_eq!(unsafe { &*ptr }, "hello"); + /// let x = unsafe { Arc::from_raw_in(ptr, alloc) }; + /// assert_eq!(&*x, "hello"); + /// ``` + #[must_use = "losing the pointer will leak memory"] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn into_raw_with_allocator(this: Self) -> (*const T, A) { + let this = mem::ManuallyDrop::new(this); + let ptr = Self::as_ptr(&this); + // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + let alloc = unsafe { ptr::read(&this.alloc) }; + (ptr, alloc) + } + /// Provides a raw pointer to the data. /// /// The counts are not affected in any way and the `Arc` is not consumed. The pointer is valid for @@ -2468,6 +2497,14 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Arc { // [2]: (https://github.com/rust-lang/rust/pull/41714) acquire!(self.inner().strong); + // Make sure we aren't trying to "drop" the shared static for empty slices + // used by Default::default. + debug_assert!( + !ptr::addr_eq(self.ptr.as_ptr(), &STATIC_INNER_SLICE.inner), + "Arcs backed by a static should never reach a strong count of 0. \ + Likely decrement_strong_count or from_raw were called too many times.", + ); + unsafe { self.drop_slow(); } @@ -2740,6 +2777,45 @@ impl Weak { result } + /// Consumes the `Weak`, returning the wrapped pointer and allocator. + /// + /// This converts the weak pointer into a raw pointer, while still preserving the ownership of + /// one weak reference (the weak count is not modified by this operation). It can be turned + /// back into the `Weak` with [`from_raw_in`]. + /// + /// The same restrictions of accessing the target of the pointer as with + /// [`as_ptr`] apply. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// use std::sync::{Arc, Weak}; + /// use std::alloc::System; + /// + /// let strong = Arc::new_in("hello".to_owned(), System); + /// let weak = Arc::downgrade(&strong); + /// let (raw, alloc) = weak.into_raw_with_allocator(); + /// + /// assert_eq!(1, Arc::weak_count(&strong)); + /// assert_eq!("hello", unsafe { &*raw }); + /// + /// drop(unsafe { Weak::from_raw_in(raw, alloc) }); + /// assert_eq!(0, Arc::weak_count(&strong)); + /// ``` + /// + /// [`from_raw_in`]: Weak::from_raw_in + /// [`as_ptr`]: Weak::as_ptr + #[must_use = "losing the pointer will leak memory"] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn into_raw_with_allocator(self) -> (*const T, A) { + let this = mem::ManuallyDrop::new(self); + let result = this.as_ptr(); + // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + let alloc = unsafe { ptr::read(&this.alloc) }; + (result, alloc) + } + /// Converts a raw pointer previously created by [`into_raw`] back into `Weak` in the provided /// allocator. /// @@ -3059,6 +3135,15 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Weak { if inner.weak.fetch_sub(1, Release) == 1 { acquire!(inner.weak); + + // Make sure we aren't trying to "deallocate" the shared static for empty slices + // used by Default::default. + debug_assert!( + !ptr::addr_eq(self.ptr.as_ptr(), &STATIC_INNER_SLICE.inner), + "Arc/Weaks backed by a static should never be deallocated. \ + Likely decrement_strong_count or from_raw were called too many times.", + ); + unsafe { self.alloc.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())) } @@ -3300,6 +3385,89 @@ impl Default for Arc { } } +/// Struct to hold the static `ArcInner` used for empty `Arc` as +/// returned by `Default::default`. +/// +/// Layout notes: +/// * `repr(align(16))` so we can use it for `[T]` with `align_of::() <= 16`. +/// * `repr(C)` so `inner` is at offset 0 (and thus guaranteed to actually be aligned to 16). +/// * `[u8; 1]` (to be initialized with 0) so it can be used for `Arc`. +#[repr(C, align(16))] +struct SliceArcInnerForStatic { + inner: ArcInner<[u8; 1]>, +} +#[cfg(not(no_global_oom_handling))] +const MAX_STATIC_INNER_SLICE_ALIGNMENT: usize = 16; + +static STATIC_INNER_SLICE: SliceArcInnerForStatic = SliceArcInnerForStatic { + inner: ArcInner { + strong: atomic::AtomicUsize::new(1), + weak: atomic::AtomicUsize::new(1), + data: [0], + }, +}; + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "more_rc_default_impls", since = "1.80.0")] +impl Default for Arc { + /// Creates an empty str inside an Arc + /// + /// This may or may not share an allocation with other Arcs. + #[inline] + fn default() -> Self { + let arc: Arc<[u8]> = Default::default(); + debug_assert!(core::str::from_utf8(&*arc).is_ok()); + let (ptr, alloc) = Arc::into_inner_with_allocator(arc); + unsafe { Arc::from_ptr_in(ptr.as_ptr() as *mut ArcInner, alloc) } + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "more_rc_default_impls", since = "1.80.0")] +impl Default for Arc { + /// Creates an empty CStr inside an Arc + /// + /// This may or may not share an allocation with other Arcs. + #[inline] + fn default() -> Self { + use core::ffi::CStr; + let inner: NonNull> = NonNull::from(&STATIC_INNER_SLICE.inner); + let inner: NonNull> = + NonNull::new(inner.as_ptr() as *mut ArcInner).unwrap(); + // `this` semantically is the Arc "owned" by the static, so make sure not to drop it. + let this: mem::ManuallyDrop> = + unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) }; + (*this).clone() + } +} + +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "more_rc_default_impls", since = "1.80.0")] +impl Default for Arc<[T]> { + /// Creates an empty `[T]` inside an Arc + /// + /// This may or may not share an allocation with other Arcs. + #[inline] + fn default() -> Self { + if mem::align_of::() <= MAX_STATIC_INNER_SLICE_ALIGNMENT { + // We take a reference to the whole struct instead of the ArcInner<[u8; 1]> inside it so + // we don't shrink the range of bytes the ptr is allowed to access under Stacked Borrows. + // (Miri complains on 32-bit targets with Arc<[Align16]> otherwise.) + // (Note that NonNull::from(&STATIC_INNER_SLICE.inner) is fine under Tree Borrows.) + let inner: NonNull = NonNull::from(&STATIC_INNER_SLICE); + let inner: NonNull> = inner.cast(); + // `this` semantically is the Arc "owned" by the static, so make sure not to drop it. + let this: mem::ManuallyDrop> = + unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) }; + return (*this).clone(); + } + + // If T's alignment is too large for the static, make a new unique allocation. + let arr: [T; 0] = []; + Arc::from(arr) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Hash for Arc { fn hash(&self, state: &mut H) { diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index 88aa1b1b0e08..22541a2b9d82 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -259,7 +259,8 @@ where inner.cap, inner.buf.cast::(), inner.end as *const T, - inner.cap * mem::size_of::() / mem::size_of::(), + // SAFETY: the multiplication can not overflow, since `inner.cap * size_of::()` is the size of the allocation. + inner.cap.unchecked_mul(mem::size_of::()) / mem::size_of::(), ) }; @@ -374,7 +375,7 @@ where // - it lets us thread the write pointer through its innards and get it back in the end let sink = InPlaceDrop { inner: dst_buf, dst: dst_buf }; let sink = - self.try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(end)).unwrap(); + self.try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(end)).into_ok(); // iteration succeeded, don't drop head unsafe { ManuallyDrop::new(sink).dst.sub_ptr(dst_buf) } } diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index b0226c848332..c47989337708 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -289,6 +289,60 @@ impl Iterator for IntoIter { }; } + fn fold(mut self, mut accum: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + if T::IS_ZST { + while self.ptr.as_ptr() != self.end.cast_mut() { + // SAFETY: we just checked that `self.ptr` is in bounds. + let tmp = unsafe { self.ptr.read() }; + // See `next` for why we subtract from `end` here. + self.end = self.end.wrapping_byte_sub(1); + accum = f(accum, tmp); + } + } else { + // SAFETY: `self.end` can only be null if `T` is a ZST. + while self.ptr != non_null!(self.end, T) { + // SAFETY: we just checked that `self.ptr` is in bounds. + let tmp = unsafe { self.ptr.read() }; + // SAFETY: the maximum this can be is `self.end`. + // Increment `self.ptr` first to avoid double dropping in the event of a panic. + self.ptr = unsafe { self.ptr.add(1) }; + accum = f(accum, tmp); + } + } + accum + } + + fn try_fold(&mut self, mut accum: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: core::ops::Try, + { + if T::IS_ZST { + while self.ptr.as_ptr() != self.end.cast_mut() { + // SAFETY: we just checked that `self.ptr` is in bounds. + let tmp = unsafe { self.ptr.read() }; + // See `next` for why we subtract from `end` here. + self.end = self.end.wrapping_byte_sub(1); + accum = f(accum, tmp)?; + } + } else { + // SAFETY: `self.end` can only be null if `T` is a ZST. + while self.ptr != non_null!(self.end, T) { + // SAFETY: we just checked that `self.ptr` is in bounds. + let tmp = unsafe { self.ptr.read() }; + // SAFETY: the maximum this can be is `self.end`. + // Increment `self.ptr` first to avoid double dropping in the event of a panic. + self.ptr = unsafe { self.ptr.add(1) }; + accum = f(accum, tmp)?; + } + } + R::from_output(accum) + } + unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> Self::Item where Self: TrustedRandomAccessNoCoerce, diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index b2e22d8715a8..743429d26dbf 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2643,15 +2643,13 @@ impl Vec<[T; N], A> { /// # Examples /// /// ``` - /// #![feature(slice_flatten)] - /// /// let mut vec = vec![[1, 2, 3], [4, 5, 6], [7, 8, 9]]; /// assert_eq!(vec.pop(), Some([7, 8, 9])); /// /// let mut flattened = vec.into_flattened(); /// assert_eq!(flattened.pop(), Some(6)); /// ``` - #[unstable(feature = "slice_flatten", issue = "95629")] + #[stable(feature = "slice_flatten", since = "1.80.0")] pub fn into_flattened(self) -> Vec { let (ptr, len, cap, alloc) = self.into_raw_parts_with_alloc(); let (new_len, new_cap) = if T::IS_ZST { diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 0eae4ca4b8ba..89538f272f06 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -24,7 +24,6 @@ #![feature(binary_heap_into_iter_sorted)] #![feature(binary_heap_drain_sorted)] #![feature(slice_ptr_get)] -#![feature(binary_heap_as_slice)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] @@ -36,7 +35,6 @@ #![feature(const_str_from_utf8)] #![feature(panic_update_hook)] #![feature(pointer_is_aligned_to)] -#![feature(slice_flatten)] #![feature(thin_box)] #![feature(strict_provenance)] #![feature(drain_keep_rest)] diff --git a/library/alloc/tests/vec_deque_alloc_error.rs b/library/alloc/tests/vec_deque_alloc_error.rs new file mode 100644 index 000000000000..8b516ddbc5c5 --- /dev/null +++ b/library/alloc/tests/vec_deque_alloc_error.rs @@ -0,0 +1,50 @@ +#![feature(alloc_error_hook, allocator_api)] + +use std::{ + alloc::{set_alloc_error_hook, AllocError, Allocator, Layout, System}, + collections::VecDeque, + panic::{catch_unwind, AssertUnwindSafe}, + ptr::NonNull, +}; + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_shrink_to_unwind() { + // This tests that `shrink_to` leaves the deque in a consistent state when + // the call to `RawVec::shrink_to_fit` unwinds. The code is adapted from #123369 + // but changed to hopefully not have any UB even if the test fails. + + struct BadAlloc; + + unsafe impl Allocator for BadAlloc { + fn allocate(&self, l: Layout) -> Result, AllocError> { + // We allocate zeroed here so that the whole buffer of the deque + // is always initialized. That way, even if the deque is left in + // an inconsistent state, no uninitialized memory should be accessed. + System.allocate_zeroed(l) + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + unsafe { System.deallocate(ptr, layout) } + } + + unsafe fn shrink( + &self, + _ptr: NonNull, + _old_layout: Layout, + _new_layout: Layout, + ) -> Result, AllocError> { + Err(AllocError) + } + } + + set_alloc_error_hook(|_| panic!("alloc error")); + + let mut v = VecDeque::with_capacity_in(15, BadAlloc); + v.push_back(1); + v.push_front(2); + // This should unwind because it calls `BadAlloc::shrink` and then `handle_alloc_error` which unwinds. + assert!(catch_unwind(AssertUnwindSafe(|| v.shrink_to_fit())).is_err()); + // This should only pass if the deque is left in a consistent state. + assert_eq!(v, [2, 1]); +} diff --git a/library/backtrace b/library/backtrace index e15130618237..72265bea2108 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit e15130618237eb3e2d4b622549f9647b4c1d9ca3 +Subproject commit 72265bea210891ae47bbe6d4f17b493ef0606619 diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index a02fcf504168..cf9fddd269aa 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -31,6 +31,22 @@ rand_xorshift = { version = "0.3.0", default-features = false } [features] # Make panics and failed asserts immediately abort without formatting any message panic_immediate_abort = [] +# Choose algorithms that are optimized for binary size instead of runtime performance +optimize_for_size = [] # Make `RefCell` store additional debugging information, which is printed out when # a borrow error occurs debug_refcell = [] + +[lints.rust.unexpected_cfgs] +level = "warn" +# x.py uses beta cargo, so `check-cfg` entries do not yet take effect +# for rust-lang/rust. But for users of `-Zbuild-std` it does. +check-cfg = [ + 'cfg(bootstrap)', + 'cfg(no_fp_fmt_parse)', + 'cfg(stdarch_intel_sde)', + # core use #[path] imports to portable-simd `core_simd` crate + # and to stdarch `core_arch` crate which messes-up with Cargo list + # of declared features, we therefor expect any feature cfg + 'cfg(feature, values(any()))', +] diff --git a/library/core/benches/str/debug.rs b/library/core/benches/str/debug.rs index 7c72228f0fb5..cb91169eed8e 100644 --- a/library/core/benches/str/debug.rs +++ b/library/core/benches/str/debug.rs @@ -44,7 +44,7 @@ fn ascii_escapes(b: &mut Bencher) { assert_fmt( s, r#""some\tmore\tascii\ttext\nthis time with some \"escapes\", also 64 byte""#, - 21, + 15, ); b.iter(|| { black_box(format!("{:?}", black_box(s))); @@ -72,7 +72,7 @@ fn mostly_unicode(b: &mut Bencher) { #[bench] fn mixed(b: &mut Bencher) { let s = "\"❤️\"\n\"hűha ez betű\"\n\"кириллических букв\"."; - assert_fmt(s, r#""\"❤\u{fe0f}\"\n\"hűha ez betű\"\n\"кириллических букв\".""#, 36); + assert_fmt(s, r#""\"❤\u{fe0f}\"\n\"hűha ez betű\"\n\"кириллических букв\".""#, 21); b.iter(|| { black_box(format!("{:?}", black_box(s))); }); diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index e3d2cd2a31fb..b314d0536a35 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -38,7 +38,7 @@ pub struct IntoIter { alive: IndexRange, } -// Note: the `#[rustc_skip_array_during_method_dispatch]` on `trait IntoIterator` +// Note: the `#[rustc_skip_during_method_dispatch(array)]` on `trait IntoIterator` // hides this implementation from explicit `.into_iter()` calls on editions < 2021, // so those calls will still resolve to the slice implementation, by reference. #[stable(feature = "array_into_iter_impl", since = "1.53.0")] diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 05874ab6c4cb..3e4eadbb7c97 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -10,7 +10,7 @@ use crate::convert::Infallible; use crate::error::Error; use crate::fmt; use crate::hash::{self, Hash}; -use crate::iter::UncheckedIterator; +use crate::iter::{repeat_n, UncheckedIterator}; use crate::mem::{self, MaybeUninit}; use crate::ops::{ ChangeOutputType, ControlFlow, FromResidual, Index, IndexMut, NeverShortCircuit, Residual, Try, @@ -27,6 +27,33 @@ pub(crate) use drain::drain_array_with; #[stable(feature = "array_value_iter", since = "1.51.0")] pub use iter::IntoIter; +/// Creates an array of type `[T; N]` by repeatedly cloning a value. +/// +/// This is the same as `[val; N]`, but it also works for types that do not +/// implement [`Copy`]. +/// +/// The provided value will be used as an element of the resulting array and +/// will be cloned N - 1 times to fill up the rest. If N is zero, the value +/// will be dropped. +/// +/// # Example +/// +/// Creating muliple copies of a `String`: +/// ```rust +/// #![feature(array_repeat)] +/// +/// use std::array; +/// +/// let string = "Hello there!".to_string(); +/// let strings = array::repeat(string); +/// assert_eq!(strings, ["Hello there!", "Hello there!"]); +/// ``` +#[inline] +#[unstable(feature = "array_repeat", issue = "126695")] +pub fn repeat(val: T) -> [T; N] { + from_trusted_iterator(repeat_n(val, N)) +} + /// Creates an array of type [T; N], where each element `T` is the returned value from `cb` /// using that element's index. /// @@ -533,11 +560,9 @@ impl [T; N] { /// assert_eq!(c, Some(a)); /// ``` #[unstable(feature = "array_try_map", issue = "79711")] - pub fn try_map(self, f: F) -> ChangeOutputType + pub fn try_map(self, f: impl FnMut(T) -> R) -> ChangeOutputType where - F: FnMut(T) -> R, - R: Try, - R::Residual: Residual<[R::Output; N]>, + R: Try>, { drain_array_with(self, |iter| try_from_trusted_iterator(iter.map(f))) } diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 4b491ffdafa7..b3189f14f9e4 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -82,6 +82,20 @@ //! //! The corresponding [`Sync`] version of `OnceCell` is [`OnceLock`]. //! +//! ## `LazyCell` +//! +//! A common pattern with OnceCell is, for a given OnceCell, to use the same function on every +//! call to [`OnceCell::get_or_init`] with that cell. This is what is offered by [`LazyCell`], +//! which pairs cells of `T` with functions of `F`, and always calls `F` before it yields `&T`. +//! This happens implicitly by simply attempting to dereference the LazyCell to get its contents, +//! so its use is much more transparent with a place which has been initialized by a constant. +//! +//! More complicated patterns that don't fit this description can be built on `OnceCell` instead. +//! +//! `LazyCell` works by providing an implementation of `impl Deref` that calls the function, +//! so you can just use it by dereference (e.g. `*lazy_cell` or `lazy_cell.deref()`). +//! +//! The corresponding [`Sync`] version of `LazyCell` is [`LazyLock`]. //! //! # When to choose interior mutability //! @@ -230,6 +244,7 @@ //! [`RwLock`]: ../../std/sync/struct.RwLock.html //! [`Mutex`]: ../../std/sync/struct.Mutex.html //! [`OnceLock`]: ../../std/sync/struct.OnceLock.html +//! [`LazyLock`]: ../../std/sync/struct.LazyLock.html //! [`Sync`]: ../../std/marker/trait.Sync.html //! [`atomic`]: crate::sync::atomic @@ -238,14 +253,14 @@ use crate::cmp::Ordering; use crate::fmt::{self, Debug, Display}; use crate::marker::{PhantomData, Unsize}; -use crate::mem::{self, size_of}; +use crate::mem; use crate::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn}; use crate::ptr::{self, NonNull}; mod lazy; mod once; -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] pub use lazy::LazyCell; #[stable(feature = "once_cell", since = "1.70.0")] pub use once::OnceCell; diff --git a/library/core/src/cell/lazy.rs b/library/core/src/cell/lazy.rs index 1b213f6a2941..7c7130ec075e 100644 --- a/library/core/src/cell/lazy.rs +++ b/library/core/src/cell/lazy.rs @@ -18,8 +18,6 @@ enum State { /// # Examples /// /// ``` -/// #![feature(lazy_cell)] -/// /// use std::cell::LazyCell; /// /// let lazy: LazyCell = LazyCell::new(|| { @@ -36,7 +34,7 @@ enum State { /// // 92 /// // 92 /// ``` -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] pub struct LazyCell T> { state: UnsafeCell>, } @@ -47,8 +45,6 @@ impl T> LazyCell { /// # Examples /// /// ``` - /// #![feature(lazy_cell)] - /// /// use std::cell::LazyCell; /// /// let hello = "Hello, World!".to_string(); @@ -58,7 +54,8 @@ impl T> LazyCell { /// assert_eq!(&*lazy, "HELLO, WORLD!"); /// ``` #[inline] - #[unstable(feature = "lazy_cell", issue = "109736")] + #[stable(feature = "lazy_cell", since = "1.80.0")] + #[rustc_const_stable(feature = "lazy_cell", since = "1.80.0")] pub const fn new(f: F) -> LazyCell { LazyCell { state: UnsafeCell::new(State::Uninit(f)) } } @@ -70,7 +67,6 @@ impl T> LazyCell { /// # Examples /// /// ``` - /// #![feature(lazy_cell)] /// #![feature(lazy_cell_consume)] /// /// use std::cell::LazyCell; @@ -82,7 +78,7 @@ impl T> LazyCell { /// assert_eq!(&*lazy, "HELLO, WORLD!"); /// assert_eq!(LazyCell::into_inner(lazy).ok(), Some("HELLO, WORLD!".to_string())); /// ``` - #[unstable(feature = "lazy_cell_consume", issue = "109736")] + #[unstable(feature = "lazy_cell_consume", issue = "125623")] pub fn into_inner(this: Self) -> Result { match this.state.into_inner() { State::Init(data) => Ok(data), @@ -99,8 +95,6 @@ impl T> LazyCell { /// # Examples /// /// ``` - /// #![feature(lazy_cell)] - /// /// use std::cell::LazyCell; /// /// let lazy = LazyCell::new(|| 92); @@ -109,7 +103,7 @@ impl T> LazyCell { /// assert_eq!(&*lazy, &92); /// ``` #[inline] - #[unstable(feature = "lazy_cell", issue = "109736")] + #[stable(feature = "lazy_cell", since = "1.80.0")] pub fn force(this: &LazyCell) -> &T { // SAFETY: // This invalidates any mutable references to the data. The resulting @@ -173,7 +167,7 @@ impl LazyCell { } } -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl T> Deref for LazyCell { type Target = T; #[inline] @@ -182,7 +176,7 @@ impl T> Deref for LazyCell { } } -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl Default for LazyCell { /// Creates a new lazy value using `Default` as the initializing function. #[inline] @@ -191,7 +185,7 @@ impl Default for LazyCell { } } -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl fmt::Debug for LazyCell { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_tuple("LazyCell"); diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs index a7c3dfc982d1..872b4da4dbfd 100644 --- a/library/core/src/cell/once.rs +++ b/library/core/src/cell/once.rs @@ -2,12 +2,12 @@ use crate::cell::UnsafeCell; use crate::fmt; use crate::mem; -/// A cell which can be written to only once. +/// A cell which can nominally be written to only once. /// /// This allows obtaining a shared `&T` reference to its inner value without copying or replacing /// it (unlike [`Cell`]), and without runtime borrow checks (unlike [`RefCell`]). However, /// only immutable references can be obtained unless one has a mutable reference to the cell -/// itself. +/// itself. In the same vein, the cell can only be re-initialized with such a mutable reference. /// /// For a thread-safe version of this struct, see [`std::sync::OnceLock`]. /// diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index fc6022ab7535..f3f757ce69df 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -898,6 +898,7 @@ pub trait Ord: Eq + PartialOrd { /// assert_eq!(2.clamp(-2, 1), 1); /// ``` #[must_use] + #[inline] #[stable(feature = "clamp", since = "1.50.0")] fn clamp(self, min: Self, max: Self) -> Self where diff --git a/library/core/src/error.md b/library/core/src/error.md index a5deb71e6b80..4b62391cafc3 100644 --- a/library/core/src/error.md +++ b/library/core/src/error.md @@ -17,8 +17,8 @@ The following are the primary interfaces of the panic system and the responsibilities they cover: * [`panic!`] and [`panic_any`] (Constructing, Propagated automatically) -* [`PanicInfo`] (Reporting) -* [`set_hook`], [`take_hook`], and [`#[panic_handler]`][panic-handler] (Reporting) +* [`set_hook`], [`take_hook`], and [`PanicHookInfo`] (Reporting) +* [`#[panic_handler]`][panic-handler] and [`PanicInfo`] (Reporting in no_std) * [`catch_unwind`] and [`resume_unwind`] (Discarding, Propagating) The following are the primary interfaces of the error system and the @@ -125,6 +125,7 @@ expect-as-precondition style error messages remember to focus on the word should be available and executable by the current user". [`panic_any`]: ../../std/panic/fn.panic_any.html +[`PanicHookInfo`]: ../../std/panic/struct.PanicHookInfo.html [`PanicInfo`]: crate::panic::PanicInfo [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html [`resume_unwind`]: ../../std/panic/fn.resume_unwind.html diff --git a/library/core/src/error.rs b/library/core/src/error.rs index a3f2b767054e..150e4f3f3185 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -1,5 +1,5 @@ #![doc = include_str!("error.md")] -#![unstable(feature = "error_in_core", issue = "103765")] +#![stable(feature = "error_in_core", since = "CURRENT_RUSTC_VERSION")] #[cfg(test)] mod tests; @@ -130,7 +130,6 @@ pub trait Error: Debug + Display { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// use core::fmt; /// use core::error::{request_ref, Request}; /// @@ -361,8 +360,7 @@ impl dyn Error { /// Get a string value from an error. /// /// ```rust -/// # #![feature(error_generic_member_access)] -/// # #![feature(error_in_core)] +/// #![feature(error_generic_member_access)] /// use std::error::Error; /// use core::error::request_value; /// @@ -385,8 +383,7 @@ where /// Get a string reference from an error. /// /// ```rust -/// # #![feature(error_generic_member_access)] -/// # #![feature(error_in_core)] +/// #![feature(error_generic_member_access)] /// use core::error::Error; /// use core::error::request_ref; /// @@ -407,9 +404,9 @@ fn request_by_type_tag<'a, I>(err: &'a (impl Error + ?Sized)) -> Option, { - let mut tagged = TaggedOption::<'a, I>(None); + let mut tagged = Tagged { tag_id: TypeId::of::(), value: TaggedOption::<'a, I>(None) }; err.provide(tagged.as_request()); - tagged.0 + tagged.value.0 } /////////////////////////////////////////////////////////////////////////////// @@ -458,7 +455,6 @@ where /// /// ``` /// #![feature(error_generic_member_access)] -/// #![feature(error_in_core)] /// use core::fmt; /// use core::error::Request; /// use core::error::request_ref; @@ -511,16 +507,9 @@ where /// #[unstable(feature = "error_generic_member_access", issue = "99301")] #[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435 -pub struct Request<'a>(dyn Erased<'a> + 'a); +pub struct Request<'a>(Tagged + 'a>); impl<'a> Request<'a> { - /// Create a new `&mut Request` from a `&mut dyn Erased` trait object. - fn new<'b>(erased: &'b mut (dyn Erased<'a> + 'a)) -> &'b mut Request<'a> { - // SAFETY: transmuting `&mut (dyn Erased<'a> + 'a)` to `&mut Request<'a>` is safe since - // `Request` is repr(transparent). - unsafe { &mut *(erased as *mut dyn Erased<'a> as *mut Request<'a>) } - } - /// Provide a value or other type with only static lifetimes. /// /// # Examples @@ -529,7 +518,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// @@ -564,7 +552,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// @@ -600,7 +587,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// @@ -633,7 +619,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// @@ -700,7 +685,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// use core::error::request_value; @@ -788,7 +772,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// use core::error::request_ref; @@ -945,32 +928,33 @@ pub(crate) mod tags { /// An `Option` with a type tag `I`. /// /// Since this struct implements `Erased`, the type can be erased to make a dynamically typed -/// option. The type can be checked dynamically using `Erased::tag_id` and since this is statically +/// option. The type can be checked dynamically using `Tagged::tag_id` and since this is statically /// checked for the concrete type, there is some degree of type safety. #[repr(transparent)] pub(crate) struct TaggedOption<'a, I: tags::Type<'a>>(pub Option); -impl<'a, I: tags::Type<'a>> TaggedOption<'a, I> { +impl<'a, I: tags::Type<'a>> Tagged> { pub(crate) fn as_request(&mut self) -> &mut Request<'a> { - Request::new(self as &mut (dyn Erased<'a> + 'a)) + let erased = self as &mut Tagged + 'a>; + // SAFETY: transmuting `&mut Tagged + 'a>` to `&mut Request<'a>` is safe since + // `Request` is repr(transparent). + unsafe { &mut *(erased as *mut Tagged> as *mut Request<'a>) } } } /// Represents a type-erased but identifiable object. /// /// This trait is exclusively implemented by the `TaggedOption` type. -unsafe trait Erased<'a>: 'a { - /// The `TypeId` of the erased type. - fn tag_id(&self) -> TypeId; +unsafe trait Erased<'a>: 'a {} + +unsafe impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> {} + +struct Tagged { + tag_id: TypeId, + value: E, } -unsafe impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> { - fn tag_id(&self) -> TypeId { - TypeId::of::() - } -} - -impl<'a> dyn Erased<'a> + 'a { +impl<'a> Tagged + 'a> { /// Returns some reference to the dynamic value if it is tagged with `I`, /// or `None` otherwise. #[inline] @@ -978,9 +962,9 @@ impl<'a> dyn Erased<'a> + 'a { where I: tags::Type<'a>, { - if self.tag_id() == TypeId::of::() { + if self.tag_id == TypeId::of::() { // SAFETY: Just checked whether we're pointing to an I. - Some(unsafe { &*(self as *const Self).cast::>() }) + Some(&unsafe { &*(self as *const Self).cast::>>() }.value) } else { None } @@ -993,9 +977,12 @@ impl<'a> dyn Erased<'a> + 'a { where I: tags::Type<'a>, { - if self.tag_id() == TypeId::of::() { - // SAFETY: Just checked whether we're pointing to an I. - Some(unsafe { &mut *(self as *mut Self).cast::>() }) + if self.tag_id == TypeId::of::() { + Some( + // SAFETY: Just checked whether we're pointing to an I. + &mut unsafe { &mut *(self as *mut Self).cast::>>() } + .value, + ) } else { None } diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 27dacbb23d95..618897b3abab 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -133,7 +133,8 @@ mod c_char_definition { any(target_arch = "aarch64", target_arch = "riscv64") ), all(target_os = "nto", target_arch = "aarch64"), - target_os = "horizon" + target_os = "horizon", + target_os = "aix", ))] { pub type c_char = u8; } else { diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 9b372eac5245..c25bc5a1b13c 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -338,20 +338,19 @@ pub struct Arguments<'a> { impl<'a> Arguments<'a> { #[inline] #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] - pub const fn new_const(pieces: &'a [&'static str]) -> Self { - if pieces.len() > 1 { - panic!("invalid args"); - } + pub const fn new_const(pieces: &'a [&'static str; N]) -> Self { + const { assert!(N <= 1) }; Arguments { pieces, fmt: None, args: &[] } } /// When using the format_args!() macro, this function is used to generate the /// Arguments structure. #[inline] - pub fn new_v1(pieces: &'a [&'static str], args: &'a [rt::Argument<'a>]) -> Arguments<'a> { - if pieces.len() < args.len() || pieces.len() > args.len() + 1 { - panic!("invalid args"); - } + pub fn new_v1( + pieces: &'a [&'static str; P], + args: &'a [rt::Argument<'a>; A], + ) -> Arguments<'a> { + const { assert!(P >= A && P <= A + 1, "invalid args") } Arguments { pieces, fmt: None, args } } @@ -2399,23 +2398,47 @@ impl Display for bool { impl Debug for str { fn fmt(&self, f: &mut Formatter<'_>) -> Result { f.write_char('"')?; - let mut from = 0; - for (i, c) in self.char_indices() { - let esc = c.escape_debug_ext(EscapeDebugExtArgs { - escape_grapheme_extended: true, - escape_single_quote: false, - escape_double_quote: true, - }); - // If char needs escaping, flush backlog so far and write, else skip - if esc.len() != 1 { - f.write_str(&self[from..i])?; - for c in esc { - f.write_char(c)?; - } - from = i + c.len_utf8(); - } + + // substring we know is printable + let mut printable_range = 0..0; + + fn needs_escape(b: u8) -> bool { + b > 0x7E || b < 0x20 || b == b'\\' || b == b'"' } - f.write_str(&self[from..])?; + + // the loop here first skips over runs of printable ASCII as a fast path. + // other chars (unicode, or ASCII that needs escaping) are then handled per-`char`. + let mut rest = self; + while rest.len() > 0 { + let Some(non_printable_start) = rest.as_bytes().iter().position(|&b| needs_escape(b)) + else { + printable_range.end += rest.len(); + break; + }; + + printable_range.end += non_printable_start; + // SAFETY: the position was derived from an iterator, so is known to be within bounds, and at a char boundary + rest = unsafe { rest.get_unchecked(non_printable_start..) }; + + let mut chars = rest.chars(); + if let Some(c) = chars.next() { + let esc = c.escape_debug_ext(EscapeDebugExtArgs { + escape_grapheme_extended: true, + escape_single_quote: false, + escape_double_quote: true, + }); + if esc.len() != 1 { + f.write_str(&self[printable_range.clone()])?; + Display::fmt(&esc, f)?; + printable_range.start = printable_range.end + c.len_utf8(); + } + printable_range.end += c.len_utf8(); + } + rest = chars.as_str(); + } + + f.write_str(&self[printable_range])?; + f.write_char('"') } } @@ -2431,13 +2454,12 @@ impl Display for str { impl Debug for char { fn fmt(&self, f: &mut Formatter<'_>) -> Result { f.write_char('\'')?; - for c in self.escape_debug_ext(EscapeDebugExtArgs { + let esc = self.escape_debug_ext(EscapeDebugExtArgs { escape_grapheme_extended: true, escape_single_quote: true, escape_double_quote: false, - }) { - f.write_char(c)? - } + }); + Display::fmt(&esc, f)?; f.write_char('\'') } } diff --git a/library/core/src/fmt/nofloat.rs b/library/core/src/fmt/nofloat.rs index a36e7efcd95c..6b07236f1da1 100644 --- a/library/core/src/fmt/nofloat.rs +++ b/library/core/src/fmt/nofloat.rs @@ -4,6 +4,7 @@ macro_rules! floating { ($ty:ident) => { #[stable(feature = "rust1", since = "1.0.0")] impl Debug for $ty { + #[inline] fn fmt(&self, _fmt: &mut Formatter<'_>) -> Result { panic!("floating point support is turned off"); } diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index ab2158394bf1..3a5a5af8bf5d 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -212,6 +212,7 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ macro_rules! impl_Display { ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => { + #[cfg(not(feature = "optimize_for_size"))] fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { // 2^128 is about 3*10^38, so 39 gives an extra byte of space let mut buf = [MaybeUninit::::uninit(); 39]; @@ -277,6 +278,38 @@ macro_rules! impl_Display { f.pad_integral(is_nonnegative, "", buf_slice) } + #[cfg(feature = "optimize_for_size")] + fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // 2^128 is about 3*10^38, so 39 gives an extra byte of space + let mut buf = [MaybeUninit::::uninit(); 39]; + let mut curr = buf.len(); + let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); + + // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning + // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at + // each step this is kept the same as `n` is divided. Since `n` is always + // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]` + // is safe to access. + unsafe { + loop { + curr -= 1; + buf_ptr.add(curr).write((n % 10) as u8 + b'0'); + n /= 10; + + if n == 0 { + break; + } + } + } + + // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8 + let buf_slice = unsafe { + str::from_utf8_unchecked( + slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr)) + }; + f.pad_integral(is_nonnegative, "", buf_slice) + } + $(#[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for $t { #[allow(unused_comparisons)] diff --git a/library/core/src/future/async_drop.rs b/library/core/src/future/async_drop.rs index 0eb8d7bb3289..63193bbfb35e 100644 --- a/library/core/src/future/async_drop.rs +++ b/library/core/src/future/async_drop.rs @@ -1,4 +1,4 @@ -#![unstable(feature = "async_drop", issue = "none")] +#![unstable(feature = "async_drop", issue = "126482")] use crate::fmt; use crate::future::{Future, IntoFuture}; @@ -10,27 +10,27 @@ use crate::task::{ready, Context, Poll}; /// Asynchronously drops a value by running `AsyncDrop::async_drop` /// on a value and its fields recursively. -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] pub fn async_drop(value: T) -> AsyncDropOwning { AsyncDropOwning { value: MaybeUninit::new(value), dtor: None, _pinned: PhantomPinned } } /// A future returned by the [`async_drop`]. -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] pub struct AsyncDropOwning { value: MaybeUninit, dtor: Option>, _pinned: PhantomPinned, } -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] impl fmt::Debug for AsyncDropOwning { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AsyncDropOwning").finish_non_exhaustive() } } -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] impl Future for AsyncDropOwning { type Output = (); @@ -86,24 +86,24 @@ unsafe fn async_drop_in_place_raw( /// returned future stores the `to_drop` pointer and user is required /// to guarantee that dropped value doesn't move. /// -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] pub unsafe fn async_drop_in_place(to_drop: *mut T) -> AsyncDropInPlace { // SAFETY: `async_drop_in_place_raw` has the same safety requirements unsafe { AsyncDropInPlace(async_drop_in_place_raw(to_drop)) } } /// A future returned by the [`async_drop_in_place`]. -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] pub struct AsyncDropInPlace(::AsyncDestructor); -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] impl fmt::Debug for AsyncDropInPlace { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AsyncDropInPlace").finish_non_exhaustive() } } -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] impl Future for AsyncDropInPlace { type Output = (); @@ -117,18 +117,18 @@ impl Future for AsyncDropInPlace { // FIXME(zetanumbers): Add same restrictions on AsyncDrop impls as // with Drop impls /// Custom code within the asynchronous destructor. -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] #[lang = "async_drop"] pub trait AsyncDrop { /// A future returned by the [`AsyncDrop::async_drop`] to be part /// of the async destructor. - #[unstable(feature = "async_drop", issue = "none")] + #[unstable(feature = "async_drop", issue = "126482")] type Dropper<'a>: Future where Self: 'a; /// Constructs the asynchronous destructor for this type. - #[unstable(feature = "async_drop", issue = "none")] + #[unstable(feature = "async_drop", issue = "126482")] fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_>; } @@ -161,6 +161,11 @@ async unsafe fn surface_drop_in_place(ptr: *mut T) { /// wrapped future completes by returning `Poll::Ready(())` on poll. This /// is useful for constructing async destructors to guarantee this /// "fuse" property +// +// FIXME: Consider optimizing combinators to not have to use fuse in majority +// of cases, perhaps by adding `#[(rustc_)idempotent(_future)]` attribute for +// async functions and blocks with the unit return type. However current layout +// optimizations currently encode `None` case into the async block's discriminant. struct Fuse { inner: Option, } @@ -251,6 +256,13 @@ async unsafe fn either, M: IntoFuture, T } } +#[lang = "async_drop_deferred_drop_in_place"] +async unsafe fn deferred_drop_in_place(to_drop: *mut T) { + // SAFETY: same safety requirements as with drop_in_place (implied by + // function's name) + unsafe { crate::ptr::drop_in_place(to_drop) } +} + /// Used for noop async destructors. We don't use [`core::future::Ready`] /// because it panics after its second poll, which could be potentially /// bad if that would happen during the cleanup. diff --git a/library/core/src/future/future.rs b/library/core/src/future/future.rs index f965afc8a593..c80cfdcebf70 100644 --- a/library/core/src/future/future.rs +++ b/library/core/src/future/future.rs @@ -35,7 +35,7 @@ use crate::task::{Context, Poll}; pub trait Future { /// The type of value produced on completion. #[stable(feature = "futures_api", since = "1.36.0")] - #[rustc_diagnostic_item = "FutureOutput"] + #[lang = "future_output"] type Output; /// Attempt to resolve the future to a final value, registering diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs index 873cccc7e96f..3a1451abfa40 100644 --- a/library/core/src/future/mod.rs +++ b/library/core/src/future/mod.rs @@ -37,7 +37,7 @@ pub use ready::{ready, Ready}; #[stable(feature = "future_poll_fn", since = "1.64.0")] pub use poll_fn::{poll_fn, PollFn}; -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] pub use async_drop::{async_drop, async_drop_in_place, AsyncDrop, AsyncDropInPlace}; /// This type is needed because: diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index d58e1dbb3e4d..6b5054a9f061 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -65,7 +65,6 @@ use crate::marker::DiscriminantKind; use crate::marker::Tuple; -use crate::mem::align_of; use crate::ptr; use crate::ub_checks; @@ -987,7 +986,7 @@ pub const unsafe fn assume(b: bool) { #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)] +#[miri::intrinsic_fallback_is_spec] pub const fn likely(b: bool) -> bool { b } @@ -1007,7 +1006,7 @@ pub const fn likely(b: bool) -> bool { #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)] +#[miri::intrinsic_fallback_is_spec] pub const fn unlikely(b: bool) -> bool { b } @@ -1483,10 +1482,10 @@ extern "rust-intrinsic" { /// /// # Safety /// - /// Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of an allocated object. If either pointer is out of - /// bounds or arithmetic overflow occurs then any further use of the - /// returned value will result in undefined behavior. + /// If the computed offset is non-zero, then both the starting and resulting pointer must be + /// either in bounds or at the end of an allocated object. If either pointer is out + /// of bounds or arithmetic overflow occurs then any further use of the returned value will + /// result in undefined behavior. /// /// The stabilized version of this intrinsic is [`pointer::offset`]. #[must_use = "returns a new pointer rather than modifying its argument"] @@ -1502,7 +1501,7 @@ extern "rust-intrinsic" { /// # Safety /// /// Unlike the `offset` intrinsic, this intrinsic does not restrict the - /// resulting pointer to point into or one byte past the end of an allocated + /// resulting pointer to point into or at the end of an allocated /// object, and it wraps with two's complement arithmetic. The resulting /// value is not necessarily valid to be used to actually access memory. /// @@ -2483,7 +2482,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] #[rustc_do_not_const_check] #[inline] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)] +#[miri::intrinsic_fallback_is_spec] pub const fn ptr_guaranteed_cmp(ptr: *const T, other: *const T) -> u8 { (ptr == other) as u8 } @@ -2748,7 +2747,7 @@ pub const fn ub_checks() -> bool { #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_nounwind] #[rustc_intrinsic] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)] +#[miri::intrinsic_fallback_is_spec] pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 { // const eval overrides this function, but runtime code for now just returns null pointers. // See . @@ -2769,7 +2768,7 @@ pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 { #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_nounwind] #[rustc_intrinsic] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)] +#[miri::intrinsic_fallback_is_spec] pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) { // Runtime NOP } @@ -2821,6 +2820,20 @@ impl AggregateRawPtr<*mut T> for *mut P { type Metadata =